littlefs2
littlefs2 copied to clipboard
Shouldn't the `Storage` trait require the implementer to be a singleton?
test/ui/sync-fail.rs
shows how trying to close a file on a different filesystem fails at compile time. That compile time protection holds in that example because each filesystem instance has a different type but one can, in safe code, create multiple FS instances of the same type and then close a file on a different filesystem instance (that's still the same type). Variation of test/ui/sync-fail.rs
that shows this:
use littlefs2::{
consts, driver,
fs::{File, Filesystem},
io::{Result, Write},
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,
lookaheadwords_size_ty=consts::U1,
filename_max_plus_one_ty=consts::U256,
path_max_plus_one_ty=consts::U256,
result=Result,
);
fn main() {
let mut ram1 = Ram::default();
let mut storage1 = RamStorage::new(&mut ram1);
let mut alloc1 = Filesystem::allocate();
Filesystem::format(&mut storage1).unwrap();
let mut fs1 = Filesystem::mount(&mut alloc1, &mut storage1).unwrap();
let mut ram2 = Ram::default();
let mut storage2 = RamStorage::new(&mut ram2);
let mut alloc2 = Filesystem::allocate();
Filesystem::format(&mut storage2).unwrap();
let mut fs2 = Filesystem::mount(&mut alloc2, &mut storage2).unwrap();
let mut alloc = File::allocate();
// open file in FS1
let mut file = File::create("a.txt", &mut alloc, &mut fs1, &mut storage1).unwrap();
file.write(&mut fs1, &mut storage1, b"Hello!").unwrap();
// sync the file in FS2
file.close(&mut fs2, &mut storage2).unwrap();
}
Here ram1
and ram2
point to different to different memory blocks. The above example doesn't appear to trigger UB but it's probably corrupting the second filesystem. A more complex variation of the above example may cause UB though.
A potential solution to this is to make trait Storage
unsafe and require that's implemented on singleton types. This way all instances of Ram
are handles to the same memory (but then you have be careful about Rust aliasing rules and will likely have to make Ram
!Send
and !Sync
...)