mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	This is a style change to make littlefs's callbacks consistent with most callback declarations found in C. That is, taking in a user-provided `void*`. Previously, these callbacks took a pointer to the config struct itself, which indirectly contained a user provided context, and this gets the job done, but taking in a callback with a `void*` is arguably more expected, has a better chance of integrating with C++/OS-specific code, and is more likely to be optimized out by a clever compiler. --- As a part of these changes, the geometry for the test bds needed to be moved into bd specific configuration objects. This is a good change as it also allows for testing situations where littlefs's geometry does not match the underlying bd.
		
			
				
	
	
		
			297 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			TOML
		
	
	
	
	
	
			
		
		
	
	
			297 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			TOML
		
	
	
	
	
	
| # Tests for recovering from conditions which shouldn't normally
 | |
| # happen during normal operation of littlefs
 | |
| 
 | |
| # invalid pointer tests (outside of block_count)
 | |
| 
 | |
| [[case]] # invalid tail-pointer test
 | |
| define.TAIL_TYPE = ['LFS_TYPE_HARDTAIL', 'LFS_TYPE_SOFTTAIL']
 | |
| define.INVALSET = [0x3, 0x1, 0x2]
 | |
| in = "lfs.c"
 | |
| code = '''
 | |
|     // create littlefs
 | |
|     lfs_formatcfg(&lfs, &cfg) => 0;
 | |
| 
 | |
|     // change tail-pointer to invalid pointers
 | |
|     lfs.cfg = &cfg;
 | |
|     lfs_initcommon(&lfs) => 0;
 | |
|     lfs_mdir_t mdir;
 | |
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
 | |
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
 | |
|             {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8),
 | |
|                 (lfs_block_t[2]){
 | |
|                     (INVALSET & 0x1) ? 0xcccccccc : 0,
 | |
|                     (INVALSET & 0x2) ? 0xcccccccc : 0}})) => 0;
 | |
|     lfs_deinit(&lfs) => 0;
 | |
| 
 | |
|     // test that mount fails gracefully
 | |
|     lfs_mountcfg(&lfs, &cfg) => LFS_ERR_CORRUPT;
 | |
| '''
 | |
| 
 | |
| [[case]] # invalid dir pointer test
 | |
| define.INVALSET = [0x3, 0x1, 0x2]
 | |
| in = "lfs.c"
 | |
| code = '''
 | |
|     // create littlefs
 | |
|     lfs_formatcfg(&lfs, &cfg) => 0;
 | |
|     // make a dir
 | |
|     lfs_mountcfg(&lfs, &cfg) => 0;
 | |
|     lfs_mkdir(&lfs, "dir_here") => 0;
 | |
|     lfs_unmount(&lfs) => 0;
 | |
| 
 | |
|     // change the dir pointer to be invalid
 | |
|     lfs.cfg = &cfg;
 | |
|     lfs_initcommon(&lfs) => 0;
 | |
|     lfs_mdir_t mdir;
 | |
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
 | |
|     // make sure id 1 == our directory
 | |
|     lfs_dir_get(&lfs, &mdir,
 | |
|             LFS_MKTAG(0x700, 0x3ff, 0),
 | |
|             LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("dir_here")), buffer)
 | |
|                 => LFS_MKTAG(LFS_TYPE_DIR, 1, strlen("dir_here"));
 | |
|     assert(memcmp((char*)buffer, "dir_here", strlen("dir_here")) == 0);
 | |
|     // change dir pointer
 | |
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
 | |
|             {LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, 8),
 | |
|                 (lfs_block_t[2]){
 | |
|                     (INVALSET & 0x1) ? 0xcccccccc : 0,
 | |
|                     (INVALSET & 0x2) ? 0xcccccccc : 0}})) => 0;
 | |
|     lfs_deinit(&lfs) => 0;
 | |
| 
 | |
|     // test that accessing our bad dir fails, note there's a number
 | |
|     // of ways to access the dir, some can fail, but some don't
 | |
|     lfs_mountcfg(&lfs, &cfg) => 0;
 | |
|     lfs_stat(&lfs, "dir_here", &info) => 0;
 | |
|     assert(strcmp(info.name, "dir_here") == 0);
 | |
|     assert(info.type == LFS_TYPE_DIR);
 | |
| 
 | |
|     lfs_dir_open(&lfs, &dir, "dir_here") => LFS_ERR_CORRUPT;
 | |
|     lfs_stat(&lfs, "dir_here/file_here", &info) => LFS_ERR_CORRUPT;
 | |
|     lfs_dir_open(&lfs, &dir, "dir_here/dir_here") => LFS_ERR_CORRUPT;
 | |
|     lfs_file_open(&lfs, &file, "dir_here/file_here",
 | |
|             LFS_O_RDONLY) => LFS_ERR_CORRUPT;
 | |
|     lfs_file_open(&lfs, &file, "dir_here/file_here",
 | |
|             LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_CORRUPT;
 | |
|     lfs_unmount(&lfs) => 0;
 | |
| '''
 | |
