mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	Fixed allocation bugs near the end of storage
Also added better testing specifically for these corner cases around the end of storage
This commit is contained in:
		
							
								
								
									
										72
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -256,12 +256,12 @@ static inline bool lfs_pairisnull(const lfs_block_t pair[2]) { | |||||||
| static inline int lfs_paircmp( | static inline int lfs_paircmp( | ||||||
|         const lfs_block_t paira[2], |         const lfs_block_t paira[2], | ||||||
|         const lfs_block_t pairb[2]) { |         const lfs_block_t pairb[2]) { | ||||||
|     return !((paira[0] == pairb[0] && paira[1] == pairb[1]) || |     return !(paira[0] == pairb[0] || paira[1] == pairb[1] || | ||||||
|              (paira[0] == pairb[1] && paira[1] == pairb[0])); |              paira[0] == pairb[1] || paira[1] == pairb[0]); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir) { | static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir) { | ||||||
|     // Allocate pair of dir blocks |     // allocate pair of dir blocks | ||||||
|     for (int i = 0; i < 2; i++) { |     for (int i = 0; i < 2; i++) { | ||||||
|         int err = lfs_alloc(lfs, &dir->pair[i]); |         int err = lfs_alloc(lfs, &dir->pair[i]); | ||||||
|         if (err) { |         if (err) { | ||||||
| @@ -269,21 +269,26 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Rather than clobbering one of the blocks we just pretend |     // we couldn't find unique blocks, we're out of space | ||||||
|  |     if (dir->pair[0] == dir->pair[1]) { | ||||||
|  |         return LFS_ERR_NOSPC; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // rather than clobbering one of the blocks we just pretend | ||||||
|     // the revision may be valid |     // the revision may be valid | ||||||
|     int err = lfs_bd_read(lfs, dir->pair[0], 0, &dir->d.rev, 4); |     int err = lfs_bd_read(lfs, dir->pair[0], 0, &dir->d.rev, 4); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Set defaults |     // set defaults | ||||||
|     dir->d.rev += 1; |     dir->d.rev += 1; | ||||||
|     dir->d.size = sizeof(dir->d); |     dir->d.size = sizeof(dir->d); | ||||||
|     dir->d.tail[0] = 0; |     dir->d.tail[0] = 0; | ||||||
|     dir->d.tail[1] = 0; |     dir->d.tail[1] = 0; | ||||||
|     dir->off = sizeof(dir->d); |     dir->off = sizeof(dir->d); | ||||||
|  |  | ||||||
|     // Don't write out yet, let caller take care of that |     // don't write out yet, let caller take care of that | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -484,6 +489,7 @@ static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir, | |||||||
|             return lfs_dir_commit(lfs, dir, entry, data); |             return lfs_dir_commit(lfs, dir, entry, data); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // we need to allocate a new dir block | ||||||
|         if (!(0x80000000 & dir->d.size)) { |         if (!(0x80000000 & dir->d.size)) { | ||||||
|             lfs_dir_t newdir; |             lfs_dir_t newdir; | ||||||
|             int err = lfs_dir_alloc(lfs, &newdir); |             int err = lfs_dir_alloc(lfs, &newdir); | ||||||
| @@ -491,6 +497,13 @@ static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir, | |||||||
|                 return err; |                 return err; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             // our allocator doesn't track blocks before being appended, | ||||||
|  |             // so even if we found some blocks, they may not be unique | ||||||
|  |             if (entry->d.type == LFS_TYPE_DIR && | ||||||
|  |                     lfs_paircmp(entry->d.u.dir, newdir.pair) == 0) { | ||||||
|  |                 return LFS_ERR_NOSPC; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             newdir.d.tail[0] = dir->d.tail[0]; |             newdir.d.tail[0] = dir->d.tail[0]; | ||||||
|             newdir.d.tail[1] = dir->d.tail[1]; |             newdir.d.tail[1] = dir->d.tail[1]; | ||||||
|             entry->off = newdir.d.size; |             entry->off = newdir.d.size; | ||||||
| @@ -1677,7 +1690,7 @@ static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2]) { | |||||||
|         .d.tail[1] = lfs->root[1], |         .d.tail[1] = lfs->root[1], | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     while (parent.d.tail[0]) { |     while (true) { | ||||||
|         lfs_entry_t entry; |         lfs_entry_t entry; | ||||||
|         int err = lfs_dir_fetch(lfs, &parent, parent.d.tail); |         int err = lfs_dir_fetch(lfs, &parent, parent.d.tail); | ||||||
|         if (err) { |         if (err) { | ||||||
| @@ -1699,9 +1712,11 @@ static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2]) { | |||||||
|                 return true; |                 return true; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return false; |         if (lfs_pairisnull(parent.d.tail)) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| int lfs_deorphan(lfs_t *lfs) { | int lfs_deorphan(lfs_t *lfs) { | ||||||
| @@ -1715,31 +1730,34 @@ int lfs_deorphan(lfs_t *lfs) { | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     while (pdir.d.tail[0]) { |     while (!lfs_pairisnull(pdir.d.tail)) { | ||||||
|         int err = lfs_dir_fetch(lfs, &cdir, pdir.d.tail); |         int err = lfs_dir_fetch(lfs, &cdir, pdir.d.tail); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // check if we have a parent |         // only check head blocks | ||||||
|         int parent = lfs_parent(lfs, pdir.d.tail); |         if (!(0x80000000 & pdir.d.size)) { | ||||||
|         if (parent < 0) { |             // check if we have a parent | ||||||
|             return parent; |             int parent = lfs_parent(lfs, pdir.d.tail); | ||||||
|         } |             if (parent < 0) { | ||||||
|  |                 return parent; | ||||||
|         if (!parent) { |  | ||||||
|             // we are an orphan |  | ||||||
|             LFS_DEBUG("Orphan %d %d", pdir.d.tail[0], pdir.d.tail[1]); |  | ||||||
|  |  | ||||||
|             pdir.d.tail[0] = cdir.d.tail[0]; |  | ||||||
|             pdir.d.tail[1] = cdir.d.tail[1]; |  | ||||||
|  |  | ||||||
|             err = lfs_dir_commit(lfs, &pdir, NULL, NULL); |  | ||||||
|             if (err) { |  | ||||||
|                 return err; |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             break; |             if (!parent) { | ||||||
|  |                 // we are an orphan | ||||||
|  |                 LFS_DEBUG("Orphan %d %d", pdir.d.tail[0], pdir.d.tail[1]); | ||||||
|  |  | ||||||
|  |                 pdir.d.tail[0] = cdir.d.tail[0]; | ||||||
|  |                 pdir.d.tail[1] = cdir.d.tail[1]; | ||||||
|  |  | ||||||
|  |                 err = lfs_dir_commit(lfs, &pdir, NULL, NULL); | ||||||
|  |                 if (err) { | ||||||
|  |                     return err; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         memcpy(&pdir, &cdir, sizeof(pdir)); |         memcpy(&pdir, &cdir, sizeof(pdir)); | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -195,6 +195,7 @@ typedef struct lfs { | |||||||
|     lfs_size_t words;       // number of 32-bit words that can fit in a block |     lfs_size_t words;       // number of 32-bit words that can fit in a block | ||||||
|  |  | ||||||
|     lfs_block_t root[2]; |     lfs_block_t root[2]; | ||||||
|  |     lfs_dir_t *scratch; | ||||||
|     lfs_file_t *files; |     lfs_file_t *files; | ||||||
|  |  | ||||||
|     struct { |     struct { | ||||||
|   | |||||||
| @@ -193,5 +193,69 @@ tests/test.py << TEST | |||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
|  |  | ||||||
|  | echo "--- Dir exhaustion test ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_stat(&lfs, "exhaustion", &info) => 0; | ||||||
|  |     lfs_size_t fullsize = info.size; | ||||||
|  |     lfs_remove(&lfs, "exhaustion") => 0; | ||||||
|  |  | ||||||
|  |     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||||
|  |     size = strlen("blahblahblahblah"); | ||||||
|  |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|  |     for (lfs_size_t i = 0; i < fullsize - 2*512; i += size) { | ||||||
|  |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|  |     } | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|  |     lfs_mkdir(&lfs, "exhaustiondir") => 0; | ||||||
|  |     lfs_remove(&lfs, "exhaustiondir") => 0; | ||||||
|  |  | ||||||
|  |     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_APPEND); | ||||||
|  |     size = strlen("blahblahblahblah"); | ||||||
|  |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|  |     lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|  |     lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Chained dir exhaustion test ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_stat(&lfs, "exhaustion", &info) => 0; | ||||||
|  |     lfs_size_t fullsize = info.size; | ||||||
|  |  | ||||||
|  |     lfs_remove(&lfs, "exhaustion") => 0; | ||||||
|  |     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||||
|  |     size = strlen("blahblahblahblah"); | ||||||
|  |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|  |     for (lfs_size_t i = 0; i < fullsize - 19*512; i += size) { | ||||||
|  |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|  |     } | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < 9; i++) { | ||||||
|  |         sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i); | ||||||
|  |         lfs_mkdir(&lfs, (char*)buffer) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; | ||||||
|  |  | ||||||
|  |     lfs_remove(&lfs, "exhaustion") => 0; | ||||||
|  |     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||||
|  |     size = strlen("blahblahblahblah"); | ||||||
|  |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|  |     for (lfs_size_t i = 0; i < fullsize - 20*512; i += size) { | ||||||
|  |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|  |     } | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|  |     lfs_mkdir(&lfs, "exhaustiondir") => 0; | ||||||
|  |     lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  |  | ||||||
| echo "--- Results ---" | echo "--- Results ---" | ||||||
| tests/stats.py | tests/stats.py | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user