b3ab83d5b5
The main change is adding LFS3_M_REVPERTURB, which will be necessary for
preerase allocations, but I got distracted and ended up giving the
revision count subsystem a bit of a refactor.
Main changes:
- Added LFS3_M_REVPERTURB, which ensures the leading bit in the
revision count changes after each allocation/relocation/compaction.
This is generally optional, but will be required for preerase
allocations. Our ecksum system is only reliable if we ensure at least
one bit changes, otherwise the chance of ecksum collision is very
high.
The downside of LFS3_M_REVPERTURB is that we need to read the contents
of the new block to figure out what the bit should change to. Probably
a minimal cost in the system, but still a good reason to make the
behavior optional.
Does LFS3_M_REVPERTURB have any use outside of preerased allocation?
I'm not sure. Maybe it has some niche use reducing the chance of bd
ECC collisions?
- Dropped LFS3_M_REVDBG, but adding low-effort debug bits that are
always enabled.
Making LFS3_M_REVDBG conditional was probably overkill. The flag
checks probably cost more than the actual debug bits when enabled.
Instead, replaced with a simpler, low-effort debug bit system, where
we only set the debug bits during mdir allocation/relocation. These
bits shouldn't change during normal compaction, but we _don't_
introduce debug bits if mounting a filesystem from a driver without
these debug bits.
- Restricted recycle counter to at most 20-bits to make space for
things. This ensures perturb/debug bits don't get overwritten (though
we really only care about perturb bits).
2^20 (~1M) recycles is probably enough for any device littlefs will
run on, especially considering the recycle_count should probably be
several orders of magnitude smaller than the device's expected erase
cycles.
Worst case this can always be increased in the future without
backwards incompatible changes. The only hard requirement for revision
counts is that the full 32-bits are comparable.
- Simplified lfs3_rev_inc and friends, and moved most of the
disk-dependent revision count stuff down into lfs3_rbyd appendrev.
This deduplicates the messy revision count handling in
lfs3_btree_commit_.
Though note the implicit lfs3_rbyd_appendrev now defaults to writing
the btree debug bits ('b'). A bit of a hack, but works for littlefs.
Here's the resulting encoding:
vvvv---- -------- -------- -ddddddd
vvvvrrrr rrrrrr-- -------- -ddddddd
vvvvrrrr rrrrrrnn nnnnnnnn pddddddd
'-.''----.----''----.----' ^'--.--'
'------|----------|------|---|---- 4-bit relocation revision
'----------|------|---|---- recycle-bits recycle counter
'------|---|---- pseudorandom noise (if revnoise)
'---|---- perturb bit (if revperturb)
'---- low-effort debug bits
11-1--- - h = mroot anchor
11-11-1 - m = mdir
11---1- - b = btree node
Note we store revision counts as le32s, so the perturb bit should end up
as the leading bit in the first byte.
Costs a bit more code (mostly because the debug bits are now
unconditional, even if low-effort), but simplifies the codebase:
code stack ctx
before: 35124 2136 660
after: 35144 (+0.1%) 2136 (+0.0%) 660 (+0.0%)
after+yesrevperturb: 35192 (+0.2%) 2136 (+0.0%) 660 (+0.0%)
code stack ctx
gbmap+np before: 38252 2144 776
gbmap+np after: 38272 (+0.1%) 2144 (+0.0%) 776 (+0.0%)
gbmap+np after+yrp: 38328 (+0.2%) 2144 (+0.0%) 776 (+0.0%)
code stack ctx
gbmap+yp before: 38832 2168 796
gbmap+yp after: 38852 (+0.1%) 2168 (+0.0%) 796 (+0.0%)
gbmap+yp after+yrp: 38908 (+0.2%) 2168 (+0.0%) 796 (+0.0%)
2291 lines
78 KiB
TOML
2291 lines
78 KiB
TOML
# Advanced mount tests
|
|
after = ['test_mtree', 'test_trvs']
|
|
|
|
# Test both with and without the gbmap if available
|
|
defines.GBMAP = [false, true]
|
|
if = 'LFS3_IFYES_GBMAP(GBMAP, true, !GBMAP)'
|
|
|
|
|
|
# test we can mount
|
|
[cases.test_mount_simple]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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.REVPERTURB = [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.COMPACT = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = [
|
|
'LFS3_IFYES_REVPERTURB(REVPERTURB, true, !REVPERTURB)',
|
|
'LFS3_IFYES_REVNOISE(REVNOISE, true, !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',
|
|
'!RDONLY || !COMPACT',
|
|
]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3,
|
|
((RDONLY) ? LFS3_M_RDONLY : LFS3_M_RDWR)
|
|
| ((FLUSH) ? LFS3_M_FLUSH : 0)
|
|
| ((SYNC) ? LFS3_M_SYNC : 0)
|
|
| ((REVPERTURB)
|
|
? LFS3_IFDEF_REVPERTURB(LFS3_M_REVPERTURB, -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)
|
|
| ((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)
|
|
| ((REVPERTURB)
|
|
? LFS3_IFDEF_REVPERTURB(LFS3_I_REVPERTURB, -1)
|
|
: 0)
|
|
| ((REVNOISE) ? LFS3_IFDEF_REVNOISE(LFS3_I_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 && !GBMAP) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((!COMPACT) ? LFS3_I_COMPACT : 0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 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.REVPERTURB = [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.COMPACT = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = [
|
|
'LFS3_IFYES_REVPERTURB(REVPERTURB, true, !REVPERTURB)',
|
|
'LFS3_IFYES_REVNOISE(REVNOISE, true, !REVNOISE)',
|
|
'LFS3_IFDEF_CKPROGS(true, !CKPROGS)',
|
|
'LFS3_IFDEF_CKFETCHES(true, !CKFETCHES)',
|
|
'LFS3_IFDEF_CKMETAPARITY(true, !CKMETAPARITY)',
|
|
'LFS3_IFDEF_CKDATACKSUMS(true, !CKDATACKSUMS)',
|
|
]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((REVPERTURB)
|
|
? LFS3_IFDEF_REVPERTURB(LFS3_F_REVPERTURB, -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)
|
|
| ((MKCONSISTENT) ? LFS3_F_MKCONSISTENT : 0)
|
|
| ((LOOKAHEAD) ? LFS3_F_LOOKAHEAD : 0)
|
|
| ((COMPACT) ? LFS3_F_COMPACT : 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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
|
|
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
|
|
| LFS3_I_MKCONSISTENT
|
|
| ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
|
|
// by default we need a lookahead scan
|
|
//
|
|
// unless we have a gbmap, the gbmap is persistent so usually
|
|
// _doesn't_ 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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
|
|
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
|
|
| LFS3_I_MKCONSISTENT
|
|
| ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
|
|
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
|
|
| LFS3_I_MKCONSISTENT
|
|
| LFS3_I_COMPACT
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_t_lookgbmap]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = [
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
ifdef = 'LFS3_GBMAP'
|
|
if = 'GBMAP'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
|
|
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
|
|
| LFS3_I_MKCONSISTENT
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
|
|
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
|
|
| LFS3_I_MKCONSISTENT
|
|
| LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// with LFS3_M_LOOKAHEAD, mount rebuild the gbmap
|
|
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
|
|
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
|
|
| LFS3_I_MKCONSISTENT
|
|
| LFS3_I_COMPACT
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_t_compact]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = [
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
|
|
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
|
|
| LFS3_I_MKCONSISTENT
|
|
| ((!GBMAP || SIZE >= BLOCK_SIZE/4)
|
|
? LFS3_I_LOOKAHEAD
|
|
: 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// with LFS3_M_COMPACT, mount compacts 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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
|
|
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
|
|
| LFS3_I_MKCONSISTENT
|
|
| ((!LOOKAHEAD && (!GBMAP || SIZE >= BLOCK_SIZE/4))
|
|
? LFS3_I_LOOKAHEAD
|
|
: 0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 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 = 'FCACHE_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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
|
|
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
|
|
| LFS3_I_MKCONSISTENT
|
|
| ((!GBMAP || ORPHANS >= 100)
|
|
? LFS3_I_LOOKAHEAD
|
|
: 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 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 == (
|
|
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
|
|
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
|
|
| ((!LOOKAHEAD && (!GBMAP || ORPHANS >= 100))
|
|
? LFS3_I_LOOKAHEAD
|
|
: 0)
|
|
| ((!COMPACT) ? LFS3_I_COMPACT : 0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 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',
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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',
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(1, LFS3_tag_RM | LFS3_TAG_MAGIC, 0),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(2, LFS3_TAG_MAGIC, 0, LFS3_FROM_BUF, 8),
|
|
LFS3_RATTR_ARG("lottlefs"),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(2, LFS3_TAG_VERSION, 0, LFS3_FROM_BUF, 2),
|
|
LFS3_RATTR_ARG(((const uint8_t[2]){
|
|
LFS3_DISK_VERSION_MAJOR+1,
|
|
0})),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(2, LFS3_TAG_VERSION, 0, LFS3_FROM_BUF, 2),
|
|
LFS3_RATTR_ARG(((const uint8_t[2]){
|
|
LFS3_DISK_VERSION_MAJOR,
|
|
LFS3_DISK_VERSION_MINOR+1})),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(2, LFS3_TAG_RCOMPAT, 0, LFS3_FROM_LE32),
|
|
LFS3_RATTR_ARG(
|
|
lfs3_rcompat(&lfs3)
|
|
| LFS3_RCOMPAT_NONSTANDARD),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(2, LFS3_TAG_WCOMPAT, 0, LFS3_FROM_LE32),
|
|
LFS3_RATTR_ARG(
|
|
lfs3_wcompat(&lfs3)
|
|
| LFS3_WCOMPAT_NONSTANDARD),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(2, LFS3_TAG_OCOMPAT, 0, LFS3_FROM_LE32),
|
|
LFS3_RATTR_ARG(
|
|
lfs3_ocompat(&lfs3)
|
|
| LFS3_OCOMPAT_NONSTANDARD),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(2, LFS3_TAG_WCOMPAT, 0, LFS3_FROM_LE32),
|
|
LFS3_RATTR_ARG(
|
|
lfs3_wcompat(&lfs3)
|
|
| LFS3_WCOMPAT_RDONLY),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(2, LFS3_TAG_RCOMPAT, 0, LFS3_FROM_LE32),
|
|
LFS3_RATTR_ARG(
|
|
lfs3_rcompat(&lfs3)
|
|
| LFS3_RCOMPAT_WRONLY),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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 rcompat_buf[sizeof(uint32_t) + OVERFLOW/8];
|
|
memset(rcompat_buf, 0, sizeof(rcompat_buf));
|
|
lfs3_data_fromle32(lfs3_rcompat(&lfs3), rcompat_buf);
|
|
rcompat_buf[sizeof(uint32_t) + FLAG/8] |= 1 << (FLAG%8);
|
|
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR(3, LFS3_TAG_RCOMPAT, 0, LFS3_FROM_DATA),
|
|
LFS3_RATTR_ARG(sizeof(rcompat_buf)),
|
|
LFS3_RATTR_ARG(rcompat_buf),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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 wcompat_buf[sizeof(uint32_t) + OVERFLOW/8];
|
|
memset(wcompat_buf, 0, sizeof(wcompat_buf));
|
|
lfs3_data_fromle32(lfs3_wcompat(&lfs3), wcompat_buf);
|
|
wcompat_buf[sizeof(uint32_t) + FLAG/8] |= 1 << (FLAG%8);
|
|
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR(3, LFS3_TAG_WCOMPAT, 0, LFS3_FROM_DATA),
|
|
LFS3_RATTR_ARG(sizeof(wcompat_buf)),
|
|
LFS3_RATTR_ARG(wcompat_buf),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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 ocompat_buf[sizeof(uint32_t) + OVERFLOW/8];
|
|
memset(ocompat_buf, 0, sizeof(ocompat_buf));
|
|
ocompat_buf[sizeof(uint32_t) + FLAG/8] |= 1 << (FLAG%8);
|
|
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR(3, LFS3_TAG_OCOMPAT, 0, LFS3_FROM_DATA),
|
|
LFS3_RATTR_ARG(sizeof(ocompat_buf)),
|
|
LFS3_RATTR_ARG(ocompat_buf),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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 rcompat_buf[sizeof(uint32_t) + OVERFLOW/8];
|
|
memset(rcompat_buf, 0, sizeof(rcompat_buf));
|
|
lfs3_data_fromle32(lfs3_rcompat(&lfs3), rcompat_buf);
|
|
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR(3, LFS3_TAG_RCOMPAT, 0, LFS3_FROM_DATA),
|
|
LFS3_RATTR_ARG(sizeof(rcompat_buf)),
|
|
LFS3_RATTR_ARG(rcompat_buf),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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 wcompat_buf[sizeof(uint32_t) + OVERFLOW/8];
|
|
memset(wcompat_buf, 0, sizeof(wcompat_buf));
|
|
lfs3_data_fromle32(lfs3_wcompat(&lfs3), wcompat_buf);
|
|
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR(3, LFS3_TAG_WCOMPAT, 0, LFS3_FROM_DATA),
|
|
LFS3_RATTR_ARG(sizeof(wcompat_buf)),
|
|
LFS3_RATTR_ARG(wcompat_buf),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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 ocompat_buf[sizeof(uint32_t) + OVERFLOW/8];
|
|
memset(ocompat_buf, 0, sizeof(ocompat_buf));
|
|
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR(3, LFS3_TAG_OCOMPAT, 0, LFS3_FROM_DATA),
|
|
LFS3_RATTR_ARG(sizeof(ocompat_buf)),
|
|
LFS3_RATTR_ARG(ocompat_buf),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(2, LFS3_TAG_GEOMETRY, 0, LFS3_FROM_GEOMETRY),
|
|
LFS3_RATTR_ARG((&(lfs3_geometry_t){
|
|
INC_BLOCK_SIZE,
|
|
BLOCK_COUNT})),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(2, LFS3_TAG_GEOMETRY, 0, LFS3_FROM_GEOMETRY),
|
|
LFS3_RATTR_ARG((&(lfs3_geometry_t){
|
|
BLOCK_SIZE,
|
|
INC_BLOCK_COUNT})),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(2, LFS3_TAG_NAMELIMIT, 0, LFS3_FROM_LEB128),
|
|
LFS3_RATTR_ARG(INC_NAME_LIMIT),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
|
|
// set an incompatible file limit
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
|
|
// 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
|
|
uint8_t filelimit_buf[1+LFS3_LEB128_DSIZE];
|
|
filelimit_buf[0] = 0xff;
|
|
lfs3_ssize_t d_ = lfs3_toleb128(LFS3_FILE_MAX,
|
|
filelimit_buf+1, LFS3_LEB128_DSIZE);
|
|
assert(d_ >= 0);
|
|
lfs3_size_t d = 1 + d_;
|
|
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR(3, LFS3_TAG_FILELIMIT, 0, LFS3_FROM_DATA),
|
|
LFS3_RATTR_ARG(d),
|
|
LFS3_RATTR_ARG(filelimit_buf),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
|
|
// create an unknown config
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS(
|
|
LFS3_RATTR(3, LFS3_TAG_CONFIG + 0x42, 0, LFS3_FROM_DATA),
|
|
LFS3_RATTR_ARG(strlen("oh no!")),
|
|
LFS3_RATTR_ARG("oh no!"),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
LFS3_FROM_NAME),
|
|
LFS3_RATTR_ARG(did),
|
|
LFS3_RATTR_ARG(path),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
LFS3_FROM_NAME),
|
|
LFS3_RATTR_ARG(did),
|
|
LFS3_RATTR_ARG(path),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
LFS3_FROM_NAME),
|
|
LFS3_RATTR_ARG(did),
|
|
LFS3_RATTR_ARG(path),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
LFS3_FROM_NAME),
|
|
LFS3_RATTR_ARG(did),
|
|
LFS3_RATTR_ARG(path),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
LFS3_FROM_NAME),
|
|
LFS3_RATTR_ARG(did),
|
|
LFS3_RATTR_ARG(path),
|
|
LFS3_RATTR_NULL)) => 0;
|
|
path = "c";
|
|
lfs3_mtree_pathlookup(&lfs3, &path,
|
|
&mdir, &did) => LFS3_TAG_REG;
|
|
lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS(
|
|
LFS3_RATTR(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
LFS3_FROM_NAME),
|
|
LFS3_RATTR_ARG(did),
|
|
LFS3_RATTR_ARG(path),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
LFS3_FROM_NAME),
|
|
LFS3_RATTR_ARG(did),
|
|
LFS3_RATTR_ARG(path),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
LFS3_FROM_NAME),
|
|
LFS3_RATTR_ARG(did),
|
|
LFS3_RATTR_ARG(path),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0,
|
|
LFS3_FROM_NAME),
|
|
LFS3_RATTR_ARG(did),
|
|
LFS3_RATTR_ARG(path),
|
|
LFS3_RATTR_NULL)) => 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
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
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;
|
|
'''
|