From 12e464e9c37dd865e432716856bf2a30fd948297 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 22 May 2019 14:24:05 -0500 Subject: [PATCH] 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 --- lfs.c | 5 +- tests/test_truncate.sh | 144 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 2 deletions(-) diff --git a/lfs.c b/lfs.c index b0e6819..71dad3a 100644 --- a/lfs.c +++ b/lfs.c @@ -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"