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:
Christopher Haster
2023-07-28 12:58:16 -05:00
parent edd12e1f93
commit 4cf5509c91
3 changed files with 276 additions and 193 deletions
+174 -70
View File
@@ -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;
}
}