Fixed issue with writes following a truncate

The problem was not setting the file state correctly after the truncate.
To truncate < size, we end up using the cache to traverse the ctz
skip-list far away from where our file->pos is.

We can leave the last block in the cache in case we're going to append
to the file, but if we do this we need to set up file->block+file->off
to tell use where we are in the file, and set the LFS_F_READING flag to
indicate that our cache contains read data.

Note this is different than the LFS_F_DIRTY, which we need also. The
purpose of the flags are as follows:
- LFS_F_DIRTY - file ctz skip-list branch is out of sync with
  filesystem, need to update metadata
- LFS_F_READING - file cache is in use for reading, need to drop cache
- LFS_F_WRITING - file cache is in use for writing, need to write out
  cache to disk

The difference between flags is subtle but important because read/prog
caches are handled differently. Prog caches have asserts in place to
catch programs without erases (the infamous pcache->block == 0xffffffff
assert).

Though maybe the names deserve an update...

Found by ebinans
This commit is contained in:
Christopher Haster
2019-05-22 14:24:05 -05:00
parent f35fb8c148
commit 8bf45c3a29
2 changed files with 147 additions and 2 deletions

5
lfs.c
View File

@@ -2864,13 +2864,14 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
// lookup new head in ctz skip list // lookup new head in ctz skip list
err = lfs_ctz_find(lfs, NULL, &file->cache, err = lfs_ctz_find(lfs, NULL, &file->cache,
file->ctz.head, file->ctz.size, file->ctz.head, file->ctz.size,
size, &file->ctz.head, &(lfs_off_t){0}); size, &file->block, &file->off);
if (err) { if (err) {
return err; return err;
} }
file->ctz.head = file->block;
file->ctz.size = size; file->ctz.size = size;
file->flags |= LFS_F_DIRTY; file->flags |= LFS_F_DIRTY | LFS_F_READING;
} else if (size > oldsize) { } else if (size > oldsize) {
lfs_off_t pos = file->pos; lfs_off_t pos = file->pos;

View File

@@ -11,6 +11,150 @@ tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0; lfs_format(&lfs, &cfg) => 0;
TEST TEST
echo "--- Simple truncate ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "baldynoop",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
strcpy((char*)buffer, "hair");
size = strlen((char*)buffer);
for (lfs_off_t j = 0; j < $LARGESIZE; j += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_size(&lfs, &file[0]) => $LARGESIZE;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "baldynoop", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file[0]) => $LARGESIZE;
lfs_file_truncate(&lfs, &file[0], $MEDIUMSIZE) => 0;
lfs_file_size(&lfs, &file[0]) => $MEDIUMSIZE;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "baldynoop", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file[0]) => $MEDIUMSIZE;
size = strlen("hair");
for (lfs_off_t j = 0; j < $MEDIUMSIZE; j += size) {
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "hair", size) => 0;
}
lfs_file_read(&lfs, &file[0], buffer, size) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Truncate and read ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "baldyread",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
strcpy((char*)buffer, "hair");
size = strlen((char*)buffer);
for (lfs_off_t j = 0; j < $LARGESIZE; j += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_size(&lfs, &file[0]) => $LARGESIZE;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "baldyread", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file[0]) => $LARGESIZE;
lfs_file_truncate(&lfs, &file[0], $MEDIUMSIZE) => 0;
lfs_file_size(&lfs, &file[0]) => $MEDIUMSIZE;
size = strlen("hair");
for (lfs_off_t j = 0; j < $MEDIUMSIZE; j += size) {
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "hair", size) => 0;
}
lfs_file_read(&lfs, &file[0], buffer, size) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "baldyread", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file[0]) => $MEDIUMSIZE;
size = strlen("hair");
for (lfs_off_t j = 0; j < $MEDIUMSIZE; j += size) {
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "hair", size) => 0;
}
lfs_file_read(&lfs, &file[0], buffer, size) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Truncate and write ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "baldywrite",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
strcpy((char*)buffer, "hair");
size = strlen((char*)buffer);
for (lfs_off_t j = 0; j < $LARGESIZE; j += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_size(&lfs, &file[0]) => $LARGESIZE;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "baldywrite", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file[0]) => $LARGESIZE;
lfs_file_truncate(&lfs, &file[0], $MEDIUMSIZE) => 0;
lfs_file_size(&lfs, &file[0]) => $MEDIUMSIZE;
strcpy((char*)buffer, "bald");
size = strlen((char*)buffer);
for (lfs_off_t j = 0; j < $MEDIUMSIZE; j += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_size(&lfs, &file[0]) => $MEDIUMSIZE;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "baldywrite", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file[0]) => $MEDIUMSIZE;
size = strlen("bald");
for (lfs_off_t j = 0; j < $MEDIUMSIZE; j += size) {
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "bald", size) => 0;
}
lfs_file_read(&lfs, &file[0], buffer, size) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
# More aggressive general truncation tests
truncate_test() { truncate_test() {
STARTSIZES="$1" STARTSIZES="$1"
STARTSEEKS="$2" STARTSEEKS="$2"