diff --git a/Cargo.toml b/Cargo.toml index 1cc635dfb..7a17bd3e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ default-features = false version = "0.2" [dependencies.littlefs2-sys] -version = "0.1.6" +version = "0.2" [dependencies.serde] version = "1" @@ -57,3 +57,6 @@ log-error = [] # member `char name[LFS_NAME_MAX+1]`. # This means that if we change `traits::Storage::FILENAME_MAX_PLUS_ONE`, # we need to pass this on! + +[patch.crates-io] +littlefs2-sys = { git = "https://github.com/sosthene-nitrokey/littlefs2-sys.git", branch = "update" } diff --git a/src/driver.rs b/src/driver.rs index fb42103c0..ffa50687e 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -44,6 +44,12 @@ pub trait Storage { /// Value zero is invalid, must be positive or -1. const BLOCK_CYCLES: isize = -1; + // Optional upper limit on total space given to metadata pairs in bytes. On + // devices with large blocks (e.g. 128kB) setting this to a low size (2-8kB) + // can help bound the metadata compaction time. Must be <= block_size. + // Defaults to block_size when zero. + const METADATA_MAX: u32 = 0; + /// littlefs uses a read cache, a write cache, and one cache per per file. /// Must be a multiple of `READ_SIZE` and `WRITE_SIZE`. /// Must be a factor of `BLOCK_SIZE`. diff --git a/src/fs.rs b/src/fs.rs index 4e1ef87b1..3a4e162d1 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -61,6 +61,7 @@ impl Allocation { let lookahead_size: u32 = 8 * ::LOOKAHEAD_SIZE::U32; let block_cycles: i32 = Storage::BLOCK_CYCLES as _; let block_count: u32 = Storage::BLOCK_COUNT as _; + let metadata_max = Storage::METADATA_MAX; debug_assert!(block_cycles >= -1); debug_assert!(block_cycles != 0); @@ -132,6 +133,7 @@ impl Allocation { name_max: filename_max_plus_one.wrapping_sub(1), file_max, attr_max, + metadata_max, }; Self { diff --git a/src/tests.rs b/src/tests.rs index 14a450a6e..5159d6ceb 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -41,8 +41,8 @@ ram_storage!( #[test] fn version() { - assert_eq!(crate::version().format, (2, 0)); - assert_eq!(crate::version().backend, (2, 2)); + assert_eq!(crate::version().format, (2, 1)); + assert_eq!(crate::version().backend, (2, 6)); } #[test] diff --git a/tests-old-fs/empty.bin b/tests-old-fs/empty.bin new file mode 100644 index 000000000..1eb401e04 Binary files /dev/null and b/tests-old-fs/empty.bin differ diff --git a/tests-old-fs/recurse.bin b/tests-old-fs/recurse.bin new file mode 100644 index 000000000..b1bfe814e Binary files /dev/null and b/tests-old-fs/recurse.bin differ diff --git a/tests-old-fs/root.bin b/tests-old-fs/root.bin new file mode 100644 index 000000000..6b7fad791 Binary files /dev/null and b/tests-old-fs/root.bin differ diff --git a/tests/create_old_fs.rs b/tests/create_old_fs.rs new file mode 100644 index 000000000..cd92be1b0 --- /dev/null +++ b/tests/create_old_fs.rs @@ -0,0 +1,188 @@ +use littlefs2::{ + consts, driver, + fs::Filesystem, + io::Result, + path, + path::{Path, PathBuf}, + ram_storage, +}; + +ram_storage!( + name=RamStorage, + backend=Ram, + trait=driver::Storage, + erase_value=0xff, + read_size=20*5, + write_size=20*7, + cache_size_ty=consts::U700, + block_size=20*35, + block_count=32, + lookahead_size_ty=consts::U16, + filename_max_plus_one_ty=consts::U256, + path_max_plus_one_ty=consts::U256, + result=Result, +); + +struct FileTest { + name: &'static Path, + content: &'static [u8], +} + +struct DirTest { + files: &'static [FileTest], + sub_dirs: &'static [(&'static Path, DirTest)], +} + +struct FsTest { + root: DirTest, + name: &'static str, +} + +const EMPTY_DIR: FsTest = FsTest { + name: "empty.bin", + root: DirTest { + files: &[], + sub_dirs: &[], + }, +}; + +const ROOT_FULL: FsTest = FsTest { + name: "root.bin", + root: DirTest { + files: &[ + FileTest { + name: path!("test_file.txt"), + content: b"Test content - test_file.txt", + }, + FileTest { + name: path!("test_file2.txt"), + content: b"Test content - test_file2.txt", + }, + FileTest { + name: path!("test_file3.txt"), + content: b"Test content - test_file3.txt", + }, + ], + sub_dirs: &[], + }, +}; + +const RECURSE: FsTest = FsTest { + name: "recurse.bin", + root: DirTest { + files: ROOT_FULL.root.files, + sub_dirs: &[ + ( + path!("root1"), + DirTest { + files: &[ + FileTest { + name: path!("test_sub_file.txt"), + content: b"Test content - test_sub_file.txt", + }, + FileTest { + name: path!("test_sub_file2.txt"), + content: b"Test content - test_sub_file2.txt", + }, + ], + sub_dirs: &[( + path!("sub-dir"), + DirTest { + files: &[ + FileTest { + name: path!("test_sub_sub_file.txt"), + content: b"Test content - test_sub_sub_file.txt", + }, + FileTest { + name: path!("test_sub_sub_file2.txt"), + content: b"Test content - test_sub_sub_file2.txt", + }, + ], + sub_dirs: &[], + }, + )], + }, + ), + ( + path!("root2"), + DirTest { + files: &[], + sub_dirs: &[], + }, + ), + ], + }, +}; + +const ALL: &[FsTest] = &[EMPTY_DIR, ROOT_FULL, RECURSE]; + +fn write_dir(fs: &Filesystem, dir: &DirTest, current_dir: PathBuf) { + println!("Writing current_dir: {current_dir}"); + for f in dir.files { + let mut buf = current_dir.clone(); + buf.push(f.name); + println!( + "Writing {}, ({})", + f.name, + std::str::from_utf8(f.content).unwrap() + ); + fs.write(&buf, f.content).unwrap(); + } + + for (name, d) in dir.sub_dirs { + let mut buf = current_dir.clone(); + buf.push(name); + fs.create_dir(&buf).unwrap(); + write_dir(fs, d, buf); + } +} + +fn read_dir(fs: &Filesystem, dir: &DirTest, current_dir: PathBuf) { + println!("Reading current_dir: {current_dir}"); + for f in dir.files { + let mut buf = current_dir.clone(); + buf.push(f.name); + dbg!(&buf); + let read = fs.read::<1024>(&buf).unwrap(); + assert_eq!(std::str::from_utf8(&read), std::str::from_utf8(f.content)); + } + + for (name, d) in dir.sub_dirs { + let mut buf = current_dir.clone(); + buf.push(name); + read_dir(fs, d, buf); + } +} + +#[test] +#[ignore] +fn create() { + for fs_test in ALL { + println!("Got to test: {}", fs_test.name); + let mut backend = Ram::default(); + let mut storage = RamStorage::new(&mut backend); + Filesystem::format(&mut storage).unwrap(); + Filesystem::mount_and_then(&mut storage, |fs| { + write_dir(fs, &fs_test.root, PathBuf::new()); + Ok(()) + }) + .unwrap(); + std::fs::write(format!("tests-old-fs/{}", fs_test.name), &backend.buf).unwrap(); + } +} + +#[test] +fn read() { + for fs_test in ALL { + println!("Got to test: {}", fs_test.name); + let mut backend = Ram::default(); + let buf = std::fs::read(format!("tests-old-fs/{}", fs_test.name)).unwrap(); + backend.buf.copy_from_slice(&buf); + let mut storage = RamStorage::new(&mut backend); + Filesystem::mount_and_then(&mut storage, |fs| { + read_dir(fs, &fs_test.root, PathBuf::new()); + Ok(()) + }) + .unwrap(); + } +}