Added test_wl and aggressive orphan/zombie fuzz tests
test_wl is intended to test wear-leveling, although right now that just involves heavy-duty fuzz tests with extremely low block_recycles. What may be more interesting is the addition of aggressive orphan/zombie tests: - test_forphans_orphanzombie_fuzz - test_forphans_orphanzombiedir_fuzz - test_wl_orphanzombie_fuzz - test_wl_orphanzombiedir_fuzz These tests mix random file/dir operations while keeping random file handles open, creating a complex environment for hitting weird orphan/ zombie corner cases. And they did find a bug! We were asserting on LFS_ERR_RANGE when migrating shrubs/sprouts during lfsr_mdir_commit__. The tricky thing about lfsr_mdir_commit__ is that we need to expect LFS_ERR_RANGE from any append operations, since this is what trigger mdir compaction. This is especially tricky since LFS_ERR_RANGE is a hard error in most other functions. Easy fix. lfsr_mdir_commit__ contains no more LFS_ERR_RANGE asserts. With these tests hopefully that's the last time we see this mistake.
This commit is contained in:
@@ -6082,7 +6082,6 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
LFSR_TAG_SHRUB | LFSR_TAG_DATA, 0,
|
||||
&file->bshrub.u.bsprout, 1));
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -6090,7 +6089,6 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
&file->bshrub_.u.bsprout,
|
||||
&file->bshrub.u.bsprout);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -6105,7 +6103,6 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
&file->bshrub_.u.bshrub,
|
||||
&file->bshrub.u.bshrub);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -970,7 +970,6 @@ code = '''
|
||||
|
||||
[cases.test_btree_update_fuzz]
|
||||
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
||||
defines.SAMPLES = 10
|
||||
defines.SEED = 'range(20)'
|
||||
in = 'lfs.c'
|
||||
code = '''
|
||||
|
||||
@@ -5942,3 +5942,759 @@ code = '''
|
||||
}
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
|
||||
# fuzz tests involving many orphans + zombies, this gets a bit crazy
|
||||
[cases.test_forphans_orphanzombie_fuzz]
|
||||
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
||||
# do more ops than files to encourage file rewrites
|
||||
defines.DENSITY = 2
|
||||
defines.SIZE = [
|
||||
'0',
|
||||
'FBUFFER_SIZE/2',
|
||||
'2*FBUFFER_SIZE',
|
||||
'BLOCK_SIZE/2',
|
||||
'BLOCK_SIZE',
|
||||
'2*BLOCK_SIZE',
|
||||
'4*BLOCK_SIZE',
|
||||
]
|
||||
defines.SEED = 'range(20)'
|
||||
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfsr_format(&lfs, CFG) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
|
||||
// set up a simulation to compare against
|
||||
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
||||
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
||||
lfs_size_t sim_size = 0;
|
||||
|
||||
typedef struct sim_file {
|
||||
lfs_size_t x;
|
||||
bool orphan;
|
||||
bool zombie;
|
||||
uint32_t prng;
|
||||
lfsr_file_t file;
|
||||
} sim_file_t;
|
||||
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
|
||||
lfs_size_t sim_file_count = 0;
|
||||
|
||||
uint32_t prng = SEED;
|
||||
for (lfs_size_t i = 0; i < N; i++) {
|
||||
nonsense:;
|
||||
// choose which operation to do
|
||||
uint8_t op = TEST_PRNG(&prng) % 5;
|
||||
|
||||
// open a new file?
|
||||
if (op == 0) {
|
||||
if (sim_file_count >= ((N+DENSITY-1) / DENSITY)) {
|
||||
goto nonsense;
|
||||
}
|
||||
// choose a pseudo-random number
|
||||
lfs_size_t x = TEST_PRNG(&prng) % ((N+DENSITY-1) / DENSITY);
|
||||
|
||||
// already exists?
|
||||
bool orphan = true;
|
||||
uint32_t wprng = 0;
|
||||
for (lfs_size_t j = 0; j < sim_size; j++) {
|
||||
if (sim[j] == x) {
|
||||
orphan = false;
|
||||
wprng = sim_prngs[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// choose a random seed if we don't exist
|
||||
if (orphan) {
|
||||
wprng = TEST_PRNG(&prng);
|
||||
}
|
||||
|
||||
// open in our sim
|
||||
lfs_size_t j = sim_file_count;
|
||||
sim_files[j] = malloc(sizeof(sim_file_t));
|
||||
sim_files[j]->x = x;
|
||||
sim_files[j]->orphan = orphan;
|
||||
sim_files[j]->zombie = false;
|
||||
sim_files[j]->prng = wprng;
|
||||
sim_file_count++;
|
||||
|
||||
// open the actual file
|
||||
char name[256];
|
||||
sprintf(name, "batman%03x", x);
|
||||
lfsr_file_open(&lfs, &sim_files[j]->file, name,
|
||||
LFS_O_RDWR | LFS_O_CREAT) => 0;
|
||||
|
||||
// write some initial data if we don't exist
|
||||
if (orphan) {
|
||||
uint8_t wbuf[SIZE];
|
||||
for (lfs_size_t k = 0; k < SIZE; k++) {
|
||||
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
||||
}
|
||||
|
||||
// write/rewrite a file?
|
||||
} else if (op == 1) {
|
||||
if (sim_file_count == 0) {
|
||||
goto nonsense;
|
||||
}
|
||||
// choose a random file handle
|
||||
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
||||
lfs_size_t x = sim_files[j]->x;
|
||||
// choose a random seed
|
||||
uint32_t wprng = TEST_PRNG(&prng);
|
||||
|
||||
// update sim
|
||||
sim_files[j]->prng = wprng;
|
||||
if (!sim_files[j]->zombie) {
|
||||
// insert into our sim
|
||||
for (lfs_size_t k = 0;; k++) {
|
||||
if (k >= sim_size || sim[k] >= x) {
|
||||
// already seen?
|
||||
if (k < sim_size && sim[k] == x) {
|
||||
// new prng
|
||||
sim_prngs[k] = wprng;
|
||||
} else {
|
||||
// insert
|
||||
memmove(&sim[k+1], &sim[k],
|
||||
(sim_size-k)*sizeof(lfs_size_t));
|
||||
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
||||
(sim_size-k)*sizeof(uint32_t));
|
||||
sim_size += 1;
|
||||
sim[k] = x;
|
||||
sim_prngs[k] = wprng;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// update related sim files
|
||||
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
||||
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
||||
sim_files[k]->orphan = false;
|
||||
sim_files[k]->prng = wprng;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write to the file
|
||||
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
||||
uint8_t wbuf[SIZE];
|
||||
for (lfs_size_t k = 0; k < SIZE; k++) {
|
||||
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
||||
lfsr_file_sync(&lfs, &sim_files[j]->file)
|
||||
=> ((!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT);
|
||||
|
||||
// close a file?
|
||||
} else if (op == 2) {
|
||||
if (sim_file_count == 0) {
|
||||
goto nonsense;
|
||||
}
|
||||
// choose a random file handle
|
||||
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
||||
|
||||
// this doesn't really test anything, but if we don't close
|
||||
// files eventually everything will end up zombies
|
||||
|
||||
// close the file without affected disk
|
||||
lfsr_file_desync(&lfs, &sim_files[j]->file) => 0;
|
||||
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
||||
|
||||
// remove from list
|
||||
free(sim_files[j]);
|
||||
sim_files[j] = sim_files[sim_file_count-1];
|
||||
sim_file_count -= 1;
|
||||
|
||||
// remove a file?
|
||||
} else if (op == 3) {
|
||||
if (sim_size == 0) {
|
||||
goto nonsense;
|
||||
}
|
||||
// choose a random file to delete
|
||||
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
||||
lfs_size_t x = sim[j];
|
||||
|
||||
// delete from our sim
|
||||
memmove(&sim[j], &sim[j+1],
|
||||
(sim_size-(j+1))*sizeof(lfs_size_t));
|
||||
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
||||
(sim_size-(j+1))*sizeof(uint32_t));
|
||||
sim_size -= 1;
|
||||
|
||||
// mark any related sim files as zombied
|
||||
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
||||
if (sim_files[k]->x == x) {
|
||||
sim_files[k]->zombie = true;
|
||||
}
|
||||
}
|
||||
|
||||
// delete this file
|
||||
char name[256];
|
||||
sprintf(name, "batman%03x", x);
|
||||
lfsr_remove(&lfs, name) => 0;
|
||||
|
||||
// rename a file?
|
||||
} else if (op == 4) {
|
||||
if (sim_size == 0) {
|
||||
goto nonsense;
|
||||
}
|
||||
// choose a random file to rename, and a random number to
|
||||
// rename to
|
||||
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
||||
lfs_size_t x = sim[j];
|
||||
lfs_size_t y = TEST_PRNG(&prng) % ((N+DENSITY-1) / DENSITY);
|
||||
uint32_t wprng = sim_prngs[j];
|
||||
|
||||
// update our sim
|
||||
for (lfs_size_t k = 0;; k++) {
|
||||
if (k >= sim_size || sim[k] >= y) {
|
||||
// renaming and replacing
|
||||
if (k < sim_size && sim[k] == y && x != y) {
|
||||
// delete the original entry
|
||||
memmove(&sim[j], &sim[j+1],
|
||||
(sim_size-(j+1))*sizeof(lfs_size_t));
|
||||
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
||||
(sim_size-(j+1))*sizeof(uint32_t));
|
||||
sim_size -= 1;
|
||||
if (k > j) {
|
||||
k -= 1;
|
||||
}
|
||||
// update the prng
|
||||
sim_prngs[k] = wprng;
|
||||
// just renaming
|
||||
} else {
|
||||
// first delete
|
||||
memmove(&sim[j], &sim[j+1],
|
||||
(sim_size-(j+1))*sizeof(lfs_size_t));
|
||||
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
||||
(sim_size-(j+1))*sizeof(uint32_t));
|
||||
if (k > j) {
|
||||
k -= 1;
|
||||
}
|
||||
// then insert
|
||||
memmove(&sim[k+1], &sim[k],
|
||||
(sim_size-k)*sizeof(lfs_size_t));
|
||||
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
||||
(sim_size-k)*sizeof(uint32_t));
|
||||
sim[k] = y;
|
||||
sim_prngs[k] = wprng;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// update any related sim files
|
||||
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
||||
// move source files
|
||||
if (sim_files[k]->x == x) {
|
||||
sim_files[k]->x = y;
|
||||
|
||||
// mark target files as zombied
|
||||
} else if (sim_files[k]->x == y) {
|
||||
sim_files[k]->zombie = true;
|
||||
}
|
||||
}
|
||||
|
||||
// rename this file
|
||||
char old_name[256];
|
||||
sprintf(old_name, "batman%03x", x);
|
||||
char new_name[256];
|
||||
sprintf(new_name, "batman%03x", y);
|
||||
lfsr_rename(&lfs, old_name, new_name) => 0;
|
||||
}
|
||||
}
|
||||
|
||||
// check that disk matches our simulation
|
||||
for (lfs_size_t j = 0; j < sim_size; j++) {
|
||||
char name[256];
|
||||
sprintf(name, "batman%03x", sim[j]);
|
||||
struct lfs_info info;
|
||||
lfsr_stat(&lfs, name, &info) => 0;
|
||||
assert(strcmp(info.name, name) == 0);
|
||||
assert(info.type == LFS_TYPE_REG);
|
||||
assert(info.size == SIZE);
|
||||
}
|
||||
|
||||
lfsr_dir_t dir;
|
||||
lfsr_dir_open(&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);
|
||||
assert(info.size == 0);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(info.size == 0);
|
||||
for (lfs_size_t j = 0; j < sim_size; j++) {
|
||||
char name[256];
|
||||
sprintf(name, "batman%03x", sim[j]);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, name) == 0);
|
||||
assert(info.type == LFS_TYPE_REG);
|
||||
assert(info.size == SIZE);
|
||||
}
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
lfsr_dir_close(&lfs, &dir) => 0;
|
||||
|
||||
for (lfs_size_t j = 0; j < sim_size; j++) {
|
||||
char name[256];
|
||||
sprintf(name, "batman%03x", sim[j]);
|
||||
lfsr_file_t file;
|
||||
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
||||
|
||||
uint32_t wprng = sim_prngs[j];
|
||||
uint8_t wbuf[SIZE];
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
||||
}
|
||||
|
||||
uint8_t rbuf[SIZE];
|
||||
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
}
|
||||
|
||||
// check that our file handles match our simulation
|
||||
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
||||
uint32_t wprng = sim_files[j]->prng;
|
||||
uint8_t wbuf[SIZE];
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
||||
}
|
||||
|
||||
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
||||
uint8_t rbuf[SIZE];
|
||||
lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
||||
}
|
||||
|
||||
// clean up sim/lfs
|
||||
free(sim);
|
||||
free(sim_prngs);
|
||||
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
||||
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
||||
free(sim_files[j]);
|
||||
}
|
||||
free(sim_files);
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# fuzz tests involving many orphans + zombies + dirs, this gets a bit crazy
|
||||
[cases.test_forphans_orphanzombiedir_fuzz]
|
||||
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
||||
# do more ops than files to encourage file rewrites
|
||||
defines.DENSITY = 2
|
||||
defines.SIZE = [
|
||||
'0',
|
||||
'FBUFFER_SIZE/2',
|
||||
'2*FBUFFER_SIZE',
|
||||
'BLOCK_SIZE/2',
|
||||
'BLOCK_SIZE',
|
||||
'2*BLOCK_SIZE',
|
||||
'4*BLOCK_SIZE',
|
||||
]
|
||||
defines.SEED = 'range(20)'
|
||||
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfsr_format(&lfs, CFG) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
|
||||
// set up a simulation to compare against
|
||||
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
||||
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
||||
bool *sim_isdirs = malloc(N*sizeof(bool));
|
||||
lfs_size_t sim_size = 0;
|
||||
|
||||
typedef struct sim_file {
|
||||
lfs_size_t x;
|
||||
bool orphan;
|
||||
bool zombie;
|
||||
uint32_t prng;
|
||||
lfsr_file_t file;
|
||||
} sim_file_t;
|
||||
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
|
||||
lfs_size_t sim_file_count = 0;
|
||||
|
||||
uint32_t prng = SEED;
|
||||
for (lfs_size_t i = 0; i < N; i++) {
|
||||
nonsense:;
|
||||
// choose which operation to do
|
||||
uint8_t op = TEST_PRNG(&prng) % 8;
|
||||
|
||||
// open a new file?
|
||||
if (op == 0) {
|
||||
if (sim_file_count >= ((N+DENSITY-1) / DENSITY)) {
|
||||
goto nonsense;
|
||||
}
|
||||
// choose a pseudo-random number
|
||||
lfs_size_t x = TEST_PRNG(&prng) % ((N+DENSITY-1) / DENSITY);
|
||||
|
||||
// already exists?
|
||||
bool orphan = true;
|
||||
uint32_t wprng = 0;
|
||||
for (lfs_size_t j = 0; j < sim_size; j++) {
|
||||
if (sim[j] == x) {
|
||||
if (sim_isdirs[j]) {
|
||||
goto nonsense;
|
||||
}
|
||||
orphan = false;
|
||||
wprng = sim_prngs[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// choose a random seed if we don't exist
|
||||
if (orphan) {
|
||||
wprng = TEST_PRNG(&prng);
|
||||
}
|
||||
|
||||
// open in our sim
|
||||
lfs_size_t j = sim_file_count;
|
||||
sim_files[j] = malloc(sizeof(sim_file_t));
|
||||
sim_files[j]->x = x;
|
||||
sim_files[j]->orphan = orphan;
|
||||
sim_files[j]->zombie = false;
|
||||
sim_files[j]->prng = wprng;
|
||||
sim_file_count++;
|
||||
|
||||
// open the actual file
|
||||
char name[256];
|
||||
sprintf(name, "batman%03x", x);
|
||||
lfsr_file_open(&lfs, &sim_files[j]->file, name,
|
||||
LFS_O_RDWR | LFS_O_CREAT) => 0;
|
||||
|
||||
// write some initial data if we don't exist
|
||||
if (orphan) {
|
||||
uint8_t wbuf[SIZE];
|
||||
for (lfs_size_t k = 0; k < SIZE; k++) {
|
||||
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
||||
}
|
||||
|
||||
// write/rewrite a file?
|
||||
} else if (op == 1) {
|
||||
if (sim_file_count == 0) {
|
||||
goto nonsense;
|
||||
}
|
||||
// choose a random file handle
|
||||
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
||||
lfs_size_t x = sim_files[j]->x;
|
||||
// choose a random seed
|
||||
uint32_t wprng = TEST_PRNG(&prng);
|
||||
|
||||
// update sim
|
||||
sim_files[j]->prng = wprng;
|
||||
if (!sim_files[j]->zombie) {
|
||||
// insert into our sim
|
||||
for (lfs_size_t k = 0;; k++) {
|
||||
if (k >= sim_size || sim[k] >= x) {
|
||||
// already seen?
|
||||
if (k < sim_size && sim[k] == x) {
|
||||
// new prng
|
||||
sim_prngs[k] = wprng;
|
||||
} else {
|
||||
// insert
|
||||
memmove(&sim[k+1], &sim[k],
|
||||
(sim_size-k)*sizeof(lfs_size_t));
|
||||
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
||||
(sim_size-k)*sizeof(uint32_t));
|
||||
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
||||
(sim_size-k)*sizeof(bool));
|
||||
sim_size += 1;
|
||||
sim[k] = x;
|
||||
sim_prngs[k] = wprng;
|
||||
sim_isdirs[k] = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// update related sim files
|
||||
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
||||
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
||||
sim_files[k]->orphan = false;
|
||||
sim_files[k]->prng = wprng;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write to the file
|
||||
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
||||
uint8_t wbuf[SIZE];
|
||||
for (lfs_size_t k = 0; k < SIZE; k++) {
|
||||
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
||||
lfsr_file_sync(&lfs, &sim_files[j]->file)
|
||||
=> ((!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT);
|
||||
|
||||
// close a file?
|
||||
} else if (op == 2) {
|
||||
if (sim_file_count == 0) {
|
||||
goto nonsense;
|
||||
}
|
||||
// choose a random file handle
|
||||
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
||||
|
||||
// this doesn't really test anything, but if we don't close
|
||||
// files eventually everything will end up zombies
|
||||
|
||||
// close the file without affected disk
|
||||
lfsr_file_desync(&lfs, &sim_files[j]->file) => 0;
|
||||
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
||||
|
||||
// remove from list
|
||||
free(sim_files[j]);
|
||||
sim_files[j] = sim_files[sim_file_count-1];
|
||||
sim_file_count -= 1;
|
||||
|
||||
// remove a file?
|
||||
} else if (op == 3) {
|
||||
if (sim_size == 0) {
|
||||
goto nonsense;
|
||||
}
|
||||
// choose a random file to delete
|
||||
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
||||
lfs_size_t x = sim[j];
|
||||
|
||||
// delete from our sim
|
||||
memmove(&sim[j], &sim[j+1],
|
||||
(sim_size-(j+1))*sizeof(lfs_size_t));
|
||||
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
||||
(sim_size-(j+1))*sizeof(uint32_t));
|
||||
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
||||
(sim_size-(j+1))*sizeof(bool));
|
||||
sim_size -= 1;
|
||||
|
||||
// mark any related sim files as zombied
|
||||
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
||||
if (sim_files[k]->x == x) {
|
||||
sim_files[k]->zombie = true;
|
||||
}
|
||||
}
|
||||
|
||||
// delete this file
|
||||
char name[256];
|
||||
sprintf(name, "batman%03x", x);
|
||||
lfsr_remove(&lfs, name) => 0;
|
||||
|
||||
// rename a file?
|
||||
} else if (op == 4) {
|
||||
if (sim_size == 0) {
|
||||
goto nonsense;
|
||||
}
|
||||
// choose a random file to rename, and a random number to
|
||||
// rename to
|
||||
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
||||
lfs_size_t x = sim[j];
|
||||
lfs_size_t y = TEST_PRNG(&prng) % ((N+DENSITY-1) / DENSITY);
|
||||
uint32_t wprng = sim_prngs[j];
|
||||
bool isdir = sim_isdirs[j];
|
||||
|
||||
// update our sim
|
||||
for (lfs_size_t k = 0;; k++) {
|
||||
if (k >= sim_size || sim[k] >= y) {
|
||||
// renaming and replacing
|
||||
if (k < sim_size && sim[k] == y && x != y) {
|
||||
// type mismatch?
|
||||
if (sim_isdirs[k] != isdir) {
|
||||
goto nonsense;
|
||||
}
|
||||
|
||||
// delete the original entry
|
||||
memmove(&sim[j], &sim[j+1],
|
||||
(sim_size-(j+1))*sizeof(lfs_size_t));
|
||||
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
||||
(sim_size-(j+1))*sizeof(uint32_t));
|
||||
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
||||
(sim_size-(j+1))*sizeof(bool));
|
||||
sim_size -= 1;
|
||||
if (k > j) {
|
||||
k -= 1;
|
||||
}
|
||||
// update the prng
|
||||
sim_prngs[k] = wprng;
|
||||
// just renaming
|
||||
} else {
|
||||
// first delete
|
||||
memmove(&sim[j], &sim[j+1],
|
||||
(sim_size-(j+1))*sizeof(lfs_size_t));
|
||||
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
||||
(sim_size-(j+1))*sizeof(uint32_t));
|
||||
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
||||
(sim_size-(j+1))*sizeof(bool));
|
||||
if (k > j) {
|
||||
k -= 1;
|
||||
}
|
||||
// then insert
|
||||
memmove(&sim[k+1], &sim[k],
|
||||
(sim_size-k)*sizeof(lfs_size_t));
|
||||
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
||||
(sim_size-k)*sizeof(uint32_t));
|
||||
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
||||
(sim_size-k)*sizeof(bool));
|
||||
sim[k] = y;
|
||||
sim_prngs[k] = wprng;
|
||||
sim_isdirs[k] = isdir;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// update any related sim files
|
||||
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
||||
// move source files
|
||||
if (sim_files[k]->x == x) {
|
||||
sim_files[k]->x = y;
|
||||
|
||||
// mark target files as zombied
|
||||
} else if (sim_files[k]->x == y) {
|
||||
sim_files[k]->zombie = true;
|
||||
}
|
||||
}
|
||||
|
||||
// rename this file
|
||||
char old_name[256];
|
||||
sprintf(old_name, "batman%03x", x);
|
||||
char new_name[256];
|
||||
sprintf(new_name, "batman%03x", y);
|
||||
lfsr_rename(&lfs, old_name, new_name) => 0;
|
||||
|
||||
// toss a directory into the mix
|
||||
} else if (op == 5) {
|
||||
// choose a pseudo-random number
|
||||
lfs_size_t x = TEST_PRNG(&prng) % ((N+DENSITY-1) / DENSITY);
|
||||
|
||||
// insert into our sim, use negative numbers for dirs
|
||||
for (lfs_size_t k = 0;; k++) {
|
||||
if (k >= sim_size || sim[k] >= x) {
|
||||
// already seen?
|
||||
if (k < sim_size && sim[k] == x) {
|
||||
goto nonsense;
|
||||
} else {
|
||||
// insert
|
||||
memmove(&sim[k+1], &sim[k],
|
||||
(sim_size-k)*sizeof(lfs_size_t));
|
||||
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
||||
(sim_size-k)*sizeof(uint32_t));
|
||||
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
||||
(sim_size-k)*sizeof(bool));
|
||||
sim_size += 1;
|
||||
sim[k] = x;
|
||||
sim_prngs[k] = 0;
|
||||
sim_isdirs[k] = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// mark any related sim files as zombied
|
||||
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
||||
if (sim_files[k]->x == x) {
|
||||
sim_files[k]->zombie = true;
|
||||
}
|
||||
}
|
||||
|
||||
// make the directory
|
||||
char name[256];
|
||||
sprintf(name, "batman%03x", x);
|
||||
lfsr_mkdir(&lfs, name) => 0;
|
||||
}
|
||||
}
|
||||
|
||||
// check that disk matches our simulation
|
||||
for (lfs_size_t j = 0; j < sim_size; j++) {
|
||||
char name[256];
|
||||
sprintf(name, "batman%03x", sim[j]);
|
||||
struct lfs_info info;
|
||||
lfsr_stat(&lfs, name, &info) => 0;
|
||||
assert(strcmp(info.name, name) == 0);
|
||||
if (sim_isdirs[j]) {
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
} else {
|
||||
assert(info.type == LFS_TYPE_REG);
|
||||
assert(info.size == SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
lfsr_dir_t dir;
|
||||
lfsr_dir_open(&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);
|
||||
assert(info.size == 0);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(info.size == 0);
|
||||
for (lfs_size_t j = 0; j < sim_size; j++) {
|
||||
char name[256];
|
||||
sprintf(name, "batman%03x", sim[j]);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, name) == 0);
|
||||
if (sim_isdirs[j]) {
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
} else {
|
||||
assert(info.type == LFS_TYPE_REG);
|
||||
assert(info.size == SIZE);
|
||||
}
|
||||
}
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
lfsr_dir_close(&lfs, &dir) => 0;
|
||||
|
||||
for (lfs_size_t j = 0; j < sim_size; j++) {
|
||||
if (sim_isdirs[j]) {
|
||||
char name[256];
|
||||
sprintf(name, "batman%03x", sim[j]);
|
||||
lfsr_file_t file;
|
||||
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => LFS_ERR_ISDIR;
|
||||
|
||||
} else {
|
||||
char name[256];
|
||||
sprintf(name, "batman%03x", sim[j]);
|
||||
lfsr_file_t file;
|
||||
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
||||
|
||||
uint32_t wprng = sim_prngs[j];
|
||||
uint8_t wbuf[SIZE];
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
||||
}
|
||||
|
||||
uint8_t rbuf[SIZE];
|
||||
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
}
|
||||
}
|
||||
|
||||
// check that our file handles match our simulation
|
||||
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
||||
uint32_t wprng = sim_files[j]->prng;
|
||||
uint8_t wbuf[SIZE];
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
||||
}
|
||||
|
||||
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
||||
uint8_t rbuf[SIZE];
|
||||
lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
||||
}
|
||||
|
||||
// clean up sim/lfs
|
||||
free(sim);
|
||||
free(sim_prngs);
|
||||
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
||||
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
||||
free(sim_files[j]);
|
||||
}
|
||||
free(sim_files);
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
|
||||
+1136
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user