little_fs/
file_system.rs

1use core::mem::MaybeUninit;
2
3use alloc::{boxed::Box, collections::btree_map::BTreeMap, ffi::CString, vec::Vec};
4use file_system::{
5    Device, Entry, FileIdentifier, FileIdentifierInner, FileSystemIdentifier, FileSystemTraits,
6    Flags, Inode, Kind, LocalFileIdentifier, Metadata, Mode, Path, Permissions, Position, Size,
7    Statistics_type, Time, get_new_file_identifier,
8};
9use futures::block_on;
10use synchronization::{blocking_mutex::raw::CriticalSectionRawMutex, rwlock::RwLock};
11use users::{GroupIdentifier, UserIdentifier};
12
13use super::{Configuration, Directory, File, convert_result, littlefs};
14
15use file_system::{Error, Result};
16
17struct Inner {
18    file_system: littlefs::lfs_t,
19    open_files: BTreeMap<LocalFileIdentifier, File>,
20    open_directories: BTreeMap<LocalFileIdentifier, Directory>,
21}
22
23pub struct FileSystem {
24    inner: RwLock<CriticalSectionRawMutex, Inner>,
25    cache_size: usize,
26}
27
28impl Drop for FileSystem {
29    fn drop(&mut self) {
30        // - Close all the open files
31        let mut inner = self.write_inner();
32
33        let keys = inner.open_files.keys().cloned().collect::<Vec<_>>();
34
35        for key in keys {
36            if let Some(file) = inner.open_files.remove(&key) {
37                let _ = file.close(&mut inner.file_system);
38            }
39        }
40
41        let configuration =
42            unsafe { Box::from_raw(inner.file_system.cfg as *mut littlefs::lfs_config) };
43
44        let _read_buffer = unsafe {
45            Vec::from_raw_parts(
46                configuration.read_buffer as *mut u8,
47                0,
48                configuration.cache_size as usize,
49            )
50        };
51        let _write_buffer = unsafe {
52            Vec::from_raw_parts(
53                configuration.prog_buffer as *mut u8,
54                0,
55                configuration.cache_size as usize,
56            )
57        };
58        let _look_ahead_buffer = unsafe {
59            Vec::from_raw_parts(
60                configuration.lookahead_buffer as *mut u8,
61                0,
62                configuration.lookahead_size as usize,
63            )
64        };
65
66        // Get the device
67        let _device = unsafe { Box::from_raw(inner.file_system.cfg.read().context as *mut Device) };
68
69        // - Unmount the file system
70        unsafe {
71            littlefs::lfs_unmount(&mut inner.file_system as *mut _);
72        }
73
74        // Configuration, Buffer sand Device are dropped here
75    }
76}
77
78impl FileSystem {
79    pub fn new(device: Device, cache_size: usize) -> Result<Self> {
80        let block_size = device.get_block_size().map_err(|_| Error::InputOutput)?;
81        let size = device.get_size().map_err(|_| Error::InputOutput)?;
82
83        let configuration: littlefs::lfs_config = Configuration::new(
84            device,
85            block_size,
86            usize::from(size),
87            cache_size,
88            cache_size,
89        )
90        .ok_or(Error::InvalidParameter)?
91        .try_into()
92        .map_err(|_| Error::InvalidParameter)?;
93
94        let configuration = Box::new(configuration);
95
96        let mut file_system = MaybeUninit::<littlefs::lfs_t>::uninit();
97
98        convert_result(unsafe {
99            littlefs::lfs_mount(
100                file_system.as_mut_ptr() as *mut _,
101                Box::into_raw(configuration),
102            )
103        })?;
104
105        let inner = Inner {
106            file_system: unsafe { file_system.assume_init() },
107            open_files: BTreeMap::new(),
108            open_directories: BTreeMap::new(),
109        };
110
111        Ok(Self {
112            inner: RwLock::new(inner),
113            cache_size,
114        })
115    }
116
117    pub fn format(device: Device, cache_size: usize) -> Result<()> {
118        let block_size = device.get_block_size().map_err(|_| Error::InputOutput)?;
119        let size = device.get_size().map_err(|_| Error::InputOutput)?;
120
121        let configuration: littlefs::lfs_config = Configuration::new(
122            device,
123            block_size,
124            usize::from(size),
125            cache_size,
126            cache_size,
127        )
128        .ok_or(Error::InvalidParameter)?
129        .try_into()
130        .map_err(|_| Error::InvalidParameter)?;
131
132        let configuration = Box::new(configuration);
133
134        let mut file_system = MaybeUninit::<littlefs::lfs_t>::uninit();
135
136        convert_result(unsafe {
137            littlefs::lfs_format(file_system.as_mut_ptr(), Box::into_raw(configuration))
138        })?;
139
140        Ok(())
141    }
142
143    fn borrow_mutable_inner_2_splitted(
144        inner_2: &mut Inner,
145    ) -> (
146        &mut littlefs::lfs_t,
147        &mut BTreeMap<LocalFileIdentifier, File>,
148        &mut BTreeMap<LocalFileIdentifier, Directory>,
149    ) {
150        (
151            &mut inner_2.file_system,
152            &mut inner_2.open_files,
153            &mut inner_2.open_directories,
154        )
155    }
156
157    #[cfg(target_pointer_width = "64")]
158    const DIRECTORY_FLAG: FileIdentifierInner = 1 << 31;
159    #[cfg(target_pointer_width = "32")]
160    const Directory_flag: FileIdentifierInner = 1 << 15;
161
162    const DIRECTORY_MINIMUM: FileIdentifier = FileIdentifier::new(Self::DIRECTORY_FLAG);
163
164    pub fn is_file(file: LocalFileIdentifier) -> bool {
165        file.split().1 < Self::DIRECTORY_MINIMUM
166    }
167
168    fn read_inner(
169        &self,
170    ) -> synchronization::rwlock::RwLockReadGuard<'_, CriticalSectionRawMutex, Inner> {
171        block_on(self.inner.read())
172    }
173
174    fn write_inner(
175        &self,
176    ) -> synchronization::rwlock::RwLockWriteGuard<'_, CriticalSectionRawMutex, Inner> {
177        block_on(self.inner.write())
178    }
179}
180
181unsafe impl Send for FileSystem {}
182
183unsafe impl Sync for FileSystem {}
184
185impl FileSystemTraits for FileSystem {
186    fn open(
187        &self,
188        task: task::TaskIdentifier,
189        path: &Path,
190        flags: Flags,
191        time: Time,
192        user: UserIdentifier,
193        group: GroupIdentifier,
194    ) -> Result<LocalFileIdentifier> {
195        let mut inner = self.write_inner();
196
197        let file = File::open(
198            &mut inner.file_system,
199            path,
200            flags,
201            self.cache_size,
202            time,
203            user,
204            group,
205        )?;
206
207        let file_identifier = get_new_file_identifier(
208            task,
209            Some(FileIdentifier::MINIMUM),
210            Some(Self::DIRECTORY_MINIMUM),
211            &inner.open_files,
212        )?;
213
214        if inner.open_files.insert(file_identifier, file).is_some() {
215            return Err(Error::InternalError);
216        }
217
218        Ok(file_identifier)
219    }
220
221    fn close(&self, file: LocalFileIdentifier) -> Result<()> {
222        let mut inner = self.write_inner();
223
224        let file = inner
225            .open_files
226            .remove(&file)
227            .ok_or(Error::InvalidIdentifier)?;
228
229        file.close(&mut inner.file_system)?;
230
231        Ok(())
232    }
233
234    fn close_all(&self, task: task::TaskIdentifier) -> Result<()> {
235        let mut inner = self.write_inner();
236
237        // Get all the keys of the open files that belong to the task
238        let keys = inner
239            .open_files
240            .keys()
241            .filter(|key| key.split().0 == task)
242            .cloned()
243            .collect::<Vec<_>>();
244
245        // Close all the files corresponding to the keys
246        for key in keys {
247            if let Some(file) = inner.open_files.remove(&key) {
248                file.close(&mut inner.file_system)?;
249            }
250        }
251
252        Ok(())
253    }
254
255    fn duplicate(&self, file: LocalFileIdentifier) -> Result<LocalFileIdentifier> {
256        let (task, _) = file.split();
257
258        let mut inner = self.write_inner();
259
260        let file = inner
261            .open_files
262            .get(&file)
263            .ok_or(Error::InvalidIdentifier)?;
264
265        let file = file.clone();
266
267        let file_identifier = get_new_file_identifier(
268            task,
269            Some(Self::DIRECTORY_MINIMUM),
270            Some(FileIdentifier::MAXIMUM),
271            &inner.open_files,
272        )?;
273
274        if inner.open_files.insert(file_identifier, file).is_some() {
275            return Err(Error::InternalError);
276        }
277
278        Ok(file_identifier)
279    }
280
281    fn transfert(
282        &self,
283        new_task: task::TaskIdentifier,
284        file_identifier: LocalFileIdentifier,
285        new_file: Option<FileIdentifier>,
286    ) -> Result<LocalFileIdentifier> {
287        let mut inner = self.write_inner();
288
289        let file = inner
290            .open_files
291            .remove(&file_identifier)
292            .ok_or(Error::InvalidIdentifier)?;
293
294        let file_identifier = if let Some(new_file) = new_file {
295            let file = LocalFileIdentifier::new(new_task, new_file);
296
297            if inner.open_files.contains_key(&file) {
298                return Err(Error::InvalidIdentifier);
299            }
300
301            file
302        } else if Self::is_file(file_identifier) {
303            get_new_file_identifier(
304                new_task,
305                Some(FileIdentifier::MINIMUM),
306                Some(Self::DIRECTORY_MINIMUM),
307                &inner.open_files,
308            )?
309        } else {
310            get_new_file_identifier(
311                new_task,
312                Some(Self::DIRECTORY_MINIMUM),
313                Some(FileIdentifier::MAXIMUM),
314                &inner.open_directories,
315            )?
316        };
317
318        if inner.open_files.insert(file_identifier, file).is_some() {
319            return Err(Error::InternalError); // Should never happen
320        }
321
322        Ok(file_identifier)
323    }
324
325    fn remove(&self, path: &Path) -> Result<()> {
326        let path = CString::new(path.as_str()).map_err(|_| Error::InvalidParameter)?;
327
328        let mut inner = self.write_inner();
329
330        convert_result(unsafe {
331            littlefs::lfs_remove(&mut inner.file_system as *mut _, path.as_ptr())
332        })?;
333
334        Ok(())
335    }
336
337    fn read(&self, file: LocalFileIdentifier, buffer: &mut [u8], _: Time) -> Result<Size> {
338        let mut inner = self.write_inner();
339
340        let (file_system, open_files, _) = Self::borrow_mutable_inner_2_splitted(&mut inner);
341
342        let file = open_files.get_mut(&file).ok_or(Error::InvalidIdentifier)?;
343
344        file.read(file_system, buffer)
345    }
346
347    fn write(&self, file: LocalFileIdentifier, buffer: &[u8], _: Time) -> Result<Size> {
348        let mut inner = self.write_inner();
349
350        let (file_system, open_files, _) = Self::borrow_mutable_inner_2_splitted(&mut inner);
351
352        let file = open_files.get_mut(&file).ok_or(Error::InvalidIdentifier)?;
353
354        file.write(file_system, buffer)
355    }
356
357    fn rename(&self, source: &Path, destination: &Path) -> Result<()> {
358        let source = CString::new(source.as_str()).map_err(|_| Error::InvalidParameter)?;
359
360        let destination =
361            CString::new(destination.as_str()).map_err(|_| Error::InvalidParameter)?;
362
363        let mut inner = self.write_inner();
364
365        convert_result(unsafe {
366            littlefs::lfs_rename(
367                &mut inner.file_system as *mut _,
368                source.as_ptr(),
369                destination.as_ptr(),
370            )
371        })?;
372
373        Ok(())
374    }
375
376    fn set_position(&self, file: LocalFileIdentifier, position: &Position) -> Result<Size> {
377        let mut inner = self.write_inner();
378
379        let (file_system, open_files, _) = Self::borrow_mutable_inner_2_splitted(&mut inner);
380
381        let file = open_files.get_mut(&file).ok_or(Error::InvalidIdentifier)?;
382
383        file.set_position(file_system, position)
384    }
385
386    fn flush(&self, file: LocalFileIdentifier) -> Result<()> {
387        let mut inner = self.write_inner();
388
389        let (file_system, open_files, _) = Self::borrow_mutable_inner_2_splitted(&mut inner);
390
391        let file = open_files.get_mut(&file).ok_or(Error::InvalidIdentifier)?;
392
393        file.flush(file_system)
394    }
395
396    fn get_statistics(&self, file: LocalFileIdentifier) -> Result<Statistics_type> {
397        let mut inner = self.write_inner();
398
399        let (file_system, open_files, open_directories) =
400            Self::borrow_mutable_inner_2_splitted(&mut inner);
401
402        // open_directoriesy to get the metadata of the directories
403        if open_directories.get_mut(&file).is_some() {
404            let current_time: Time = time::get_instance().get_current_time().unwrap().into();
405
406            Ok(Statistics_type::new(
407                FileSystemIdentifier::new(0),
408                Inode::new(0),
409                1,
410                Size::new(0),
411                current_time,
412                current_time,
413                current_time,
414                Kind::Directory,
415                Permissions::new_default(Kind::Directory),
416                UserIdentifier::new(0),
417                GroupIdentifier::new(0),
418            ))
419        } else if let Some(file) = open_files.get_mut(&file) {
420            Ok(file.get_statistics(file_system)?)
421        } else {
422            Err(Error::InvalidIdentifier)
423        }
424    }
425
426    fn get_mode(&self, file: LocalFileIdentifier) -> Result<Mode> {
427        let inner = self.read_inner();
428
429        let result = if Self::is_file(file) {
430            inner
431                .open_files
432                .get(&file)
433                .ok_or(Error::InvalidIdentifier)?
434                .get_mode()
435        } else {
436            inner
437                .open_directories
438                .get(&file)
439                .ok_or(Error::InvalidIdentifier)?;
440
441            Mode::READ_ONLY
442        };
443
444        Ok(result)
445    }
446
447    fn open_directory(
448        &self,
449        path: &Path,
450        task: task::TaskIdentifier,
451    ) -> Result<LocalFileIdentifier> {
452        let mut inner = self.write_inner();
453
454        let directory = Directory::open(&mut inner.file_system, path)?;
455
456        let file_identifier = get_new_file_identifier(
457            task,
458            Some(Self::DIRECTORY_MINIMUM),
459            Some(FileIdentifier::MAXIMUM),
460            &inner.open_directories,
461        )?;
462
463        if inner
464            .open_directories
465            .insert(file_identifier, directory)
466            .is_some()
467        {
468            return Err(Error::InternalError);
469        }
470
471        Ok(file_identifier)
472    }
473
474    fn read_directory(&self, file: LocalFileIdentifier) -> Result<Option<Entry>> {
475        let mut inner = self.write_inner();
476
477        let (file_system, _, open_directories) = Self::borrow_mutable_inner_2_splitted(&mut inner);
478
479        let directory = open_directories
480            .get_mut(&file)
481            .ok_or(Error::InvalidIdentifier)?;
482
483        directory.read(file_system)
484    }
485
486    fn set_position_directory(&self, file: LocalFileIdentifier, position: Size) -> Result<()> {
487        let mut inner = self.write_inner();
488
489        let (file_system, _, open_directories) = Self::borrow_mutable_inner_2_splitted(&mut inner);
490
491        let directory = open_directories
492            .get_mut(&file)
493            .ok_or(Error::InvalidIdentifier)?;
494
495        directory.set_position(file_system, position)
496    }
497
498    fn rewind_directory(&self, file: LocalFileIdentifier) -> Result<()> {
499        let mut inner = self.write_inner();
500
501        let (file_system, _, open_directories) = Self::borrow_mutable_inner_2_splitted(&mut inner);
502
503        let directory = open_directories
504            .get_mut(&file)
505            .ok_or(Error::InvalidIdentifier)?;
506
507        directory.rewind(file_system)?;
508
509        Ok(())
510    }
511
512    fn close_directory(&self, file: LocalFileIdentifier) -> Result<()> {
513        let mut inner = self.write_inner();
514
515        let (file_system, _, open_directories) = Self::borrow_mutable_inner_2_splitted(&mut inner);
516
517        let mut directory = open_directories
518            .remove(&file)
519            .ok_or(Error::InvalidIdentifier)?;
520
521        directory.close(file_system)?;
522
523        Ok(())
524    }
525
526    fn create_directory(
527        &self,
528        path: &Path,
529
530        time: Time,
531        user: UserIdentifier,
532        group: GroupIdentifier,
533    ) -> Result<()> {
534        let mut inner = self.write_inner();
535
536        Directory::create_directory(&mut inner.file_system, path)?;
537
538        let metadata = Metadata::get_default(Kind::Directory, time, user, group)
539            .ok_or(Error::InvalidParameter)?;
540
541        File::set_metadata_from_path(&mut inner.file_system, path, &metadata)?;
542
543        Ok(())
544    }
545
546    fn get_position_directory(&self, file: LocalFileIdentifier) -> Result<Size> {
547        let mut inner = self.write_inner();
548
549        let (file_system, _, open_directories) = Self::borrow_mutable_inner_2_splitted(&mut inner);
550
551        let directory = open_directories
552            .get_mut(&file)
553            .ok_or(Error::InvalidIdentifier)?;
554
555        directory.get_position(file_system)
556    }
557
558    fn set_metadata_from_path(&self, path: &Path, metadata: &Metadata) -> Result<()> {
559        let mut inner = self.write_inner();
560
561        File::set_metadata_from_path(&mut inner.file_system, path, metadata)?;
562
563        Ok(())
564    }
565
566    fn get_metadata_from_path(&self, path: &Path) -> Result<Metadata> {
567        let mut inner = self.write_inner();
568
569        File::get_metadata_from_path(&mut inner.file_system, path)
570    }
571
572    fn get_metadata(&self, file: LocalFileIdentifier) -> Result<Metadata> {
573        let mut inner = self.write_inner();
574
575        let (_, open_files, _) = Self::borrow_mutable_inner_2_splitted(&mut inner);
576
577        let file = open_files.get_mut(&file).ok_or(Error::InvalidIdentifier)?;
578
579        Ok(file.get_metadata()?.clone())
580    }
581}
582
583#[cfg(test)]
584mod tests {
585    extern crate std;
586
587    use alloc::sync::Arc;
588    use file_system::{MemoryDevice, create_device};
589    use task::test;
590
591    use super::*;
592
593    const CACHE_SIZE: usize = 256;
594
595    fn initialize() -> FileSystem {
596        let _ = users::initialize();
597
598        task::initialize();
599
600        let _ = time::initialize(create_device!(drivers::native::TimeDriver::new()));
601
602        let mock_device = MemoryDevice::<512>::new(2048 * 512);
603
604        let device = Device::new(Arc::new(mock_device));
605
606        FileSystem::format(device.clone(), CACHE_SIZE).unwrap();
607
608        FileSystem::new(device, CACHE_SIZE).unwrap()
609    }
610
611    #[test]
612    async fn test_open_close_delete() {
613        file_system::tests::test_open_close_delete(initialize()).await;
614    }
615
616    #[test]
617    async fn test_read_write() {
618        file_system::tests::test_read_write(initialize()).await;
619    }
620
621    #[test]
622    async fn test_move() {
623        file_system::tests::test_move(initialize()).await;
624    }
625
626    #[test]
627    async fn test_set_position() {
628        file_system::tests::test_set_position(initialize()).await;
629    }
630
631    #[test]
632    async fn test_flush() {
633        file_system::tests::test_flush(initialize()).await;
634    }
635
636    #[test]
637    async fn test_set_get_metadata() {
638        file_system::tests::test_set_get_metadata(initialize()).await;
639    }
640
641    #[test]
642    async fn test_read_directory() {
643        file_system::tests::test_read_directory(initialize()).await;
644    }
645
646    #[test]
647    async fn test_set_position_directory() {
648        file_system::tests::test_set_position_directory(initialize()).await;
649    }
650
651    #[test]
652    async fn test_rewind_directory() {
653        file_system::tests::test_rewind_directory(initialize()).await;
654    }
655
656    #[test]
657    async fn test_create_remove_directory() {
658        file_system::tests::test_create_remove_directory(initialize()).await;
659    }
660
661    #[cfg(feature = "std")]
662    #[test]
663    async fn test_loader() {
664        file_system::tests::test_loader(initialize());
665    }
666}