Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/littlefs-project/littlefs.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Haster <chaster@utexas.edu>2018-01-21 02:30:40 +0300
committerChristopher Haster <chaster@utexas.edu>2018-01-21 04:22:44 +0300
commitd88f0ac02ff93b549418d38d464b5591fce4f688 (patch)
treea10fb4ed3bbcaed8d24180a04532c20c15a24fc7
parent2ad435ed6309096979d6f90aa762632d23202a42 (diff)
Added lfs_file_truncate
As a copy-on-write filesystem, the truncate function is a very nice function to have, as it can take advantage of reusing the data already written out to disk.
-rw-r--r--Makefile2
-rw-r--r--lfs.c57
-rw-r--r--lfs.h5
-rwxr-xr-xtests/test_truncate.sh133
4 files changed, 195 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index cf978e7..bf047f1 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/lfs.c b/lfs.c
index ccc623b..ff4d1da 100644
--- a/lfs.c
+++ b/lfs.c
@@ -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;
+ }
}
diff --git a/lfs.h b/lfs.h
index b612f62..e85780a 100644
--- a/lfs.h
+++ b/lfs.h
@@ -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