| 
 | |
| [[case]] # invalid file pointer test
 | |
| in = "lfs.c"
 | |
| define.SIZE = [10, 1000, 100000] # faked file size
 | |
| code = '''
 | |
|     // create littlefs
 | |
|     lfs_formatcfg(&lfs, &cfg) => 0;
 | |
|     // make a file
 | |
|     lfs_mountcfg(&lfs, &cfg) => 0;
 | |
|     lfs_file_open(&lfs, &file, "file_here",
 | |
|             LFS_O_WRONLY | LFS_O_CREAT) => 0;
 | |
|     lfs_file_close(&lfs, &file) => 0;
 | |
|     lfs_unmount(&lfs) => 0;
 | |
| 
 | |
|     // change the file pointer to be invalid
 | |
|     lfs.cfg = &cfg;
 | |
|     lfs_initcommon(&lfs) => 0;
 | |
|     lfs_mdir_t mdir;
 | |
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
 | |
|     // make sure id 1 == our file
 | |
|     lfs_dir_get(&lfs, &mdir,
 | |
|             LFS_MKTAG(0x700, 0x3ff, 0),
 | |
|             LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("file_here")), buffer)
 | |
|                 => LFS_MKTAG(LFS_TYPE_REG, 1, strlen("file_here"));
 | |
|     assert(memcmp((char*)buffer, "file_here", strlen("file_here")) == 0);
 | |
|     // change file pointer
 | |
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
 | |
|             {LFS_MKTAG(LFS_TYPE_CTZSTRUCT, 1, sizeof(struct lfs_ctz)),
 | |
|                 &(struct lfs_ctz){0xcccccccc, lfs_tole32(SIZE)}})) => 0;
 | |
|     lfs_deinit(&lfs) => 0;
 | |
| 
 | |
|     // test that accessing our bad file fails, note there's a number
 | |
|     // of ways to access the dir, some can fail, but some don't
 | |
|     lfs_mountcfg(&lfs, &cfg) => 0;
 | |
|     lfs_stat(&lfs, "file_here", &info) => 0;
 | |
|     assert(strcmp(info.name, "file_here") == 0);
 | |
|     assert(info.type == LFS_TYPE_REG);
 | |
|     assert(info.size == SIZE);
 | |
| 
 | |
|     lfs_file_open(&lfs, &file, "file_here", LFS_O_RDONLY) => 0;
 | |
|     lfs_file_read(&lfs, &file, buffer, SIZE) => LFS_ERR_CORRUPT;
 | |
|     lfs_file_close(&lfs, &file) => 0;
 | |
| 
 | |
|     // any allocs that traverse CTZ must unfortunately must fail
 | |
|     if (SIZE > 2*LFS_BLOCK_SIZE) {
 | |
|         lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT;
 | |
|     }
 | |
|     lfs_unmount(&lfs) => 0;
 | |
| '''
 | |
| 
 | |
| [[case]] # invalid pointer in CTZ skip-list test
 | |
| define.SIZE = ['2*LFS_BLOCK_SIZE', '3*LFS_BLOCK_SIZE', '4*LFS_BLOCK_SIZE']
 | |
| in = "lfs.c"
 | |
