trv: Greatly simplified filesystem traversal

The main idea here is to drop the flag-encoded tstate state machine, and
replace it with a matrix controlled by special mid + bid values:

                    -- mid ->
             -5   -4   -3   -2 >=-1
  bid   -2    x    x              x  --> mdir
   v  >=-1         x  gbm  gbm    x  --> bshrub/btree

              '----|----|----|----|----> mroot anchor
                   '----|----|----|----> mroot chain + mtree
                        '----|----|----> gbmap   (in-ram gbmap)
                             '----|----> gbmap_p (on-disk gbmap)
                                  '----> file bshrubs/btrees

This was motivated by the observation that everything in our filesystem
can be modeled as mdir + bshrub/btree tuples, as long as some states are
noops. And we can cleanly encode these tuples in the unused negative
mid + bid ranges without needing an explicit state machine.

Well, that and the previous tstate state machine approach being an ugly
pile of switch cases and messy logic.

Note though that some mids may need to traverse multiple mdirs/bshrub/
btrees:

- The mroot chain + mtree (mid=-4) needs to traverse all mroots in the
  mroot chain, and detect any cycles.

- File mdirs (mid>=-1) need to traverse both the on-disk bshrub/btree
  and any opened file handles' bshrubs/btrees before moving onto the
  next mid.

  This grows O(n^2) because all file handles are in one big unsorted
  linked-list, but as usual we don't care.

In addition to the greatly simplified traversal logic, the new state
matrix simplifies traversal clobbering: Setting bid=-2 always forces a
bshrub/btree refetch.

This comes at the cost of traversal _precision_, i.e. we can now revisit
previously visited bshrub/btree nodes. But I think this is well worth it
for more robust traversal clobbering. Traversal clobbering is delicate
and difficult to get right.

Besides, we can already revisit blocks due to CoW references, so what's
the harm in revisiting blocks when under mutation?

---

The simpler traversal logic leads to a nice amount of code savings
across the board:

                 code          stack          ctx
  before:       36476           2304          660
  after:        35940 (-1.5%)   2280 (-1.0%)  660 (+0.0%)

                 code          stack          ctx
  gbmap before: 39524           2320          772
  gbmap after:  38916 (-1.5%)   2296 (-1.0%)  772 (+0.0%)

                 code          stack          ctx
  gc before:    36548           2304          804
  gc after:     36012 (-1.5%)   2280 (-1.0%)  776 (-3.5%)

Note the ctx savings in LFS3_GC mode. Most of the stack/ctx savings
comes from the smaller lfs3_mtrv_t struct, which no longer needs to
stage bshrubs (we no longer care about bshrubs across mdir commit as a
part of the above clobbering simplifications):

                before  after
  lfs3_mtrv_t:     128    100 (-21.9%)
  lfs3_mgc_t:      128    100 (-21.9%)
  lfs3_trv_t:      136    108 (-20.6%)

Unfortunately, the simpler clobbering means now any gc work needs the
block queue (i.e. lfs3_trv_t), solely so clobbering the block queue
doesn't clobber unallocated memory. Not great but hopefully fixable.

---

Some other notes:

- As a part of simplifying traversal clobbering, everything is triggered
  by lfs3_alloc_ckpoint (via lfs3_trv_ckpoint_).

  This may clobber traversals more than is strictly necessary, but
  that's kinda the idea. Better safe than sorry.

  And no more need to explicit lfs3_handle_clobber calls is nice.

- Opened file handle iteration is now tracked by the traversal handle's
  position in the handle linked-list, instead of a separate handle
  pointer. This means one less thing to disentangle and makes traversals
  no longer a special case for things like lfs3_handle_close.

  You may think this bumps traversals up to O(n^3) in-ram, but because
  we only ever visit each unique handle + mid once, we can keep the
  total O(n^2) if we're smart about linked-list updates!

- lfs3_mdir_commit needed to be tweaked to accept mids<=-1, instead of
  just mid=-1 for the mroot. Unfortunately I don't know how much this
  costs on its own.

- The reorganization of lfs3_mtrv_t means lfs3_mtortoise_t gets its own
  struct again!

