diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | lfs.c | 57 | ||||
-rw-r--r-- | lfs.h | 5 | ||||
-rwxr-xr-x | tests/test_truncate.sh | 133 |
4 files changed, 195 insertions, 2 deletions
@@ -33,7 +33,7 @@ size: $(OBJ) $(SIZE) -t $^ .SUFFIXES: -test: test_format test_dirs test_files test_seek test_parallel \ +test: test_format test_dirs test_files test_seek test_truncate test_parallel \ test_alloc test_paths test_orphan test_move test_corrupt test_%: tests/test_%.sh ifdef QUIET @@ -1664,6 +1664,57 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, return file->pos; } +int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { + if ((file->flags & 3) == LFS_O_RDONLY) { + return LFS_ERR_INVAL; + } + + if (size < lfs_file_size(lfs, file)) { + // need to flush since directly changing metadata + int err = lfs_file_flush(lfs, file); + if (err) { + return err; + } + + // lookup new head in ctz skip list + err = lfs_ctz_find(lfs, &file->cache, NULL, + file->head, file->size, + size, &file->head, &(lfs_off_t){0}); + if (err) { + return err; + } + + file->size = size; + file->flags |= LFS_F_DIRTY; + } else if (size > lfs_file_size(lfs, file)) { + lfs_off_t pos = file->pos; + + // flush+seek if not already at end + if (file->pos != lfs_file_size(lfs, file)) { + int err = lfs_file_seek(lfs, file, 0, SEEK_END); + if (err) { + return err; + } + } + + // fill with zeros + while (file->pos < size) { + lfs_ssize_t res = lfs_file_write(lfs, file, &(uint8_t){0}, 1); + if (res < 0) { + return res; + } + } + + // restore pos + int err = lfs_file_seek(lfs, file, pos, LFS_SEEK_SET); + if (err < 0) { + return err; + } + } + + return 0; +} + lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file) { return file->pos; } @@ -1678,7 +1729,11 @@ int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file) { } lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) { - return lfs_max(file->pos, file->size); + if (file->flags & LFS_F_WRITING) { + return lfs_max(file->pos, file->size); + } else { + return file->size; + } } @@ -364,6 +364,11 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, lfs_soff_t off, int whence); +// Truncates the size of the file to the specified size +// +// Returns a negative error code on failure. +int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size); + // Return the position of the file // // Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR) diff --git a/tests/test_truncate.sh b/tests/test_truncate.sh new file mode 100755 index 0000000..1da0cce --- /dev/null +++ b/tests/test_truncate.sh @@ -0,0 +1,133 @@ +#!/bin/bash +set -eu + +SMALLSIZE=32 +MEDIUMSIZE=2048 +LARGESIZE=8192 + +echo "=== Truncate tests ===" +rm -rf blocks +tests/test.py << TEST + lfs_format(&lfs, &cfg) => 0; +TEST + +truncate_test() { +STARTSIZES="$1" +HOTSIZES="$2" +COLDSIZES="$3" +tests/test.py << TEST + static const lfs_off_t startsizes[] = {$STARTSIZES}; + static const lfs_off_t hotsizes[] = {$HOTSIZES}; + + lfs_mount(&lfs, &cfg) => 0; + + for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { + sprintf((char*)buffer, "hairyhead%d", i); + lfs_file_open(&lfs, &file[0], (const char*)buffer, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + + strcpy((char*)buffer, "hair"); + size = strlen((char*)buffer); + for (int j = 0; j < startsizes[i]; j += size) { + lfs_file_write(&lfs, &file[0], buffer, size) => size; + } + lfs_file_size(&lfs, &file[0]) => startsizes[i]; + + lfs_file_truncate(&lfs, &file[0], hotsizes[i]) => 0; + lfs_file_size(&lfs, &file[0]) => hotsizes[i]; + + lfs_file_close(&lfs, &file[0]) => 0; + } + + lfs_unmount(&lfs) => 0; +TEST +tests/test.py << TEST + static const lfs_off_t startsizes[] = {$STARTSIZES}; + static const lfs_off_t hotsizes[] = {$HOTSIZES}; + static const lfs_off_t coldsizes[] = {$COLDSIZES}; + + lfs_mount(&lfs, &cfg) => 0; + + for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { + sprintf((char*)buffer, "hairyhead%d", i); + lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDWR) => 0; + lfs_file_size(&lfs, &file[0]) => hotsizes[i]; + + size = strlen("hair"); + int j = 0; + for (; j < startsizes[i] && j < hotsizes[i]; j += size) { + lfs_file_read(&lfs, &file[0], buffer, size) => size; + memcmp(buffer, "hair", size) => 0; + } + + for (; j < hotsizes[i]; j += size) { + lfs_file_read(&lfs, &file[0], buffer, size) => size; + memcmp(buffer, "\0\0\0\0", size) => 0; + } + + lfs_file_truncate(&lfs, &file[0], coldsizes[i]) => 0; + lfs_file_size(&lfs, &file[0]) => coldsizes[i]; + + lfs_file_close(&lfs, &file[0]) => 0; + } + + lfs_unmount(&lfs) => 0; +TEST +tests/test.py << TEST + static const lfs_off_t startsizes[] = {$STARTSIZES}; + static const lfs_off_t hotsizes[] = {$HOTSIZES}; + static const lfs_off_t coldsizes[] = {$COLDSIZES}; + + lfs_mount(&lfs, &cfg) => 0; + + for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { + sprintf((char*)buffer, "hairyhead%d", i); + lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDONLY) => 0; + lfs_file_size(&lfs, &file[0]) => coldsizes[i]; + + size = strlen("hair"); + int j = 0; + for (; j < startsizes[i] && j < hotsizes[i] && j < coldsizes[i]; + j += size) { + lfs_file_read(&lfs, &file[0], buffer, size) => size; + memcmp(buffer, "hair", size) => 0; + } + + for (; j < coldsizes[i]; j += size) { + lfs_file_read(&lfs, &file[0], buffer, size) => size; + memcmp(buffer, "\0\0\0\0", size) => 0; + } + + lfs_file_close(&lfs, &file[0]) => 0; + } + + lfs_unmount(&lfs) => 0; +TEST +} + +echo "--- Cold shrinking truncate ---" +truncate_test \ + "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \ + "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \ + " 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" + +echo "--- Cold expanding truncate ---" +truncate_test \ + " 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \ + " 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \ + "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" + +echo "--- Warm shrinking truncate ---" +truncate_test \ + "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \ + " 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \ + " 0, 0, 0, 0, 0" + +echo "--- Warm expanding truncate ---" +truncate_test \ + " 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \ + "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \ + "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" + +echo "--- Results ---" +tests/stats.py |