mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-11-01 16:14:13 +01:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			v2.2.0
			...
			test-revam
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | b392c49e36 | ||
|  | 17efa7b3b9 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -8,5 +8,3 @@ blocks/ | ||||
| lfs | ||||
| test.c | ||||
| tests/*.toml.* | ||||
| scripts/__pycache__ | ||||
| .gdb_history | ||||
|   | ||||
| @@ -115,9 +115,6 @@ the filesystem until sync or close is called on the file. | ||||
|  | ||||
| ## Other notes | ||||
|  | ||||
| Littlefs is written in C, and specifically should compile with any compiler | ||||
| that conforms to the `C99` standard. | ||||
|  | ||||
| All littlefs calls have the potential to return a negative error code. The | ||||
| errors can be either one of those found in the `enum lfs_error` in | ||||
| [lfs.h](lfs.h), or an error returned by the user's block device operations. | ||||
|   | ||||
							
								
								
									
										10
									
								
								SPEC.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								SPEC.md
									
									
									
									
									
								
							| @@ -289,7 +289,7 @@ Layout of the name tag: | ||||
| ``` | ||||
|         tag                          data | ||||
| [--      32      --][---        variable length        ---] | ||||
| [1| 3| 8 | 10 | 10 ][---          (size * 8)           ---] | ||||
| [1| 3| 8 | 10 | 10 ][---            (size)             ---] | ||||
|  ^  ^  ^    ^    ^- size               ^- file name | ||||
|  |  |  |    '------ id | ||||
|  |  |  '----------- file type | ||||
| @@ -470,7 +470,7 @@ Layout of the inline-struct tag: | ||||
| ``` | ||||
|         tag                          data | ||||
| [--      32      --][---        variable length        ---] | ||||
| [1|- 11 -| 10 | 10 ][---           (size * 8)          ---] | ||||
| [1|- 11 -| 10 | 10 ][---            (size)             ---] | ||||
|  ^    ^     ^    ^- size               ^- inline data | ||||
|  |    |     '------ id | ||||
|  |    '------------ type (0x201) | ||||
| @@ -556,7 +556,7 @@ Layout of the user-attr tag: | ||||
| ``` | ||||
|         tag                          data | ||||
| [--      32      --][---        variable length        ---] | ||||
| [1| 3| 8 | 10 | 10 ][---           (size * 8)          ---] | ||||
| [1| 3| 8 | 10 | 10 ][---            (size)             ---] | ||||
|  ^  ^  ^    ^    ^- size               ^- attr data | ||||
|  |  |  |    '------ id | ||||
|  |  |  '----------- attr type | ||||
| @@ -764,9 +764,9 @@ Layout of the CRC tag: | ||||
| ``` | ||||
|         tag                                    data | ||||
| [--      32      --][--      32      --|---        variable length        ---] | ||||
| [1| 3| 8 | 10 | 10 ][--      32      --|---        (size * 8 - 32)        ---] | ||||
| [1| 3| 8 | 10 | 10 ][--      32      --|---            (size)             ---] | ||||
|  ^  ^  ^    ^    ^            ^- crc                      ^- padding | ||||
|  |  |  |    |    '- size | ||||
|  |  |  |    |    '- size (12) | ||||
|  |  |  |    '------ id (0x3ff) | ||||
|  |  |  '----------- valid state | ||||
|  |  '-------------- type1 (0x5) | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
|  | ||||
| int lfs_filebd_createcfg(const struct lfs_config *cfg, const char *path, | ||||
|         const struct lfs_filebd_config *bdcfg) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_createcfg(%p {.context=%p, " | ||||
|     LFS_TRACE("lfs_filebd_createcfg(%p {.context=%p, " | ||||
|                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||
|                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||
|                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " | ||||
| @@ -30,16 +30,16 @@ int lfs_filebd_createcfg(const struct lfs_config *cfg, const char *path, | ||||
|     bd->fd = open(path, O_RDWR | O_CREAT, 0666); | ||||
|     if (bd->fd < 0) { | ||||
|         int err = -errno; | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_createcfg -> %d", err); | ||||
|         LFS_TRACE("lfs_filebd_createcfg -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_createcfg -> %d", 0); | ||||
|     LFS_TRACE("lfs_filebd_createcfg -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_filebd_create(const struct lfs_config *cfg, const char *path) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_create(%p {.context=%p, " | ||||
|     LFS_TRACE("lfs_filebd_create(%p {.context=%p, " | ||||
|                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||
|                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||
|                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " | ||||
| @@ -51,27 +51,26 @@ int lfs_filebd_create(const struct lfs_config *cfg, const char *path) { | ||||
|             path); | ||||
|     static const struct lfs_filebd_config defaults = {.erase_value=-1}; | ||||
|     int err = lfs_filebd_createcfg(cfg, path, &defaults); | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_create -> %d", err); | ||||
|     LFS_TRACE("lfs_filebd_create -> %d", err); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int lfs_filebd_destroy(const struct lfs_config *cfg) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_destroy(%p)", (void*)cfg); | ||||
|     LFS_TRACE("lfs_filebd_destroy(%p)", (void*)cfg); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
|     int err = close(bd->fd); | ||||
|     if (err < 0) { | ||||
|         err = -errno; | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_destroy -> %d", err); | ||||
|         LFS_TRACE("lfs_filebd_destroy -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_destroy -> %d", 0); | ||||
|     LFS_TRACE("lfs_filebd_destroy -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_filebd_read(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         lfs_off_t off, void *buffer, lfs_size_t size) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_read(%p, " | ||||
|                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|     LFS_TRACE("lfs_filebd_read(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
|  | ||||
| @@ -90,24 +89,24 @@ int lfs_filebd_read(const struct lfs_config *cfg, lfs_block_t block, | ||||
|             (off_t)block*cfg->block_size + (off_t)off, SEEK_SET); | ||||
|     if (res1 < 0) { | ||||
|         int err = -errno; | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err); | ||||
|         LFS_TRACE("lfs_filebd_read -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     ssize_t res2 = read(bd->fd, buffer, size); | ||||
|     if (res2 < 0) { | ||||
|         int err = -errno; | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err); | ||||
|         LFS_TRACE("lfs_filebd_read -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_read -> %d", 0); | ||||
|     LFS_TRACE("lfs_filebd_read -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         lfs_off_t off, const void *buffer, lfs_size_t size) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|     LFS_TRACE("lfs_filebd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
|  | ||||
| @@ -122,7 +121,7 @@ int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|                 (off_t)block*cfg->block_size + (off_t)off, SEEK_SET); | ||||
|         if (res1 < 0) { | ||||
|             int err = -errno; | ||||
|             LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); | ||||
|             LFS_TRACE("lfs_filebd_prog -> %d", err); | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
| @@ -131,7 +130,7 @@ int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|             ssize_t res2 = read(bd->fd, &c, 1); | ||||
|             if (res2 < 0) { | ||||
|                 int err = -errno; | ||||
|                 LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); | ||||
|                 LFS_TRACE("lfs_filebd_prog -> %d", err); | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
| @@ -144,23 +143,23 @@ int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|             (off_t)block*cfg->block_size + (off_t)off, SEEK_SET); | ||||
|     if (res1 < 0) { | ||||
|         int err = -errno; | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); | ||||
|         LFS_TRACE("lfs_filebd_prog -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     ssize_t res2 = write(bd->fd, buffer, size); | ||||
|     if (res2 < 0) { | ||||
|         int err = -errno; | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); | ||||
|         LFS_TRACE("lfs_filebd_prog -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", 0); | ||||
|     LFS_TRACE("lfs_filebd_prog -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     LFS_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
|  | ||||
|     // check if erase is valid | ||||
| @@ -171,7 +170,7 @@ int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|         off_t res1 = lseek(bd->fd, (off_t)block*cfg->block_size, SEEK_SET); | ||||
|         if (res1 < 0) { | ||||
|             int err = -errno; | ||||
|             LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err); | ||||
|             LFS_TRACE("lfs_filebd_erase -> %d", err); | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
| @@ -179,27 +178,27 @@ int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|             ssize_t res2 = write(bd->fd, &(uint8_t){bd->cfg->erase_value}, 1); | ||||
|             if (res2 < 0) { | ||||
|                 int err = -errno; | ||||
|                 LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err); | ||||
|                 LFS_TRACE("lfs_filebd_erase -> %d", err); | ||||
|                 return err; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", 0); | ||||
|     LFS_TRACE("lfs_filebd_erase -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_filebd_sync(const struct lfs_config *cfg) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_sync(%p)", (void*)cfg); | ||||
|     LFS_TRACE("lfs_filebd_sync(%p)", (void*)cfg); | ||||
|     // file sync | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
|     int err = fsync(bd->fd); | ||||
|     if (err) { | ||||
|         err = -errno; | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0); | ||||
|         LFS_TRACE("lfs_filebd_sync -> %d", 0); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0); | ||||
|     LFS_TRACE("lfs_filebd_sync -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -15,14 +15,6 @@ extern "C" | ||||
| { | ||||
| #endif | ||||
|  | ||||
|  | ||||
| // Block device specific tracing | ||||
| #ifdef LFS_FILEBD_YES_TRACE | ||||
| #define LFS_FILEBD_TRACE(...) LFS_TRACE(__VA_ARGS__) | ||||
| #else | ||||
| #define LFS_FILEBD_TRACE(...) | ||||
| #endif | ||||
|  | ||||
| // filebd config (optional) | ||||
| struct lfs_filebd_config { | ||||
|     // 8-bit erase value to use for simulating erases. -1 does not simulate | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
|  | ||||
| int lfs_rambd_createcfg(const struct lfs_config *cfg, | ||||
|         const struct lfs_rambd_config *bdcfg) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_createcfg(%p {.context=%p, " | ||||
|     LFS_TRACE("lfs_rambd_createcfg(%p {.context=%p, " | ||||
|                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||
|                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||
|                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " | ||||
| @@ -27,7 +27,7 @@ int lfs_rambd_createcfg(const struct lfs_config *cfg, | ||||
|     } else { | ||||
|         bd->buffer = lfs_malloc(cfg->block_size * cfg->block_count); | ||||
|         if (!bd->buffer) { | ||||
|             LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", LFS_ERR_NOMEM); | ||||
|             LFS_TRACE("lfs_rambd_createcfg -> %d", LFS_ERR_NOMEM); | ||||
|             return LFS_ERR_NOMEM; | ||||
|         } | ||||
|     } | ||||
| @@ -38,12 +38,12 @@ int lfs_rambd_createcfg(const struct lfs_config *cfg, | ||||
|                 cfg->block_size * cfg->block_count); | ||||
|     } | ||||
|  | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", 0); | ||||
|     LFS_TRACE("lfs_rambd_createcfg -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_rambd_create(const struct lfs_config *cfg) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_create(%p {.context=%p, " | ||||
|     LFS_TRACE("lfs_rambd_create(%p {.context=%p, " | ||||
|                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||
|                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||
|                 ".block_size=%"PRIu32", .block_count=%"PRIu32"})", | ||||
| @@ -53,25 +53,24 @@ int lfs_rambd_create(const struct lfs_config *cfg) { | ||||
|             cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count); | ||||
|     static const struct lfs_rambd_config defaults = {.erase_value=-1}; | ||||
|     int err = lfs_rambd_createcfg(cfg, &defaults); | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_create -> %d", err); | ||||
|     LFS_TRACE("lfs_rambd_create -> %d", err); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int lfs_rambd_destroy(const struct lfs_config *cfg) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_destroy(%p)", (void*)cfg); | ||||
|     LFS_TRACE("lfs_rambd_destroy(%p)", (void*)cfg); | ||||
|     // clean up memory | ||||
|     lfs_rambd_t *bd = cfg->context; | ||||
|     if (!bd->cfg->buffer) { | ||||
|         lfs_free(bd->buffer); | ||||
|     } | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_destroy -> %d", 0); | ||||
|     LFS_TRACE("lfs_rambd_destroy -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_rambd_read(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         lfs_off_t off, void *buffer, lfs_size_t size) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_read(%p, " | ||||
|                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|     LFS_TRACE("lfs_rambd_read(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_rambd_t *bd = cfg->context; | ||||
|  | ||||
| @@ -83,14 +82,13 @@ int lfs_rambd_read(const struct lfs_config *cfg, lfs_block_t block, | ||||
|     // read data | ||||
|     memcpy(buffer, &bd->buffer[block*cfg->block_size + off], size); | ||||
|  | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_read -> %d", 0); | ||||
|     LFS_TRACE("lfs_rambd_read -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_rambd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         lfs_off_t off, const void *buffer, lfs_size_t size) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_prog(%p, " | ||||
|                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|     LFS_TRACE("lfs_rambd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_rambd_t *bd = cfg->context; | ||||
|  | ||||
| @@ -110,12 +108,12 @@ int lfs_rambd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|     // program data | ||||
|     memcpy(&bd->buffer[block*cfg->block_size + off], buffer, size); | ||||
|  | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_prog -> %d", 0); | ||||
|     LFS_TRACE("lfs_rambd_prog -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_rambd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     LFS_TRACE("lfs_rambd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     lfs_rambd_t *bd = cfg->context; | ||||
|  | ||||
|     // check if erase is valid | ||||
| @@ -127,14 +125,14 @@ int lfs_rambd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|                 bd->cfg->erase_value, cfg->block_size); | ||||
|     } | ||||
|  | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_erase -> %d", 0); | ||||
|     LFS_TRACE("lfs_rambd_erase -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_rambd_sync(const struct lfs_config *cfg) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_sync(%p)", (void*)cfg); | ||||
|     LFS_TRACE("lfs_rambd_sync(%p)", (void*)cfg); | ||||
|     // sync does nothing because we aren't backed by anything real | ||||
|     (void)cfg; | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_sync -> %d", 0); | ||||
|     LFS_TRACE("lfs_rambd_sync -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -15,14 +15,6 @@ extern "C" | ||||
| { | ||||
| #endif | ||||
|  | ||||
|  | ||||
| // Block device specific tracing | ||||
| #ifdef LFS_RAMBD_YES_TRACE | ||||
| #define LFS_RAMBD_TRACE(...) LFS_TRACE(__VA_ARGS__) | ||||
| #else | ||||
| #define LFS_RAMBD_TRACE(...) | ||||
| #endif | ||||
|  | ||||
| // rambd config (optional) | ||||
| struct lfs_rambd_config { | ||||
|     // 8-bit erase value to simulate erasing with. -1 indicates no erase | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
|  | ||||
| int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path, | ||||
|         const struct lfs_testbd_config *bdcfg) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_createcfg(%p {.context=%p, " | ||||
|     LFS_TRACE("lfs_testbd_createcfg(%p {.context=%p, " | ||||
|                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||
|                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||
|                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " | ||||
| @@ -38,9 +38,9 @@ int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path, | ||||
|         if (bd->cfg->wear_buffer) { | ||||
|             bd->wear = bd->cfg->wear_buffer; | ||||
|         } else { | ||||
|             bd->wear = lfs_malloc(sizeof(lfs_testbd_wear_t)*cfg->block_count); | ||||
|             bd->wear = lfs_malloc(sizeof(lfs_testbd_wear_t) * cfg->block_count); | ||||
|             if (!bd->wear) { | ||||
|                 LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM); | ||||
|                 LFS_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM); | ||||
|                 return LFS_ERR_NOMEM; | ||||
|             } | ||||
|         } | ||||
| @@ -54,7 +54,7 @@ int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path, | ||||
|             .erase_value = bd->cfg->erase_value, | ||||
|         }; | ||||
|         int err = lfs_filebd_createcfg(cfg, path, &bd->u.file.cfg); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err); | ||||
|         LFS_TRACE("lfs_testbd_createcfg -> %d", err); | ||||
|         return err; | ||||
|     } else { | ||||
|         bd->u.ram.cfg = (struct lfs_rambd_config){ | ||||
| @@ -62,13 +62,13 @@ int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path, | ||||
|             .buffer = bd->cfg->buffer, | ||||
|         }; | ||||
|         int err = lfs_rambd_createcfg(cfg, &bd->u.ram.cfg); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err); | ||||
|         LFS_TRACE("lfs_testbd_createcfg -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
| } | ||||
|  | ||||
| int lfs_testbd_create(const struct lfs_config *cfg, const char *path) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_create(%p {.context=%p, " | ||||
|     LFS_TRACE("lfs_testbd_create(%p {.context=%p, " | ||||
|                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||
|                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||
|                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " | ||||
| @@ -80,12 +80,12 @@ int lfs_testbd_create(const struct lfs_config *cfg, const char *path) { | ||||
|             path); | ||||
|     static const struct lfs_testbd_config defaults = {.erase_value=-1}; | ||||
|     int err = lfs_testbd_createcfg(cfg, path, &defaults); | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_create -> %d", err); | ||||
|     LFS_TRACE("lfs_testbd_create -> %d", err); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_destroy(const struct lfs_config *cfg) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_destroy(%p)", (void*)cfg); | ||||
|     LFS_TRACE("lfs_testbd_destroy(%p)", (void*)cfg); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|     if (bd->cfg->erase_cycles && !bd->cfg->wear_buffer) { | ||||
|         lfs_free(bd->wear); | ||||
| @@ -93,11 +93,11 @@ int lfs_testbd_destroy(const struct lfs_config *cfg) { | ||||
|  | ||||
|     if (bd->persist) { | ||||
|         int err = lfs_filebd_destroy(cfg); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err); | ||||
|         LFS_TRACE("lfs_testbd_destroy -> %d", err); | ||||
|         return err; | ||||
|     } else { | ||||
|         int err = lfs_rambd_destroy(cfg); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err); | ||||
|         LFS_TRACE("lfs_testbd_destroy -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
| } | ||||
| @@ -145,8 +145,7 @@ static int lfs_testbd_rawsync(const struct lfs_config *cfg) { | ||||
| /// block device API /// | ||||
| int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         lfs_off_t off, void *buffer, lfs_size_t size) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_read(%p, " | ||||
|                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|     LFS_TRACE("lfs_testbd_read(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|  | ||||
| @@ -158,20 +157,19 @@ int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block, | ||||
|     // block bad? | ||||
|     if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles && | ||||
|             bd->cfg->badblock_behavior == LFS_TESTBD_BADBLOCK_READERROR) { | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT); | ||||
|         LFS_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT); | ||||
|         return LFS_ERR_CORRUPT; | ||||
|     } | ||||
|  | ||||
|     // read | ||||
|     int err = lfs_testbd_rawread(cfg, block, off, buffer, size); | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_read -> %d", err); | ||||
|     LFS_TRACE("lfs_testbd_read -> %d", err); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         lfs_off_t off, const void *buffer, lfs_size_t size) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_prog(%p, " | ||||
|                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|     LFS_TRACE("lfs_testbd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|  | ||||
| @@ -184,13 +182,13 @@ int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|     if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles) { | ||||
|         if (bd->cfg->badblock_behavior == | ||||
|                 LFS_TESTBD_BADBLOCK_PROGERROR) { | ||||
|             LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", LFS_ERR_CORRUPT); | ||||
|             LFS_TRACE("lfs_testbd_prog -> %d", LFS_ERR_CORRUPT); | ||||
|             return LFS_ERR_CORRUPT; | ||||
|         } else if (bd->cfg->badblock_behavior == | ||||
|                 LFS_TESTBD_BADBLOCK_PROGNOOP || | ||||
|                 bd->cfg->badblock_behavior == | ||||
|                 LFS_TESTBD_BADBLOCK_ERASENOOP) { | ||||
|             LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0); | ||||
|             LFS_TRACE("lfs_testbd_prog -> %d", 0); | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
| @@ -198,7 +196,7 @@ int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|     // prog | ||||
|     int err = lfs_testbd_rawprog(cfg, block, off, buffer, size); | ||||
|     if (err) { | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err); | ||||
|         LFS_TRACE("lfs_testbd_prog -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
| @@ -213,12 +211,12 @@ int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0); | ||||
|     LFS_TRACE("lfs_testbd_prog -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     LFS_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|  | ||||
|     // check if erase is valid | ||||
| @@ -229,11 +227,11 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|         if (bd->wear[block] >= bd->cfg->erase_cycles) { | ||||
|             if (bd->cfg->badblock_behavior == | ||||
|                     LFS_TESTBD_BADBLOCK_ERASEERROR) { | ||||
|                 LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT); | ||||
|                 LFS_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT); | ||||
|                 return LFS_ERR_CORRUPT; | ||||
|             } else if (bd->cfg->badblock_behavior == | ||||
|                     LFS_TESTBD_BADBLOCK_ERASENOOP) { | ||||
|                 LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", 0); | ||||
|                 LFS_TRACE("lfs_testbd_erase -> %d", 0); | ||||
|                 return 0; | ||||
|             } | ||||
|         } else { | ||||
| @@ -245,7 +243,7 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|     // erase | ||||
|     int err = lfs_testbd_rawerase(cfg, block); | ||||
|     if (err) { | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err); | ||||
|         LFS_TRACE("lfs_testbd_erase -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
| @@ -260,14 +258,14 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0); | ||||
|     LFS_TRACE("lfs_testbd_prog -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_sync(const struct lfs_config *cfg) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_sync(%p)", (void*)cfg); | ||||
|     LFS_TRACE("lfs_testbd_sync(%p)", (void*)cfg); | ||||
|     int err = lfs_testbd_rawsync(cfg); | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_sync -> %d", err); | ||||
|     LFS_TRACE("lfs_testbd_sync -> %d", err); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| @@ -275,20 +273,20 @@ int lfs_testbd_sync(const struct lfs_config *cfg) { | ||||
| /// simulated wear operations /// | ||||
| lfs_testbd_swear_t lfs_testbd_getwear(const struct lfs_config *cfg, | ||||
|         lfs_block_t block) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_getwear(%p, %"PRIu32")", (void*)cfg, block); | ||||
|     LFS_TRACE("lfs_testbd_getwear(%p, %"PRIu32")", (void*)cfg, block); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|  | ||||
|     // check if block is valid | ||||
|     LFS_ASSERT(bd->cfg->erase_cycles); | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|  | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_getwear -> %"PRIu32, bd->wear[block]); | ||||
|     LFS_TRACE("lfs_testbd_getwear -> %"PRIu32, bd->wear[block]); | ||||
|     return bd->wear[block]; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_setwear(const struct lfs_config *cfg, | ||||
|         lfs_block_t block, lfs_testbd_wear_t wear) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)cfg, block); | ||||
|     LFS_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)cfg, block); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|  | ||||
|     // check if block is valid | ||||
| @@ -297,6 +295,6 @@ int lfs_testbd_setwear(const struct lfs_config *cfg, | ||||
|  | ||||
|     bd->wear[block] = wear; | ||||
|  | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_setwear -> %d", 0); | ||||
|     LFS_TRACE("lfs_testbd_setwear -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -19,13 +19,6 @@ extern "C" | ||||
| #endif | ||||
|  | ||||
|  | ||||
| // Block device specific tracing | ||||
| #ifdef LFS_TESTBD_YES_TRACE | ||||
| #define LFS_TESTBD_TRACE(...) LFS_TRACE(__VA_ARGS__) | ||||
| #else | ||||
| #define LFS_TESTBD_TRACE(...) | ||||
| #endif | ||||
|  | ||||
| // Mode determining how "bad blocks" behave during testing. This simulates | ||||
| // some real-world circumstances such as progs not sticking (prog-noop), | ||||
| // a readonly disk (erase-noop), and ECC failures (read-error). | ||||
|   | ||||
							
								
								
									
										154
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										154
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -29,8 +29,8 @@ static int lfs_bd_read(lfs_t *lfs, | ||||
|         lfs_block_t block, lfs_off_t off, | ||||
|         void *buffer, lfs_size_t size) { | ||||
|     uint8_t *data = buffer; | ||||
|     if (block >= lfs->cfg->block_count || | ||||
|             off+size > lfs->cfg->block_size) { | ||||
|     LFS_ASSERT(block != LFS_BLOCK_NULL); | ||||
|     if (off+size > lfs->cfg->block_size) { | ||||
|         return LFS_ERR_CORRUPT; | ||||
|     } | ||||
|  | ||||
| @@ -71,21 +71,6 @@ static int lfs_bd_read(lfs_t *lfs, | ||||
|             diff = lfs_min(diff, rcache->off-off); | ||||
|         } | ||||
|  | ||||
|         if (size >= hint && off % lfs->cfg->read_size == 0 && | ||||
|                 size >= lfs->cfg->read_size) { | ||||
|             // bypass cache? | ||||
|             diff = lfs_aligndown(diff, lfs->cfg->read_size); | ||||
|             int err = lfs->cfg->read(lfs->cfg, block, off, data, diff); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             data += diff; | ||||
|             off += diff; | ||||
|             size -= diff; | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         // load to cache, first condition can no longer fail | ||||
|         LFS_ASSERT(block < lfs->cfg->block_count); | ||||
|         rcache->block = block; | ||||
| @@ -188,7 +173,7 @@ static int lfs_bd_prog(lfs_t *lfs, | ||||
|         lfs_block_t block, lfs_off_t off, | ||||
|         const void *buffer, lfs_size_t size) { | ||||
|     const uint8_t *data = buffer; | ||||
|     LFS_ASSERT(block == LFS_BLOCK_INLINE || block < lfs->cfg->block_count); | ||||
|     LFS_ASSERT(block != LFS_BLOCK_NULL); | ||||
|     LFS_ASSERT(off + size <= lfs->cfg->block_size); | ||||
|  | ||||
|     while (size > 0) { | ||||
| @@ -433,6 +418,7 @@ int lfs_fs_traverseraw(lfs_t *lfs, | ||||
|         int (*cb)(void *data, lfs_block_t block), void *data, | ||||
|         bool includeorphans); | ||||
| static int lfs_fs_forceconsistency(lfs_t *lfs); | ||||
| static int lfs_fs_deorphan(lfs_t *lfs); | ||||
| static int lfs_deinit(lfs_t *lfs); | ||||
| #ifdef LFS_MIGRATE | ||||
| static int lfs1_traverse(lfs_t *lfs, | ||||
| @@ -452,19 +438,6 @@ static int lfs_alloc_lookahead(void *p, lfs_block_t block) { | ||||
|     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) { | ||||
|     while (true) { | ||||
|         while (lfs->free.i != lfs->free.size) { | ||||
| @@ -505,12 +478,16 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { | ||||
|         memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size); | ||||
|         int err = lfs_fs_traverseraw(lfs, lfs_alloc_lookahead, lfs, true); | ||||
|         if (err) { | ||||
|             lfs_alloc_reset(lfs); | ||||
|             return err; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void lfs_alloc_ack(lfs_t *lfs) { | ||||
|     lfs->free.ack = lfs->cfg->block_count; | ||||
| } | ||||
|  | ||||
|  | ||||
| /// Metadata pair and directory operations /// | ||||
| static lfs_stag_t lfs_dir_getslice(lfs_t *lfs, const lfs_mdir_t *dir, | ||||
|         lfs_tag_t gmask, lfs_tag_t gtag, | ||||
| @@ -772,12 +749,6 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|     // scanning the entire directory | ||||
|     lfs_stag_t besttag = -1; | ||||
|  | ||||
|     // if either block address is invalid we return LFS_ERR_CORRUPT here, | ||||
|     // otherwise later writes to the pair could fail | ||||
|     if (pair[0] >= lfs->cfg->block_count || pair[1] >= lfs->cfg->block_count) { | ||||
|         return LFS_ERR_CORRUPT; | ||||
|     } | ||||
|  | ||||
|     // find the block with the most recent revision | ||||
|     uint32_t revs[2] = {0, 0}; | ||||
|     int r = 0; | ||||
| @@ -826,6 +797,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|                 if (err == LFS_ERR_CORRUPT) { | ||||
|                     // can't continue? | ||||
|                     dir->erased = false; | ||||
|                     dir->first = false; | ||||
|                     break; | ||||
|                 } | ||||
|                 return err; | ||||
| @@ -838,9 +810,11 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|             if (!lfs_tag_isvalid(tag)) { | ||||
|                 dir->erased = (lfs_tag_type1(ptag) == LFS_TYPE_CRC && | ||||
|                         dir->off % lfs->cfg->prog_size == 0); | ||||
|                 dir->first = false; | ||||
|                 break; | ||||
|             } else if (off + lfs_tag_dsize(tag) > lfs->cfg->block_size) { | ||||
|                 dir->erased = false; | ||||
|                 dir->first = false; | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
| @@ -855,6 +829,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|                 if (err) { | ||||
|                     if (err == LFS_ERR_CORRUPT) { | ||||
|                         dir->erased = false; | ||||
|                         dir->first = false; | ||||
|                         break; | ||||
|                     } | ||||
|                     return err; | ||||
| @@ -863,6 +838,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|  | ||||
|                 if (crc != dcrc) { | ||||
|                     dir->erased = false; | ||||
|                     dir->first = false; | ||||
|                     break; | ||||
|                 } | ||||
|  | ||||
| @@ -896,6 +872,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|                 if (err) { | ||||
|                     if (err == LFS_ERR_CORRUPT) { | ||||
|                         dir->erased = false; | ||||
|                         dir->first = false; | ||||
|                         break; | ||||
|                     } | ||||
|                     return err; | ||||
| @@ -929,6 +906,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|                 if (err) { | ||||
|                     if (err == LFS_ERR_CORRUPT) { | ||||
|                         dir->erased = false; | ||||
|                         dir->first = false; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
| @@ -942,6 +920,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|                 if (res < 0) { | ||||
|                     if (res == LFS_ERR_CORRUPT) { | ||||
|                         dir->erased = false; | ||||
|                         dir->first = false; | ||||
|                         break; | ||||
|                     } | ||||
|                     return res; | ||||
| @@ -994,7 +973,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|         dir->rev = revs[(r+1)%2]; | ||||
|     } | ||||
|  | ||||
|     LFS_ERROR("Corrupted dir pair at {0x%"PRIx32", 0x%"PRIx32"}", | ||||
|     LFS_ERROR("Corrupted dir pair at %"PRIx32" %"PRIx32, | ||||
|             dir->pair[0], dir->pair[1]); | ||||
|     return LFS_ERR_CORRUPT; | ||||
| } | ||||
| @@ -1385,6 +1364,7 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) { | ||||
|     dir->tail[0] = LFS_BLOCK_NULL; | ||||
|     dir->tail[1] = LFS_BLOCK_NULL; | ||||
|     dir->erased = false; | ||||
|     dir->first = true; | ||||
|     dir->split = false; | ||||
|  | ||||
|     // don't write out yet, let caller take care of that | ||||
| @@ -1521,7 +1501,7 @@ static int lfs_dir_compact(lfs_t *lfs, | ||||
|     // 2. block_cycles = 2n, which, due to aliasing, would only ever relocate | ||||
|     //    one metadata block in the pair, effectively making this useless | ||||
|     if (lfs->cfg->block_cycles > 0 && | ||||
|             (dir->rev % ((lfs->cfg->block_cycles+1)|1) == 0)) { | ||||
|             (dir->rev % ((lfs->cfg->block_cycles+4)/*|1*/) == 0)) { // TODO what | ||||
|         if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) { | ||||
|             // oh no! we're writing too much to the superblock, | ||||
|             // should we expand? | ||||
| @@ -1682,13 +1662,12 @@ relocate: | ||||
|         relocated = true; | ||||
|         lfs_cache_drop(lfs, &lfs->pcache); | ||||
|         if (!tired) { | ||||
|             LFS_DEBUG("Bad block at 0x%"PRIx32, dir->pair[1]); | ||||
|             LFS_DEBUG("Bad block at %"PRIx32, dir->pair[1]); | ||||
|         } | ||||
|  | ||||
|         // can't relocate superblock, filesystem is now frozen | ||||
|         if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) { | ||||
|             LFS_WARN("Superblock 0x%"PRIx32" has become unwritable", | ||||
|                     dir->pair[1]); | ||||
|             LFS_WARN("Superblock %"PRIx32" has become unwritable", dir->pair[1]); | ||||
|             return LFS_ERR_NOSPC; | ||||
|         } | ||||
|  | ||||
| @@ -1703,9 +1682,13 @@ relocate: | ||||
|     } | ||||
|  | ||||
|     if (relocated) { | ||||
|         if (!dir->first) { | ||||
|             // TODO something funky! | ||||
|             dir->pair[0] = dir->pair[0]; | ||||
|             dir->pair[1] = oldpair[1]; | ||||
|         } | ||||
|         // update references if we relocated | ||||
|         LFS_DEBUG("Relocating {0x%"PRIx32", 0x%"PRIx32"} " | ||||
|                     "-> {0x%"PRIx32", 0x%"PRIx32"}", | ||||
|         LFS_DEBUG("Relocating %"PRIx32" %"PRIx32" -> %"PRIx32" %"PRIx32, | ||||
|                 oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); | ||||
|         int err = lfs_fs_relocate(lfs, oldpair, dir->pair); | ||||
|         if (err) { | ||||
| @@ -2229,6 +2212,7 @@ static int lfs_ctz_find(lfs_t *lfs, | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         LFS_ASSERT(head >= 2 && head <= lfs->cfg->block_count); | ||||
|         current -= 1 << skip; | ||||
|     } | ||||
|  | ||||
| @@ -2248,6 +2232,7 @@ static int lfs_ctz_extend(lfs_t *lfs, | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|         LFS_ASSERT(nblock >= 2 && nblock <= lfs->cfg->block_count); | ||||
|  | ||||
|         { | ||||
|             err = lfs_bd_erase(lfs, nblock); | ||||
| @@ -2320,6 +2305,8 @@ static int lfs_ctz_extend(lfs_t *lfs, | ||||
|                         return err; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 LFS_ASSERT(nhead >= 2 && nhead <= lfs->cfg->block_count); | ||||
|             } | ||||
|  | ||||
|             *block = nblock; | ||||
| @@ -2328,7 +2315,7 @@ static int lfs_ctz_extend(lfs_t *lfs, | ||||
|         } | ||||
|  | ||||
| relocate: | ||||
|         LFS_DEBUG("Bad block at 0x%"PRIx32, nblock); | ||||
|         LFS_DEBUG("Bad block at %"PRIx32, nblock); | ||||
|  | ||||
|         // just clear cache and try a new block | ||||
|         lfs_cache_drop(lfs, pcache); | ||||
| @@ -2632,7 +2619,7 @@ static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) { | ||||
|         return 0; | ||||
|  | ||||
| relocate: | ||||
|         LFS_DEBUG("Bad block at 0x%"PRIx32, nblock); | ||||
|         LFS_DEBUG("Bad block at %"PRIx32, nblock); | ||||
|  | ||||
|         // just clear cache and try a new block | ||||
|         lfs_cache_drop(lfs, &lfs->pcache); | ||||
| @@ -2709,7 +2696,7 @@ static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { | ||||
|                 break; | ||||
|  | ||||
| relocate: | ||||
|                 LFS_DEBUG("Bad block at 0x%"PRIx32, file->block); | ||||
|                 LFS_DEBUG("Bad block at %"PRIx32, file->block); | ||||
|                 err = lfs_file_relocate(lfs, file); | ||||
|                 if (err) { | ||||
|                     return err; | ||||
| @@ -3208,7 +3195,7 @@ int lfs_remove(lfs_t *lfs, const char *path) { | ||||
|     } | ||||
|  | ||||
|     lfs->mlist = dir.next; | ||||
|     if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { | ||||
|     if (lfs_tag_type3(tag) == LFS_TYPE_DIR/* && lfs_tag_size(lfs->gstate.tag) > 0*/) { | ||||
|         // fix orphan | ||||
|         lfs_fs_preporphans(lfs, -1); | ||||
|  | ||||
| @@ -3690,15 +3677,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|  | ||||
|     // scan directory blocks for superblock and any global updates | ||||
|     lfs_mdir_t dir = {.tail = {0, 1}}; | ||||
|     lfs_block_t cycle = 0; | ||||
|     while (!lfs_pair_isnull(dir.tail)) { | ||||
|         if (cycle >= lfs->cfg->block_count/2) { | ||||
|             // loop detected | ||||
|             err = LFS_ERR_CORRUPT; | ||||
|             goto cleanup; | ||||
|         } | ||||
|         cycle += 1; | ||||
|  | ||||
|         // fetch next block in tail list | ||||
|         lfs_stag_t tag = lfs_dir_fetchmatch(lfs, &dir, dir.tail, | ||||
|                 LFS_MKTAG(0x7ff, 0x3ff, 0), | ||||
| @@ -3733,7 +3712,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|             uint16_t minor_version = (0xffff & (superblock.version >>  0)); | ||||
|             if ((major_version != LFS_DISK_VERSION_MAJOR || | ||||
|                  minor_version > LFS_DISK_VERSION_MINOR)) { | ||||
|                 LFS_ERROR("Invalid version v%"PRIu16".%"PRIu16, | ||||
|                 LFS_ERROR("Invalid version %"PRIu16".%"PRIu16, | ||||
|                         major_version, minor_version); | ||||
|                 err = LFS_ERR_INVAL; | ||||
|                 goto cleanup; | ||||
| @@ -3789,7 +3768,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|  | ||||
|     // update littlefs with gstate | ||||
|     if (!lfs_gstate_iszero(&lfs->gstate)) { | ||||
|         LFS_DEBUG("Found pending gstate 0x%08"PRIx32"%08"PRIx32"%08"PRIx32, | ||||
|         LFS_DEBUG("Found pending gstate %08"PRIx32" %08"PRIx32" %08"PRIx32, | ||||
|                 lfs->gstate.tag, | ||||
|                 lfs->gstate.pair[0], | ||||
|                 lfs->gstate.pair[1]); | ||||
| @@ -3798,7 +3777,10 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     lfs->gdisk = lfs->gstate; | ||||
|  | ||||
|     // setup free lookahead | ||||
|     lfs_alloc_reset(lfs); | ||||
|     lfs->free.off = lfs->seed % lfs->cfg->block_size; | ||||
|     lfs->free.size = 0; | ||||
|     lfs->free.i = 0; | ||||
|     lfs_alloc_ack(lfs); | ||||
|  | ||||
|     LFS_TRACE("lfs_mount -> %d", 0); | ||||
|     return 0; | ||||
| @@ -3837,14 +3819,7 @@ int lfs_fs_traverseraw(lfs_t *lfs, | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     lfs_block_t cycle = 0; | ||||
|     while (!lfs_pair_isnull(dir.tail)) { | ||||
|         if (cycle >= lfs->cfg->block_count/2) { | ||||
|             // loop detected | ||||
|             return LFS_ERR_CORRUPT; | ||||
|         } | ||||
|         cycle += 1; | ||||
|  | ||||
|         for (int i = 0; i < 2; i++) { | ||||
|             int err = cb(data, dir.tail[i]); | ||||
|             if (err) { | ||||
| @@ -3928,14 +3903,7 @@ static int lfs_fs_pred(lfs_t *lfs, | ||||
|     // iterate over all directory directory entries | ||||
|     pdir->tail[0] = 0; | ||||
|     pdir->tail[1] = 1; | ||||
|     lfs_block_t cycle = 0; | ||||
|     while (!lfs_pair_isnull(pdir->tail)) { | ||||
|         if (cycle >= lfs->cfg->block_count/2) { | ||||
|             // loop detected | ||||
|             return LFS_ERR_CORRUPT; | ||||
|         } | ||||
|         cycle += 1; | ||||
|  | ||||
|         if (lfs_pair_cmp(pdir->tail, pair) == 0) { | ||||
|             return 0; | ||||
|         } | ||||
| @@ -3978,14 +3946,7 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], | ||||
|     // use fetchmatch with callback to find pairs | ||||
|     parent->tail[0] = 0; | ||||
|     parent->tail[1] = 1; | ||||
|     lfs_block_t cycle = 0; | ||||
|     while (!lfs_pair_isnull(parent->tail)) { | ||||
|         if (cycle >= lfs->cfg->block_count/2) { | ||||
|             // loop detected | ||||
|             return LFS_ERR_CORRUPT; | ||||
|         } | ||||
|         cycle += 1; | ||||
|  | ||||
|         lfs_stag_t tag = lfs_dir_fetchmatch(lfs, parent, parent->tail, | ||||
|                 LFS_MKTAG(0x7ff, 0, 0x3ff), | ||||
|                 LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8), | ||||
| @@ -4004,6 +3965,8 @@ static int lfs_fs_relocate(lfs_t *lfs, | ||||
|         const lfs_block_t oldpair[2], lfs_block_t newpair[2]) { | ||||
|     // update internal root | ||||
|     if (lfs_pair_cmp(oldpair, lfs->root) == 0) { | ||||
|         LFS_DEBUG("Relocating root %"PRIx32" %"PRIx32, | ||||
|                 newpair[0], newpair[1]); | ||||
|         lfs->root[0] = newpair[0]; | ||||
|         lfs->root[1] = newpair[1]; | ||||
|     } | ||||
| @@ -4039,7 +4002,7 @@ static int lfs_fs_relocate(lfs_t *lfs, | ||||
|         if (lfs_gstate_hasmovehere(&lfs->gstate, parent.pair)) { | ||||
|             moveid = lfs_tag_id(lfs->gstate.tag); | ||||
|             LFS_DEBUG("Fixing move while relocating " | ||||
|                     "{0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16"\n", | ||||
|                     "%"PRIx32" %"PRIx32" %"PRIx16"\n", | ||||
|                     parent.pair[0], parent.pair[1], moveid); | ||||
|             lfs_fs_prepmove(lfs, 0x3ff, NULL); | ||||
|             if (moveid < lfs_tag_id(tag)) { | ||||
| @@ -4061,6 +4024,12 @@ static int lfs_fs_relocate(lfs_t *lfs, | ||||
|         lfs_fs_preporphans(lfs, -1); | ||||
|     } | ||||
|  | ||||
| #if 0 | ||||
|     int err = lfs_fs_deorphan(lfs); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
| #else | ||||
|     // find pred | ||||
|     int err = lfs_fs_pred(lfs, oldpair, &parent); | ||||
|     if (err && err != LFS_ERR_NOENT) { | ||||
| @@ -4075,7 +4044,7 @@ static int lfs_fs_relocate(lfs_t *lfs, | ||||
|         if (lfs_gstate_hasmovehere(&lfs->gstate, parent.pair)) { | ||||
|             moveid = lfs_tag_id(lfs->gstate.tag); | ||||
|             LFS_DEBUG("Fixing move while relocating " | ||||
|                     "{0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16"\n", | ||||
|                     "%"PRIx32" %"PRIx32" %"PRIx16"\n", | ||||
|                     parent.pair[0], parent.pair[1], moveid); | ||||
|             lfs_fs_prepmove(lfs, 0x3ff, NULL); | ||||
|         } | ||||
| @@ -4091,6 +4060,7 @@ static int lfs_fs_relocate(lfs_t *lfs, | ||||
|             return err; | ||||
|         } | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -4116,7 +4086,7 @@ static int lfs_fs_demove(lfs_t *lfs) { | ||||
|     } | ||||
|  | ||||
|     // Fix bad moves | ||||
|     LFS_DEBUG("Fixing move {0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16, | ||||
|     LFS_DEBUG("Fixing move %"PRIx32" %"PRIx32" %"PRIx16, | ||||
|             lfs->gdisk.pair[0], | ||||
|             lfs->gdisk.pair[1], | ||||
|             lfs_tag_id(lfs->gdisk.tag)); | ||||
| @@ -4167,7 +4137,7 @@ static int lfs_fs_deorphan(lfs_t *lfs) { | ||||
|  | ||||
|             if (tag == LFS_ERR_NOENT) { | ||||
|                 // we are an orphan | ||||
|                 LFS_DEBUG("Fixing orphan {0x%"PRIx32", 0x%"PRIx32"}", | ||||
|                 LFS_DEBUG("Fixing orphan %"PRIx32" %"PRIx32, | ||||
|                         pdir.tail[0], pdir.tail[1]); | ||||
|  | ||||
|                 err = lfs_dir_drop(lfs, &pdir, &dir); | ||||
| @@ -4189,8 +4159,8 @@ static int lfs_fs_deorphan(lfs_t *lfs) { | ||||
|  | ||||
|             if (!lfs_pair_sync(pair, pdir.tail)) { | ||||
|                 // we have desynced | ||||
|                 LFS_DEBUG("Fixing half-orphan {0x%"PRIx32", 0x%"PRIx32"} " | ||||
|                             "-> {0x%"PRIx32", 0x%"PRIx32"}", | ||||
|                 LFS_DEBUG("Fixing half-orphan " | ||||
|                         "%"PRIx32" %"PRIx32" -> %"PRIx32" %"PRIx32, | ||||
|                         pdir.tail[0], pdir.tail[1], pair[0], pair[1]); | ||||
|  | ||||
|                 lfs_pair_tole32(pair); | ||||
| @@ -4453,7 +4423,7 @@ static int lfs1_dir_fetch(lfs_t *lfs, | ||||
|     } | ||||
|  | ||||
|     if (!valid) { | ||||
|         LFS_ERROR("Corrupted dir pair at {0x%"PRIx32", 0x%"PRIx32"}", | ||||
|         LFS_ERROR("Corrupted dir pair at %" PRIx32 " %" PRIx32 , | ||||
|                 tpair[0], tpair[1]); | ||||
|         return LFS_ERR_CORRUPT; | ||||
|     } | ||||
| @@ -4641,8 +4611,7 @@ static int lfs1_mount(lfs_t *lfs, struct lfs1 *lfs1, | ||||
|         } | ||||
|  | ||||
|         if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) { | ||||
|             LFS_ERROR("Invalid superblock at {0x%"PRIx32", 0x%"PRIx32"}", | ||||
|                     0, 1); | ||||
|             LFS_ERROR("Invalid superblock at %d %d", 0, 1); | ||||
|             err = LFS_ERR_CORRUPT; | ||||
|             goto cleanup; | ||||
|         } | ||||
| @@ -4651,7 +4620,7 @@ static int lfs1_mount(lfs_t *lfs, struct lfs1 *lfs1, | ||||
|         uint16_t minor_version = (0xffff & (superblock.d.version >>  0)); | ||||
|         if ((major_version != LFS1_DISK_VERSION_MAJOR || | ||||
|              minor_version > LFS1_DISK_VERSION_MINOR)) { | ||||
|             LFS_ERROR("Invalid version v%d.%d", major_version, minor_version); | ||||
|             LFS_ERROR("Invalid version %d.%d", major_version, minor_version); | ||||
|             err = LFS_ERR_INVAL; | ||||
|             goto cleanup; | ||||
|         } | ||||
| @@ -4817,8 +4786,7 @@ int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|  | ||||
|             // Copy over first block to thread into fs. Unfortunately | ||||
|             // if this fails there is not much we can do. | ||||
|             LFS_DEBUG("Migrating {0x%"PRIx32", 0x%"PRIx32"} " | ||||
|                         "-> {0x%"PRIx32", 0x%"PRIx32"}", | ||||
|             LFS_DEBUG("Migrating %"PRIx32" %"PRIx32" -> %"PRIx32" %"PRIx32, | ||||
|                     lfs->root[0], lfs->root[1], dir1.head[0], dir1.head[1]); | ||||
|  | ||||
|             err = lfs_bd_erase(lfs, dir1.head[1]); | ||||
|   | ||||
							
								
								
									
										3
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -21,7 +21,7 @@ extern "C" | ||||
| // Software library version | ||||
| // Major (top-nibble), incremented on backwards incompatible changes | ||||
| // Minor (bottom-nibble), incremented on feature additions | ||||
| #define LFS_VERSION 0x00020002 | ||||
| #define LFS_VERSION 0x00020001 | ||||
| #define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) | ||||
| #define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >>  0)) | ||||
|  | ||||
| @@ -311,6 +311,7 @@ typedef struct lfs_mdir { | ||||
|     uint16_t count; | ||||
|     bool erased; | ||||
|     bool split; | ||||
|     bool first; | ||||
|     lfs_block_t tail[2]; | ||||
| } lfs_mdir_t; | ||||
|  | ||||
|   | ||||
							
								
								
									
										30
									
								
								lfs_util.h
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								lfs_util.h
									
									
									
									
									
								
							| @@ -50,35 +50,31 @@ extern "C" | ||||
|  | ||||
| // Logging functions | ||||
| #ifdef LFS_YES_TRACE | ||||
| #define LFS_TRACE_(fmt, ...) \ | ||||
|     printf("%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "") | ||||
| #define LFS_TRACE(fmt, ...) \ | ||||
|     printf("%s:%d:trace: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #else | ||||
| #define LFS_TRACE(...) | ||||
| #define LFS_TRACE(fmt, ...) | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_NO_DEBUG | ||||
| #define LFS_DEBUG_(fmt, ...) \ | ||||
|     printf("%s:%d:debug: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #define LFS_DEBUG(...) LFS_DEBUG_(__VA_ARGS__, "") | ||||
| #define LFS_DEBUG(fmt, ...) \ | ||||
|     printf("%s:%d:debug: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #else | ||||
| #define LFS_DEBUG(...) | ||||
| #define LFS_DEBUG(fmt, ...) | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_NO_WARN | ||||
| #define LFS_WARN_(fmt, ...) \ | ||||
|     printf("%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #define LFS_WARN(...) LFS_WARN_(__VA_ARGS__, "") | ||||
| #define LFS_WARN(fmt, ...) \ | ||||
|     printf("%s:%d:warn: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #else | ||||
| #define LFS_WARN(...) | ||||
| #define LFS_WARN(fmt, ...) | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_NO_ERROR | ||||
| #define LFS_ERROR_(fmt, ...) \ | ||||
|     printf("%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #define LFS_ERROR(...) LFS_ERROR_(__VA_ARGS__, "") | ||||
| #define LFS_ERROR(fmt, ...) \ | ||||
|     printf("%s:%d:error: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #else | ||||
| #define LFS_ERROR(...) | ||||
| #define LFS_ERROR(fmt, ...) | ||||
| #endif | ||||
|  | ||||
| // Runtime assertions | ||||
| @@ -111,7 +107,7 @@ static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) { | ||||
|     return lfs_aligndown(a + alignment-1, alignment); | ||||
| } | ||||
|  | ||||
| // Find the smallest power of 2 greater than or equal to a | ||||
| // Find the next smallest power of 2 less than or equal to a | ||||
| static inline uint32_t lfs_npw2(uint32_t a) { | ||||
| #if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) | ||||
|     return 32 - __builtin_clz(a-1); | ||||
|   | ||||
| @@ -166,8 +166,8 @@ def mkassert(type, comp, lh, rh, size=None): | ||||
|         'type': type.lower(), 'TYPE': type.upper(), | ||||
|         'comp': comp.lower(), 'COMP': comp.upper(), | ||||
|         'prefix': PREFIX.lower(), 'PREFIX': PREFIX.upper(), | ||||
|         'lh': lh.strip(' '), | ||||
|         'rh': rh.strip(' '), | ||||
|         'lh': lh.strip(), | ||||
|         'rh': rh.strip(), | ||||
|         'size': size, | ||||
|     } | ||||
|     if size: | ||||
|   | ||||
| @@ -233,8 +233,8 @@ class MetadataPair: | ||||
|  | ||||
|     def __lt__(self, other): | ||||
|         # corrupt blocks don't count | ||||
|         if not self or not other: | ||||
|             return bool(other) | ||||
|         if not self and other: | ||||
|             return True | ||||
|  | ||||
|         # use sequence arithmetic to avoid overflow | ||||
|         return not ((other.rev - self.rev) & 0x80000000) | ||||
| @@ -318,24 +318,6 @@ def main(args): | ||||
|  | ||||
|     # find most recent pair | ||||
|     mdir = MetadataPair(blocks) | ||||
|  | ||||
|     try: | ||||
|         mdir.tail = mdir[Tag('tail', 0, 0)] | ||||
|         if mdir.tail.size != 8 or mdir.tail.data == 8*b'\xff': | ||||
|             mdir.tail = None | ||||
|     except KeyError: | ||||
|         mdir.tail = None | ||||
|  | ||||
|     print("mdir {%s} rev %d%s%s%s" % ( | ||||
|         ', '.join('%#x' % b | ||||
|             for b in [args.block1, args.block2] | ||||
|             if b is not None), | ||||
|         mdir.rev, | ||||
|         ' (was %s)' % ', '.join('%d' % m.rev for m in mdir.pair[1:]) | ||||
|         if len(mdir.pair) > 1 else '', | ||||
|         ' (corrupted!)' if not mdir else '', | ||||
|         ' -> {%#x, %#x}' % struct.unpack('<II', mdir.tail.data) | ||||
|         if mdir.tail else '')) | ||||
|     if args.all: | ||||
|         mdir.dump_all(truncate=not args.no_truncate) | ||||
|     elif args.log: | ||||
|   | ||||
| @@ -7,14 +7,79 @@ import io | ||||
| import itertools as it | ||||
| from readmdir import Tag, MetadataPair | ||||
|  | ||||
| def popc(x): | ||||
|     return bin(x).count('1') | ||||
|  | ||||
| def ctz(x): | ||||
|     return len(bin(x)) - len(bin(x).rstrip('0')) | ||||
|  | ||||
| def dumpentries(args, mdir, f): | ||||
|     for k, id_ in enumerate(mdir.ids): | ||||
|         name = mdir[Tag('name', id_, 0)] | ||||
|         struct_ = mdir[Tag('struct', id_, 0)] | ||||
|  | ||||
|         f.write("id %d %s %s" % ( | ||||
|             id_, name.typerepr(), | ||||
|             json.dumps(name.data.decode('utf8')))) | ||||
|         if struct_.is_('dirstruct'): | ||||
|             f.write(" dir {%#x, %#x}" % struct.unpack( | ||||
|                 '<II', struct_.data[:8].ljust(8, b'\xff'))) | ||||
|         if struct_.is_('ctzstruct'): | ||||
|             f.write(" ctz {%#x} size %d" % struct.unpack( | ||||
|                 '<II', struct_.data[:8].ljust(8, b'\xff'))) | ||||
|         if struct_.is_('inlinestruct'): | ||||
|             f.write(" inline size %d" % struct_.size) | ||||
|         f.write("\n") | ||||
|  | ||||
|         if args.data and struct_.is_('inlinestruct'): | ||||
|             for i in range(0, len(struct_.data), 16): | ||||
|                 f.write("  %08x: %-47s  %-16s\n" % ( | ||||
|                     i, ' '.join('%02x' % c for c in struct_.data[i:i+16]), | ||||
|                     ''.join(c if c >= ' ' and c <= '~' else '.' | ||||
|                         for c in map(chr, struct_.data[i:i+16])))) | ||||
|         elif args.data and struct_.is_('ctzstruct'): | ||||
|             block, size = struct.unpack( | ||||
|                 '<II', struct_.data[:8].ljust(8, b'\xff')) | ||||
|             data = [] | ||||
|             i = 0 if size == 0 else (size-1) // (args.block_size - 8) | ||||
|             if i != 0: | ||||
|                 i = ((size-1) - 4*popc(i-1)+2) // (args.block_size - 8) | ||||
|             with open(args.disk, 'rb') as f2: | ||||
|                 while i >= 0: | ||||
|                     f2.seek(block * args.block_size) | ||||
|                     dat = f2.read(args.block_size) | ||||
|                     data.append(dat[4*(ctz(i)+1) if i != 0 else 0:]) | ||||
|                     block, = struct.unpack('<I', dat[:4].ljust(4, b'\xff')) | ||||
|                     i -= 1 | ||||
|  | ||||
|             data = bytes(it.islice( | ||||
|                 it.chain.from_iterable(reversed(data)), size)) | ||||
|             for i in range(0, min(len(data), 256) | ||||
|                     if not args.no_truncate else len(data), 16): | ||||
|                 f.write("  %08x: %-47s  %-16s\n" % ( | ||||
|                     i, ' '.join('%02x' % c for c in data[i:i+16]), | ||||
|                     ''.join(c if c >= ' ' and c <= '~' else '.' | ||||
|                         for c in map(chr, data[i:i+16])))) | ||||
|  | ||||
|         for tag in mdir.tags: | ||||
|             if tag.id==id_ and tag.is_('userattr'): | ||||
|                 f.write("id %d %s size %d\n" % ( | ||||
|                     id_, tag.typerepr(), tag.size)) | ||||
|  | ||||
|                 if args.data: | ||||
|                     for i in range(0, len(tag.data), 16): | ||||
|                         f.write("  %-47s  %-16s\n" % ( | ||||
|                             ' '.join('%02x' % c for c in tag.data[i:i+16]), | ||||
|                             ''.join(c if c >= ' ' and c <= '~' else '.' | ||||
|                                 for c in map(chr, tag.data[i:i+16])))) | ||||
|  | ||||
| def main(args): | ||||
|     superblock = None | ||||
|     gstate = b'\0\0\0\0\0\0\0\0\0\0\0\0' | ||||
|     dirs = [] | ||||
|     mdirs = [] | ||||
|     corrupted = [] | ||||
|     cycle = False | ||||
|     with open(args.disk, 'rb') as f: | ||||
|         dirs = [] | ||||
|         superblock = None | ||||
|         gstate = b'' | ||||
|         mdirs = [] | ||||
|         cycle = False | ||||
|         tail = (args.block1, args.block2) | ||||
|         hard = False | ||||
|         while True: | ||||
| @@ -61,10 +126,6 @@ def main(args): | ||||
|             except KeyError: | ||||
|                 pass | ||||
|  | ||||
|             # corrupted? | ||||
|             if not mdir: | ||||
|                 corrupted.append(mdir) | ||||
|  | ||||
|             # add to directories | ||||
|             mdirs.append(mdir) | ||||
|             if mdir.tail is None or not mdir.tail.is_('hardtail'): | ||||
| @@ -99,44 +160,54 @@ def main(args): | ||||
|  | ||||
|         dir[0].path = path.replace('//', '/') | ||||
|  | ||||
|     # print littlefs + version info | ||||
|     version = ('?', '?') | ||||
|     if superblock: | ||||
|         version = tuple(reversed( | ||||
|             struct.unpack('<HH', superblock[1].data[0:4].ljust(4, b'\xff')))) | ||||
|     print("%-47s%s" % ("littlefs v%s.%s" % version, | ||||
|         "data (truncated, if it fits)" | ||||
|         if not any([args.no_truncate, args.tags, args.log, args.all]) else "")) | ||||
|     # dump tree | ||||
|     if not args.superblock and not args.gstate and not args.mdirs: | ||||
|         args.superblock = True | ||||
|         args.gstate = True | ||||
|         args.mdirs = True | ||||
|  | ||||
|     # print gstate | ||||
|     if args.superblock and superblock: | ||||
|         print("superblock %s v%d.%d" % ( | ||||
|             json.dumps(superblock[0].data.decode('utf8')), | ||||
|             struct.unpack('<H', superblock[1].data[2:2+2])[0], | ||||
|             struct.unpack('<H', superblock[1].data[0:0+2])[0])) | ||||
|         print( | ||||
|             "  block_size %d\n" | ||||
|             "  block_count %d\n" | ||||
|             "  name_max %d\n" | ||||
|             "  file_max %d\n" | ||||
|             "  attr_max %d" % struct.unpack( | ||||
|                 '<IIIII', superblock[1].data[4:4+20].ljust(20, b'\xff'))) | ||||
|  | ||||
|     if args.gstate and gstate: | ||||
|         print("gstate 0x%s" % ''.join('%02x' % c for c in gstate)) | ||||
|         tag = Tag(struct.unpack('<I', gstate[0:4].ljust(4, b'\xff'))[0]) | ||||
|         blocks = struct.unpack('<II', gstate[4:4+8].ljust(8, b'\xff')) | ||||
|     if tag.size or not tag.isvalid: | ||||
|         print("  orphans >=%d" % max(tag.size, 1)) | ||||
|         if tag.size: | ||||
|             print("  orphans %d" % tag.size) | ||||
|         if tag.type: | ||||
|             print("  move dir {%#x, %#x} id %d" % ( | ||||
|                 blocks[0], blocks[1], tag.id)) | ||||
|  | ||||
|     # print mdir info | ||||
|     if args.mdirs: | ||||
|         for i, dir in enumerate(dirs): | ||||
|             print("dir %s" % (json.dumps(dir[0].path) | ||||
|                 if hasattr(dir[0], 'path') else '(orphan)')) | ||||
|  | ||||
|             for j, mdir in enumerate(dir): | ||||
|             print("mdir {%#x, %#x} rev %d (was %d)%s%s" % ( | ||||
|                 mdir.blocks[0], mdir.blocks[1], mdir.rev, mdir.pair[1].rev, | ||||
|                 ' (corrupted!)' if not mdir else '', | ||||
|                 ' -> {%#x, %#x}' % struct.unpack('<II', mdir.tail.data) | ||||
|                 if mdir.tail else '')) | ||||
|                 print("mdir {%#x, %#x} rev %d%s" % ( | ||||
|                     mdir.blocks[0], mdir.blocks[1], mdir.rev, | ||||
|                     ' (corrupted)' if not mdir else '')) | ||||
|  | ||||
|                 f = io.StringIO() | ||||
|             if args.log: | ||||
|                 if args.tags: | ||||
|                     mdir.dump_tags(f, truncate=not args.no_truncate) | ||||
|                 elif args.log: | ||||
|                     mdir.dump_log(f, truncate=not args.no_truncate) | ||||
|                 elif args.all: | ||||
|                     mdir.dump_all(f, truncate=not args.no_truncate) | ||||
|                 else: | ||||
|                 mdir.dump_tags(f, truncate=not args.no_truncate) | ||||
|                     dumpentries(args, mdir, f) | ||||
|  | ||||
|                 lines = list(filter(None, f.getvalue().split('\n'))) | ||||
|                 for k, line in enumerate(lines): | ||||
| @@ -146,18 +217,15 @@ def main(args): | ||||
|                         '|', | ||||
|                         line)) | ||||
|  | ||||
|     errcode = 0 | ||||
|     for mdir in corrupted: | ||||
|         errcode = errcode or 1 | ||||
|         print("*** corrupted mdir {%#x, %#x}! ***" % ( | ||||
|             mdir.blocks[0], mdir.blocks[1])) | ||||
|     if cycle: | ||||
|         print("*** cycle detected! -> {%#x, %#x} ***" % (cycle[0], cycle[1])) | ||||
|  | ||||
|     if cycle: | ||||
|         errcode = errcode or 2 | ||||
|         print("*** cycle detected {%#x, %#x}! ***" % ( | ||||
|             cycle[0], cycle[1])) | ||||
|  | ||||
|     return errcode | ||||
|         return 2 | ||||
|     elif not all(mdir for dir in dirs for mdir in dir): | ||||
|         return 1 | ||||
|     else: | ||||
|         return 0; | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     import argparse | ||||
| @@ -170,14 +238,24 @@ if __name__ == "__main__": | ||||
|         help="Size of a block in bytes.") | ||||
|     parser.add_argument('block1', nargs='?', default=0, | ||||
|         type=lambda x: int(x, 0), | ||||
|         help="Optional first block address for finding the superblock.") | ||||
|         help="Optional first block address for finding the root.") | ||||
|     parser.add_argument('block2', nargs='?', default=1, | ||||
|         type=lambda x: int(x, 0), | ||||
|         help="Optional second block address for finding the superblock.") | ||||
|         help="Optional second block address for finding the root.") | ||||
|     parser.add_argument('-s', '--superblock', action='store_true', | ||||
|         help="Show contents of the superblock.") | ||||
|     parser.add_argument('-g', '--gstate', action='store_true', | ||||
|         help="Show contents of global-state.") | ||||
|     parser.add_argument('-m', '--mdirs', action='store_true', | ||||
|         help="Show contents of metadata-pairs/directories.") | ||||
|     parser.add_argument('-t', '--tags', action='store_true', | ||||
|         help="Show metadata tags instead of reconstructing entries.") | ||||
|     parser.add_argument('-l', '--log', action='store_true', | ||||
|         help="Show tags in log.") | ||||
|     parser.add_argument('-a', '--all', action='store_true', | ||||
|         help="Show all tags in log, included tags in corrupted commits.") | ||||
|     parser.add_argument('-d', '--data', action='store_true', | ||||
|         help="Also show the raw contents of files/attrs/tags.") | ||||
|     parser.add_argument('-T', '--no-truncate', action='store_true', | ||||
|         help="Show the full contents of files/attrs/tags.") | ||||
|         help="Don't truncate large amounts of data.") | ||||
|     sys.exit(main(parser.parse_args())) | ||||
|   | ||||
| @@ -231,7 +231,7 @@ class TestCase: | ||||
|                 ncmd.extend(['-ex', 'r']) | ||||
|                 if failure.assert_: | ||||
|                     ncmd.extend(['-ex', 'up 2']) | ||||
|             elif gdb == 'main': | ||||
|             elif gdb == 'start': | ||||
|                 ncmd.extend([ | ||||
|                     '-ex', 'b %s:%d' % (self.suite.path, self.code_lineno), | ||||
|                     '-ex', 'r']) | ||||
| @@ -760,7 +760,7 @@ if __name__ == "__main__": | ||||
|         help="Store disk image in a file.") | ||||
|     parser.add_argument('-b', '--build', action='store_true', | ||||
|         help="Only build the tests, do not execute.") | ||||
|     parser.add_argument('-g', '--gdb', choices=['init', 'main', 'assert'], | ||||
|     parser.add_argument('-g', '--gdb', choices=['init', 'start', 'assert'], | ||||
|         nargs='?', const='assert', | ||||
|         help="Drop into gdb on test failure.") | ||||
|     parser.add_argument('--no-internal', action='store_true', | ||||
|   | ||||
| @@ -323,90 +323,6 @@ code = ''' | ||||
|     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_ | ||||
| # on the geometry of the block device. But they are valuable. Eventually they | ||||
| # should be removed and replaced with generalized tests. | ||||
|   | ||||
| @@ -1,288 +0,0 @@ | ||||
| # 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_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     // change tail-pointer to invalid pointers | ||||
|     lfs_init(&lfs, &cfg) => 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_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||
| ''' | ||||
|  | ||||
| [[case]] # invalid dir pointer test | ||||
| define.INVALSET = [0x3, 0x1, 0x2] | ||||
| in = "lfs.c" | ||||
| code = ''' | ||||
|     // create littlefs | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     // make a dir | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "dir_here") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|     // change the dir pointer to be invalid | ||||
|     lfs_init(&lfs, &cfg) => 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_mount(&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_format(&lfs, &cfg) => 0; | ||||
|     // make a file | ||||
|     lfs_mount(&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_init(&lfs, &cfg) => 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_mount(&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_format(&lfs, &cfg) => 0; | ||||
|     // make a file | ||||
|     lfs_mount(&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_init(&lfs, &cfg) => 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]; | ||||
|     cfg.read(&cfg, 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)); | ||||
|     cfg.erase(&cfg, ctz.head) => 0; | ||||
|     cfg.prog(&cfg, 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_mount(&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_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     // create an invalid gstate | ||||
|     lfs_init(&lfs, &cfg) => 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_mount(&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_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     // change tail-pointer to point to ourself | ||||
|     lfs_init(&lfs, &cfg) => 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_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||
| ''' | ||||
|  | ||||
| [[case]] # metadata-pair threaded-list 2-length loop test | ||||
| in = "lfs.c" | ||||
| code = ''' | ||||
|     // create littlefs with child dir | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "child") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|     // find child | ||||
|     lfs_init(&lfs, &cfg) => 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_mount(&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_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "child") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|     // find child | ||||
|     lfs_init(&lfs, &cfg) => 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_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||
| ''' | ||||
| @@ -350,116 +350,117 @@ exhausted: | ||||
|     LFS_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]); | ||||
| ''' | ||||
|  | ||||
| [[case]] # test that we wear blocks roughly evenly | ||||
| define.LFS_ERASE_CYCLES = 0xffffffff | ||||
| define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster | ||||
| define.LFS_BLOCK_CYCLES = [5, 4, 3, 2, 1] | ||||
| define.CYCLES = 100 | ||||
| define.FILES = 10 | ||||
| if = 'LFS_BLOCK_CYCLES < CYCLES/10' | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "roadrunner") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|     uint32_t cycle = 0; | ||||
|     while (cycle < CYCLES) { | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         for (uint32_t i = 0; i < FILES; i++) { | ||||
|             // chose name, roughly random seed, and random 2^n size | ||||
|             sprintf(path, "roadrunner/test%d", i); | ||||
|             srand(cycle * i); | ||||
|             size = 1 << 4; //((rand() % 10)+2); | ||||
|  | ||||
|             lfs_file_open(&lfs, &file, path, | ||||
|                     LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|  | ||||
|             for (lfs_size_t j = 0; j < size; j++) { | ||||
|                 char c = 'a' + (rand() % 26); | ||||
|                 lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); | ||||
|                 assert(res == 1 || res == LFS_ERR_NOSPC); | ||||
|                 if (res == LFS_ERR_NOSPC) { | ||||
|                     err = lfs_file_close(&lfs, &file); | ||||
|                     assert(err == 0 || err == LFS_ERR_NOSPC); | ||||
|                     lfs_unmount(&lfs) => 0; | ||||
|                     goto exhausted; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             err = lfs_file_close(&lfs, &file); | ||||
|             assert(err == 0 || err == LFS_ERR_NOSPC); | ||||
|             if (err == LFS_ERR_NOSPC) { | ||||
|                 lfs_unmount(&lfs) => 0; | ||||
|                 goto exhausted; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         for (uint32_t i = 0; i < FILES; i++) { | ||||
|             // check for errors | ||||
|             sprintf(path, "roadrunner/test%d", i); | ||||
|             srand(cycle * i); | ||||
|             size = 1 << 4; //((rand() % 10)+2); | ||||
|  | ||||
|             lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|             for (lfs_size_t j = 0; j < size; j++) { | ||||
|                 char c = 'a' + (rand() % 26); | ||||
|                 char r; | ||||
|                 lfs_file_read(&lfs, &file, &r, 1) => 1; | ||||
|                 assert(r == c); | ||||
|             } | ||||
|  | ||||
|             lfs_file_close(&lfs, &file) => 0; | ||||
|         } | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|         cycle += 1; | ||||
|     } | ||||
|  | ||||
| exhausted: | ||||
|     // should still be readable | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     for (uint32_t i = 0; i < FILES; i++) { | ||||
|         // check for errors | ||||
|         sprintf(path, "roadrunner/test%d", i); | ||||
|         lfs_stat(&lfs, path, &info) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|     LFS_WARN("completed %d cycles", cycle); | ||||
|  | ||||
|     // check the wear on our block device | ||||
|     lfs_testbd_wear_t minwear = -1; | ||||
|     lfs_testbd_wear_t totalwear = 0; | ||||
|     lfs_testbd_wear_t maxwear = 0; | ||||
|     // skip 0 and 1 as superblock movement is intentionally avoided | ||||
|     for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) { | ||||
|         lfs_testbd_wear_t wear = lfs_testbd_getwear(&cfg, b); | ||||
|         printf("%08x: wear %d\n", b, wear); | ||||
|         assert(wear >= 0); | ||||
|         if (wear < minwear) { | ||||
|             minwear = wear; | ||||
|         } | ||||
|         if (wear > maxwear) { | ||||
|             maxwear = wear; | ||||
|         } | ||||
|         totalwear += wear; | ||||
|     } | ||||
|     lfs_testbd_wear_t avgwear = totalwear / LFS_BLOCK_COUNT; | ||||
|     LFS_WARN("max wear: %d cycles", maxwear); | ||||
|     LFS_WARN("avg wear: %d cycles", totalwear / LFS_BLOCK_COUNT); | ||||
|     LFS_WARN("min wear: %d cycles", minwear); | ||||
|  | ||||
|     // find standard deviation^2 | ||||
|     lfs_testbd_wear_t dev2 = 0; | ||||
|     for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) { | ||||
|         lfs_testbd_wear_t wear = lfs_testbd_getwear(&cfg, b); | ||||
|         assert(wear >= 0); | ||||
|         lfs_testbd_swear_t diff = wear - avgwear; | ||||
|         dev2 += diff*diff; | ||||
|     } | ||||
|     dev2 /= totalwear; | ||||
|     LFS_WARN("std dev^2: %d", dev2); | ||||
|     assert(dev2 < 8); | ||||
| ''' | ||||
| # TODO fixme | ||||
| #[[case]] # test that we wear blocks roughly evenly | ||||
| #define.LFS_ERASE_CYCLES = 0xffffffff | ||||
| #define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster | ||||
| #define.LFS_BLOCK_CYCLES = [5, 4, 3, 2, 1] | ||||
| #define.CYCLES = 100 | ||||
| #define.FILES = 10 | ||||
| #if = 'LFS_BLOCK_CYCLES < CYCLES/10' | ||||
| #code = ''' | ||||
| #    lfs_format(&lfs, &cfg) => 0; | ||||
| #    lfs_mount(&lfs, &cfg) => 0; | ||||
| #    lfs_mkdir(&lfs, "roadrunner") => 0; | ||||
| #    lfs_unmount(&lfs) => 0; | ||||
| # | ||||
| #    uint32_t cycle = 0; | ||||
| #    while (cycle < CYCLES) { | ||||
| #        lfs_mount(&lfs, &cfg) => 0; | ||||
| #        for (uint32_t i = 0; i < FILES; i++) { | ||||
| #            // chose name, roughly random seed, and random 2^n size | ||||
| #            sprintf(path, "roadrunner/test%d", i); | ||||
| #            srand(cycle * i); | ||||
| #            size = 1 << 4; //((rand() % 10)+2); | ||||
| # | ||||
| #            lfs_file_open(&lfs, &file, path, | ||||
| #                    LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
| # | ||||
| #            for (lfs_size_t j = 0; j < size; j++) { | ||||
| #                char c = 'a' + (rand() % 26); | ||||
| #                lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); | ||||
| #                assert(res == 1 || res == LFS_ERR_NOSPC); | ||||
| #                if (res == LFS_ERR_NOSPC) { | ||||
| #                    err = lfs_file_close(&lfs, &file); | ||||
| #                    assert(err == 0 || err == LFS_ERR_NOSPC); | ||||
| #                    lfs_unmount(&lfs) => 0; | ||||
| #                    goto exhausted; | ||||
| #                } | ||||
| #            } | ||||
| # | ||||
| #            err = lfs_file_close(&lfs, &file); | ||||
| #            assert(err == 0 || err == LFS_ERR_NOSPC); | ||||
| #            if (err == LFS_ERR_NOSPC) { | ||||
| #                lfs_unmount(&lfs) => 0; | ||||
| #                goto exhausted; | ||||
| #            } | ||||
| #        } | ||||
| # | ||||
| #        for (uint32_t i = 0; i < FILES; i++) { | ||||
| #            // check for errors | ||||
| #            sprintf(path, "roadrunner/test%d", i); | ||||
| #            srand(cycle * i); | ||||
| #            size = 1 << 4; //((rand() % 10)+2); | ||||
| # | ||||
| #            lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
| #            for (lfs_size_t j = 0; j < size; j++) { | ||||
| #                char c = 'a' + (rand() % 26); | ||||
| #                char r; | ||||
| #                lfs_file_read(&lfs, &file, &r, 1) => 1; | ||||
| #                assert(r == c); | ||||
| #            } | ||||
| # | ||||
| #            lfs_file_close(&lfs, &file) => 0; | ||||
| #        } | ||||
| #        lfs_unmount(&lfs) => 0; | ||||
| # | ||||
| #        cycle += 1; | ||||
| #    } | ||||
| # | ||||
| #exhausted: | ||||
| #    // should still be readable | ||||
| #    lfs_mount(&lfs, &cfg) => 0; | ||||
| #    for (uint32_t i = 0; i < FILES; i++) { | ||||
| #        // check for errors | ||||
| #        sprintf(path, "roadrunner/test%d", i); | ||||
| #        lfs_stat(&lfs, path, &info) => 0; | ||||
| #    } | ||||
| #    lfs_unmount(&lfs) => 0; | ||||
| # | ||||
| #    LFS_WARN("completed %d cycles", cycle); | ||||
| # | ||||
| #    // check the wear on our block device | ||||
| #    lfs_testbd_wear_t minwear = -1; | ||||
| #    lfs_testbd_wear_t totalwear = 0; | ||||
| #    lfs_testbd_wear_t maxwear = 0; | ||||
| #    // skip 0 and 1 as superblock movement is intentionally avoided | ||||
| #    for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) { | ||||
| #        lfs_testbd_wear_t wear = lfs_testbd_getwear(&cfg, b); | ||||
| #        printf("%08x: wear %d\n", b, wear); | ||||
| #        assert(wear >= 0); | ||||
| #        if (wear < minwear) { | ||||
| #            minwear = wear; | ||||
| #        } | ||||
| #        if (wear > maxwear) { | ||||
| #            maxwear = wear; | ||||
| #        } | ||||
| #        totalwear += wear; | ||||
| #    } | ||||
| #    lfs_testbd_wear_t avgwear = totalwear / LFS_BLOCK_COUNT; | ||||
| #    LFS_WARN("max wear: %d cycles", maxwear); | ||||
| #    LFS_WARN("avg wear: %d cycles", totalwear / LFS_BLOCK_COUNT); | ||||
| #    LFS_WARN("min wear: %d cycles", minwear); | ||||
| # | ||||
| #    // find standard deviation^2 | ||||
| #    lfs_testbd_wear_t dev2 = 0; | ||||
| #    for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) { | ||||
| #        lfs_testbd_wear_t wear = lfs_testbd_getwear(&cfg, b); | ||||
| #        assert(wear >= 0); | ||||
| #        lfs_testbd_swear_t diff = wear - avgwear; | ||||
| #        dev2 += diff*diff; | ||||
| #    } | ||||
| #    dev2 /= totalwear; | ||||
| #    LFS_WARN("std dev^2: %d", dev2); | ||||
| #    assert(dev2 < 8); | ||||
| #''' | ||||
|  | ||||
|   | ||||
| @@ -59,7 +59,7 @@ code = ''' | ||||
| [[case]] # reentrant testing for orphans, basically just spam mkdir/remove | ||||
| reentrant = true | ||||
| # TODO fix this case, caused by non-DAG trees | ||||
| if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)' | ||||
| #if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)' | ||||
| define = [ | ||||
|     {FILES=6,  DEPTH=1, CYCLES=20}, | ||||
|     {FILES=26, DEPTH=1, CYCLES=20}, | ||||
|   | ||||
| @@ -31,7 +31,7 @@ code = ''' | ||||
|         for (int i = 0; i < COUNT; i++) { | ||||
|             sprintf(path, "test%03d_loooooooooooooooooong_name", i); | ||||
|             lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|             strcmp(info.name, path) => 0; | ||||
|             assert(strcmp(info.name, path) == 0); | ||||
|         } | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|         lfs_dir_close(&lfs, &dir) => 0; | ||||
| @@ -54,7 +54,7 @@ code = ''' | ||||
|     for (int i = 0; i < COUNT; i++) { | ||||
|         sprintf(path, "test%03d_loooooooooooooooooong_name", i); | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         strcmp(info.name, path) => 0; | ||||
|         assert(strcmp(info.name, path) == 0); | ||||
|     } | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
| @@ -97,7 +97,7 @@ code = ''' | ||||
|         for (int i = 0; i < COUNT; i++) { | ||||
|             sprintf(path, "test%03d_loooooooooooooooooong_name", i); | ||||
|             lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|             strcmp(info.name, path) => 0; | ||||
|             assert(strcmp(info.name,  path) == 0); | ||||
|             info.size => 0; | ||||
|  | ||||
|             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); | ||||
| @@ -113,7 +113,7 @@ code = ''' | ||||
|         for (int i = 0; i < COUNT; i++) { | ||||
|             sprintf(path, "test%03d_loooooooooooooooooong_name", i); | ||||
|             lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|             strcmp(info.name, path) => 0; | ||||
|             assert(strcmp(info.name,  path) == 0); | ||||
|             info.size => 2; | ||||
|  | ||||
|             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); | ||||
| @@ -129,7 +129,7 @@ code = ''' | ||||
|         for (int i = 0; i < COUNT; i++) { | ||||
|             sprintf(path, "test%03d_loooooooooooooooooong_name", i); | ||||
|             lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|             strcmp(info.name, path) => 0; | ||||
|             assert(strcmp(info.name,  path) == 0); | ||||
|             info.size => 2; | ||||
|         } | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
| @@ -143,12 +143,90 @@ code = ''' | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # non-DAG tree test | ||||
| define.LFS_BLOCK_CYCLES = [8, 1] | ||||
| define.N = [10, 100, 1000] | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     // first create directories | ||||
|     lfs_mkdir(&lfs, "child_1") => 0; | ||||
|     lfs_mkdir(&lfs, "child_2") => 0; | ||||
|     // then move the second child under the first, | ||||
|     // this creates a cycle since the second child should have been | ||||
|     // inserted before the first | ||||
|     lfs_rename(&lfs, "child_2", "child_1/child_2") => 0; | ||||
|     // now try to force second child to relocate | ||||
|     lfs_file_open(&lfs, &file, "child_1/child_2/grandchild", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     size = 0; | ||||
|     for (int i = 0; i < N; i++) { | ||||
|         lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; | ||||
|         sprintf((char*)buffer, "%d", i); | ||||
|         size = strlen((char*)buffer); | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         lfs_file_sync(&lfs, &file) => 0; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs); | ||||
|  | ||||
|     // check that nothing broke | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir, "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     assert(strcmp(info.name, ".") == 0); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     assert(strcmp(info.name, "..") == 0); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     assert(strcmp(info.name, "child_1") == 0); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|      | ||||
|     lfs_dir_open(&lfs, &dir, "/child_1") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     assert(strcmp(info.name, ".") == 0); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     assert(strcmp(info.name, "..") == 0); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     assert(strcmp(info.name, "child_2") == 0); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir, "/child_1/child_2") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     assert(strcmp(info.name, ".") == 0); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     assert(strcmp(info.name, "..") == 0); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     assert(info.type == LFS_TYPE_REG); | ||||
|     assert(strcmp(info.name, "grandchild") == 0); | ||||
|     assert(info.size == size); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "child_1/child_2/grandchild", | ||||
|             LFS_O_RDONLY) => 0; | ||||
|     uint8_t rbuffer[1024]; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, sizeof(rbuffer)) => size; | ||||
|     assert(memcmp(rbuffer, buffer, size) == 0); | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # reentrant testing for relocations, this is the same as the | ||||
|          # orphan testing, except here we also set block_cycles so that | ||||
|          # almost every tree operation needs a relocation | ||||
| reentrant = true | ||||
| # TODO fix this case, caused by non-DAG trees | ||||
| if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)' | ||||
| #if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)' | ||||
| define = [ | ||||
|     {FILES=6,  DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1}, | ||||
|     {FILES=26, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1}, | ||||
| @@ -210,7 +288,7 @@ code = ''' | ||||
| [[case]] # reentrant testing for relocations, but now with random renames! | ||||
| reentrant = true | ||||
| # TODO fix this case, caused by non-DAG trees | ||||
| if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)' | ||||
| #if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)' | ||||
| define = [ | ||||
|     {FILES=6,  DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1}, | ||||
|     {FILES=26, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1}, | ||||
|   | ||||
| @@ -27,55 +27,41 @@ code = ''' | ||||
| ''' | ||||
|  | ||||
| [[case]] # expanding superblock | ||||
| define.LFS_BLOCK_CYCLES = [32, 33, 1] | ||||
| define.BLOCK_CYCLES = [32, 33, 1] | ||||
| define.N = [10, 100, 1000] | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     for (int i = 0; i < N; i++) { | ||||
|         lfs_file_open(&lfs, &file, "dummy", | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs_mkdir(&lfs, "dummy") => 0; | ||||
|         lfs_stat(&lfs, "dummy", &info) => 0; | ||||
|         assert(strcmp(info.name, "dummy") == 0); | ||||
|         assert(info.type == LFS_TYPE_REG); | ||||
|         lfs_remove(&lfs, "dummy") => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|     // one last check after power-cycle | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "dummy", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_mkdir(&lfs, "dummy") => 0; | ||||
|     lfs_stat(&lfs, "dummy", &info) => 0; | ||||
|     assert(strcmp(info.name, "dummy") == 0); | ||||
|     assert(info.type == LFS_TYPE_REG); | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # expanding superblock with power cycle | ||||
| define.LFS_BLOCK_CYCLES = [32, 33, 1] | ||||
| define.BLOCK_CYCLES = [32, 33, 1] | ||||
| define.N = [10, 100, 1000] | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     for (int i = 0; i < N; i++) { | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         // remove lingering dummy? | ||||
|         err = lfs_stat(&lfs, "dummy", &info); | ||||
|         err = lfs_remove(&lfs, "dummy"); | ||||
|         assert(err == 0 || (err == LFS_ERR_NOENT && i == 0)); | ||||
|         if (!err) { | ||||
|             assert(strcmp(info.name, "dummy") == 0); | ||||
|             assert(info.type == LFS_TYPE_REG); | ||||
|             lfs_remove(&lfs, "dummy") => 0; | ||||
|         } | ||||
|          | ||||
|         lfs_file_open(&lfs, &file, "dummy", | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs_mkdir(&lfs, "dummy") => 0; | ||||
|         lfs_stat(&lfs, "dummy", &info) => 0; | ||||
|         assert(strcmp(info.name, "dummy") == 0); | ||||
|         assert(info.type == LFS_TYPE_REG); | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|     } | ||||
|  | ||||
| @@ -83,12 +69,11 @@ code = ''' | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "dummy", &info) => 0; | ||||
|     assert(strcmp(info.name, "dummy") == 0); | ||||
|     assert(info.type == LFS_TYPE_REG); | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # reentrant expanding superblock | ||||
| define.LFS_BLOCK_CYCLES = [2, 1] | ||||
| define.BLOCK_CYCLES = [2, 1] | ||||
| define.N = 24 | ||||
| reentrant = true | ||||
| code = ''' | ||||
| @@ -100,20 +85,12 @@ code = ''' | ||||
|  | ||||
|     for (int i = 0; i < N; i++) { | ||||
|         // remove lingering dummy? | ||||
|         err = lfs_stat(&lfs, "dummy", &info); | ||||
|         err = lfs_remove(&lfs, "dummy"); | ||||
|         assert(err == 0 || (err == LFS_ERR_NOENT && i == 0)); | ||||
|         if (!err) { | ||||
|             assert(strcmp(info.name, "dummy") == 0); | ||||
|             assert(info.type == LFS_TYPE_REG); | ||||
|             lfs_remove(&lfs, "dummy") => 0; | ||||
|         } | ||||
|          | ||||
|         lfs_file_open(&lfs, &file, "dummy", | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs_mkdir(&lfs, "dummy") => 0; | ||||
|         lfs_stat(&lfs, "dummy", &info) => 0; | ||||
|         assert(strcmp(info.name, "dummy") == 0); | ||||
|         assert(info.type == LFS_TYPE_REG); | ||||
|     } | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| @@ -122,6 +99,5 @@ code = ''' | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "dummy", &info) => 0; | ||||
|     assert(strcmp(info.name, "dummy") == 0); | ||||
|     assert(info.type == LFS_TYPE_REG); | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| ''' | ||||
|   | ||||
		Reference in New Issue
	
	Block a user