mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	Added support for handling corrupted blocks
This provides a limited form of wear leveling. While wear is not actually balanced across blocks, the filesystem can recover from corrupted blocks and extend the lifetime of a device nearly as much as dynamic wear leveling. For use-cases where wear is important, it would be better to use a full form of dynamic wear-leveling at the block level. (or consider a logging filesystem). Corrupted block handling was simply added on top of the existing logic in place for the filesystem, so it's a bit more noodly than it may have to be, but it gets the work done.
This commit is contained in:
		
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @@ -32,7 +32,7 @@ size: $(OBJ) | |||||||
|  |  | ||||||
| .SUFFIXES: | .SUFFIXES: | ||||||
| test: test_format test_dirs test_files test_seek test_parallel \ | test: test_format test_dirs test_files test_seek test_parallel \ | ||||||
| 	test_alloc test_paths test_orphan | 	test_alloc test_paths test_orphan test_corrupt | ||||||
| test_%: tests/test_%.sh | test_%: tests/test_%.sh | ||||||
| 	./$< | 	./$< | ||||||
|  |  | ||||||
|   | |||||||
| @@ -144,13 +144,24 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, | |||||||
|         return -errno; |         return -errno; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     err = fseek(f, off, SEEK_SET); | ||||||
|  |     if (err) { | ||||||
|  |         return -errno; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     uint8_t dat; | ||||||
|  |     res = fread(&dat, 1, 1, f); | ||||||
|  |     if (res < 1) { | ||||||
|  |         return -errno; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     err = fclose(f); |     err = fclose(f); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return -errno; |         return -errno; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     emu->stats.prog_count += 1; |     emu->stats.prog_count += 1; | ||||||
|     return 0; |     return (dat != data[0]) ? LFS_ERR_CORRUPT : 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { | int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||||
|   | |||||||
							
								
								
									
										598
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										598
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -191,6 +191,16 @@ static int lfs_cmp(lfs_t *lfs, lfs_block_t block, | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /// Internal operations predeclared here /// | ||||||
|  | int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); | ||||||
|  | static int lfs_pred(lfs_t *lfs, const lfs_block_t dir[2], lfs_dir_t *pdir); | ||||||
|  | static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2], | ||||||
|  |         lfs_dir_t *parent, lfs_entry_t *entry); | ||||||
|  | static int lfs_relocate(lfs_t *lfs, | ||||||
|  |         const lfs_block_t oldpair[2], const lfs_block_t newpair[2]); | ||||||
|  | int lfs_deorphan(lfs_t *lfs); | ||||||
|  |  | ||||||
|  |  | ||||||
| /// Block allocator /// | /// Block allocator /// | ||||||
| static int lfs_alloc_lookahead(void *p, lfs_block_t block) { | static int lfs_alloc_lookahead(void *p, lfs_block_t block) { | ||||||
|     lfs_t *lfs = p; |     lfs_t *lfs = p; | ||||||
| @@ -203,11 +213,27 @@ static int lfs_alloc_lookahead(void *p, lfs_block_t block) { | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int lfs_alloc_scan(lfs_t *lfs, lfs_block_t *block) { | static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { | ||||||
|     lfs_block_t end = lfs->free.start + lfs->cfg->block_count; |     // deorphan if we haven't yet, only needed once after poweron | ||||||
|  |     if (!lfs->deorphaned) { | ||||||
|  |         int err = lfs_deorphan(lfs); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     while (true) { |     while (true) { | ||||||
|         while (lfs->free.off < lfs->cfg->lookahead) { |         while (true) { | ||||||
|  |             // check if we have looked at all blocks since last ack | ||||||
|  |             if (lfs->free.start + lfs->free.off == lfs->free.end) { | ||||||
|  |                 LFS_WARN("No more free space %d", lfs->free.end); | ||||||
|  |                 return LFS_ERR_NOSPC; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (lfs->free.off >= lfs->cfg->lookahead) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             lfs_block_t off = lfs->free.off; |             lfs_block_t off = lfs->free.off; | ||||||
|             lfs->free.off += 1; |             lfs->free.off += 1; | ||||||
|  |  | ||||||
| @@ -218,12 +244,8 @@ static int lfs_alloc_scan(lfs_t *lfs, lfs_block_t *block) { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // could not find block |  | ||||||
|         lfs->free.start += lfs->cfg->lookahead; |         lfs->free.start += lfs->cfg->lookahead; | ||||||
|         lfs->free.off = 0; |         lfs->free.off = 0; | ||||||
|         if (lfs_scmp(lfs->free.start, end) > 0) { |  | ||||||
|             return LFS_ERR_NOSPC; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // find mask of free blocks from tree |         // find mask of free blocks from tree | ||||||
|         memset(lfs->free.lookahead, 0, lfs->cfg->lookahead/8); |         memset(lfs->free.lookahead, 0, lfs->cfg->lookahead/8); | ||||||
| @@ -234,27 +256,8 @@ static int lfs_alloc_scan(lfs_t *lfs, lfs_block_t *block) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { | static void lfs_alloc_ack(lfs_t *lfs) { | ||||||
|     // try to scan for free block |     lfs->free.end = lfs->free.start + lfs->free.off + lfs->cfg->block_count; | ||||||
|     int err = lfs_alloc_scan(lfs, block); |  | ||||||
|     if (err != LFS_ERR_NOSPC) { |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // still can't allocate a block? check for orphans |  | ||||||
|     err = lfs_deorphan(lfs); |  | ||||||
|     if (err) { |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // scan again or die trying |  | ||||||
|     err = lfs_alloc_scan(lfs, block); |  | ||||||
|     if (err) { |  | ||||||
|         LFS_WARN("No more free space%s", ""); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -276,6 +279,13 @@ static inline int lfs_paircmp( | |||||||
|              paira[0] == pairb[1] || paira[1] == pairb[0]); |              paira[0] == pairb[1] || paira[1] == pairb[0]); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static inline bool lfs_pairsync( | ||||||
|  |         const lfs_block_t paira[2], | ||||||
|  |         const lfs_block_t pairb[2]) { | ||||||
|  |     return (paira[0] == pairb[0] && paira[1] == pairb[1]) || | ||||||
|  |            (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++) { | ||||||
| @@ -285,11 +295,6 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // 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 |     // rather than clobbering one of the blocks we just pretend | ||||||
|     // the revision may be valid |     // the revision may be valid | ||||||
|     int err = lfs_read(lfs, dir->pair[0], 0, &dir->d.rev, 4); |     int err = lfs_read(lfs, dir->pair[0], 0, &dir->d.rev, 4); | ||||||
| @@ -360,13 +365,30 @@ static int lfs_dir_fetch(lfs_t *lfs, | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | struct lfs_region { | ||||||
|  |     lfs_off_t oldoff; | ||||||
|  |     lfs_size_t oldlen; | ||||||
|  |     const void *newdata; | ||||||
|  |     lfs_size_t newlen; | ||||||
|  | }; | ||||||
|  |  | ||||||
| static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, | static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, | ||||||
|         const lfs_entry_t *entry, const void *data) { |         const struct lfs_region *regions, int count) { | ||||||
|     dir->d.rev += 1; |     dir->d.rev += 1; | ||||||
|     lfs_pairswap(dir->pair); |     lfs_pairswap(dir->pair); | ||||||
|  |     for (int i = 0; i < count; i++) { | ||||||
|  |         dir->d.size += regions[i].newlen - regions[i].oldlen; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const lfs_block_t oldpair[2] = {dir->pair[0], dir->pair[1]}; | ||||||
|  |     bool relocated = false; | ||||||
|  |  | ||||||
|  |     while (true) { | ||||||
|         int err = lfs_erase(lfs, dir->pair[0]); |         int err = lfs_erase(lfs, dir->pair[0]); | ||||||
|         if (err) { |         if (err) { | ||||||
|  |             if (err == LFS_ERR_CORRUPT) { | ||||||
|  |                 goto relocate; | ||||||
|  |             } | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -374,125 +396,118 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, | |||||||
|         crc = lfs_crc(crc, &dir->d, sizeof(dir->d)); |         crc = lfs_crc(crc, &dir->d, sizeof(dir->d)); | ||||||
|         err = lfs_prog(lfs, dir->pair[0], 0, &dir->d, sizeof(dir->d)); |         err = lfs_prog(lfs, dir->pair[0], 0, &dir->d, sizeof(dir->d)); | ||||||
|         if (err) { |         if (err) { | ||||||
|  |             if (err == LFS_ERR_CORRUPT) { | ||||||
|  |                 goto relocate; | ||||||
|  |             } | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     lfs_off_t off = sizeof(dir->d); |         int i = 0; | ||||||
|     lfs_size_t size = 0x7fffffff & dir->d.size; |         lfs_off_t oldoff = sizeof(dir->d); | ||||||
|     while (off < size) { |         lfs_off_t newoff = sizeof(dir->d); | ||||||
|         if (entry && off == entry->off) { |         lfs_size_t newsize = 0x7fffffff & dir->d.size; | ||||||
|             crc = lfs_crc(crc, &entry->d, sizeof(entry->d)); |         while (newoff < newsize) { | ||||||
|  |             if (i < count && regions[i].oldoff == oldoff) { | ||||||
|  |                 crc = lfs_crc(crc, regions[i].newdata, regions[i].newlen); | ||||||
|                 int err = lfs_prog(lfs, dir->pair[0], |                 int err = lfs_prog(lfs, dir->pair[0], | ||||||
|                     off, &entry->d, sizeof(entry->d)); |                         newoff, regions[i].newdata, regions[i].newlen); | ||||||
|                 if (err) { |                 if (err) { | ||||||
|  |                     if (err == LFS_ERR_CORRUPT) { | ||||||
|  |                         goto relocate; | ||||||
|  |                     } | ||||||
|                     return err; |                     return err; | ||||||
|                 } |                 } | ||||||
|             off += sizeof(entry->d); |  | ||||||
|  |  | ||||||
|             if (data) { |                 oldoff += regions[i].oldlen; | ||||||
|                 crc = lfs_crc(crc, data, entry->d.len - sizeof(entry->d)); |                 newoff += regions[i].newlen; | ||||||
|                 int err = lfs_prog(lfs, dir->pair[0], |                 i += 1; | ||||||
|                         off, data, entry->d.len - sizeof(entry->d)); |  | ||||||
|                 if (err) { |  | ||||||
|                     return err; |  | ||||||
|                 } |  | ||||||
|                 off += entry->d.len - sizeof(entry->d); |  | ||||||
|             } |  | ||||||
|             } else { |             } else { | ||||||
|                 uint8_t data; |                 uint8_t data; | ||||||
|             int err = lfs_read(lfs, dir->pair[1], off, &data, 1); |                 int err = lfs_read(lfs, oldpair[1], oldoff, &data, 1); | ||||||
|                 if (err) { |                 if (err) { | ||||||
|                     return err; |                     return err; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 crc = lfs_crc(crc, &data, 1); |                 crc = lfs_crc(crc, &data, 1); | ||||||
|             err = lfs_prog(lfs, dir->pair[0], off, &data, 1); |                 err = lfs_prog(lfs, dir->pair[0], newoff, &data, 1); | ||||||
|                 if (err) { |                 if (err) { | ||||||
|  |                     if (err == LFS_ERR_CORRUPT) { | ||||||
|  |                         goto relocate; | ||||||
|  |                     } | ||||||
|                     return err; |                     return err; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|             off += 1; |                 oldoff += 1; | ||||||
|  |                 newoff += 1; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     while (off < lfs->cfg->block_size-4) { |         while (newoff < lfs->cfg->block_size-4) { | ||||||
|             uint8_t data = 0xff; |             uint8_t data = 0xff; | ||||||
|             crc = lfs_crc(crc, &data, 1); |             crc = lfs_crc(crc, &data, 1); | ||||||
|         err = lfs_prog(lfs, dir->pair[0], off, &data, 1); |             err = lfs_prog(lfs, dir->pair[0], newoff, &data, 1); | ||||||
|             if (err) { |             if (err) { | ||||||
|  |                 if (err == LFS_ERR_CORRUPT) { | ||||||
|  |                     goto relocate; | ||||||
|  |                 } | ||||||
|                 return err; |                 return err; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|         off += 1; |             newoff += 1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         err = lfs_prog(lfs, dir->pair[0], lfs->cfg->block_size-4, &crc, 4); |         err = lfs_prog(lfs, dir->pair[0], lfs->cfg->block_size-4, &crc, 4); | ||||||
|         if (err) { |         if (err) { | ||||||
|  |             if (err == LFS_ERR_CORRUPT) { | ||||||
|  |                 goto relocate; | ||||||
|  |             } | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     return lfs_sync(lfs); |         err = lfs_sync(lfs); | ||||||
|  |         if (err) { | ||||||
|  |             if (err == LFS_ERR_CORRUPT) { | ||||||
|  |                 goto relocate; | ||||||
|  |             } | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // successful commit | ||||||
|  |         if (relocated) { | ||||||
|  |             LFS_DEBUG("Relocating %d %d to %d %d", | ||||||
|  |                     oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); | ||||||
|  |             return lfs_relocate(lfs, oldpair, dir->pair); | ||||||
|  |         } | ||||||
|  |         return 0; | ||||||
|  |  | ||||||
|  | relocate: | ||||||
|  |         LFS_DEBUG("Bad block at %d", dir->pair[0]); | ||||||
|  |  | ||||||
|  |         // drop caches and prepare to relocate block | ||||||
|  |         relocated = true; | ||||||
|  |         lfs->pcache.block = 0xffffffff; | ||||||
|  |  | ||||||
|  |         // can't relocate superblock, filesystem is now frozen | ||||||
|  |         if (lfs_paircmp(oldpair, (const lfs_block_t[2]){0, 1}) == 0) { | ||||||
|  |             LFS_WARN("Superblock %d has become unwritable", oldpair[0]); | ||||||
|  |             return LFS_ERR_CORRUPT; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // relocate half of pair | ||||||
|  |         err = lfs_alloc(lfs, &dir->pair[0]); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static int lfs_dir_shift(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) { | static int lfs_dir_update(lfs_t *lfs, lfs_dir_t *dir, | ||||||
|     dir->d.rev += 1; |         const lfs_entry_t *entry, const void *data) { | ||||||
|     dir->d.size -= entry->d.len; |     return lfs_dir_commit(lfs, dir, (struct lfs_region[]){ | ||||||
|     lfs_pairswap(dir->pair); |             {entry->off, sizeof(entry->d), &entry->d, sizeof(entry->d)}, | ||||||
|  |             {entry->off+sizeof(entry->d), entry->d.len-sizeof(entry->d), | ||||||
|     int err = lfs_erase(lfs, dir->pair[0]); |              data, entry->d.len-sizeof(entry->d)} | ||||||
|     if (err) { |         }, data ? 2 : 1); | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     uint32_t crc = 0xffffffff; |  | ||||||
|     crc = lfs_crc(crc, &dir->d, sizeof(dir->d)); |  | ||||||
|     err = lfs_prog(lfs, dir->pair[0], 0, &dir->d, sizeof(dir->d)); |  | ||||||
|     if (err) { |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs_off_t woff = sizeof(dir->d); |  | ||||||
|     lfs_off_t roff = sizeof(dir->d); |  | ||||||
|     lfs_size_t size = 0x7fffffff & dir->d.size; |  | ||||||
|     while (woff < size) { |  | ||||||
|         if (roff == entry->off) { |  | ||||||
|             roff += entry->d.len; |  | ||||||
|         } else { |  | ||||||
|             uint8_t data; |  | ||||||
|             int err = lfs_read(lfs, dir->pair[1], roff, &data, 1); |  | ||||||
|             if (err) { |  | ||||||
|                 return err; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             crc = lfs_crc(crc, &data, 1); |  | ||||||
|             err = lfs_prog(lfs, dir->pair[0], woff, &data, 1); |  | ||||||
|             if (err) { |  | ||||||
|                 return err; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             woff += 1; |  | ||||||
|             roff += 1; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     while (woff < lfs->cfg->block_size-4) { |  | ||||||
|         uint8_t data = 0xff; |  | ||||||
|         crc = lfs_crc(crc, &data, 1); |  | ||||||
|         err = lfs_prog(lfs, dir->pair[0], woff, &data, 1); |  | ||||||
|         if (err) { |  | ||||||
|             return err; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         woff += 1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     err = lfs_prog(lfs, dir->pair[0], lfs->cfg->block_size-4, &crc, 4); |  | ||||||
|     if (err) { |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return lfs_sync(lfs); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir, | static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir, | ||||||
| @@ -501,8 +516,10 @@ static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir, | |||||||
|     while (true) { |     while (true) { | ||||||
|         if (dir->d.size + entry->d.len <= lfs->cfg->block_size - 4) { |         if (dir->d.size + entry->d.len <= lfs->cfg->block_size - 4) { | ||||||
|             entry->off = dir->d.size; |             entry->off = dir->d.size; | ||||||
|             dir->d.size += entry->d.len; |             return lfs_dir_commit(lfs, dir, (struct lfs_region[]){ | ||||||
|             return lfs_dir_commit(lfs, dir, entry, data); |                     {entry->off, 0, &entry->d, sizeof(entry->d)}, | ||||||
|  |                     {entry->off, 0, data, entry->d.len - sizeof(entry->d)} | ||||||
|  |                 }, 2); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // we need to allocate a new dir block |         // we need to allocate a new dir block | ||||||
| @@ -513,18 +530,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; | ||||||
|             newdir.d.size += entry->d.len; |             err = lfs_dir_commit(lfs, &newdir, (struct lfs_region[]){ | ||||||
|             err = lfs_dir_commit(lfs, &newdir, entry, data); |                     {entry->off, 0, &entry->d, sizeof(entry->d)}, | ||||||
|  |                     {entry->off, 0, data, entry->d.len - sizeof(entry->d)} | ||||||
|  |                 }, 2); | ||||||
|             if (err) { |             if (err) { | ||||||
|                 return err; |                 return err; | ||||||
|             } |             } | ||||||
| @@ -532,7 +544,7 @@ static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir, | |||||||
|             dir->d.size |= 0x80000000; |             dir->d.size |= 0x80000000; | ||||||
|             dir->d.tail[0] = newdir.pair[0]; |             dir->d.tail[0] = newdir.pair[0]; | ||||||
|             dir->d.tail[1] = newdir.pair[1]; |             dir->d.tail[1] = newdir.pair[1]; | ||||||
|             return lfs_dir_commit(lfs, dir, NULL, NULL); |             return lfs_dir_commit(lfs, dir, NULL, 0); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         int err = lfs_dir_fetch(lfs, dir, dir->d.tail); |         int err = lfs_dir_fetch(lfs, dir, dir->d.tail); | ||||||
| @@ -546,33 +558,29 @@ static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) { | |||||||
|     // either shift out the one entry or remove the whole dir block |     // either shift out the one entry or remove the whole dir block | ||||||
|     if (dir->d.size == sizeof(dir->d)) { |     if (dir->d.size == sizeof(dir->d)) { | ||||||
|         lfs_dir_t pdir; |         lfs_dir_t pdir; | ||||||
|         int err = lfs_dir_fetch(lfs, &pdir, lfs->root); |         int res = lfs_pred(lfs, dir->pair, &pdir); | ||||||
|         if (err) { |         if (res < 0) { | ||||||
|             return err; |             return res; | ||||||
|         } |  | ||||||
|  |  | ||||||
|         while (lfs_paircmp(pdir.d.tail, dir->pair) != 0) { |  | ||||||
|             int err = lfs_dir_fetch(lfs, &pdir, pdir.d.tail); |  | ||||||
|             if (err) { |  | ||||||
|                 return err; |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (!(pdir.d.size & 0x80000000)) { |         if (!(pdir.d.size & 0x80000000)) { | ||||||
|             return lfs_dir_shift(lfs, dir, entry); |             return lfs_dir_commit(lfs, dir, (struct lfs_region[]){ | ||||||
|  |                 {entry->off, entry->d.len, NULL, 0}, | ||||||
|  |             }, 1); | ||||||
|         } else { |         } else { | ||||||
|             pdir.d.tail[0] = dir->d.tail[0]; |             pdir.d.tail[0] = dir->d.tail[0]; | ||||||
|             pdir.d.tail[1] = dir->d.tail[1]; |             pdir.d.tail[1] = dir->d.tail[1]; | ||||||
|             return lfs_dir_commit(lfs, &pdir, NULL, NULL); |             return lfs_dir_commit(lfs, dir, NULL, 0); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         return lfs_dir_shift(lfs, dir, entry); |         return lfs_dir_commit(lfs, dir, (struct lfs_region[]){ | ||||||
|  |             {entry->off, entry->d.len, NULL, 0}, | ||||||
|  |         }, 1); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) { | static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) { | ||||||
|     while (true) { |     while (dir->off + sizeof(entry->d) > (0x7fffffff & dir->d.size)) { | ||||||
|         if (dir->off + sizeof(entry->d) > (0x7fffffff & dir->d.size)) { |  | ||||||
|         if (!(0x80000000 & dir->d.size)) { |         if (!(0x80000000 & dir->d.size)) { | ||||||
|             entry->off = dir->off; |             entry->off = dir->off; | ||||||
|             return LFS_ERR_NOENT; |             return LFS_ERR_NOENT; | ||||||
| @@ -585,7 +593,6 @@ static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) { | |||||||
|  |  | ||||||
|         dir->off = sizeof(dir->d); |         dir->off = sizeof(dir->d); | ||||||
|         dir->pos += sizeof(dir->d); |         dir->pos += sizeof(dir->d); | ||||||
|             continue; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int err = lfs_read(lfs, dir->pair[0], dir->off, |     int err = lfs_read(lfs, dir->pair[0], dir->off, | ||||||
| @@ -596,12 +603,8 @@ static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) { | |||||||
|  |  | ||||||
|     dir->off += entry->d.len; |     dir->off += entry->d.len; | ||||||
|     dir->pos += entry->d.len; |     dir->pos += entry->d.len; | ||||||
|         if ((0xff & entry->d.type) == LFS_TYPE_REG || |  | ||||||
|             (0xff & entry->d.type) == LFS_TYPE_DIR) { |  | ||||||
|     entry->off = dir->off - entry->d.len; |     entry->off = dir->off - entry->d.len; | ||||||
|     return 0; |     return 0; | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir, | static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir, | ||||||
| @@ -653,7 +656,9 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir, | |||||||
|                 return err; |                 return err; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (entry->d.len - sizeof(entry->d) != pathlen) { |             if (((0xff & entry->d.type) != LFS_TYPE_REG && | ||||||
|  |                  (0xff & entry->d.type) != LFS_TYPE_DIR) || | ||||||
|  |                 entry->d.len - sizeof(entry->d) != pathlen) { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -663,7 +668,7 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir, | |||||||
|                 return ret; |                 return ret; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             // Found match |             // found match | ||||||
|             if (ret == true) { |             if (ret == true) { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
| @@ -707,7 +712,9 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { | |||||||
|         return err ? err : LFS_ERR_EXISTS; |         return err ? err : LFS_ERR_EXISTS; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Build up new directory |     // build up new directory | ||||||
|  |     lfs_alloc_ack(lfs); | ||||||
|  |  | ||||||
|     lfs_dir_t dir; |     lfs_dir_t dir; | ||||||
|     err = lfs_dir_alloc(lfs, &dir); |     err = lfs_dir_alloc(lfs, &dir); | ||||||
|     if (err) { |     if (err) { | ||||||
| @@ -716,7 +723,7 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { | |||||||
|     dir.d.tail[0] = cwd.d.tail[0]; |     dir.d.tail[0] = cwd.d.tail[0]; | ||||||
|     dir.d.tail[1] = cwd.d.tail[1]; |     dir.d.tail[1] = cwd.d.tail[1]; | ||||||
|  |  | ||||||
|     err = lfs_dir_commit(lfs, &dir, NULL, NULL); |     err = lfs_dir_commit(lfs, &dir, NULL, 0); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
| @@ -729,7 +736,13 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { | |||||||
|     cwd.d.tail[0] = dir.pair[0]; |     cwd.d.tail[0] = dir.pair[0]; | ||||||
|     cwd.d.tail[1] = dir.pair[1]; |     cwd.d.tail[1] = dir.pair[1]; | ||||||
|  |  | ||||||
|     return lfs_dir_append(lfs, &cwd, &entry, path); |     err = lfs_dir_append(lfs, &cwd, &entry, path); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs_alloc_ack(lfs); | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) { | int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) { | ||||||
| @@ -773,7 +786,7 @@ int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) { | |||||||
| } | } | ||||||
|  |  | ||||||
| int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir) { | int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir) { | ||||||
|     // Do nothing, dir is always synchronized |     // do nothing, dir is always synchronized | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -794,17 +807,24 @@ int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     lfs_entry_t entry; |     lfs_entry_t entry; | ||||||
|  |     while (true) { | ||||||
|         int err = lfs_dir_next(lfs, dir, &entry); |         int err = lfs_dir_next(lfs, dir, &entry); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return (err == LFS_ERR_NOENT) ? 0 : err; |             return (err == LFS_ERR_NOENT) ? 0 : err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         if ((0xff & entry.d.type) == LFS_TYPE_REG || | ||||||
|  |             (0xff & entry.d.type) == LFS_TYPE_DIR) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     info->type = entry.d.type & 0xff; |     info->type = entry.d.type & 0xff; | ||||||
|     if (info->type == LFS_TYPE_REG) { |     if (info->type == LFS_TYPE_REG) { | ||||||
|         info->size = entry.d.u.file.size; |         info->size = entry.d.u.file.size; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     err = lfs_read(lfs, dir->pair[0], entry.off + sizeof(entry.d), |     int err = lfs_read(lfs, dir->pair[0], entry.off + sizeof(entry.d), | ||||||
|             info->name, entry.d.len - sizeof(entry.d)); |             info->name, entry.d.len - sizeof(entry.d)); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return err; |         return err; | ||||||
| @@ -906,6 +926,7 @@ static int lfs_index_extend(lfs_t *lfs, | |||||||
|         lfs_cache_t *rcache, lfs_cache_t *pcache, |         lfs_cache_t *rcache, lfs_cache_t *pcache, | ||||||
|         lfs_block_t head, lfs_size_t size, |         lfs_block_t head, lfs_size_t size, | ||||||
|         lfs_off_t *block, lfs_block_t *off) { |         lfs_off_t *block, lfs_block_t *off) { | ||||||
|  |     while (true) { | ||||||
|         // go ahead and grab a block |         // go ahead and grab a block | ||||||
|         int err = lfs_alloc(lfs, block); |         int err = lfs_alloc(lfs, block); | ||||||
|         if (err) { |         if (err) { | ||||||
| @@ -914,6 +935,9 @@ static int lfs_index_extend(lfs_t *lfs, | |||||||
|  |  | ||||||
|         err = lfs_erase(lfs, *block); |         err = lfs_erase(lfs, *block); | ||||||
|         if (err) { |         if (err) { | ||||||
|  |             if (err == LFS_ERR_CORRUPT) { | ||||||
|  |                 goto relocate; | ||||||
|  |             } | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -937,6 +961,9 @@ static int lfs_index_extend(lfs_t *lfs, | |||||||
|  |  | ||||||
|                 err = lfs_cache_prog(lfs, pcache, *block, i, &data, 1); |                 err = lfs_cache_prog(lfs, pcache, *block, i, &data, 1); | ||||||
|                 if (err) { |                 if (err) { | ||||||
|  |                     if (err == LFS_ERR_CORRUPT) { | ||||||
|  |                         goto relocate; | ||||||
|  |                     } | ||||||
|                     return err; |                     return err; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @@ -953,6 +980,9 @@ static int lfs_index_extend(lfs_t *lfs, | |||||||
|         for (lfs_off_t i = 0; i < skips; i++) { |         for (lfs_off_t i = 0; i < skips; i++) { | ||||||
|             int err = lfs_cache_prog(lfs, pcache, *block, 4*i, &head, 4); |             int err = lfs_cache_prog(lfs, pcache, *block, 4*i, &head, 4); | ||||||
|             if (err) { |             if (err) { | ||||||
|  |                 if (err == LFS_ERR_CORRUPT) { | ||||||
|  |                     goto relocate; | ||||||
|  |                 } | ||||||
|                 return err; |                 return err; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -966,6 +996,13 @@ static int lfs_index_extend(lfs_t *lfs, | |||||||
|  |  | ||||||
|         *off = 4*skips; |         *off = 4*skips; | ||||||
|         return 0; |         return 0; | ||||||
|  |  | ||||||
|  | relocate: | ||||||
|  |         LFS_DEBUG("Bad block at %d", *block); | ||||||
|  |  | ||||||
|  |         // just clear cache and try a new block | ||||||
|  |         pcache->block = 0xffffffff; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static int lfs_index_traverse(lfs_t *lfs, | static int lfs_index_traverse(lfs_t *lfs, | ||||||
| @@ -1003,7 +1040,7 @@ static int lfs_index_traverse(lfs_t *lfs, | |||||||
| /// Top level file operations /// | /// Top level file operations /// | ||||||
| int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||||
|         const char *path, int flags) { |         const char *path, int flags) { | ||||||
|     // Allocate entry for file if it doesn't exist |     // allocate entry for file if it doesn't exist | ||||||
|     lfs_dir_t cwd; |     lfs_dir_t cwd; | ||||||
|     int err = lfs_dir_fetch(lfs, &cwd, lfs->root); |     int err = lfs_dir_fetch(lfs, &cwd, lfs->root); | ||||||
|     if (err) { |     if (err) { | ||||||
| @@ -1044,7 +1081,6 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | |||||||
|     file->size = entry.d.u.file.size; |     file->size = entry.d.u.file.size; | ||||||
|     file->flags = flags; |     file->flags = flags; | ||||||
|     file->pos = 0; |     file->pos = 0; | ||||||
|     file->block = -1; // TODO rm me? |  | ||||||
|  |  | ||||||
|     if (flags & LFS_O_TRUNC) { |     if (flags & LFS_O_TRUNC) { | ||||||
|         file->head = -1; |         file->head = -1; | ||||||
| @@ -1181,7 +1217,7 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { | |||||||
|         entry.d.u.file.head = file->head; |         entry.d.u.file.head = file->head; | ||||||
|         entry.d.u.file.size = file->size; |         entry.d.u.file.size = file->size; | ||||||
|  |  | ||||||
|         err = lfs_dir_commit(lfs, &cwd, &entry, NULL); |         err = lfs_dir_update(lfs, &cwd, &entry, NULL); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
| @@ -1283,6 +1319,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             // extend file with new blocks |             // extend file with new blocks | ||||||
|  |             lfs_alloc_ack(lfs); | ||||||
|             int err = lfs_index_extend(lfs, &lfs->rcache, &file->cache, |             int err = lfs_index_extend(lfs, &lfs->rcache, &file->cache, | ||||||
|                     file->block, file->pos, |                     file->block, file->pos, | ||||||
|                     &file->block, &file->off); |                     &file->block, &file->off); | ||||||
| @@ -1293,16 +1330,64 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, | |||||||
|  |  | ||||||
|         // program as much as we can in current block |         // program as much as we can in current block | ||||||
|         lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->off); |         lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->off); | ||||||
|  |         while (true) { | ||||||
|             int err = lfs_cache_prog(lfs, &file->cache, |             int err = lfs_cache_prog(lfs, &file->cache, | ||||||
|                     file->block, file->off, data, diff); |                     file->block, file->off, data, diff); | ||||||
|             if (err) { |             if (err) { | ||||||
|  |                 if (err == LFS_ERR_CORRUPT) { | ||||||
|  |                     goto relocate; | ||||||
|  |                 } | ||||||
|                 return err; |                 return err; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             break; | ||||||
|  | relocate: | ||||||
|  |             LFS_DEBUG("Bad block at %d", file->block); | ||||||
|  |  | ||||||
|  |             // just relocate what exists into new block | ||||||
|  |             lfs_block_t nblock; | ||||||
|  |             err = lfs_alloc(lfs, &nblock); | ||||||
|  |             if (err) { | ||||||
|  |                 return err; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // either read from dirty cache or disk | ||||||
|  |             for (lfs_off_t i = 0; i < file->off; i++) { | ||||||
|  |                 uint8_t data; | ||||||
|  |                 if (file->cache.block == file->block && i >= file->cache.off) { | ||||||
|  |                     data = file->cache.buffer[i - file->cache.off]; | ||||||
|  |                 } else { | ||||||
|  |                     // just read from disk | ||||||
|  |                     err = lfs_read(lfs, file->block, i, &data, 1); | ||||||
|  |                     if (err) { | ||||||
|  |                         return err; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 err = lfs_prog(lfs, nblock, i, &data, 1); | ||||||
|  |                 if (err) { | ||||||
|  |                     if (err == LFS_ERR_CORRUPT) { | ||||||
|  |                         goto relocate; | ||||||
|  |                     } | ||||||
|  |                     return err; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // copy over new state of file | ||||||
|  |             memcpy(file->cache.buffer, lfs->pcache.buffer, lfs->cfg->prog_size); | ||||||
|  |             file->cache.block = lfs->pcache.block; | ||||||
|  |             file->cache.off = lfs->pcache.off; | ||||||
|  |             lfs->pcache.block = 0xffffffff; | ||||||
|  |  | ||||||
|  |             file->block = nblock; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         file->pos += diff; |         file->pos += diff; | ||||||
|         file->off += diff; |         file->off += diff; | ||||||
|         data += diff; |         data += diff; | ||||||
|         nsize -= diff; |         nsize -= diff; | ||||||
|  |  | ||||||
|  |         lfs_alloc_ack(lfs); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return size; |     return size; | ||||||
| @@ -1485,7 +1570,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | |||||||
|     newentry.d.len = sizeof(newentry.d) + strlen(newpath); |     newentry.d.len = sizeof(newentry.d) + strlen(newpath); | ||||||
|  |  | ||||||
|     if (prevexists) { |     if (prevexists) { | ||||||
|         int err = lfs_dir_commit(lfs, &newcwd, &newentry, newpath); |         int err = lfs_dir_update(lfs, &newcwd, &newentry, newpath); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
| @@ -1574,14 +1659,17 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // setup files as an empty list |     // setup default state | ||||||
|  |     lfs->root[0] = 0xffffffff; | ||||||
|  |     lfs->root[1] = 0xffffffff; | ||||||
|     lfs->files = NULL; |     lfs->files = NULL; | ||||||
|  |     lfs->deorphaned = false; | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int lfs_deinit(lfs_t *lfs) { | static int lfs_deinit(lfs_t *lfs) { | ||||||
|     // Free allocated memory |     // free allocated memory | ||||||
|     if (!lfs->cfg->read_buffer) { |     if (!lfs->cfg->read_buffer) { | ||||||
|         free(lfs->rcache.buffer); |         free(lfs->rcache.buffer); | ||||||
|     } |     } | ||||||
| @@ -1603,26 +1691,28 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Create free lookahead |     // create free lookahead | ||||||
|     memset(lfs->free.lookahead, 0, lfs->cfg->lookahead/8); |     memset(lfs->free.lookahead, 0, lfs->cfg->lookahead/8); | ||||||
|     lfs->free.start = 0; |     lfs->free.start = 0; | ||||||
|     lfs->free.off = 0; |     lfs->free.off = 0; | ||||||
|  |     lfs->free.end = lfs->free.start + lfs->cfg->block_count; | ||||||
|  |  | ||||||
|     // Create superblock dir |     // create superblock dir | ||||||
|  |     lfs_alloc_ack(lfs); | ||||||
|     lfs_dir_t superdir; |     lfs_dir_t superdir; | ||||||
|     err = lfs_dir_alloc(lfs, &superdir); |     err = lfs_dir_alloc(lfs, &superdir); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Write root directory |     // write root directory | ||||||
|     lfs_dir_t root; |     lfs_dir_t root; | ||||||
|     err = lfs_dir_alloc(lfs, &root); |     err = lfs_dir_alloc(lfs, &root); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     err = lfs_dir_commit(lfs, &root, NULL, NULL); |     err = lfs_dir_commit(lfs, &root, NULL, 0); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
| @@ -1630,7 +1720,7 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|     lfs->root[0] = root.pair[0]; |     lfs->root[0] = root.pair[0]; | ||||||
|     lfs->root[1] = root.pair[1]; |     lfs->root[1] = root.pair[1]; | ||||||
|  |  | ||||||
|     // Write superblocks |     // write superblocks | ||||||
|     lfs_superblock_t superblock = { |     lfs_superblock_t superblock = { | ||||||
|         .off = sizeof(superdir.d), |         .off = sizeof(superdir.d), | ||||||
|         .d.type = LFS_TYPE_SUPERBLOCK, |         .d.type = LFS_TYPE_SUPERBLOCK, | ||||||
| @@ -1643,18 +1733,24 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|     }; |     }; | ||||||
|     superdir.d.tail[0] = root.pair[0]; |     superdir.d.tail[0] = root.pair[0]; | ||||||
|     superdir.d.tail[1] = root.pair[1]; |     superdir.d.tail[1] = root.pair[1]; | ||||||
|     superdir.d.size += sizeof(superdir.d); |     superdir.d.size = sizeof(superdir.d) + sizeof(superblock.d); | ||||||
|  |  | ||||||
|  |     // write both pairs to be safe | ||||||
|  |     bool valid = false; | ||||||
|     for (int i = 0; i < 2; i++) { |     for (int i = 0; i < 2; i++) { | ||||||
|         // Write both pairs for extra safety, do some finagling to pretend |         int err = lfs_dir_commit(lfs, &superdir, (struct lfs_region[]){ | ||||||
|         // the superblock is an entry |                 {sizeof(superdir.d), sizeof(superblock.d), | ||||||
|         int err = lfs_dir_commit(lfs, &superdir, |                  &superblock.d, sizeof(superblock.d)} | ||||||
|                 (const lfs_entry_t*)&superblock, |             }, 1); | ||||||
|                 (const struct lfs_disk_entry*)&superblock.d + 1); |         if (err && err != LFS_ERR_CORRUPT) { | ||||||
|         if (err) { |  | ||||||
|             LFS_ERROR("Failed to write superblock at %d", superdir.pair[0]); |  | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         valid = valid || !err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!valid) { | ||||||
|  |         return LFS_ERR_CORRUPT; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // sanity check that fetch works |     // sanity check that fetch works | ||||||
| @@ -1663,6 +1759,7 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     lfs_alloc_ack(lfs); | ||||||
|     return lfs_deinit(lfs); |     return lfs_deinit(lfs); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1675,6 +1772,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|     // setup free lookahead |     // setup free lookahead | ||||||
|     lfs->free.start = -lfs->cfg->lookahead; |     lfs->free.start = -lfs->cfg->lookahead; | ||||||
|     lfs->free.off = lfs->cfg->lookahead; |     lfs->free.off = lfs->cfg->lookahead; | ||||||
|  |     lfs->free.end = lfs->free.start + lfs->cfg->block_count; | ||||||
|  |  | ||||||
|     // load superblock |     // load superblock | ||||||
|     lfs_dir_t dir; |     lfs_dir_t dir; | ||||||
| @@ -1711,6 +1809,10 @@ int lfs_unmount(lfs_t *lfs) { | |||||||
|  |  | ||||||
| /// Littlefs specific operations /// | /// Littlefs specific operations /// | ||||||
| int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) { | int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) { | ||||||
|  |     if (lfs_pairisnull(lfs->root)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // iterate over metadata pairs |     // iterate over metadata pairs | ||||||
|     lfs_dir_t dir; |     lfs_dir_t dir; | ||||||
|     lfs_entry_t entry; |     lfs_entry_t entry; | ||||||
| @@ -1777,22 +1879,49 @@ int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) { | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2]) { | static int lfs_pred(lfs_t *lfs, const lfs_block_t dir[2], lfs_dir_t *pdir) { | ||||||
|     // iterate over all directory directory entries |     if (lfs_pairisnull(lfs->root)) { | ||||||
|     lfs_dir_t parent = { |         return 0; | ||||||
|         .d.tail[0] = lfs->root[0], |     } | ||||||
|         .d.tail[1] = lfs->root[1], |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     while (true) { |     // iterate over all directory directory entries | ||||||
|         lfs_entry_t entry; |     int err = lfs_dir_fetch(lfs, pdir, (const lfs_block_t[2]){0, 1}); | ||||||
|         int err = lfs_dir_fetch(lfs, &parent, parent.d.tail); |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     while (!lfs_pairisnull(pdir->d.tail)) { | ||||||
|  |         if (lfs_paircmp(pdir->d.tail, dir) == 0) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         int err = lfs_dir_fetch(lfs, pdir, pdir->d.tail); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2], | ||||||
|  |         lfs_dir_t *parent, lfs_entry_t *entry) { | ||||||
|  |     if (lfs_pairisnull(lfs->root)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     parent->d.tail[0] = 0; | ||||||
|  |     parent->d.tail[1] = 1; | ||||||
|  |  | ||||||
|  |     // iterate over all directory directory entries | ||||||
|  |     while (!lfs_pairisnull(parent->d.tail)) { | ||||||
|  |         int err = lfs_dir_fetch(lfs, parent, parent->d.tail); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         while (true) { |         while (true) { | ||||||
|             int err = lfs_dir_next(lfs, &parent, &entry); |             int err = lfs_dir_next(lfs, parent, entry); | ||||||
|             if (err && err != LFS_ERR_NOENT) { |             if (err && err != LFS_ERR_NOENT) { | ||||||
|                 return err; |                 return err; | ||||||
|             } |             } | ||||||
| @@ -1801,29 +1930,81 @@ static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2]) { | |||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if ((0xf & entry.d.type) == LFS_TYPE_DIR && |             if (((0xf & entry->d.type) == LFS_TYPE_DIR) &&  | ||||||
|                     lfs_paircmp(entry.d.u.dir, dir) == 0) { |                  lfs_paircmp(entry->d.u.dir, dir) == 0) { | ||||||
|                 return true; |                 return true; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|         if (lfs_pairisnull(parent.d.tail)) { |  | ||||||
|     return false; |     return false; | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| int lfs_deorphan(lfs_t *lfs) { | static int lfs_relocate(lfs_t *lfs, | ||||||
|     // iterate over all directories |         const lfs_block_t oldpair[2], const lfs_block_t newpair[2]) { | ||||||
|     lfs_dir_t pdir; |     // find parent | ||||||
|     lfs_dir_t cdir; |     lfs_dir_t parent; | ||||||
|  |     lfs_entry_t entry; | ||||||
|  |     int res = lfs_parent(lfs, oldpair, &parent, &entry); | ||||||
|  |     if (res < 0) { | ||||||
|  |         return res; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // skip root |     if (res) { | ||||||
|     int err = lfs_dir_fetch(lfs, &pdir, lfs->root); |         // update disk, this creates a desync | ||||||
|  |         entry.d.u.dir[0] = newpair[0]; | ||||||
|  |         entry.d.u.dir[1] = newpair[1]; | ||||||
|  |  | ||||||
|  |         int err = lfs_dir_update(lfs, &parent, &entry, NULL); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // update internal root | ||||||
|  |         if (lfs_paircmp(oldpair, lfs->root) == 0) { | ||||||
|  |             LFS_DEBUG("Relocating root %d %d", newpair[0], newpair[1]); | ||||||
|  |             lfs->root[0] = newpair[0]; | ||||||
|  |             lfs->root[1] = newpair[1]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // clean up bad block, which should now be a desync | ||||||
|  |         return lfs_deorphan(lfs); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // find pred | ||||||
|  |     res = lfs_pred(lfs, oldpair, &parent); | ||||||
|  |     if (res < 0) { | ||||||
|  |         return res; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (res) { | ||||||
|  |         // just replace bad pair, no desync can occur | ||||||
|  |         parent.d.tail[0] = newpair[0]; | ||||||
|  |         parent.d.tail[0] = newpair[0]; | ||||||
|  |  | ||||||
|  |         return lfs_dir_commit(lfs, &parent, NULL, 0); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // couldn't find dir, must be new | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs_deorphan(lfs_t *lfs) { | ||||||
|  |     lfs->deorphaned = true; | ||||||
|  |     if (lfs_pairisnull(lfs->root)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs_dir_t pdir; | ||||||
|  |     lfs_dir_t cdir; | ||||||
|  |  | ||||||
|  |     // skip superblock | ||||||
|  |     int err = lfs_dir_fetch(lfs, &pdir, (const lfs_block_t[2]){0, 1}); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // iterate over all directories | ||||||
|     while (!lfs_pairisnull(pdir.d.tail)) { |     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) { | ||||||
| @@ -1833,19 +2014,36 @@ int lfs_deorphan(lfs_t *lfs) { | |||||||
|         // only check head blocks |         // only check head blocks | ||||||
|         if (!(0x80000000 & pdir.d.size)) { |         if (!(0x80000000 & pdir.d.size)) { | ||||||
|             // check if we have a parent |             // check if we have a parent | ||||||
|             int parent = lfs_parent(lfs, pdir.d.tail); |             lfs_dir_t parent; | ||||||
|             if (parent < 0) { |             lfs_entry_t entry; | ||||||
|                 return parent; |             int res = lfs_parent(lfs, pdir.d.tail, &parent, &entry); | ||||||
|  |             if (res < 0) { | ||||||
|  |                 return res; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (!parent) { |             if (!res) { | ||||||
|                 // we are an orphan |                 // we are an orphan | ||||||
|                 LFS_DEBUG("Orphan %d %d", pdir.d.tail[0], pdir.d.tail[1]); |                 LFS_DEBUG("Orphan %d %d", pdir.d.tail[0], pdir.d.tail[1]); | ||||||
|  |  | ||||||
|                 pdir.d.tail[0] = cdir.d.tail[0]; |                 pdir.d.tail[0] = cdir.d.tail[0]; | ||||||
|                 pdir.d.tail[1] = cdir.d.tail[1]; |                 pdir.d.tail[1] = cdir.d.tail[1]; | ||||||
|  |  | ||||||
|                 err = lfs_dir_commit(lfs, &pdir, NULL, NULL); |                 err = lfs_dir_commit(lfs, &pdir, NULL, 0); | ||||||
|  |                 if (err) { | ||||||
|  |                     return err; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (!lfs_pairsync(entry.d.u.dir, pdir.d.tail)) { | ||||||
|  |                 // we have desynced | ||||||
|  |                 LFS_DEBUG("Desync %d %d", entry.d.u.dir[0], entry.d.u.dir[1]); | ||||||
|  |  | ||||||
|  |                 pdir.d.tail[0] = entry.d.u.dir[0]; | ||||||
|  |                 pdir.d.tail[1] = entry.d.u.dir[1]; | ||||||
|  |  | ||||||
|  |                 err = lfs_dir_commit(lfs, &pdir, NULL, 0); | ||||||
|                 if (err) { |                 if (err) { | ||||||
|                     return err; |                     return err; | ||||||
|                 } |                 } | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -43,7 +43,7 @@ enum lfs_error { | |||||||
| enum lfs_type { | enum lfs_type { | ||||||
|     LFS_TYPE_REG        = 0x01, |     LFS_TYPE_REG        = 0x01, | ||||||
|     LFS_TYPE_DIR        = 0x02, |     LFS_TYPE_DIR        = 0x02, | ||||||
|     LFS_TYPE_SUPERBLOCK = 0x10, |     LFS_TYPE_SUPERBLOCK = 0x12, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| enum lfs_open_flags { | enum lfs_open_flags { | ||||||
| @@ -193,15 +193,16 @@ typedef struct lfs_superblock { | |||||||
|     struct lfs_disk_superblock { |     struct lfs_disk_superblock { | ||||||
|         uint16_t type; |         uint16_t type; | ||||||
|         uint16_t len; |         uint16_t len; | ||||||
|  |         lfs_block_t root[2]; | ||||||
|         uint32_t version; |         uint32_t version; | ||||||
|         char magic[8]; |         char magic[8]; | ||||||
|         uint32_t block_size; |         uint32_t block_size; | ||||||
|         uint32_t block_count; |         uint32_t block_count; | ||||||
|         lfs_block_t root[2]; |  | ||||||
|     } d; |     } d; | ||||||
| } lfs_superblock_t; | } lfs_superblock_t; | ||||||
|  |  | ||||||
| typedef struct lfs_free { | typedef struct lfs_free { | ||||||
|  |     lfs_block_t end; | ||||||
|     lfs_block_t start; |     lfs_block_t start; | ||||||
|     lfs_block_t off; |     lfs_block_t off; | ||||||
|     uint32_t *lookahead; |     uint32_t *lookahead; | ||||||
| @@ -212,8 +213,8 @@ typedef struct lfs { | |||||||
|     const struct lfs_config *cfg; |     const struct lfs_config *cfg; | ||||||
|  |  | ||||||
|     lfs_block_t root[2]; |     lfs_block_t root[2]; | ||||||
|     lfs_dir_t *scratch; |  | ||||||
|     lfs_file_t *files; |     lfs_file_t *files; | ||||||
|  |     bool deorphaned; | ||||||
|  |  | ||||||
|     lfs_cache_t rcache; |     lfs_cache_t rcache; | ||||||
|     lfs_cache_t pcache; |     lfs_cache_t pcache; | ||||||
| @@ -257,8 +258,8 @@ 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); | ||||||
|  |  | ||||||
| // miscellaneous lfs specific operations | // miscellaneous lfs specific operations | ||||||
| int lfs_deorphan(lfs_t *lfs); |  | ||||||
| int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); | int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); | ||||||
|  | int lfs_deorphan(lfs_t *lfs); | ||||||
|  |  | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -13,11 +13,17 @@ void test_log(const char *s, uintmax_t v) {{ | |||||||
|  |  | ||||||
| void test_assert(const char *file, unsigned line, | void test_assert(const char *file, unsigned line, | ||||||
|         const char *s, uintmax_t v, uintmax_t e) {{ |         const char *s, uintmax_t v, uintmax_t e) {{ | ||||||
|     static const char *last[2] = {{0, 0}}; |     static const char *last[6] = {{0, 0}}; | ||||||
|     if (v != e || !(last[0] == s || last[1] == s)) {{ |     if (v != e || !(last[0] == s || last[1] == s || | ||||||
|  |             last[2] == s || last[3] == s || | ||||||
|  |             last[4] == s || last[5] == s)) {{ | ||||||
|         test_log(s, v); |         test_log(s, v); | ||||||
|         last[0] = last[1]; |         last[0] = last[1]; | ||||||
|         last[1] = s; |         last[1] = last[2]; | ||||||
|  |         last[2] = last[3]; | ||||||
|  |         last[3] = last[4]; | ||||||
|  |         last[4] = last[5]; | ||||||
|  |         last[5] = s; | ||||||
|     }} |     }} | ||||||
|  |  | ||||||
|     if (v != e) {{ |     if (v != e) {{ | ||||||
|   | |||||||
							
								
								
									
										106
									
								
								tests/test_corrupt.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										106
									
								
								tests/test_corrupt.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,106 @@ | |||||||
|  | #!/bin/bash | ||||||
|  | set -eu | ||||||
|  |  | ||||||
|  | echo "=== Corrupt tests ===" | ||||||
|  |  | ||||||
|  | NAMEMULT=64 | ||||||
|  | FILEMULT=1 | ||||||
|  |  | ||||||
|  | lfs_mktree() { | ||||||
|  | tests/test.py ${1:-} << TEST | ||||||
|  |     lfs_format(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     for (int i = 1; i < 10; i++) { | ||||||
|  |         for (int j = 0; j < $NAMEMULT; j++) { | ||||||
|  |             buffer[j] = '0'+i; | ||||||
|  |         } | ||||||
|  |         buffer[$NAMEMULT] = '\0'; | ||||||
|  |         lfs_mkdir(&lfs, (char*)buffer) => 0; | ||||||
|  |  | ||||||
|  |         buffer[$NAMEMULT] = '/'; | ||||||
|  |         for (int j = 0; j < $NAMEMULT; j++) { | ||||||
|  |             buffer[j+$NAMEMULT+1] = '0'+i; | ||||||
|  |         } | ||||||
|  |         buffer[2*$NAMEMULT+1] = '\0'; | ||||||
|  |         lfs_file_open(&lfs, &file[0], (char*)buffer, | ||||||
|  |                 LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||||
|  |          | ||||||
|  |         size = $NAMEMULT; | ||||||
|  |         for (int j = 0; j < i*$FILEMULT; j++) { | ||||||
|  |             lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     } | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  | } | ||||||
|  |  | ||||||
|  | lfs_chktree() { | ||||||
|  | tests/test.py ${1:-} << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     for (int i = 1; i < 10; i++) { | ||||||
|  |         for (int j = 0; j < $NAMEMULT; j++) { | ||||||
|  |             buffer[j] = '0'+i; | ||||||
|  |         } | ||||||
|  |         buffer[$NAMEMULT] = '\0'; | ||||||
|  |         lfs_stat(&lfs, (char*)buffer, &info) => 0; | ||||||
|  |         info.type => LFS_TYPE_DIR; | ||||||
|  |  | ||||||
|  |         buffer[$NAMEMULT] = '/'; | ||||||
|  |         for (int j = 0; j < $NAMEMULT; j++) { | ||||||
|  |             buffer[j+$NAMEMULT+1] = '0'+i; | ||||||
|  |         } | ||||||
|  |         buffer[2*$NAMEMULT+1] = '\0'; | ||||||
|  |         lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0; | ||||||
|  |          | ||||||
|  |         size = $NAMEMULT; | ||||||
|  |         for (int j = 0; j < i*$FILEMULT; j++) { | ||||||
|  |             lfs_file_read(&lfs, &file[0], rbuffer, size) => size; | ||||||
|  |             memcmp(buffer, rbuffer, size) => 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     } | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  | } | ||||||
|  |  | ||||||
|  | echo "--- Sanity check ---" | ||||||
|  | rm -rf blocks | ||||||
|  | lfs_mktree | ||||||
|  | lfs_chktree | ||||||
|  |  | ||||||
|  | echo "--- Block corruption ---" | ||||||
|  | for i in {0..33} | ||||||
|  | do  | ||||||
|  |     rm -rf blocks | ||||||
|  |     mkdir blocks | ||||||
|  |     ln -s /dev/zero blocks/$(printf '%x' $i) | ||||||
|  |     lfs_mktree | ||||||
|  |     lfs_chktree | ||||||
|  | done | ||||||
|  |  | ||||||
|  | echo "--- Big region corruption ---" | ||||||
|  | rm -rf blocks | ||||||
|  | mkdir blocks | ||||||
|  | for i in {2..255} | ||||||
|  | do | ||||||
|  |     ln -s /dev/zero blocks/$(printf '%x' $i) | ||||||
|  | done | ||||||
|  | lfs_mktree | ||||||
|  | lfs_chktree | ||||||
|  |  | ||||||
|  | echo "--- Alternating corruption ---" | ||||||
|  | rm -rf blocks | ||||||
|  | mkdir blocks | ||||||
|  | for i in {2..511..2} | ||||||
|  | do | ||||||
|  |     ln -s /dev/zero blocks/$(printf '%x' $i) | ||||||
|  | done | ||||||
|  | lfs_mktree | ||||||
|  | lfs_chktree | ||||||
|  |  | ||||||
|  | echo "--- Results ---" | ||||||
|  | tests/stats.py | ||||||
| @@ -124,6 +124,7 @@ tests/test.py << TEST | |||||||
| TEST | TEST | ||||||
|  |  | ||||||
| echo "--- Directory remove ---" | echo "--- Directory remove ---" | ||||||
|  | # TESTING HERE | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_remove(&lfs, "potato") => LFS_ERR_INVAL; |     lfs_remove(&lfs, "potato") => LFS_ERR_INVAL; | ||||||
|   | |||||||
| @@ -10,8 +10,8 @@ tests/test.py << TEST | |||||||
| TEST | TEST | ||||||
|  |  | ||||||
| echo "--- Invalid superblocks ---" | echo "--- Invalid superblocks ---" | ||||||
| ln -f -s /dev/null blocks/0 | ln -f -s /dev/zero blocks/0 | ||||||
| ln -f -s /dev/null blocks/1 | ln -f -s /dev/zero blocks/1 | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_format(&lfs, &cfg) => LFS_ERR_CORRUPT; |     lfs_format(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||||
| TEST | TEST | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user