mirror of
https://github.com/eledio-devices/thirdparty-littlefs.git
synced 2025-11-01 00:38:29 +01:00
Compare commits
8 Commits
v2.4.0
...
test-revam
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9e403d55c | ||
|
|
d58aaf88dc | ||
|
|
71c844be53 | ||
|
|
75cd51b39e | ||
|
|
fc354801fa | ||
|
|
557ec332fe | ||
|
|
5e839df234 | ||
|
|
47ab0426b1 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -7,4 +7,4 @@
|
|||||||
blocks/
|
blocks/
|
||||||
lfs
|
lfs
|
||||||
test.c
|
test.c
|
||||||
tests_/*.toml.*
|
tests/*.toml.*
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -45,7 +45,7 @@ test:
|
|||||||
./scripts/test.py $(TFLAGS)
|
./scripts/test.py $(TFLAGS)
|
||||||
.SECONDEXPANSION:
|
.SECONDEXPANSION:
|
||||||
test%: tests/test$$(firstword $$(subst \#, ,%)).toml
|
test%: tests/test$$(firstword $$(subst \#, ,%)).toml
|
||||||
./scripts/test.py $(TFLAGS) $@
|
./scripts/test.py $@ $(TFLAGS)
|
||||||
|
|
||||||
-include $(DEP)
|
-include $(DEP)
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
|
|||||||
|
|
||||||
memset(bd->wear, 0, sizeof(lfs_testbd_wear_t) * cfg->block_count);
|
memset(bd->wear, 0, sizeof(lfs_testbd_wear_t) * cfg->block_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create underlying block device
|
// create underlying block device
|
||||||
if (bd->persist) {
|
if (bd->persist) {
|
||||||
bd->u.file.cfg = (struct lfs_filebd_config){
|
bd->u.file.cfg = (struct lfs_filebd_config){
|
||||||
@@ -155,9 +155,8 @@ int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block,
|
|||||||
LFS_ASSERT(block < cfg->block_count);
|
LFS_ASSERT(block < cfg->block_count);
|
||||||
|
|
||||||
// block bad?
|
// block bad?
|
||||||
if (bd->cfg->erase_cycles &&
|
if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles &&
|
||||||
bd->cfg->badblock_behavior == LFS_TESTBD_BADBLOCK_NOREAD &&
|
bd->cfg->badblock_behavior == LFS_TESTBD_BADBLOCK_READERROR) {
|
||||||
bd->wear[block] >= bd->cfg->erase_cycles) {
|
|
||||||
LFS_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT);
|
LFS_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT);
|
||||||
return LFS_ERR_CORRUPT;
|
return LFS_ERR_CORRUPT;
|
||||||
}
|
}
|
||||||
@@ -180,11 +179,18 @@ int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
|||||||
LFS_ASSERT(block < cfg->block_count);
|
LFS_ASSERT(block < cfg->block_count);
|
||||||
|
|
||||||
// block bad?
|
// block bad?
|
||||||
if (bd->cfg->erase_cycles &&
|
if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles) {
|
||||||
bd->cfg->badblock_behavior == LFS_TESTBD_BADBLOCK_NOPROG &&
|
if (bd->cfg->badblock_behavior ==
|
||||||
bd->wear[block] >= bd->cfg->erase_cycles) {
|
LFS_TESTBD_BADBLOCK_PROGERROR) {
|
||||||
LFS_TRACE("lfs_testbd_prog -> %d", LFS_ERR_CORRUPT);
|
LFS_TRACE("lfs_testbd_prog -> %d", LFS_ERR_CORRUPT);
|
||||||
return LFS_ERR_CORRUPT;
|
return LFS_ERR_CORRUPT;
|
||||||
|
} else if (bd->cfg->badblock_behavior ==
|
||||||
|
LFS_TESTBD_BADBLOCK_PROGNOOP ||
|
||||||
|
bd->cfg->badblock_behavior ==
|
||||||
|
LFS_TESTBD_BADBLOCK_ERASENOOP) {
|
||||||
|
LFS_TRACE("lfs_testbd_prog -> %d", 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// prog
|
// prog
|
||||||
@@ -219,9 +225,14 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
|||||||
// block bad?
|
// block bad?
|
||||||
if (bd->cfg->erase_cycles) {
|
if (bd->cfg->erase_cycles) {
|
||||||
if (bd->wear[block] >= bd->cfg->erase_cycles) {
|
if (bd->wear[block] >= bd->cfg->erase_cycles) {
|
||||||
if (bd->cfg->badblock_behavior == LFS_TESTBD_BADBLOCK_NOERASE) {
|
if (bd->cfg->badblock_behavior ==
|
||||||
|
LFS_TESTBD_BADBLOCK_ERASEERROR) {
|
||||||
LFS_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT);
|
LFS_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT);
|
||||||
return LFS_ERR_CORRUPT;
|
return LFS_ERR_CORRUPT;
|
||||||
|
} else if (bd->cfg->badblock_behavior ==
|
||||||
|
LFS_TESTBD_BADBLOCK_ERASENOOP) {
|
||||||
|
LFS_TRACE("lfs_testbd_erase -> %d", 0);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// mark wear
|
// mark wear
|
||||||
|
|||||||
@@ -19,14 +19,18 @@ extern "C"
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Mode determining how "bad blocks" behave during testing. This
|
// Mode determining how "bad blocks" behave during testing. This simulates
|
||||||
// simulates some real-world circumstances such as writes not
|
// some real-world circumstances such as progs not sticking (prog-noop),
|
||||||
// going through (noprog), erases not sticking (noerase), and ECC
|
// a readonly disk (erase-noop), and ECC failures (read-error).
|
||||||
// failures (noread).
|
//
|
||||||
|
// Not that read-noop is not allowed. Read _must_ return a consistent (but
|
||||||
|
// may be arbitrary) value on every read.
|
||||||
enum lfs_testbd_badblock_behavior {
|
enum lfs_testbd_badblock_behavior {
|
||||||
LFS_TESTBD_BADBLOCK_NOPROG = 0,
|
LFS_TESTBD_BADBLOCK_PROGERROR,
|
||||||
LFS_TESTBD_BADBLOCK_NOERASE = 1,
|
LFS_TESTBD_BADBLOCK_ERASEERROR,
|
||||||
LFS_TESTBD_BADBLOCK_NOREAD = 2,
|
LFS_TESTBD_BADBLOCK_READERROR,
|
||||||
|
LFS_TESTBD_BADBLOCK_PROGNOOP,
|
||||||
|
LFS_TESTBD_BADBLOCK_ERASENOOP,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Type for measuring wear
|
// Type for measuring wear
|
||||||
@@ -82,7 +86,7 @@ typedef struct lfs_testbd {
|
|||||||
/// Block device API ///
|
/// Block device API ///
|
||||||
|
|
||||||
// Create a test block device using the geometry in lfs_config
|
// Create a test block device using the geometry in lfs_config
|
||||||
//
|
//
|
||||||
// Note that filebd is used if a path is provided, if path is NULL
|
// Note that filebd is used if a path is provided, if path is NULL
|
||||||
// testbd will use rambd which can be much faster.
|
// testbd will use rambd which can be much faster.
|
||||||
int lfs_testbd_create(const struct lfs_config *cfg, const char *path);
|
int lfs_testbd_create(const struct lfs_config *cfg, const char *path);
|
||||||
|
|||||||
235
lfs.c
235
lfs.c
@@ -414,6 +414,9 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t dir[2],
|
|||||||
lfs_mdir_t *parent);
|
lfs_mdir_t *parent);
|
||||||
static int lfs_fs_relocate(lfs_t *lfs,
|
static int lfs_fs_relocate(lfs_t *lfs,
|
||||||
const lfs_block_t oldpair[2], lfs_block_t newpair[2]);
|
const lfs_block_t oldpair[2], lfs_block_t newpair[2]);
|
||||||
|
int lfs_fs_traverseraw(lfs_t *lfs,
|
||||||
|
int (*cb)(void *data, lfs_block_t block), void *data,
|
||||||
|
bool includeorphans);
|
||||||
static int lfs_fs_forceconsistency(lfs_t *lfs);
|
static int lfs_fs_forceconsistency(lfs_t *lfs);
|
||||||
static int lfs_deinit(lfs_t *lfs);
|
static int lfs_deinit(lfs_t *lfs);
|
||||||
#ifdef LFS_MIGRATE
|
#ifdef LFS_MIGRATE
|
||||||
@@ -472,7 +475,7 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
|
|||||||
|
|
||||||
// find mask of free blocks from tree
|
// find mask of free blocks from tree
|
||||||
memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size);
|
memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size);
|
||||||
int err = lfs_fs_traverse(lfs, lfs_alloc_lookahead, lfs);
|
int err = lfs_fs_traverseraw(lfs, lfs_alloc_lookahead, lfs, true);
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -802,11 +805,13 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
|
|||||||
tag = lfs_frombe32(tag) ^ ptag;
|
tag = lfs_frombe32(tag) ^ ptag;
|
||||||
|
|
||||||
// next commit not yet programmed or we're not in valid range
|
// next commit not yet programmed or we're not in valid range
|
||||||
if (!lfs_tag_isvalid(tag) ||
|
if (!lfs_tag_isvalid(tag)) {
|
||||||
off + lfs_tag_dsize(tag) > lfs->cfg->block_size) {
|
|
||||||
dir->erased = (lfs_tag_type1(ptag) == LFS_TYPE_CRC &&
|
dir->erased = (lfs_tag_type1(ptag) == LFS_TYPE_CRC &&
|
||||||
dir->off % lfs->cfg->prog_size == 0);
|
dir->off % lfs->cfg->prog_size == 0);
|
||||||
break;
|
break;
|
||||||
|
} else if (off + lfs_tag_dsize(tag) > lfs->cfg->block_size) {
|
||||||
|
dir->erased = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptag = tag;
|
ptag = tag;
|
||||||
@@ -1230,6 +1235,7 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
|
|||||||
const lfs_off_t off1 = commit->off + sizeof(lfs_tag_t);
|
const lfs_off_t off1 = commit->off + sizeof(lfs_tag_t);
|
||||||
const lfs_off_t end = lfs_alignup(off1 + sizeof(uint32_t),
|
const lfs_off_t end = lfs_alignup(off1 + sizeof(uint32_t),
|
||||||
lfs->cfg->prog_size);
|
lfs->cfg->prog_size);
|
||||||
|
uint32_t ncrc = commit->crc;
|
||||||
|
|
||||||
// create crc tags to fill up remainder of commit, note that
|
// create crc tags to fill up remainder of commit, note that
|
||||||
// padding is not crcd, which lets fetches skip padding but
|
// padding is not crcd, which lets fetches skip padding but
|
||||||
@@ -1266,6 +1272,7 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ncrc = commit->crc;
|
||||||
commit->off += sizeof(tag)+lfs_tag_size(tag);
|
commit->off += sizeof(tag)+lfs_tag_size(tag);
|
||||||
commit->ptag = tag ^ ((lfs_tag_t)reset << 31);
|
commit->ptag = tag ^ ((lfs_tag_t)reset << 31);
|
||||||
commit->crc = LFS_BLOCK_NULL; // reset crc for next "commit"
|
commit->crc = LFS_BLOCK_NULL; // reset crc for next "commit"
|
||||||
@@ -1292,6 +1299,12 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check against written crc to detect if block is readonly
|
||||||
|
// (we may pick up old commits)
|
||||||
|
if (i == noff && crc != ncrc) {
|
||||||
|
return LFS_ERR_CORRUPT;
|
||||||
|
}
|
||||||
|
|
||||||
crc = lfs_crc(crc, &dat, 1);
|
crc = lfs_crc(crc, &dat, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1320,6 +1333,9 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// zero for reproducability in case initial block is unreadable
|
||||||
|
dir->rev = 0;
|
||||||
|
|
||||||
// rather than clobbering one of the blocks we just pretend
|
// rather than clobbering one of the blocks we just pretend
|
||||||
// the revision may be valid
|
// the revision may be valid
|
||||||
int err = lfs_bd_read(lfs,
|
int err = lfs_bd_read(lfs,
|
||||||
@@ -1420,9 +1436,9 @@ static int lfs_dir_compact(lfs_t *lfs,
|
|||||||
lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount,
|
lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount,
|
||||||
lfs_mdir_t *source, uint16_t begin, uint16_t end) {
|
lfs_mdir_t *source, uint16_t begin, uint16_t end) {
|
||||||
// save some state in case block is bad
|
// save some state in case block is bad
|
||||||
const lfs_block_t oldpair[2] = {dir->pair[1], dir->pair[0]};
|
const lfs_block_t oldpair[2] = {dir->pair[0], dir->pair[1]};
|
||||||
bool relocated = false;
|
bool relocated = false;
|
||||||
bool exhausted = false;
|
bool tired = false;
|
||||||
|
|
||||||
// should we split?
|
// should we split?
|
||||||
while (end - begin > 1) {
|
while (end - begin > 1) {
|
||||||
@@ -1469,8 +1485,14 @@ static int lfs_dir_compact(lfs_t *lfs,
|
|||||||
|
|
||||||
// increment revision count
|
// increment revision count
|
||||||
dir->rev += 1;
|
dir->rev += 1;
|
||||||
|
// If our revision count == n * block_cycles, we should force a relocation,
|
||||||
|
// this is how littlefs wear-levels at the metadata-pair level. Note that we
|
||||||
|
// actually use (block_cycles+1)|1, this is to avoid two corner cases:
|
||||||
|
// 1. block_cycles = 1, which would prevent relocations from terminating
|
||||||
|
// 2. block_cycles = 2n, which, due to aliasing, would only ever relocate
|
||||||
|
// one metadata block in the pair, effectively making this useless
|
||||||
if (lfs->cfg->block_cycles > 0 &&
|
if (lfs->cfg->block_cycles > 0 &&
|
||||||
(dir->rev % (lfs->cfg->block_cycles+1) == 0)) {
|
(dir->rev % ((lfs->cfg->block_cycles+1)|1) == 0)) {
|
||||||
if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) {
|
if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) {
|
||||||
// oh no! we're writing too much to the superblock,
|
// oh no! we're writing too much to the superblock,
|
||||||
// should we expand?
|
// should we expand?
|
||||||
@@ -1506,7 +1528,7 @@ static int lfs_dir_compact(lfs_t *lfs,
|
|||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
// we're writing too much, time to relocate
|
// we're writing too much, time to relocate
|
||||||
exhausted = true;
|
tired = true;
|
||||||
goto relocate;
|
goto relocate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1630,23 +1652,23 @@ relocate:
|
|||||||
// commit was corrupted, drop caches and prepare to relocate block
|
// commit was corrupted, drop caches and prepare to relocate block
|
||||||
relocated = true;
|
relocated = true;
|
||||||
lfs_cache_drop(lfs, &lfs->pcache);
|
lfs_cache_drop(lfs, &lfs->pcache);
|
||||||
if (!exhausted) {
|
if (!tired) {
|
||||||
LFS_DEBUG("Bad block at %"PRIx32, dir->pair[1]);
|
LFS_DEBUG("Bad block at %"PRIx32, dir->pair[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// can't relocate superblock, filesystem is now frozen
|
// can't relocate superblock, filesystem is now frozen
|
||||||
if (lfs_pair_cmp(oldpair, (const lfs_block_t[2]){0, 1}) == 0) {
|
if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) {
|
||||||
LFS_WARN("Superblock %"PRIx32" has become unwritable", oldpair[1]);
|
LFS_WARN("Superblock %"PRIx32" has become unwritable", dir->pair[1]);
|
||||||
return LFS_ERR_NOSPC;
|
return LFS_ERR_NOSPC;
|
||||||
}
|
}
|
||||||
|
|
||||||
// relocate half of pair
|
// relocate half of pair
|
||||||
int err = lfs_alloc(lfs, &dir->pair[1]);
|
int err = lfs_alloc(lfs, &dir->pair[1]);
|
||||||
if (err && (err != LFS_ERR_NOSPC || !exhausted)) {
|
if (err && (err != LFS_ERR_NOSPC || !tired)) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
exhausted = false;
|
tired = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1684,6 +1706,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// calculate changes to the directory
|
// calculate changes to the directory
|
||||||
|
lfs_mdir_t olddir = *dir;
|
||||||
bool hasdelete = false;
|
bool hasdelete = false;
|
||||||
for (int i = 0; i < attrcount; i++) {
|
for (int i = 0; i < attrcount; i++) {
|
||||||
if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_CREATE) {
|
if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_CREATE) {
|
||||||
@@ -1705,11 +1728,16 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
|
|||||||
lfs_mdir_t pdir;
|
lfs_mdir_t pdir;
|
||||||
int err = lfs_fs_pred(lfs, dir->pair, &pdir);
|
int err = lfs_fs_pred(lfs, dir->pair, &pdir);
|
||||||
if (err && err != LFS_ERR_NOENT) {
|
if (err && err != LFS_ERR_NOENT) {
|
||||||
|
*dir = olddir;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err != LFS_ERR_NOENT && pdir.split) {
|
if (err != LFS_ERR_NOENT && pdir.split) {
|
||||||
return lfs_dir_drop(lfs, &pdir, dir);
|
err = lfs_dir_drop(lfs, &pdir, dir);
|
||||||
|
if (err) {
|
||||||
|
*dir = olddir;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1737,6 +1765,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
|
|||||||
if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
|
if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
|
||||||
goto compact;
|
goto compact;
|
||||||
}
|
}
|
||||||
|
*dir = olddir;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1749,6 +1778,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
|
|||||||
if (!lfs_gstate_iszero(&delta)) {
|
if (!lfs_gstate_iszero(&delta)) {
|
||||||
err = lfs_dir_getgstate(lfs, dir, &delta);
|
err = lfs_dir_getgstate(lfs, dir, &delta);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
*dir = olddir;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1760,6 +1790,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
|
|||||||
if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
|
if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
|
||||||
goto compact;
|
goto compact;
|
||||||
}
|
}
|
||||||
|
*dir = olddir;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1770,6 +1801,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
|
|||||||
if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
|
if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
|
||||||
goto compact;
|
goto compact;
|
||||||
}
|
}
|
||||||
|
*dir = olddir;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1788,6 +1820,7 @@ compact:
|
|||||||
int err = lfs_dir_compact(lfs, dir, attrs, attrcount,
|
int err = lfs_dir_compact(lfs, dir, attrs, attrcount,
|
||||||
dir, 0, dir->count);
|
dir, 0, dir->count);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
*dir = olddir;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1800,9 +1833,8 @@ compact:
|
|||||||
// we need to copy the pair so they don't get clobbered if we refetch
|
// we need to copy the pair so they don't get clobbered if we refetch
|
||||||
// our mdir.
|
// our mdir.
|
||||||
for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) {
|
for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) {
|
||||||
if (&d->m != dir && lfs_pair_cmp(d->m.pair, dir->pair) == 0) {
|
if (&d->m != dir && lfs_pair_cmp(d->m.pair, olddir.pair) == 0) {
|
||||||
d->m = *dir;
|
d->m = *dir;
|
||||||
|
|
||||||
for (int i = 0; i < attrcount; i++) {
|
for (int i = 0; i < attrcount; i++) {
|
||||||
if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE &&
|
if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE &&
|
||||||
d->id == lfs_tag_id(attrs[i].tag)) {
|
d->id == lfs_tag_id(attrs[i].tag)) {
|
||||||
@@ -1825,9 +1857,8 @@ compact:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lfs_block_t pair[2] = {dir->pair[0], dir->pair[1]};
|
|
||||||
for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) {
|
for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) {
|
||||||
if (lfs_pair_cmp(d->m.pair, pair) == 0) {
|
if (lfs_pair_cmp(d->m.pair, olddir.pair) == 0) {
|
||||||
while (d->id >= d->m.count && d->m.split) {
|
while (d->id >= d->m.count && d->m.split) {
|
||||||
// we split and id is on tail now
|
// we split and id is on tail now
|
||||||
d->id -= d->m.count;
|
d->id -= d->m.count;
|
||||||
@@ -2204,16 +2235,16 @@ static int lfs_ctz_extend(lfs_t *lfs,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size -= 1;
|
lfs_size_t noff = size - 1;
|
||||||
lfs_off_t index = lfs_ctz_index(lfs, &size);
|
lfs_off_t index = lfs_ctz_index(lfs, &noff);
|
||||||
size += 1;
|
noff = noff + 1;
|
||||||
|
|
||||||
// just copy out the last block if it is incomplete
|
// just copy out the last block if it is incomplete
|
||||||
if (size != lfs->cfg->block_size) {
|
if (noff != lfs->cfg->block_size) {
|
||||||
for (lfs_off_t i = 0; i < size; i++) {
|
for (lfs_off_t i = 0; i < noff; i++) {
|
||||||
uint8_t data;
|
uint8_t data;
|
||||||
err = lfs_bd_read(lfs,
|
err = lfs_bd_read(lfs,
|
||||||
NULL, rcache, size-i,
|
NULL, rcache, noff-i,
|
||||||
head, i, &data, 1);
|
head, i, &data, 1);
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
@@ -2231,19 +2262,19 @@ static int lfs_ctz_extend(lfs_t *lfs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
*block = nblock;
|
*block = nblock;
|
||||||
*off = size;
|
*off = noff;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// append block
|
// append block
|
||||||
index += 1;
|
index += 1;
|
||||||
lfs_size_t skips = lfs_ctz(index) + 1;
|
lfs_size_t skips = lfs_ctz(index) + 1;
|
||||||
|
lfs_block_t nhead = head;
|
||||||
for (lfs_off_t i = 0; i < skips; i++) {
|
for (lfs_off_t i = 0; i < skips; i++) {
|
||||||
head = lfs_tole32(head);
|
nhead = lfs_tole32(nhead);
|
||||||
err = lfs_bd_prog(lfs, pcache, rcache, true,
|
err = lfs_bd_prog(lfs, pcache, rcache, true,
|
||||||
nblock, 4*i, &head, 4);
|
nblock, 4*i, &nhead, 4);
|
||||||
head = lfs_fromle32(head);
|
nhead = lfs_fromle32(nhead);
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err == LFS_ERR_CORRUPT) {
|
if (err == LFS_ERR_CORRUPT) {
|
||||||
goto relocate;
|
goto relocate;
|
||||||
@@ -2253,15 +2284,15 @@ static int lfs_ctz_extend(lfs_t *lfs,
|
|||||||
|
|
||||||
if (i != skips-1) {
|
if (i != skips-1) {
|
||||||
err = lfs_bd_read(lfs,
|
err = lfs_bd_read(lfs,
|
||||||
NULL, rcache, sizeof(head),
|
NULL, rcache, sizeof(nhead),
|
||||||
head, 4*i, &head, sizeof(head));
|
nhead, 4*i, &nhead, sizeof(nhead));
|
||||||
head = lfs_fromle32(head);
|
nhead = lfs_fromle32(nhead);
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LFS_ASSERT(head >= 2 && head <= lfs->cfg->block_count);
|
LFS_ASSERT(nhead >= 2 && nhead <= lfs->cfg->block_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
*block = nblock;
|
*block = nblock;
|
||||||
@@ -2677,66 +2708,52 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
|
|||||||
LFS_TRACE("lfs_file_sync(%p, %p)", (void*)lfs, (void*)file);
|
LFS_TRACE("lfs_file_sync(%p, %p)", (void*)lfs, (void*)file);
|
||||||
LFS_ASSERT(file->flags & LFS_F_OPENED);
|
LFS_ASSERT(file->flags & LFS_F_OPENED);
|
||||||
|
|
||||||
while (true) {
|
int err = lfs_file_flush(lfs, file);
|
||||||
int err = lfs_file_flush(lfs, file);
|
if (err) {
|
||||||
if (err) {
|
file->flags |= LFS_F_ERRED;
|
||||||
file->flags |= LFS_F_ERRED;
|
LFS_TRACE("lfs_file_sync -> %d", err);
|
||||||
LFS_TRACE("lfs_file_sync -> %d", err);
|
return err;
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((file->flags & LFS_F_DIRTY) &&
|
|
||||||
!(file->flags & LFS_F_ERRED) &&
|
|
||||||
!lfs_pair_isnull(file->m.pair)) {
|
|
||||||
// update dir entry
|
|
||||||
uint16_t type;
|
|
||||||
const void *buffer;
|
|
||||||
lfs_size_t size;
|
|
||||||
struct lfs_ctz ctz;
|
|
||||||
if (file->flags & LFS_F_INLINE) {
|
|
||||||
// inline the whole file
|
|
||||||
type = LFS_TYPE_INLINESTRUCT;
|
|
||||||
buffer = file->cache.buffer;
|
|
||||||
size = file->ctz.size;
|
|
||||||
} else {
|
|
||||||
// update the ctz reference
|
|
||||||
type = LFS_TYPE_CTZSTRUCT;
|
|
||||||
// copy ctz so alloc will work during a relocate
|
|
||||||
ctz = file->ctz;
|
|
||||||
lfs_ctz_tole32(&ctz);
|
|
||||||
buffer = &ctz;
|
|
||||||
size = sizeof(ctz);
|
|
||||||
}
|
|
||||||
|
|
||||||
// commit file data and attributes
|
|
||||||
err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS(
|
|
||||||
{LFS_MKTAG(type, file->id, size), buffer},
|
|
||||||
{LFS_MKTAG(LFS_FROM_USERATTRS, file->id,
|
|
||||||
file->cfg->attr_count), file->cfg->attrs}));
|
|
||||||
if (err) {
|
|
||||||
if (err == LFS_ERR_NOSPC && (file->flags & LFS_F_INLINE)) {
|
|
||||||
goto relocate;
|
|
||||||
}
|
|
||||||
file->flags |= LFS_F_ERRED;
|
|
||||||
LFS_TRACE("lfs_file_sync -> %d", err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
file->flags &= ~LFS_F_DIRTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
LFS_TRACE("lfs_file_sync -> %d", 0);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
relocate:
|
|
||||||
// inline file doesn't fit anymore
|
|
||||||
err = lfs_file_outline(lfs, file);
|
|
||||||
if (err) {
|
|
||||||
file->flags |= LFS_F_ERRED;
|
|
||||||
LFS_TRACE("lfs_file_sync -> %d", err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((file->flags & LFS_F_DIRTY) &&
|
||||||
|
!(file->flags & LFS_F_ERRED) &&
|
||||||
|
!lfs_pair_isnull(file->m.pair)) {
|
||||||
|
// update dir entry
|
||||||
|
uint16_t type;
|
||||||
|
const void *buffer;
|
||||||
|
lfs_size_t size;
|
||||||
|
struct lfs_ctz ctz;
|
||||||
|
if (file->flags & LFS_F_INLINE) {
|
||||||
|
// inline the whole file
|
||||||
|
type = LFS_TYPE_INLINESTRUCT;
|
||||||
|
buffer = file->cache.buffer;
|
||||||
|
size = file->ctz.size;
|
||||||
|
} else {
|
||||||
|
// update the ctz reference
|
||||||
|
type = LFS_TYPE_CTZSTRUCT;
|
||||||
|
// copy ctz so alloc will work during a relocate
|
||||||
|
ctz = file->ctz;
|
||||||
|
lfs_ctz_tole32(&ctz);
|
||||||
|
buffer = &ctz;
|
||||||
|
size = sizeof(ctz);
|
||||||
|
}
|
||||||
|
|
||||||
|
// commit file data and attributes
|
||||||
|
err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS(
|
||||||
|
{LFS_MKTAG(type, file->id, size), buffer},
|
||||||
|
{LFS_MKTAG(LFS_FROM_USERATTRS, file->id,
|
||||||
|
file->cfg->attr_count), file->cfg->attrs}));
|
||||||
|
if (err) {
|
||||||
|
file->flags |= LFS_F_ERRED;
|
||||||
|
LFS_TRACE("lfs_file_sync -> %d", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
file->flags &= ~LFS_F_DIRTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
LFS_TRACE("lfs_file_sync -> %d", 0);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
|
lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
|
||||||
@@ -3764,10 +3781,9 @@ int lfs_unmount(lfs_t *lfs) {
|
|||||||
|
|
||||||
|
|
||||||
/// Filesystem filesystem operations ///
|
/// Filesystem filesystem operations ///
|
||||||
int lfs_fs_traverse(lfs_t *lfs,
|
int lfs_fs_traverseraw(lfs_t *lfs,
|
||||||
int (*cb)(void *data, lfs_block_t block), void *data) {
|
int (*cb)(void *data, lfs_block_t block), void *data,
|
||||||
LFS_TRACE("lfs_fs_traverse(%p, %p, %p)",
|
bool includeorphans) {
|
||||||
(void*)lfs, (void*)(uintptr_t)cb, data);
|
|
||||||
// iterate over metadata pairs
|
// iterate over metadata pairs
|
||||||
lfs_mdir_t dir = {.tail = {0, 1}};
|
lfs_mdir_t dir = {.tail = {0, 1}};
|
||||||
|
|
||||||
@@ -3776,7 +3792,6 @@ int lfs_fs_traverse(lfs_t *lfs,
|
|||||||
if (lfs->lfs1) {
|
if (lfs->lfs1) {
|
||||||
int err = lfs1_traverse(lfs, cb, data);
|
int err = lfs1_traverse(lfs, cb, data);
|
||||||
if (err) {
|
if (err) {
|
||||||
LFS_TRACE("lfs_fs_traverse -> %d", err);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3789,7 +3804,6 @@ int lfs_fs_traverse(lfs_t *lfs,
|
|||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
int err = cb(data, dir.tail[i]);
|
int err = cb(data, dir.tail[i]);
|
||||||
if (err) {
|
if (err) {
|
||||||
LFS_TRACE("lfs_fs_traverse -> %d", err);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3797,7 +3811,6 @@ int lfs_fs_traverse(lfs_t *lfs,
|
|||||||
// iterate through ids in directory
|
// iterate through ids in directory
|
||||||
int err = lfs_dir_fetch(lfs, &dir, dir.tail);
|
int err = lfs_dir_fetch(lfs, &dir, dir.tail);
|
||||||
if (err) {
|
if (err) {
|
||||||
LFS_TRACE("lfs_fs_traverse -> %d", err);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3809,7 +3822,6 @@ int lfs_fs_traverse(lfs_t *lfs,
|
|||||||
if (tag == LFS_ERR_NOENT) {
|
if (tag == LFS_ERR_NOENT) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
LFS_TRACE("lfs_fs_traverse -> %"PRId32, tag);
|
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
lfs_ctz_fromle32(&ctz);
|
lfs_ctz_fromle32(&ctz);
|
||||||
@@ -3818,9 +3830,16 @@ int lfs_fs_traverse(lfs_t *lfs,
|
|||||||
err = lfs_ctz_traverse(lfs, NULL, &lfs->rcache,
|
err = lfs_ctz_traverse(lfs, NULL, &lfs->rcache,
|
||||||
ctz.head, ctz.size, cb, data);
|
ctz.head, ctz.size, cb, data);
|
||||||
if (err) {
|
if (err) {
|
||||||
LFS_TRACE("lfs_fs_traverse -> %d", err);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
} else if (includeorphans &&
|
||||||
|
lfs_tag_type3(tag) == LFS_TYPE_DIRSTRUCT) {
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
err = cb(data, (&ctz.head)[i]);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3835,7 +3854,6 @@ int lfs_fs_traverse(lfs_t *lfs,
|
|||||||
int err = lfs_ctz_traverse(lfs, &f->cache, &lfs->rcache,
|
int err = lfs_ctz_traverse(lfs, &f->cache, &lfs->rcache,
|
||||||
f->ctz.head, f->ctz.size, cb, data);
|
f->ctz.head, f->ctz.size, cb, data);
|
||||||
if (err) {
|
if (err) {
|
||||||
LFS_TRACE("lfs_fs_traverse -> %d", err);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3844,16 +3862,23 @@ int lfs_fs_traverse(lfs_t *lfs,
|
|||||||
int err = lfs_ctz_traverse(lfs, &f->cache, &lfs->rcache,
|
int err = lfs_ctz_traverse(lfs, &f->cache, &lfs->rcache,
|
||||||
f->block, f->pos, cb, data);
|
f->block, f->pos, cb, data);
|
||||||
if (err) {
|
if (err) {
|
||||||
LFS_TRACE("lfs_fs_traverse -> %d", err);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LFS_TRACE("lfs_fs_traverse -> %d", 0);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lfs_fs_traverse(lfs_t *lfs,
|
||||||
|
int (*cb)(void *data, lfs_block_t block), void *data) {
|
||||||
|
LFS_TRACE("lfs_fs_traverse(%p, %p, %p)",
|
||||||
|
(void*)lfs, (void*)(uintptr_t)cb, data);
|
||||||
|
int err = lfs_fs_traverseraw(lfs, cb, data, true);
|
||||||
|
LFS_TRACE("lfs_fs_traverse -> %d", 0);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int lfs_fs_pred(lfs_t *lfs,
|
static int lfs_fs_pred(lfs_t *lfs,
|
||||||
const lfs_block_t pair[2], lfs_mdir_t *pdir) {
|
const lfs_block_t pair[2], lfs_mdir_t *pdir) {
|
||||||
// iterate over all directory directory entries
|
// iterate over all directory directory entries
|
||||||
@@ -3902,7 +3927,9 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2],
|
|||||||
// use fetchmatch with callback to find pairs
|
// use fetchmatch with callback to find pairs
|
||||||
parent->tail[0] = 0;
|
parent->tail[0] = 0;
|
||||||
parent->tail[1] = 1;
|
parent->tail[1] = 1;
|
||||||
|
int i = 0;
|
||||||
while (!lfs_pair_isnull(parent->tail)) {
|
while (!lfs_pair_isnull(parent->tail)) {
|
||||||
|
i += 1;
|
||||||
lfs_stag_t tag = lfs_dir_fetchmatch(lfs, parent, parent->tail,
|
lfs_stag_t tag = lfs_dir_fetchmatch(lfs, parent, parent->tail,
|
||||||
LFS_MKTAG(0x7ff, 0, 0x3ff),
|
LFS_MKTAG(0x7ff, 0, 0x3ff),
|
||||||
LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8),
|
LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8),
|
||||||
@@ -4157,7 +4184,7 @@ static int lfs_fs_size_count(void *p, lfs_block_t block) {
|
|||||||
lfs_ssize_t lfs_fs_size(lfs_t *lfs) {
|
lfs_ssize_t lfs_fs_size(lfs_t *lfs) {
|
||||||
LFS_TRACE("lfs_fs_size(%p)", (void*)lfs);
|
LFS_TRACE("lfs_fs_size(%p)", (void*)lfs);
|
||||||
lfs_size_t size = 0;
|
lfs_size_t size = 0;
|
||||||
int err = lfs_fs_traverse(lfs, lfs_fs_size_count, &size);
|
int err = lfs_fs_traverseraw(lfs, lfs_fs_size_count, &size, false);
|
||||||
if (err) {
|
if (err) {
|
||||||
LFS_TRACE("lfs_fs_size -> %d", err);
|
LFS_TRACE("lfs_fs_size -> %d", err);
|
||||||
return err;
|
return err;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import struct
|
import struct
|
||||||
import binascii
|
import binascii
|
||||||
|
import sys
|
||||||
import itertools as it
|
import itertools as it
|
||||||
|
|
||||||
TAG_TYPES = {
|
TAG_TYPES = {
|
||||||
@@ -271,37 +272,39 @@ class MetadataPair:
|
|||||||
|
|
||||||
raise KeyError(gmask, gtag)
|
raise KeyError(gmask, gtag)
|
||||||
|
|
||||||
def _dump_tags(self, tags, truncate=True):
|
def _dump_tags(self, tags, f=sys.stdout, truncate=True):
|
||||||
sys.stdout.write("%-8s %-8s %-13s %4s %4s %s\n" % (
|
f.write("%-8s %-8s %-13s %4s %4s" % (
|
||||||
'off', 'tag', 'type', 'id', 'len',
|
'off', 'tag', 'type', 'id', 'len'))
|
||||||
'data (truncated)' if truncate else 12*' '+'data'))
|
if truncate:
|
||||||
|
f.write(' data (truncated)')
|
||||||
|
f.write('\n')
|
||||||
|
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
sys.stdout.write("%08x: %08x %-13s %4s %4s" % (
|
f.write("%08x: %08x %-13s %4s %4s" % (
|
||||||
tag.off, tag,
|
tag.off, tag,
|
||||||
tag.typerepr(), tag.idrepr(), tag.sizerepr()))
|
tag.typerepr(), tag.idrepr(), tag.sizerepr()))
|
||||||
if truncate:
|
if truncate:
|
||||||
sys.stdout.write(" %-23s %-8s\n" % (
|
f.write(" %-23s %-8s\n" % (
|
||||||
' '.join('%02x' % c for c in tag.data[:8]),
|
' '.join('%02x' % c for c in tag.data[:8]),
|
||||||
''.join(c if c >= ' ' and c <= '~' else '.'
|
''.join(c if c >= ' ' and c <= '~' else '.'
|
||||||
for c in map(chr, tag.data[:8]))))
|
for c in map(chr, tag.data[:8]))))
|
||||||
else:
|
else:
|
||||||
sys.stdout.write("\n")
|
f.write("\n")
|
||||||
for i in range(0, len(tag.data), 16):
|
for i in range(0, len(tag.data), 16):
|
||||||
sys.stdout.write("%08x: %-47s %-16s\n" % (
|
f.write(" %08x: %-47s %-16s\n" % (
|
||||||
tag.off+i,
|
tag.off+i,
|
||||||
' '.join('%02x' % c for c in tag.data[i:i+16]),
|
' '.join('%02x' % c for c in tag.data[i:i+16]),
|
||||||
''.join(c if c >= ' ' and c <= '~' else '.'
|
''.join(c if c >= ' ' and c <= '~' else '.'
|
||||||
for c in map(chr, tag.data[i:i+16]))))
|
for c in map(chr, tag.data[i:i+16]))))
|
||||||
|
|
||||||
def dump_tags(self, truncate=True):
|
def dump_tags(self, f=sys.stdout, truncate=True):
|
||||||
self._dump_tags(self.tags, truncate=truncate)
|
self._dump_tags(self.tags, f=f, truncate=truncate)
|
||||||
|
|
||||||
def dump_log(self, truncate=True):
|
def dump_log(self, f=sys.stdout, truncate=True):
|
||||||
self._dump_tags(self.log, truncate=truncate)
|
self._dump_tags(self.log, f=f, truncate=truncate)
|
||||||
|
|
||||||
def dump_all(self, truncate=True):
|
def dump_all(self, f=sys.stdout, truncate=True):
|
||||||
self._dump_tags(self.all_, truncate=truncate)
|
self._dump_tags(self.all_, f=f, truncate=truncate)
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
blocks = []
|
blocks = []
|
||||||
@@ -337,10 +340,10 @@ if __name__ == "__main__":
|
|||||||
help="First block address for finding the metadata pair.")
|
help="First block address for finding the metadata pair.")
|
||||||
parser.add_argument('block2', nargs='?', type=lambda x: int(x, 0),
|
parser.add_argument('block2', nargs='?', type=lambda x: int(x, 0),
|
||||||
help="Second block address for finding the metadata pair.")
|
help="Second block address for finding the metadata pair.")
|
||||||
parser.add_argument('-a', '--all', action='store_true',
|
|
||||||
help="Show all tags in log, included tags in corrupted commits.")
|
|
||||||
parser.add_argument('-l', '--log', action='store_true',
|
parser.add_argument('-l', '--log', action='store_true',
|
||||||
help="Show tags in log.")
|
help="Show tags in log.")
|
||||||
|
parser.add_argument('-a', '--all', action='store_true',
|
||||||
|
help="Show all tags in log, included tags in corrupted commits.")
|
||||||
parser.add_argument('-T', '--no-truncate', action='store_true',
|
parser.add_argument('-T', '--no-truncate', action='store_true',
|
||||||
help="Don't truncate large amounts of data in tags.")
|
help="Don't truncate large amounts of data.")
|
||||||
sys.exit(main(parser.parse_args()))
|
sys.exit(main(parser.parse_args()))
|
||||||
|
|||||||
@@ -13,45 +13,6 @@ def popc(x):
|
|||||||
def ctz(x):
|
def ctz(x):
|
||||||
return len(bin(x)) - len(bin(x).rstrip('0'))
|
return len(bin(x)) - len(bin(x).rstrip('0'))
|
||||||
|
|
||||||
def dumptags(args, mdir, f):
|
|
||||||
if args.all:
|
|
||||||
tags = mdir.all_
|
|
||||||
elif args.log:
|
|
||||||
tags = mdir.log
|
|
||||||
else:
|
|
||||||
tags = mdir.tags
|
|
||||||
|
|
||||||
for k, tag in enumerate(tags):
|
|
||||||
f.write("tag %08x %s" % (tag, tag.typerepr()))
|
|
||||||
if tag.id != 0x3ff:
|
|
||||||
f.write(" id %d" % tag.id)
|
|
||||||
if tag.size != 0x3ff:
|
|
||||||
f.write(" size %d" % tag.size)
|
|
||||||
if tag.is_('name'):
|
|
||||||
f.write(" name %s" %
|
|
||||||
json.dumps(tag.data.decode('utf8')))
|
|
||||||
if tag.is_('dirstruct'):
|
|
||||||
f.write(" dir {%#x, %#x}" % struct.unpack(
|
|
||||||
'<II', tag.data[:8].ljust(8, b'\xff')))
|
|
||||||
if tag.is_('ctzstruct'):
|
|
||||||
f.write(" ctz {%#x} size %d" % struct.unpack(
|
|
||||||
'<II', tag.data[:8].ljust(8, b'\xff')))
|
|
||||||
if tag.is_('inlinestruct'):
|
|
||||||
f.write(" inline size %d" % tag.size)
|
|
||||||
if tag.is_('gstate'):
|
|
||||||
f.write(" 0x%s" % ''.join('%02x' % c for c in tag.data))
|
|
||||||
if tag.is_('tail'):
|
|
||||||
f.write(" tail {%#x, %#x}" % struct.unpack(
|
|
||||||
'<II', tag.data[:8].ljust(8, b'\xff')))
|
|
||||||
f.write("\n")
|
|
||||||
|
|
||||||
if args.data:
|
|
||||||
for i in range(0, len(tag.data), 16):
|
|
||||||
f.write(" %-47s %-16s\n" % (
|
|
||||||
' '.join('%02x' % c for c in tag.data[i:i+16]),
|
|
||||||
''.join(c if c >= ' ' and c <= '~' else '.'
|
|
||||||
for c in map(chr, tag.data[i:i+16]))))
|
|
||||||
|
|
||||||
def dumpentries(args, mdir, f):
|
def dumpentries(args, mdir, f):
|
||||||
for k, id_ in enumerate(mdir.ids):
|
for k, id_ in enumerate(mdir.ids):
|
||||||
name = mdir[Tag('name', id_, 0)]
|
name = mdir[Tag('name', id_, 0)]
|
||||||
@@ -72,8 +33,8 @@ def dumpentries(args, mdir, f):
|
|||||||
|
|
||||||
if args.data and struct_.is_('inlinestruct'):
|
if args.data and struct_.is_('inlinestruct'):
|
||||||
for i in range(0, len(struct_.data), 16):
|
for i in range(0, len(struct_.data), 16):
|
||||||
f.write(" %-47s %-16s\n" % (
|
f.write(" %08x: %-47s %-16s\n" % (
|
||||||
' '.join('%02x' % c for c in struct_.data[i:i+16]),
|
i, ' '.join('%02x' % c for c in struct_.data[i:i+16]),
|
||||||
''.join(c if c >= ' ' and c <= '~' else '.'
|
''.join(c if c >= ' ' and c <= '~' else '.'
|
||||||
for c in map(chr, struct_.data[i:i+16]))))
|
for c in map(chr, struct_.data[i:i+16]))))
|
||||||
elif args.data and struct_.is_('ctzstruct'):
|
elif args.data and struct_.is_('ctzstruct'):
|
||||||
@@ -95,8 +56,8 @@ def dumpentries(args, mdir, f):
|
|||||||
it.chain.from_iterable(reversed(data)), size))
|
it.chain.from_iterable(reversed(data)), size))
|
||||||
for i in range(0, min(len(data), 256)
|
for i in range(0, min(len(data), 256)
|
||||||
if not args.no_truncate else len(data), 16):
|
if not args.no_truncate else len(data), 16):
|
||||||
f.write(" %-47s %-16s\n" % (
|
f.write(" %08x: %-47s %-16s\n" % (
|
||||||
' '.join('%02x' % c for c in data[i:i+16]),
|
i, ' '.join('%02x' % c for c in data[i:i+16]),
|
||||||
''.join(c if c >= ' ' and c <= '~' else '.'
|
''.join(c if c >= ' ' and c <= '~' else '.'
|
||||||
for c in map(chr, data[i:i+16]))))
|
for c in map(chr, data[i:i+16]))))
|
||||||
|
|
||||||
@@ -118,9 +79,17 @@ def main(args):
|
|||||||
superblock = None
|
superblock = None
|
||||||
gstate = b''
|
gstate = b''
|
||||||
mdirs = []
|
mdirs = []
|
||||||
|
cycle = False
|
||||||
tail = (args.block1, args.block2)
|
tail = (args.block1, args.block2)
|
||||||
hard = False
|
hard = False
|
||||||
while True:
|
while True:
|
||||||
|
for m in it.chain((m for d in dirs for m in d), mdirs):
|
||||||
|
if set(m.blocks) == set(tail):
|
||||||
|
# cycle detected
|
||||||
|
cycle = m.blocks
|
||||||
|
if cycle:
|
||||||
|
break
|
||||||
|
|
||||||
# load mdir
|
# load mdir
|
||||||
data = []
|
data = []
|
||||||
blocks = {}
|
blocks = {}
|
||||||
@@ -129,6 +98,7 @@ def main(args):
|
|||||||
data.append(f.read(args.block_size)
|
data.append(f.read(args.block_size)
|
||||||
.ljust(args.block_size, b'\xff'))
|
.ljust(args.block_size, b'\xff'))
|
||||||
blocks[id(data[-1])] = block
|
blocks[id(data[-1])] = block
|
||||||
|
|
||||||
mdir = MetadataPair(data)
|
mdir = MetadataPair(data)
|
||||||
mdir.blocks = tuple(blocks[id(p.data)] for p in mdir.pair)
|
mdir.blocks = tuple(blocks[id(p.data)] for p in mdir.pair)
|
||||||
|
|
||||||
@@ -171,7 +141,7 @@ def main(args):
|
|||||||
# find paths
|
# find paths
|
||||||
dirtable = {}
|
dirtable = {}
|
||||||
for dir in dirs:
|
for dir in dirs:
|
||||||
dirtable[tuple(sorted(dir[0].blocks))] = dir
|
dirtable[frozenset(dir[0].blocks)] = dir
|
||||||
|
|
||||||
pending = [("/", dirs[0])]
|
pending = [("/", dirs[0])]
|
||||||
while pending:
|
while pending:
|
||||||
@@ -183,7 +153,7 @@ def main(args):
|
|||||||
npath = tag.data.decode('utf8')
|
npath = tag.data.decode('utf8')
|
||||||
dirstruct = mdir[Tag('dirstruct', tag.id, 0)]
|
dirstruct = mdir[Tag('dirstruct', tag.id, 0)]
|
||||||
nblocks = struct.unpack('<II', dirstruct.data)
|
nblocks = struct.unpack('<II', dirstruct.data)
|
||||||
nmdir = dirtable[tuple(sorted(nblocks))]
|
nmdir = dirtable[frozenset(nblocks)]
|
||||||
pending.append(((path + '/' + npath), nmdir))
|
pending.append(((path + '/' + npath), nmdir))
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
@@ -230,8 +200,12 @@ def main(args):
|
|||||||
' (corrupted)' if not mdir else ''))
|
' (corrupted)' if not mdir else ''))
|
||||||
|
|
||||||
f = io.StringIO()
|
f = io.StringIO()
|
||||||
if args.tags or args.all or args.log:
|
if args.tags:
|
||||||
dumptags(args, mdir, f)
|
mdir.dump_tags(f, truncate=not args.no_truncate)
|
||||||
|
elif args.log:
|
||||||
|
mdir.dump_log(f, truncate=not args.no_truncate)
|
||||||
|
elif args.all:
|
||||||
|
mdir.dump_all(f, truncate=not args.no_truncate)
|
||||||
else:
|
else:
|
||||||
dumpentries(args, mdir, f)
|
dumpentries(args, mdir, f)
|
||||||
|
|
||||||
@@ -243,7 +217,15 @@ def main(args):
|
|||||||
'|',
|
'|',
|
||||||
line))
|
line))
|
||||||
|
|
||||||
return 0 if all(mdir for dir in dirs for mdir in dir) else 1
|
if cycle:
|
||||||
|
print("*** cycle detected! -> {%#x, %#x} ***" % (cycle[0], cycle[1]))
|
||||||
|
|
||||||
|
if cycle:
|
||||||
|
return 2
|
||||||
|
elif not all(mdir for dir in dirs for mdir in dir):
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
return 0;
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import argparse
|
import argparse
|
||||||
@@ -268,12 +250,12 @@ if __name__ == "__main__":
|
|||||||
help="Show contents of metadata-pairs/directories.")
|
help="Show contents of metadata-pairs/directories.")
|
||||||
parser.add_argument('-t', '--tags', action='store_true',
|
parser.add_argument('-t', '--tags', action='store_true',
|
||||||
help="Show metadata tags instead of reconstructing entries.")
|
help="Show metadata tags instead of reconstructing entries.")
|
||||||
parser.add_argument('-a', '--all', action='store_true',
|
|
||||||
help="Show all tags in log, included tags in corrupted commits.")
|
|
||||||
parser.add_argument('-l', '--log', action='store_true',
|
parser.add_argument('-l', '--log', action='store_true',
|
||||||
help="Show tags in log.")
|
help="Show tags in log.")
|
||||||
|
parser.add_argument('-a', '--all', action='store_true',
|
||||||
|
help="Show all tags in log, included tags in corrupted commits.")
|
||||||
parser.add_argument('-d', '--data', action='store_true',
|
parser.add_argument('-d', '--data', action='store_true',
|
||||||
help="Also show the raw contents of files/attrs/tags.")
|
help="Also show the raw contents of files/attrs/tags.")
|
||||||
parser.add_argument('-T', '--no-truncate', action='store_true',
|
parser.add_argument('-T', '--no-truncate', action='store_true',
|
||||||
help="Don't truncate large amounts of data in files.")
|
help="Don't truncate large amounts of data.")
|
||||||
sys.exit(main(parser.parse_args()))
|
sys.exit(main(parser.parse_args()))
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ DEFINES = {
|
|||||||
'LFS_LOOKAHEAD_SIZE': 16,
|
'LFS_LOOKAHEAD_SIZE': 16,
|
||||||
'LFS_ERASE_VALUE': 0xff,
|
'LFS_ERASE_VALUE': 0xff,
|
||||||
'LFS_ERASE_CYCLES': 0,
|
'LFS_ERASE_CYCLES': 0,
|
||||||
'LFS_BADBLOCK_BEHAVIOR': 'LFS_TESTBD_BADBLOCK_NOPROG',
|
'LFS_BADBLOCK_BEHAVIOR': 'LFS_TESTBD_BADBLOCK_PROGERROR',
|
||||||
}
|
}
|
||||||
PROLOGUE = """
|
PROLOGUE = """
|
||||||
// prologue
|
// prologue
|
||||||
@@ -182,7 +182,19 @@ class TestCase:
|
|||||||
elif args.get('no_internal', False) and self.in_ is not None:
|
elif args.get('no_internal', False) and self.in_ is not None:
|
||||||
return False
|
return False
|
||||||
elif self.if_ is not None:
|
elif self.if_ is not None:
|
||||||
return eval(self.if_, None, self.defines.copy())
|
if_ = self.if_
|
||||||
|
while True:
|
||||||
|
for k, v in self.defines.items():
|
||||||
|
if k in if_:
|
||||||
|
if_ = if_.replace(k, '(%s)' % v)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
if_ = (
|
||||||
|
re.sub('(\&\&|\?)', ' and ',
|
||||||
|
re.sub('(\|\||:)', ' or ',
|
||||||
|
re.sub('!(?!=)', ' not ', if_))))
|
||||||
|
return eval(if_)
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -235,33 +247,37 @@ class TestCase:
|
|||||||
mpty = os.fdopen(mpty, 'r', 1)
|
mpty = os.fdopen(mpty, 'r', 1)
|
||||||
stdout = []
|
stdout = []
|
||||||
assert_ = None
|
assert_ = None
|
||||||
while True:
|
try:
|
||||||
try:
|
while True:
|
||||||
line = mpty.readline()
|
|
||||||
except OSError as e:
|
|
||||||
if e.errno == errno.EIO:
|
|
||||||
break
|
|
||||||
raise
|
|
||||||
stdout.append(line)
|
|
||||||
if args.get('verbose', False):
|
|
||||||
sys.stdout.write(line)
|
|
||||||
# intercept asserts
|
|
||||||
m = re.match(
|
|
||||||
'^{0}([^:]+):(\d+):(?:\d+:)?{0}{1}:{0}(.*)$'
|
|
||||||
.format('(?:\033\[[\d;]*.| )*', 'assert'),
|
|
||||||
line)
|
|
||||||
if m and assert_ is None:
|
|
||||||
try:
|
try:
|
||||||
with open(m.group(1)) as f:
|
line = mpty.readline()
|
||||||
lineno = int(m.group(2))
|
except OSError as e:
|
||||||
line = next(it.islice(f, lineno-1, None)).strip('\n')
|
if e.errno == errno.EIO:
|
||||||
assert_ = {
|
break
|
||||||
'path': m.group(1),
|
raise
|
||||||
'line': line,
|
stdout.append(line)
|
||||||
'lineno': lineno,
|
if args.get('verbose', False):
|
||||||
'message': m.group(3)}
|
sys.stdout.write(line)
|
||||||
except:
|
# intercept asserts
|
||||||
pass
|
m = re.match(
|
||||||
|
'^{0}([^:]+):(\d+):(?:\d+:)?{0}{1}:{0}(.*)$'
|
||||||
|
.format('(?:\033\[[\d;]*.| )*', 'assert'),
|
||||||
|
line)
|
||||||
|
if m and assert_ is None:
|
||||||
|
try:
|
||||||
|
with open(m.group(1)) as f:
|
||||||
|
lineno = int(m.group(2))
|
||||||
|
line = (next(it.islice(f, lineno-1, None))
|
||||||
|
.strip('\n'))
|
||||||
|
assert_ = {
|
||||||
|
'path': m.group(1),
|
||||||
|
'line': line,
|
||||||
|
'lineno': lineno,
|
||||||
|
'message': m.group(3)}
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise TestFailure(self, 1, stdout, None)
|
||||||
proc.wait()
|
proc.wait()
|
||||||
|
|
||||||
# did we pass?
|
# did we pass?
|
||||||
@@ -654,6 +670,10 @@ def main(**args):
|
|||||||
if filtered != sum(len(suite.perms) for suite in suites):
|
if filtered != sum(len(suite.perms) for suite in suites):
|
||||||
print('filtered down to %d permutations' % filtered)
|
print('filtered down to %d permutations' % filtered)
|
||||||
|
|
||||||
|
# only requested to build?
|
||||||
|
if args.get('build', False):
|
||||||
|
return 0
|
||||||
|
|
||||||
print('====== testing ======')
|
print('====== testing ======')
|
||||||
try:
|
try:
|
||||||
for suite in suites:
|
for suite in suites:
|
||||||
@@ -678,18 +698,19 @@ def main(**args):
|
|||||||
perm=perm, path=perm.suite.path, lineno=perm.lineno,
|
perm=perm, path=perm.suite.path, lineno=perm.lineno,
|
||||||
returncode=perm.result.returncode or 0))
|
returncode=perm.result.returncode or 0))
|
||||||
if perm.result.stdout:
|
if perm.result.stdout:
|
||||||
for line in (perm.result.stdout
|
if perm.result.assert_:
|
||||||
if not perm.result.assert_
|
stdout = perm.result.stdout[:-1]
|
||||||
else perm.result.stdout[:-1]):
|
else:
|
||||||
|
stdout = perm.result.stdout
|
||||||
|
if (not args.get('verbose', False) and len(stdout) > 5):
|
||||||
|
sys.stdout.write('...\n')
|
||||||
|
for line in stdout[-5:]:
|
||||||
sys.stdout.write(line)
|
sys.stdout.write(line)
|
||||||
if perm.result.assert_:
|
if perm.result.assert_:
|
||||||
sys.stdout.write(
|
sys.stdout.write(
|
||||||
"\033[01m{path}:{lineno}:\033[01;31massert:\033[m "
|
"\033[01m{path}:{lineno}:\033[01;31massert:\033[m "
|
||||||
"{message}\n{line}\n".format(
|
"{message}\n{line}\n".format(
|
||||||
**perm.result.assert_))
|
**perm.result.assert_))
|
||||||
else:
|
|
||||||
for line in perm.result.stdout:
|
|
||||||
sys.stdout.write(line)
|
|
||||||
sys.stdout.write('\n')
|
sys.stdout.write('\n')
|
||||||
failed += 1
|
failed += 1
|
||||||
|
|
||||||
@@ -728,6 +749,8 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument('-p', '--persist', choices=['erase', 'noerase'],
|
parser.add_argument('-p', '--persist', choices=['erase', 'noerase'],
|
||||||
nargs='?', const='erase',
|
nargs='?', const='erase',
|
||||||
help="Store disk image in a file.")
|
help="Store disk image in a file.")
|
||||||
|
parser.add_argument('-b', '--build', action='store_true',
|
||||||
|
help="Only build the tests, do not execute.")
|
||||||
parser.add_argument('-g', '--gdb', choices=['init', 'start', 'assert'],
|
parser.add_argument('-g', '--gdb', choices=['init', 'start', 'assert'],
|
||||||
nargs='?', const='assert',
|
nargs='?', const='assert',
|
||||||
help="Drop into gdb on test failure.")
|
help="Drop into gdb on test failure.")
|
||||||
|
|||||||
@@ -329,7 +329,7 @@ code = '''
|
|||||||
[[case]] # chained dir exhaustion test
|
[[case]] # chained dir exhaustion test
|
||||||
define.LFS_BLOCK_SIZE = 512
|
define.LFS_BLOCK_SIZE = 512
|
||||||
define.LFS_BLOCK_COUNT = 1024
|
define.LFS_BLOCK_COUNT = 1024
|
||||||
if = 'LFS_BLOCK_SIZE == 512 and LFS_BLOCK_COUNT == 1024'
|
if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024'
|
||||||
code = '''
|
code = '''
|
||||||
lfs_format(&lfs, &cfg) => 0;
|
lfs_format(&lfs, &cfg) => 0;
|
||||||
lfs_mount(&lfs, &cfg) => 0;
|
lfs_mount(&lfs, &cfg) => 0;
|
||||||
@@ -400,7 +400,7 @@ code = '''
|
|||||||
[[case]] # split dir test
|
[[case]] # split dir test
|
||||||
define.LFS_BLOCK_SIZE = 512
|
define.LFS_BLOCK_SIZE = 512
|
||||||
define.LFS_BLOCK_COUNT = 1024
|
define.LFS_BLOCK_COUNT = 1024
|
||||||
if = 'LFS_BLOCK_SIZE == 512 and LFS_BLOCK_COUNT == 1024'
|
if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024'
|
||||||
code = '''
|
code = '''
|
||||||
lfs_format(&lfs, &cfg) => 0;
|
lfs_format(&lfs, &cfg) => 0;
|
||||||
lfs_mount(&lfs, &cfg) => 0;
|
lfs_mount(&lfs, &cfg) => 0;
|
||||||
@@ -445,7 +445,7 @@ code = '''
|
|||||||
[[case]] # outdated lookahead test
|
[[case]] # outdated lookahead test
|
||||||
define.LFS_BLOCK_SIZE = 512
|
define.LFS_BLOCK_SIZE = 512
|
||||||
define.LFS_BLOCK_COUNT = 1024
|
define.LFS_BLOCK_COUNT = 1024
|
||||||
if = 'LFS_BLOCK_SIZE == 512 and LFS_BLOCK_COUNT == 1024'
|
if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024'
|
||||||
code = '''
|
code = '''
|
||||||
lfs_format(&lfs, &cfg) => 0;
|
lfs_format(&lfs, &cfg) => 0;
|
||||||
lfs_mount(&lfs, &cfg) => 0;
|
lfs_mount(&lfs, &cfg) => 0;
|
||||||
@@ -510,7 +510,7 @@ code = '''
|
|||||||
[[case]] # outdated lookahead and split dir test
|
[[case]] # outdated lookahead and split dir test
|
||||||
define.LFS_BLOCK_SIZE = 512
|
define.LFS_BLOCK_SIZE = 512
|
||||||
define.LFS_BLOCK_COUNT = 1024
|
define.LFS_BLOCK_COUNT = 1024
|
||||||
if = 'LFS_BLOCK_SIZE == 512 and LFS_BLOCK_COUNT == 1024'
|
if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024'
|
||||||
code = '''
|
code = '''
|
||||||
lfs_format(&lfs, &cfg) => 0;
|
lfs_format(&lfs, &cfg) => 0;
|
||||||
lfs_mount(&lfs, &cfg) => 0;
|
lfs_mount(&lfs, &cfg) => 0;
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
[[case]] # single bad blocks
|
[[case]] # single bad blocks
|
||||||
|
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
|
||||||
define.LFS_ERASE_CYCLES = 0xffffffff
|
define.LFS_ERASE_CYCLES = 0xffffffff
|
||||||
define.LFS_BADBLOCK_BEHAVIOR = 'LFS_TESTBD_BADBLOCK_NOPROG'
|
define.LFS_ERASE_VALUE = [0x00, 0xff, -1]
|
||||||
|
define.LFS_BADBLOCK_BEHAVIOR = [
|
||||||
|
'LFS_TESTBD_BADBLOCK_PROGERROR',
|
||||||
|
'LFS_TESTBD_BADBLOCK_ERASEERROR',
|
||||||
|
'LFS_TESTBD_BADBLOCK_READERROR',
|
||||||
|
'LFS_TESTBD_BADBLOCK_PROGNOOP',
|
||||||
|
'LFS_TESTBD_BADBLOCK_ERASENOOP',
|
||||||
|
]
|
||||||
define.NAMEMULT = 64
|
define.NAMEMULT = 64
|
||||||
define.FILEMULT = 1
|
define.FILEMULT = 1
|
||||||
code = '''
|
code = '''
|
||||||
@@ -64,144 +72,16 @@ code = '''
|
|||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
|
|
||||||
[[case]] # single persistent blocks (can't erase)
|
|
||||||
define.LFS_ERASE_CYCLES = 0xffffffff
|
|
||||||
define.LFS_BADBLOCK_BEHAVIOR = 'LFS_TESTBD_BADBLOCK_NOERASE'
|
|
||||||
define.NAMEMULT = 64
|
|
||||||
define.FILEMULT = 1
|
|
||||||
code = '''
|
|
||||||
for (lfs_block_t badblock = 2; badblock < LFS_BLOCK_COUNT; badblock++) {
|
|
||||||
lfs_testbd_setwear(&cfg, badblock-1, 0) => 0;
|
|
||||||
lfs_testbd_setwear(&cfg, badblock, 0xffffffff) => 0;
|
|
||||||
|
|
||||||
lfs_format(&lfs, &cfg) => 0;
|
|
||||||
|
|
||||||
lfs_mount(&lfs, &cfg) => 0;
|
|
||||||
for (int i = 1; i < 10; i++) {
|
|
||||||
for (int j = 0; j < NAMEMULT; j++) {
|
|
||||||
buffer[j] = '0'+i;
|
|
||||||
}
|
|
||||||
buffer[NAMEMULT] = '\0';
|
|
||||||
lfs_mkdir(&lfs, (char*)buffer) => 0;
|
|
||||||
|
|
||||||
buffer[NAMEMULT] = '/';
|
|
||||||
for (int j = 0; j < NAMEMULT; j++) {
|
|
||||||
buffer[j+NAMEMULT+1] = '0'+i;
|
|
||||||
}
|
|
||||||
buffer[2*NAMEMULT+1] = '\0';
|
|
||||||
lfs_file_open(&lfs, &file, (char*)buffer,
|
|
||||||
LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
|
||||||
|
|
||||||
size = NAMEMULT;
|
|
||||||
for (int j = 0; j < i*FILEMULT; j++) {
|
|
||||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
|
||||||
}
|
|
||||||
|
|
||||||
lfs_file_close(&lfs, &file) => 0;
|
|
||||||
}
|
|
||||||
lfs_unmount(&lfs) => 0;
|
|
||||||
|
|
||||||
lfs_mount(&lfs, &cfg) => 0;
|
|
||||||
for (int i = 1; i < 10; i++) {
|
|
||||||
for (int j = 0; j < NAMEMULT; j++) {
|
|
||||||
buffer[j] = '0'+i;
|
|
||||||
}
|
|
||||||
buffer[NAMEMULT] = '\0';
|
|
||||||
lfs_stat(&lfs, (char*)buffer, &info) => 0;
|
|
||||||
info.type => LFS_TYPE_DIR;
|
|
||||||
|
|
||||||
buffer[NAMEMULT] = '/';
|
|
||||||
for (int j = 0; j < NAMEMULT; j++) {
|
|
||||||
buffer[j+NAMEMULT+1] = '0'+i;
|
|
||||||
}
|
|
||||||
buffer[2*NAMEMULT+1] = '\0';
|
|
||||||
lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0;
|
|
||||||
|
|
||||||
size = NAMEMULT;
|
|
||||||
for (int j = 0; j < i*FILEMULT; j++) {
|
|
||||||
uint8_t rbuffer[1024];
|
|
||||||
lfs_file_read(&lfs, &file, rbuffer, size) => size;
|
|
||||||
memcmp(buffer, rbuffer, size) => 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lfs_file_close(&lfs, &file) => 0;
|
|
||||||
}
|
|
||||||
lfs_unmount(&lfs) => 0;
|
|
||||||
}
|
|
||||||
'''
|
|
||||||
|
|
||||||
[[case]] # single unreadable blocks (can't read)
|
|
||||||
define.LFS_ERASE_CYCLES = 0xffffffff
|
|
||||||
define.LFS_BADBLOCK_BEHAVIOR = 'LFS_TESTBD_BADBLOCK_NOREAD'
|
|
||||||
define.NAMEMULT = 64
|
|
||||||
define.FILEMULT = 1
|
|
||||||
code = '''
|
|
||||||
for (lfs_block_t badblock = 2; badblock < LFS_BLOCK_COUNT/2; badblock++) {
|
|
||||||
lfs_testbd_setwear(&cfg, badblock-1, 0) => 0;
|
|
||||||
lfs_testbd_setwear(&cfg, badblock, 0xffffffff) => 0;
|
|
||||||
|
|
||||||
lfs_format(&lfs, &cfg) => 0;
|
|
||||||
|
|
||||||
lfs_mount(&lfs, &cfg) => 0;
|
|
||||||
for (int i = 1; i < 10; i++) {
|
|
||||||
for (int j = 0; j < NAMEMULT; j++) {
|
|
||||||
buffer[j] = '0'+i;
|
|
||||||
}
|
|
||||||
buffer[NAMEMULT] = '\0';
|
|
||||||
lfs_mkdir(&lfs, (char*)buffer) => 0;
|
|
||||||
|
|
||||||
buffer[NAMEMULT] = '/';
|
|
||||||
for (int j = 0; j < NAMEMULT; j++) {
|
|
||||||
buffer[j+NAMEMULT+1] = '0'+i;
|
|
||||||
}
|
|
||||||
buffer[2*NAMEMULT+1] = '\0';
|
|
||||||
lfs_file_open(&lfs, &file, (char*)buffer,
|
|
||||||
LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
|
||||||
|
|
||||||
size = NAMEMULT;
|
|
||||||
for (int j = 0; j < i*FILEMULT; j++) {
|
|
||||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
|
||||||
}
|
|
||||||
|
|
||||||
lfs_file_close(&lfs, &file) => 0;
|
|
||||||
}
|
|
||||||
lfs_unmount(&lfs) => 0;
|
|
||||||
|
|
||||||
lfs_mount(&lfs, &cfg) => 0;
|
|
||||||
for (int i = 1; i < 10; i++) {
|
|
||||||
for (int j = 0; j < NAMEMULT; j++) {
|
|
||||||
buffer[j] = '0'+i;
|
|
||||||
}
|
|
||||||
buffer[NAMEMULT] = '\0';
|
|
||||||
lfs_stat(&lfs, (char*)buffer, &info) => 0;
|
|
||||||
info.type => LFS_TYPE_DIR;
|
|
||||||
|
|
||||||
buffer[NAMEMULT] = '/';
|
|
||||||
for (int j = 0; j < NAMEMULT; j++) {
|
|
||||||
buffer[j+NAMEMULT+1] = '0'+i;
|
|
||||||
}
|
|
||||||
buffer[2*NAMEMULT+1] = '\0';
|
|
||||||
lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0;
|
|
||||||
|
|
||||||
size = NAMEMULT;
|
|
||||||
for (int j = 0; j < i*FILEMULT; j++) {
|
|
||||||
uint8_t rbuffer[1024];
|
|
||||||
lfs_file_read(&lfs, &file, rbuffer, size) => size;
|
|
||||||
memcmp(buffer, rbuffer, size) => 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lfs_file_close(&lfs, &file) => 0;
|
|
||||||
}
|
|
||||||
lfs_unmount(&lfs) => 0;
|
|
||||||
}
|
|
||||||
'''
|
|
||||||
|
|
||||||
[[case]] # region corruption (causes cascading failures)
|
[[case]] # region corruption (causes cascading failures)
|
||||||
|
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
|
||||||
define.LFS_ERASE_CYCLES = 0xffffffff
|
define.LFS_ERASE_CYCLES = 0xffffffff
|
||||||
|
define.LFS_ERASE_VALUE = [0x00, 0xff, -1]
|
||||||
define.LFS_BADBLOCK_BEHAVIOR = [
|
define.LFS_BADBLOCK_BEHAVIOR = [
|
||||||
'LFS_TESTBD_BADBLOCK_NOPROG',
|
'LFS_TESTBD_BADBLOCK_PROGERROR',
|
||||||
'LFS_TESTBD_BADBLOCK_NOERASE',
|
'LFS_TESTBD_BADBLOCK_ERASEERROR',
|
||||||
'LFS_TESTBD_BADBLOCK_NOREAD',
|
'LFS_TESTBD_BADBLOCK_READERROR',
|
||||||
|
'LFS_TESTBD_BADBLOCK_PROGNOOP',
|
||||||
|
'LFS_TESTBD_BADBLOCK_ERASENOOP',
|
||||||
]
|
]
|
||||||
define.NAMEMULT = 64
|
define.NAMEMULT = 64
|
||||||
define.FILEMULT = 1
|
define.FILEMULT = 1
|
||||||
@@ -266,11 +146,15 @@ code = '''
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
[[case]] # alternating corruption (causes cascading failures)
|
[[case]] # alternating corruption (causes cascading failures)
|
||||||
|
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
|
||||||
define.LFS_ERASE_CYCLES = 0xffffffff
|
define.LFS_ERASE_CYCLES = 0xffffffff
|
||||||
|
define.LFS_ERASE_VALUE = [0x00, 0xff, -1]
|
||||||
define.LFS_BADBLOCK_BEHAVIOR = [
|
define.LFS_BADBLOCK_BEHAVIOR = [
|
||||||
'LFS_TESTBD_BADBLOCK_NOPROG',
|
'LFS_TESTBD_BADBLOCK_PROGERROR',
|
||||||
'LFS_TESTBD_BADBLOCK_NOERASE',
|
'LFS_TESTBD_BADBLOCK_ERASEERROR',
|
||||||
'LFS_TESTBD_BADBLOCK_NOREAD',
|
'LFS_TESTBD_BADBLOCK_READERROR',
|
||||||
|
'LFS_TESTBD_BADBLOCK_PROGNOOP',
|
||||||
|
'LFS_TESTBD_BADBLOCK_ERASENOOP',
|
||||||
]
|
]
|
||||||
define.NAMEMULT = 64
|
define.NAMEMULT = 64
|
||||||
define.FILEMULT = 1
|
define.FILEMULT = 1
|
||||||
@@ -337,10 +221,13 @@ code = '''
|
|||||||
# other corner cases
|
# other corner cases
|
||||||
[[case]] # bad superblocks (corrupt 1 or 0)
|
[[case]] # bad superblocks (corrupt 1 or 0)
|
||||||
define.LFS_ERASE_CYCLES = 0xffffffff
|
define.LFS_ERASE_CYCLES = 0xffffffff
|
||||||
|
define.LFS_ERASE_VALUE = [0x00, 0xff, -1]
|
||||||
define.LFS_BADBLOCK_BEHAVIOR = [
|
define.LFS_BADBLOCK_BEHAVIOR = [
|
||||||
'LFS_TESTBD_BADBLOCK_NOPROG',
|
'LFS_TESTBD_BADBLOCK_PROGERROR',
|
||||||
'LFS_TESTBD_BADBLOCK_NOERASE',
|
'LFS_TESTBD_BADBLOCK_ERASEERROR',
|
||||||
'LFS_TESTBD_BADBLOCK_NOREAD',
|
'LFS_TESTBD_BADBLOCK_READERROR',
|
||||||
|
'LFS_TESTBD_BADBLOCK_PROGNOOP',
|
||||||
|
'LFS_TESTBD_BADBLOCK_ERASENOOP',
|
||||||
]
|
]
|
||||||
code = '''
|
code = '''
|
||||||
lfs_testbd_setwear(&cfg, 0, 0xffffffff) => 0;
|
lfs_testbd_setwear(&cfg, 0, 0xffffffff) => 0;
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ code = '''
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
[[case]] # reentrant many directory creation/rename/removal
|
[[case]] # reentrant many directory creation/rename/removal
|
||||||
define.N = [5, 25]
|
define.N = [5, 10] # TODO changed from 20, should we be able to do more?
|
||||||
reentrant = true
|
reentrant = true
|
||||||
code = '''
|
code = '''
|
||||||
err = lfs_mount(&lfs, &cfg);
|
err = lfs_mount(&lfs, &cfg);
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
[[case]] # test running a filesystem to exhaustion
|
[[case]] # test running a filesystem to exhaustion
|
||||||
define.LFS_ERASE_CYCLES = 10
|
define.LFS_ERASE_CYCLES = 10
|
||||||
define.LFS_BLOCK_COUNT = 256 # small bd so it runs faster
|
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
|
||||||
define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
|
define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
|
||||||
define.LFS_BADBLOCK_BEHAVIOR = [
|
define.LFS_BADBLOCK_BEHAVIOR = [
|
||||||
'LFS_TESTBD_BADBLOCK_NOPROG',
|
'LFS_TESTBD_BADBLOCK_PROGERROR',
|
||||||
'LFS_TESTBD_BADBLOCK_NOERASE',
|
'LFS_TESTBD_BADBLOCK_ERASEERROR',
|
||||||
'LFS_TESTBD_BADBLOCK_NOREAD',
|
'LFS_TESTBD_BADBLOCK_READERROR',
|
||||||
|
'LFS_TESTBD_BADBLOCK_PROGNOOP',
|
||||||
|
'LFS_TESTBD_BADBLOCK_ERASENOOP',
|
||||||
]
|
]
|
||||||
define.FILES = 10
|
define.FILES = 10
|
||||||
code = '''
|
code = '''
|
||||||
@@ -40,7 +42,7 @@ code = '''
|
|||||||
if (err == LFS_ERR_NOSPC) {
|
if (err == LFS_ERR_NOSPC) {
|
||||||
goto exhausted;
|
goto exhausted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < FILES; i++) {
|
for (uint32_t i = 0; i < FILES; i++) {
|
||||||
// check for errors
|
// check for errors
|
||||||
@@ -57,7 +59,7 @@ code = '''
|
|||||||
}
|
}
|
||||||
|
|
||||||
lfs_file_close(&lfs, &file) => 0;
|
lfs_file_close(&lfs, &file) => 0;
|
||||||
}
|
}
|
||||||
lfs_unmount(&lfs) => 0;
|
lfs_unmount(&lfs) => 0;
|
||||||
|
|
||||||
cycle += 1;
|
cycle += 1;
|
||||||
@@ -70,7 +72,7 @@ exhausted:
|
|||||||
// check for errors
|
// check for errors
|
||||||
sprintf(path, "roadrunner/test%d", i);
|
sprintf(path, "roadrunner/test%d", i);
|
||||||
lfs_stat(&lfs, path, &info) => 0;
|
lfs_stat(&lfs, path, &info) => 0;
|
||||||
}
|
}
|
||||||
lfs_unmount(&lfs) => 0;
|
lfs_unmount(&lfs) => 0;
|
||||||
|
|
||||||
LFS_WARN("completed %d cycles", cycle);
|
LFS_WARN("completed %d cycles", cycle);
|
||||||
@@ -79,12 +81,14 @@ exhausted:
|
|||||||
[[case]] # test running a filesystem to exhaustion
|
[[case]] # test running a filesystem to exhaustion
|
||||||
# which also requires expanding superblocks
|
# which also requires expanding superblocks
|
||||||
define.LFS_ERASE_CYCLES = 10
|
define.LFS_ERASE_CYCLES = 10
|
||||||
define.LFS_BLOCK_COUNT = 256 # small bd so it runs faster
|
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
|
||||||
define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
|
define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
|
||||||
define.LFS_BADBLOCK_BEHAVIOR = [
|
define.LFS_BADBLOCK_BEHAVIOR = [
|
||||||
'LFS_TESTBD_BADBLOCK_NOPROG',
|
'LFS_TESTBD_BADBLOCK_PROGERROR',
|
||||||
'LFS_TESTBD_BADBLOCK_NOERASE',
|
'LFS_TESTBD_BADBLOCK_ERASEERROR',
|
||||||
'LFS_TESTBD_BADBLOCK_NOREAD',
|
'LFS_TESTBD_BADBLOCK_READERROR',
|
||||||
|
'LFS_TESTBD_BADBLOCK_PROGNOOP',
|
||||||
|
'LFS_TESTBD_BADBLOCK_ERASENOOP',
|
||||||
]
|
]
|
||||||
define.FILES = 10
|
define.FILES = 10
|
||||||
code = '''
|
code = '''
|
||||||
@@ -116,7 +120,7 @@ code = '''
|
|||||||
if (err == LFS_ERR_NOSPC) {
|
if (err == LFS_ERR_NOSPC) {
|
||||||
goto exhausted;
|
goto exhausted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < FILES; i++) {
|
for (uint32_t i = 0; i < FILES; i++) {
|
||||||
// check for errors
|
// check for errors
|
||||||
@@ -133,7 +137,7 @@ code = '''
|
|||||||
}
|
}
|
||||||
|
|
||||||
lfs_file_close(&lfs, &file) => 0;
|
lfs_file_close(&lfs, &file) => 0;
|
||||||
}
|
}
|
||||||
lfs_unmount(&lfs) => 0;
|
lfs_unmount(&lfs) => 0;
|
||||||
|
|
||||||
cycle += 1;
|
cycle += 1;
|
||||||
@@ -146,7 +150,7 @@ exhausted:
|
|||||||
// check for errors
|
// check for errors
|
||||||
sprintf(path, "test%d", i);
|
sprintf(path, "test%d", i);
|
||||||
lfs_stat(&lfs, path, &info) => 0;
|
lfs_stat(&lfs, path, &info) => 0;
|
||||||
}
|
}
|
||||||
lfs_unmount(&lfs) => 0;
|
lfs_unmount(&lfs) => 0;
|
||||||
|
|
||||||
LFS_WARN("completed %d cycles", cycle);
|
LFS_WARN("completed %d cycles", cycle);
|
||||||
@@ -158,21 +162,11 @@ exhausted:
|
|||||||
# check for.
|
# check for.
|
||||||
|
|
||||||
[[case]] # wear-level test running a filesystem to exhaustion
|
[[case]] # wear-level test running a filesystem to exhaustion
|
||||||
define.LFS_ERASE_CYCLES = 10
|
define.LFS_ERASE_CYCLES = 20
|
||||||
define.LFS_BLOCK_COUNT = 256 # small bd so it runs faster
|
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
|
||||||
define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
|
define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
|
||||||
define.LFS_BADBLOCK_BEHAVIOR = [
|
|
||||||
'LFS_TESTBD_BADBLOCK_NOPROG',
|
|
||||||
'LFS_TESTBD_BADBLOCK_NOERASE',
|
|
||||||
'LFS_TESTBD_BADBLOCK_NOREAD',
|
|
||||||
]
|
|
||||||
define.FILES = 10
|
define.FILES = 10
|
||||||
code = '''
|
code = '''
|
||||||
lfs_format(&lfs, &cfg) => 0;
|
|
||||||
lfs_mount(&lfs, &cfg) => 0;
|
|
||||||
lfs_mkdir(&lfs, "roadrunner") => 0;
|
|
||||||
lfs_unmount(&lfs) => 0;
|
|
||||||
|
|
||||||
uint32_t run_cycles[2];
|
uint32_t run_cycles[2];
|
||||||
const uint32_t run_block_count[2] = {LFS_BLOCK_COUNT/2, LFS_BLOCK_COUNT};
|
const uint32_t run_block_count[2] = {LFS_BLOCK_COUNT/2, LFS_BLOCK_COUNT};
|
||||||
|
|
||||||
@@ -182,6 +176,11 @@ code = '''
|
|||||||
(b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0;
|
(b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lfs_format(&lfs, &cfg) => 0;
|
||||||
|
lfs_mount(&lfs, &cfg) => 0;
|
||||||
|
lfs_mkdir(&lfs, "roadrunner") => 0;
|
||||||
|
lfs_unmount(&lfs) => 0;
|
||||||
|
|
||||||
uint32_t cycle = 0;
|
uint32_t cycle = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
lfs_mount(&lfs, &cfg) => 0;
|
lfs_mount(&lfs, &cfg) => 0;
|
||||||
@@ -208,7 +207,7 @@ code = '''
|
|||||||
if (err == LFS_ERR_NOSPC) {
|
if (err == LFS_ERR_NOSPC) {
|
||||||
goto exhausted;
|
goto exhausted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < FILES; i++) {
|
for (uint32_t i = 0; i < FILES; i++) {
|
||||||
// check for errors
|
// check for errors
|
||||||
@@ -225,7 +224,7 @@ code = '''
|
|||||||
}
|
}
|
||||||
|
|
||||||
lfs_file_close(&lfs, &file) => 0;
|
lfs_file_close(&lfs, &file) => 0;
|
||||||
}
|
}
|
||||||
lfs_unmount(&lfs) => 0;
|
lfs_unmount(&lfs) => 0;
|
||||||
|
|
||||||
cycle += 1;
|
cycle += 1;
|
||||||
@@ -238,7 +237,7 @@ exhausted:
|
|||||||
// check for errors
|
// check for errors
|
||||||
sprintf(path, "roadrunner/test%d", i);
|
sprintf(path, "roadrunner/test%d", i);
|
||||||
lfs_stat(&lfs, path, &info) => 0;
|
lfs_stat(&lfs, path, &info) => 0;
|
||||||
}
|
}
|
||||||
lfs_unmount(&lfs) => 0;
|
lfs_unmount(&lfs) => 0;
|
||||||
|
|
||||||
run_cycles[run] = cycle;
|
run_cycles[run] = cycle;
|
||||||
@@ -247,22 +246,15 @@ exhausted:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check we increased the lifetime by 2x with ~10% error
|
// check we increased the lifetime by 2x with ~10% error
|
||||||
LFS_ASSERT(run_cycles[1] > 2*run_cycles[0]-run_cycles[0]/10);
|
LFS_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]);
|
||||||
'''
|
'''
|
||||||
|
|
||||||
[[case]] # wear-level test + expanding superblock
|
[[case]] # wear-level test + expanding superblock
|
||||||
define.LFS_ERASE_CYCLES = 10
|
define.LFS_ERASE_CYCLES = 20
|
||||||
define.LFS_BLOCK_COUNT = 256 # small bd so it runs faster
|
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
|
||||||
define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
|
define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
|
||||||
define.LFS_BADBLOCK_BEHAVIOR = [
|
|
||||||
'LFS_TESTBD_BADBLOCK_NOPROG',
|
|
||||||
'LFS_TESTBD_BADBLOCK_NOERASE',
|
|
||||||
'LFS_TESTBD_BADBLOCK_NOREAD',
|
|
||||||
]
|
|
||||||
define.FILES = 10
|
define.FILES = 10
|
||||||
code = '''
|
code = '''
|
||||||
lfs_format(&lfs, &cfg) => 0;
|
|
||||||
|
|
||||||
uint32_t run_cycles[2];
|
uint32_t run_cycles[2];
|
||||||
const uint32_t run_block_count[2] = {LFS_BLOCK_COUNT/2, LFS_BLOCK_COUNT};
|
const uint32_t run_block_count[2] = {LFS_BLOCK_COUNT/2, LFS_BLOCK_COUNT};
|
||||||
|
|
||||||
@@ -272,6 +264,8 @@ code = '''
|
|||||||
(b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0;
|
(b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lfs_format(&lfs, &cfg) => 0;
|
||||||
|
|
||||||
uint32_t cycle = 0;
|
uint32_t cycle = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
lfs_mount(&lfs, &cfg) => 0;
|
lfs_mount(&lfs, &cfg) => 0;
|
||||||
@@ -298,7 +292,7 @@ code = '''
|
|||||||
if (err == LFS_ERR_NOSPC) {
|
if (err == LFS_ERR_NOSPC) {
|
||||||
goto exhausted;
|
goto exhausted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < FILES; i++) {
|
for (uint32_t i = 0; i < FILES; i++) {
|
||||||
// check for errors
|
// check for errors
|
||||||
@@ -315,7 +309,7 @@ code = '''
|
|||||||
}
|
}
|
||||||
|
|
||||||
lfs_file_close(&lfs, &file) => 0;
|
lfs_file_close(&lfs, &file) => 0;
|
||||||
}
|
}
|
||||||
lfs_unmount(&lfs) => 0;
|
lfs_unmount(&lfs) => 0;
|
||||||
|
|
||||||
cycle += 1;
|
cycle += 1;
|
||||||
@@ -328,7 +322,7 @@ exhausted:
|
|||||||
// check for errors
|
// check for errors
|
||||||
sprintf(path, "test%d", i);
|
sprintf(path, "test%d", i);
|
||||||
lfs_stat(&lfs, path, &info) => 0;
|
lfs_stat(&lfs, path, &info) => 0;
|
||||||
}
|
}
|
||||||
lfs_unmount(&lfs) => 0;
|
lfs_unmount(&lfs) => 0;
|
||||||
|
|
||||||
run_cycles[run] = cycle;
|
run_cycles[run] = cycle;
|
||||||
@@ -337,5 +331,115 @@ exhausted:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check we increased the lifetime by 2x with ~10% error
|
// check we increased the lifetime by 2x with ~10% error
|
||||||
LFS_ASSERT(run_cycles[1] > 2*run_cycles[0]-run_cycles[0]/10);
|
LFS_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]);
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
[[case]] # test that we wear blocks roughly evenly
|
||||||
|
define.LFS_ERASE_CYCLES = 0xffffffff
|
||||||
|
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
|
||||||
|
define.LFS_BLOCK_CYCLES = [5, 4, 3, 2, 1]
|
||||||
|
#define.LFS_BLOCK_CYCLES = [4, 2]
|
||||||
|
define.CYCLES = 100
|
||||||
|
define.FILES = 10
|
||||||
|
code = '''
|
||||||
|
lfs_format(&lfs, &cfg) => 0;
|
||||||
|
lfs_mount(&lfs, &cfg) => 0;
|
||||||
|
lfs_mkdir(&lfs, "roadrunner") => 0;
|
||||||
|
lfs_unmount(&lfs) => 0;
|
||||||
|
|
||||||
|
uint32_t cycle = 0;
|
||||||
|
while (cycle < CYCLES) {
|
||||||
|
lfs_mount(&lfs, &cfg) => 0;
|
||||||
|
for (uint32_t i = 0; i < FILES; i++) {
|
||||||
|
// chose name, roughly random seed, and random 2^n size
|
||||||
|
sprintf(path, "roadrunner/test%d", i);
|
||||||
|
srand(cycle * i);
|
||||||
|
size = 1 << 4; //((rand() % 10)+2);
|
||||||
|
|
||||||
|
lfs_file_open(&lfs, &file, path,
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
|
||||||
|
|
||||||
|
for (lfs_size_t j = 0; j < size; j++) {
|
||||||
|
char c = 'a' + (rand() % 26);
|
||||||
|
lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
|
||||||
|
assert(res == 1 || res == LFS_ERR_NOSPC);
|
||||||
|
if (res == LFS_ERR_NOSPC) {
|
||||||
|
goto exhausted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = lfs_file_close(&lfs, &file);
|
||||||
|
assert(err == 0 || err == LFS_ERR_NOSPC);
|
||||||
|
if (err == LFS_ERR_NOSPC) {
|
||||||
|
goto exhausted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < FILES; i++) {
|
||||||
|
// check for errors
|
||||||
|
sprintf(path, "roadrunner/test%d", i);
|
||||||
|
srand(cycle * i);
|
||||||
|
size = 1 << 4; //((rand() % 10)+2);
|
||||||
|
|
||||||
|
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
|
||||||
|
for (lfs_size_t j = 0; j < size; j++) {
|
||||||
|
char c = 'a' + (rand() % 26);
|
||||||
|
char r;
|
||||||
|
lfs_file_read(&lfs, &file, &r, 1) => 1;
|
||||||
|
assert(r == c);
|
||||||
|
}
|
||||||
|
|
||||||
|
lfs_file_close(&lfs, &file) => 0;
|
||||||
|
}
|
||||||
|
lfs_unmount(&lfs) => 0;
|
||||||
|
|
||||||
|
cycle += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
exhausted:
|
||||||
|
// should still be readable
|
||||||
|
lfs_mount(&lfs, &cfg) => 0;
|
||||||
|
for (uint32_t i = 0; i < FILES; i++) {
|
||||||
|
// check for errors
|
||||||
|
sprintf(path, "roadrunner/test%d", i);
|
||||||
|
lfs_stat(&lfs, path, &info) => 0;
|
||||||
|
}
|
||||||
|
lfs_unmount(&lfs) => 0;
|
||||||
|
|
||||||
|
LFS_WARN("completed %d cycles", cycle);
|
||||||
|
|
||||||
|
// check the wear on our block device
|
||||||
|
lfs_testbd_wear_t minwear = -1;
|
||||||
|
lfs_testbd_wear_t totalwear = 0;
|
||||||
|
lfs_testbd_wear_t maxwear = 0;
|
||||||
|
// skip 0 and 1 as superblock movement is intentionally avoided
|
||||||
|
for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) {
|
||||||
|
lfs_testbd_wear_t wear = lfs_testbd_getwear(&cfg, b);
|
||||||
|
printf("%08x: wear %d\n", b, wear);
|
||||||
|
assert(wear >= 0);
|
||||||
|
if (wear < minwear) {
|
||||||
|
minwear = wear;
|
||||||
|
}
|
||||||
|
if (wear > maxwear) {
|
||||||
|
maxwear = wear;
|
||||||
|
}
|
||||||
|
totalwear += wear;
|
||||||
|
}
|
||||||
|
lfs_testbd_wear_t avgwear = totalwear / LFS_BLOCK_COUNT;
|
||||||
|
LFS_WARN("max wear: %d cycles", maxwear);
|
||||||
|
LFS_WARN("avg wear: %d cycles", totalwear / LFS_BLOCK_COUNT);
|
||||||
|
LFS_WARN("min wear: %d cycles", minwear);
|
||||||
|
|
||||||
|
// find standard deviation^2
|
||||||
|
lfs_testbd_wear_t dev2 = 0;
|
||||||
|
for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) {
|
||||||
|
lfs_testbd_wear_t wear = lfs_testbd_getwear(&cfg, b);
|
||||||
|
assert(wear >= 0);
|
||||||
|
lfs_testbd_swear_t diff = wear - avgwear;
|
||||||
|
dev2 += diff*diff;
|
||||||
|
}
|
||||||
|
dev2 /= totalwear;
|
||||||
|
LFS_WARN("std dev^2: %d", dev2);
|
||||||
|
assert(dev2 < 8);
|
||||||
|
'''
|
||||||
|
|
||||||
|
|||||||
@@ -57,10 +57,12 @@ code = '''
|
|||||||
|
|
||||||
[[case]] # reentrant testing for orphans, basically just spam mkdir/remove
|
[[case]] # reentrant testing for orphans, basically just spam mkdir/remove
|
||||||
reentrant = true
|
reentrant = true
|
||||||
|
# TODO fix this case, caused by non-DAG trees
|
||||||
|
if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)'
|
||||||
define = [
|
define = [
|
||||||
{FILES=6, DEPTH=1, CYCLES=50},
|
{FILES=6, DEPTH=1, CYCLES=20},
|
||||||
{FILES=26, DEPTH=1, CYCLES=50},
|
{FILES=26, DEPTH=1, CYCLES=20},
|
||||||
{FILES=3, DEPTH=3, CYCLES=50},
|
{FILES=3, DEPTH=3, CYCLES=20},
|
||||||
]
|
]
|
||||||
code = '''
|
code = '''
|
||||||
err = lfs_mount(&lfs, &cfg);
|
err = lfs_mount(&lfs, &cfg);
|
||||||
|
|||||||
@@ -147,10 +147,12 @@ code = '''
|
|||||||
# orphan testing, except here we also set block_cycles so that
|
# orphan testing, except here we also set block_cycles so that
|
||||||
# almost every tree operation needs a relocation
|
# almost every tree operation needs a relocation
|
||||||
reentrant = true
|
reentrant = true
|
||||||
|
# TODO fix this case, caused by non-DAG trees
|
||||||
|
if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)'
|
||||||
define = [
|
define = [
|
||||||
{FILES=6, DEPTH=1, CYCLES=50, LFS_BLOCK_CYCLES=1},
|
{FILES=6, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1},
|
||||||
{FILES=26, DEPTH=1, CYCLES=50, LFS_BLOCK_CYCLES=1},
|
{FILES=26, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1},
|
||||||
{FILES=3, DEPTH=3, CYCLES=50, LFS_BLOCK_CYCLES=1},
|
{FILES=3, DEPTH=3, CYCLES=20, LFS_BLOCK_CYCLES=1},
|
||||||
]
|
]
|
||||||
code = '''
|
code = '''
|
||||||
err = lfs_mount(&lfs, &cfg);
|
err = lfs_mount(&lfs, &cfg);
|
||||||
@@ -207,10 +209,12 @@ code = '''
|
|||||||
|
|
||||||
[[case]] # reentrant testing for relocations, but now with random renames!
|
[[case]] # reentrant testing for relocations, but now with random renames!
|
||||||
reentrant = true
|
reentrant = true
|
||||||
|
# TODO fix this case, caused by non-DAG trees
|
||||||
|
if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)'
|
||||||
define = [
|
define = [
|
||||||
{FILES=6, DEPTH=1, CYCLES=50, LFS_BLOCK_CYCLES=1},
|
{FILES=6, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1},
|
||||||
{FILES=26, DEPTH=1, CYCLES=50, LFS_BLOCK_CYCLES=1},
|
{FILES=26, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1},
|
||||||
{FILES=3, DEPTH=3, CYCLES=50, LFS_BLOCK_CYCLES=1},
|
{FILES=3, DEPTH=3, CYCLES=20, LFS_BLOCK_CYCLES=1},
|
||||||
]
|
]
|
||||||
code = '''
|
code = '''
|
||||||
err = lfs_mount(&lfs, &cfg);
|
err = lfs_mount(&lfs, &cfg);
|
||||||
|
|||||||
Reference in New Issue
Block a user