Implemented orphans resulting from closing desynced scratch files
This is a fun corner case. What happens when you close a desynced scratch file? The obvious answer seems to be just remove the scratch file in lfsr_file_close. But then what if the file is rdonly? desynced because of an error? We really shouldn't write to disk at all when closing a desync or rdonly file. This needs to be a hard rule. So the only option is to defer the work until later somehow. Fortunately, we already have several mechanisms that lead to a very nice solution. I'm very happy with this: 1. There's nothing that says our in-device grm queue needs to always match what's on-disk (we need a separate copy for xoring anyways because of the risk of leb128 encoding differences). So if we have <=2 orphans, we can just push these onto our grm. On the next write operation, the normal grm fixing code takes over and removes the pending orphans O(1). 2. If we have >2 orphans, the best we can do is mark the filesystem as having orphans, and trigger an orphan scan on the next write operation O(nlogn). But how often do you think littlefs's use cases will end up with >2 orphans? Note we also need to scan the opened-file list to make sure we're the _last_ reference to the scratch file. Otherwise we corrupt other opened file handles! --- This commit also includes a fix for a bug where the traversal mdir fell out of sync when dropping mdirs as a part of scratch file cleanup. Found when adding more tests, this would cause scratch files to go unreclaimed.
This commit is contained in:
@@ -6163,7 +6163,7 @@ static int lfsr_mtree_commit(lfs_t *lfs,
|
|||||||
//
|
//
|
||||||
// this is atomic updates any opened mdirs, lfs_t, gstate, etc
|
// this is atomic updates any opened mdirs, lfs_t, gstate, etc
|
||||||
//
|
//
|
||||||
static int lfsr_mdir_drop(lfs_t *lfs, const lfsr_mdir_t *mdir) {
|
static int lfsr_mdir_drop(lfs_t *lfs, lfsr_mdir_t *mdir) {
|
||||||
// mdir should be empty at this point
|
// mdir should be empty at this point
|
||||||
LFS_ASSERT(mdir->rbyd.weight == 0);
|
LFS_ASSERT(mdir->rbyd.weight == 0);
|
||||||
// yeah, you really shouldn't try to drop the mroot
|
// yeah, you really shouldn't try to drop the mroot
|
||||||
@@ -6237,19 +6237,22 @@ static int lfsr_mdir_drop(lfs_t *lfs, const lfsr_mdir_t *mdir) {
|
|||||||
opened;
|
opened;
|
||||||
opened = opened->next) {
|
opened = opened->next) {
|
||||||
// update mids
|
// update mids
|
||||||
if (opened->mdir.mid > mdir->mid) {
|
if (opened->mdir.mid >= mdir->mid) {
|
||||||
opened->mdir.mid -= lfsr_mweight(lfs);
|
opened->mdir.mid -= lfsr_mweight(lfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update directory bookmarks
|
// update directory bookmarks
|
||||||
if (opened->type == LFS_TYPE_DIR) {
|
if (opened->type == LFS_TYPE_DIR) {
|
||||||
lfsr_dir_t *dir = (lfsr_dir_t*)opened;
|
lfsr_dir_t *dir = (lfsr_dir_t*)opened;
|
||||||
if (dir->bookmark > mdir->mid) {
|
if (dir->bookmark >= mdir->mid) {
|
||||||
dir->bookmark -= lfsr_mweight(lfs);
|
dir->bookmark -= lfsr_mweight(lfs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update our mdir, in case we're using it as an iterator
|
||||||
|
mdir->mid -= lfsr_mweight(lfs);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6554,6 +6557,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
|||||||
// gstate must have been committed by a lower-level function at this point
|
// gstate must have been committed by a lower-level function at this point
|
||||||
LFS_ASSERT(lfsr_grm_iszero(lfs->grm_d));
|
LFS_ASSERT(lfsr_grm_iszero(lfs->grm_d));
|
||||||
|
|
||||||
|
// TODO merge with attr updates below?
|
||||||
for (lfs_size_t i = 0; i < attr_count; i++) {
|
for (lfs_size_t i = 0; i < attr_count; i++) {
|
||||||
// update any gstate
|
// update any gstate
|
||||||
if (attrs[i].tag == LFSR_TAG_GRM) {
|
if (attrs[i].tag == LFSR_TAG_GRM) {
|
||||||
@@ -6688,9 +6692,9 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
|||||||
// We handle drops differently than splits/relocates, since these updates
|
// We handle drops differently than splits/relocates, since these updates
|
||||||
// become visible as soon as the commit completes.
|
// become visible as soon as the commit completes.
|
||||||
//
|
//
|
||||||
if (lfsr_mdir_cmp(&mdir_, &lfs->mroot) != 0
|
if (lfsr_mdir_cmp(mdir, &lfs->mroot) != 0
|
||||||
&& mdir_.rbyd.weight == 0) {
|
&& mdir->rbyd.weight == 0) {
|
||||||
err = lfsr_mdir_drop(lfs, &mdir_);
|
err = lfsr_mdir_drop(lfs, mdir);
|
||||||
if (err) {
|
if (err) {
|
||||||
lfs->hasorphans = true;
|
lfs->hasorphans = true;
|
||||||
return err;
|
return err;
|
||||||
@@ -7810,11 +7814,11 @@ static int lfsr_mountinited(lfs_t *lfs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check for any scratch files
|
// check for any scratch files
|
||||||
for (lfs_size_t i = 0;
|
for (lfs_size_t rid = 0;
|
||||||
i < (lfs_size_t)tinfo.u.mdir.rbyd.weight;
|
rid < (lfs_size_t)tinfo.u.mdir.rbyd.weight;
|
||||||
i++) {
|
rid++) {
|
||||||
err = lfsr_mdir_lookup(lfs, &tinfo.u.mdir,
|
err = lfsr_mdir_lookup(lfs, &tinfo.u.mdir,
|
||||||
i, LFSR_TAG_SCRATCH,
|
rid, LFSR_TAG_SCRATCH,
|
||||||
NULL);
|
NULL);
|
||||||
if (err && err != LFS_ERR_NOENT) {
|
if (err && err != LFS_ERR_NOENT) {
|
||||||
return err;
|
return err;
|
||||||
@@ -7825,7 +7829,7 @@ static int lfsr_mountinited(lfs_t *lfs) {
|
|||||||
LFS_DEBUG("Found orphaned scratch file "
|
LFS_DEBUG("Found orphaned scratch file "
|
||||||
"%"PRId32".%"PRId32,
|
"%"PRId32".%"PRId32,
|
||||||
lfsr_mid_bid(lfs, tinfo.u.mdir.mid) >> lfs->mbits,
|
lfsr_mid_bid(lfs, tinfo.u.mdir.mid) >> lfs->mbits,
|
||||||
i);
|
rid);
|
||||||
lfs->hasorphans = true;
|
lfs->hasorphans = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8230,7 +8234,18 @@ static int lfsr_fs_preparemutation(lfs_t *lfs) {
|
|||||||
// fix pending grms
|
// fix pending grms
|
||||||
bool pl = false;
|
bool pl = false;
|
||||||
if (lfsr_grm_hasrm(&lfs->grm)) {
|
if (lfsr_grm_hasrm(&lfs->grm)) {
|
||||||
LFS_DEBUG("Fixing pending grms...");
|
if (lfsr_grm_count(&lfs->grm) == 2) {
|
||||||
|
LFS_DEBUG("Fixing grm "
|
||||||
|
"%"PRId32".%"PRId32" %"PRId32".%"PRId32"...",
|
||||||
|
lfsr_mid_bid(lfs, lfs->grm.rms[0]) >> lfs->mbits,
|
||||||
|
lfsr_mid_rid(lfs, lfs->grm.rms[0]),
|
||||||
|
lfsr_mid_bid(lfs, lfs->grm.rms[1]) >> lfs->mbits,
|
||||||
|
lfsr_mid_rid(lfs, lfs->grm.rms[1]));
|
||||||
|
} else {
|
||||||
|
LFS_DEBUG("Fixing grm %"PRId32".%"PRId32,
|
||||||
|
lfsr_mid_bid(lfs, lfs->grm.rms[0]) >> lfs->mbits,
|
||||||
|
lfsr_mid_rid(lfs, lfs->grm.rms[0]));
|
||||||
|
}
|
||||||
pl = true;
|
pl = true;
|
||||||
|
|
||||||
int err = lfsr_fs_fixgrm(lfs);
|
int err = lfsr_fs_fixgrm(lfs);
|
||||||
@@ -8249,7 +8264,7 @@ static int lfsr_fs_preparemutation(lfs_t *lfs) {
|
|||||||
// outdating the grm
|
// outdating the grm
|
||||||
//
|
//
|
||||||
if (lfs->hasorphans) {
|
if (lfs->hasorphans) {
|
||||||
LFS_DEBUG("Fixing orphaned scratch files...");
|
LFS_DEBUG("Fixing orphans...");
|
||||||
pl = true;
|
pl = true;
|
||||||
|
|
||||||
int err = lfsr_fs_fixorphans(lfs);
|
int err = lfsr_fs_fixorphans(lfs);
|
||||||
@@ -9222,6 +9237,38 @@ int lfsr_file_close(lfs_t *lfs, lfsr_file_t *file) {
|
|||||||
lfs_free(file->buffer);
|
lfs_free(file->buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// never synced?
|
||||||
|
if (lfsr_f_isuncreat(file->flags)) {
|
||||||
|
// are we orphaning a scratch file?
|
||||||
|
//
|
||||||
|
// make sure we check _after_ removing ourselves
|
||||||
|
bool orphaned = true;
|
||||||
|
for (lfsr_opened_t *opened = lfs->opened;
|
||||||
|
opened;
|
||||||
|
opened = opened->next) {
|
||||||
|
if (opened->type == LFS_TYPE_REG
|
||||||
|
&& opened->mdir.mid == file->mdir.mid) {
|
||||||
|
orphaned = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orphaned) {
|
||||||
|
// this gets a bit tricky, since we're not able to write to the
|
||||||
|
// filesystem if we're rdonly or desynced, fortunately we have
|
||||||
|
// a few tricks
|
||||||
|
|
||||||
|
// first try to push onto our grm queue
|
||||||
|
if (lfsr_grm_count(&lfs->grm) < 2) {
|
||||||
|
lfsr_grm_pushrm(&lfs->grm, file->mdir.mid);
|
||||||
|
|
||||||
|
// fallback to just marking the filesystem as orphaned
|
||||||
|
} else {
|
||||||
|
lfs->hasorphans = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -987,3 +987,761 @@ code = '''
|
|||||||
lfsr_file_close(&lfs, &file___) => 0;
|
lfsr_file_close(&lfs, &file___) => 0;
|
||||||
lfsr_unmount(&lfs) => 0;
|
lfsr_unmount(&lfs) => 0;
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
[cases.test_fscratch_orphan]
|
||||||
|
defines.SIZE = '4*BLOCK_SIZE'
|
||||||
|
defines.CHUNK = 64
|
||||||
|
code = '''
|
||||||
|
lfs_t lfs;
|
||||||
|
lfsr_format(&lfs, CFG) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
|
||||||
|
// create a desync file
|
||||||
|
lfsr_file_t file;
|
||||||
|
lfsr_file_open(&lfs, &file, "gello",
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
||||||
|
|
||||||
|
// write to the file
|
||||||
|
uint32_t prng = 42;
|
||||||
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
||||||
|
uint8_t wbuf[CHUNK];
|
||||||
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
||||||
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||||
|
}
|
||||||
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
||||||
|
|
||||||
|
// as far as the filesystem is concerned, the file does not exist yet
|
||||||
|
// via stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "gello", &info) => LFS_ERR_NOENT;
|
||||||
|
// via readdir
|
||||||
|
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);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
// rdonly rejected
|
||||||
|
lfsr_file_t file_;
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
||||||
|
// non-create rejected
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDWR) => LFS_ERR_NOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// close
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
// because the file was desynced, it should still not exist
|
||||||
|
// via stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "gello", &info) => LFS_ERR_NOENT;
|
||||||
|
// via readdir
|
||||||
|
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);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
// rdonly rejected
|
||||||
|
lfsr_file_t file_;
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
||||||
|
// non-create rejected
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDWR) => LFS_ERR_NOENT;
|
||||||
|
|
||||||
|
// even after a remount
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
|
||||||
|
// because the file was desynced, it should still not exist
|
||||||
|
// via stat
|
||||||
|
lfsr_stat(&lfs, "gello", &info) => LFS_ERR_NOENT;
|
||||||
|
// via readdir
|
||||||
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, ".") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
// rdonly rejected
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
||||||
|
// non-create rejected
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDWR) => LFS_ERR_NOENT;
|
||||||
|
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
'''
|
||||||
|
|
||||||
|
[cases.test_fscratch_orphan_wdwr]
|
||||||
|
defines.SIZE = '4*BLOCK_SIZE'
|
||||||
|
defines.CHUNK = 64
|
||||||
|
# 1 => sync before orphaning
|
||||||
|
# 2 => sync after orphaning
|
||||||
|
defines.SYNC = [0, 1, 2, 3]
|
||||||
|
code = '''
|
||||||
|
lfs_t lfs;
|
||||||
|
lfsr_format(&lfs, CFG) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
|
||||||
|
// create a desync file
|
||||||
|
lfsr_file_t file;
|
||||||
|
lfsr_file_open(&lfs, &file, "gello",
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
||||||
|
// open a second reference
|
||||||
|
lfsr_file_t file__;
|
||||||
|
lfsr_file_open(&lfs, &file__, "gello",
|
||||||
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||||
|
// and a third for checking sync broadcasts
|
||||||
|
lfsr_file_t file___;
|
||||||
|
lfsr_file_open(&lfs, &file___, "gello",
|
||||||
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||||
|
|
||||||
|
// write to the first file
|
||||||
|
uint32_t prng = 42;
|
||||||
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
||||||
|
uint8_t wbuf[CHUNK];
|
||||||
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
||||||
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||||
|
}
|
||||||
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
||||||
|
|
||||||
|
// as far as the filesystem is concerned, the file does not exist yet
|
||||||
|
// via stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "gello", &info) => LFS_ERR_NOENT;
|
||||||
|
// via readdir
|
||||||
|
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);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
// rdonly rejected
|
||||||
|
lfsr_file_t file_;
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
||||||
|
// non-create rejected
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDWR) => LFS_ERR_NOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write to the second file
|
||||||
|
prng = 42+1;
|
||||||
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
||||||
|
uint8_t wbuf[CHUNK];
|
||||||
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
||||||
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||||
|
}
|
||||||
|
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
|
||||||
|
|
||||||
|
// as far as the filesystem is concerned, the file does not exist yet
|
||||||
|
// via stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "gello", &info) => LFS_ERR_NOENT;
|
||||||
|
// via readdir
|
||||||
|
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);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
// rdonly rejected
|
||||||
|
lfsr_file_t file_;
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
||||||
|
// non-create rejected
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDWR) => LFS_ERR_NOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SYNC & 0x1) {
|
||||||
|
// sync the second file
|
||||||
|
lfsr_file_sync(&lfs, &file__) => 0;
|
||||||
|
|
||||||
|
// now it should show up
|
||||||
|
// via stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "gello", &info) => 0;
|
||||||
|
assert(strcmp(info.name, "gello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == SIZE);
|
||||||
|
// via readdir
|
||||||
|
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);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "gello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == SIZE);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
// via open
|
||||||
|
lfsr_file_t file_;
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDONLY) => 0;
|
||||||
|
uint32_t prng_ = 42+1;
|
||||||
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
||||||
|
uint8_t wbuf[CHUNK];
|
||||||
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
||||||
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
||||||
|
}
|
||||||
|
uint8_t rbuf[CHUNK];
|
||||||
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
||||||
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
||||||
|
}
|
||||||
|
lfsr_file_close(&lfs, &file_) => 0;
|
||||||
|
|
||||||
|
// recieved sync broadcast?
|
||||||
|
lfsr_file_rewind(&lfs, &file___) => 0;
|
||||||
|
prng_ = 42+1;
|
||||||
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
||||||
|
uint8_t wbuf[CHUNK];
|
||||||
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
||||||
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
||||||
|
}
|
||||||
|
uint8_t rbuf[CHUNK];
|
||||||
|
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
|
||||||
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// close the first file, this should do nothing but discard the
|
||||||
|
// file contents
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
if (SYNC & 0x2) {
|
||||||
|
// sync the second file
|
||||||
|
lfsr_file_sync(&lfs, &file__) => 0;
|
||||||
|
|
||||||
|
// now it should show up
|
||||||
|
// via stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "gello", &info) => 0;
|
||||||
|
assert(strcmp(info.name, "gello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == SIZE);
|
||||||
|
// via readdir
|
||||||
|
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);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "gello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == SIZE);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
// via open
|
||||||
|
lfsr_file_t file_;
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDONLY) => 0;
|
||||||
|
uint32_t prng_ = 42+1;
|
||||||
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
||||||
|
uint8_t wbuf[CHUNK];
|
||||||
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
||||||
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
||||||
|
}
|
||||||
|
uint8_t rbuf[CHUNK];
|
||||||
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
||||||
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
||||||
|
}
|
||||||
|
lfsr_file_close(&lfs, &file_) => 0;
|
||||||
|
|
||||||
|
// recieved sync broadcast?
|
||||||
|
lfsr_file_rewind(&lfs, &file___) => 0;
|
||||||
|
prng_ = 42+1;
|
||||||
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
||||||
|
uint8_t wbuf[CHUNK];
|
||||||
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
||||||
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
||||||
|
}
|
||||||
|
uint8_t rbuf[CHUNK];
|
||||||
|
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
|
||||||
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// close the second file
|
||||||
|
lfsr_file_close(&lfs, &file__) => 0;
|
||||||
|
|
||||||
|
// now it should show up
|
||||||
|
// via stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "gello", &info) => 0;
|
||||||
|
assert(strcmp(info.name, "gello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == SIZE);
|
||||||
|
// via readdir
|
||||||
|
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);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "gello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == SIZE);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
// via open
|
||||||
|
lfsr_file_t file_;
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDONLY) => 0;
|
||||||
|
uint32_t prng_ = 42+1;
|
||||||
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
||||||
|
uint8_t wbuf[CHUNK];
|
||||||
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
||||||
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
||||||
|
}
|
||||||
|
uint8_t rbuf[CHUNK];
|
||||||
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
||||||
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
||||||
|
}
|
||||||
|
lfsr_file_close(&lfs, &file_) => 0;
|
||||||
|
|
||||||
|
// recieved sync broadcast?
|
||||||
|
lfsr_file_rewind(&lfs, &file___) => 0;
|
||||||
|
prng_ = 42+1;
|
||||||
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
||||||
|
uint8_t wbuf[CHUNK];
|
||||||
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
||||||
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
||||||
|
}
|
||||||
|
uint8_t rbuf[CHUNK];
|
||||||
|
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
|
||||||
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
lfsr_file_close(&lfs, &file___) => 0;
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
'''
|
||||||
|
|
||||||
|
[cases.test_fscratch_orphan_open]
|
||||||
|
defines.ORPHANS = [1, 2, 3, 100]
|
||||||
|
# 0 => don't remount
|
||||||
|
# 1 => remount after write
|
||||||
|
# 2 => remount before write
|
||||||
|
defines.REMOUNT = [0, 1, 2]
|
||||||
|
code = '''
|
||||||
|
lfs_t lfs;
|
||||||
|
lfsr_format(&lfs, CFG) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
|
||||||
|
// create neighboring orphaned files
|
||||||
|
//
|
||||||
|
// more orphans requires different techniques for cleaning up orphans
|
||||||
|
lfsr_file_t orphans[ORPHANS-1];
|
||||||
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
||||||
|
char name[256];
|
||||||
|
sprintf(name, "fello%03x", i);
|
||||||
|
lfsr_file_open(&lfs, &orphans[i], name,
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
||||||
|
lfsr_file_write(&lfs, &orphans[i],
|
||||||
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
||||||
|
=> strlen("WoOoOoOoOoO");
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an orphaned file
|
||||||
|
lfsr_file_t file;
|
||||||
|
lfsr_file_open(&lfs, &file, "gello",
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
||||||
|
lfsr_file_write(&lfs, &file,
|
||||||
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
||||||
|
=> strlen("WoOoOoOoOoO");
|
||||||
|
|
||||||
|
// close all orphans at once, or else the open calls would just
|
||||||
|
// clean up each orphans
|
||||||
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
||||||
|
lfsr_file_close(&lfs, &orphans[i]) => 0;
|
||||||
|
}
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
if (REMOUNT == 2) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new file over the orphan
|
||||||
|
lfsr_file_open(&lfs, &file, "gello",
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||||
|
lfsr_file_write(&lfs, &file, "hello!", strlen("hello!"))
|
||||||
|
=> strlen("hello!");
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
if (REMOUNT == 1) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the new file is readable
|
||||||
|
// via stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "gello", &info) => 0;
|
||||||
|
assert(strcmp(info.name, "gello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == strlen("hello!"));
|
||||||
|
// via readdir
|
||||||
|
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);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "gello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == strlen("hello!"));
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
// via open
|
||||||
|
lfsr_file_t file_;
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDONLY) => 0;
|
||||||
|
uint8_t rbuf[256];
|
||||||
|
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("hello!");
|
||||||
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
||||||
|
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
'''
|
||||||
|
|
||||||
|
[cases.test_fscratch_orphan_mkdir]
|
||||||
|
defines.ORPHANS = [1, 2, 3, 100]
|
||||||
|
# 0 => don't remount
|
||||||
|
# 1 => remount after write
|
||||||
|
# 2 => remount before write
|
||||||
|
defines.REMOUNT = [0, 1, 2]
|
||||||
|
code = '''
|
||||||
|
lfs_t lfs;
|
||||||
|
lfsr_format(&lfs, CFG) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
|
||||||
|
// create neighboring orphaned files
|
||||||
|
//
|
||||||
|
// more orphans requires different techniques for cleaning up orphans
|
||||||
|
lfsr_file_t orphans[ORPHANS-1];
|
||||||
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
||||||
|
char name[256];
|
||||||
|
sprintf(name, "fello%03x", i);
|
||||||
|
lfsr_file_open(&lfs, &orphans[i], name,
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
||||||
|
lfsr_file_write(&lfs, &orphans[i],
|
||||||
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
||||||
|
=> strlen("WoOoOoOoOoO");
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an orphaned file
|
||||||
|
lfsr_file_t file;
|
||||||
|
lfsr_file_open(&lfs, &file, "gello",
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
||||||
|
lfsr_file_write(&lfs, &file,
|
||||||
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
||||||
|
=> strlen("WoOoOoOoOoO");
|
||||||
|
|
||||||
|
// close all orphans at once, or else the open calls would just
|
||||||
|
// clean up each orphans
|
||||||
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
||||||
|
lfsr_file_close(&lfs, &orphans[i]) => 0;
|
||||||
|
}
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
if (REMOUNT == 2) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new dir over the orphan
|
||||||
|
lfsr_mkdir(&lfs, "gello") => 0;
|
||||||
|
|
||||||
|
if (REMOUNT == 1) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the new dir is readable
|
||||||
|
// via stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "gello", &info) => 0;
|
||||||
|
assert(strcmp(info.name, "gello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
// via readdir
|
||||||
|
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);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "gello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
'''
|
||||||
|
|
||||||
|
[cases.test_fscratch_orphan_remove]
|
||||||
|
defines.ORPHANS = [1, 2, 3, 100]
|
||||||
|
# 0 => don't remount
|
||||||
|
# 1 => remount after write
|
||||||
|
# 2 => remount before write
|
||||||
|
defines.REMOUNT = [0, 1, 2]
|
||||||
|
code = '''
|
||||||
|
lfs_t lfs;
|
||||||
|
lfsr_format(&lfs, CFG) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
|
||||||
|
// create neighboring orphaned files
|
||||||
|
//
|
||||||
|
// more orphans requires different techniques for cleaning up orphans
|
||||||
|
lfsr_file_t orphans[ORPHANS-1];
|
||||||
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
||||||
|
char name[256];
|
||||||
|
sprintf(name, "fello%03x", i);
|
||||||
|
lfsr_file_open(&lfs, &orphans[i], name,
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
||||||
|
lfsr_file_write(&lfs, &orphans[i],
|
||||||
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
||||||
|
=> strlen("WoOoOoOoOoO");
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an orphaned file
|
||||||
|
lfsr_file_t file;
|
||||||
|
lfsr_file_open(&lfs, &file, "gello",
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
||||||
|
lfsr_file_write(&lfs, &file,
|
||||||
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
||||||
|
=> strlen("WoOoOoOoOoO");
|
||||||
|
|
||||||
|
// close all orphans at once, or else the open calls would just
|
||||||
|
// clean up each orphans
|
||||||
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
||||||
|
lfsr_file_close(&lfs, &orphans[i]) => 0;
|
||||||
|
}
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
if (REMOUNT == 2) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// orphans aren't real, so remove should fail
|
||||||
|
lfsr_remove(&lfs, "gello") => LFS_ERR_NOENT;
|
||||||
|
|
||||||
|
if (REMOUNT == 1) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// just make sure things look ok
|
||||||
|
// via stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "gello", &info) => LFS_ERR_NOENT;
|
||||||
|
// via readdir
|
||||||
|
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);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
'''
|
||||||
|
|
||||||
|
[cases.test_fscratch_orphan_rename_dst]
|
||||||
|
defines.ORPHANS = [1, 2, 3, 100]
|
||||||
|
# 0 => don't remount
|
||||||
|
# 1 => remount after write
|
||||||
|
# 2 => remount before write
|
||||||
|
defines.REMOUNT = [0, 1, 2]
|
||||||
|
code = '''
|
||||||
|
lfs_t lfs;
|
||||||
|
lfsr_format(&lfs, CFG) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
|
||||||
|
// make our src file before orphans, otherwise open just cleans
|
||||||
|
// things up
|
||||||
|
lfsr_file_t file;
|
||||||
|
lfsr_file_open(&lfs, &file, "hello",
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||||
|
lfsr_file_write(&lfs, &file, "hello!", strlen("hello!"))
|
||||||
|
=> strlen("hello!");
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
// create neighboring orphaned files
|
||||||
|
//
|
||||||
|
// more orphans requires different techniques for cleaning up orphans
|
||||||
|
lfsr_file_t orphans[ORPHANS-1];
|
||||||
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
||||||
|
char name[256];
|
||||||
|
sprintf(name, "fello%03x", i);
|
||||||
|
lfsr_file_open(&lfs, &orphans[i], name,
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
||||||
|
lfsr_file_write(&lfs, &orphans[i],
|
||||||
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
||||||
|
=> strlen("WoOoOoOoOoO");
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an orphaned file
|
||||||
|
lfsr_file_open(&lfs, &file, "gello",
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
||||||
|
lfsr_file_write(&lfs, &file,
|
||||||
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
||||||
|
=> strlen("WoOoOoOoOoO");
|
||||||
|
|
||||||
|
// close all orphans at once, or else the open calls would just
|
||||||
|
// clean up each orphans
|
||||||
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
||||||
|
lfsr_file_close(&lfs, &orphans[i]) => 0;
|
||||||
|
}
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
if (REMOUNT == 2) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rename onto orphan
|
||||||
|
lfsr_rename(&lfs, "hello", "gello") => 0;
|
||||||
|
|
||||||
|
if (REMOUNT == 1) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the new file is readable
|
||||||
|
// via stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "gello", &info) => 0;
|
||||||
|
assert(strcmp(info.name, "gello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == strlen("hello!"));
|
||||||
|
// via readdir
|
||||||
|
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);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "gello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == strlen("hello!"));
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
// via open
|
||||||
|
lfsr_file_t file_;
|
||||||
|
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDONLY) => 0;
|
||||||
|
uint8_t rbuf[256];
|
||||||
|
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("hello!");
|
||||||
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
||||||
|
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
'''
|
||||||
|
|
||||||
|
[cases.test_fscratch_orphan_rename_src]
|
||||||
|
defines.ORPHANS = [1, 2, 3, 100]
|
||||||
|
# 0 => don't remount
|
||||||
|
# 1 => remount after write
|
||||||
|
# 2 => remount before write
|
||||||
|
defines.REMOUNT = [0, 1, 2]
|
||||||
|
code = '''
|
||||||
|
lfs_t lfs;
|
||||||
|
lfsr_format(&lfs, CFG) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
|
||||||
|
// create neighboring orphaned files
|
||||||
|
//
|
||||||
|
// more orphans requires different techniques for cleaning up orphans
|
||||||
|
lfsr_file_t orphans[ORPHANS-1];
|
||||||
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
||||||
|
char name[256];
|
||||||
|
sprintf(name, "fello%03x", i);
|
||||||
|
lfsr_file_open(&lfs, &orphans[i], name,
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
||||||
|
lfsr_file_write(&lfs, &orphans[i],
|
||||||
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
||||||
|
=> strlen("WoOoOoOoOoO");
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an orphaned file
|
||||||
|
lfsr_file_t file;
|
||||||
|
lfsr_file_open(&lfs, &file, "gello",
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
||||||
|
lfsr_file_write(&lfs, &file,
|
||||||
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
||||||
|
=> strlen("WoOoOoOoOoO");
|
||||||
|
|
||||||
|
// close all orphans at once, or else the open calls would just
|
||||||
|
// clean up each orphans
|
||||||
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
||||||
|
lfsr_file_close(&lfs, &orphans[i]) => 0;
|
||||||
|
}
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
if (REMOUNT == 2) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// orphans aren't real, so rename should fail
|
||||||
|
lfsr_rename(&lfs, "gello", "hello") => LFS_ERR_NOENT;
|
||||||
|
|
||||||
|
if (REMOUNT == 1) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// just make sure things look ok
|
||||||
|
// via stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "gello", &info) => LFS_ERR_NOENT;
|
||||||
|
// via readdir
|
||||||
|
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);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
'''
|
||||||
|
|||||||
Reference in New Issue
Block a user