d308ec8322
Mainly to make room for some future planned stuff:
- Moved the mroot's redund bits from LFSR_TAG_GEOMETRY to
LFSR_TAG_MAGIC:
LFSR_TAG_MAGIC 0x003r v--- ---- --11 --rr
This has the benefit of living in a fixed location (off=0x5), which
may make mounting/debugging easier. It also makes LFSR_TAG_GEOMETRY
less of a special case (LFSR_TAG_MAGIC is already a _very_ special
case).
Unfortunately, this does get in the way of our previous magic=0x3
encoding. To compensate (and to avoid conflicts with LFSR_TAG_NULL),
I've added the 0x3_ prefix. This has the funny side-effect of
rendering redunds 0-3 as ascii 0-3 (0x30-0x33), which is a complete
accident but may actually be useful when debugging.
Currently all config tags fit in the 0x3_ prefix, which is nice for
debugging but not a hard requirement.
- Flipped LFSR_TAG_FILELIMIT/NAMELIMIT:
LFSR_TAG_FILELIMIT 0x0039 v--- ---- --11 1--1
LFSR_TAG_NAMELIMIT 0x003a v--- ---- --11 1-1-
The file limit is a _bit_ more fundamental. It's effectively the
required integer size for the filesystem.
These may also be followed by LFSR_TAG_ATTRLIMIT based on how future
attr revisits go.
- Rearranged struct tags so that LFSR_TAG_BRANCH = 0x300:
LFSR_TAG_BRANCH 0x030r v--- --11 ---- --rr
LFSR_TAG_DATA 0x0304 v--- --11 ---- -1--
LFSR_TAG_BLOCK 0x0308 v--- --11 ---- 1err
LFSR_TAG_DDKEY* 0x0310 v--- --11 ---1 ----
LFSR_TAG_DID 0x0314 v--- --11 ---1 -1--
LFSR_TAG_BSHRUB 0x0318 v--- --11 ---1 1---
LFSR_TAG_BTREE 0x031c v--- --11 ---1 11rr
LFSR_TAG_MROOT 0x032r v--- --11 --1- --rr
LFSR_TAG_MDIR 0x0324 v--- --11 --1- -1rr
LFSR_TAG_MTREE 0x032c v--- --11 --1- 11rr
*Planned
LFSR_TAG_BRANCH is a very special tag when it comes to bshrub/btree
traversal, so I think it deserves the subtype=0 slot.
This also just makes everything fit together better, and makes room
for the future planned ddkey tag.
Code changes minimal:
code stack ctx
before: 35728 2440 640
after: 35732 (+0.0%) 2440 (+0.0%) 640 (+0.0%)
1966 lines
65 KiB
TOML
1966 lines
65 KiB
TOML
# Advanced mount tests
|
|
after = ['test_mtree', 'test_traversal']
|
|
|
|
|
|
# test we can mount
|
|
[cases.test_mount_simple]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test that various mount flags don't assert and are returned by
|
|
# lfsr_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.CKPARITY = [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 = [
|
|
'LFS_IFDEF_REVDBG(true, !REVDBG)',
|
|
'LFS_IFDEF_REVNOISE(true, !REVNOISE)',
|
|
'!REVDBG || !REVNOISE',
|
|
'LFS_IFDEF_CKPROGS(true, !CKPROGS)',
|
|
'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)',
|
|
'LFS_IFDEF_CKPARITY(true, !CKPARITY)',
|
|
'LFS_IFDEF_CKDATACKSUMS(true, !CKDATACKSUMS)',
|
|
'!RDONLY || !MKCONSISTENT',
|
|
'!RDONLY || !LOOKAHEAD',
|
|
'!RDONLY || !COMPACT',
|
|
]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
((RDONLY) ? LFS_M_RDONLY : LFS_M_RDWR)
|
|
| ((FLUSH) ? LFS_M_FLUSH : 0)
|
|
| ((SYNC) ? LFS_M_SYNC : 0)
|
|
| ((REVDBG) ? LFS_IFDEF_REVDBG(LFS_M_REVDBG, -1) : 0)
|
|
| ((REVNOISE) ? LFS_IFDEF_REVNOISE(LFS_M_REVNOISE, -1) : 0)
|
|
| ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_M_CKPROGS, -1) : 0)
|
|
| ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_M_CKFETCHES, -1) : 0)
|
|
| ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_M_CKPARITY, -1) : 0)
|
|
| ((CKDATACKSUMS)
|
|
? LFS_IFDEF_CKDATACKSUMS(LFS_M_CKDATACKSUMS, -1)
|
|
: 0)
|
|
| ((MKCONSISTENT) ? LFS_M_MKCONSISTENT : 0)
|
|
| ((LOOKAHEAD) ? LFS_M_LOOKAHEAD : 0)
|
|
| ((COMPACT) ? LFS_M_COMPACT : 0)
|
|
| ((CKMETA) ? LFS_M_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_M_CKDATA : 0),
|
|
CFG) => 0;
|
|
|
|
// lfsr_fs_stat only returns some flags
|
|
struct lfs_fsinfo fsinfo;
|
|
lfsr_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((RDONLY) ? LFS_I_RDONLY : 0)
|
|
| ((FLUSH) ? LFS_I_FLUSH : 0)
|
|
| ((SYNC) ? LFS_I_SYNC : 0)
|
|
| ((REVDBG) ? LFS_IFDEF_REVDBG(LFS_M_REVDBG, -1) : 0)
|
|
| ((REVNOISE) ? LFS_IFDEF_REVNOISE(LFS_M_REVNOISE, -1) : 0)
|
|
| ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_I_CKPROGS, -1) : 0)
|
|
| ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_I_CKFETCHES, -1) : 0)
|
|
| ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_I_CKPARITY, -1) : 0)
|
|
| ((CKDATACKSUMS)
|
|
? LFS_IFDEF_CKDATACKSUMS(LFS_I_CKDATACKSUMS, -1)
|
|
: 0)
|
|
| ((!MKCONSISTENT) ? LFS_I_MKCONSISTENT : 0)
|
|
| ((!LOOKAHEAD) ? LFS_I_LOOKAHEAD : 0)
|
|
| ((!COMPACT) ? LFS_I_COMPACT : 0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS_I_CKDATA : 0)));
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test that various format flags don't, uh, 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.CKPARITY = [false, true]
|
|
defines.CKDATACKSUMS = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = [
|
|
'LFS_IFDEF_REVDBG(true, !REVDBG)',
|
|
'LFS_IFDEF_REVNOISE(true, !REVNOISE)',
|
|
'!REVDBG || !REVNOISE',
|
|
'LFS_IFDEF_CKPROGS(true, !CKPROGS)',
|
|
'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)',
|
|
'LFS_IFDEF_CKPARITY(true, !CKPARITY)',
|
|
'LFS_IFDEF_CKDATACKSUMS(true, !CKDATACKSUMS)',
|
|
]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs,
|
|
LFS_F_RDWR
|
|
| ((REVDBG) ? LFS_IFDEF_REVDBG(LFS_F_REVDBG, -1) : 0)
|
|
| ((REVNOISE) ? LFS_IFDEF_REVNOISE(LFS_F_REVNOISE, -1) : 0)
|
|
| ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_F_CKPROGS, -1) : 0)
|
|
| ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_F_CKFETCHES, -1) : 0)
|
|
| ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_F_CKPARITY, -1) : 0)
|
|
| ((CKDATACKSUMS)
|
|
? LFS_IFDEF_CKDATACKSUMS(LFS_F_CKDATACKSUMS, -1)
|
|
: 0)
|
|
| ((CKMETA) ? LFS_F_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_F_CKDATA : 0),
|
|
CFG) => 0;
|
|
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_unmount(&lfs) => 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 = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
|
|
// by default we need a lookahead scan
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
struct lfs_fsinfo fsinfo;
|
|
lfsr_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS_I_MKCONSISTENT
|
|
| LFS_I_LOOKAHEAD
|
|
| LFS_I_COMPACT
|
|
| LFS_I_CKMETA
|
|
| LFS_I_CKDATA));
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// with LFS_M_LOOKAHEAD, mount performs a lookahead scan
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| LFS_M_LOOKAHEAD
|
|
| ((CKMETA) ? LFS_M_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_M_CKDATA : 0),
|
|
CFG) => 0;
|
|
lfsr_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS_I_MKCONSISTENT
|
|
| LFS_I_COMPACT
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS_I_CKDATA : 0)));
|
|
lfsr_unmount(&lfs) => 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 = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// first lets create a compactable filesystem
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// write to our mdir until >gc_compact_thresh full
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jellyfish",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// hack, don't use the internals like this
|
|
uint8_t wbuf[SIZE];
|
|
while ((file.b.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// by default mount does not compact
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
struct lfs_fsinfo fsinfo;
|
|
lfsr_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS_I_MKCONSISTENT
|
|
| LFS_I_LOOKAHEAD
|
|
| LFS_I_COMPACT
|
|
| LFS_I_CKMETA
|
|
| LFS_I_CKDATA));
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// with LFS_M_COMPACT, mount compact any uncompacted blocks
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| LFS_M_COMPACT
|
|
| ((LOOKAHEAD) ? LFS_M_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS_M_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_M_CKDATA : 0),
|
|
CFG) => 0;
|
|
lfsr_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS_I_MKCONSISTENT
|
|
| ((!LOOKAHEAD) ? LFS_I_LOOKAHEAD : 0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS_I_CKDATA : 0)));
|
|
|
|
// mdir should have been compacted
|
|
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
|
assert((file.b.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// check we can still read the file
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 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 = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// first lets create some orphans
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create two files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "cuttlefish",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "octopus",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &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)
|
|
//
|
|
lfsr_file_t orphans[ORPHANS];
|
|
for (lfs_size_t i = 0; i < ORPHANS; i++) {
|
|
char name[256];
|
|
sprintf(name, "jellyfish%03x", i);
|
|
lfsr_file_open(&lfs, &orphans[i], name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < ORPHANS; i++) {
|
|
lfsr_file_close(&lfs, &orphans[i]) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// by default we clean up orphans lazily
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
struct lfs_fsinfo fsinfo;
|
|
lfsr_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS_I_MKCONSISTENT
|
|
| LFS_I_LOOKAHEAD
|
|
| LFS_I_COMPACT
|
|
| LFS_I_CKMETA
|
|
| LFS_I_CKDATA));
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// with LFS_M_MKCONSISTENT, mount cleans up orphans eagerly
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| LFS_M_MKCONSISTENT
|
|
| ((LOOKAHEAD) ? LFS_M_LOOKAHEAD : 0)
|
|
| ((COMPACT) ? LFS_M_COMPACT : 0)
|
|
| ((CKMETA) ? LFS_M_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_M_CKDATA : 0),
|
|
CFG) => 0;
|
|
lfsr_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!LOOKAHEAD) ? LFS_I_LOOKAHEAD : 0)
|
|
| ((!COMPACT) ? LFS_I_COMPACT : 0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS_I_CKDATA : 0)));
|
|
|
|
// check we can still read the files
|
|
lfsr_file_open(&lfs, &file, "cuttlefish", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "octopus", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 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 = '''
|
|
lfs_block_t i = 0;
|
|
while (true) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create an interesting filesystem
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "squid%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t, 0) => 0;
|
|
lfs_block_t k = 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
goto done;
|
|
}
|
|
|
|
// this gets a bit tricky be cause we need to clobber both
|
|
// blocks in mdir pairs
|
|
if (tinfo.btype == LFS_BTYPE_MDIR
|
|
|| tinfo.btype == LFS_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 != LFS_BTYPE_MDIR || k == i+1) {
|
|
i += (tinfo.btype == LFS_BTYPE_MDIR) ? 2 : 1;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
goto clobbered;
|
|
}
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// mount with LFS_M_CKMETA, we should detect clobbered blocks
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| LFS_M_CKMETA,
|
|
CFG) => LFS_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 = '''
|
|
lfs_block_t i = 0;
|
|
while (true) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create an interesting filesystem
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "squid%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t, 0) => 0;
|
|
lfs_block_t k = 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
goto done;
|
|
}
|
|
|
|
// this gets a bit tricky be cause we need to clobber both
|
|
// blocks in mdir pairs
|
|
if (tinfo.btype == LFS_BTYPE_MDIR
|
|
|| tinfo.btype == LFS_BTYPE_BTREE
|
|
|| tinfo.btype == LFS_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 != LFS_BTYPE_MDIR || k == i+1) {
|
|
i += (tinfo.btype == LFS_BTYPE_MDIR) ? 2 : 1;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
goto clobbered;
|
|
}
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// mount with LFS_M_CKDATA, we should detect clobbered blocks
|
|
//
|
|
// note LFS_M_CKDATA implies LFS_M_CKMETA
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| LFS_M_CKDATA,
|
|
CFG) => LFS_ERR_CORRUPT;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
|
|
|
|
## incompatiblity tests ##
|
|
|
|
# test that we fail if we find no magic
|
|
[cases.test_mount_incompat_no_magic]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
|
|
// delete the magic string
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR(
|
|
LFSR_TAG_RM | LFSR_TAG_MAGIC, 0))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should now fail
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_CORRUPT;
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_CORRUPT;
|
|
'''
|
|
|
|
# test that we fail if we find bad magic
|
|
[cases.test_mount_incompat_bad_magic]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
|
|
// tweak the magic string
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_BUF(
|
|
LFSR_TAG_MAGIC, 0,
|
|
"lottlefs", 8))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should now fail
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_CORRUPT;
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_CORRUPT;
|
|
'''
|
|
|
|
# test that we fail to mount after a major version bump
|
|
[cases.test_mount_incompat_major]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
|
|
// bump the major version
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_BUF(
|
|
LFSR_TAG_VERSION, 0,
|
|
((const uint8_t[2]){
|
|
LFS_DISK_VERSION_MAJOR+1,
|
|
0}), 2))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should now fail
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
|
|
'''
|
|
|
|
# test that we fail to mount after a minor version bump
|
|
[cases.test_mount_incompat_minor]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
|
|
// bump the minor version
|
|
//
|
|
// note we're messing around with internals to do this! this
|
|
// is not a user API
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_BUF(
|
|
LFSR_TAG_VERSION, 0,
|
|
((const uint8_t[2]){
|
|
LFS_DISK_VERSION_MAJOR,
|
|
LFS_DISK_VERSION_MINOR+1}), 2))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should now fail
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
|
|
'''
|
|
|
|
# test that we fail to mount incompatible rcompat flags
|
|
[cases.test_mount_incompat_rcompat]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_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
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_LE32(
|
|
LFSR_TAG_RCOMPAT, 0,
|
|
LFSR_RCOMPAT_COMPAT
|
|
| LFSR_RCOMPAT_NONSTANDARD))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should now fail
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
|
|
'''
|
|
|
|
# test that we fail to mount incompatible wcompat flags
|
|
[cases.test_mount_incompat_wcompat]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_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
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_LE32(
|
|
LFSR_TAG_WCOMPAT, 0,
|
|
LFSR_WCOMPAT_COMPAT
|
|
| LFSR_WCOMPAT_NONSTANDARD))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should now fail
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
|
|
|
|
// but we _can_ mount readonly
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test that an incompatible ocompat flag is a noop
|
|
[cases.test_mount_incompat_ocompat]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_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
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_LE32(
|
|
LFSR_TAG_OCOMPAT, 0,
|
|
LFSR_OCOMPAT_COMPAT
|
|
| LFSR_OCOMPAT_NONSTANDARD))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should _not_ fail, ocompat should always be ignored
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test that we fail to mount rdonly images
|
|
[cases.test_mount_incompat_rdonly]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_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
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_LE32(
|
|
LFSR_TAG_WCOMPAT, 0,
|
|
LFSR_WCOMPAT_COMPAT
|
|
| LFSR_WCOMPAT_RDONLY))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should now fail
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
|
|
|
|
// but we _can_ mount readonly
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test that we fail to mount wronly images
|
|
[cases.test_mount_incompat_wronly]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_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
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_LE32(
|
|
LFSR_TAG_RCOMPAT, 0,
|
|
LFSR_RCOMPAT_COMPAT
|
|
| LFSR_RCOMPAT_WRONLY))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should now fail
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
|
|
'''
|
|
|
|
# these are just a bit harder to detect
|
|
[cases.test_mount_incompat_rcompat_overflow]
|
|
defines.OVERFLOW = 72
|
|
defines.FLAG = 'range(72)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_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);
|
|
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
uint8_t rcompat_buf[LFSR_LE32_DSIZE];
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_CAT(
|
|
LFSR_TAG_RCOMPAT, 0,
|
|
lfsr_data_fromle32(LFSR_RCOMPAT_COMPAT, rcompat_buf),
|
|
LFSR_DATA_BUF(overflow, sizeof(overflow))))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should now fail
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_wcompat_overflow]
|
|
defines.OVERFLOW = 72
|
|
defines.FLAG = 'range(72)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_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);
|
|
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
uint8_t wcompat_buf[LFSR_LE32_DSIZE];
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_CAT(
|
|
LFSR_TAG_WCOMPAT, 0,
|
|
lfsr_data_fromle32(LFSR_WCOMPAT_COMPAT, wcompat_buf),
|
|
LFSR_DATA_BUF(overflow, sizeof(overflow))))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should now fail
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
|
|
|
|
// but we _can_ mount readonly
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_ocompat_overflow]
|
|
defines.OVERFLOW = 72
|
|
defines.FLAG = 'range(72)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_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);
|
|
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
uint8_t ocompat_buf[LFSR_LE32_DSIZE];
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_CAT(
|
|
LFSR_TAG_OCOMPAT, 0,
|
|
lfsr_data_fromle32(LFSR_OCOMPAT_COMPAT, ocompat_buf),
|
|
LFSR_DATA_BUF(overflow, sizeof(overflow))))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should _not_ fail, ocompat should always be ignored
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# but just appending zeros is _not_ an error
|
|
[cases.test_mount_incompat_rcompat_padding]
|
|
defines.OVERFLOW = 72
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_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));
|
|
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
uint8_t rcompat_buf[LFSR_LE32_DSIZE];
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_CAT(
|
|
LFSR_TAG_RCOMPAT, 0,
|
|
lfsr_data_fromle32(LFSR_RCOMPAT_COMPAT, rcompat_buf),
|
|
LFSR_DATA_BUF(overflow, sizeof(overflow))))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should _not_ fail, extra zeros should be ignored
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_wcompat_padding]
|
|
defines.OVERFLOW = 72
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_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));
|
|
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
uint8_t wcompat_buf[LFSR_LE32_DSIZE];
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_CAT(
|
|
LFSR_TAG_WCOMPAT, 0,
|
|
lfsr_data_fromle32(LFSR_WCOMPAT_COMPAT, wcompat_buf),
|
|
LFSR_DATA_BUF(overflow, sizeof(overflow))))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should _not_ fail, extra zeros should be ignored
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_ocompat_padding]
|
|
defines.OVERFLOW = 72
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_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));
|
|
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
uint8_t ocompat_buf[LFSR_LE32_DSIZE];
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_CAT(
|
|
LFSR_TAG_OCOMPAT, 0,
|
|
lfsr_data_fromle32(LFSR_OCOMPAT_COMPAT, ocompat_buf),
|
|
LFSR_DATA_BUF(overflow, sizeof(overflow))))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should _not_ fail, extra zeros should be ignored
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => 0;
|
|
lfsr_unmount(&lfs) => 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 = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_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
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_GEOMETRY(
|
|
LFSR_TAG_GEOMETRY, 0,
|
|
(&(lfsr_geometry_t){
|
|
INC_BLOCK_SIZE,
|
|
BLOCK_COUNT})))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should now fail
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_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 = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_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
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_GEOMETRY(
|
|
LFSR_TAG_GEOMETRY, 0,
|
|
(&(lfsr_geometry_t){
|
|
BLOCK_SIZE,
|
|
INC_BLOCK_COUNT})))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should now fail
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
|
|
'''
|
|
|
|
# test that we fail to mount after incompatible name limit
|
|
[cases.test_mount_incompat_name_limit]
|
|
defines.INC_NAME_LIMIT = ['LFS_NAME_MAX*2']
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_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
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_LEB128(
|
|
LFSR_TAG_NAMELIMIT, 0,
|
|
INC_NAME_LIMIT))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should now fail
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
|
|
'''
|
|
|
|
# test that we fail to mount after incompatible file limit
|
|
[cases.test_mount_incompat_file_limit]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_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
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
uint8_t file_limit_buf[LFSR_LEB128_DSIZE];
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_CAT(
|
|
LFSR_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
|
|
LFSR_DATA_BUF("\xff", 1),
|
|
lfsr_data_fromleb128(LFS_FILE_MAX, file_limit_buf)))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should now fail
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
|
|
'''
|
|
|
|
# test what happens if we find an unknown config
|
|
[cases.test_mount_incompat_unknown_config]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
|
|
// create an unknown config
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_RATTRS(
|
|
LFSR_RATTR_BUF(
|
|
LFSR_TAG_CONFIG + 0x42, 0,
|
|
"oh no!", strlen("oh no!")))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount should now fail
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
|
|
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
|
|
'''
|
|
|
|
# test what happens if we find an unknown file type
|
|
[cases.test_mount_incompat_unknown_type]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
|
|
// create some files
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "a",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"hi a!", strlen("hi a!")) => strlen("hi a!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_open(&lfs, &file, "b",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"oh no!", strlen("oh no!")) => strlen("oh no!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_open(&lfs, &file, "c",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"hi c!", strlen("hi c!")) => strlen("hi c!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// change a file's type to something unknown
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
const char *path = "b";
|
|
lfsr_mdir_t mdir;
|
|
lfsr_did_t did;
|
|
lfsr_mtree_pathlookup(&lfs, &path,
|
|
&mdir, NULL, &did) => 0;
|
|
lfsr_mdir_commit(&lfs, &mdir, LFSR_RATTRS(
|
|
LFSR_RATTR_CAT(
|
|
LFSR_TAG_MASK8 | (LFSR_TAG_NAME + 0x13), 0,
|
|
lfsr_data_fromleb128(did, (uint8_t[LFSR_LEB128_DSIZE]){0}),
|
|
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// our file should appear as an unknown type
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
// open/mkdir should error
|
|
lfsr_file_open(&lfs, &file, "b",
|
|
LFS_O_RDONLY) => LFS_ERR_NOTSUP;
|
|
lfsr_file_open(&lfs, &file, "b",
|
|
LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_NOTSUP;
|
|
|
|
lfsr_mkdir(&lfs, "b") => LFS_ERR_EXIST;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_unknown_type_rm]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
|
|
// create some files
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "a",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"hi a!", strlen("hi a!")) => strlen("hi a!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_open(&lfs, &file, "b",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"oh no!", strlen("oh no!")) => strlen("oh no!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_open(&lfs, &file, "c",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"hi c!", strlen("hi c!")) => strlen("hi c!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// change a file's type to something unknown
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
const char *path = "b";
|
|
lfsr_mdir_t mdir;
|
|
lfsr_did_t did;
|
|
lfsr_mtree_pathlookup(&lfs, &path,
|
|
&mdir, NULL, &did) => 0;
|
|
lfsr_mdir_commit(&lfs, &mdir, LFSR_RATTRS(
|
|
LFSR_RATTR_CAT(
|
|
LFSR_TAG_MASK8 | (LFSR_TAG_NAME + 0x13), 0,
|
|
lfsr_data_fromleb128(did, (uint8_t[LFSR_LEB128_DSIZE]){0}),
|
|
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// our file should appear as an unknown type
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
// removing unknown files should still work, if this would leak
|
|
// resources the new type should set a wcompat flag
|
|
lfsr_remove(&lfs, "b") => 0;
|
|
|
|
// check that things look reasonable
|
|
lfsr_stat(&lfs, "b", &info) => LFS_ERR_NOENT;
|
|
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_unknown_type_mv_src]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
|
|
// create some files
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "a",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"hi a!", strlen("hi a!")) => strlen("hi a!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_open(&lfs, &file, "b",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"oh no!", strlen("oh no!")) => strlen("oh no!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_open(&lfs, &file, "c",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"hi c!", strlen("hi c!")) => strlen("hi c!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// change a file's type to something unknown
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
const char *path = "b";
|
|
lfsr_mdir_t mdir;
|
|
lfsr_did_t did;
|
|
lfsr_mtree_pathlookup(&lfs, &path,
|
|
&mdir, NULL, &did) => 0;
|
|
lfsr_mdir_commit(&lfs, &mdir, LFSR_RATTRS(
|
|
LFSR_RATTR_CAT(
|
|
LFSR_TAG_MASK8 | (LFSR_TAG_NAME + 0x13), 0,
|
|
lfsr_data_fromleb128(did, (uint8_t[LFSR_LEB128_DSIZE]){0}),
|
|
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// our file should appear as an unknown type
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
// renaming unknown files should still work, if this would leak
|
|
// resources the new type should set a wcompat flag
|
|
lfsr_rename(&lfs, "b", "c") => 0;
|
|
|
|
// check that things look reasonable after renaming/removing
|
|
lfsr_stat(&lfs, "c", &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_unknown_type_mv_dst]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
|
|
// create some files
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "a",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"hi a!", strlen("hi a!")) => strlen("hi a!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_open(&lfs, &file, "b",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"oh no!", strlen("oh no!")) => strlen("oh no!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_open(&lfs, &file, "c",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"hi c!", strlen("hi c!")) => strlen("hi c!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// change a file's type to something unknown
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
const char *path = "b";
|
|
lfsr_mdir_t mdir;
|
|
lfsr_did_t did;
|
|
lfsr_mtree_pathlookup(&lfs, &path,
|
|
&mdir, NULL, &did) => 0;
|
|
lfsr_mdir_commit(&lfs, &mdir, LFSR_RATTRS(
|
|
LFSR_RATTR_CAT(
|
|
LFSR_TAG_MASK8 | (LFSR_TAG_NAME + 0x13), 0,
|
|
lfsr_data_fromleb128(did, (uint8_t[LFSR_LEB128_DSIZE]){0}),
|
|
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// our file should appear as an unknown type
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
// renaming unknown files should still work, if this would leak
|
|
// resources the new type should set a wcompat flag
|
|
lfsr_rename(&lfs, "c", "b") => 0;
|
|
|
|
// check that things look reasonable after renaming/removing
|
|
lfsr_stat(&lfs, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_unknown_type_mv_src_dst]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
|
|
// create some files
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "a",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"hi a!", strlen("hi a!")) => strlen("hi a!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_open(&lfs, &file, "b",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"oh no!", strlen("oh no!")) => strlen("oh no!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_open(&lfs, &file, "c",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"oh hi!", strlen("oh hi!")) => strlen("oh hi!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// change a file's type to something unknown
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
const char *path = "b";
|
|
lfsr_mdir_t mdir;
|
|
lfsr_did_t did;
|
|
lfsr_mtree_pathlookup(&lfs, &path,
|
|
&mdir, NULL, &did) => 0;
|
|
lfsr_mdir_commit(&lfs, &mdir, LFSR_RATTRS(
|
|
LFSR_RATTR_CAT(
|
|
LFSR_TAG_MASK8 | (LFSR_TAG_NAME + 0x13), 0,
|
|
lfsr_data_fromleb128(did, (uint8_t[LFSR_LEB128_DSIZE]){0}),
|
|
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
|
|
path = "c";
|
|
lfsr_mtree_pathlookup(&lfs, &path,
|
|
&mdir, NULL, &did) => 0;
|
|
lfsr_mdir_commit(&lfs, &mdir, LFSR_RATTRS(
|
|
LFSR_RATTR_CAT(
|
|
LFSR_TAG_MASK8 | (LFSR_TAG_NAME + 0x13), 0,
|
|
lfsr_data_fromleb128(did, (uint8_t[LFSR_LEB128_DSIZE]){0}),
|
|
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// our file should appear as an unknown type
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
// renaming unknown files should still work, if this would leak
|
|
// resources the new type should set a wcompat flag
|
|
lfsr_rename(&lfs, "b", "c") => 0;
|
|
|
|
// check that things look reasonable after renaming/removing
|
|
lfsr_stat(&lfs, "c", &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_unknown_type_mv_noop]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
|
|
// create some files
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "a",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"hi a!", strlen("hi a!")) => strlen("hi a!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_open(&lfs, &file, "b",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"oh no!", strlen("oh no!")) => strlen("oh no!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_open(&lfs, &file, "c",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"hi c!", strlen("hi c!")) => strlen("hi c!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// change a file's type to something unknown
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
const char *path = "b";
|
|
lfsr_mdir_t mdir;
|
|
lfsr_did_t did;
|
|
lfsr_mtree_pathlookup(&lfs, &path,
|
|
&mdir, NULL, &did) => 0;
|
|
lfsr_mdir_commit(&lfs, &mdir, LFSR_RATTRS(
|
|
LFSR_RATTR_CAT(
|
|
LFSR_TAG_MASK8 | (LFSR_TAG_NAME + 0x13), 0,
|
|
lfsr_data_fromleb128(did, (uint8_t[LFSR_LEB128_DSIZE]){0}),
|
|
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// our file should appear as an unknown type
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
// renaming unknown files should still work, if this would leak
|
|
// resources the new type should set a wcompat flag
|
|
lfsr_rename(&lfs, "b", "b") => 0;
|
|
|
|
// check that things look reasonable after renaming/removing
|
|
lfsr_stat(&lfs, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi c!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_unknown_type_mv_notdir]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
|
|
// create some files
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "a",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"hi a!", strlen("hi a!")) => strlen("hi a!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_open(&lfs, &file, "b",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"oh no!", strlen("oh no!")) => strlen("oh no!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_mkdir(&lfs, "c") => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// change a file's type to something unknown
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
const char *path = "b";
|
|
lfsr_mdir_t mdir;
|
|
lfsr_did_t did;
|
|
lfsr_mtree_pathlookup(&lfs, &path,
|
|
&mdir, NULL, &did) => 0;
|
|
lfsr_mdir_commit(&lfs, &mdir, LFSR_RATTRS(
|
|
LFSR_RATTR_CAT(
|
|
LFSR_TAG_MASK8 | (LFSR_TAG_NAME + 0x13), 0,
|
|
lfsr_data_fromleb128(did, (uint8_t[LFSR_LEB128_DSIZE]){0}),
|
|
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// our file should appear as an unknown type
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
// renaming unknown files should still work, if this would leak
|
|
// resources the new type should set a wcompat flag
|
|
lfsr_rename(&lfs, "c", "b") => LFS_ERR_NOTDIR;
|
|
|
|
// check that things look reasonable after renaming/removing
|
|
lfsr_stat(&lfs, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mount_incompat_unknown_type_mv_isdir]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// create a superblock
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
|
|
// create some files
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "a",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"hi a!", strlen("hi a!")) => strlen("hi a!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_open(&lfs, &file, "b",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"oh no!", strlen("oh no!")) => strlen("oh no!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_mkdir(&lfs, "c") => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// change a file's type to something unknown
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
const char *path = "b";
|
|
lfsr_mdir_t mdir;
|
|
lfsr_did_t did;
|
|
lfsr_mtree_pathlookup(&lfs, &path,
|
|
&mdir, NULL, &did) => 0;
|
|
lfsr_mdir_commit(&lfs, &mdir, LFSR_RATTRS(
|
|
LFSR_RATTR_CAT(
|
|
LFSR_TAG_MASK8 | (LFSR_TAG_NAME + 0x13), 0,
|
|
lfsr_data_fromleb128(did, (uint8_t[LFSR_LEB128_DSIZE]){0}),
|
|
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// mount
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// our file should appear as an unknown type
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
// renaming unknown files should still work, if this would leak
|
|
// resources the new type should set a wcompat flag
|
|
lfsr_rename(&lfs, "b", "c") => LFS_ERR_ISDIR;
|
|
|
|
// check that things look reasonable after renaming/removing
|
|
lfsr_stat(&lfs, "b", &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "a") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("hi a!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "b") == 0);
|
|
assert(info.type == LFS_TYPE_UNKNOWN);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "c") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|