mirror of
https://github.com/littlefs-project/littlefs.git
synced 2025-12-01 12:20:02 +00:00
And tweaked a few related comments. I'm still on the fence with this name, I don't think it's great, but it at least betters describes the "repopulation" operation than "rebuilding". The important distinction is that we don't throw away information. Bad/erased block info (future) is still carried over into the new gbmap snapshot, and persists unless you explicitly call rmgbmap + mkgbmap. So, adopting gbmap_repop_thresh for now to see if it's just a habit thing, but may adopt a different name in the future. As a plus, gbmap_repop_thresh is two characters shorter.
2139 lines
72 KiB
TOML
2139 lines
72 KiB
TOML
# Advanced mount tests
|
|
after = ['test_mtree', 'test_trvs']
|
|
|
|
|
|
# test we can mount
|
|
[cases.test_mount_simple]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that various mount flags don't assert and are returned by
|
|
# lfs3_fs_stat
|
|
[cases.test_mount_flags]
|
|
defines.RDONLY = [false, true]
|
|
defines.FLUSH = [false, true]
|
|
defines.SYNC = [false, true]
|
|
defines.REVDBG = [false, true]
|
|
defines.REVNOISE = [false, true]
|
|
defines.CKPROGS = [false, true]
|
|
defines.CKFETCHES = [false, true]
|
|
defines.CKMETAPARITY = [false, true]
|
|
defines.CKDATACKSUMS = [false, true]
|
|
defines.MKCONSISTENT = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.REPOPGBMAP = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = [
|
|
'LFS3_IFDEF_REVDBG(true, !REVDBG)',
|
|
'LFS3_IFDEF_REVNOISE(true, !REVNOISE)',
|
|
'!REVDBG || !REVNOISE',
|
|
'LFS3_IFDEF_CKPROGS(true, !CKPROGS)',
|
|
'LFS3_IFDEF_CKFETCHES(true, !CKFETCHES)',
|
|
'LFS3_IFDEF_CKMETAPARITY(true, !CKMETAPARITY)',
|
|
'LFS3_IFDEF_CKDATACKSUMS(true, !CKDATACKSUMS)',
|
|
'!RDONLY || !MKCONSISTENT',
|
|
'!RDONLY || !LOOKAHEAD',
|
|
'LFS3_IFDEF_YES_GBMAP(true, !REPOPGBMAP)',
|
|
'!RDONLY || !REPOPGBMAP',
|
|
'!RDONLY || !COMPACT',
|
|
]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
lfs3_mount(&lfs3,
|
|
((RDONLY) ? LFS3_M_RDONLY : LFS3_M_RDWR)
|
|
| ((FLUSH) ? LFS3_M_FLUSH : 0)
|
|
| ((SYNC) ? LFS3_M_SYNC : 0)
|
|
| ((REVDBG) ? LFS3_IFDEF_REVDBG(LFS3_M_REVDBG, -1) : 0)
|
|
| ((REVNOISE) ? LFS3_IFDEF_REVNOISE(LFS3_M_REVNOISE, -1) : 0)
|
|
| ((CKPROGS) ? LFS3_IFDEF_CKPROGS(LFS3_M_CKPROGS, -1) : 0)
|
|
| ((CKFETCHES) ? LFS3_IFDEF_CKFETCHES(LFS3_M_CKFETCHES, -1) : 0)
|
|
| ((CKMETAPARITY)
|
|
? LFS3_IFDEF_CKMETAPARITY(LFS3_M_CKMETAPARITY, -1)
|
|
: 0)
|
|
| ((CKDATACKSUMS)
|
|
? LFS3_IFDEF_CKDATACKSUMS(LFS3_M_CKDATACKSUMS, -1)
|
|
: 0)
|
|
| ((MKCONSISTENT) ? LFS3_M_MKCONSISTENT : 0)
|
|
| ((LOOKAHEAD) ? LFS3_M_LOOKAHEAD : 0)
|
|
| ((REPOPGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_M_REPOPGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACT) ? LFS3_M_COMPACT : 0)
|
|
| ((CKMETA) ? LFS3_M_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_M_CKDATA : 0),
|
|
CFG) => 0;
|
|
|
|
// lfs3_fs_stat only returns some flags
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((RDONLY) ? LFS3_I_RDONLY : 0)
|
|
| ((FLUSH) ? LFS3_I_FLUSH : 0)
|
|
| ((SYNC) ? LFS3_I_SYNC : 0)
|
|
| ((REVDBG) ? LFS3_IFDEF_REVDBG(LFS3_M_REVDBG, -1) : 0)
|
|
| ((REVNOISE) ? LFS3_IFDEF_REVNOISE(LFS3_M_REVNOISE, -1) : 0)
|
|
| ((CKPROGS) ? LFS3_IFDEF_CKPROGS(LFS3_I_CKPROGS, -1) : 0)
|
|
| ((CKFETCHES) ? LFS3_IFDEF_CKFETCHES(LFS3_I_CKFETCHES, -1) : 0)
|
|
| ((CKMETAPARITY)
|
|
? LFS3_IFDEF_CKMETAPARITY(LFS3_I_CKMETAPARITY, -1)
|
|
: 0)
|
|
| ((CKDATACKSUMS)
|
|
? LFS3_IFDEF_CKDATACKSUMS(LFS3_I_CKDATACKSUMS, -1)
|
|
: 0)
|
|
| ((!MKCONSISTENT) ? LFS3_I_MKCONSISTENT : 0)
|
|
| ((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((!COMPACT) ? LFS3_I_COMPACT : 0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| LFS3_IFDEF_YES_GBMAP(LFS3_I_GBMAP, 0)));
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that various format flags don't assert or anything
|
|
#
|
|
# these end up passed to mount internally
|
|
[cases.test_mount_format_flags]
|
|
defines.REVDBG = [false, true]
|
|
defines.REVNOISE = [false, true]
|
|
defines.CKPROGS = [false, true]
|
|
defines.CKFETCHES = [false, true]
|
|
defines.CKMETAPARITY = [false, true]
|
|
defines.CKDATACKSUMS = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.GBMAP = [false, true]
|
|
if = [
|
|
'LFS3_IFDEF_REVDBG(true, !REVDBG)',
|
|
'LFS3_IFDEF_REVNOISE(true, !REVNOISE)',
|
|
'!REVDBG || !REVNOISE',
|
|
'LFS3_IFDEF_CKPROGS(true, !CKPROGS)',
|
|
'LFS3_IFDEF_CKFETCHES(true, !CKFETCHES)',
|
|
'LFS3_IFDEF_CKMETAPARITY(true, !CKMETAPARITY)',
|
|
'LFS3_IFDEF_CKDATACKSUMS(true, !CKDATACKSUMS)',
|
|
'LFS3_IFDEF_GBMAP(true, !GBMAP)',
|
|
]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((REVDBG) ? LFS3_IFDEF_REVDBG(LFS3_F_REVDBG, -1) : 0)
|
|
| ((REVNOISE) ? LFS3_IFDEF_REVNOISE(LFS3_F_REVNOISE, -1) : 0)
|
|
| ((CKPROGS) ? LFS3_IFDEF_CKPROGS(LFS3_F_CKPROGS, -1) : 0)
|
|
| ((CKFETCHES) ? LFS3_IFDEF_CKFETCHES(LFS3_F_CKFETCHES, -1) : 0)
|
|
| ((CKMETAPARITY)
|
|
? LFS3_IFDEF_CKMETAPARITY(LFS3_F_CKMETAPARITY, -1)
|
|
: 0)
|
|
| ((CKDATACKSUMS)
|
|
? LFS3_IFDEF_CKDATACKSUMS(LFS3_F_CKDATACKSUMS, -1)
|
|
: 0)
|
|
| ((CKMETA) ? LFS3_F_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_F_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// test that format-only flags are read correctly
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_MKCONSISTENT
|
|
| LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| LFS3_IFDEF_YES_GBMAP(
|
|
LFS3_I_GBMAP,
|
|
(GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
|
|
# test that on-mount traversals do what they say they do
|
|
|
|
[cases.test_mount_t_lookahead]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// by default we need a lookahead scan
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_MKCONSISTENT
|
|
| LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| LFS3_IFDEF_YES_GBMAP(LFS3_I_GBMAP, 0)));
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// with LFS3_M_LOOKAHEAD, mount performs a lookahead scan
|
|
lfs3_mount(&lfs3,
|
|
LFS3_M_RDWR
|
|
| LFS3_M_LOOKAHEAD
|
|
| ((CKMETA) ? LFS3_M_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_M_CKDATA : 0),
|
|
CFG) => 0;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_MKCONSISTENT
|
|
| LFS3_I_COMPACT
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| LFS3_IFDEF_YES_GBMAP(LFS3_I_GBMAP, 0)));
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_t_repopgbmap]
|
|
ifdef = 'LFS3_GBMAP'
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = [
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR | LFS3_F_GBMAP, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// gbmap is persistant, so by default we _don't_ need a gbmap scan
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_MKCONSISTENT
|
|
| LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, 0)));
|
|
// write to a file
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "jellyfish",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// but if we allocated any blocks our gbmap will be inexhaustive
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_MKCONSISTENT
|
|
| LFS3_I_LOOKAHEAD
|
|
| LFS3_I_REPOPGBMAP
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, 0)));
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// with LFS3_M_REPOPGBMAP, mount performs a gbmap repop
|
|
lfs3_mount(&lfs3,
|
|
LFS3_M_RDWR
|
|
| LFS3_M_LOOKAHEAD
|
|
| LFS3_M_REPOPGBMAP
|
|
| ((CKMETA) ? LFS3_M_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_M_CKDATA : 0),
|
|
CFG) => 0;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_MKCONSISTENT
|
|
| LFS3_I_COMPACT
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, 0)));
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_t_compact]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// first lets create a compactable filesystem
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// write to our mdir until >gc_compact_thresh full
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "jellyfish",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
|
|
// hack, don't use the internals like this
|
|
uint8_t wbuf[SIZE];
|
|
while ((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfs3_file_rewind(&lfs3, &file) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file) => 0;
|
|
}
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// by default mount does not compact
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_MKCONSISTENT
|
|
| LFS3_I_LOOKAHEAD
|
|
| LFS3_IFDEF_YES_GBMAP(
|
|
(SIZE >= BLOCK_SIZE/4) ? LFS3_I_REPOPGBMAP : 0,
|
|
0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| LFS3_IFDEF_YES_GBMAP(LFS3_I_GBMAP, 0)));
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// with LFS3_M_COMPACT, mount compact any uncompacted blocks
|
|
lfs3_mount(&lfs3,
|
|
LFS3_M_RDWR
|
|
| LFS3_M_COMPACT
|
|
| ((LOOKAHEAD) ? LFS3_M_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_M_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_M_CKDATA : 0),
|
|
CFG) => 0;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_MKCONSISTENT
|
|
| ((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0)
|
|
| LFS3_IFDEF_YES_GBMAP(
|
|
(SIZE >= BLOCK_SIZE/4) ? LFS3_I_REPOPGBMAP : 0,
|
|
0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| LFS3_IFDEF_YES_GBMAP(LFS3_I_GBMAP, 0)));
|
|
|
|
// mdir should have been compacted
|
|
lfs3_file_open(&lfs3, &file, "jellyfish", LFS3_O_RDONLY) => 0;
|
|
assert((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// check we can still read the file
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_t_mkconsistent]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FILE_CACHE_SIZE/2'
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.ORPHANS = [0, 1, 2, 3, 100]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// first lets create some orphans
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "cuttlefish",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "octopus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfs3_file_t orphans[ORPHANS];
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
char name[256];
|
|
sprintf(name, "jellyfish%03x", i);
|
|
lfs3_file_open(&lfs3, &orphans[i], name,
|
|
LFS3_O_WRONLY
|
|
| LFS3_O_CREAT
|
|
| LFS3_O_EXCL
|
|
| LFS3_O_DESYNC) => 0;
|
|
}
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
lfs3_file_close(&lfs3, &orphans[i]) => 0;
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// by default we clean up orphans lazily
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_MKCONSISTENT
|
|
| LFS3_I_LOOKAHEAD
|
|
| LFS3_IFDEF_YES_GBMAP(
|
|
(ORPHANS >= 100) ? LFS3_I_REPOPGBMAP : 0,
|
|
0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| LFS3_IFDEF_YES_GBMAP(LFS3_I_GBMAP, 0)));
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// with LFS3_M_MKCONSISTENT, mount cleans up orphans eagerly
|
|
lfs3_mount(&lfs3,
|
|
LFS3_M_RDWR
|
|
| LFS3_M_MKCONSISTENT
|
|
| ((LOOKAHEAD) ? LFS3_M_LOOKAHEAD : 0)
|
|
| ((COMPACT) ? LFS3_M_COMPACT : 0)
|
|
| ((CKMETA) ? LFS3_M_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_M_CKDATA : 0),
|
|
CFG) => 0;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0)
|
|
| LFS3_IFDEF_YES_GBMAP(
|
|
(ORPHANS >= 100) ? LFS3_I_REPOPGBMAP : 0,
|
|
0)
|
|
| ((!COMPACT) ? LFS3_I_COMPACT : 0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| LFS3_IFDEF_YES_GBMAP(LFS3_I_GBMAP, 0)));
|
|
|
|
// check we can still read the files
|
|
lfs3_file_open(&lfs3, &file, "cuttlefish", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "octopus", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test we can detect at least fully clobbered blocks
|
|
#
|
|
# these are tested more thoroughly in test_ck
|
|
[cases.test_mount_t_ckmeta]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs3_block_t i = 0;
|
|
while (true) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create an interesting filesystem
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "squid%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
// this gets a bit tricky be cause we need to clobber both
|
|
// blocks in mdir pairs
|
|
if (tinfo.btype == LFS3_BTYPE_MDIR
|
|
|| tinfo.btype == LFS3_BTYPE_BTREE) {
|
|
if (k == i || k == i+1) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
if (tinfo.btype != LFS3_BTYPE_MDIR || k == i+1) {
|
|
i += (tinfo.btype == LFS3_BTYPE_MDIR) ? 2 : 1;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto clobbered;
|
|
}
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// mount with LFS3_M_CKMETA, we should detect clobbered blocks
|
|
lfs3_mount(&lfs3,
|
|
LFS3_M_RDWR
|
|
| LFS3_M_CKMETA,
|
|
CFG) => LFS3_ERR_CORRUPT;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_mount_t_ckdata]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs3_block_t i = 0;
|
|
while (true) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create an interesting filesystem
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "squid%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
// this gets a bit tricky be cause we need to clobber both
|
|
// blocks in mdir pairs
|
|
if (tinfo.btype == LFS3_BTYPE_MDIR
|
|
|| tinfo.btype == LFS3_BTYPE_BTREE
|
|
|| tinfo.btype == LFS3_BTYPE_DATA) {
|
|
if (k == i || k == i+1) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
if (tinfo.btype != LFS3_BTYPE_MDIR || k == i+1) {
|
|
i += (tinfo.btype == LFS3_BTYPE_MDIR) ? 2 : 1;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto clobbered;
|
|
}
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// mount with LFS3_M_CKDATA, we should detect clobbered blocks
|
|
//
|
|
// note LFS3_M_CKDATA implies LFS3_M_CKMETA
|
|
lfs3_mount(&lfs3,
|
|
LFS3_M_RDWR
|
|
| LFS3_M_CKDATA,
|
|
CFG) => LFS3_ERR_CORRUPT;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
|
|
|
|
## incompatiblity tests ##
|
|
|
|
# test that we fail if we find no magic
|
|
[cases.test_mount_incompat_no_magic]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// delete the magic string
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR(
|
|
LFS3_TAG_RM | LFS3_TAG_MAGIC, 0))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should now fail
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_CORRUPT;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_CORRUPT;
|
|
'''
|
|
|
|
# test that we fail if we find bad magic
|
|
[cases.test_mount_incompat_bad_magic]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// tweak the magic string
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_BUF(
|
|
LFS3_TAG_MAGIC, 0,
|
|
"lottlefs", 8))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should now fail
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_CORRUPT;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_CORRUPT;
|
|
'''
|
|
|
|
# test that we fail to mount after a major version bump
|
|
[cases.test_mount_incompat_major]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// bump the major version
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_BUF(
|
|
LFS3_TAG_VERSION, 0,
|
|
((const uint8_t[2]){
|
|
LFS3_DISK_VERSION_MAJOR+1,
|
|
0}), 2))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should now fail
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP;
|
|
'''
|
|
|
|
# test that we fail to mount after a minor version bump
|
|
[cases.test_mount_incompat_minor]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// bump the minor version
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_BUF(
|
|
LFS3_TAG_VERSION, 0,
|
|
((const uint8_t[2]){
|
|
LFS3_DISK_VERSION_MAJOR,
|
|
LFS3_DISK_VERSION_MINOR+1}), 2))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should now fail
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP;
|
|
'''
|
|
|
|
# test that we fail to mount incompatible rcompat flags
|
|
[cases.test_mount_incompat_rcompat]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// set the nonstandard rcompat flag, this will always be incompatible
|
|
// with standard littlefs
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_LE32(
|
|
LFS3_TAG_RCOMPAT, 0,
|
|
lfs3_rcompat(&lfs3)
|
|
| LFS3_RCOMPAT_NONSTANDARD))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should now fail
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP;
|
|
'''
|
|
|
|
# test that we fail to mount incompatible wcompat flags
|
|
[cases.test_mount_incompat_wcompat]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// set the nonstandard rcompat flag, this will always be incompatible
|
|
// with standard littlefs
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_LE32(
|
|
LFS3_TAG_WCOMPAT, 0,
|
|
lfs3_wcompat(&lfs3)
|
|
| LFS3_WCOMPAT_NONSTANDARD))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should now fail
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP;
|
|
|
|
// but we _can_ mount readonly
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that an incompatible ocompat flag is a noop
|
|
[cases.test_mount_incompat_ocompat]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// set the nonstandard ocompat flag, this will always be incompatible
|
|
// with standard littlefs
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_LE32(
|
|
LFS3_TAG_OCOMPAT, 0,
|
|
lfs3_ocompat(&lfs3)
|
|
| LFS3_OCOMPAT_NONSTANDARD))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should _not_ fail, ocompat should always be ignored
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that we fail to mount rdonly images
|
|
[cases.test_mount_incompat_rdonly]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// set the rdonly flag, this prevents writing from a littlefs image
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_LE32(
|
|
LFS3_TAG_WCOMPAT, 0,
|
|
lfs3_wcompat(&lfs3)
|
|
| LFS3_WCOMPAT_RDONLY))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should now fail
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP;
|
|
|
|
// but we _can_ mount readonly
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that we fail to mount wronly images
|
|
[cases.test_mount_incompat_wronly]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// set the wronly flag, this prevents reading from a littlefs image
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_LE32(
|
|
LFS3_TAG_RCOMPAT, 0,
|
|
lfs3_rcompat(&lfs3)
|
|
| LFS3_RCOMPAT_WRONLY))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should now fail
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP;
|
|
'''
|
|
|
|
# these are just a bit harder to detect
|
|
[cases.test_mount_incompat_rcompat_overflow]
|
|
defines.OVERFLOW = 72
|
|
defines.FLAG = 'range(72)'
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// set a really far rcompat flag
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
uint8_t overflow[OVERFLOW / 8];
|
|
memset(overflow, 0, sizeof(overflow));
|
|
overflow[FLAG / 8] |= 1 << (FLAG % 8);
|
|
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
uint8_t rcompat_buf[LFS3_LE32_DSIZE];
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_CAT(
|
|
LFS3_TAG_RCOMPAT, 0,
|
|
lfs3_data_fromle32(lfs3_rcompat(&lfs3), rcompat_buf),
|
|
LFS3_DATA_BUF(overflow, sizeof(overflow))))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should now fail
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_wcompat_overflow]
|
|
defines.OVERFLOW = 72
|
|
defines.FLAG = 'range(72)'
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
uint8_t flags[9] = {0};
|
|
flags[FLAG / 8] |= 1 << (FLAG % 8);
|
|
|
|
// set a really far wcompat flag
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
uint8_t overflow[OVERFLOW / 8];
|
|
memset(overflow, 0, sizeof(overflow));
|
|
overflow[FLAG / 8] |= 1 << (FLAG % 8);
|
|
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
uint8_t wcompat_buf[LFS3_LE32_DSIZE];
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_CAT(
|
|
LFS3_TAG_WCOMPAT, 0,
|
|
lfs3_data_fromle32(lfs3_wcompat(&lfs3), wcompat_buf),
|
|
LFS3_DATA_BUF(overflow, sizeof(overflow))))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should now fail
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP;
|
|
|
|
// but we _can_ mount readonly
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_ocompat_overflow]
|
|
defines.OVERFLOW = 72
|
|
defines.FLAG = 'range(72)'
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// set a really far ocompat flag
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
uint8_t overflow[OVERFLOW / 8];
|
|
memset(overflow, 0, sizeof(overflow));
|
|
overflow[FLAG / 8] |= 1 << (FLAG % 8);
|
|
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
uint8_t ocompat_buf[LFS3_LE32_DSIZE];
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_CAT(
|
|
LFS3_TAG_OCOMPAT, 0,
|
|
lfs3_data_fromle32(lfs3_ocompat(&lfs3), ocompat_buf),
|
|
LFS3_DATA_BUF(overflow, sizeof(overflow))))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should _not_ fail, ocompat should always be ignored
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# but just appending zeros is _not_ an error
|
|
[cases.test_mount_incompat_rcompat_padding]
|
|
defines.OVERFLOW = 72
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// set a really far rcompat flag
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
uint8_t overflow[OVERFLOW / 8];
|
|
memset(overflow, 0, sizeof(overflow));
|
|
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
uint8_t rcompat_buf[LFS3_LE32_DSIZE];
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_CAT(
|
|
LFS3_TAG_RCOMPAT, 0,
|
|
lfs3_data_fromle32(lfs3_rcompat(&lfs3), rcompat_buf),
|
|
LFS3_DATA_BUF(overflow, sizeof(overflow))))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should _not_ fail, extra zeros should be ignored
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_wcompat_padding]
|
|
defines.OVERFLOW = 72
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
uint8_t flags[9] = {0};
|
|
flags[FLAG / 8] |= 1 << (FLAG % 8);
|
|
|
|
// set a really far wcompat flag
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
uint8_t overflow[OVERFLOW / 8];
|
|
memset(overflow, 0, sizeof(overflow));
|
|
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
uint8_t wcompat_buf[LFS3_LE32_DSIZE];
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_CAT(
|
|
LFS3_TAG_WCOMPAT, 0,
|
|
lfs3_data_fromle32(lfs3_wcompat(&lfs3), wcompat_buf),
|
|
LFS3_DATA_BUF(overflow, sizeof(overflow))))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should _not_ fail, extra zeros should be ignored
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_ocompat_padding]
|
|
defines.OVERFLOW = 72
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// set a really far ocompat flag
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
uint8_t overflow[OVERFLOW / 8];
|
|
memset(overflow, 0, sizeof(overflow));
|
|
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
uint8_t ocompat_buf[LFS3_LE32_DSIZE];
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_CAT(
|
|
LFS3_TAG_OCOMPAT, 0,
|
|
lfs3_data_fromle32(lfs3_ocompat(&lfs3), ocompat_buf),
|
|
LFS3_DATA_BUF(overflow, sizeof(overflow))))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should _not_ fail, extra zeros should be ignored
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that we fail to mount incompatible block sizes
|
|
[cases.test_mount_incompat_block_size]
|
|
defines.INC_BLOCK_SIZE = ['BLOCK_SIZE/2', 'BLOCK_SIZE*2']
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// set an incompatible block size
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_GEOMETRY(
|
|
LFS3_TAG_GEOMETRY, 0,
|
|
(&(lfs3_geometry_t){
|
|
INC_BLOCK_SIZE,
|
|
BLOCK_COUNT})))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should now fail
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP;
|
|
'''
|
|
|
|
# test that we fail to mount after incompatible block counts
|
|
[cases.test_mount_incompat_block_count]
|
|
defines.INC_BLOCK_COUNT = ['BLOCK_COUNT*2']
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// set an incompatible block count
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_GEOMETRY(
|
|
LFS3_TAG_GEOMETRY, 0,
|
|
(&(lfs3_geometry_t){
|
|
BLOCK_SIZE,
|
|
INC_BLOCK_COUNT})))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should now fail
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP;
|
|
'''
|
|
|
|
# test that we fail to mount after incompatible name limit
|
|
[cases.test_mount_incompat_name_limit]
|
|
defines.INC_NAME_LIMIT = ['LFS3_NAME_MAX*2']
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// set an incompatible block size
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_LEB128(
|
|
LFS3_TAG_NAMELIMIT, 0,
|
|
INC_NAME_LIMIT))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should now fail
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP;
|
|
'''
|
|
|
|
# test that we fail to mount after incompatible file limit
|
|
[cases.test_mount_incompat_file_limit]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// set an incompatible file limit
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
uint8_t file_limit_buf[LFS3_LEB128_DSIZE];
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_CAT(
|
|
LFS3_TAG_FILELIMIT, 0,
|
|
// it's a bit difficult to test this since file limit
|
|
// is usually our integer limit, but we can force a
|
|
// larger value by inserting an extra byte into our
|
|
// leb128 encoding
|
|
LFS3_DATA_BUF("\xff", 1),
|
|
lfs3_data_fromleb128(LFS3_FILE_MAX, file_limit_buf)))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should now fail
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP;
|
|
'''
|
|
|
|
# test what happens if we find an unknown config
|
|
[cases.test_mount_incompat_unknown_config]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// create an unknown config
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR_BUF(
|
|
LFS3_TAG_CONFIG + 0x42, 0,
|
|
"oh no!", strlen("oh no!")))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount should now fail
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP;
|
|
'''
|
|
|
|
# test what happens if we find an unknown file type
|
|
[cases.test_mount_incompat_unknown_type]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// create some files
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "a",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"hi a!", strlen("hi a!")) => strlen("hi a!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_file_open(&lfs3, &file, "b",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"oh no!", strlen("oh no!")) => strlen("oh no!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_file_open(&lfs3, &file, "c",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"hi c!", strlen("hi c!")) => strlen("hi c!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// change a file's type to something unknown
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
const char *path = "b";
|
|
lfs3_mdir_t mdir;
|
|
lfs3_did_t did;
|
|
lfs3_mtree_pathlookup(&lfs3, &path,
|
|
&mdir, &did) => LFS3_TAG_REG;
|
|
lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS(
|
|
LFS3_RATTR_CAT(
|
|
LFS3_TAG_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
lfs3_data_fromleb128(did, (uint8_t[LFS3_LEB128_DSIZE]){0}),
|
|
LFS3_DATA_BUF(path, lfs3_path_namelen(path))))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// our file should appear as an unknown type
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// open/mkdir should error
|
|
lfs3_file_open(&lfs3, &file, "b",
|
|
LFS3_O_RDONLY) => LFS3_ERR_NOTSUP;
|
|
lfs3_file_open(&lfs3, &file, "b",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT) => LFS3_ERR_NOTSUP;
|
|
|
|
lfs3_mkdir(&lfs3, "b") => LFS3_ERR_EXIST;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_unknown_type_rm]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// create some files
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "a",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"hi a!", strlen("hi a!")) => strlen("hi a!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_file_open(&lfs3, &file, "b",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"oh no!", strlen("oh no!")) => strlen("oh no!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_file_open(&lfs3, &file, "c",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"hi c!", strlen("hi c!")) => strlen("hi c!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// change a file's type to something unknown
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
const char *path = "b";
|
|
lfs3_mdir_t mdir;
|
|
lfs3_did_t did;
|
|
lfs3_mtree_pathlookup(&lfs3, &path,
|
|
&mdir, &did) => LFS3_TAG_REG;
|
|
lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS(
|
|
LFS3_RATTR_CAT(
|
|
LFS3_TAG_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
lfs3_data_fromleb128(did, (uint8_t[LFS3_LEB128_DSIZE]){0}),
|
|
LFS3_DATA_BUF(path, lfs3_path_namelen(path))))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// our file should appear as an unknown type
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// removing unknown files should still work, if this would leak
|
|
// resources the new type should set a wcompat flag
|
|
lfs3_remove(&lfs3, "b") => 0;
|
|
|
|
// check that things look reasonable
|
|
lfs3_stat(&lfs3, "b", &info) => LFS3_ERR_NOENT;
|
|
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_unknown_type_mv_src]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// create some files
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "a",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"hi a!", strlen("hi a!")) => strlen("hi a!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_file_open(&lfs3, &file, "b",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"oh no!", strlen("oh no!")) => strlen("oh no!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_file_open(&lfs3, &file, "c",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"hi c!", strlen("hi c!")) => strlen("hi c!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// change a file's type to something unknown
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
const char *path = "b";
|
|
lfs3_mdir_t mdir;
|
|
lfs3_did_t did;
|
|
lfs3_mtree_pathlookup(&lfs3, &path,
|
|
&mdir, &did) => LFS3_TAG_REG;
|
|
lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS(
|
|
LFS3_RATTR_CAT(
|
|
LFS3_TAG_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
lfs3_data_fromleb128(did, (uint8_t[LFS3_LEB128_DSIZE]){0}),
|
|
LFS3_DATA_BUF(path, lfs3_path_namelen(path))))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// our file should appear as an unknown type
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// renaming unknown files should still work, if this would leak
|
|
// resources the new type should set a wcompat flag
|
|
lfs3_rename(&lfs3, "b", "c") => 0;
|
|
|
|
// check that things look reasonable after renaming/removing
|
|
lfs3_stat(&lfs3, "c", &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_unknown_type_mv_dst]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// create some files
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "a",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"hi a!", strlen("hi a!")) => strlen("hi a!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_file_open(&lfs3, &file, "b",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"oh no!", strlen("oh no!")) => strlen("oh no!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_file_open(&lfs3, &file, "c",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"hi c!", strlen("hi c!")) => strlen("hi c!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// change a file's type to something unknown
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
const char *path = "b";
|
|
lfs3_mdir_t mdir;
|
|
lfs3_did_t did;
|
|
lfs3_mtree_pathlookup(&lfs3, &path,
|
|
&mdir, &did) => LFS3_TAG_REG;
|
|
lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS(
|
|
LFS3_RATTR_CAT(
|
|
LFS3_TAG_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
lfs3_data_fromleb128(did, (uint8_t[LFS3_LEB128_DSIZE]){0}),
|
|
LFS3_DATA_BUF(path, lfs3_path_namelen(path))))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// our file should appear as an unknown type
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// renaming unknown files should still work, if this would leak
|
|
// resources the new type should set a wcompat flag
|
|
lfs3_rename(&lfs3, "c", "b") => 0;
|
|
|
|
// check that things look reasonable after renaming/removing
|
|
lfs3_stat(&lfs3, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_unknown_type_mv_src_dst]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// create some files
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "a",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"hi a!", strlen("hi a!")) => strlen("hi a!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_file_open(&lfs3, &file, "b",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"oh no!", strlen("oh no!")) => strlen("oh no!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_file_open(&lfs3, &file, "c",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"oh hi!", strlen("oh hi!")) => strlen("oh hi!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// change a file's type to something unknown
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
const char *path = "b";
|
|
lfs3_mdir_t mdir;
|
|
lfs3_did_t did;
|
|
lfs3_mtree_pathlookup(&lfs3, &path,
|
|
&mdir, &did) => LFS3_TAG_REG;
|
|
lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS(
|
|
LFS3_RATTR_CAT(
|
|
LFS3_TAG_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
lfs3_data_fromleb128(did, (uint8_t[LFS3_LEB128_DSIZE]){0}),
|
|
LFS3_DATA_BUF(path, lfs3_path_namelen(path))))) => 0;
|
|
path = "c";
|
|
lfs3_mtree_pathlookup(&lfs3, &path,
|
|
&mdir, &did) => LFS3_TAG_REG;
|
|
lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS(
|
|
LFS3_RATTR_CAT(
|
|
LFS3_TAG_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
lfs3_data_fromleb128(did, (uint8_t[LFS3_LEB128_DSIZE]){0}),
|
|
LFS3_DATA_BUF(path, lfs3_path_namelen(path))))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// our file should appear as an unknown type
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// renaming unknown files should still work, if this would leak
|
|
// resources the new type should set a wcompat flag
|
|
lfs3_rename(&lfs3, "b", "c") => 0;
|
|
|
|
// check that things look reasonable after renaming/removing
|
|
lfs3_stat(&lfs3, "c", &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_unknown_type_mv_noop]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// create some files
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "a",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"hi a!", strlen("hi a!")) => strlen("hi a!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_file_open(&lfs3, &file, "b",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"oh no!", strlen("oh no!")) => strlen("oh no!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_file_open(&lfs3, &file, "c",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"hi c!", strlen("hi c!")) => strlen("hi c!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// change a file's type to something unknown
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
const char *path = "b";
|
|
lfs3_mdir_t mdir;
|
|
lfs3_did_t did;
|
|
lfs3_mtree_pathlookup(&lfs3, &path,
|
|
&mdir, &did) => LFS3_TAG_REG;
|
|
lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS(
|
|
LFS3_RATTR_CAT(
|
|
LFS3_TAG_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
lfs3_data_fromleb128(did, (uint8_t[LFS3_LEB128_DSIZE]){0}),
|
|
LFS3_DATA_BUF(path, lfs3_path_namelen(path))))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// our file should appear as an unknown type
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// renaming unknown files should still work, if this would leak
|
|
// resources the new type should set a wcompat flag
|
|
lfs3_rename(&lfs3, "b", "b") => 0;
|
|
|
|
// check that things look reasonable after renaming/removing
|
|
lfs3_stat(&lfs3, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_unknown_type_mv_notdir]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// create some files
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "a",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"hi a!", strlen("hi a!")) => strlen("hi a!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_file_open(&lfs3, &file, "b",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"oh no!", strlen("oh no!")) => strlen("oh no!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_mkdir(&lfs3, "c") => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// change a file's type to something unknown
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
const char *path = "b";
|
|
lfs3_mdir_t mdir;
|
|
lfs3_did_t did;
|
|
lfs3_mtree_pathlookup(&lfs3, &path,
|
|
&mdir, &did) => LFS3_TAG_REG;
|
|
lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS(
|
|
LFS3_RATTR_CAT(
|
|
LFS3_TAG_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
lfs3_data_fromleb128(did, (uint8_t[LFS3_LEB128_DSIZE]){0}),
|
|
LFS3_DATA_BUF(path, lfs3_path_namelen(path))))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// our file should appear as an unknown type
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// renaming unknown files should still work, if this would leak
|
|
// resources the new type should set a wcompat flag
|
|
lfs3_rename(&lfs3, "c", "b") => LFS3_ERR_NOTDIR;
|
|
|
|
// check that things look reasonable after renaming/removing
|
|
lfs3_stat(&lfs3, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_unknown_type_mv_isdir]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// create some files
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "a",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"hi a!", strlen("hi a!")) => strlen("hi a!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_file_open(&lfs3, &file, "b",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file,
|
|
"oh no!", strlen("oh no!")) => strlen("oh no!");
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_mkdir(&lfs3, "c") => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// change a file's type to something unknown
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
const char *path = "b";
|
|
lfs3_mdir_t mdir;
|
|
lfs3_did_t did;
|
|
lfs3_mtree_pathlookup(&lfs3, &path,
|
|
&mdir, &did) => LFS3_TAG_REG;
|
|
lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS(
|
|
LFS3_RATTR_CAT(
|
|
LFS3_TAG_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
lfs3_data_fromleb128(did, (uint8_t[LFS3_LEB128_DSIZE]){0}),
|
|
LFS3_DATA_BUF(path, lfs3_path_namelen(path))))) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// mount
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// our file should appear as an unknown type
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// renaming unknown files should still work, if this would leak
|
|
// resources the new type should set a wcompat flag
|
|
lfs3_rename(&lfs3, "b", "c") => LFS3_ERR_ISDIR;
|
|
|
|
// check that things look reasonable after renaming/removing
|
|
lfs3_stat(&lfs3, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS3_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
|
|
# Ok, here's an interesting one, test that we fail if we're
|
|
# "out-of-phase", i.e. the mrootanchor has been shifted by a small
|
|
# number of blocks.
|
|
#
|
|
# This can happen if we find the wrong mrootanchor (after, say, a magic
|
|
# scan), and risks filesystem corruption. To prevent this, we include 2
|
|
# phase bits in cksum tags to detect up to a 3 block shift (the maximum
|
|
# number of redund mrootanchors)
|
|
#
|
|
[cases.test_mount_incompat_out_of_phase]
|
|
defines.PHASE = [1, 2, 3, 4]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
|
|
// with some files
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "r2d2",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
char wbuf[256];
|
|
strcpy(wbuf, "beep boop");
|
|
lfs3_file_write(&lfs3, &file, wbuf, strlen(wbuf)) => strlen(wbuf);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_file_open(&lfs3, &file, "c3po",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
strcpy(wbuf, "we seem to be made to suffer");
|
|
lfs3_file_write(&lfs3, &file, wbuf, strlen(wbuf)) => strlen(wbuf);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// shift the filesystem out-of-phase
|
|
uint8_t shift_buf[BLOCK_SIZE];
|
|
for (lfs3_size_t i = 0; i < 4; i++) {
|
|
CFG->read(CFG, 4-1-i, 0, shift_buf, BLOCK_SIZE) => 0;
|
|
CFG->erase(CFG, 4-1-i + PHASE) => 0;
|
|
CFG->prog(CFG, 4-1-i + PHASE, 0, shift_buf, BLOCK_SIZE) => 0;
|
|
|
|
memset(shift_buf, 0, BLOCK_SIZE);
|
|
strcpy((char*)shift_buf,
|
|
"these aren't the files you're looking for ;)");
|
|
CFG->erase(CFG, 4-1-i) => 0;
|
|
CFG->prog(CFG, 4-1-i, 0, shift_buf, BLOCK_SIZE) => 0;
|
|
}
|
|
|
|
// mount should now fail
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_CORRUPT;
|
|
lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_CORRUPT;
|
|
'''
|