mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	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
		
			
				
	
	
		
			303 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			303 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| #!/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
 | |
| 
 | |
| 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"
 | |
| HOTSIZES="$3"
 | |
| COLDSIZES="$4"
 | |
| tests/test.py << TEST
 | |
|     static const lfs_off_t startsizes[] = {$STARTSIZES};
 | |
|     static const lfs_off_t startseeks[] = {$STARTSEEKS};
 | |
|     static const lfs_off_t hotsizes[]   = {$HOTSIZES};
 | |
| 
 | |
|     lfs_mount(&lfs, &cfg) => 0;
 | |
| 
 | |
|     for (unsigned 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 (lfs_off_t j = 0; j < startsizes[i]; j += size) {
 | |
|             lfs_file_write(&lfs, &file[0], buffer, size) => size;
 | |
|         }
 | |
|         lfs_file_size(&lfs, &file[0]) => startsizes[i];
 | |
| 
 | |
|         if (startseeks[i] != startsizes[i]) {
 | |
|             lfs_file_seek(&lfs, &file[0],
 | |
|                     startseeks[i], LFS_SEEK_SET) => startseeks[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 (unsigned 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");
 | |
|         lfs_off_t 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 (unsigned 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");
 | |
|         lfs_off_t 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" \
 | |
|     "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" \
 | |
|     "           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" \
 | |
|     "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" \
 | |
|     "           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 "--- Mid-file shrinking truncate ---"
 | |
| truncate_test \
 | |
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \
 | |
|     "  $LARGESIZE,   $LARGESIZE,   $LARGESIZE,   $LARGESIZE,   $LARGESIZE" \
 | |
|     "           0,   $SMALLSIZE,  $MEDIUMSIZE,   $LARGESIZE, 2*$LARGESIZE" \
 | |
|     "           0,            0,            0,            0,            0"
 | |
| 
 | |
| echo "--- Mid-file expanding truncate ---"
 | |
| truncate_test \
 | |
|     "           0,   $SMALLSIZE,  $MEDIUMSIZE,   $LARGESIZE, 2*$LARGESIZE" \
 | |
|     "           0,            0,   $SMALLSIZE,  $MEDIUMSIZE,   $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
 |