| code = '''
 | |
|     // create littlefs
 | |
|     lfs_formatcfg(&lfs, &cfg) => 0;
 | |
|     // make a file
 | |
|     lfs_mountcfg(&lfs, &cfg) => 0;
 | |
|     lfs_file_open(&lfs, &file, "file_here",
 | |
|             LFS_O_WRONLY | LFS_O_CREAT) => 0;
 | |
|     for (int i = 0; i < SIZE; i++) {
 | |
|         char c = 'c';
 | |
|         lfs_file_write(&lfs, &file, &c, 1) => 1;
 | |
|     }
 | |
|     lfs_file_close(&lfs, &file) => 0;
 | |
|     lfs_unmount(&lfs) => 0;
 | |
|     // change pointer in CTZ skip-list to be invalid
 | |
|     lfs.cfg = &cfg;
 | |
|     lfs_initcommon(&lfs) => 0;
 | |
|     lfs_mdir_t mdir;
 | |
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
 | |
|     // make sure id 1 == our file and get our CTZ structure
 | |
|     lfs_dir_get(&lfs, &mdir,
 | |
|             LFS_MKTAG(0x700, 0x3ff, 0),
 | |
|             LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("file_here")), buffer)
 | |
|                 => LFS_MKTAG(LFS_TYPE_REG, 1, strlen("file_here"));
 | |
|     assert(memcmp((char*)buffer, "file_here", strlen("file_here")) == 0);
 | |
|     struct lfs_ctz ctz;
 | |
|     lfs_dir_get(&lfs, &mdir,
 | |
|             LFS_MKTAG(0x700, 0x3ff, 0),
 | |
|             LFS_MKTAG(LFS_TYPE_STRUCT, 1, sizeof(struct lfs_ctz)), &ctz)
 | |
|                 => LFS_MKTAG(LFS_TYPE_CTZSTRUCT, 1, sizeof(struct lfs_ctz));
 | |
|     lfs_ctz_fromle32(&ctz);
 | |
|     // rewrite block to contain bad pointer
 | |
|     uint8_t bbuffer[LFS_BLOCK_SIZE];
 | |
|     lfs_testbd_read(&bd, ctz.head, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
 | |
|     uint32_t bad = lfs_tole32(0xcccccccc);
 | |
|     memcpy(&bbuffer[0], &bad, sizeof(bad));
 | |
|     memcpy(&bbuffer[4], &bad, sizeof(bad));
 | |
|     lfs_testbd_erase(&bd, ctz.head) => 0;
 | |
|     lfs_testbd_prog(&bd, ctz.head, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
 | |
|     lfs_deinit(&lfs) => 0;
 | |
| 
 | |
|     // test that accessing our bad file fails, note there's a number
 | |
|     // of ways to access the dir, some can fail, but some don't
 | |
|     lfs_mountcfg(&lfs, &cfg) => 0;
 | |
|     lfs_stat(&lfs, "file_here", &info) => 0;
 | |
|     assert(strcmp(info.name, "file_here") == 0);
 | |
|     assert(info.type == LFS_TYPE_REG);
 | |
|     assert(info.size == SIZE);
 | |
| 
 | |
|     lfs_file_open(&lfs, &file, "file_here", LFS_O_RDONLY) => 0;
 | |
|     lfs_file_read(&lfs, &file, buffer, SIZE) => LFS_ERR_CORRUPT;
 | |
|     lfs_file_close(&lfs, &file) => 0;
 | |
| 
 | |
|     // any allocs that traverse CTZ must unfortunately must fail
 | |
|     if (SIZE > 2*LFS_BLOCK_SIZE) {
 | |
|         lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT;
 | |
|     }
 | |
|     lfs_unmount(&lfs) => 0;
 | |
| '''
 | |
| 
 | |
| 
 | |
| [[case]] # invalid gstate pointer
 | |
| define.INVALSET = [0x3, 0x1, 0x2]
 | |
| in = "lfs.c"
 | |
| code = '''
 | |
|     // create littlefs
 | |
|     lfs_formatcfg(&lfs, &cfg) => 0;
 | |
| 
 | |
|     // create an invalid gstate
 | |
|     lfs.cfg = &cfg;
 | |
|     lfs_initcommon(&lfs) => 0;
 | |
|     lfs_mdir_t mdir;
 | |
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
 | |
|     lfs_fs_prepmove(&lfs, 1, (lfs_block_t [2]){
 | |
|             (INVALSET & 0x1) ? 0xcccccccc : 0,
 | |
|             (INVALSET & 0x2) ? 0xcccccccc : 0});
 | |
|     lfs_dir_commit(&lfs, &mdir, NULL, 0) => 0;
 | |
|     lfs_deinit(&lfs) => 0;
 | |
| 
 | |
|     // test that mount fails gracefully
 | |
|     // mount may not fail, but our first alloc should fail when
 | |
|     // we try to fix the gstate
 | |
|     lfs_mountcfg(&lfs, &cfg) => 0;
 | |
|     lfs_mkdir(&lfs, "should_fail") => LFS_ERR_CORRUPT;
 | |
|     lfs_unmount(&lfs) => 0;
 | |
| '''
 | |
