mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-11-01 08:48:31 +01:00 
			
		
		
		
	Compare commits
	
		
			60 Commits
		
	
	
		
			test-revam
			...
			script-fix
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 2cd895dbba | ||
|  | 1a59954ec6 | ||
|  | 6a7012774d | ||
|  | 288a5cbc8d | ||
|  | 5783eea0de | ||
|  | 2bb523421e | ||
|  | 7388b2938a | ||
|  | ce425a56c3 | ||
|  | a99a93fb27 | ||
|  | 45afded784 | ||
|  | 00a9ba7826 | ||
|  | fc6988c7c3 | ||
|  | d0f055d321 | ||
|  | b9fa33f9bc | ||
|  | 2efebf8e9b | ||
|  | 754b4c3cda | ||
|  | 584eb26efc | ||
|  | 008ebc37df | ||
|  | 66272067ab | ||
|  | e273a82679 | ||
|  | 1dc6ae94b9 | ||
|  | 817ef02d24 | ||
|  | b8dcf10974 | ||
|  | 0aba71d0d6 | ||
|  | 0ea2871e24 | ||
|  | d04c1392c0 | ||
|  | f215027fd4 | ||
|  | 1ae4b36f2a | ||
|  | 480cdd9f81 | ||
|  | 6303558aee | ||
|  | 4bd653dd00 | ||
|  | 8e6826c4e2 | ||
|  | 10ac6b9cf0 | ||
|  | 87a2cb0e41 | ||
|  | 6d0ec5e851 | ||
|  | 4c9146ea53 | ||
|  | 5a9f38df01 | ||
|  | 1b033e9ab6 | ||
|  | a049f1318e | ||
|  | 7257681f5d | ||
|  | 2da340af69 | ||
|  | 02881e591b | ||
|  | 38024d5a17 | ||
|  | 4a9bac4418 | ||
|  | 6121495444 | ||
|  | 6372f515fe | ||
|  | 6622f3deee | ||
|  | 5137e4b0ba | ||
|  | ff84902970 | ||
|  | 01e42abd10 | ||
|  | f9dbec3d92 | ||
|  | f17d3d7eba | ||
|  | 5e5b5d8572 | ||
|  | d498b9fb31 | ||
|  | 4677421aba | ||
|  | 64f70f51b0 | ||
|  | 4fb188369d | ||
|  | c8e9a64a21 | ||
|  | 626006af0c | ||
|  | 5a12c443b8 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -8,3 +8,5 @@ blocks/ | ||||
| lfs | ||||
| test.c | ||||
| tests/*.toml.* | ||||
| scripts/__pycache__ | ||||
| .gdb_history | ||||
|   | ||||
							
								
								
									
										32
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -208,6 +208,38 @@ jobs: | ||||
|     script: | ||||
|       - make test TFLAGS+="-k --valgrind" | ||||
|  | ||||
|   # test compilation in read-only mode | ||||
|   - stage: test | ||||
|     env: | ||||
|       - NAME=littlefs-readonly | ||||
|       - CC="arm-linux-gnueabi-gcc --static -mthumb" | ||||
|       - CFLAGS="-Werror -DLFS_READONLY" | ||||
|     if: branch !~ -prefix$ | ||||
|     install: | ||||
|       - *install-common | ||||
|       - sudo apt-get install | ||||
|             gcc-arm-linux-gnueabi | ||||
|             libc6-dev-armel-cross | ||||
|       - arm-linux-gnueabi-gcc --version | ||||
|     # report-size will compile littlefs and report the size | ||||
|     script: [*report-size] | ||||
|  | ||||
|   # test compilation in thread-safe mode | ||||
|   - stage: test | ||||
|     env: | ||||
|       - NAME=littlefs-threadsafe | ||||
|       - CC="arm-linux-gnueabi-gcc --static -mthumb" | ||||
|       - CFLAGS="-Werror -DLFS_THREADSAFE" | ||||
|     if: branch !~ -prefix$ | ||||
|     install: | ||||
|       - *install-common | ||||
|       - sudo apt-get install | ||||
|             gcc-arm-linux-gnueabi | ||||
|             libc6-dev-armel-cross | ||||
|       - arm-linux-gnueabi-gcc --version | ||||
|     # report-size will compile littlefs and report the size | ||||
|     script: [*report-size] | ||||
|  | ||||
|   # self-host with littlefs-fuse for fuzz test | ||||
|   - stage: test | ||||
|     env: | ||||
|   | ||||
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @@ -26,8 +26,6 @@ endif | ||||
| override CFLAGS += -I. | ||||
| override CFLAGS += -std=c99 -Wall -pedantic | ||||
| override CFLAGS += -Wextra -Wshadow -Wjump-misses-init -Wundef | ||||
| # Remove missing-field-initializers because of GCC bug | ||||
| override CFLAGS += -Wno-missing-field-initializers | ||||
|  | ||||
| ifdef VERBOSE | ||||
| override TFLAGS += -v | ||||
|   | ||||
| @@ -115,6 +115,9 @@ 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. | ||||
| @@ -218,6 +221,11 @@ License Identifiers that are here available: http://spdx.org/licenses/ | ||||
| - [littlefs-js] - A javascript wrapper for littlefs. I'm not sure why you would | ||||
|   want this, but it is handy for demos.  You can see it in action | ||||
|   [here][littlefs-js-demo]. | ||||
|    | ||||
| - [littlefs-python] - A Python wrapper for littlefs. The project allows you | ||||
|   to create images of the filesystem on your PC. Check if littlefs will fit | ||||
|   your needs, create images for a later download to the target memory or | ||||
|   inspect the content of a binary image of the target memory. | ||||
|  | ||||
| - [mklfs] - A command line tool built by the [Lua RTOS] guys for making | ||||
|   littlefs images from a host PC. Supports Windows, Mac OS, and Linux. | ||||
| @@ -247,3 +255,4 @@ License Identifiers that are here available: http://spdx.org/licenses/ | ||||
| [LittleFileSystem]: https://os.mbed.com/docs/mbed-os/v5.12/apis/littlefilesystem.html | ||||
| [SPIFFS]: https://github.com/pellepl/spiffs | ||||
| [Dhara]: https://github.com/dlbeer/dhara | ||||
| [littlefs-python]: https://pypi.org/project/littlefs-python/ | ||||
|   | ||||
							
								
								
									
										18
									
								
								SPEC.md
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								SPEC.md
									
									
									
									
									
								
							| @@ -289,8 +289,8 @@ Layout of the name tag: | ||||
| ``` | ||||
|         tag                          data | ||||
| [--      32      --][---        variable length        ---] | ||||
| [1| 3| 8 | 10 | 10 ][---            (size)             ---] | ||||
|  ^  ^  ^    ^    ^- size               ^- file name | ||||
| [1| 3| 8 | 10 | 10 ][---          (size * 8)           ---] | ||||
|  ^  ^  ^    ^    ^- size                   ^- file name | ||||
|  |  |  |    '------ id | ||||
|  |  |  '----------- file type | ||||
|  |  '-------------- type1 (0x0) | ||||
| @@ -470,8 +470,8 @@ Layout of the inline-struct tag: | ||||
| ``` | ||||
|         tag                          data | ||||
| [--      32      --][---        variable length        ---] | ||||
| [1|- 11 -| 10 | 10 ][---            (size)             ---] | ||||
|  ^    ^     ^    ^- size               ^- inline data | ||||
| [1|- 11 -| 10 | 10 ][---           (size * 8)          ---] | ||||
|  ^    ^     ^    ^- size                    ^- inline data | ||||
|  |    |     '------ id | ||||
|  |    '------------ type (0x201) | ||||
|  '----------------- valid bit | ||||
| @@ -556,8 +556,8 @@ Layout of the user-attr tag: | ||||
| ``` | ||||
|         tag                          data | ||||
| [--      32      --][---        variable length        ---] | ||||
| [1| 3| 8 | 10 | 10 ][---            (size)             ---] | ||||
|  ^  ^  ^    ^    ^- size               ^- attr data | ||||
| [1| 3| 8 | 10 | 10 ][---           (size * 8)          ---] | ||||
|  ^  ^  ^    ^    ^- size                    ^- attr data | ||||
|  |  |  |    '------ id | ||||
|  |  |  '----------- attr type | ||||
|  |  '-------------- type1 (0x3) | ||||
| @@ -764,9 +764,9 @@ Layout of the CRC tag: | ||||
| ``` | ||||
|         tag                                    data | ||||
| [--      32      --][--      32      --|---        variable length        ---] | ||||
| [1| 3| 8 | 10 | 10 ][--      32      --|---            (size)             ---] | ||||
|  ^  ^  ^    ^    ^            ^- crc                      ^- padding | ||||
|  |  |  |    |    '- size (12) | ||||
| [1| 3| 8 | 10 | 10 ][--      32      --|---        (size * 8 - 32)        ---] | ||||
|  ^  ^  ^    ^    ^            ^- crc                             ^- padding | ||||
|  |  |  |    |    '- size | ||||
|  |  |  |    '------ 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_TRACE("lfs_filebd_createcfg(%p {.context=%p, " | ||||
|     LFS_FILEBD_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_TRACE("lfs_filebd_createcfg -> %d", err); | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_createcfg -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     LFS_TRACE("lfs_filebd_createcfg -> %d", 0); | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_createcfg -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_filebd_create(const struct lfs_config *cfg, const char *path) { | ||||
|     LFS_TRACE("lfs_filebd_create(%p {.context=%p, " | ||||
|     LFS_FILEBD_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,26 +51,27 @@ 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_TRACE("lfs_filebd_create -> %d", err); | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_create -> %d", err); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int lfs_filebd_destroy(const struct lfs_config *cfg) { | ||||
|     LFS_TRACE("lfs_filebd_destroy(%p)", (void*)cfg); | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_destroy(%p)", (void*)cfg); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
|     int err = close(bd->fd); | ||||
|     if (err < 0) { | ||||
|         err = -errno; | ||||
|         LFS_TRACE("lfs_filebd_destroy -> %d", err); | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_destroy -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|     LFS_TRACE("lfs_filebd_destroy -> %d", 0); | ||||
|     LFS_FILEBD_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_TRACE("lfs_filebd_read(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_read(%p, " | ||||
|                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
|  | ||||
| @@ -89,24 +90,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_TRACE("lfs_filebd_read -> %d", err); | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     ssize_t res2 = read(bd->fd, buffer, size); | ||||
|     if (res2 < 0) { | ||||
|         int err = -errno; | ||||
|         LFS_TRACE("lfs_filebd_read -> %d", err); | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     LFS_TRACE("lfs_filebd_read -> %d", 0); | ||||
|     LFS_FILEBD_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_TRACE("lfs_filebd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
|  | ||||
| @@ -121,7 +122,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_TRACE("lfs_filebd_prog -> %d", err); | ||||
|             LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
| @@ -130,7 +131,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_TRACE("lfs_filebd_prog -> %d", err); | ||||
|                 LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
| @@ -143,23 +144,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_TRACE("lfs_filebd_prog -> %d", err); | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     ssize_t res2 = write(bd->fd, buffer, size); | ||||
|     if (res2 < 0) { | ||||
|         int err = -errno; | ||||
|         LFS_TRACE("lfs_filebd_prog -> %d", err); | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     LFS_TRACE("lfs_filebd_prog -> %d", 0); | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|     LFS_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
|  | ||||
|     // check if erase is valid | ||||
| @@ -170,7 +171,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_TRACE("lfs_filebd_erase -> %d", err); | ||||
|             LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err); | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
| @@ -178,27 +179,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_TRACE("lfs_filebd_erase -> %d", err); | ||||
|                 LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err); | ||||
|                 return err; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     LFS_TRACE("lfs_filebd_erase -> %d", 0); | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_filebd_sync(const struct lfs_config *cfg) { | ||||
|     LFS_TRACE("lfs_filebd_sync(%p)", (void*)cfg); | ||||
|     LFS_FILEBD_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_TRACE("lfs_filebd_sync -> %d", 0); | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     LFS_TRACE("lfs_filebd_sync -> %d", 0); | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -15,6 +15,14 @@ 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_TRACE("lfs_rambd_createcfg(%p {.context=%p, " | ||||
|     LFS_RAMBD_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_TRACE("lfs_rambd_createcfg -> %d", LFS_ERR_NOMEM); | ||||
|             LFS_RAMBD_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_TRACE("lfs_rambd_createcfg -> %d", 0); | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_rambd_create(const struct lfs_config *cfg) { | ||||
|     LFS_TRACE("lfs_rambd_create(%p {.context=%p, " | ||||
|     LFS_RAMBD_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,24 +53,25 @@ 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_TRACE("lfs_rambd_create -> %d", err); | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_create -> %d", err); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int lfs_rambd_destroy(const struct lfs_config *cfg) { | ||||
|     LFS_TRACE("lfs_rambd_destroy(%p)", (void*)cfg); | ||||
|     LFS_RAMBD_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_TRACE("lfs_rambd_destroy -> %d", 0); | ||||
|     LFS_RAMBD_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_TRACE("lfs_rambd_read(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_read(%p, " | ||||
|                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_rambd_t *bd = cfg->context; | ||||
|  | ||||
| @@ -82,13 +83,14 @@ 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_TRACE("lfs_rambd_read -> %d", 0); | ||||
|     LFS_RAMBD_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_TRACE("lfs_rambd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_prog(%p, " | ||||
|                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_rambd_t *bd = cfg->context; | ||||
|  | ||||
| @@ -108,12 +110,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_TRACE("lfs_rambd_prog -> %d", 0); | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_prog -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_rambd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|     LFS_TRACE("lfs_rambd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     lfs_rambd_t *bd = cfg->context; | ||||
|  | ||||
|     // check if erase is valid | ||||
| @@ -125,14 +127,14 @@ int lfs_rambd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|                 bd->cfg->erase_value, cfg->block_size); | ||||
|     } | ||||
|  | ||||
|     LFS_TRACE("lfs_rambd_erase -> %d", 0); | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_erase -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_rambd_sync(const struct lfs_config *cfg) { | ||||
|     LFS_TRACE("lfs_rambd_sync(%p)", (void*)cfg); | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_sync(%p)", (void*)cfg); | ||||
|     // sync does nothing because we aren't backed by anything real | ||||
|     (void)cfg; | ||||
|     LFS_TRACE("lfs_rambd_sync -> %d", 0); | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_sync -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -15,6 +15,14 @@ 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_TRACE("lfs_testbd_createcfg(%p {.context=%p, " | ||||
|     LFS_TESTBD_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_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM); | ||||
|                 LFS_TESTBD_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_TRACE("lfs_testbd_createcfg -> %d", err); | ||||
|         LFS_TESTBD_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_TRACE("lfs_testbd_createcfg -> %d", err); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
| } | ||||
|  | ||||
| int lfs_testbd_create(const struct lfs_config *cfg, const char *path) { | ||||
|     LFS_TRACE("lfs_testbd_create(%p {.context=%p, " | ||||
|     LFS_TESTBD_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_TRACE("lfs_testbd_create -> %d", err); | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_create -> %d", err); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_destroy(const struct lfs_config *cfg) { | ||||
|     LFS_TRACE("lfs_testbd_destroy(%p)", (void*)cfg); | ||||
|     LFS_TESTBD_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_TRACE("lfs_testbd_destroy -> %d", err); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err); | ||||
|         return err; | ||||
|     } else { | ||||
|         int err = lfs_rambd_destroy(cfg); | ||||
|         LFS_TRACE("lfs_testbd_destroy -> %d", err); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
| } | ||||
| @@ -145,7 +145,8 @@ 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_TRACE("lfs_testbd_read(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_read(%p, " | ||||
|                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|  | ||||
| @@ -157,19 +158,20 @@ 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_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT); | ||||
|         return LFS_ERR_CORRUPT; | ||||
|     } | ||||
|  | ||||
|     // read | ||||
|     int err = lfs_testbd_rawread(cfg, block, off, buffer, size); | ||||
|     LFS_TRACE("lfs_testbd_read -> %d", err); | ||||
|     LFS_TESTBD_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_TRACE("lfs_testbd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_prog(%p, " | ||||
|                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|  | ||||
| @@ -182,13 +184,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_TRACE("lfs_testbd_prog -> %d", LFS_ERR_CORRUPT); | ||||
|             LFS_TESTBD_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_TRACE("lfs_testbd_prog -> %d", 0); | ||||
|             LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0); | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
| @@ -196,7 +198,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_TRACE("lfs_testbd_prog -> %d", err); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
| @@ -205,18 +207,18 @@ int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         bd->power_cycles -= 1; | ||||
|         if (bd->power_cycles == 0) { | ||||
|             // sync to make sure we persist the last changes | ||||
|             assert(lfs_testbd_rawsync(cfg) == 0); | ||||
|             LFS_ASSERT(lfs_testbd_rawsync(cfg) == 0); | ||||
|             // simulate power loss | ||||
|             exit(33); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     LFS_TRACE("lfs_testbd_prog -> %d", 0); | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|     LFS_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|  | ||||
|     // check if erase is valid | ||||
| @@ -227,11 +229,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_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT); | ||||
|                 LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT); | ||||
|                 return LFS_ERR_CORRUPT; | ||||
|             } else if (bd->cfg->badblock_behavior == | ||||
|                     LFS_TESTBD_BADBLOCK_ERASENOOP) { | ||||
|                 LFS_TRACE("lfs_testbd_erase -> %d", 0); | ||||
|                 LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", 0); | ||||
|                 return 0; | ||||
|             } | ||||
|         } else { | ||||
| @@ -243,7 +245,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_TRACE("lfs_testbd_erase -> %d", err); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
| @@ -252,20 +254,20 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|         bd->power_cycles -= 1; | ||||
|         if (bd->power_cycles == 0) { | ||||
|             // sync to make sure we persist the last changes | ||||
|             assert(lfs_testbd_rawsync(cfg) == 0); | ||||
|             LFS_ASSERT(lfs_testbd_rawsync(cfg) == 0); | ||||
|             // simulate power loss | ||||
|             exit(33); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     LFS_TRACE("lfs_testbd_prog -> %d", 0); | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_sync(const struct lfs_config *cfg) { | ||||
|     LFS_TRACE("lfs_testbd_sync(%p)", (void*)cfg); | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_sync(%p)", (void*)cfg); | ||||
|     int err = lfs_testbd_rawsync(cfg); | ||||
|     LFS_TRACE("lfs_testbd_sync -> %d", err); | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_sync -> %d", err); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| @@ -273,20 +275,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_TRACE("lfs_testbd_getwear(%p, %"PRIu32")", (void*)cfg, block); | ||||
|     LFS_TESTBD_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_TRACE("lfs_testbd_getwear -> %"PRIu32, bd->wear[block]); | ||||
|     LFS_TESTBD_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_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)cfg, block); | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)cfg, block); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|  | ||||
|     // check if block is valid | ||||
| @@ -295,6 +297,6 @@ int lfs_testbd_setwear(const struct lfs_config *cfg, | ||||
|  | ||||
|     bd->wear[block] = wear; | ||||
|  | ||||
|     LFS_TRACE("lfs_testbd_setwear -> %d", 0); | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_setwear -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -19,6 +19,13 @@ 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). | ||||
|   | ||||
							
								
								
									
										50
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -9,6 +9,7 @@ | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| #include "lfs_util.h" | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" | ||||
| @@ -21,7 +22,7 @@ extern "C" | ||||
| // Software library version | ||||
| // Major (top-nibble), incremented on backwards incompatible changes | ||||
| // Minor (bottom-nibble), incremented on feature additions | ||||
| #define LFS_VERSION 0x00020001 | ||||
| #define LFS_VERSION 0x00020003 | ||||
| #define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) | ||||
| #define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >>  0)) | ||||
|  | ||||
| @@ -111,34 +112,37 @@ enum lfs_type { | ||||
|     LFS_TYPE_INLINESTRUCT   = 0x201, | ||||
|     LFS_TYPE_SOFTTAIL       = 0x600, | ||||
|     LFS_TYPE_HARDTAIL       = 0x601, | ||||
|     LFS_TYPE_BRANCH         = 0x681, | ||||
|     LFS_TYPE_MOVESTATE      = 0x7ff, | ||||
|  | ||||
|     // internal chip sources | ||||
|     LFS_FROM_NOOP           = 0x000, | ||||
|     LFS_FROM_MOVE           = 0x101, | ||||
|     LFS_FROM_DROP           = 0x102, | ||||
|     LFS_FROM_USERATTRS      = 0x103, | ||||
|     LFS_FROM_USERATTRS      = 0x102, | ||||
| }; | ||||
|  | ||||
| // File open flags | ||||
| enum lfs_open_flags { | ||||
|     // open flags | ||||
|     LFS_O_RDONLY = 1,         // Open a file as read only | ||||
| #ifndef LFS_READONLY | ||||
|     LFS_O_WRONLY = 2,         // Open a file as write only | ||||
|     LFS_O_RDWR   = 3,         // Open a file as read and write | ||||
|     LFS_O_CREAT  = 0x0100,    // Create a file if it does not exist | ||||
|     LFS_O_EXCL   = 0x0200,    // Fail if a file already exists | ||||
|     LFS_O_TRUNC  = 0x0400,    // Truncate the existing file to zero size | ||||
|     LFS_O_APPEND = 0x0800,    // Move to end of file on every write | ||||
| #endif | ||||
|  | ||||
|     // internally used flags | ||||
| #ifndef LFS_READONLY | ||||
|     LFS_F_DIRTY   = 0x010000, // File does not match storage | ||||
|     LFS_F_WRITING = 0x020000, // File has been written since last flush | ||||
| #endif | ||||
|     LFS_F_READING = 0x040000, // File has been read since last flush | ||||
|     LFS_F_ERRED   = 0x080000, // An error occured during write | ||||
| #ifndef LFS_READONLY | ||||
|     LFS_F_ERRED   = 0x080000, // An error occurred during write | ||||
| #endif | ||||
|     LFS_F_INLINE  = 0x100000, // Currently inlined in directory entry | ||||
|     LFS_F_OPENED  = 0x200000, // File has been opened | ||||
| }; | ||||
|  | ||||
| // File seek flags | ||||
| @@ -176,6 +180,16 @@ struct lfs_config { | ||||
|     // are propogated to the user. | ||||
|     int (*sync)(const struct lfs_config *c); | ||||
|  | ||||
| #ifdef LFS_THREADSAFE | ||||
|     // Lock the underlying block device. Negative error codes | ||||
|     // are propogated to the user. | ||||
|     int (*lock)(const struct lfs_config *c); | ||||
|  | ||||
|     // Unlock the underlying block device. Negative error codes | ||||
|     // are propogated to the user. | ||||
|     int (*unlock)(const struct lfs_config *c); | ||||
| #endif | ||||
|  | ||||
|     // Minimum size of a block read. All read operations will be a | ||||
|     // multiple of this value. | ||||
|     lfs_size_t read_size; | ||||
| @@ -312,11 +326,8 @@ typedef struct lfs_mdir { | ||||
|     uint32_t etag; | ||||
|     uint16_t count; | ||||
|     bool erased; | ||||
|     bool first; // TODO come on | ||||
|     bool split; | ||||
|     bool mustrelocate; // TODO not great either | ||||
|     lfs_block_t tail[2]; | ||||
|     lfs_block_t branch[2]; | ||||
| } lfs_mdir_t; | ||||
|  | ||||
| // littlefs directory type | ||||
| @@ -371,9 +382,6 @@ typedef struct lfs { | ||||
|     lfs_cache_t pcache; | ||||
|  | ||||
|     lfs_block_t root[2]; | ||||
|     lfs_block_t relocate_tail[2]; | ||||
|     lfs_block_t relocate_end[2]; | ||||
|     bool relocate_do_hack; // TODO fixme | ||||
|     struct lfs_mlist { | ||||
|         struct lfs_mlist *next; | ||||
|         uint16_t id; | ||||
| @@ -407,6 +415,7 @@ typedef struct lfs { | ||||
|  | ||||
| /// Filesystem functions /// | ||||
|  | ||||
| #ifndef LFS_READONLY | ||||
| // Format a block device with the littlefs | ||||
| // | ||||
| // Requires a littlefs object and config struct. This clobbers the littlefs | ||||
| @@ -415,6 +424,7 @@ typedef struct lfs { | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_format(lfs_t *lfs, const struct lfs_config *config); | ||||
| #endif | ||||
|  | ||||
| // Mounts a littlefs | ||||
| // | ||||
| @@ -434,12 +444,15 @@ int lfs_unmount(lfs_t *lfs); | ||||
|  | ||||
| /// General operations /// | ||||
|  | ||||
| #ifndef LFS_READONLY | ||||
| // Removes a file or directory | ||||
| // | ||||
| // If removing a directory, the directory must be empty. | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_remove(lfs_t *lfs, const char *path); | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_READONLY | ||||
| // Rename or move a file or directory | ||||
| // | ||||
| // If the destination exists, it must match the source in type. | ||||
| @@ -447,6 +460,7 @@ int lfs_remove(lfs_t *lfs, const char *path); | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath); | ||||
| #endif | ||||
|  | ||||
| // Find info about a file or directory | ||||
| // | ||||
| @@ -469,6 +483,7 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info); | ||||
| lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, | ||||
|         uint8_t type, void *buffer, lfs_size_t size); | ||||
|  | ||||
| #ifndef LFS_READONLY | ||||
| // Set custom attributes | ||||
| // | ||||
| // Custom attributes are uniquely identified by an 8-bit type and limited | ||||
| @@ -478,13 +493,16 @@ lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_setattr(lfs_t *lfs, const char *path, | ||||
|         uint8_t type, const void *buffer, lfs_size_t size); | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_READONLY | ||||
| // Removes a custom attribute | ||||
| // | ||||
| // If an attribute is not found, nothing happens. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type); | ||||
| #endif | ||||
|  | ||||
|  | ||||
| /// File operations /// | ||||
| @@ -533,6 +551,7 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file); | ||||
| lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, | ||||
|         void *buffer, lfs_size_t size); | ||||
|  | ||||
| #ifndef LFS_READONLY | ||||
| // Write data to file | ||||
| // | ||||
| // Takes a buffer and size indicating the data to write. The file will not | ||||
| @@ -541,6 +560,7 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, | ||||
| // Returns the number of bytes written, or a negative error code on failure. | ||||
| lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, | ||||
|         const void *buffer, lfs_size_t size); | ||||
| #endif | ||||
|  | ||||
| // Change the position of the file | ||||
| // | ||||
| @@ -549,10 +569,12 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, | ||||
| lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, | ||||
|         lfs_soff_t off, int whence); | ||||
|  | ||||
| #ifndef LFS_READONLY | ||||
| // Truncates the size of the file to the specified size | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size); | ||||
| #endif | ||||
|  | ||||
| // Return the position of the file | ||||
| // | ||||
| @@ -575,10 +597,12 @@ lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file); | ||||
|  | ||||
| /// Directory operations /// | ||||
|  | ||||
| #ifndef LFS_READONLY | ||||
| // Create a directory | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_mkdir(lfs_t *lfs, const char *path); | ||||
| #endif | ||||
|  | ||||
| // Open a directory | ||||
| // | ||||
| @@ -640,6 +664,7 @@ lfs_ssize_t lfs_fs_size(lfs_t *lfs); | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); | ||||
|  | ||||
| #ifndef LFS_READONLY | ||||
| #ifdef LFS_MIGRATE | ||||
| // Attempts to migrate a previous version of littlefs | ||||
| // | ||||
| @@ -654,6 +679,7 @@ int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg); | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
|  | ||||
| #ifdef __cplusplus | ||||
|   | ||||
							
								
								
									
										30
									
								
								lfs_util.h
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								lfs_util.h
									
									
									
									
									
								
							| @@ -50,31 +50,35 @@ extern "C" | ||||
|  | ||||
| // Logging functions | ||||
| #ifdef LFS_YES_TRACE | ||||
| #define LFS_TRACE(fmt, ...) \ | ||||
|     printf("%s:%d:trace: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #define LFS_TRACE_(fmt, ...) \ | ||||
|     printf("%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "") | ||||
| #else | ||||
| #define LFS_TRACE(fmt, ...) | ||||
| #define LFS_TRACE(...) | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_NO_DEBUG | ||||
| #define LFS_DEBUG(fmt, ...) \ | ||||
|     printf("%s:%d:debug: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #define LFS_DEBUG_(fmt, ...) \ | ||||
|     printf("%s:%d:debug: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #define LFS_DEBUG(...) LFS_DEBUG_(__VA_ARGS__, "") | ||||
| #else | ||||
| #define LFS_DEBUG(fmt, ...) | ||||
| #define LFS_DEBUG(...) | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_NO_WARN | ||||
| #define LFS_WARN(fmt, ...) \ | ||||
|     printf("%s:%d:warn: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #define LFS_WARN_(fmt, ...) \ | ||||
|     printf("%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #define LFS_WARN(...) LFS_WARN_(__VA_ARGS__, "") | ||||
| #else | ||||
| #define LFS_WARN(fmt, ...) | ||||
| #define LFS_WARN(...) | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_NO_ERROR | ||||
| #define LFS_ERROR(fmt, ...) \ | ||||
|     printf("%s:%d:error: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #define LFS_ERROR_(fmt, ...) \ | ||||
|     printf("%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #define LFS_ERROR(...) LFS_ERROR_(__VA_ARGS__, "") | ||||
| #else | ||||
| #define LFS_ERROR(fmt, ...) | ||||
| #define LFS_ERROR(...) | ||||
| #endif | ||||
|  | ||||
| // Runtime assertions | ||||
| @@ -107,7 +111,7 @@ static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) { | ||||
|     return lfs_aligndown(a + alignment-1, alignment); | ||||
| } | ||||
|  | ||||
| // Find the next smallest power of 2 less than or equal to a | ||||
| // Find the smallest power of 2 greater 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); | ||||
|   | ||||
| @@ -18,10 +18,9 @@ TAG_TYPES = { | ||||
|     'ctzstruct':    (0x7ff, 0x202), | ||||
|     'inlinestruct': (0x7ff, 0x201), | ||||
|     'userattr':     (0x700, 0x300), | ||||
|     'tail':         (0x700, 0x600), # TODO rename these? | ||||
|     'tail':         (0x700, 0x600), | ||||
|     'softtail':     (0x7ff, 0x600), | ||||
|     'hardtail':     (0x7ff, 0x601), | ||||
|     'branch':       (0x7ff, 0x681), | ||||
|     'gstate':       (0x700, 0x700), | ||||
|     'movestate':    (0x7ff, 0x7ff), | ||||
|     'crc':          (0x700, 0x500), | ||||
| @@ -104,7 +103,7 @@ class Tag: | ||||
|  | ||||
|     def mkmask(self): | ||||
|         return Tag( | ||||
|             0x780 if self.is_('tail') else 0x700 if self.isunique else 0x7ff, # TODO best way? | ||||
|             0x700 if self.isunique else 0x7ff, | ||||
|             0x3ff if self.isattr else 0, | ||||
|             0) | ||||
|  | ||||
| @@ -319,14 +318,24 @@ def main(args): | ||||
|  | ||||
|     # find most recent pair | ||||
|     mdir = MetadataPair(blocks) | ||||
|     print("mdir {%s} rev %d%s%s" % ( | ||||
|  | ||||
|     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 '')) | ||||
|         ' (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: | ||||
|   | ||||
| @@ -5,107 +5,24 @@ import sys | ||||
| import json | ||||
| import io | ||||
| import itertools as it | ||||
| import collections as c | ||||
| 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, mdirs, f): | ||||
|     for k, id_ in enumerate(mdir.ids): | ||||
|         name = mdir[Tag('name', id_, 0)] | ||||
|         struct_ = mdir[Tag('struct', id_, 0)] | ||||
|  | ||||
|         desc = "id %d %s %s" % ( | ||||
|             id_, name.typerepr(), | ||||
|             json.dumps(name.data.decode('utf8'))) | ||||
|         if struct_.is_('dirstruct'): | ||||
|             pair = struct.unpack('<II', struct_.data[:8].ljust(8, b'\xff')) | ||||
|             desc += " dir {%#x, %#x}%s" % ( | ||||
|                 pair[0], pair[1], | ||||
|                 '?' if frozenset(pair) not in mdirs else '') | ||||
|         if struct_.is_('ctzstruct'): | ||||
|             desc += " ctz {%#x} size %d" % struct.unpack( | ||||
|                 '<II', struct_.data[:8].ljust(8, b'\xff')) | ||||
|         if struct_.is_('inlinestruct'): | ||||
|             desc += " inline size %d" % struct_.size | ||||
|  | ||||
|         data = None | ||||
|         if struct_.is_('inlinestruct'): | ||||
|             data = struct_.data | ||||
|         elif 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)) | ||||
|  | ||||
|         f.write("%-45s%s\n" % (desc, | ||||
|             "%-23s  %-8s" % ( | ||||
|                 ' '.join('%02x' % c for c in data[:8]), | ||||
|                 ''.join(c if c >= ' ' and c <= '~' else '.' | ||||
|                     for c in map(chr, data[:8]))) | ||||
|             if not args.no_truncate and len(desc) < 45 | ||||
|                 and data is not None else "")) | ||||
|  | ||||
|         if name.is_('superblock') and struct_.is_('inlinestruct'): | ||||
|             f.write( | ||||
|                 "  block_size %d\n" | ||||
|                 "  block_count %d\n" | ||||
|                 "  name_max %d\n" | ||||
|                 "  file_max %d\n" | ||||
|                 "  attr_max %d\n" % struct.unpack( | ||||
|                     '<IIIII', struct_.data[4:4+20].ljust(20, b'\xff'))) | ||||
|  | ||||
|         for tag in mdir.tags: | ||||
|             if tag.id==id_ and tag.is_('userattr'): | ||||
|                 desc = "%s size %d" % (tag.typerepr(), tag.size) | ||||
|                 f.write("  %-43s%s\n" % (desc, | ||||
|                     "%-23s  %-8s" % ( | ||||
|                         ' '.join('%02x' % c for c in tag.data[:8]), | ||||
|                         ''.join(c if c >= ' ' and c <= '~' else '.' | ||||
|                             for c in map(chr, tag.data[:8]))) | ||||
|                     if not args.no_truncate and len(desc) < 43 else "")) | ||||
|  | ||||
|                 if args.no_truncate: | ||||
|                     for i in range(0, len(tag.data), 16): | ||||
|                         f.write("    %08x: %-47s  %-16s\n" % ( | ||||
|                             i, ' '.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])))) | ||||
|  | ||||
|         if args.no_truncate and data is not None: | ||||
|             for i in range(0, 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])))) | ||||
|  | ||||
| def main(args): | ||||
|     superblock = None | ||||
|     gstate = b'\0\0\0\0\0\0\0\0\0\0\0\0' | ||||
|     mdirs = c.OrderedDict() | ||||
|     dirs = [] | ||||
|     mdirs = [] | ||||
|     corrupted = [] | ||||
|     cycle = False | ||||
|     with open(args.disk, 'rb') as f: | ||||
|         tail = (args.block1, args.block2) | ||||
|         while tail: | ||||
|             if frozenset(tail) in mdirs: | ||||
|                 # cycle detected | ||||
|                 cycle = tail | ||||
|         hard = False | ||||
|         while True: | ||||
|             for m in it.chain((m for d in dirs for m in d), mdirs): | ||||
|                 if set(m.blocks) == set(tail): | ||||
|                     # cycle detected | ||||
|                     cycle = m.blocks | ||||
|             if cycle: | ||||
|                 break | ||||
|  | ||||
|             # load mdir | ||||
| @@ -128,13 +45,6 @@ def main(args): | ||||
|             except KeyError: | ||||
|                 mdir.tail = None | ||||
|  | ||||
|             try: | ||||
|                 mdir.branch = mdir[Tag('branch', 0, 0)] | ||||
|                 if mdir.branch.size != 8 or mdir.branch.data == 8*b'\xff': | ||||
|                     mdir.branch = None | ||||
|             except KeyError: | ||||
|                 mdir.branch = None | ||||
|  | ||||
|             # have superblock? | ||||
|             try: | ||||
|                 nsuperblock = mdir[ | ||||
| @@ -155,53 +65,39 @@ def main(args): | ||||
|             if not mdir: | ||||
|                 corrupted.append(mdir) | ||||
|  | ||||
|             # add to metadata-pairs | ||||
|             mdirs[frozenset(mdir.blocks)] = mdir | ||||
|             tail = (struct.unpack('<II', mdir.tail.data) | ||||
|                 if mdir.tail else None) | ||||
|             # add to directories | ||||
|             mdirs.append(mdir) | ||||
|             if mdir.tail is None or not mdir.tail.is_('hardtail'): | ||||
|                 dirs.append(mdirs) | ||||
|                 mdirs = [] | ||||
|  | ||||
|     # derive paths and build directories | ||||
|     dirs = {} | ||||
|     rogue = {} | ||||
|     pending = [('/', (args.block1, args.block2))] | ||||
|             if mdir.tail is None: | ||||
|                 break | ||||
|  | ||||
|             tail = struct.unpack('<II', mdir.tail.data) | ||||
|             hard = mdir.tail.is_('hardtail') | ||||
|  | ||||
|     # find paths | ||||
|     dirtable = {} | ||||
|     for dir in dirs: | ||||
|         dirtable[frozenset(dir[0].blocks)] = dir | ||||
|  | ||||
|     pending = [("/", dirs[0])] | ||||
|     while pending: | ||||
|         path, branch = pending.pop(0) | ||||
|         dir = [] | ||||
|         while branch and frozenset(branch) in mdirs: | ||||
|             mdir = mdirs[frozenset(branch)] | ||||
|             dir.append(mdir) | ||||
|  | ||||
|         path, dir = pending.pop(0) | ||||
|         for mdir in dir: | ||||
|             for tag in mdir.tags: | ||||
|                 if tag.is_('dir'): | ||||
|                     try: | ||||
|                         npath = path + '/' + tag.data.decode('utf8') | ||||
|                         npath = npath.replace('//', '/') | ||||
|                         npath = tag.data.decode('utf8') | ||||
|                         dirstruct = mdir[Tag('dirstruct', tag.id, 0)] | ||||
|                         npair = struct.unpack('<II', dirstruct.data) | ||||
|                         pending.append((npath, npair)) | ||||
|                         nblocks = struct.unpack('<II', dirstruct.data) | ||||
|                         nmdir = dirtable[frozenset(nblocks)] | ||||
|                         pending.append(((path + '/' + npath), nmdir)) | ||||
|                     except KeyError: | ||||
|                         pass | ||||
|  | ||||
|             branch = (struct.unpack('<II', mdir.branch.data) | ||||
|                 if mdir.branch else None) | ||||
|  | ||||
|         if not dir: | ||||
|             rogue[path] = branch | ||||
|         else: | ||||
|             dirs[path] = dir | ||||
|  | ||||
|     # also find orphans | ||||
|     not_orphans = {frozenset(mdir.blocks) | ||||
|         for dir in dirs.values() | ||||
|         for mdir in dir} | ||||
|     orphans = [] | ||||
|     for pair, mdir in mdirs.items(): | ||||
|         if pair not in not_orphans: | ||||
|             if len(orphans) > 0 and (pair == frozenset( | ||||
|                     struct.unpack('<II', orphans[-1][-1].tail.data))): | ||||
|                 orphans[-1].append(mdir) | ||||
|             else: | ||||
|                 orphans.append([mdir]) | ||||
|         dir[0].path = path.replace('//', '/') | ||||
|  | ||||
|     # print littlefs + version info | ||||
|     version = ('?', '?') | ||||
| @@ -210,28 +106,22 @@ def main(args): | ||||
|             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 "")) | ||||
|         if not any([args.no_truncate, args.log, args.all]) else "")) | ||||
|  | ||||
|     # print gstate | ||||
|     badgstate = None | ||||
|     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.type: | ||||
|         if frozenset(blocks) not in mdirs: | ||||
|             badgstate = gstate | ||||
|         print("  move dir {%#x, %#x}%s id %d" % ( | ||||
|             blocks[0], blocks[1], | ||||
|             '?' if frozenset(blocks) not in mdirs else '', | ||||
|             tag.id)) | ||||
|         print("  move dir {%#x, %#x} id %d" % ( | ||||
|             blocks[0], blocks[1], tag.id)) | ||||
|  | ||||
|     # print dir info | ||||
|     for path, dir in it.chain( | ||||
|             sorted(dirs.items()), | ||||
|             zip(it.repeat(None), orphans)): | ||||
|         print("dir %s" % json.dumps(path) if path else "orphaned") | ||||
|     # print mdir info | ||||
|     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" % ( | ||||
| @@ -241,21 +131,19 @@ def main(args): | ||||
|                 if mdir.tail else '')) | ||||
|  | ||||
|             f = io.StringIO() | ||||
|             if args.tags: | ||||
|                 mdir.dump_tags(f, truncate=not args.no_truncate) | ||||
|             elif args.log: | ||||
|             if args.log: | ||||
|                 mdir.dump_log(f, truncate=not args.no_truncate) | ||||
|             elif args.all: | ||||
|                 mdir.dump_all(f, truncate=not args.no_truncate) | ||||
|             else: | ||||
|                 dumpentries(args, mdir, mdirs, f) | ||||
|                 mdir.dump_tags(f, truncate=not args.no_truncate) | ||||
|  | ||||
|             lines = list(filter(None, f.getvalue().split('\n'))) | ||||
|             for k, line in enumerate(lines): | ||||
|                 print("%s %s" % ( | ||||
|                     ' ' if j == len(dir)-1 else | ||||
|                     'v' if k == len(lines)-1 else | ||||
|                     '|' if path else '.', | ||||
|                     '|', | ||||
|                     line)) | ||||
|  | ||||
|     errcode = 0 | ||||
| @@ -264,18 +152,8 @@ def main(args): | ||||
|         print("*** corrupted mdir {%#x, %#x}! ***" % ( | ||||
|             mdir.blocks[0], mdir.blocks[1])) | ||||
|  | ||||
|     for path, pair in rogue.items(): | ||||
|         errcode = errcode or 2 | ||||
|         print("*** couldn't find dir %s {%#x, %#x}! ***" % ( | ||||
|             json.dumps(path), pair[0], pair[1])) | ||||
|  | ||||
|     if badgstate: | ||||
|         errcode = errcode or 3 | ||||
|         print("*** bad gstate 0x%s! ***" % | ||||
|             ''.join('%02x' % c for c in gstate)) | ||||
|  | ||||
|     if cycle: | ||||
|         errcode = errcode or 4 | ||||
|         errcode = errcode or 2 | ||||
|         print("*** cycle detected {%#x, %#x}! ***" % ( | ||||
|             cycle[0], cycle[1])) | ||||
|  | ||||
| @@ -292,12 +170,10 @@ 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 root.") | ||||
|         help="Optional first block address for finding the superblock.") | ||||
|     parser.add_argument('block2', nargs='?', default=1, | ||||
|         type=lambda x: int(x, 0), | ||||
|         help="Optional second block address for finding the root.") | ||||
|     parser.add_argument('-t', '--tags', action='store_true', | ||||
|         help="Show metadata tags instead of reconstructing entries.") | ||||
|         help="Optional second block address for finding the superblock.") | ||||
|     parser.add_argument('-l', '--log', action='store_true', | ||||
|         help="Show tags in log.") | ||||
|     parser.add_argument('-a', '--all', action='store_true', | ||||
|   | ||||
| @@ -231,7 +231,7 @@ class TestCase: | ||||
|                 ncmd.extend(['-ex', 'r']) | ||||
|                 if failure.assert_: | ||||
|                     ncmd.extend(['-ex', 'up 2']) | ||||
|             elif gdb == 'start' or isinstance(gdb, int): | ||||
|             elif gdb == 'main': | ||||
|                 ncmd.extend([ | ||||
|                     '-ex', 'b %s:%d' % (self.suite.path, self.code_lineno), | ||||
|                     '-ex', 'r']) | ||||
| @@ -329,9 +329,7 @@ class ReentrantTestCase(TestCase): | ||||
|                 persist = 'noerase' | ||||
|  | ||||
|             # exact cycle we should drop into debugger? | ||||
|             if gdb and failure and ( | ||||
|                     failure.cycleno == cycles or | ||||
|                     (isinstance(gdb, int) and gdb == cycles)): | ||||
|             if gdb and failure and failure.cycleno == cycles: | ||||
|                 return super().test(gdb=gdb, persist=persist, cycles=cycles, | ||||
|                     failure=failure, **args) | ||||
|  | ||||
| @@ -748,9 +746,9 @@ if __name__ == "__main__": | ||||
|     parser.add_argument('testpaths', nargs='*', default=[TESTDIR], | ||||
|         help="Description of test(s) to run. By default, this is all tests \ | ||||
|             found in the \"{0}\" directory. Here, you can specify a different \ | ||||
|             directory of tests, a specific file, a suite by name, and even a \ | ||||
|             specific test case by adding brackets. For example \ | ||||
|             \"test_dirs[0]\" or \"{0}/test_dirs.toml[0]\".".format(TESTDIR)) | ||||
|             directory of tests, a specific file, a suite by name, and even \ | ||||
|             specific test cases and permutations. For example \ | ||||
|             \"test_dirs#1\" or \"{0}/test_dirs.toml#1#1\".".format(TESTDIR)) | ||||
|     parser.add_argument('-D', action='append', default=[], | ||||
|         help="Overriding parameter definitions.") | ||||
|     parser.add_argument('-v', '--verbose', action='store_true', | ||||
| @@ -762,8 +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', metavar='{init,start,assert},CYCLE', | ||||
|         type=lambda n: n if n in {'init', 'start', 'assert'} else int(n, 0), | ||||
|     parser.add_argument('-g', '--gdb', choices=['init', 'main', 'assert'], | ||||
|         nargs='?', const='assert', | ||||
|         help="Drop into gdb on test failure.") | ||||
|     parser.add_argument('--no-internal', action='store_true', | ||||
|   | ||||
| @@ -323,6 +323,90 @@ 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. | ||||
|   | ||||
| @@ -246,8 +246,6 @@ code = ''' | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|     } | ||||
|     // TODO rm me | ||||
|     lfs_mkdir(&lfs, "a") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
| @@ -258,9 +256,6 @@ code = ''' | ||||
|     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, "a") == 0); | ||||
|     for (int i = 0; i < N; i++) { | ||||
|         sprintf(path, "file%03d", i); | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|   | ||||
| @@ -151,6 +151,7 @@ code = ''' | ||||
|             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; | ||||
| @@ -245,6 +246,7 @@ code = ''' | ||||
|             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( | ||||
| @@ -274,6 +276,7 @@ code = ''' | ||||
|             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( | ||||
|   | ||||
| @@ -148,7 +148,7 @@ code = ''' | ||||
|          # 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 +210,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}, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user