mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 16:14:16 +01:00 
			
		
		
		
	WIP Fixed ENOSPC issues with zero-granularity blocks
This commit is contained in:
		
							
								
								
									
										139
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										139
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -886,6 +886,11 @@ split: | |||||||
|         // drop caches and create tail |         // drop caches and create tail | ||||||
|         lfs->pcache.block = 0xffffffff; |         lfs->pcache.block = 0xffffffff; | ||||||
|  |  | ||||||
|  |         if (ack == -1) { | ||||||
|  |             // If we can't fit in this block, we won't fit in next block | ||||||
|  |             return LFS_ERR_NOSPC; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         lfs_mdir_t tail; |         lfs_mdir_t tail; | ||||||
|         int err = lfs_dir_alloc(lfs, &tail, dir->split, dir->tail); |         int err = lfs_dir_alloc(lfs, &tail, dir->split, dir->tail); | ||||||
|         if (err) { |         if (err) { | ||||||
| @@ -1971,49 +1976,53 @@ int lfs_file_close(lfs_t *lfs, lfs_file_t *file) { | |||||||
| } | } | ||||||
|  |  | ||||||
| static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) { | static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) { | ||||||
| relocate:; |     while (true) { | ||||||
|     // just relocate what exists into new block |         // just relocate what exists into new block | ||||||
|     lfs_block_t nblock; |         lfs_block_t nblock; | ||||||
|     int err = lfs_alloc(lfs, &nblock); |         int err = lfs_alloc(lfs, &nblock); | ||||||
|     if (err) { |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     err = lfs_bd_erase(lfs, nblock); |  | ||||||
|     if (err) { |  | ||||||
|         if (err == LFS_ERR_CORRUPT) { |  | ||||||
|             goto relocate; |  | ||||||
|         } |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // either read from dirty cache or disk |  | ||||||
|     for (lfs_off_t i = 0; i < file->off; i++) { |  | ||||||
|         uint8_t data; |  | ||||||
|         err = lfs_cache_read(lfs, &lfs->rcache, &file->cache, |  | ||||||
|                 file->block, i, &data, 1); |  | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         err = lfs_cache_prog(lfs, &lfs->pcache, &lfs->rcache, |         err = lfs_bd_erase(lfs, nblock); | ||||||
|                 nblock, i, &data, 1); |  | ||||||
|         if (err) { |         if (err) { | ||||||
|             if (err == LFS_ERR_CORRUPT) { |             if (err == LFS_ERR_CORRUPT) { | ||||||
|                 goto relocate; |                 goto relocate; | ||||||
|             } |             } | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // either read from dirty cache or disk | ||||||
|  |         for (lfs_off_t i = 0; i < file->off; i++) { | ||||||
|  |             uint8_t data; | ||||||
|  |             err = lfs_cache_read(lfs, &lfs->rcache, &file->cache, | ||||||
|  |                     file->block, i, &data, 1); | ||||||
|  |             if (err) { | ||||||
|  |                 return err; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             err = lfs_cache_prog(lfs, &lfs->pcache, &lfs->rcache, | ||||||
|  |                     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; | ||||||
|  |         return 0; | ||||||
|  |  | ||||||
|  | relocate: | ||||||
|  |         continue; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // 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; |  | ||||||
|     return 0; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { | static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { | ||||||
| @@ -2067,6 +2076,7 @@ static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
| relocate: | relocate: | ||||||
|                 LFS_DEBUG("Bad block at %d", file->block); |                 LFS_DEBUG("Bad block at %d", file->block); | ||||||
|                 err = lfs_file_relocate(lfs, file); |                 err = lfs_file_relocate(lfs, file); | ||||||
| @@ -2091,48 +2101,58 @@ relocate: | |||||||
| } | } | ||||||
|  |  | ||||||
| int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { | int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { | ||||||
|     int err = lfs_file_flush(lfs, file); |     while (true) { | ||||||
|     if (err) { |         int err = lfs_file_flush(lfs, file); | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if ((file->flags & LFS_F_DIRTY) && |  | ||||||
|             !(file->flags & LFS_F_ERRED) && |  | ||||||
|             !lfs_pairisnull(file->pair)) { |  | ||||||
|         // update dir entry |  | ||||||
|         // TODO keep list of dirs including these guys for no |  | ||||||
|         // need of another reload? |  | ||||||
|         lfs_mdir_t cwd; |  | ||||||
|         err = lfs_dir_fetch(lfs, &cwd, file->pair); |  | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // either update the references or inline the whole file |         if ((file->flags & LFS_F_DIRTY) && | ||||||
|         if (!(file->flags & LFS_F_INLINE)) { |                 !(file->flags & LFS_F_ERRED) && | ||||||
|             int err = lfs_dir_commit(lfs, &cwd, |                 !lfs_pairisnull(file->pair)) { | ||||||
|                     LFS_MKATTR(LFS_TYPE_CTZSTRUCT, file->id, |             // update dir entry | ||||||
|                         &file->ctz.head, sizeof(file->ctz), |             // TODO keep list of dirs including these guys for no | ||||||
|                     LFS_MKATTR(LFS_FROM_ATTRS, file->id, file->cfg->attrs, 0, |             // need of another reload? | ||||||
|                     NULL))); |             lfs_mdir_t cwd; | ||||||
|  |             err = lfs_dir_fetch(lfs, &cwd, file->pair); | ||||||
|             if (err) { |             if (err) { | ||||||
|                 return err; |                 return err; | ||||||
|             } |             } | ||||||
|         } else { |  | ||||||
|  |             // either update the references or inline the whole file | ||||||
|             int err = lfs_dir_commit(lfs, &cwd, |             int err = lfs_dir_commit(lfs, &cwd, | ||||||
|                     LFS_MKATTR(LFS_TYPE_INLINESTRUCT, file->id, |  | ||||||
|                             file->cache.buffer, file->ctz.size, |  | ||||||
|                     LFS_MKATTR(LFS_FROM_ATTRS, file->id, file->cfg->attrs, 0, |                     LFS_MKATTR(LFS_FROM_ATTRS, file->id, file->cfg->attrs, 0, | ||||||
|                     NULL))); |                     (file->flags & LFS_F_INLINE) ? | ||||||
|  |                         LFS_MKATTR(LFS_TYPE_INLINESTRUCT, file->id, | ||||||
|  |                             file->cache.buffer, file->ctz.size, NULL) : | ||||||
|  |                         LFS_MKATTR(LFS_TYPE_CTZSTRUCT, file->id, | ||||||
|  |                             &file->ctz.head, sizeof(file->ctz), NULL))); | ||||||
|             if (err) { |             if (err) { | ||||||
|  |                 if (err == LFS_ERR_NOSPC && (file->flags & LFS_F_INLINE)) { | ||||||
|  |                     goto relocate; | ||||||
|  |                 } | ||||||
|                 return err; |                 return err; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             file->flags &= ~LFS_F_DIRTY; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         file->flags &= ~LFS_F_DIRTY; |         return 0; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; | relocate: | ||||||
|  |         // inline file doesn't fit anymore | ||||||
|  |         file->block = 0xfffffffe; | ||||||
|  |         file->off = file->pos; | ||||||
|  |  | ||||||
|  |         lfs_alloc_ack(lfs); | ||||||
|  |         err = lfs_file_relocate(lfs, file); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         file->flags &= ~LFS_F_INLINE; | ||||||
|  |         file->flags |= LFS_F_WRITING; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, | lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, | ||||||
| @@ -3304,6 +3324,7 @@ static int32_t lfs_parent(lfs_t *lfs, const lfs_block_t pair[2], | |||||||
| // TODO rename to lfs_dir_relocate? | // TODO rename to lfs_dir_relocate? | ||||||
| static int lfs_relocate(lfs_t *lfs, | static int lfs_relocate(lfs_t *lfs, | ||||||
|         const lfs_block_t oldpair[2], const lfs_block_t newpair[2]) { |         const lfs_block_t oldpair[2], const lfs_block_t newpair[2]) { | ||||||
|  |     // TODO name lfs_dir_relocate? | ||||||
|     // find parent |     // find parent | ||||||
|     lfs_mdir_t parent; |     lfs_mdir_t parent; | ||||||
|     int32_t tag = lfs_parent(lfs, oldpair, &parent); |     int32_t tag = lfs_parent(lfs, oldpair, &parent); | ||||||
|   | |||||||
							
								
								
									
										39
									
								
								tests/corrupt.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										39
									
								
								tests/corrupt.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | #!/usr/bin/env python | ||||||
|  |  | ||||||
|  | import struct | ||||||
|  | import sys | ||||||
|  | import os | ||||||
|  |  | ||||||
|  | def main(*paths): | ||||||
|  |     # find most recent block | ||||||
|  |     file = None | ||||||
|  |     rev = None | ||||||
|  |     for path in paths: | ||||||
|  |         try: | ||||||
|  |             nfile = open(path, 'r+b') | ||||||
|  |             nrev, = struct.unpack('<I', nfile.read(4)) | ||||||
|  |  | ||||||
|  |             assert rev != nrev | ||||||
|  |             if not file or ((rev - nrev) & 0x80000000): | ||||||
|  |                 file = nfile | ||||||
|  |                 rev = nrev | ||||||
|  |         except IOError: | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |     # go to last commit | ||||||
|  |     tag = 0 | ||||||
|  |     while True: | ||||||
|  |         try: | ||||||
|  |             ntag, = struct.unpack('<I', file.read(4)) | ||||||
|  |         except struct.error: | ||||||
|  |             break | ||||||
|  |  | ||||||
|  |         tag ^= ntag | ||||||
|  |         file.seek(tag & 0xfff, os.SEEK_CUR) | ||||||
|  |  | ||||||
|  |     # lob off last 3 bytes | ||||||
|  |     file.seek(-((tag & 0xfff) + 3), os.SEEK_CUR) | ||||||
|  |     file.truncate() | ||||||
|  |  | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     main(*sys.argv[1:]) | ||||||
| @@ -59,7 +59,7 @@ tests/test.py << TEST | |||||||
|     lfs_rename(&lfs, "b/hello", "c/hello") => 0; |     lfs_rename(&lfs, "b/hello", "c/hello") => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
| truncate -s-7 blocks/6 | tests/corrupt.py blocks/{6,7} | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_dir_open(&lfs, &dir[0], "b") => 0; |     lfs_dir_open(&lfs, &dir[0], "b") => 0; | ||||||
| @@ -86,8 +86,8 @@ tests/test.py << TEST | |||||||
|     lfs_rename(&lfs, "c/hello", "d/hello") => 0; |     lfs_rename(&lfs, "c/hello", "d/hello") => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
| truncate -s-7 blocks/8 | tests/corrupt.py blocks/{8,9} | ||||||
| truncate -s-7 blocks/a | tests/corrupt.py blocks/{a,b} | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_dir_open(&lfs, &dir[0], "c") => 0; |     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||||
| @@ -166,7 +166,7 @@ tests/test.py << TEST | |||||||
|     lfs_rename(&lfs, "b/hi", "c/hi") => 0; |     lfs_rename(&lfs, "b/hi", "c/hi") => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
| truncate -s-7 blocks/7 | tests/corrupt.py blocks/{6,7} | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_dir_open(&lfs, &dir[0], "b") => 0; |     lfs_dir_open(&lfs, &dir[0], "b") => 0; | ||||||
| @@ -193,8 +193,8 @@ tests/test.py << TEST | |||||||
|     lfs_rename(&lfs, "c/hi", "d/hi") => 0; |     lfs_rename(&lfs, "c/hi", "d/hi") => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
| truncate -s-7 blocks/9 | tests/corrupt.py blocks/{8,9} | ||||||
| truncate -s-7 blocks/b | tests/corrupt.py blocks/{a,b} | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_dir_open(&lfs, &dir[0], "c") => 0; |     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ tests/test.py << TEST | |||||||
| TEST | TEST | ||||||
| # corrupt most recent commit, this should be the update to the previous | # corrupt most recent commit, this should be the update to the previous | ||||||
| # linked-list entry and should orphan the child | # linked-list entry and should orphan the child | ||||||
| truncate -s-14 blocks/8 | tests/corrupt.py blocks/{8,9} | ||||||
| tests/test.py  << TEST | tests/test.py  << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user