- No more tstate state machine also frees up a big chunk of the
  traversal flag space, which was getting pretty cramped.
This commit is contained in:
Christopher Haster
2025-10-29 01:51:37 -05:00
parent 9e006fd7dc
commit d1d69c0a52
6 changed files with 602 additions and 589 deletions

879
lfs3.c

File diff suppressed because it is too large Load Diff

27
lfs3.h
View File

@ -338,7 +338,6 @@ enum lfs3_btype {
// internally used flags, don't use these
#define LFS3_t_TYPE 0xf0000000 // The traversal's type
#define LFS3_t_TSTATE 0x000f0000 // The current traversal state
#define LFS3_t_BTYPE 0x00f00000 // The current block type
#define LFS3_t_ZOMBIE 0x08000000 // File has been removed
#define LFS3_t_DIRTY 0x04000000 // Filesystem ckpointed outside traversal
@ -858,14 +857,26 @@ typedef struct lfs3_btrv {
lfs3_srid_t rid;
} lfs3_btrv_t;
typedef struct lfs3_mtortoise {
// this aligns with btrv.bid
lfs3_sbid_t bid;
lfs3_block_t blocks[2];
lfs3_block_t dist;
uint8_t nlog2;
} lfs3_mtortoise_t;
typedef struct lfs3_mtrv {
// mdir/bshrub/btree state, this also includes our traversal
// state machine and cycle detection state
lfs3_bshrub_t b;
// opened file state
lfs3_handle_t *h;
// bshrub/btree traversal state
lfs3_btrv_t btrv;
// mtree traversal state, our position in then handle linked-list
// is also used to keep track of what handles we've seen
lfs3_handle_t h;
// current bshrub/btree
lfs3_btree_t b;
union {
// bshrub/btree traversal state
lfs3_btrv_t btrv;
// mtortoise for cycle detection
lfs3_mtortoise_t mtortoise;
} u;
// recalculate gcksum when traversing with ckmeta
uint32_t gcksum;

View File

@ -154,18 +154,6 @@ t_BOOKMARK = 0x40000000 # i^ Type = bookmark
t_ORPHAN = 0x50000000 # i^ Type = orphan
t_TRAVERSAL = 0x60000000 # i^ Type = traversal
t_UNKNOWN = 0x70000000 # i^ Type = unknown
t_TSTATE = 0x000f0000 # im The current traversal state
t_MROOTANCHOR = 0x00000000 # i^ Tstate = mroot-anchor
t_MROOTCHAIN = 0x00010000 # i^ Tstate = mroot-chain
t_MTREE = 0x00020000 # i^ Tstate = mtree
t_MDIRS = 0x00030000 # i^ Tstate = mtree-mdirs
t_MDIR = 0x00040000 # i^ Tstate = mdir
t_BTREE = 0x00050000 # i^ Tstate = btree
t_HANDLES = 0x00060000 # i^ Tstate = open-mdirs
t_HBTREE = 0x00070000 # i^ Tstate = open-btree
t_GBMAP = 0x00080000 # i^ Tstate = gbmap
t_GBMAP_P = 0x00090000 # i^ Tstate = gbmap_p
t_DONE = 0x000a0000 # i^ Tstate = done
t_BTYPE = 0x00f00000 # im The current block type
t_MDIR = 0x00100000 # i^ Btype = mdir
t_BTREE = 0x00200000 # i^ Btype = btree

View File

@ -1025,7 +1025,7 @@ code = '''
}
if (tinfo.btype == LFS3_BTYPE_BTREE
&& lfs3_t_tstate(trv.gc.t.b.h.flags) != LFS3_TSTATE_GBMAP) {
&& trv.gc.t.h.mdir.mid != LFS3_MID_GBMAP) {
if (k == i) {
// clobber this block
printf("clobbering 0x%x\n", tinfo.block);
@ -1125,7 +1125,7 @@ code = '''
if ((tinfo.btype == LFS3_BTYPE_BTREE
|| tinfo.btype == LFS3_BTYPE_DATA)
&& lfs3_t_tstate(trv.gc.t.b.h.flags) != LFS3_TSTATE_GBMAP) {
&& trv.gc.t.h.mdir.mid != LFS3_MID_GBMAP) {
if (k == i) {
// clobber this block
printf("clobbering 0x%x\n", tinfo.block);
@ -1231,7 +1231,7 @@ code = '''
}
if (tinfo.btype == LFS3_BTYPE_BTREE
&& lfs3_t_tstate(trv.gc.t.b.h.flags) != LFS3_TSTATE_GBMAP) {
&& trv.gc.t.h.mdir.mid != LFS3_MID_GBMAP) {
// found an interesting block?
if (k == i) {
badblock = tinfo.block;
@ -1382,7 +1382,7 @@ code = '''
if ((tinfo.btype == LFS3_BTYPE_BTREE
|| tinfo.btype == LFS3_BTYPE_DATA)
&& lfs3_t_tstate(trv.gc.t.b.h.flags) != LFS3_TSTATE_GBMAP) {
&& trv.gc.t.h.mdir.mid != LFS3_MID_GBMAP) {
// found an interesting block?
if (k == i) {
badblock = tinfo.block;

View File

@ -57,7 +57,7 @@ code = '''
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_RELOOKAHEAD);
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.h);
// run GC until we make progress
for (lfs3_block_t i = 0;; i++) {
@ -127,11 +127,11 @@ code = '''
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_RELOOKAHEAD);
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.h);
// run GC one step
lfs3_fs_gc(&lfs3) => 0;
assert(lfs3.handles == &lfs3.gc.gc.t.b.h);
assert(lfs3.handles == &lfs3.gc.gc.t.h);
// mutate the filesystem
lfs3_file_open(&lfs3, &file, "spider",
@ -143,7 +143,7 @@ code = '''
lfs3_file_close(&lfs3, &file) => 0;
// run GC until our traversal is done
while (lfs3.handles == &lfs3.gc.gc.t.b.h) {
while (lfs3.handles == &lfs3.gc.gc.t.h) {
lfs3_fs_gc(&lfs3) => 0;
}
@ -318,7 +318,7 @@ code = '''
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_REGBMAP);
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.h);
// run GC until we make progress
for (lfs3_block_t i = 0;; i++) {
@ -391,11 +391,11 @@ code = '''
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_REGBMAP);
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.h);
// run GC one step
lfs3_fs_gc(&lfs3) => 0;
assert(lfs3.handles == &lfs3.gc.gc.t.b.h);
assert(lfs3.handles == &lfs3.gc.gc.t.h);
// mutate the filesystem
lfs3_file_open(&lfs3, &file, "spider",
@ -407,7 +407,7 @@ code = '''
lfs3_file_close(&lfs3, &file) => 0;
// run GC until our traversal is done
while (lfs3.handles == &lfs3.gc.gc.t.b.h) {
while (lfs3.handles == &lfs3.gc.gc.t.h) {
lfs3_fs_gc(&lfs3) => 0;
}
@ -592,7 +592,7 @@ code = '''
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_COMPACTMETA);
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.h);
// run GC until we make progress
for (lfs3_block_t i = 0;; i++) {
@ -684,19 +684,19 @@ code = '''
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_COMPACTMETA);
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.h);
// run GC one traversal + one step
while (true) {
lfs3_fs_gc(&lfs3) => 0;
// internal traversal done?
if (lfs3.handles != &lfs3.gc.gc.t.b.h) {
if (lfs3.handles != &lfs3.gc.gc.t.h) {
break;
}
}
lfs3_fs_gc(&lfs3) => 0;
assert(lfs3.handles == &lfs3.gc.gc.t.b.h);
assert(lfs3.handles == &lfs3.gc.gc.t.h);
// mutate the filesystem
lfs3_file_rewind(&lfs3, &file) => 0;
@ -707,7 +707,7 @@ code = '''
lfs3_file_sync(&lfs3, &file) => 0;
// run GC until our traversal is done (twice for compactmeta)
while (lfs3.handles == &lfs3.gc.gc.t.b.h) {
while (lfs3.handles == &lfs3.gc.gc.t.h) {
lfs3_fs_gc(&lfs3) => 0;
}
@ -811,7 +811,7 @@ code = '''
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_MKCONSISTENT);
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.h);
// run GC until we make progress
for (lfs3_block_t i = 0;; i++) {
@ -917,7 +917,7 @@ code = '''
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_MKCONSISTENT);
#ifdef LFS3_GC
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.h);
#endif
// call lfs3_fs_mkconsistent
@ -1021,9 +1021,9 @@ code = '''
}
// run GC one step
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.h);
lfs3_fs_gc(&lfs3) => 0;
assert(lfs3.handles == &lfs3.gc.gc.t.b.h);
assert(lfs3.handles == &lfs3.gc.gc.t.h);
// create the rest of the orphans after GC has started
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
@ -1045,7 +1045,7 @@ code = '''
assert(fsinfo.flags & LFS3_I_MKCONSISTENT);
// run GC until our traversal is done
while (lfs3.handles == &lfs3.gc.gc.t.b.h) {
while (lfs3.handles == &lfs3.gc.gc.t.h) {
lfs3_fs_gc(&lfs3) => 0;
}

View File

@ -1882,12 +1882,12 @@ code = '''
lfs3_file_open(&lfs3, &file, "spider",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_close(&lfs3, &file) => 0;
} else {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
assert(tinfo.block == 0 || tinfo.block == 1);
}
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
assert(tinfo.block == 0 || tinfo.block == 1);
if (WHEN == 2) {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "spider",
@ -1948,9 +1948,6 @@ code = '''
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_close(&lfs3, &file) => 0;
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
assert(tinfo.block == 0 || tinfo.block == 1);
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
@ -2034,12 +2031,12 @@ code = '''
if (WHEN == 1) {
lfs3_mkdir(&lfs3, "spider") => 0;
} else {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
assert(tinfo.block == 0 || tinfo.block == 1);
}
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
assert(tinfo.block == 0 || tinfo.block == 1);
if (WHEN == 2) {
lfs3_mkdir(&lfs3, "spider") => 0;
}
@ -2104,12 +2101,12 @@ code = '''
if (WHEN == 1) {
lfs3_remove(&lfs3, "spider") => 0;
} else {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
assert(tinfo.block == 0 || tinfo.block == 1);
}
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
assert(tinfo.block == 0 || tinfo.block == 1);
if (WHEN == 2) {
lfs3_remove(&lfs3, "spider") => 0;
}
@ -2174,11 +2171,12 @@ code = '''
if (WHEN == 1) {
lfs3_rename(&lfs3, "spider", "scorpion") => 0;
} else {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
assert(tinfo.block == 0 || tinfo.block == 1);
}
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
assert(tinfo.block == 0 || tinfo.block == 1);
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
@ -2188,6 +2186,13 @@ code = '''
if (WHEN == 2) {
lfs3_rename(&lfs3, "spider", "scorpion") => 0;
// traverse gbmap again
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
}
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
@ -2248,7 +2253,8 @@ code = '''
| ((CKMETA) ? LFS3_T_CKMETA : 0)
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
while (true) {
// mutating traversals may never terminate, so stop at some point
for (lfs3_size_t i = 0; i < 4*BLOCK_COUNT; i++) {
// rewrite the file every step of the traversal
lfs3_file_open(&lfs3, &file, "spider",
LFS3_O_WRONLY | LFS3_O_TRUNC) => 0;
@ -2337,7 +2343,8 @@ code = '''
| ((CKMETA) ? LFS3_T_CKMETA : 0)
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
while (true) {
// mutating traversals may never terminate, so stop at some point
for (lfs3_size_t i = 0; i < 4*BLOCK_COUNT; i++) {
// rewrite the file every step of the traversal
lfs3_file_rewind(&lfs3, &file) => 0;
for (lfs3_size_t j = 0; j < SIZE; j++) {
@ -2572,6 +2579,14 @@ code = '''
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// traverse btree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
// traverse btree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
@ -2682,6 +2697,11 @@ code = '''
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
@ -2891,6 +2911,14 @@ code = '''
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
lfs3_file_flush(&lfs3, &file1) => 0;
// traverse btree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
// traverse btree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
@ -3003,6 +3031,11 @@ code = '''
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
lfs3_file_flush(&lfs3, &file1) => 0;
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
@ -3108,6 +3141,15 @@ code = '''
}
lfs3_file_close(&lfs3, &file1) => 0;
// if not desync, traversal rewinds to mdir
if (!DESYNC) {
// traverse gbmap again
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
}
// we should be at end of traversal now
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
@ -3222,6 +3264,18 @@ code = '''
}
lfs3_file_close(&lfs3, &file1) => 0;
// if not desync, traversal rewinds to mdir
if (!DESYNC) {
// traverse btree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
// traverse one data block
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
}
// traverse another data block
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
// traverse btree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
@ -3341,6 +3395,15 @@ code = '''
}
lfs3_file_close(&lfs3, &file1) => 0;
// if not desync, traversal rewinds to mdir
if (!DESYNC) {
// traverse one data block
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
}
// traverse another data block
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
@ -3676,6 +3739,11 @@ code = '''
// rename one file over another
lfs3_rename(&lfs3, "tarantula", "spider") => 0;
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
@ -3773,6 +3841,12 @@ code = '''
i += 1;
}
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// traverse mdir
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
@ -3904,7 +3978,9 @@ code = '''
i += 1;
}
// traverse another data block
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
// traverse two data blocks
@ -3912,7 +3988,7 @@ code = '''
assert(tinfo.btype == LFS3_BTYPE_DATA);
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
// traverse mdir
// traverse an mdir
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
@ -4043,7 +4119,9 @@ code = '''
i += 1;
}
// traverse another data block
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
// traverse two data blocks
@ -4146,6 +4224,12 @@ code = '''
lfs3_remove(&lfs3, "uloborus") => 0;
}
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
@ -4258,7 +4342,9 @@ code = '''
lfs3_remove(&lfs3, "uloborus") => 0;
}
// traverse another data block
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
// traverse two data blocks
@ -4370,6 +4456,17 @@ code = '''
// still traverse inlined mroots, so if this breaks in the future
// I wouldn't worry too much about it
//
// traverse an mdir
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
@ -4496,7 +4593,9 @@ code = '''
lfs3_remove(&lfs3, "uloborus") => 0;
}
// traverse another data block
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
// traverse two data blocks
@ -4635,6 +4734,15 @@ code = '''
i += 1;
}
// traverse mtree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// traverse mdir
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
@ -4846,7 +4954,9 @@ code = '''
i += 1;
}
// traverse another data block
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
// traverse mdir
@ -5044,7 +5154,9 @@ code = '''
lfs3_file_close(&lfs3, &file) => 0;
i += 1;
}
// traverse another data block
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
// traverse mdir
@ -5201,6 +5313,15 @@ code = '''
lfs3_remove(&lfs3, "vulsor") => 0;
}
// traverse mtree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// traverse mdir
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
@ -5410,7 +5531,9 @@ code = '''
lfs3_remove(&lfs3, "vulsor") => 0;
}
// traverse another data block
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
// traverse two data blocks
@ -5578,6 +5701,20 @@ code = '''
lfs3_remove(&lfs3, "vulsor") => 0;
}
// traverse mdir
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
// traverse mtree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// traverse mdir
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
@ -5797,7 +5934,9 @@ code = '''
lfs3_remove(&lfs3, "vulsor") => 0;
}
// traverse another data block
// traverse two data blocks
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
// traverse two data blocks
@ -7193,10 +7332,12 @@ code = '''
// keep traversing
if (ORPHANS <= 3) {
// traverse mroot
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
assert(tinfo.block == 0 || tinfo.block == 1);
if (ORPHANS == 0) {
// traverse mroot
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
assert(tinfo.block == 0 || tinfo.block == 1);
}
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
@ -7204,6 +7345,12 @@ code = '''
assert(tinfo.block == 2);
}
} else {
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// traverse mdirs
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
@ -8350,10 +8497,12 @@ code = '''
// keep traversing
if (ORPHANS <= 3) {
// traverse mroot
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
assert(tinfo.block == 0 || tinfo.block == 1);
if (ORPHANS == 0) {
// traverse mroot
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
assert(tinfo.block == 0 || tinfo.block == 1);
}
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
@ -8361,6 +8510,12 @@ code = '''
assert(tinfo.block == 2);
}
} else {
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// traverse mdirs
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);