a34bcdb5bf
This drops the requirement that all file types are introduced with a
related wcompat flag. Instead, the wcompat flag is only required if
modification _would_ leak resources, and we treat unknown file types as
though they are regular files.
This allows modification of unknown file types without the risk of
breaking anything.
To compare with before the unknown-type rework:
Before:
> Unknown file types are allowed and may leak resources if modified,
> so attempted modification (rename/remove) will error with
> LFS_ERR_NOTSUP.
Now:
> Unknown file types are allowed but must not leak resources if
> modified. If an unknown file type would leak resources, it should set
> a related wcompat flag to only allow mounting RDONLY.
Note this includes directories, which can leak bookmarks if removed, so
filesystems using directories should set the LFSR_WCOMPAT_DIR flag.
But we no longer need the LFSR_WCOMPAT_REG/LFSR_WCOMPAT_STICKYNOTE
flags.
---
The real tricky part was getting lfsr_rename to work with unknown types,
as this broke the invariant that we only ever commit tags we know about.
Fixing this required:
- Fetching the non-unknown-mapped tag in lfsr_rename
- Mapping all name tags to LFSR_TAG_NAME in lfsr_rbyd_appendrattr_
- Adopting LFSR_RATTR_NAME for bookmark name tags
This was broken by the above lfsr_rbyd_appendrattr_ change, but it's
probably good to handle these the same as other name tags anyways.
This adds a bit of code, but not enough that I think this isn't worth
it (or worth a build-time option):
code stack ctx
before: 35924 2440 640
after: 35992 (+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 + 0x13, 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;
|
|
'''
|