mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	Merge pull request #401 from thrasher8390/bugfix/thrasher8390/issue-394-lookahead-buffer-corruption
Lookahead corruption fix given an IO Error during traversal
This commit is contained in:
		
							
								
								
									
										24
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -437,6 +437,19 @@ static int lfs_alloc_lookahead(void *p, lfs_block_t block) { | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void lfs_alloc_ack(lfs_t *lfs) { | ||||||
|  |     lfs->free.ack = lfs->cfg->block_count; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Invalidate the lookahead buffer. This is done during mounting and | ||||||
|  | // failed traversals | ||||||
|  | static void lfs_alloc_reset(lfs_t *lfs) { | ||||||
|  |     lfs->free.off = lfs->seed % lfs->cfg->block_size; | ||||||
|  |     lfs->free.size = 0; | ||||||
|  |     lfs->free.i = 0; | ||||||
|  |     lfs_alloc_ack(lfs); | ||||||
|  | } | ||||||
|  |  | ||||||
| static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { | static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { | ||||||
|     while (true) { |     while (true) { | ||||||
|         while (lfs->free.i != lfs->free.size) { |         while (lfs->free.i != lfs->free.size) { | ||||||
| @@ -477,16 +490,12 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { | |||||||
|         memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size); |         memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size); | ||||||
|         int err = lfs_fs_traverseraw(lfs, lfs_alloc_lookahead, lfs, true); |         int err = lfs_fs_traverseraw(lfs, lfs_alloc_lookahead, lfs, true); | ||||||
|         if (err) { |         if (err) { | ||||||
|  |             lfs_alloc_reset(lfs); | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void lfs_alloc_ack(lfs_t *lfs) { |  | ||||||
|     lfs->free.ack = lfs->cfg->block_count; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /// Metadata pair and directory operations /// | /// Metadata pair and directory operations /// | ||||||
| static lfs_stag_t lfs_dir_getslice(lfs_t *lfs, const lfs_mdir_t *dir, | static lfs_stag_t lfs_dir_getslice(lfs_t *lfs, const lfs_mdir_t *dir, | ||||||
|         lfs_tag_t gmask, lfs_tag_t gtag, |         lfs_tag_t gmask, lfs_tag_t gtag, | ||||||
| @@ -3772,10 +3781,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|     lfs->gdisk = lfs->gstate; |     lfs->gdisk = lfs->gstate; | ||||||
|  |  | ||||||
|     // setup free lookahead |     // setup free lookahead | ||||||
|     lfs->free.off = lfs->seed % lfs->cfg->block_size; |     lfs_alloc_reset(lfs); | ||||||
|     lfs->free.size = 0; |  | ||||||
|     lfs->free.i = 0; |  | ||||||
|     lfs_alloc_ack(lfs); |  | ||||||
|  |  | ||||||
|     LFS_TRACE("lfs_mount -> %d", 0); |     LFS_TRACE("lfs_mount -> %d", 0); | ||||||
|     return 0; |     return 0; | ||||||
|   | |||||||
| @@ -323,6 +323,90 @@ code = ''' | |||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| ''' | ''' | ||||||
|  |  | ||||||
|  | [[case]] # what if we have a bad block during an allocation scan? | ||||||
|  | in = "lfs.c" | ||||||
|  | define.LFS_ERASE_CYCLES = 0xffffffff | ||||||
|  | define.LFS_BADBLOCK_BEHAVIOR = 'LFS_TESTBD_BADBLOCK_READERROR' | ||||||
|  | code = ''' | ||||||
|  |     lfs_format(&lfs, &cfg) => 0; | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     // first fill to exhaustion to find available space | ||||||
|  |     lfs_file_open(&lfs, &file, "pacman", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||||
|  |     strcpy((char*)buffer, "waka"); | ||||||
|  |     size = strlen("waka"); | ||||||
|  |     lfs_size_t filesize = 0; | ||||||
|  |     while (true) { | ||||||
|  |         lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size); | ||||||
|  |         assert(res == (lfs_ssize_t)size || res == LFS_ERR_NOSPC); | ||||||
|  |         if (res == LFS_ERR_NOSPC) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         filesize += size; | ||||||
|  |     } | ||||||
|  |     lfs_file_close(&lfs, &file) => 0; | ||||||
|  |     // now fill all but a couple of blocks of the filesystem with data | ||||||
|  |     filesize -= 3*LFS_BLOCK_SIZE; | ||||||
|  |     lfs_file_open(&lfs, &file, "pacman", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||||
|  |     strcpy((char*)buffer, "waka"); | ||||||
|  |     size = strlen("waka"); | ||||||
|  |     for (lfs_size_t i = 0; i < filesize/size; i++) { | ||||||
|  |         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||||
|  |     } | ||||||
|  |     lfs_file_close(&lfs, &file) => 0; | ||||||
|  |     // also save head of file so we can error during lookahead scan | ||||||
|  |     lfs_block_t fileblock = file.ctz.head; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  |  | ||||||
|  |     // remount to force an alloc scan | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     // but mark the head of our file as a "bad block", this is force our | ||||||
|  |     // scan to bail early | ||||||
|  |     lfs_testbd_setwear(&cfg, fileblock, 0xffffffff) => 0; | ||||||
|  |     lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||||
|  |     strcpy((char*)buffer, "chomp"); | ||||||
|  |     size = strlen("chomp"); | ||||||
|  |     while (true) { | ||||||
|  |         lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size); | ||||||
|  |         assert(res == (lfs_ssize_t)size || res == LFS_ERR_CORRUPT); | ||||||
|  |         if (res == LFS_ERR_CORRUPT) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs_file_close(&lfs, &file) => 0; | ||||||
|  |  | ||||||
|  |     // now reverse the "bad block" and try to write the file again until we | ||||||
|  |     // run out of space | ||||||
|  |     lfs_testbd_setwear(&cfg, fileblock, 0) => 0; | ||||||
|  |     lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||||
|  |     strcpy((char*)buffer, "chomp"); | ||||||
|  |     size = strlen("chomp"); | ||||||
|  |     while (true) { | ||||||
|  |         lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size); | ||||||
|  |         assert(res == (lfs_ssize_t)size || res == LFS_ERR_NOSPC); | ||||||
|  |         if (res == LFS_ERR_NOSPC) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs_file_close(&lfs, &file) => 0; | ||||||
|  |  | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  |  | ||||||
|  |     // check that the disk isn't hurt | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_file_open(&lfs, &file, "pacman", LFS_O_RDONLY) => 0; | ||||||
|  |     strcpy((char*)buffer, "waka"); | ||||||
|  |     size = strlen("waka"); | ||||||
|  |     for (lfs_size_t i = 0; i < filesize/size; i++) { | ||||||
|  |         uint8_t rbuffer[4]; | ||||||
|  |         lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||||
|  |         assert(memcmp(rbuffer, buffer, size) == 0); | ||||||
|  |     } | ||||||
|  |     lfs_file_close(&lfs, &file) => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  |  | ||||||
| # Below, I don't like these tests. They're fragile and depend _heavily_ | # Below, I don't like these tests. They're fragile and depend _heavily_ | ||||||
| # on the geometry of the block device. But they are valuable. Eventually they | # on the geometry of the block device. But they are valuable. Eventually they | ||||||
| # should be removed and replaced with generalized tests. | # should be removed and replaced with generalized tests. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user