mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 16:14:16 +01:00 
			
		
		
		
	Added sticky-bit for preventing file syncs after write errors
Short story, files are no longer committed to directories during file sync/close if the last write did not complete successfully. This avoids a set of interesting user-experience issues related to the end-of-life behaviour of the filesystem. As a filesystem approaches end-of-life, the chances of running into LFS_ERR_NOSPC grows rather quickly. Since this condition occurs after at the end of a devices life, it's likely that operating in these conditions hasn't been tested thoroughly. In the specific case of file-writes, you can hit an LFS_ERR_NOSPC after parts of the file have been written out. If the program simply continues and closes the file, the file is written out half completed. Since littlefs has a strong garuntee the prevents half-writes, it's unlikely this state of the file would be expected. To make things worse, since close is also responsible for memory cleanup, it's actually _impossible_ to continue working as it was without leaking memory. By prevent the file commits, end-of-life behaviour should at least retain a previous copy of the filesystem without any surprises.
This commit is contained in:
		
							
								
								
									
										9
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -1403,7 +1403,9 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ((file->flags & LFS_F_DIRTY) && !lfs_pairisnull(file->pair)) { |     if ((file->flags & LFS_F_DIRTY) && | ||||||
|  |             !(file->flags & LFS_F_ERRED) && | ||||||
|  |             !lfs_pairisnull(file->pair)) { | ||||||
|         // update dir entry |         // update dir entry | ||||||
|         lfs_dir_t cwd; |         lfs_dir_t cwd; | ||||||
|         int err = lfs_dir_fetch(lfs, &cwd, file->pair); |         int err = lfs_dir_fetch(lfs, &cwd, file->pair); | ||||||
| @@ -1537,6 +1539,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, | |||||||
|                         file->head, file->size, |                         file->head, file->size, | ||||||
|                         file->pos-1, &file->block, &file->off); |                         file->pos-1, &file->block, &file->off); | ||||||
|                 if (err) { |                 if (err) { | ||||||
|  |                     file->flags |= LFS_F_ERRED; | ||||||
|                     return err; |                     return err; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
| @@ -1550,6 +1553,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, | |||||||
|                     file->block, file->pos, |                     file->block, file->pos, | ||||||
|                     &file->block, &file->off); |                     &file->block, &file->off); | ||||||
|             if (err) { |             if (err) { | ||||||
|  |                 file->flags |= LFS_F_ERRED; | ||||||
|                 return err; |                 return err; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -1565,6 +1569,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, | |||||||
|                 if (err == LFS_ERR_CORRUPT) { |                 if (err == LFS_ERR_CORRUPT) { | ||||||
|                     goto relocate; |                     goto relocate; | ||||||
|                 } |                 } | ||||||
|  |                 file->flags |= LFS_F_ERRED; | ||||||
|                 return err; |                 return err; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -1572,6 +1577,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, | |||||||
| relocate: | relocate: | ||||||
|             err = lfs_file_relocate(lfs, file); |             err = lfs_file_relocate(lfs, file); | ||||||
|             if (err) { |             if (err) { | ||||||
|  |                 file->flags |= LFS_F_ERRED; | ||||||
|                 return err; |                 return err; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -1584,6 +1590,7 @@ relocate: | |||||||
|         lfs_alloc_ack(lfs); |         lfs_alloc_ack(lfs); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     file->flags &= ~LFS_F_ERRED; | ||||||
|     return size; |     return size; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -75,6 +75,7 @@ enum lfs_open_flags { | |||||||
|     LFS_F_DIRTY   = 0x10000, // File does not match storage |     LFS_F_DIRTY   = 0x10000, // File does not match storage | ||||||
|     LFS_F_WRITING = 0x20000, // File has been written since last flush |     LFS_F_WRITING = 0x20000, // File has been written since last flush | ||||||
|     LFS_F_READING = 0x40000, // File has been read since last flush |     LFS_F_READING = 0x40000, // File has been read since last flush | ||||||
|  |     LFS_F_ERRED   = 0x80000, // An error occured during write | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // File seek flags | // File seek flags | ||||||
|   | |||||||
| @@ -121,6 +121,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("exhaustion"); |     size = strlen("exhaustion"); | ||||||
|     memcpy(buffer, "exhaustion", size); |     memcpy(buffer, "exhaustion", size); | ||||||
|     lfs_file_write(&lfs, &file[0], buffer, size) => size; |     lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|  |     lfs_file_sync(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
| @@ -142,6 +143,7 @@ tests/test.py << TEST | |||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_RDONLY); |     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_RDONLY); | ||||||
|     size = strlen("exhaustion"); |     size = strlen("exhaustion"); | ||||||
|  |     lfs_file_size(&lfs, &file[0]) => size; | ||||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; |     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||||
|     memcmp(buffer, "exhaustion", size) => 0; |     memcmp(buffer, "exhaustion", size) => 0; | ||||||
|     lfs_file_close(&lfs, &file[0]) => 0; |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
| @@ -166,6 +168,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("exhaustion"); |     size = strlen("exhaustion"); | ||||||
|     memcpy(buffer, "exhaustion", size); |     memcpy(buffer, "exhaustion", size); | ||||||
|     lfs_file_write(&lfs, &file[0], buffer, size) => size; |     lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|  |     lfs_file_sync(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
| @@ -187,6 +190,7 @@ tests/test.py << TEST | |||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_RDONLY); |     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_RDONLY); | ||||||
|     size = strlen("exhaustion"); |     size = strlen("exhaustion"); | ||||||
|  |     lfs_file_size(&lfs, &file[0]) => size; | ||||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; |     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||||
|     memcmp(buffer, "exhaustion", size) => 0; |     memcmp(buffer, "exhaustion", size) => 0; | ||||||
|     lfs_file_close(&lfs, &file[0]) => 0; |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
| @@ -196,14 +200,14 @@ TEST | |||||||
| echo "--- Dir exhaustion test ---" | echo "--- Dir exhaustion test ---" | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_stat(&lfs, "exhaustion", &info) => 0; |  | ||||||
|     lfs_size_t fullsize = info.size; |  | ||||||
|     lfs_remove(&lfs, "exhaustion") => 0; |     lfs_remove(&lfs, "exhaustion") => 0; | ||||||
|  |  | ||||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); |     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; i < fullsize - 2*512; i += size) { |     for (lfs_size_t i = 0; | ||||||
|  |             i < (cfg.block_count-6)*(cfg.block_size-8); | ||||||
|  |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
|     lfs_file_close(&lfs, &file[0]) => 0; |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
| @@ -214,7 +218,11 @@ tests/test.py << TEST | |||||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_APPEND); |     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_APPEND); | ||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|  |     for (lfs_size_t i = 0; | ||||||
|  |             i < (cfg.block_size-8); | ||||||
|  |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|  |     } | ||||||
|     lfs_file_close(&lfs, &file[0]) => 0; |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|     lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; |     lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; | ||||||
| @@ -224,14 +232,14 @@ TEST | |||||||
| echo "--- Chained dir exhaustion test ---" | echo "--- Chained dir exhaustion test ---" | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_stat(&lfs, "exhaustion", &info) => 0; |  | ||||||
|     lfs_size_t fullsize = info.size; |  | ||||||
|  |  | ||||||
|     lfs_remove(&lfs, "exhaustion") => 0; |     lfs_remove(&lfs, "exhaustion") => 0; | ||||||
|  |  | ||||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); |     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; i < fullsize - 19*512; i += size) { |     for (lfs_size_t i = 0; | ||||||
|  |             i < (cfg.block_count-24)*(cfg.block_size-8); | ||||||
|  |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
|     lfs_file_close(&lfs, &file[0]) => 0; |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
| @@ -247,7 +255,9 @@ tests/test.py << TEST | |||||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); |     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; i < fullsize - 20*512; i += size) { |     for (lfs_size_t i = 0; | ||||||
|  |             i < (cfg.block_count-26)*(cfg.block_size-8); | ||||||
|  |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
|     lfs_file_close(&lfs, &file[0]) => 0; |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user