dca915dd95
It's funny to see what originally started as a simple list of rbyd attrs
slowly morph into a full isa. But it makes sense. What we really want is
an abstract description of operations that can be played and replayed as
necessary to atomically update the mtree.
Using a fixed lfs3_rattr_t struct to represent this in C is easy, and
avoids strict-aliasing issues, but ultimately limited when it comes to
the wide-range of data we want to attach to attributes.
Unlike a computer's isa, we want to be able to include full 12-24 byte
branch pointers directly in the instruction!
---
So here's a full variable-length isa organized by words (max(uintptr_t,
uint32_t)).
The first 32-bit word extends the 16-bit tag with an extra 16-bits of
control information:
wwll llff ffcc cccc tttt tttt tttt tttt
^'-.-''-.-''--.--' : :
'--|----|-----|----:-----------------:-- compressed weight
:: '----|-----|----:-----------------:-- total len
:: '-----|----:-----------------:-- from encoder
:: '----:-----------------:-- optional count
:: rgmm kkkk -kkk kkkk
11 => w=-1 ^^ ^ '-.' '---.---'
00 => w=0 '|-|---|------|------ rm bit
01 => w=+1 '-|---|------|------ grow bit
10 => w=attached '---|------|------ mask bits
'------|------ tag suptype
'------ tag subtype
The 4-bit length field always encodes the full length of the
instruction, including the instruction itself and optional weight. The
4-bit from + 6-bit count fields operate independently and tell
lfs3_rbyd_appendrattr_ how to actually encode the data related to the
instruction.
To work around strict-aliasing issues, complex structs are expected to
be broken down into words and reconstructed in lfs3_rbyd_appendrattr_.
Most of our structs are organized into words anyways. For example:
// new child
*r++ = LFS3_RATTR(5, LFS3_TAG_BRANCH, -2, LFS3_FROM_BRANCH);
*r++ = LFS3_RATTR_WEIGHT(+child_->weight);
*r++ = LFS3_RATTR_ARG(child_->blocks[0]);
*r++ = LFS3_RATTR_ARG(child_->trunk);
*r++ = LFS3_RATTR_ARG(child_->cksum);
This also changes rattr-lists to be null-terminated, which makes a bit
more sense in a variable-length isa:
*r++ = LFS3_RATTR_NULL; // all zeros, including length
One concern with null-terminated rattr-lists is how easy it is to
forget the null-terminator, but an assert that all non-null rattrs have
non-zero length seemed to catch the many many mistakes during adoption.
Alternatively, separate LFS3_FROM_NULL/LFS3_FROM_NIL from fields could
be used if encoding space gets tight.
I'm also quite happy with the 2-bit weight feild, which allows omitting
the optional weight word for -1,0,+1 weights. These should cover at
least all mdir operations.
Note the exact encoding of the rattr fields is less of a concern than
the tag fields, as it doesn't reside on-disk can be changed on whim.
---
Saves a nice chunk of code and stack:
code stack ctx
before: 35920 2280 660
after: 35324 (-1.7%) 2176 (-4.6%) 660 (+0.0%)
code stack ctx
gbmap before: 38812 2296 772
gbmap after: 38156 (-1.7%) 2192 (-4.5%) 772 (+0.0%)
The stack savings are obvious, but the code savings a bit less so. A
variable length isa _is_ more complicated, but by limiting most encoding
decisions to compile-time (2-bit weights vs 32-bit weights for example),
the savings from fewer word manipulations on the stack wins.
1476 lines
43 KiB
TOML
1476 lines
43 KiB
TOML
# Test some low-level block-map operations
|
|
#
|
|
# Note these is very much not-exhaustive, but the entire test suite can
|
|
# be run with the gbmap if you define LFS3_YES_GBMAP:
|
|
#
|
|
# LFS3_YES_GBMAP=1 make test -j
|
|
#
|
|
after = ['test_btree', 'test_mtree']
|
|
ifdef = 'LFS3_GBMAP'
|
|
|
|
|
|
# test low-level gbmap operations
|
|
|
|
# test simple set operations
|
|
[cases.test_gbmap_mark_split]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_alloc_ckpoint(&lfs3);
|
|
|
|
// create an initial gbmap
|
|
lfs3_btree_t gbmap;
|
|
lfs3_btree_init(&gbmap);
|
|
lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS(
|
|
LFS3_RATTR(2, LFS3_TAG_BMFREE, -2),
|
|
LFS3_RATTR_WEIGHT(+BLOCK_COUNT),
|
|
LFS3_RATTR_NULL)) => 0;
|
|
|
|
// set some blocks as in-use, avoid any weird merges for now
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE) => 0;
|
|
printf("gbmap: w%d 0x%x.%x\n",
|
|
gbmap.r.weight,
|
|
gbmap.r.blocks[0],
|
|
gbmap.r.trunk);
|
|
|
|
// weight should stay the same
|
|
assert(gbmap.r.weight == BLOCK_COUNT);
|
|
|
|
// check ranges
|
|
lfs3_bid_t bid_;
|
|
lfs3_bid_t weight_;
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0,
|
|
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
|
assert(bid_ == 5);
|
|
assert(weight_ == 6);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 6,
|
|
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
|
assert(bid_ == 6);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7,
|
|
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
|
assert(bid_ == 7);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8,
|
|
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
|
assert(bid_ == 8);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 9,
|
|
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
|
assert(bid_ == 9);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 10,
|
|
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
|
assert(bid_ == 10);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11,
|
|
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
|
assert(bid_ == BLOCK_COUNT-1);
|
|
assert(weight_ == BLOCK_COUNT-11);
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_gbmap_mark_replace]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_alloc_ckpoint(&lfs3);
|
|
|
|
// create an initial gbmap
|
|
lfs3_btree_t gbmap;
|
|
lfs3_btree_init(&gbmap);
|
|
lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS(
|
|
LFS3_RATTR(2, LFS3_TAG_BMFREE, -2),
|
|
LFS3_RATTR_WEIGHT(+BLOCK_COUNT),
|
|
LFS3_RATTR_NULL)) => 0;
|
|
|
|
// set some blocks as in-use, avoid any weird merges for now
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE) => 0;
|
|
// replace those blocks as bad, this tests deleting ranges
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMBAD) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMBAD) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMBAD) => 0;
|
|
printf("gbmap: w%d 0x%x.%x\n",
|
|
gbmap.r.weight,
|
|
gbmap.r.blocks[0],
|
|
gbmap.r.trunk);
|
|
|
|
// weight should stay the same
|
|
assert(gbmap.r.weight == BLOCK_COUNT);
|
|
|
|
// check ranges
|
|
lfs3_bid_t bid_;
|
|
lfs3_bid_t weight_;
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0,
|
|
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
|
assert(bid_ == 5);
|
|
assert(weight_ == 6);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 6,
|
|
&bid_, &weight_) => LFS3_TAG_BMBAD;
|
|
assert(bid_ == 6);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7,
|
|
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
|
assert(bid_ == 7);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8,
|
|
&bid_, &weight_) => LFS3_TAG_BMBAD;
|
|
assert(bid_ == 8);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 9,
|
|
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
|
assert(bid_ == 9);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 10,
|
|
&bid_, &weight_) => LFS3_TAG_BMBAD;
|
|
assert(bid_ == 10);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11,
|
|
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
|
assert(bid_ == BLOCK_COUNT-1);
|
|
assert(weight_ == BLOCK_COUNT-11);
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_gbmap_mark_merge]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_alloc_ckpoint(&lfs3);
|
|
|
|
// create an initial gbmap
|
|
lfs3_btree_t gbmap;
|
|
lfs3_btree_init(&gbmap);
|
|
lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS(
|
|
LFS3_RATTR(2, LFS3_TAG_BMFREE, -2),
|
|
LFS3_RATTR_WEIGHT(+BLOCK_COUNT),
|
|
LFS3_RATTR_NULL)) => 0;
|
|
|
|
// set some blocks as in-use, avoid any weird merges for now
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 12, LFS3_TAG_BMINUSE) => 0;
|
|
// set neighboring blocks as in-use, triggering merges
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 5, LFS3_TAG_BMINUSE) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 9, LFS3_TAG_BMINUSE) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 13, LFS3_TAG_BMINUSE) => 0;
|
|
printf("gbmap: w%d 0x%x.%x\n",
|
|
gbmap.r.weight,
|
|
gbmap.r.blocks[0],
|
|
gbmap.r.trunk);
|
|
|
|
// weight should stay the same
|
|
assert(gbmap.r.weight == BLOCK_COUNT);
|
|
|
|
// check ranges
|
|
lfs3_bid_t bid_;
|
|
lfs3_bid_t weight_;
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0,
|
|
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
|
assert(bid_ == 4);
|
|
assert(weight_ == 5);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 5,
|
|
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
|
assert(bid_ == 6);
|
|
assert(weight_ == 2);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7,
|
|
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
|
assert(bid_ == 7);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8,
|
|
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
|
assert(bid_ == 10);
|
|
assert(weight_ == 3);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11,
|
|
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
|
assert(bid_ == 11);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 12,
|
|
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
|
assert(bid_ == 13);
|
|
assert(weight_ == 2);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 14,
|
|
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
|
assert(bid_ == BLOCK_COUNT-1);
|
|
assert(weight_ == BLOCK_COUNT-14);
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_gbmap_mark_noop]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_alloc_ckpoint(&lfs3);
|
|
|
|
// create an initial gbmap
|
|
lfs3_btree_t gbmap;
|
|
lfs3_btree_init(&gbmap);
|
|
lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS(
|
|
LFS3_RATTR(2, LFS3_TAG_BMFREE, -2),
|
|
LFS3_RATTR_WEIGHT(+BLOCK_COUNT),
|
|
LFS3_RATTR_NULL)) => 0;
|
|
|
|
// set some blocks as in-use, avoid any weird merges for now
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE) => 0;
|
|
// test a bunch of noops
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 7, LFS3_TAG_BMFREE) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 9, LFS3_TAG_BMFREE) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE) => 0;
|
|
printf("gbmap: w%d 0x%x.%x\n",
|
|
gbmap.r.weight,
|
|
gbmap.r.blocks[0],
|
|
gbmap.r.trunk);
|
|
|
|
// weight should stay the same
|
|
assert(gbmap.r.weight == BLOCK_COUNT);
|
|
|
|
// check ranges
|
|
lfs3_bid_t bid_;
|
|
lfs3_bid_t weight_;
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0,
|
|
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
|
assert(bid_ == 5);
|
|
assert(weight_ == 6);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 6,
|
|
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
|
assert(bid_ == 6);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7,
|
|
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
|
assert(bid_ == 7);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8,
|
|
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
|
assert(bid_ == 8);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 9,
|
|
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
|
assert(bid_ == 9);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 10,
|
|
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
|
assert(bid_ == 10);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11,
|
|
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
|
assert(bid_ == BLOCK_COUNT-1);
|
|
assert(weight_ == BLOCK_COUNT-11);
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_gbmap_mark_bounds]
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_alloc_ckpoint(&lfs3);
|
|
|
|
// create an initial gbmap
|
|
lfs3_btree_t gbmap;
|
|
lfs3_btree_init(&gbmap);
|
|
lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS(
|
|
LFS3_RATTR(2, LFS3_TAG_BMFREE, -2),
|
|
LFS3_RATTR_WEIGHT(+BLOCK_COUNT),
|
|
LFS3_RATTR_NULL)) => 0;
|
|
|
|
// test that setting the first and last blocks don't break anything
|
|
//
|
|
// though in theory blocks 0x{0,1} are immutable...
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, 0, LFS3_TAG_BMINUSE) => 0;
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, BLOCK_COUNT-1, LFS3_TAG_BMINUSE) => 0;
|
|
printf("gbmap: w%d 0x%x.%x\n",
|
|
gbmap.r.weight,
|
|
gbmap.r.blocks[0],
|
|
gbmap.r.trunk);
|
|
|
|
// weight should stay the same
|
|
assert(gbmap.r.weight == BLOCK_COUNT);
|
|
|
|
// check ranges
|
|
lfs3_bid_t bid_;
|
|
lfs3_bid_t weight_;
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0,
|
|
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
|
assert(bid_ == 0);
|
|
assert(weight_ == 1);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 1,
|
|
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
|
assert(bid_ == BLOCK_COUNT-2);
|
|
assert(weight_ == BLOCK_COUNT-2);
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, BLOCK_COUNT-1,
|
|
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
|
assert(bid_ == BLOCK_COUNT-1);
|
|
assert(weight_ == 1);
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_gbmap_mark_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.TYPES = [2, 3, 4]
|
|
defines.SEED = 'range(20)'
|
|
fuzz = 'SEED'
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_alloc_ckpoint(&lfs3);
|
|
|
|
// create an initial gbmap
|
|
lfs3_btree_t gbmap;
|
|
lfs3_btree_init(&gbmap);
|
|
lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS(
|
|
LFS3_RATTR(2, LFS3_TAG_BMFREE, -2),
|
|
LFS3_RATTR_WEIGHT(+BLOCK_COUNT),
|
|
LFS3_RATTR_NULL)) => 0;
|
|
|
|
// create a simulation to compare against
|
|
lfs3_tag_t *sim = malloc(BLOCK_COUNT*sizeof(lfs3_tag_t));
|
|
for (lfs3_size_t i = 0; i < BLOCK_COUNT; i++) {
|
|
sim[i] = LFS3_TAG_BMFREE;
|
|
}
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// choose a pseudo-random block
|
|
lfs3_block_t block = TEST_PRNG(&prng) % BLOCK_COUNT;
|
|
// and pseudo-random gbmap type
|
|
lfs3_tag_t tag = LFS3_TAG_BMRANGE + (TEST_PRNG(&prng) % TYPES);
|
|
|
|
// set in gbmap
|
|
lfs3_gbmap_mark(&lfs3, &gbmap, block, tag) => 0;
|
|
|
|
// and set in sim
|
|
sim[block] = tag;
|
|
}
|
|
printf("gbmap: w%d 0x%x.%x\n",
|
|
gbmap.r.weight,
|
|
gbmap.r.blocks[0],
|
|
gbmap.r.trunk);
|
|
|
|
// check if gbmap matches sim
|
|
lfs3_size_t i = 0;
|
|
while (i < BLOCK_COUNT) {
|
|
// we need to convert our sim's raw blocks to compressed ranges,
|
|
// in theory our gbmap is optimal
|
|
lfs3_tag_t tag = sim[i];
|
|
lfs3_size_t d = 1;
|
|
while (i+d < BLOCK_COUNT && sim[i+d] == tag) {
|
|
d += 1;
|
|
}
|
|
|
|
// does our gbmap contain the optimal range?
|
|
lfs3_bid_t bid_;
|
|
lfs3_bid_t weight_;
|
|
lfs3_gbmap_lookupnext(&lfs3, &gbmap, i,
|
|
&bid_, &weight_) => tag;
|
|
assert(bid_ == i+(weight_-1));
|
|
assert(weight_ == d);
|
|
|
|
i += d;
|
|
}
|
|
|
|
free(sim);
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
|
|
|
|
# test high-level gbmap operations
|
|
|
|
# test that the gbmap generally works
|
|
[cases.test_gbmap_files]
|
|
defines.COUNT = [
|
|
'BLOCK_COUNT',
|
|
'BLOCK_COUNT-1',
|
|
'BLOCK_COUNT/2',
|
|
'BLOCK_COUNT/4',
|
|
'5',
|
|
'3',
|
|
]
|
|
defines.SIZE = [
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.N = ['5', 'BLOCK_COUNT/2']
|
|
defines.WRAPAROUND = 3
|
|
if = [
|
|
'COUNT >= 3',
|
|
'COUNT >= 2*N*SIZE/BLOCK_SIZE',
|
|
]
|
|
code = '''
|
|
// test various block counts
|
|
struct lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = COUNT;
|
|
lfs3_t lfs3;
|
|
// note the gbmap flag
|
|
lfs3_format(&lfs3, LFS3_F_RDWR | LFS3_F_GBMAP, &cfg) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// check that we were formatted with the gbmap
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags & LFS3_I_GBMAP);
|
|
|
|
// create n files repeatedly until we're sure we've wrapped around
|
|
// a few times
|
|
uint32_t prng = 42;
|
|
uint32_t prng_ = prng;
|
|
for (lfs3_size_t i = 0;
|
|
i < (WRAPAROUND*COUNT)
|
|
/ (N*(SIZE/BLOCK_SIZE));
|
|
i++) {
|
|
prng = prng_;
|
|
for (lfs3_size_t n = 0; n < N; n++) {
|
|
char name[256];
|
|
sprintf(name, "file%08d", n);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// check that our file writes worked
|
|
prng_ = prng;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%08d", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that we can remove the gbmap and things still work
|
|
[cases.test_gbmap_rmgbmap]
|
|
ifndef = 'LFS3_YES_GBMAP'
|
|
defines.COUNT = [
|
|
'BLOCK_COUNT',
|
|
'BLOCK_COUNT-1',
|
|
'BLOCK_COUNT/2',
|
|
'BLOCK_COUNT/4',
|
|
'5',
|
|
'3',
|
|
]
|
|
defines.SIZE = [
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.N = ['5', 'BLOCK_COUNT/2']
|
|
defines.WRAPAROUND = 3
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after
|
|
# REMOUNT=2 => remount before
|
|
# REMOUNT=3 => remount both after and before
|
|
defines.REMOUNT = [0, 1, 2, 3]
|
|
if = [
|
|
'COUNT >= 3',
|
|
'COUNT >= 2*N*SIZE/BLOCK_SIZE',
|
|
]
|
|
code = '''
|
|
// test various block counts
|
|
struct lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = COUNT;
|
|
lfs3_t lfs3;
|
|
// note the gbmap flag
|
|
lfs3_format(&lfs3, LFS3_F_RDWR | LFS3_F_GBMAP, &cfg) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// check that we were formatted with the gbmap
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags & LFS3_I_GBMAP);
|
|
|
|
// create n files repeatedly until we're sure we've wrapped around
|
|
// a few times
|
|
uint32_t prng = 42;
|
|
uint32_t prng_ = prng;
|
|
for (lfs3_size_t i = 0;
|
|
i < (WRAPAROUND*COUNT)
|
|
/ (N*(SIZE/BLOCK_SIZE));
|
|
i++) {
|
|
prng = prng_;
|
|
for (lfs3_size_t n = 0; n < N; n++) {
|
|
char name[256];
|
|
sprintf(name, "file%08d", n);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// remount before?
|
|
if (REMOUNT & 2) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// now remove the gbmap
|
|
lfs3_fs_rmgbmap(&lfs3) => 0;
|
|
|
|
// remount after?
|
|
if (REMOUNT & 1) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// we should no longer have a gbmap
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(!(fsinfo.flags & LFS3_I_GBMAP));
|
|
|
|
// create n files repeatedly until we're sure we've wrapped around
|
|
// a few times
|
|
for (lfs3_size_t i = 0;
|
|
i < (WRAPAROUND*COUNT)
|
|
/ (N*(SIZE/BLOCK_SIZE));
|
|
i++) {
|
|
prng = prng_;
|
|
for (lfs3_size_t n = 0; n < N; n++) {
|
|
char name[256];
|
|
sprintf(name, "file%08d", n);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// check that our file writes worked
|
|
prng_ = prng;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%08d", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that we can make the gbmap and things still work
|
|
[cases.test_gbmap_mkgbmap]
|
|
ifndef = 'LFS3_YES_GBMAP'
|
|
defines.COUNT = [
|
|
'BLOCK_COUNT',
|
|
'BLOCK_COUNT-1',
|
|
'BLOCK_COUNT/2',
|
|
'BLOCK_COUNT/4',
|
|
'5',
|
|
'3',
|
|
]
|
|
defines.SIZE = [
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.N = ['5', 'BLOCK_COUNT/2']
|
|
defines.WRAPAROUND = 3
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after
|
|
# REMOUNT=2 => remount before
|
|
# REMOUNT=3 => remount both after and before
|
|
defines.REMOUNT = [0, 1, 2, 3]
|
|
if = [
|
|
'COUNT >= 3',
|
|
'COUNT >= 2*N*SIZE/BLOCK_SIZE',
|
|
]
|
|
code = '''
|
|
// test various block counts
|
|
struct lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = COUNT;
|
|
lfs3_t lfs3;
|
|
// note the lack of gbmap flag
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, &cfg) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// check that we were formatted without the gbmap
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(!(fsinfo.flags & LFS3_I_GBMAP));
|
|
|
|
// create n files repeatedly until we're sure we've wrapped around
|
|
// a few times
|
|
uint32_t prng = 42;
|
|
uint32_t prng_ = prng;
|
|
for (lfs3_size_t i = 0;
|
|
i < (WRAPAROUND*COUNT)
|
|
/ (N*(SIZE/BLOCK_SIZE));
|
|
i++) {
|
|
prng = prng_;
|
|
for (lfs3_size_t n = 0; n < N; n++) {
|
|
char name[256];
|
|
sprintf(name, "file%08d", n);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// remount before?
|
|
if (REMOUNT & 2) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// now make the gbmap
|
|
lfs3_fs_mkgbmap(&lfs3) => 0;
|
|
|
|
// remount after?
|
|
if (REMOUNT & 1) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// we should now have a gbmap
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags & LFS3_I_GBMAP);
|
|
|
|
// create n files repeatedly until we're sure we've wrapped around
|
|
// a few times
|
|
for (lfs3_size_t i = 0;
|
|
i < (WRAPAROUND*COUNT)
|
|
/ (N*(SIZE/BLOCK_SIZE));
|
|
i++) {
|
|
prng = prng_;
|
|
for (lfs3_size_t n = 0; n < N; n++) {
|
|
char name[256];
|
|
sprintf(name, "file%08d", n);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// check that our file writes worked
|
|
prng_ = prng;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%08d", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that we can remove and then remake the gbmap and things still
|
|
# work
|
|
#
|
|
# this leaves the disk with garbage gstate that mkgbmap needs to clean
|
|
# up
|
|
[cases.test_gbmap_rmmkgbmap]
|
|
ifndef = 'LFS3_YES_GBMAP'
|
|
defines.COUNT = [
|
|
'BLOCK_COUNT',
|
|
'BLOCK_COUNT-1',
|
|
'BLOCK_COUNT/2',
|
|
'BLOCK_COUNT/4',
|
|
'5',
|
|
'3',
|
|
]
|
|
defines.SIZE = [
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.N = ['5', 'BLOCK_COUNT/2']
|
|
defines.WRAPAROUND = 3
|
|
# REMOUNT=0x1 => remount after
|
|
# REMOUNT=0x2 => remount before make
|
|
# REMOUNT=0x4 => remount after remove
|
|
# REMOUNT=0x8 => remount before
|
|
defines.REMOUNT = 'range(0x10)'
|
|
if = [
|
|
'COUNT >= 3',
|
|
'COUNT >= 2*N*SIZE/BLOCK_SIZE',
|
|
]
|
|
code = '''
|
|
// test various block counts
|
|
struct lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = COUNT;
|
|
lfs3_t lfs3;
|
|
// note the gbmap flag
|
|
lfs3_format(&lfs3, LFS3_F_RDWR | LFS3_F_GBMAP, &cfg) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// check that we were formatted with the gbmap
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags & LFS3_I_GBMAP);
|
|
|
|
// create n files repeatedly until we're sure we've wrapped around
|
|
// a few times
|
|
uint32_t prng = 42;
|
|
uint32_t prng_ = prng;
|
|
for (lfs3_size_t i = 0;
|
|
i < (WRAPAROUND*COUNT)
|
|
/ (N*(SIZE/BLOCK_SIZE));
|
|
i++) {
|
|
prng = prng_;
|
|
for (lfs3_size_t n = 0; n < N; n++) {
|
|
char name[256];
|
|
sprintf(name, "file%08d", n);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// remount before?
|
|
if (REMOUNT & 8) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// now remove the gbmap
|
|
lfs3_fs_rmgbmap(&lfs3) => 0;
|
|
|
|
// remount after remove?
|
|
if (REMOUNT & 4) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// we should no longer have a gbmap
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(!(fsinfo.flags & LFS3_I_GBMAP));
|
|
|
|
// create n files repeatedly until we're sure we've wrapped around
|
|
// a few times
|
|
for (lfs3_size_t i = 0;
|
|
i < (WRAPAROUND*COUNT)
|
|
/ (N*(SIZE/BLOCK_SIZE));
|
|
i++) {
|
|
prng = prng_;
|
|
for (lfs3_size_t n = 0; n < N; n++) {
|
|
char name[256];
|
|
sprintf(name, "file%08d", n);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// remount before make?
|
|
if (REMOUNT & 2) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// now remake the gbmap
|
|
lfs3_fs_mkgbmap(&lfs3) => 0;
|
|
|
|
// remount after?
|
|
if (REMOUNT & 1) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// we should now have a gbmap
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags & LFS3_I_GBMAP);
|
|
|
|
// create n files repeatedly until we're sure we've wrapped around
|
|
// a few times
|
|
for (lfs3_size_t i = 0;
|
|
i < (WRAPAROUND*COUNT)
|
|
/ (N*(SIZE/BLOCK_SIZE));
|
|
i++) {
|
|
prng = prng_;
|
|
for (lfs3_size_t n = 0; n < N; n++) {
|
|
char name[256];
|
|
sprintf(name, "file%08d", n);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// check that our file writes worked
|
|
prng_ = prng;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%08d", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that we can make and then remove the gbmap and things still
|
|
# work
|
|
#
|
|
# not exactly sure what this tests, but including for completeness
|
|
[cases.test_gbmap_mkrmgbmap]
|
|
ifndef = 'LFS3_YES_GBMAP'
|
|
defines.COUNT = [
|
|
'BLOCK_COUNT',
|
|
'BLOCK_COUNT-1',
|
|
'BLOCK_COUNT/2',
|
|
'BLOCK_COUNT/4',
|
|
'5',
|
|
'3',
|
|
]
|
|
defines.SIZE = [
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.N = ['5', 'BLOCK_COUNT/2']
|
|
defines.WRAPAROUND = 3
|
|
# REMOUNT=0x1 => remount after
|
|
# REMOUNT=0x2 => remount before remove
|
|
# REMOUNT=0x4 => remount after make
|
|
# REMOUNT=0x8 => remount before
|
|
defines.REMOUNT = 'range(0x10)'
|
|
if = [
|
|
'COUNT >= 3',
|
|
'COUNT >= 2*N*SIZE/BLOCK_SIZE',
|
|
]
|
|
code = '''
|
|
// test various block counts
|
|
struct lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = COUNT;
|
|
lfs3_t lfs3;
|
|
// note the lack of gbmap flag
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, &cfg) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// check that we were formatted without the gbmap
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(!(fsinfo.flags & LFS3_I_GBMAP));
|
|
|
|
// create n files repeatedly until we're sure we've wrapped around
|
|
// a few times
|
|
uint32_t prng = 42;
|
|
uint32_t prng_ = prng;
|
|
for (lfs3_size_t i = 0;
|
|
i < (WRAPAROUND*COUNT)
|
|
/ (N*(SIZE/BLOCK_SIZE));
|
|
i++) {
|
|
prng = prng_;
|
|
for (lfs3_size_t n = 0; n < N; n++) {
|
|
char name[256];
|
|
sprintf(name, "file%08d", n);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// remount before?
|
|
if (REMOUNT & 8) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// now make the gbmap
|
|
lfs3_fs_mkgbmap(&lfs3) => 0;
|
|
|
|
// remount after make?
|
|
if (REMOUNT & 4) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// we should now have a gbmap
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags & LFS3_I_GBMAP);
|
|
|
|
// create n files repeatedly until we're sure we've wrapped around
|
|
// a few times
|
|
for (lfs3_size_t i = 0;
|
|
i < (WRAPAROUND*COUNT)
|
|
/ (N*(SIZE/BLOCK_SIZE));
|
|
i++) {
|
|
prng = prng_;
|
|
for (lfs3_size_t n = 0; n < N; n++) {
|
|
char name[256];
|
|
sprintf(name, "file%08d", n);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// remount before remove?
|
|
if (REMOUNT & 2) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// now remove the gbmap
|
|
lfs3_fs_rmgbmap(&lfs3) => 0;
|
|
|
|
// remount after?
|
|
if (REMOUNT & 1) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// we should no longer have a gbmap
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(!(fsinfo.flags & LFS3_I_GBMAP));
|
|
|
|
// create n files repeatedly until we're sure we've wrapped around
|
|
// a few times
|
|
for (lfs3_size_t i = 0;
|
|
i < (WRAPAROUND*COUNT)
|
|
/ (N*(SIZE/BLOCK_SIZE));
|
|
i++) {
|
|
prng = prng_;
|
|
for (lfs3_size_t n = 0; n < N; n++) {
|
|
char name[256];
|
|
sprintf(name, "file%08d", n);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// check that our file writes worked
|
|
prng_ = prng;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%08d", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that removing a non-existant gbmap errors
|
|
[cases.test_gbmap_rmgbmap_noent]
|
|
ifndef = 'LFS3_YES_GBMAP'
|
|
defines.COUNT = [
|
|
'BLOCK_COUNT',
|
|
'BLOCK_COUNT-1',
|
|
'BLOCK_COUNT/2',
|
|
'BLOCK_COUNT/4',
|
|
'5',
|
|
'3',
|
|
]
|
|
defines.SIZE = [
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.N = ['5', 'BLOCK_COUNT/2']
|
|
defines.WRAPAROUND = 3
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after
|
|
# REMOUNT=2 => remount before
|
|
# REMOUNT=3 => remount both after and before
|
|
defines.REMOUNT = [0, 1, 2, 3]
|
|
if = [
|
|
'COUNT >= 3',
|
|
'COUNT >= 2*N*SIZE/BLOCK_SIZE',
|
|
]
|
|
code = '''
|
|
// test various block counts
|
|
struct lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = COUNT;
|
|
lfs3_t lfs3;
|
|
// note the lack of gbmap flag
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, &cfg) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// check that we were formatted without the gbmap
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(!(fsinfo.flags & LFS3_I_GBMAP));
|
|
|
|
// create n files repeatedly until we're sure we've wrapped around
|
|
// a few times
|
|
uint32_t prng = 42;
|
|
uint32_t prng_ = prng;
|
|
for (lfs3_size_t i = 0;
|
|
i < (WRAPAROUND*COUNT)
|
|
/ (N*(SIZE/BLOCK_SIZE));
|
|
i++) {
|
|
prng = prng_;
|
|
for (lfs3_size_t n = 0; n < N; n++) {
|
|
char name[256];
|
|
sprintf(name, "file%08d", n);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// remount before?
|
|
if (REMOUNT & 2) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// now try to remove the gbmap
|
|
lfs3_fs_rmgbmap(&lfs3) => LFS3_ERR_NOENT;
|
|
|
|
// remount after?
|
|
if (REMOUNT & 1) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// we should no longer have a gbmap
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(!(fsinfo.flags & LFS3_I_GBMAP));
|
|
|
|
// create n files repeatedly until we're sure we've wrapped around
|
|
// a few times
|
|
for (lfs3_size_t i = 0;
|
|
i < (WRAPAROUND*COUNT)
|
|
/ (N*(SIZE/BLOCK_SIZE));
|
|
i++) {
|
|
prng = prng_;
|
|
for (lfs3_size_t n = 0; n < N; n++) {
|
|
char name[256];
|
|
sprintf(name, "file%08d", n);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// check that our file writes worked
|
|
prng_ = prng;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%08d", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that making an existant gbmap errors
|
|
[cases.test_gbmap_mkgbmap_exist]
|
|
ifndef = 'LFS3_YES_GBMAP'
|
|
defines.COUNT = [
|
|
'BLOCK_COUNT',
|
|
'BLOCK_COUNT-1',
|
|
'BLOCK_COUNT/2',
|
|
'BLOCK_COUNT/4',
|
|
'5',
|
|
'3',
|
|
]
|
|
defines.SIZE = [
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.N = ['5', 'BLOCK_COUNT/2']
|
|
defines.WRAPAROUND = 3
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after
|
|
# REMOUNT=2 => remount before
|
|
# REMOUNT=3 => remount both after and before
|
|
defines.REMOUNT = [0, 1, 2, 3]
|
|
if = [
|
|
'COUNT >= 3',
|
|
'COUNT >= 2*N*SIZE/BLOCK_SIZE',
|
|
]
|
|
code = '''
|
|
// test various block counts
|
|
struct lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = COUNT;
|
|
lfs3_t lfs3;
|
|
// note the gbmap flag
|
|
lfs3_format(&lfs3, LFS3_F_RDWR | LFS3_F_GBMAP, &cfg) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// check that we were formatted with the gbmap
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags & LFS3_I_GBMAP);
|
|
|
|
// create n files repeatedly until we're sure we've wrapped around
|
|
// a few times
|
|
uint32_t prng = 42;
|
|
uint32_t prng_ = prng;
|
|
for (lfs3_size_t i = 0;
|
|
i < (WRAPAROUND*COUNT)
|
|
/ (N*(SIZE/BLOCK_SIZE));
|
|
i++) {
|
|
prng = prng_;
|
|
for (lfs3_size_t n = 0; n < N; n++) {
|
|
char name[256];
|
|
sprintf(name, "file%08d", n);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// remount before?
|
|
if (REMOUNT & 2) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// now make the gbmap
|
|
lfs3_fs_mkgbmap(&lfs3) => LFS3_ERR_EXIST;
|
|
|
|
// remount after?
|
|
if (REMOUNT & 1) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// we should now have a gbmap
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags & LFS3_I_GBMAP);
|
|
|
|
// create n files repeatedly until we're sure we've wrapped around
|
|
// a few times
|
|
for (lfs3_size_t i = 0;
|
|
i < (WRAPAROUND*COUNT)
|
|
/ (N*(SIZE/BLOCK_SIZE));
|
|
i++) {
|
|
prng = prng_;
|
|
for (lfs3_size_t n = 0; n < N; n++) {
|
|
char name[256];
|
|
sprintf(name, "file%08d", n);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
}
|
|
|
|
// check that our file writes worked
|
|
prng_ = prng;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%08d", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|