diff options
author | Christopher Haster <chaster@utexas.edu> | 2019-05-22 22:24:05 +0300 |
---|---|---|
committer | Christopher Haster <chaster@utexas.edu> | 2019-05-24 00:43:10 +0300 |
commit | 12e464e9c37dd865e432716856bf2a30fd948297 (patch) | |
tree | d92547957eeef84badd4ff9515a5bbd17dc08b7e | |
parent | 9899c7fe486d4a581fb84ecf832a574896d411b7 (diff) |
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
-rw-r--r-- | lfs.c | 5 | ||||
-rwxr-xr-x | tests/test_truncate.sh | 144 |
2 files changed, 147 insertions, 2 deletions
@@ -2867,13 +2867,14 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { // lookup new head in ctz skip list err = lfs_ctz_find(lfs, NULL, &file->cache, file->ctz.head, file->ctz.size, - size, &file->ctz.head, &(lfs_off_t){0}); + size, &file->block, &file->off); if (err) { return err; } + file->ctz.head = file->block; file->ctz.size = size; - file->flags |= LFS_F_DIRTY; + file->flags |= LFS_F_DIRTY | LFS_F_READING; } else if (size > oldsize) { lfs_off_t pos = file->pos; diff --git a/tests/test_truncate.sh b/tests/test_truncate.sh index 053b2e0..c12fc0d 100755 --- a/tests/test_truncate.sh +++ b/tests/test_truncate.sh @@ -11,6 +11,150 @@ tests/test.py << TEST lfs_format(&lfs, &cfg) => 0; 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() { STARTSIZES="$1" STARTSEEKS="$2" |