diff --git a/lfs.c b/lfs.c index 850f2e70..8b69292d 100644 --- a/lfs.c +++ b/lfs.c @@ -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 // diff --git a/lfs.h b/lfs.h index 568d2119..6de7de8c 100644 --- a/lfs.h +++ b/lfs.h @@ -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 diff --git a/tests/t5_dirs.toml b/tests/t5_dirs.toml index 2a848b2c..4f160753 100644 --- a/tests/t5_dirs.toml +++ b/tests/t5_dirs.toml @@ -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; } }