Implemented and tested lfsr_file_truncate
Not much to say here. We need to modify trees a bit, but at least it's relatively straightforward.
This commit is contained in:
@@ -5322,6 +5322,7 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
// us due to mdir compactions
|
||||
//
|
||||
// TODO should we preserve mode for all of these?
|
||||
// TODO should we do the same for sprouts?
|
||||
} else if (lfsr_tag_key(attrs[i].tag) == LFSR_TAG_SHRUBTRUNK) {
|
||||
lfsr_file_t *file = (lfsr_file_t*)attrs[i].data.u.direct.buffer;
|
||||
|
||||
@@ -8603,7 +8604,7 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) {
|
||||
lfsr_attr_t scratch_attrs[4];
|
||||
lfsr_attr_t *attrs_ = scratch_attrs;
|
||||
|
||||
// have inlined data?
|
||||
// have a sprout/null?
|
||||
if (!lfsr_file_hasshrub(file)) {
|
||||
// left data? this may create a hole
|
||||
if (file->buffer_pos > 0) {
|
||||
@@ -8747,8 +8748,8 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) {
|
||||
// commit our attributes
|
||||
int err = lfsr_mdir_commit(lfs, &file->m.mdir, LFSR_ATTRS(
|
||||
LFSR_ATTR_(file->m.mdir.mid, SHRUBATTRS, 0, SHRUBATTRS(file,
|
||||
scratch_attrs,
|
||||
attrs_ - scratch_attrs))));
|
||||
scratch_attrs,
|
||||
attrs_ - scratch_attrs))));
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
@@ -8767,6 +8768,11 @@ lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file,
|
||||
LFS_ASSERT(lfsr_file_iswriteable(file));
|
||||
LFS_ASSERT(size <= 0x7fffffff);
|
||||
|
||||
// would this write make our file larger than our size limit?
|
||||
if (size > lfs->size_limit - file->pos) {
|
||||
return LFS_ERR_FBIG;
|
||||
}
|
||||
|
||||
// size=0 is a bit special and is gauranteed to have no effects on the
|
||||
// underlying file, this means no updating file pos or file size
|
||||
//
|
||||
@@ -8781,8 +8787,6 @@ lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file,
|
||||
file->pos = file->size;
|
||||
}
|
||||
|
||||
// TODO do we need to prepare mutation?
|
||||
|
||||
lfs_off_t pos = file->pos;
|
||||
const uint8_t *buffer_ = buffer;
|
||||
int err;
|
||||
@@ -8855,7 +8859,7 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
|
||||
// inlined file in that case
|
||||
if (file->buffer_size >= file->size) {
|
||||
LFS_ASSERT(file->buffer_size == file->size);
|
||||
LFS_ASSERT(file->buffer_pos == 0);
|
||||
LFS_ASSERT(file->buffer_pos == 0 || file->buffer_size == 0);
|
||||
|
||||
// commit our file's metadata
|
||||
err = lfsr_mdir_commit(lfs, &file->m.mdir, LFSR_ATTRS(
|
||||
@@ -8873,13 +8877,15 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
|
||||
// we risk runaway O(n^2) behavior
|
||||
file->buffer_size = 0;
|
||||
|
||||
// we need to look up the inlined data again...
|
||||
// TODO deduplicate?
|
||||
err = lfsr_mdir_lookup(lfs, &file->m.mdir,
|
||||
file->m.mdir.mid, LFSR_TAG_INLINED,
|
||||
NULL, &file->inlined.u.data);
|
||||
if (err) {
|
||||
return err;
|
||||
if (file->size > 0) {
|
||||
// we need to look up the inlined data again...
|
||||
// TODO deduplicate?
|
||||
err = lfsr_mdir_lookup(lfs, &file->m.mdir,
|
||||
file->m.mdir.mid, LFSR_TAG_INLINED,
|
||||
NULL, &file->inlined.u.data);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// first make sure to flush our buffer
|
||||
@@ -8957,6 +8963,145 @@ lfs_soff_t lfsr_file_size(lfs_t *lfs, lfsr_file_t *file) {
|
||||
return file->size;
|
||||
}
|
||||
|
||||
int lfsr_file_truncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size) {
|
||||
// exceeds our size limit?
|
||||
if (size > lfs->size_limit) {
|
||||
return LFS_ERR_FBIG;
|
||||
}
|
||||
|
||||
// do nothing if our size does not change
|
||||
if (file->size == size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO make this recoverable on failure
|
||||
|
||||
// mark as unsynced before we commit anything
|
||||
file->flags |= LFS_F_UNSYNCED;
|
||||
|
||||
lfsr_attr_t scratch_attrs[2];
|
||||
lfsr_attr_t *attrs_ = scratch_attrs;
|
||||
|
||||
// if our truncated file is contained entirely in our buffer,
|
||||
// revert to a sprout
|
||||
lfs_size_t buffer_size = lfs_min32(
|
||||
file->buffer_size,
|
||||
size - lfs_min32(file->buffer_pos, size));
|
||||
if (buffer_size >= size) {
|
||||
file->inlined.u.data = LFSR_DATA_DISK(0, 0, 0);
|
||||
|
||||
// have a sprout/null? convert to a shrub
|
||||
} else if (!lfsr_file_hasshrub(file)) {
|
||||
*attrs_++ = LFSR_ATTR(0,
|
||||
SHRUB(INLINED), +size, DISK(
|
||||
file->inlined.u.data.u.disk.block,
|
||||
file->inlined.u.data.u.disk.off,
|
||||
lfs_min32(
|
||||
lfsr_data_size(&file->inlined.u.data),
|
||||
size)));
|
||||
|
||||
// have a shrub?
|
||||
} else if (lfsr_file_hasshrub(file)) {
|
||||
// TODO can this be deduplicated with flushbuffer? some sort of
|
||||
// lfsr_file_shrubcarveleft?
|
||||
|
||||
// this should never happen, every route to zero-weight shrub
|
||||
// should revert to an inlined file
|
||||
LFS_ASSERT(file->inlined.u.rbyd.weight > 0);
|
||||
LFS_ASSERT(size > 0);
|
||||
|
||||
// left sibling?
|
||||
lfs_soff_t left_overlap = 0;
|
||||
lfsr_srid_t left_rid;
|
||||
lfsr_tag_t left_tag;
|
||||
lfsr_rid_t left_weight;
|
||||
lfsr_data_t left_data;
|
||||
int err = lfsr_rbyd_lookupnext(lfs, &file->inlined.u.rbyd,
|
||||
lfs_min32(
|
||||
size,
|
||||
file->inlined.u.rbyd.weight)-1, 0,
|
||||
&left_rid, &left_tag, &left_weight, &left_data);
|
||||
if (err && err != LFS_ERR_NOENT) {
|
||||
return err;
|
||||
}
|
||||
LFS_ASSERT(err != LFS_ERR_NOENT);
|
||||
LFS_ASSERT(left_tag == LFSR_TAG_SHRUB(INLINED));
|
||||
|
||||
// this can be negative!
|
||||
left_overlap = (left_rid+1) - size;
|
||||
|
||||
// can we get away with a simple grow attr? this may
|
||||
// create a hole
|
||||
if (left_overlap != 0
|
||||
&& size
|
||||
>= left_rid-(left_weight-1)
|
||||
+ lfsr_data_size(&left_data)) {
|
||||
*attrs_++ = LFSR_ATTR(left_rid,
|
||||
SHRUB(GROW), -left_overlap, NULL);
|
||||
|
||||
// need to carve out left data?
|
||||
} else if (left_overlap > 0) {
|
||||
*attrs_++ = LFSR_ATTR(left_rid,
|
||||
SHRUB(GROW(INLINED)), -left_overlap,
|
||||
DISK(
|
||||
left_data.u.disk.block,
|
||||
left_data.u.disk.off,
|
||||
left_weight - left_overlap));
|
||||
}
|
||||
|
||||
// remove any data we're truncating
|
||||
*attrs_++ = LFSR_ATTR(file->inlined.u.rbyd.weight
|
||||
- left_overlap - 1,
|
||||
SHRUB(RM), -(file->inlined.u.rbyd.weight
|
||||
- size - left_overlap), NULL);
|
||||
}
|
||||
|
||||
// commit any shrub changes
|
||||
if (attrs_ > scratch_attrs) {
|
||||
int err = lfsr_mdir_commit(lfs, &file->m.mdir, LFSR_ATTRS(
|
||||
LFSR_ATTR_(file->m.mdir.mid, SHRUBATTRS, 0, SHRUBATTRS(file,
|
||||
scratch_attrs,
|
||||
attrs_ - scratch_attrs))));
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO update btree
|
||||
|
||||
// update our buffer
|
||||
file->buffer_size = buffer_size;
|
||||
|
||||
// update our internal file size
|
||||
//
|
||||
// if this grows our file, leave it up to lfsr_file_sync to update
|
||||
// on on-disk metadata
|
||||
file->size = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfsr_file_fruncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size) {
|
||||
// exceeds our size limit?
|
||||
if (size > lfs->size_limit) {
|
||||
return LFS_ERR_FBIG;
|
||||
}
|
||||
|
||||
// // TODO does this risk overflow?
|
||||
// // if we're increasing the file size, just update our internal size/off
|
||||
// // and leave it to lfsr_file_sync to do most of the work
|
||||
// if (size > file->size) {
|
||||
// file->off +=
|
||||
// file->size = size;
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
// TODO
|
||||
LFS_ASSERT(false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -785,10 +785,24 @@ lfs_soff_t lfsr_file_seek(lfs_t *lfs, lfsr_file_t *file,
|
||||
lfs_soff_t off, uint8_t whence);
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Truncates the size of the file to the specified size
|
||||
// Truncate/grow the size of the file to the specified size
|
||||
//
|
||||
// If size is larger than the current file size, a hole is created, appearing
|
||||
// as if the file was filled with zeros.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size);
|
||||
int lfsr_file_truncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size);
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Truncate/grow the file, but from the front
|
||||
//
|
||||
// If size is larger than the current file size, a hole is created, appearing
|
||||
// as if the file was filled with zeros.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfsr_file_fruncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size);
|
||||
#endif
|
||||
|
||||
// Return the position of the file
|
||||
|
||||
+395
-73
@@ -644,15 +644,17 @@ code = '''
|
||||
}
|
||||
|
||||
// write first chunk?
|
||||
if (MASK & 1) {
|
||||
if (ORDER == 0) {
|
||||
if (ORDER == 0) {
|
||||
if (MASK & 0x1) {
|
||||
for (lfs_size_t i = 0; i < CHUNK; i++) {
|
||||
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
|
||||
lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
|
||||
lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK;
|
||||
} else {
|
||||
}
|
||||
} else {
|
||||
if (MASK & 0x4) {
|
||||
for (lfs_size_t i = 0; i < CHUNK; i++) {
|
||||
sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
@@ -700,15 +702,17 @@ code = '''
|
||||
}
|
||||
|
||||
// write third chunk?
|
||||
if (MASK & 4) {
|
||||
if (ORDER == 0) {
|
||||
if (ORDER == 0) {
|
||||
if (MASK & 0x4) {
|
||||
for (lfs_size_t i = 0; i < CHUNK; i++) {
|
||||
sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
|
||||
lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK;
|
||||
lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK;
|
||||
} else {
|
||||
}
|
||||
} else {
|
||||
if (MASK & 0x1) {
|
||||
for (lfs_size_t i = 0; i < CHUNK; i++) {
|
||||
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
@@ -774,7 +778,7 @@ defines.CHUNK = ['CACHE_SIZE/2', '4', '1']
|
||||
defines.MASK = [0, 1, 2, 3, 4, 5, 6, 7]
|
||||
# 0 => in-order
|
||||
# 1 => reversed
|
||||
defines.ORDER = [0] # TODO 1?
|
||||
defines.ORDER = [0, 1]
|
||||
defines.SYNC = [false, true]
|
||||
defines.REMOUNT = [false, true]
|
||||
reentrant = true
|
||||
@@ -816,15 +820,17 @@ code = '''
|
||||
}
|
||||
|
||||
// write first chunk?
|
||||
if (MASK & 1) {
|
||||
if (ORDER == 0) {
|
||||
if (ORDER == 0) {
|
||||
if (MASK & 0x1) {
|
||||
for (lfs_size_t i = 0; i < CHUNK; i++) {
|
||||
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
|
||||
lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
|
||||
lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK;
|
||||
} else {
|
||||
}
|
||||
} else {
|
||||
if (MASK & 0x4) {
|
||||
for (lfs_size_t i = 0; i < CHUNK; i++) {
|
||||
sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
@@ -872,15 +878,17 @@ code = '''
|
||||
}
|
||||
|
||||
// write third chunk?
|
||||
if (MASK & 4) {
|
||||
if (ORDER == 0) {
|
||||
if (ORDER == 0) {
|
||||
if (MASK & 0x4) {
|
||||
for (lfs_size_t i = 0; i < CHUNK; i++) {
|
||||
sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
|
||||
lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK;
|
||||
lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK;
|
||||
} else {
|
||||
}
|
||||
} else {
|
||||
if (MASK & 0x1) {
|
||||
for (lfs_size_t i = 0; i < CHUNK; i++) {
|
||||
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
@@ -936,10 +944,10 @@ code = '''
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# writing any data structure backwards always reveals issues
|
||||
[cases.test_files_reversed_overwrite]
|
||||
defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE']
|
||||
defines.CHUNK = ['CACHE_SIZE/2', '4', '1']
|
||||
# simple truncate test
|
||||
[cases.test_files_truncate]
|
||||
defines.FROM = ['0', 'CACHE_SIZE/2', '2*CACHE_SIZE']
|
||||
defines.TO = ['0', 'CACHE_SIZE/2', '2*CACHE_SIZE']
|
||||
defines.SYNC = [false, true]
|
||||
defines.REMOUNT = [false, true]
|
||||
reentrant = true
|
||||
@@ -957,12 +965,13 @@ code = '''
|
||||
lfsr_file_open(&lfs, &file, "hello",
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
|
||||
// simulate our file in ram
|
||||
uint8_t sim[SIZE];
|
||||
uint8_t sim[lfs_max32(FROM,TO)];
|
||||
memset(sim, 0, lfs_max32(FROM,TO));
|
||||
uint32_t prng = 42;
|
||||
for (lfs_size_t i = 0; i < SIZE; i++) {
|
||||
for (lfs_size_t i = 0; i < FROM; i++) {
|
||||
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE;
|
||||
lfsr_file_write(&lfs, &file, sim, FROM) => FROM;
|
||||
|
||||
// sync?
|
||||
if (SYNC) {
|
||||
@@ -977,27 +986,10 @@ code = '''
|
||||
lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0;
|
||||
}
|
||||
|
||||
// write to file incrementally and backwards
|
||||
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
||||
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
||||
sim[SIZE-i-CHUNK+j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_seek(&lfs, &file, SIZE-i-CHUNK, LFS_SEEK_SET) => SIZE-i-CHUNK;
|
||||
lfsr_file_write(&lfs, &file, &sim[SIZE-i-CHUNK], CHUNK) => CHUNK;
|
||||
// truncate to new size
|
||||
lfsr_file_truncate(&lfs, &file, TO) => 0;
|
||||
|
||||
// sync?
|
||||
if (SYNC) {
|
||||
lfsr_file_sync(&lfs, &file) => 0;
|
||||
}
|
||||
|
||||
// remount?
|
||||
if (REMOUNT) {
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0;
|
||||
}
|
||||
}
|
||||
// close
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
|
||||
// remount?
|
||||
@@ -1011,7 +1003,7 @@ code = '''
|
||||
lfsr_stat(&lfs, "hello", &info) => 0;
|
||||
assert(strcmp(info.name, "hello") == 0);
|
||||
assert(info.type == LFS_TYPE_REG);
|
||||
assert(info.size == SIZE);
|
||||
assert(info.size == TO);
|
||||
|
||||
// and with dir read
|
||||
lfsr_dir_t dir;
|
||||
@@ -1025,29 +1017,147 @@ code = '''
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, "hello") == 0);
|
||||
assert(info.type == LFS_TYPE_REG);
|
||||
assert(info.size == SIZE);
|
||||
assert(info.size == TO);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
lfsr_dir_close(&lfs, &dir) => 0;
|
||||
|
||||
// try reading our file
|
||||
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
|
||||
// is size correct?
|
||||
lfsr_file_size(&lfs, &file) => SIZE;
|
||||
lfsr_file_size(&lfs, &file) => TO;
|
||||
// try reading
|
||||
uint8_t rbuf[2*SIZE];
|
||||
memset(rbuf, 0xaa, 2*SIZE);
|
||||
lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE;
|
||||
// does our file match our simulation?
|
||||
assert(memcmp(rbuf, sim, SIZE) == 0);
|
||||
uint8_t rbuf[2*TO];
|
||||
memset(rbuf, 0xaa, 2*TO);
|
||||
lfsr_file_read(&lfs, &file, rbuf, 2*TO) => TO;
|
||||
assert(memcmp(rbuf, sim, TO) == 0);
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# the main purpose of this test is to check that data is not hidden
|
||||
# and then revealed by truncate, that would be bad
|
||||
[cases.test_files_truncate_2]
|
||||
defines.FROM = ['0', 'CACHE_SIZE/2', '2*CACHE_SIZE']
|
||||
defines.AND = ['0', 'CACHE_SIZE/2', '2*CACHE_SIZE']
|
||||
defines.TO = ['0', 'CACHE_SIZE/2', '2*CACHE_SIZE']
|
||||
defines.SYNC = [false, true]
|
||||
defines.REMOUNT = [false, true]
|
||||
reentrant = true
|
||||
code = '''
|
||||
// format once per test
|
||||
lfs_t lfs;
|
||||
int err = lfsr_mount(&lfs, CFG);
|
||||
if (err) {
|
||||
lfsr_format(&lfs, CFG) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
}
|
||||
|
||||
// create a file, truncating in case of powerloss
|
||||
lfsr_file_t file;
|
||||
lfsr_file_open(&lfs, &file, "hello",
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
|
||||
// simulate our file in ram
|
||||
uint8_t sim[lfs_max32(FROM,TO)];
|
||||
memset(sim, 0, lfs_max32(FROM,TO));
|
||||
uint32_t prng = 42;
|
||||
for (lfs_size_t i = 0; i < FROM; i++) {
|
||||
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, sim, FROM) => FROM;
|
||||
|
||||
// sync?
|
||||
if (SYNC) {
|
||||
lfsr_file_sync(&lfs, &file) => 0;
|
||||
}
|
||||
|
||||
// remount?
|
||||
if (REMOUNT) {
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0;
|
||||
}
|
||||
|
||||
// truncate to intermediate size
|
||||
lfsr_file_truncate(&lfs, &file, AND) => 0;
|
||||
memset(&sim[AND], 0, lfs_max32(FROM,TO)
|
||||
- lfs_min32(AND, lfs_max32(FROM,TO)));
|
||||
|
||||
// sync?
|
||||
if (SYNC) {
|
||||
lfsr_file_sync(&lfs, &file) => 0;
|
||||
}
|
||||
|
||||
// remount?
|
||||
if (REMOUNT) {
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0;
|
||||
}
|
||||
|
||||
// truncate to new size
|
||||
lfsr_file_truncate(&lfs, &file, TO) => 0;
|
||||
|
||||
// close
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
|
||||
// remount?
|
||||
if (REMOUNT) {
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
}
|
||||
|
||||
// check our file with stat
|
||||
struct lfs_info info;
|
||||
lfsr_stat(&lfs, "hello", &info) => 0;
|
||||
assert(strcmp(info.name, "hello") == 0);
|
||||
assert(info.type == LFS_TYPE_REG);
|
||||
assert(info.size == TO);
|
||||
|
||||
// and with dir read
|
||||
lfsr_dir_t dir;
|
||||
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, ".") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, "hello") == 0);
|
||||
assert(info.type == LFS_TYPE_REG);
|
||||
assert(info.size == TO);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||
lfsr_dir_close(&lfs, &dir) => 0;
|
||||
|
||||
// try reading our file
|
||||
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
|
||||
// is size correct?
|
||||
lfsr_file_size(&lfs, &file) => TO;
|
||||
// try reading
|
||||
uint8_t rbuf[2*TO];
|
||||
memset(rbuf, 0xaa, 2*TO);
|
||||
lfsr_file_read(&lfs, &file, rbuf, 2*TO) => TO;
|
||||
assert(memcmp(rbuf, sim, TO) == 0);
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# TODO
|
||||
# [cases.test_files_fruncate]
|
||||
# [cases.test_files_fruncate_2]
|
||||
|
||||
# writing any data structure backwards always reveals issues
|
||||
[cases.test_files_reversed_holes]
|
||||
[cases.test_files_reversed]
|
||||
defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE']
|
||||
defines.CHUNK = ['CACHE_SIZE/2', '4', '1']
|
||||
# 0 => no init
|
||||
# 1 => fill with data
|
||||
# 2 => truncate to size
|
||||
defines.INIT = [0, 1, 2]
|
||||
defines.SYNC = [false, true]
|
||||
defines.REMOUNT = [false, true]
|
||||
reentrant = true
|
||||
@@ -1067,7 +1177,17 @@ code = '''
|
||||
// simulate our file in ram
|
||||
uint8_t sim[SIZE];
|
||||
uint32_t prng = 42;
|
||||
memset(sim, 0, SIZE);
|
||||
if (INIT == 0) {
|
||||
memset(sim, 0, SIZE);
|
||||
} else if (INIT == 1) {
|
||||
for (lfs_size_t i = 0; i < SIZE; i++) {
|
||||
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE;
|
||||
} else if (INIT == 2) {
|
||||
memset(sim, 0, SIZE);
|
||||
lfsr_file_truncate(&lfs, &file, SIZE) => 0;
|
||||
}
|
||||
|
||||
// sync?
|
||||
if (SYNC) {
|
||||
@@ -1155,7 +1275,10 @@ defines.N = 100
|
||||
defines.SEED = 'range(100)'
|
||||
defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE']
|
||||
defines.CHUNK = ['CACHE_SIZE/2', '4', '1']
|
||||
defines.INIT = [false, true]
|
||||
# 0 => no init
|
||||
# 1 => fill with data
|
||||
# 2 => truncate to size
|
||||
defines.INIT = [0, 1, 2]
|
||||
defines.SYNC = [false, true]
|
||||
defines.REMOUNT = [false, true]
|
||||
code = '''
|
||||
@@ -1171,15 +1294,19 @@ code = '''
|
||||
uint8_t sim[SIZE];
|
||||
lfs_off_t size;
|
||||
uint32_t prng = SEED;
|
||||
if (INIT) {
|
||||
if (INIT == 0) {
|
||||
memset(sim, 0, SIZE);
|
||||
size = 0;
|
||||
} else if (INIT == 1) {
|
||||
for (lfs_size_t i = 0; i < SIZE; i++) {
|
||||
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE;
|
||||
size = SIZE;
|
||||
} else {
|
||||
} else if (INIT == 2) {
|
||||
memset(sim, 0, SIZE);
|
||||
size = 0;
|
||||
lfsr_file_truncate(&lfs, &file, SIZE) => 0;
|
||||
size = SIZE;
|
||||
}
|
||||
|
||||
// sync?
|
||||
@@ -1275,7 +1402,10 @@ defines.SEED = 'range(100)'
|
||||
defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE']
|
||||
# chunk is more an upper limit here
|
||||
defines.CHUNK = ['CACHE_SIZE/2', '4']
|
||||
defines.INIT = [false, true]
|
||||
# 0 => no init
|
||||
# 1 => fill with data
|
||||
# 2 => truncate to size
|
||||
defines.INIT = [0, 1, 2]
|
||||
defines.SYNC = [false, true]
|
||||
defines.REMOUNT = [false, true]
|
||||
code = '''
|
||||
@@ -1291,15 +1421,19 @@ code = '''
|
||||
uint8_t sim[SIZE];
|
||||
lfs_off_t size;
|
||||
uint32_t prng = SEED;
|
||||
if (INIT) {
|
||||
if (INIT == 0) {
|
||||
memset(sim, 0, SIZE);
|
||||
size = 0;
|
||||
} else if (INIT == 1) {
|
||||
for (lfs_size_t i = 0; i < SIZE; i++) {
|
||||
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE;
|
||||
size = SIZE;
|
||||
} else {
|
||||
} else if (INIT == 2) {
|
||||
memset(sim, 0, SIZE);
|
||||
size = 0;
|
||||
lfsr_file_truncate(&lfs, &file, SIZE) => 0;
|
||||
size = SIZE;
|
||||
}
|
||||
|
||||
// sync?
|
||||
@@ -1469,7 +1603,10 @@ defines.WHENCE = ['LFS_SEEK_SET', 'LFS_SEEK_CUR', 'LFS_SEEK_END']
|
||||
defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE']
|
||||
# chunk is more an upper limit here
|
||||
defines.CHUNK = ['CACHE_SIZE/2', '4']
|
||||
defines.INIT = [false, true]
|
||||
# 0 => no init
|
||||
# 1 => fill with data
|
||||
# 2 => truncate to size
|
||||
defines.INIT = [0, 1, 2]
|
||||
defines.SYNC = [false, true]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
@@ -1484,15 +1621,19 @@ code = '''
|
||||
uint8_t sim[SIZE];
|
||||
lfs_off_t size;
|
||||
uint32_t prng = SEED;
|
||||
if (INIT) {
|
||||
if (INIT == 0) {
|
||||
memset(sim, 0, SIZE);
|
||||
size = 0;
|
||||
} else if (INIT == 1) {
|
||||
for (lfs_size_t i = 0; i < SIZE; i++) {
|
||||
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE;
|
||||
size = SIZE;
|
||||
} else {
|
||||
} else if (INIT == 2) {
|
||||
memset(sim, 0, SIZE);
|
||||
size = 0;
|
||||
lfsr_file_truncate(&lfs, &file, SIZE) => 0;
|
||||
size = SIZE;
|
||||
}
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
|
||||
@@ -1588,7 +1729,10 @@ defines.WHENCE = ['LFS_SEEK_SET', 'LFS_SEEK_CUR', 'LFS_SEEK_END']
|
||||
defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE']
|
||||
# chunk is more an upper limit here
|
||||
defines.CHUNK = ['CACHE_SIZE/2', '4']
|
||||
defines.INIT = [false, true]
|
||||
# 0 => no init
|
||||
# 1 => fill with data
|
||||
# 2 => truncate to size
|
||||
defines.INIT = [0, 1, 2]
|
||||
defines.SYNC = [false, true]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
@@ -1603,15 +1747,19 @@ code = '''
|
||||
uint8_t sim[SIZE];
|
||||
lfs_off_t size;
|
||||
uint32_t prng = SEED;
|
||||
if (INIT) {
|
||||
if (INIT == 0) {
|
||||
memset(sim, 0, SIZE);
|
||||
size = 0;
|
||||
} else if (INIT == 1) {
|
||||
for (lfs_size_t i = 0; i < SIZE; i++) {
|
||||
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE;
|
||||
size = SIZE;
|
||||
} else {
|
||||
} else if (INIT == 2) {
|
||||
memset(sim, 0, SIZE);
|
||||
size = 0;
|
||||
lfsr_file_truncate(&lfs, &file, SIZE) => 0;
|
||||
size = SIZE;
|
||||
}
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
|
||||
@@ -1727,7 +1875,10 @@ code = '''
|
||||
[cases.test_files_seek_negative]
|
||||
defines.WHENCE = ['LFS_SEEK_SET', 'LFS_SEEK_CUR', 'LFS_SEEK_END']
|
||||
defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE']
|
||||
defines.INIT = [false, true]
|
||||
# 0 => no init
|
||||
# 1 => fill with data
|
||||
# 2 => truncate to size
|
||||
defines.INIT = [0, 1, 2]
|
||||
defines.MODE = ['LFS_O_RDONLY', 'LFS_O_WRONLY', 'LFS_O_RDWR']
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
@@ -1742,15 +1893,19 @@ code = '''
|
||||
uint8_t sim[SIZE];
|
||||
lfs_off_t size;
|
||||
uint32_t prng = 42;
|
||||
if (INIT) {
|
||||
if (INIT == 0) {
|
||||
memset(sim, 0, SIZE);
|
||||
size = 0;
|
||||
} else if (INIT == 1) {
|
||||
for (lfs_size_t i = 0; i < SIZE; i++) {
|
||||
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE;
|
||||
size = SIZE;
|
||||
} else {
|
||||
} else if (INIT == 2) {
|
||||
memset(sim, 0, SIZE);
|
||||
size = 0;
|
||||
lfsr_file_truncate(&lfs, &file, SIZE) => 0;
|
||||
size = SIZE;
|
||||
}
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
|
||||
@@ -1803,11 +1958,178 @@ code = '''
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# heavy fuzz test with rw seeks, truncate, and TODO fruncate
|
||||
[cases.test_files_rwtf_fuzz]
|
||||
defines.N = 100
|
||||
defines.SEED = 'range(100)'
|
||||
defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE']
|
||||
# chunk is more an upper limit here
|
||||
defines.CHUNK = ['CACHE_SIZE/2', '4']
|
||||
# 0 => no init
|
||||
# 1 => fill with data
|
||||
# 2 => truncate to size
|
||||
defines.INIT = [0, 1, 2]
|
||||
defines.SYNC = [false, true]
|
||||
defines.REMOUNT = [false, true]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfsr_format(&lfs, CFG) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
|
||||
// create a file
|
||||
lfsr_file_t file;
|
||||
lfsr_file_open(&lfs, &file, "hello",
|
||||
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||
// simulate our file in ram
|
||||
uint8_t sim[SIZE];
|
||||
lfs_off_t size;
|
||||
uint32_t prng = SEED;
|
||||
if (INIT == 0) {
|
||||
memset(sim, 0, SIZE);
|
||||
size = 0;
|
||||
} else if (INIT == 1) {
|
||||
for (lfs_size_t i = 0; i < SIZE; i++) {
|
||||
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE;
|
||||
size = SIZE;
|
||||
} else if (INIT == 2) {
|
||||
memset(sim, 0, SIZE);
|
||||
lfsr_file_truncate(&lfs, &file, SIZE) => 0;
|
||||
size = SIZE;
|
||||
}
|
||||
|
||||
// sync?
|
||||
if (SYNC) {
|
||||
lfsr_file_sync(&lfs, &file) => 0;
|
||||
}
|
||||
|
||||
// remount?
|
||||
if (REMOUNT) {
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDWR) => 0;
|
||||
}
|
||||
|
||||
for (lfs_size_t i = 0; i < N; i++) {
|
||||
// and if we are reading, writing, or truncating
|
||||
uint8_t op = TEST_PRNG(&prng) % 3;
|
||||
|
||||
// writing?
|
||||
if (op == 0) {
|
||||
// choose a random location
|
||||
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
||||
// and a random size, up to the chunk size
|
||||
lfs_size_t chunk = lfs_min32(
|
||||
TEST_PRNG(&prng) % CHUNK,
|
||||
SIZE - off);
|
||||
|
||||
// seek
|
||||
lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
|
||||
|
||||
// update the sim
|
||||
for (lfs_size_t j = 0; j < chunk; j++) {
|
||||
sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
if (chunk != 0) {
|
||||
size = lfs_max32(size, off+chunk);
|
||||
}
|
||||
|
||||
// update the file
|
||||
lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk;
|
||||
|
||||
// sync?
|
||||
if (SYNC) {
|
||||
lfsr_file_sync(&lfs, &file) => 0;
|
||||
}
|
||||
|
||||
// remount?
|
||||
if (REMOUNT) {
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDWR) => 0;
|
||||
}
|
||||
|
||||
// reading?
|
||||
} else if (op == 1) {
|
||||
// choose a random location
|
||||
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
||||
// and a random size, up to the chunk size
|
||||
lfs_size_t chunk = lfs_min32(
|
||||
TEST_PRNG(&prng) % CHUNK,
|
||||
SIZE - off);
|
||||
|
||||
// seek
|
||||
lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
|
||||
|
||||
// we may read less than chunk if we're past eof
|
||||
lfs_off_t expected = lfs_min32(
|
||||
chunk,
|
||||
size - lfs_min32(off, size));
|
||||
|
||||
// read the file and assert we got the correct data
|
||||
uint8_t rbuf[2*SIZE];
|
||||
memset(rbuf, 0xaa, 2*SIZE);
|
||||
lfsr_file_read(&lfs, &file, rbuf, chunk) => expected;
|
||||
assert(memcmp(rbuf, &sim[off], expected) == 0);
|
||||
|
||||
// truncating?
|
||||
} else if (op == 2) {
|
||||
// choose a random new file size
|
||||
lfs_off_t size_ = TEST_PRNG(&prng) % SIZE;
|
||||
|
||||
// update the sim
|
||||
memset(&sim[size_], 0, size - lfs_min32(size_, size));
|
||||
size = size_;
|
||||
|
||||
// truncate the file
|
||||
lfsr_file_truncate(&lfs, &file, size_) => 0;
|
||||
}
|
||||
}
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
|
||||
// check our file with stat
|
||||
struct lfs_info info;
|
||||
lfsr_stat(&lfs, "hello", &info) => 0;
|
||||
assert(strcmp(info.name, "hello") == 0);
|
||||
assert(info.type == LFS_TYPE_REG);
|
||||
assert(info.size == size);
|
||||
|
||||
// and with dir read
|
||||
lfsr_dir_t dir;
|
||||
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, ".") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||
assert(strcmp(info.name, "hello") == 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;
|
||||
|
||||
// try reading our file
|
||||
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
|
||||
// is size correct?
|
||||
lfsr_file_size(&lfs, &file) => size;
|
||||
// try reading
|
||||
uint8_t rbuf[2*SIZE];
|
||||
memset(rbuf, 0xaa, 2*SIZE);
|
||||
lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size;
|
||||
// does our file match our simulation?
|
||||
assert(memcmp(rbuf, sim, size) == 0);
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
|
||||
# TODO
|
||||
# [cases.test_files_truncate] ?
|
||||
# [cases.test_files_fruncate] ?
|
||||
# [cases.test_files_no_hidden_data]
|
||||
# [cases.test_files_rwtf_fuzz] ?
|
||||
# [cases.test_files_push] ?
|
||||
# [cases.test_files_pop] ?
|
||||
|
||||
Reference in New Issue
Block a user