| 
 | |
| # cycle detection/recovery tests
 | |
| 
 | |
| [[case]] # metadata-pair threaded-list loop test
 | |
| in = "lfs.c"
 | |
| code = '''
 | |
|     // create littlefs
 | |
|     lfs_formatcfg(&lfs, &cfg) => 0;
 | |
| 
 | |
|     // change tail-pointer to point to ourself
 | |
|     lfs.cfg = &cfg;
 | |
|     lfs_initcommon(&lfs) => 0;
 | |
|     lfs_mdir_t mdir;
 | |
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
 | |
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
 | |
|             {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8),
 | |
|                 (lfs_block_t[2]){0, 1}})) => 0;
 | |
|     lfs_deinit(&lfs) => 0;
 | |
| 
 | |
|     // test that mount fails gracefully
 | |
|     lfs_mountcfg(&lfs, &cfg) => LFS_ERR_CORRUPT;
 | |
| '''
 | |
| 
 | |
| [[case]] # metadata-pair threaded-list 2-length loop test
 | |
| in = "lfs.c"
 | |
| code = '''
 | |
|     // create littlefs with child dir
 | |
|     lfs_formatcfg(&lfs, &cfg) => 0;
 | |
|     lfs_mountcfg(&lfs, &cfg) => 0;
 | |
|     lfs_mkdir(&lfs, "child") => 0;
 | |
|     lfs_unmount(&lfs) => 0;
 | |
| 
 | |
|     // find child
 | |
|     lfs.cfg = &cfg;
 | |
|     lfs_initcommon(&lfs) => 0;
 | |
|     lfs_mdir_t mdir;
 | |
|     lfs_block_t pair[2];
 | |
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
 | |
|     lfs_dir_get(&lfs, &mdir,
 | |
|             LFS_MKTAG(0x7ff, 0x3ff, 0),
 | |
|             LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair)
 | |
|                 => LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair));
 | |
|     lfs_pair_fromle32(pair);
 | |
|     // change tail-pointer to point to root
 | |
|     lfs_dir_fetch(&lfs, &mdir, pair) => 0;
 | |
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
 | |
|             {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8),
 | |
|                 (lfs_block_t[2]){0, 1}})) => 0;
 | |
|     lfs_deinit(&lfs) => 0;
 | |
| 
 | |
|     // test that mount fails gracefully
 | |
|     lfs_mountcfg(&lfs, &cfg) => LFS_ERR_CORRUPT;
 | |
| '''
 | |
| 
 | |
| [[case]] # metadata-pair threaded-list 1-length child loop test
 | |
| in = "lfs.c"
 | |
| code = '''
 | |
|     // create littlefs with child dir
 | |
|     lfs_formatcfg(&lfs, &cfg) => 0;
 | |
|     lfs_mountcfg(&lfs, &cfg) => 0;
 | |
|     lfs_mkdir(&lfs, "child") => 0;
 | |
|     lfs_unmount(&lfs) => 0;
 | |
| 
 | |
|     // find child
 | |
|     lfs.cfg = &cfg;
 | |
|     lfs_initcommon(&lfs) => 0;
 | |
|     lfs_mdir_t mdir;
 | |
|     lfs_block_t pair[2];
 | |
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
 | |
|     lfs_dir_get(&lfs, &mdir,
 | |
|             LFS_MKTAG(0x7ff, 0x3ff, 0),
 | |
|             LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair)
 | |
|                 => LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair));
 | |
|     lfs_pair_fromle32(pair);
 | |
|     // change tail-pointer to point to ourself
 | |
|     lfs_dir_fetch(&lfs, &mdir, pair) => 0;
 | |
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
 | |
|             {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), pair})) => 0;
 | |
|     lfs_deinit(&lfs) => 0;
 | |
| 
 | |
|     // test that mount fails gracefully
 | |
|     lfs_mountcfg(&lfs, &cfg) => LFS_ERR_CORRUPT;
 | |
| '''
 |