mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 00:32:38 +01:00 
			
		
		
		
	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.
This commit is contained in:
		
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @@ -33,7 +33,7 @@ size: $(OBJ) | |||||||
| 	$(SIZE) -t $^ | 	$(SIZE) -t $^ | ||||||
|  |  | ||||||
| .SUFFIXES: | .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_alloc test_paths test_orphan test_move test_corrupt | ||||||
| test_%: tests/test_%.sh | test_%: tests/test_%.sh | ||||||
| ifdef QUIET | ifdef QUIET | ||||||
|   | |||||||
							
								
								
									
										57
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -1664,6 +1664,57 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, | |||||||
|     return file->pos; |     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) { | lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file) { | ||||||
|     return file->pos; |     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) { | 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; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								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 lfs_file_seek(lfs_t *lfs, lfs_file_t *file, | ||||||
|         lfs_soff_t off, int whence); |         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 | // Return the position of the file | ||||||
| // | // | ||||||
| // Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR) | // Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR) | ||||||
|   | |||||||
							
								
								
									
										133
									
								
								tests/test_truncate.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										133
									
								
								tests/test_truncate.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -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 | ||||||
		Reference in New Issue
	
	Block a user