Reverted most of dir offset changes, dirs to follow dstart when open
Unfortunately the previous attempt to fix the dir seek system didn't
really work. Using a packed mid/rid integer for the offset is tempting,
but since mid/rid can change with any metadata id change in the
filesystem, dir tell offsets would become invalidated if you modified
files in unrelated directories, which isn't great and likely to catch
users by surprise.
This solution builds on the previous dir offset design, which tracks the
dstart-relative position independently from the current mid/rid in our
directory. To update this correctly when there are unrelated changes to
the filesystem, we need to know if metadata id changes are in the range
between our directories dstart and current mid/rid. This in turn means
we need to track our dstart. So our opened directories need three
separate pointers we need to update on every mdir commit:
dir->pos
|
.-------+-------.
a b c d e f g h i j k l m n o p
^ ^
| |
dir->dstart dir->mdir
This has quite a few moving parts, which I was hoping to avoid.
Fortunately we don't need a second mdir, so the RAM cost is pretty
small.
We can also drop dir->did, since the dstart mid/rid render it redundant,
which is interesting.
This commit is contained in:
@@ -5056,54 +5056,6 @@ static int lfsr_btree_traversal_next(lfs_t *lfs,
|
||||
|
||||
/// Metadata pair operations ///
|
||||
|
||||
// mids are a tuple of bid (index in the mtree) and rid (index in the mdir)
|
||||
// shoved into an integer
|
||||
//
|
||||
// We want to maximize our encoding space, so we conservatively carve out
|
||||
// only enough space to fit the maximum possible number of rids in an mdir.
|
||||
// In theory, as the mdirs get larger, we need fewer mdirs for a filesystem of
|
||||
// a given word size.
|
||||
//
|
||||
// - Each file neads 1 tag minimum
|
||||
// - Each tag needs ~2 alts with our current compaction strategy
|
||||
// - Each tag/alt encodes to a minimum of 4 bytes
|
||||
//
|
||||
// This gives us ~1*3*4 or ~12 bytes per rid minimum. Rounding down to the
|
||||
// nearest power of 2 gives us ~block_size/8 rids per block at most.
|
||||
//
|
||||
#define LFSR_MID(_lfs, _bid, _rid) \
|
||||
(((_bid) << lfs_nlog2((_lfs)->cfg->block_size/8)) \
|
||||
| ((_rid) & ((1 << lfs_nlog2((_lfs)->cfg->block_size/8))-1)))
|
||||
|
||||
// reserved values to represent "." and ".." entries
|
||||
//
|
||||
// note these need to be positive values to fit in the lfsr_soff_t returned
|
||||
// by lfsr_dir_tell
|
||||
//
|
||||
// TODO is this really the best way to carve out space for these entries?
|
||||
// TODO do we need an mdir_limit in the superconfig? since this makes
|
||||
// mid=max problematic
|
||||
enum {
|
||||
LFSR_MID_DOT = 0x7fffffff & -2,
|
||||
LFSR_MID_DOTDOT = 0x7fffffff & -1,
|
||||
};
|
||||
|
||||
static inline lfs_ssize_t lfsr_mid_bid(lfs_t *lfs, lfsr_mid_t mid) {
|
||||
// TODO can we get rid of this?
|
||||
// adjust mid if mtree is inlined
|
||||
if (lfsr_mtree_isinlined(lfs)) {
|
||||
return LFSR_MID_MROOT;
|
||||
}
|
||||
// TODO should this be stored in lfs_t?
|
||||
return mid >> lfs_nlog2(lfs->cfg->block_size/8);
|
||||
}
|
||||
|
||||
static inline lfs_size_t lfsr_mid_rid(lfs_t *lfs, lfsr_mid_t mid) {
|
||||
// TODO should this be stored in lfs_t?
|
||||
return mid & ((1 << lfs_nlog2(lfs->cfg->block_size/8))-1);
|
||||
}
|
||||
|
||||
|
||||
// mptr things
|
||||
typedef struct lfsr_mptr {
|
||||
lfs_block_t blocks[2];
|
||||
@@ -6288,6 +6240,8 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
|
||||
|
||||
// first play out any attrs that change our rid
|
||||
for (lfs_size_t i = 0; i < attr_count; i++) {
|
||||
// TODO clean this up a bit
|
||||
// adjust opened mdirs?
|
||||
if (opened->mdir.mid == mdir->mid
|
||||
&& opened->rid >= attrs[i].id) {
|
||||
// removed?
|
||||
@@ -6303,6 +6257,41 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
|
||||
}
|
||||
} else {
|
||||
opened->rid += attrs[i].delta;
|
||||
// adjust dir position?
|
||||
if (type == LFS_TYPE_DIR
|
||||
&& ((((lfsr_dir_t*)opened)->dstart_mid
|
||||
== mdir->mid
|
||||
&& ((lfsr_dir_t*)opened)->dstart_rid
|
||||
< attrs[i].id)
|
||||
|| ((lfsr_dir_t*)opened)->dstart_mid
|
||||
< mdir->mid)) {
|
||||
((lfsr_dir_t*)opened)->pos += attrs[i].delta;
|
||||
}
|
||||
}
|
||||
} else if (opened->mdir.mid > mdir->mid) {
|
||||
// adjust dir position?
|
||||
if (type == LFS_TYPE_DIR
|
||||
&& ((((lfsr_dir_t*)opened)->dstart_mid
|
||||
== mdir->mid
|
||||
&& ((lfsr_dir_t*)opened)->dstart_rid
|
||||
< attrs[i].id)
|
||||
|| ((lfsr_dir_t*)opened)->dstart_mid
|
||||
< mdir->mid)) {
|
||||
((lfsr_dir_t*)opened)->pos += attrs[i].delta;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO combine with above?
|
||||
// adjust opened dstarts?
|
||||
if (type == LFS_TYPE_DIR
|
||||
&& ((lfsr_dir_t*)opened)->dstart_mid == mdir->mid
|
||||
&& ((lfsr_dir_t*)opened)->dstart_rid >= attrs[i].id) {
|
||||
// removed?
|
||||
if (((lfsr_dir_t*)opened)->dstart_rid
|
||||
+ attrs[i].delta < attrs[i].id) {
|
||||
((lfsr_dir_t*)opened)->dstart_mid = LFSR_MID_RM;
|
||||
} else {
|
||||
((lfsr_dir_t*)opened)->dstart_rid += attrs[i].delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7830,9 +7819,8 @@ int lfsr_dir_open(lfs_t *lfs, lfsr_dir_t *dir, const char *path) {
|
||||
}
|
||||
|
||||
// read our did from the mdir, unless we're root
|
||||
if (err == LFS_ERR_INVAL) {
|
||||
dir->did = 0;
|
||||
} else {
|
||||
lfs_size_t did = 0;
|
||||
if (err != LFS_ERR_INVAL) {
|
||||
lfsr_data_t data;
|
||||
int err = lfsr_mdir_lookup(lfs, &mdir, rid, LFSR_TAG_DID,
|
||||
NULL, &data);
|
||||
@@ -7840,15 +7828,32 @@ int lfsr_dir_open(lfs_t *lfs, lfsr_dir_t *dir, const char *path) {
|
||||
return err;
|
||||
}
|
||||
|
||||
lfs_ssize_t d = lfsr_data_readleb128(lfs, data, 0, &dir->did);
|
||||
lfs_ssize_t d = lfsr_data_readleb128(lfs, data, 0, &did);
|
||||
if (d < 0) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
// leave it up to rewind to initialize mid/dots
|
||||
err = lfsr_dir_rewind(lfs, dir);
|
||||
// reset pos
|
||||
dir->pos = 0;
|
||||
|
||||
// lookup our dstart in the mtree
|
||||
err = lfsr_mtree_dnamelookup(lfs, did, NULL, 0,
|
||||
&dir->mdir.mdir, &dir->mdir.rid, NULL, NULL);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_NOENT);
|
||||
return err;
|
||||
}
|
||||
|
||||
// keep track of the mid/rid of our dstart
|
||||
dir->dstart_mid = dir->mdir.mdir.mid;
|
||||
dir->dstart_rid = dir->mdir.rid;
|
||||
|
||||
// eagerly look up the next entry
|
||||
//
|
||||
// this makes handling of corner cases with mixed removes/dir reads easier
|
||||
err = lfsr_mtree_seek(lfs, &dir->mdir.mdir, &dir->mdir.rid, 1);
|
||||
if (err && err != LFS_ERR_NOENT) {
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -7867,15 +7872,15 @@ int lfsr_dir_read(lfs_t *lfs, lfsr_dir_t *dir, struct lfs_info *info) {
|
||||
memset(info, 0, sizeof(struct lfs_info));
|
||||
|
||||
// handle dots specially
|
||||
if (dir->dots == 0) {
|
||||
if (dir->pos == 0) {
|
||||
info->type = LFS_TYPE_DIR;
|
||||
strcpy(info->name, ".");
|
||||
dir->dots += 1;
|
||||
dir->pos += 1;
|
||||
return 0;
|
||||
} else if (dir->dots == 1) {
|
||||
} else if (dir->pos == 1) {
|
||||
info->type = LFS_TYPE_DIR;
|
||||
strcpy(info->name, "..");
|
||||
dir->dots += 1;
|
||||
dir->pos += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -7895,18 +7900,19 @@ int lfsr_dir_read(lfs_t *lfs, lfsr_dir_t *dir, struct lfs_info *info) {
|
||||
return err;
|
||||
}
|
||||
|
||||
lfs_size_t did;
|
||||
lfs_ssize_t d = lfsr_data_readleb128(lfs, data, 0, &did);
|
||||
if (d < 0) {
|
||||
return d;
|
||||
}
|
||||
|
||||
// did mismatch? we must be done
|
||||
if (did != dir->did) {
|
||||
// found another directory's dstart? we must be done
|
||||
if (tag == LFSR_TAG_DSTART) {
|
||||
return LFS_ERR_NOENT;
|
||||
}
|
||||
|
||||
// get file type from the tag
|
||||
info->type = lfsr_tag_filetype(tag);
|
||||
|
||||
// get file name from the name entry
|
||||
lfs_ssize_t d = lfsr_data_readleb128(lfs, data, 0, &(uint32_t){0});
|
||||
if (d < 0) {
|
||||
return d;
|
||||
}
|
||||
LFS_ASSERT(lfsr_data_size(data)-d <= LFS_NAME_MAX);
|
||||
d = lfsr_data_read(lfs, data, d, info->name, LFS_NAME_MAX);
|
||||
if (d < 0) {
|
||||
@@ -7914,84 +7920,61 @@ int lfsr_dir_read(lfs_t *lfs, lfsr_dir_t *dir, struct lfs_info *info) {
|
||||
}
|
||||
info->name[d] = '\0';
|
||||
|
||||
// get file type from the tag
|
||||
info->type = lfsr_tag_filetype(tag);
|
||||
|
||||
// TODO get size once we actually have regular files
|
||||
// TODO get size once we actually have regular files
|
||||
|
||||
// eagerly look up the next entry
|
||||
err = lfsr_mtree_seek(lfs, &dir->mdir.mdir, &dir->mdir.rid, 1);
|
||||
if (err && err != LFS_ERR_NOENT) {
|
||||
return err;
|
||||
}
|
||||
dir->pos += 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfsr_dir_seek(lfs_t *lfs, lfsr_dir_t *dir, lfs_off_t off) {
|
||||
// off should never be negative, most likely an error went unchecked
|
||||
//
|
||||
// TODO is this the right approach? should we just assert if we detect
|
||||
// a modified directory during seek?
|
||||
// Note that aside from negative offsets, all other offset are accepted.
|
||||
// This makes it so seeking to an outdated offset in the case of concurrent
|
||||
// modification is at least not an assert, though it may repeat files in
|
||||
// the directory.
|
||||
LFS_ASSERT((lfs_soff_t)off > 0);
|
||||
|
||||
// handle dots specially
|
||||
if (off == LFSR_MID_DOT || off == LFSR_MID_DOTDOT) {
|
||||
int err = lfsr_dir_rewind(lfs, dir);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
dir->dots = off - LFSR_MID_DOT;
|
||||
return 0;
|
||||
}
|
||||
dir->dots = 2;
|
||||
|
||||
// find our mdir
|
||||
lfs_ssize_t mid = lfsr_mid_bid(lfs, off);
|
||||
int err = lfsr_mtree_lookup(lfs,
|
||||
lfs_smin32(mid, (lfs_ssize_t)lfsr_mtree_weight(lfs)-1),
|
||||
&dir->mdir.mdir);
|
||||
// first rewind
|
||||
int err = lfsr_dir_rewind(lfs, dir);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// and update our rid, clamping to rbyd weight
|
||||
lfs_ssize_t rid = lfsr_mid_rid(lfs, off);
|
||||
if (mid > dir->mdir.mdir.mid
|
||||
|| rid >= (lfs_ssize_t)dir->mdir.mdir.rbyd.weight) {
|
||||
dir->mdir.rid = dir->mdir.mdir.rbyd.weight;
|
||||
} else {
|
||||
dir->mdir.rid = rid;
|
||||
// then seek to the requested offset, we leave it up to lfsr_mtree_seek
|
||||
// to make this efficient
|
||||
//
|
||||
// note the -2 to adjust for dot entries
|
||||
if (off > 2) {
|
||||
err = lfsr_mtree_seek(lfs, &dir->mdir.mdir, &dir->mdir.rid, off - 2);
|
||||
if (err && err != LFS_ERR_NOENT) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
dir->pos = off;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
lfs_soff_t lfsr_dir_tell(lfs_t *lfs, lfsr_dir_t *dir) {
|
||||
if (dir->dots == 0) {
|
||||
return LFSR_MID_DOT;
|
||||
} else if (dir->dots == 1) {
|
||||
return LFSR_MID_DOTDOT;
|
||||
} else {
|
||||
return LFSR_MID(lfs, lfs_smax32(dir->mdir.mdir.mid, 0), dir->mdir.rid);
|
||||
}
|
||||
(void)lfs;
|
||||
return dir->pos;
|
||||
}
|
||||
|
||||
int lfsr_dir_rewind(lfs_t *lfs, lfsr_dir_t *dir) {
|
||||
// reset dots
|
||||
dir->dots = 0;
|
||||
// do nothing if removed
|
||||
if (dir->dstart_mid == LFSR_MID_RM) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// lookup our dstart in the mtree
|
||||
int err = lfsr_mtree_dnamelookup(lfs, dir->did, NULL, 0,
|
||||
&dir->mdir.mdir, &dir->mdir.rid, NULL, NULL);
|
||||
// reset pos
|
||||
dir->pos = 0;
|
||||
|
||||
// lookup our dstart in the mtree again
|
||||
int err = lfsr_mtree_lookup(lfs, dir->dstart_mid, &dir->mdir.mdir);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_NOENT);
|
||||
return err;
|
||||
}
|
||||
dir->mdir.rid = dir->dstart_rid;
|
||||
|
||||
// eagerly look up the next entry
|
||||
//
|
||||
|
||||
@@ -47,11 +47,6 @@ typedef uint32_t lfs_block_t;
|
||||
typedef uint16_t lfsr_tag_t;
|
||||
typedef int16_t lfsr_stag_t;
|
||||
|
||||
// mids are a tuple of bid (index in the mtree) and rid (index in the mdir)
|
||||
// shoved into an integer
|
||||
typedef lfs_size_t lfsr_mid_t;
|
||||
typedef lfs_ssize_t lfsr_smid_t;
|
||||
|
||||
// Maximum name size in bytes, may be redefined to reduce the size of the
|
||||
// info struct. Limited to <= 1022. Stored in superblock and must be
|
||||
// respected by other littlefs drivers.
|
||||
@@ -429,8 +424,9 @@ typedef struct lfs_dir {
|
||||
|
||||
typedef struct lfsr_dir {
|
||||
lfsr_openedmdir_t mdir;
|
||||
lfs_size_t did;
|
||||
uint8_t dots;
|
||||
lfs_ssize_t dstart_mid;
|
||||
lfs_ssize_t dstart_rid;
|
||||
lfs_off_t pos;
|
||||
} lfsr_dir_t;
|
||||
|
||||
// littlefs file type
|
||||
|
||||
+174
-70
@@ -5081,37 +5081,31 @@ code = '''
|
||||
|
||||
// read our directory
|
||||
//
|
||||
// we can't really make many assumptions about tell's value, so just
|
||||
// check for no errors
|
||||
// Note tell's value is not guaranteed! We can test the exact value only
|
||||
// because these tests are tightly bound to the current littlefs version.
|
||||
//
|
||||
lfsr_dir_t dir;
|
||||
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0;
|
||||
lfs_soff_t off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 0;
|
||||
struct lfs_info info;
|
||||
off = lfsr_dir_read(&lfs, &dir, &info);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, ".") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 1;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
for (lfs_size_t i = 0; i < N; i++) {
|
||||
off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 2 + i;
|
||||
char name[256];
|
||||
sprintf(name, "dir%04d", i);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, name) == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
}
|
||||
off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 2 + N;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 2 + N;
|
||||
lfsr_dir_close(&lfs, &dir) => 0;
|
||||
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
@@ -5140,58 +5134,48 @@ code = '''
|
||||
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0;
|
||||
|
||||
// read our directory once
|
||||
lfs_soff_t off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 0;;
|
||||
struct lfs_info info;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, ".") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 1;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
for (lfs_size_t i = 0; i < N; i++) {
|
||||
off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 2 + i;
|
||||
char name[256];
|
||||
sprintf(name, "dir%04d", i);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, name) == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
}
|
||||
off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 2 + N;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 2 + N;
|
||||
|
||||
// now read it again
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 0;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, ".") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 1;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
for (lfs_size_t i = 0; i < N; i++) {
|
||||
off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 2 + i;
|
||||
char name[256];
|
||||
sprintf(name, "dir%04d", i);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, name) == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
}
|
||||
off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 2 + N;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 2 + N;
|
||||
|
||||
lfsr_dir_close(&lfs, &dir) => 0;
|
||||
|
||||
@@ -5221,53 +5205,47 @@ code = '''
|
||||
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0;
|
||||
|
||||
// read our directory once
|
||||
lfs_soff_t offs[2+N+2];
|
||||
offs[0] = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(offs[0] >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 0;
|
||||
struct lfs_info info;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, ".") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
offs[1] = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(offs[1] >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 1;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
for (lfs_size_t i = 0; i < N; i++) {
|
||||
offs[2+i] = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(offs[2+i] >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 2 + i;
|
||||
char name[256];
|
||||
sprintf(name, "dir%04d", i);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, name) == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
}
|
||||
offs[2+N+0] = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(offs[2+N+0] >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 2 + N;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
offs[2+N+1] = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(offs[2+N+1] >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 2 + N;
|
||||
|
||||
// now try to seek to each entry explicitly
|
||||
lfsr_dir_seek(&lfs, &dir, offs[0]) => 0;
|
||||
lfsr_dir_seek(&lfs, &dir, 0) => 0;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, ".") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
lfsr_dir_seek(&lfs, &dir, offs[1]) => 0;
|
||||
lfsr_dir_seek(&lfs, &dir, 1) => 0;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
for (lfs_size_t i = 0; i < N; i++) {
|
||||
lfsr_dir_seek(&lfs, &dir, offs[2+i]) => 0;
|
||||
lfsr_dir_seek(&lfs, &dir, 2 + i) => 0;
|
||||
char name[256];
|
||||
sprintf(name, "dir%04d", i);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, name) == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
}
|
||||
lfsr_dir_seek(&lfs, &dir, offs[2+N+0]) => 0;
|
||||
lfsr_dir_seek(&lfs, &dir, 2 + N) => 0;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
lfsr_dir_seek(&lfs, &dir, offs[2+N+1]) => 0;
|
||||
lfsr_dir_seek(&lfs, &dir, 2 + N) => 0;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
|
||||
lfsr_dir_close(&lfs, &dir) => 0;
|
||||
@@ -5320,8 +5298,7 @@ code = '''
|
||||
assert(strcmp(info.name, "ardvark") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
lfs_soff_t off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_tell(&lfs, &dir) => 3;
|
||||
|
||||
// reading again should still return noent
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
@@ -5329,10 +5306,13 @@ code = '''
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
|
||||
// seeking past the end of the directory should still return noent
|
||||
lfsr_dir_seek(&lfs, &dir, off) => 0;
|
||||
lfsr_dir_seek(&lfs, &dir, 3) => 0;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
|
||||
lfsr_dir_seek(&lfs, &dir, off + 1000) => 0;
|
||||
lfsr_dir_seek(&lfs, &dir, 4) => 0;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
|
||||
lfsr_dir_seek(&lfs, &dir, 1000) => 0;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
@@ -5367,7 +5347,10 @@ defines.PARENT = [false, true]
|
||||
# bit 0x2 = left neighbor
|
||||
# bit 0x1 = right neighbor
|
||||
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
|
||||
defines.SEEK = [false, true]
|
||||
# 0 => don't seek
|
||||
# 1 => seek
|
||||
# 2 => rewind then seek
|
||||
defines.SEEK = [0, 1, 2]
|
||||
# neighbors only make sense if we have a parent
|
||||
if = 'PARENT || NEIGHBORS == 0'
|
||||
code = '''
|
||||
@@ -5426,7 +5409,9 @@ code = '''
|
||||
if (SEEK) {
|
||||
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
if (SEEK >= 2) {
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
}
|
||||
lfsr_dir_seek(&lfs, &dir, off) => 0;
|
||||
}
|
||||
|
||||
@@ -5455,7 +5440,10 @@ defines.PARENT = [false, true]
|
||||
# bit 0x2 = left neighbor
|
||||
# bit 0x1 = right neighbor
|
||||
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
|
||||
defines.SEEK = [false, true]
|
||||
# 0 => don't seek
|
||||
# 1 => seek
|
||||
# 2 => rewind then seek
|
||||
defines.SEEK = [0, 1, 2]
|
||||
# neighbors only make sense if we have a parent
|
||||
if = 'PARENT || NEIGHBORS == 0'
|
||||
code = '''
|
||||
@@ -5514,7 +5502,9 @@ code = '''
|
||||
if (SEEK) {
|
||||
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
if (SEEK >= 2) {
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
}
|
||||
lfsr_dir_seek(&lfs, &dir, off) => 0;
|
||||
}
|
||||
|
||||
@@ -5552,7 +5542,10 @@ defines.PARENT = [0, 1, 2]
|
||||
# bit 0x2 = left neighbor
|
||||
# bit 0x1 = right neighbor
|
||||
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
|
||||
defines.SEEK = [false, true]
|
||||
# 0 => don't seek
|
||||
# 1 => seek
|
||||
# 2 => rewind then seek
|
||||
defines.SEEK = [0, 1, 2]
|
||||
# neighbors only make sense if we have a parent
|
||||
if = 'PARENT || NEIGHBORS == 0'
|
||||
code = '''
|
||||
@@ -5621,7 +5614,9 @@ code = '''
|
||||
if (SEEK) {
|
||||
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
if (SEEK >= 2) {
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
}
|
||||
lfsr_dir_seek(&lfs, &dir, off) => 0;
|
||||
}
|
||||
|
||||
@@ -5659,7 +5654,10 @@ defines.PARENT = [false, true]
|
||||
# bit 0x2 = left neighbor
|
||||
# bit 0x1 = right neighbor
|
||||
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
|
||||
defines.SEEK = [false, true]
|
||||
# 0 => don't seek
|
||||
# 1 => seek
|
||||
# 2 => rewind then seek
|
||||
defines.SEEK = [0, 1, 2]
|
||||
if = [
|
||||
'J != K',
|
||||
# neighbors only make sense if we have a parent
|
||||
@@ -5725,7 +5723,9 @@ code = '''
|
||||
if (SEEK) {
|
||||
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
if (SEEK >= 2) {
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
}
|
||||
lfsr_dir_seek(&lfs, &dir, off) => 0;
|
||||
}
|
||||
|
||||
@@ -5759,7 +5759,10 @@ defines.PARENT = [false, true]
|
||||
# bit 0x2 = left neighbor
|
||||
# bit 0x1 = right neighbor
|
||||
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
|
||||
defines.SEEK = [false, true]
|
||||
# 0 => don't seek
|
||||
# 1 => seek
|
||||
# 2 => rewind then seek
|
||||
defines.SEEK = [0, 1, 2]
|
||||
if = [
|
||||
'J != K',
|
||||
# neighbors only make sense if we have a parent
|
||||
@@ -5825,7 +5828,9 @@ code = '''
|
||||
if (SEEK) {
|
||||
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
if (SEEK >= 2) {
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
}
|
||||
lfsr_dir_seek(&lfs, &dir, off) => 0;
|
||||
}
|
||||
|
||||
@@ -5863,7 +5868,10 @@ defines.PARENT = [0, 1, 2]
|
||||
# bit 0x2 = left neighbor
|
||||
# bit 0x1 = right neighbor
|
||||
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
|
||||
defines.SEEK = [false, true]
|
||||
# 0 => don't seek
|
||||
# 1 => seek
|
||||
# 2 => rewind then seek
|
||||
defines.SEEK = [0, 1, 2]
|
||||
# neighbors only make sense if we have a parent
|
||||
if = [
|
||||
'J != K',
|
||||
@@ -5946,7 +5954,9 @@ code = '''
|
||||
if (SEEK) {
|
||||
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
if (SEEK >= 2) {
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
}
|
||||
lfsr_dir_seek(&lfs, &dir, off) => 0;
|
||||
}
|
||||
|
||||
@@ -5970,6 +5980,90 @@ code = '''
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# test removing the directory we are iterating over
|
||||
[cases.t5_dirs_read_rm]
|
||||
defines.N = 5
|
||||
# where in the dir read do we remove?
|
||||
defines.I = 'range(6)'
|
||||
# bit 0x2 = left neighbor
|
||||
# bit 0x1 = right neighbor
|
||||
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
|
||||
# 0 => don't seek
|
||||
# 1 => seek
|
||||
# 2 => rewind then seek
|
||||
defines.SEEK = [0, 1, 2]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfsr_format(&lfs, cfg) => 0;
|
||||
lfsr_mount(&lfs, cfg) => 0;
|
||||
|
||||
lfsr_mkdir(&lfs, "pricklypear") => 0;
|
||||
|
||||
if (NEIGHBORS & 0x2) {
|
||||
assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000);
|
||||
lfsr_mkdir(&lfs, "a_IplRNrPH") => 0;
|
||||
}
|
||||
|
||||
if (NEIGHBORS & 0x1) {
|
||||
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
|
||||
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
|
||||
}
|
||||
|
||||
// create our directories
|
||||
for (lfs_size_t i = 0; i < N; i++) {
|
||||
char name[256];
|
||||
sprintf(name, "pricklypear/dir%04d", i+1);
|
||||
lfsr_mkdir(&lfs, name) => 0;
|
||||
}
|
||||
|
||||
// start reading
|
||||
lfsr_dir_t dir;
|
||||
lfsr_dir_open(&lfs, &dir, "pricklypear") => 0;
|
||||
struct lfs_info info;
|
||||
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);
|
||||
|
||||
// read until I
|
||||
for (lfs_size_t i = 0; i < I; i++) {
|
||||
char name[256];
|
||||
sprintf(name, "dir%04d", i+1);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, name) == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
}
|
||||
|
||||
// remove the directory
|
||||
for (lfs_size_t i = 0; i < N; i++) {
|
||||
char name[256];
|
||||
sprintf(name, "pricklypear/dir%04d", i+1);
|
||||
lfsr_remove(&lfs, name) => 0;
|
||||
}
|
||||
lfsr_remove(&lfs, "pricklypear") => 0;
|
||||
|
||||
// seek after mkdir?
|
||||
if (SEEK) {
|
||||
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
if (SEEK >= 2) {
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
}
|
||||
lfsr_dir_seek(&lfs, &dir, off) => 0;
|
||||
}
|
||||
|
||||
// try to read, but this should return an error
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
lfsr_dir_close(&lfs, &dir) => 0;
|
||||
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
|
||||
|
||||
## Recursive tests
|
||||
|
||||
@@ -5981,7 +6075,10 @@ code = '''
|
||||
[cases.t5_dirs_rm_recursive]
|
||||
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
||||
defines.PARENT = [false, true]
|
||||
defines.SEEK = [false, true]
|
||||
# 0 => don't seek
|
||||
# 1 => seek
|
||||
# 2 => rewind then seek
|
||||
defines.SEEK = [0, 1, 2]
|
||||
# limit powerloss testing due to time
|
||||
if = '!TEST_PL || N <= 32'
|
||||
reentrant = true
|
||||
@@ -6062,7 +6159,9 @@ code = '''
|
||||
if (SEEK) {
|
||||
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
if (SEEK >= 2) {
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
}
|
||||
lfsr_dir_seek(&lfs, &dir, off) => 0;
|
||||
}
|
||||
}
|
||||
@@ -6102,7 +6201,10 @@ defines.BEFORE = [false, true]
|
||||
# 1 => yes
|
||||
# 2 => yes, and rename to new parent
|
||||
defines.PARENT = [0, 1, 2]
|
||||
defines.SEEK = [false, true]
|
||||
# 0 => don't seek
|
||||
# 1 => seek
|
||||
# 2 => rewind then seek
|
||||
defines.SEEK = [0, 1, 2]
|
||||
# limit powerloss testing due to time
|
||||
if = '!TEST_PL || N <= 32'
|
||||
reentrant = true
|
||||
@@ -6217,7 +6319,9 @@ code = '''
|
||||
if (SEEK) {
|
||||
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
|
||||
assert(off >= 0);
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
if (SEEK >= 2) {
|
||||
lfsr_dir_rewind(&lfs, &dir) => 0;
|
||||
}
|
||||
lfsr_dir_seek(&lfs, &dir, off) => 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user