mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	Merge pull request #85 from ARMmbed/v2-alpha
v2: Metadata logging, custom attributes, inline files, and a major version bump
This commit is contained in:
		
							
								
								
									
										65
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -18,10 +18,11 @@ script: | |||||||
|   - make test QUIET=1 |   - make test QUIET=1 | ||||||
|  |  | ||||||
|   # run tests with a few different configurations |   # run tests with a few different configurations | ||||||
|   - make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=1      -DLFS_PROG_SIZE=1" |   - make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=1      -DLFS_CACHE_SIZE=4" | ||||||
|   - make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=512    -DLFS_PROG_SIZE=512" |   - make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=512    -DLFS_CACHE_SIZE=512 -DLFS_BLOCK_CYCLES=16" | ||||||
|   - make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD=2048" |   - make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD_SIZE=256" | ||||||
|  |  | ||||||
|  |   - make clean test QUIET=1 CFLAGS+="-DLFS_INLINE_MAX=0" | ||||||
|   - make clean test QUIET=1 CFLAGS+="-DLFS_NO_INTRINSICS" |   - make clean test QUIET=1 CFLAGS+="-DLFS_NO_INTRINSICS" | ||||||
|  |  | ||||||
|   # compile and find the code size with the smallest configuration |   # compile and find the code size with the smallest configuration | ||||||
| @@ -103,7 +104,7 @@ jobs: | |||||||
|       if: branch !~ -prefix$ |       if: branch !~ -prefix$ | ||||||
|       install: |       install: | ||||||
|         - sudo apt-get install libfuse-dev |         - sudo apt-get install libfuse-dev | ||||||
|         - git clone --depth 1 https://github.com/geky/littlefs-fuse |         - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2-alpha | ||||||
|         - fusermount -V |         - fusermount -V | ||||||
|         - gcc --version |         - gcc --version | ||||||
|       before_script: |       before_script: | ||||||
| @@ -113,7 +114,7 @@ jobs: | |||||||
|  |  | ||||||
|         - mkdir mount |         - mkdir mount | ||||||
|         - sudo chmod a+rw /dev/loop0 |         - sudo chmod a+rw /dev/loop0 | ||||||
|         - dd if=/dev/zero bs=512 count=2048 of=disk |         - dd if=/dev/zero bs=512 count=4096 of=disk | ||||||
|         - losetup /dev/loop0 disk |         - losetup /dev/loop0 disk | ||||||
|       script: |       script: | ||||||
|         # self-host test |         # self-host test | ||||||
| @@ -126,7 +127,59 @@ jobs: | |||||||
|         - mkdir mount/littlefs |         - mkdir mount/littlefs | ||||||
|         - cp -r $(git ls-tree --name-only HEAD) mount/littlefs |         - cp -r $(git ls-tree --name-only HEAD) mount/littlefs | ||||||
|         - cd mount/littlefs |         - cd mount/littlefs | ||||||
|         - ls |         - stat . | ||||||
|  |         - ls -flh | ||||||
|  |         - make -B test_dirs test_files QUIET=1 | ||||||
|  |  | ||||||
|  |     # self-host with littlefs-fuse for fuzz test | ||||||
|  |     - stage: test | ||||||
|  |       env: | ||||||
|  |         - STAGE=test | ||||||
|  |         - NAME=littlefs-migration | ||||||
|  |       install: | ||||||
|  |         - sudo apt-get install libfuse-dev | ||||||
|  |         - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2-alpha v2 | ||||||
|  |         - git clone --depth 1 https://github.com/geky/littlefs-fuse v1 | ||||||
|  |         - fusermount -V | ||||||
|  |         - gcc --version | ||||||
|  |       before_script: | ||||||
|  |         # setup disk for littlefs-fuse | ||||||
|  |         - rm -rf v2/littlefs/* | ||||||
|  |         - cp -r $(git ls-tree --name-only HEAD) v2/littlefs | ||||||
|  |  | ||||||
|  |         - mkdir mount | ||||||
|  |         - sudo chmod a+rw /dev/loop0 | ||||||
|  |         - dd if=/dev/zero bs=512 count=4096 of=disk | ||||||
|  |         - losetup /dev/loop0 disk | ||||||
|  |       script: | ||||||
|  |         # compile v1 and v2 | ||||||
|  |         - make -C v1 | ||||||
|  |         - make -C v2 | ||||||
|  |  | ||||||
|  |         # run self-host test with v1 | ||||||
|  |         - v1/lfs --format /dev/loop0 | ||||||
|  |         - v1/lfs /dev/loop0 mount | ||||||
|  |  | ||||||
|  |         - ls mount | ||||||
|  |         - mkdir mount/littlefs | ||||||
|  |         - cp -r $(git ls-tree --name-only HEAD) mount/littlefs | ||||||
|  |         - cd mount/littlefs | ||||||
|  |         - stat . | ||||||
|  |         - ls -flh | ||||||
|  |         - make -B test_dirs test_files QUIET=1 | ||||||
|  |  | ||||||
|  |         # attempt to migrate | ||||||
|  |         - cd ../.. | ||||||
|  |         - fusermount -u mount | ||||||
|  |  | ||||||
|  |         - v2/lfs --migrate /dev/loop0 | ||||||
|  |         - v2/lfs /dev/loop0 mount | ||||||
|  |  | ||||||
|  |         # run self-host test with v2 right where we left off | ||||||
|  |         - ls mount | ||||||
|  |         - cd mount/littlefs | ||||||
|  |         - stat . | ||||||
|  |         - ls -flh | ||||||
|         - make -B test_dirs test_files QUIET=1 |         - make -B test_dirs test_files QUIET=1 | ||||||
|  |  | ||||||
|     # Automatically create releases |     # Automatically create releases | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								Makefile
									
									
									
									
									
								
							| @@ -26,7 +26,9 @@ override CFLAGS += -m$(WORD) | |||||||
| endif | endif | ||||||
| override CFLAGS += -I. | override CFLAGS += -I. | ||||||
| override CFLAGS += -std=c99 -Wall -pedantic | override CFLAGS += -std=c99 -Wall -pedantic | ||||||
| override CFLAGS += -Wshadow -Wunused-parameter -Wjump-misses-init -Wsign-compare | override CFLAGS += -Wextra -Wshadow -Wjump-misses-init | ||||||
|  | # Remove missing-field-initializers because of GCC bug | ||||||
|  | override CFLAGS += -Wno-missing-field-initializers | ||||||
|  |  | ||||||
|  |  | ||||||
| all: $(TARGET) | all: $(TARGET) | ||||||
| @@ -38,7 +40,8 @@ size: $(OBJ) | |||||||
|  |  | ||||||
| .SUFFIXES: | .SUFFIXES: | ||||||
| test: test_format test_dirs test_files test_seek test_truncate \ | test: test_format test_dirs test_files test_seek test_truncate \ | ||||||
| 	test_interspersed test_alloc test_paths test_orphan test_move test_corrupt | 	test_entries test_interspersed test_alloc test_paths test_attrs \ | ||||||
|  | 	test_move test_orphan test_corrupt | ||||||
| 	@rm test.c | 	@rm test.c | ||||||
| test_%: tests/test_%.sh | test_%: tests/test_%.sh | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										164
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										164
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| ## The little filesystem | ## littlefs | ||||||
|  |  | ||||||
| A little fail-safe filesystem designed for embedded systems. | A little fail-safe filesystem designed for microcontrollers. | ||||||
|  |  | ||||||
| ``` | ``` | ||||||
|    | | |     .---._____ |    | | |     .---._____ | ||||||
| @@ -11,17 +11,19 @@ A little fail-safe filesystem designed for embedded systems. | |||||||
|    | | | |    | | | | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| **Bounded RAM/ROM** - The littlefs is designed to work with a limited amount | **Power-loss resilience** - littlefs is designed to handle random power | ||||||
| of memory. Recursion is avoided and dynamic memory is limited to configurable | failures. All file operations have strong copy-on-write guarantees and if | ||||||
| buffers that can be provided statically. | power is lost the filesystem will fall back to the last known good state. | ||||||
|  |  | ||||||
| **Power-loss resilient** - The littlefs is designed for systems that may have | **Dynamic wear leveling** - littlefs is designed with flash in mind, and | ||||||
| random power failures. The littlefs has strong copy-on-write guarantees and | provides wear leveling over dynamic blocks. Additionally, littlefs can | ||||||
| storage on disk is always kept in a valid state. | detect bad blocks and work around them. | ||||||
|  |  | ||||||
| **Wear leveling** - Since the most common form of embedded storage is erodible | **Bounded RAM/ROM** - littlefs is designed to work with a small amount of | ||||||
| flash memories, littlefs provides a form of dynamic wear leveling for systems | memory. RAM usage is strictly bounded, which means RAM consumption does not | ||||||
| that can not fit a full flash translation layer. | change as the filesystem grows. The filesystem contains no unbounded | ||||||
|  | recursion and dynamic memory is limited to configurable buffers that can be | ||||||
|  | provided statically. | ||||||
|  |  | ||||||
| ## Example | ## Example | ||||||
|  |  | ||||||
| @@ -49,7 +51,8 @@ const struct lfs_config cfg = { | |||||||
|     .prog_size = 16, |     .prog_size = 16, | ||||||
|     .block_size = 4096, |     .block_size = 4096, | ||||||
|     .block_count = 128, |     .block_count = 128, | ||||||
|     .lookahead = 128, |     .cache_size = 16, | ||||||
|  |     .lookahead_size = 16, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // entry point | // entry point | ||||||
| @@ -90,11 +93,11 @@ int main(void) { | |||||||
| Detailed documentation (or at least as much detail as is currently available) | Detailed documentation (or at least as much detail as is currently available) | ||||||
| can be found in the comments in [lfs.h](lfs.h). | can be found in the comments in [lfs.h](lfs.h). | ||||||
|  |  | ||||||
| As you may have noticed, littlefs takes in a configuration structure that | littlefs takes in a configuration structure that defines how the filesystem | ||||||
| defines how the filesystem operates. The configuration struct provides the | operates. The configuration struct provides the filesystem with the block | ||||||
| filesystem with the block device operations and dimensions, tweakable | device operations and dimensions, tweakable parameters that tradeoff memory | ||||||
| parameters that tradeoff memory usage for performance, and optional | usage for performance, and optional static buffers if the user wants to avoid | ||||||
| static buffers if the user wants to avoid dynamic memory. | dynamic memory. | ||||||
|  |  | ||||||
| The state of the littlefs is stored in the `lfs_t` type which is left up | The state of the littlefs is stored in the `lfs_t` type which is left up | ||||||
| to the user to allocate, allowing multiple filesystems to be in use | to the user to allocate, allowing multiple filesystems to be in use | ||||||
| @@ -106,13 +109,13 @@ directory functions, with the deviation that the allocation of filesystem | |||||||
| structures must be provided by the user. | structures must be provided by the user. | ||||||
|  |  | ||||||
| All POSIX operations, such as remove and rename, are atomic, even in event | All POSIX operations, such as remove and rename, are atomic, even in event | ||||||
| of power-loss. Additionally, no file updates are actually committed to the | of power-loss. Additionally, no file updates are not actually committed to | ||||||
| filesystem until sync or close is called on the file. | the filesystem until sync or close is called on the file. | ||||||
|  |  | ||||||
| ## Other notes | ## Other notes | ||||||
|  |  | ||||||
| All littlefs calls have the potential to return a negative error code. The  | 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  | 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. | [lfs.h](lfs.h), or an error returned by the user's block device operations. | ||||||
|  |  | ||||||
| In the configuration struct, the `prog` and `erase` function provided by the | In the configuration struct, the `prog` and `erase` function provided by the | ||||||
| @@ -127,14 +130,60 @@ from memory, otherwise data integrity can not be guaranteed. If the `write` | |||||||
| function does not perform caching, and therefore each `read` or `write` call | function does not perform caching, and therefore each `read` or `write` call | ||||||
| hits the memory, the `sync` function can simply return 0. | hits the memory, the `sync` function can simply return 0. | ||||||
|  |  | ||||||
| ## Reference material | ## Design | ||||||
|  |  | ||||||
| [DESIGN.md](DESIGN.md) - DESIGN.md contains a fully detailed dive into how | At a high level, littlefs is a block based filesystem that uses small logs to | ||||||
| littlefs actually works. I would encourage you to read it since the | store metadata and larger copy-on-write (COW) structures to store file data. | ||||||
| solutions and tradeoffs at work here are quite interesting. |  | ||||||
|  |  | ||||||
| [SPEC.md](SPEC.md) - SPEC.md contains the on-disk specification of littlefs | In littlefs, these ingredients form a sort of two-layered cake, with the small | ||||||
| with all the nitty-gritty details. Can be useful for developing tooling. | logs (called metadata pairs) providing fast updates to metadata anywhere on | ||||||
|  | storage, while the COW structures store file data compactly and without any | ||||||
|  | wear amplification cost. | ||||||
|  |  | ||||||
|  | Both of these data structures are built out of blocks, which are fed by a | ||||||
|  | common block allocator. By limiting the number of erases allowed on a block | ||||||
|  | per allocation, the allocator provides dynamic wear leveling over the entire | ||||||
|  | filesystem. | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  |                     root | ||||||
|  |                    .--------.--------. | ||||||
|  |                    | A'| B'|         | | ||||||
|  |                    |   |   |->       | | ||||||
|  |                    |   |   |         | | ||||||
|  |                    '--------'--------' | ||||||
|  |                 .----'   '--------------. | ||||||
|  |        A       v                 B       v | ||||||
|  |       .--------.--------.       .--------.--------. | ||||||
|  |       | C'| D'|         |       | E'|new|         | | ||||||
|  |       |   |   |->       |       |   | E'|->       | | ||||||
|  |       |   |   |         |       |   |   |         | | ||||||
|  |       '--------'--------'       '--------'--------' | ||||||
|  |       .-'   '--.                  |   '------------------. | ||||||
|  |      v          v              .-'                        v | ||||||
|  | .--------.  .--------.        v                       .--------. | ||||||
|  | |   C    |  |   D    |   .--------.       write       | new E  | | ||||||
|  | |        |  |        |   |   E    |        ==>        |        | | ||||||
|  | |        |  |        |   |        |                   |        | | ||||||
|  | '--------'  '--------'   |        |                   '--------' | ||||||
|  |                          '--------'                   .-'    | | ||||||
|  |                          .-'    '-.    .-------------|------' | ||||||
|  |                         v          v  v              v | ||||||
|  |                    .--------.  .--------.       .--------. | ||||||
|  |                    |   F    |  |   G    |       | new F  | | ||||||
|  |                    |        |  |        |       |        | | ||||||
|  |                    |        |  |        |       |        | | ||||||
|  |                    '--------'  '--------'       '--------' | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | More details on how littlefs works can be found in [DESIGN.md](DESIGN.md) and | ||||||
|  | [SPEC.md](SPEC.md). | ||||||
|  |  | ||||||
|  | - [DESIGN.md](DESIGN.md) - A fully detailed dive into how littlefs works. | ||||||
|  |   I would suggest reading it as the tradeoffs at work are quite interesting. | ||||||
|  |  | ||||||
|  | - [SPEC.md](SPEC.md) - The on-disk specification of littlefs with all the | ||||||
|  |   nitty-gritty details. May be useful for tooling development. | ||||||
|  |  | ||||||
| ## Testing | ## Testing | ||||||
|  |  | ||||||
| @@ -148,9 +197,9 @@ make test | |||||||
|  |  | ||||||
| ## License | ## License | ||||||
|  |  | ||||||
| The littlefs is provided under the [BSD-3-Clause](https://spdx.org/licenses/BSD-3-Clause.html) | The littlefs is provided under the [BSD-3-Clause] license. See | ||||||
| license. See [LICENSE.md](LICENSE.md) for more information. Contributions to | [LICENSE.md](LICENSE.md) for more information. Contributions to this project | ||||||
| this project are accepted under the same license. | are accepted under the same license. | ||||||
|  |  | ||||||
| Individual files contain the following tag instead of the full license text. | Individual files contain the following tag instead of the full license text. | ||||||
|  |  | ||||||
| @@ -161,32 +210,39 @@ License Identifiers that are here available: http://spdx.org/licenses/ | |||||||
|  |  | ||||||
| ## Related projects | ## Related projects | ||||||
|  |  | ||||||
| [Mbed OS](https://github.com/ARMmbed/mbed-os/tree/master/features/filesystem/littlefs) - | - [littlefs-fuse] - A [FUSE] wrapper for littlefs. The project allows you to | ||||||
| The easiest way to get started with littlefs is to jump into [Mbed](https://os.mbed.com/), |   mount littlefs directly on a Linux machine. Can be useful for debugging | ||||||
| which already has block device drivers for most forms of embedded storage. The |   littlefs if you have an SD card handy. | ||||||
| littlefs is available in Mbed OS as the [LittleFileSystem](https://os.mbed.com/docs/latest/reference/littlefilesystem.html) |  | ||||||
| class. |  | ||||||
|  |  | ||||||
| [littlefs-fuse](https://github.com/geky/littlefs-fuse) - A [FUSE](https://github.com/libfuse/libfuse) | - [littlefs-js] - A javascript wrapper for littlefs. I'm not sure why you would | ||||||
| wrapper for littlefs. The project allows you to mount littlefs directly on a |   want this, but it is handy for demos.  You can see it in action | ||||||
| Linux machine. Can be useful for debugging littlefs if you have an SD card |   [here][littlefs-js-demo]. | ||||||
| handy. |  | ||||||
|  |  | ||||||
| [littlefs-js](https://github.com/geky/littlefs-js) - A javascript wrapper for | - [mklfs] - A command line tool built by the [Lua RTOS] guys for making | ||||||
| littlefs. I'm not sure why you would want this, but it is handy for demos. |   littlefs images from a host PC. Supports Windows, Mac OS, and Linux. | ||||||
| You can see it in action [here](http://littlefs.geky.net/demo.html). |  | ||||||
|  |  | ||||||
| [mklfs](https://github.com/whitecatboard/Lua-RTOS-ESP32/tree/master/components/mklfs/src) - | - [Mbed OS] - The easiest way to get started with littlefs is to jump into Mbed | ||||||
| A command line tool built by the [Lua RTOS](https://github.com/whitecatboard/Lua-RTOS-ESP32) |   which already has block device drivers for most forms of embedded storage. | ||||||
| guys for making littlefs images from a host PC. Supports Windows, Mac OS, |   littlefs is available in Mbed OS as the [LittleFileSystem] class. | ||||||
| and Linux. |  | ||||||
|  |  | ||||||
| [SPIFFS](https://github.com/pellepl/spiffs) - Another excellent embedded | - [SPIFFS] - Another excellent embedded filesystem for NOR flash. As a more | ||||||
| filesystem for NOR flash. As a more traditional logging filesystem with full |   traditional logging filesystem with full static wear-leveling, SPIFFS will | ||||||
| static wear-leveling, SPIFFS will likely outperform littlefs on small |   likely outperform littlefs on small memories such as the internal flash on | ||||||
| memories such as the internal flash on microcontrollers. |   microcontrollers. | ||||||
|  |  | ||||||
| [Dhara](https://github.com/dlbeer/dhara) - An interesting NAND flash | - [Dhara] - An interesting NAND flash translation layer designed for small | ||||||
| translation layer designed for small MCUs. It offers static wear-leveling and |   MCUs. It offers static wear-leveling and power-resilience with only a fixed | ||||||
| power-resilience with only a fixed O(|address|) pointer structure stored on |   _O(|address|)_ pointer structure stored on each block and in RAM. | ||||||
| each block and in RAM. |  | ||||||
|  |  | ||||||
|  | [BSD-3-Clause]: https://spdx.org/licenses/BSD-3-Clause.html | ||||||
|  | [littlefs-fuse]: https://github.com/geky/littlefs-fuse | ||||||
|  | [FUSE]: https://github.com/libfuse/libfuse | ||||||
|  | [littlefs-js]: https://github.com/geky/littlefs-js | ||||||
|  | [littlefs-js-demo]:http://littlefs.geky.net/demo.html | ||||||
|  | [mklfs]: https://github.com/whitecatboard/Lua-RTOS-ESP32/tree/master/components/mklfs/src | ||||||
|  | [Lua RTOS]: https://github.com/whitecatboard/Lua-RTOS-ESP32 | ||||||
|  | [Mbed OS]: https://github.com/armmbed/mbed-os | ||||||
|  | [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 | ||||||
|   | |||||||
| @@ -19,6 +19,40 @@ | |||||||
| #include <inttypes.h> | #include <inttypes.h> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Emulated block device utils | ||||||
|  | static inline void lfs_emubd_tole32(lfs_emubd_t *emu) { | ||||||
|  |     emu->cfg.read_size     = lfs_tole32(emu->cfg.read_size); | ||||||
|  |     emu->cfg.prog_size     = lfs_tole32(emu->cfg.prog_size); | ||||||
|  |     emu->cfg.block_size    = lfs_tole32(emu->cfg.block_size); | ||||||
|  |     emu->cfg.block_count   = lfs_tole32(emu->cfg.block_count); | ||||||
|  |  | ||||||
|  |     emu->stats.read_count  = lfs_tole32(emu->stats.read_count); | ||||||
|  |     emu->stats.prog_count  = lfs_tole32(emu->stats.prog_count); | ||||||
|  |     emu->stats.erase_count = lfs_tole32(emu->stats.erase_count); | ||||||
|  |  | ||||||
|  |     for (unsigned i = 0; i < sizeof(emu->history.blocks) / | ||||||
|  |             sizeof(emu->history.blocks[0]); i++) { | ||||||
|  |         emu->history.blocks[i] = lfs_tole32(emu->history.blocks[i]); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline void lfs_emubd_fromle32(lfs_emubd_t *emu) { | ||||||
|  |     emu->cfg.read_size     = lfs_fromle32(emu->cfg.read_size); | ||||||
|  |     emu->cfg.prog_size     = lfs_fromle32(emu->cfg.prog_size); | ||||||
|  |     emu->cfg.block_size    = lfs_fromle32(emu->cfg.block_size); | ||||||
|  |     emu->cfg.block_count   = lfs_fromle32(emu->cfg.block_count); | ||||||
|  |  | ||||||
|  |     emu->stats.read_count  = lfs_fromle32(emu->stats.read_count); | ||||||
|  |     emu->stats.prog_count  = lfs_fromle32(emu->stats.prog_count); | ||||||
|  |     emu->stats.erase_count = lfs_fromle32(emu->stats.erase_count); | ||||||
|  |  | ||||||
|  |     for (unsigned i = 0; i < sizeof(emu->history.blocks) / | ||||||
|  |             sizeof(emu->history.blocks[0]); i++) { | ||||||
|  |         emu->history.blocks[i] = lfs_fromle32(emu->history.blocks[i]); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| // Block device emulated on existing filesystem | // Block device emulated on existing filesystem | ||||||
| int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { | int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { | ||||||
|     lfs_emubd_t *emu = cfg->context; |     lfs_emubd_t *emu = cfg->context; | ||||||
| @@ -46,17 +80,31 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Load stats to continue incrementing |     // Load stats to continue incrementing | ||||||
|     snprintf(emu->child, LFS_NAME_MAX, "stats"); |     snprintf(emu->child, LFS_NAME_MAX, ".stats"); | ||||||
|  |  | ||||||
|     FILE *f = fopen(emu->path, "r"); |     FILE *f = fopen(emu->path, "r"); | ||||||
|     if (!f && errno != ENOENT) { |     if (!f) { | ||||||
|         return -errno; |         memset(&emu->stats, 0, sizeof(emu->stats)); | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (errno == ENOENT) { |  | ||||||
|         memset(&emu->stats, 0x0, sizeof(emu->stats)); |  | ||||||
|     } else { |     } else { | ||||||
|         size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f); |         size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f); | ||||||
|  |         lfs_emubd_fromle32(emu); | ||||||
|  |         if (res < 1) { | ||||||
|  |             return -errno; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         err = fclose(f); | ||||||
|  |         if (err) { | ||||||
|  |             return -errno; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Load history | ||||||
|  |     snprintf(emu->child, LFS_NAME_MAX, ".history"); | ||||||
|  |     f = fopen(emu->path, "r"); | ||||||
|  |     if (!f) { | ||||||
|  |         memset(&emu->history, 0, sizeof(emu->history)); | ||||||
|  |     } else { | ||||||
|  |         size_t res = fread(&emu->history, sizeof(emu->history), 1, f); | ||||||
|  |         lfs_emubd_fromle32(emu); | ||||||
|         if (res < 1) { |         if (res < 1) { | ||||||
|             return -errno; |             return -errno; | ||||||
|         } |         } | ||||||
| @@ -166,6 +214,13 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, | |||||||
|         return -errno; |         return -errno; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // update history and stats | ||||||
|  |     if (block != emu->history.blocks[0]) { | ||||||
|  |         memcpy(&emu->history.blocks[1], &emu->history.blocks[0], | ||||||
|  |                 sizeof(emu->history) - sizeof(emu->history.blocks[0])); | ||||||
|  |         emu->history.blocks[0] = block; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     emu->stats.prog_count += 1; |     emu->stats.prog_count += 1; | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| @@ -211,13 +266,15 @@ int lfs_emubd_sync(const struct lfs_config *cfg) { | |||||||
|     lfs_emubd_t *emu = cfg->context; |     lfs_emubd_t *emu = cfg->context; | ||||||
|  |  | ||||||
|     // Just write out info/stats for later lookup |     // Just write out info/stats for later lookup | ||||||
|     snprintf(emu->child, LFS_NAME_MAX, "config"); |     snprintf(emu->child, LFS_NAME_MAX, ".config"); | ||||||
|     FILE *f = fopen(emu->path, "w"); |     FILE *f = fopen(emu->path, "w"); | ||||||
|     if (!f) { |     if (!f) { | ||||||
|         return -errno; |         return -errno; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     lfs_emubd_tole32(emu); | ||||||
|     size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f); |     size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f); | ||||||
|  |     lfs_emubd_fromle32(emu); | ||||||
|     if (res < 1) { |     if (res < 1) { | ||||||
|         return -errno; |         return -errno; | ||||||
|     } |     } | ||||||
| @@ -227,13 +284,33 @@ int lfs_emubd_sync(const struct lfs_config *cfg) { | |||||||
|         return -errno; |         return -errno; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     snprintf(emu->child, LFS_NAME_MAX, "stats"); |     snprintf(emu->child, LFS_NAME_MAX, ".stats"); | ||||||
|     f = fopen(emu->path, "w"); |     f = fopen(emu->path, "w"); | ||||||
|     if (!f) { |     if (!f) { | ||||||
|         return -errno; |         return -errno; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     lfs_emubd_tole32(emu); | ||||||
|     res = fwrite(&emu->stats, sizeof(emu->stats), 1, f); |     res = fwrite(&emu->stats, sizeof(emu->stats), 1, f); | ||||||
|  |     lfs_emubd_fromle32(emu); | ||||||
|  |     if (res < 1) { | ||||||
|  |         return -errno; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     err = fclose(f); | ||||||
|  |     if (err) { | ||||||
|  |         return -errno; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     snprintf(emu->child, LFS_NAME_MAX, ".history"); | ||||||
|  |     f = fopen(emu->path, "w"); | ||||||
|  |     if (!f) { | ||||||
|  |         return -errno; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs_emubd_tole32(emu); | ||||||
|  |     res = fwrite(&emu->history, sizeof(emu->history), 1, f); | ||||||
|  |     lfs_emubd_fromle32(emu); | ||||||
|     if (res < 1) { |     if (res < 1) { | ||||||
|         return -errno; |         return -errno; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -45,6 +45,10 @@ typedef struct lfs_emubd { | |||||||
|         uint64_t erase_count; |         uint64_t erase_count; | ||||||
|     } stats; |     } stats; | ||||||
|  |  | ||||||
|  |     struct { | ||||||
|  |         lfs_block_t blocks[4]; | ||||||
|  |     } history; | ||||||
|  |  | ||||||
|     struct { |     struct { | ||||||
|         uint32_t read_size; |         uint32_t read_size; | ||||||
|         uint32_t prog_size; |         uint32_t prog_size; | ||||||
|   | |||||||
							
								
								
									
										416
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										416
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -21,14 +21,14 @@ extern "C" | |||||||
| // Software library version | // Software library version | ||||||
| // Major (top-nibble), incremented on backwards incompatible changes | // Major (top-nibble), incremented on backwards incompatible changes | ||||||
| // Minor (bottom-nibble), incremented on feature additions | // Minor (bottom-nibble), incremented on feature additions | ||||||
| #define LFS_VERSION 0x00010007 | #define LFS_VERSION 0x00020000 | ||||||
| #define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) | #define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) | ||||||
| #define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >>  0)) | #define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >>  0)) | ||||||
|  |  | ||||||
| // Version of On-disk data structures | // Version of On-disk data structures | ||||||
| // Major (top-nibble), incremented on backwards incompatible changes | // Major (top-nibble), incremented on backwards incompatible changes | ||||||
| // Minor (bottom-nibble), incremented on feature additions | // Minor (bottom-nibble), incremented on feature additions | ||||||
| #define LFS_DISK_VERSION 0x00010001 | #define LFS_DISK_VERSION 0x00020000 | ||||||
| #define LFS_DISK_VERSION_MAJOR (0xffff & (LFS_DISK_VERSION >> 16)) | #define LFS_DISK_VERSION_MAJOR (0xffff & (LFS_DISK_VERSION >> 16)) | ||||||
| #define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >>  0)) | #define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >>  0)) | ||||||
|  |  | ||||||
| @@ -44,57 +44,98 @@ typedef int32_t  lfs_soff_t; | |||||||
|  |  | ||||||
| typedef uint32_t lfs_block_t; | typedef uint32_t lfs_block_t; | ||||||
|  |  | ||||||
| // Max name size in bytes | // Maximum name size in bytes, may be redefined to reduce the size of the | ||||||
|  | // info struct. Limited to <= 1022. Stored in superblock and must be | ||||||
|  | // respected by other littlefs drivers. | ||||||
| #ifndef LFS_NAME_MAX | #ifndef LFS_NAME_MAX | ||||||
| #define LFS_NAME_MAX 255 | #define LFS_NAME_MAX 255 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| // Max file size in bytes | // Maximum size of a file in bytes, may be redefined to limit to support other | ||||||
|  | // drivers. Limited on disk to <= 4294967296. However, above 2147483647 the | ||||||
|  | // functions lfs_file_seek, lfs_file_size, and lfs_file_tell will return | ||||||
|  | // incorrect values due to using signed integers. Stored in superblock and | ||||||
|  | // must be respected by other littlefs drivers. | ||||||
| #ifndef LFS_FILE_MAX | #ifndef LFS_FILE_MAX | ||||||
| #define LFS_FILE_MAX 2147483647 | #define LFS_FILE_MAX 2147483647 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | // Maximum size of custom attributes in bytes, may be redefined, but there is | ||||||
|  | // no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022. | ||||||
|  | #ifndef LFS_ATTR_MAX | ||||||
|  | #define LFS_ATTR_MAX 1022 | ||||||
|  | #endif | ||||||
|  |  | ||||||
| // Possible error codes, these are negative to allow | // Possible error codes, these are negative to allow | ||||||
| // valid positive return values | // valid positive return values | ||||||
| enum lfs_error { | enum lfs_error { | ||||||
|     LFS_ERR_OK       = 0,    // No error |     LFS_ERR_OK          = 0,    // No error | ||||||
|     LFS_ERR_IO       = -5,   // Error during device operation |     LFS_ERR_IO          = -5,   // Error during device operation | ||||||
|     LFS_ERR_CORRUPT  = -52,  // Corrupted |     LFS_ERR_CORRUPT     = -84,  // Corrupted | ||||||
|     LFS_ERR_NOENT    = -2,   // No directory entry |     LFS_ERR_NOENT       = -2,   // No directory entry | ||||||
|     LFS_ERR_EXIST    = -17,  // Entry already exists |     LFS_ERR_EXIST       = -17,  // Entry already exists | ||||||
|     LFS_ERR_NOTDIR   = -20,  // Entry is not a dir |     LFS_ERR_NOTDIR      = -20,  // Entry is not a dir | ||||||
|     LFS_ERR_ISDIR    = -21,  // Entry is a dir |     LFS_ERR_ISDIR       = -21,  // Entry is a dir | ||||||
|     LFS_ERR_NOTEMPTY = -39,  // Dir is not empty |     LFS_ERR_NOTEMPTY    = -39,  // Dir is not empty | ||||||
|     LFS_ERR_BADF     = -9,   // Bad file number |     LFS_ERR_BADF        = -9,   // Bad file number | ||||||
|     LFS_ERR_FBIG     = -27,  // File too large |     LFS_ERR_FBIG        = -27,  // File too large | ||||||
|     LFS_ERR_INVAL    = -22,  // Invalid parameter |     LFS_ERR_INVAL       = -22,  // Invalid parameter | ||||||
|     LFS_ERR_NOSPC    = -28,  // No space left on device |     LFS_ERR_NOSPC       = -28,  // No space left on device | ||||||
|     LFS_ERR_NOMEM    = -12,  // No more memory available |     LFS_ERR_NOMEM       = -12,  // No more memory available | ||||||
|  |     LFS_ERR_NOATTR      = -61,  // No data/attr available | ||||||
|  |     LFS_ERR_NAMETOOLONG = -36,  // File name too long | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // File types | // File types | ||||||
| enum lfs_type { | enum lfs_type { | ||||||
|     LFS_TYPE_REG        = 0x11, |     // file types | ||||||
|     LFS_TYPE_DIR        = 0x22, |     LFS_TYPE_REG            = 0x001, | ||||||
|     LFS_TYPE_SUPERBLOCK = 0x2e, |     LFS_TYPE_DIR            = 0x002, | ||||||
|  |  | ||||||
|  |     // internally used types | ||||||
|  |     LFS_TYPE_SPLICE         = 0x400, | ||||||
|  |     LFS_TYPE_NAME           = 0x000, | ||||||
|  |     LFS_TYPE_STRUCT         = 0x200, | ||||||
|  |     LFS_TYPE_USERATTR       = 0x300, | ||||||
|  |     LFS_TYPE_FROM           = 0x100, | ||||||
|  |     LFS_TYPE_TAIL           = 0x600, | ||||||
|  |     LFS_TYPE_GLOBALS        = 0x700, | ||||||
|  |     LFS_TYPE_CRC            = 0x500, | ||||||
|  |  | ||||||
|  |     // internally used type specializations | ||||||
|  |     LFS_TYPE_CREATE         = 0x401, | ||||||
|  |     LFS_TYPE_DELETE         = 0x4ff, | ||||||
|  |     LFS_TYPE_SUPERBLOCK     = 0x0ff, | ||||||
|  |     LFS_TYPE_DIRSTRUCT      = 0x200, | ||||||
|  |     LFS_TYPE_CTZSTRUCT      = 0x202, | ||||||
|  |     LFS_TYPE_INLINESTRUCT   = 0x201, | ||||||
|  |     LFS_TYPE_SOFTTAIL       = 0x600, | ||||||
|  |     LFS_TYPE_HARDTAIL       = 0x601, | ||||||
|  |     LFS_TYPE_MOVESTATE      = 0x7ff, | ||||||
|  |  | ||||||
|  |     // internal chip sources | ||||||
|  |     LFS_FROM_NOOP           = 0x000, | ||||||
|  |     LFS_FROM_MOVE           = 0x101, | ||||||
|  |     LFS_FROM_USERATTRS      = 0x102, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // File open flags | // File open flags | ||||||
| enum lfs_open_flags { | enum lfs_open_flags { | ||||||
|     // open flags |     // open flags | ||||||
|     LFS_O_RDONLY = 1,        // Open a file as read only |     LFS_O_RDONLY = 1,         // Open a file as read only | ||||||
|     LFS_O_WRONLY = 2,        // Open a file as write only |     LFS_O_WRONLY = 2,         // Open a file as write only | ||||||
|     LFS_O_RDWR   = 3,        // Open a file as read and write |     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_CREAT  = 0x0100,    // Create a file if it does not exist | ||||||
|     LFS_O_EXCL   = 0x0200,   // Fail if a file already exists |     LFS_O_EXCL   = 0x0200,    // Fail if a file already exists | ||||||
|     LFS_O_TRUNC  = 0x0400,   // Truncate the existing file to zero size |     LFS_O_TRUNC  = 0x0400,    // Truncate the existing file to zero size | ||||||
|     LFS_O_APPEND = 0x0800,   // Move to end of file on every write |     LFS_O_APPEND = 0x0800,    // Move to end of file on every write | ||||||
|  |  | ||||||
|     // internally used flags |     // internally used flags | ||||||
|     LFS_F_DIRTY   = 0x10000, // File does not match storage |     LFS_F_DIRTY   = 0x010000, // File does not match storage | ||||||
|     LFS_F_WRITING = 0x20000, // File has been written since last flush |     LFS_F_WRITING = 0x020000, // File has been written since last flush | ||||||
|     LFS_F_READING = 0x40000, // File has been read since last flush |     LFS_F_READING = 0x040000, // File has been read since last flush | ||||||
|     LFS_F_ERRED   = 0x80000, // An error occured during write |     LFS_F_ERRED   = 0x080000, // An error occured during write | ||||||
|  |     LFS_F_INLINE  = 0x100000, // Currently inlined in directory entry | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // File seek flags | // File seek flags | ||||||
| @@ -132,52 +173,68 @@ struct lfs_config { | |||||||
|     // are propogated to the user. |     // are propogated to the user. | ||||||
|     int (*sync)(const struct lfs_config *c); |     int (*sync)(const struct lfs_config *c); | ||||||
|  |  | ||||||
|     // Minimum size of a block read. This determines the size of read buffers. |     // Minimum size of a block read. All read operations will be a | ||||||
|     // This may be larger than the physical read size to improve performance |     // multiple of this value. | ||||||
|     // by caching more of the block device. |  | ||||||
|     lfs_size_t read_size; |     lfs_size_t read_size; | ||||||
|  |  | ||||||
|     // Minimum size of a block program. This determines the size of program |     // Minimum size of a block program. All program operations will be a | ||||||
|     // buffers. This may be larger than the physical program size to improve |     // multiple of this value. | ||||||
|     // performance by caching more of the block device. |  | ||||||
|     // Must be a multiple of the read size. |  | ||||||
|     lfs_size_t prog_size; |     lfs_size_t prog_size; | ||||||
|  |  | ||||||
|     // Size of an erasable block. This does not impact ram consumption and |     // Size of an erasable block. This does not impact ram consumption and | ||||||
|     // may be larger than the physical erase size. However, this should be |     // may be larger than the physical erase size. However, non-inlined files | ||||||
|     // kept small as each file currently takes up an entire block. |     // take up at minimum one block. Must be a multiple of the read | ||||||
|     // Must be a multiple of the program size. |     // and program sizes. | ||||||
|     lfs_size_t block_size; |     lfs_size_t block_size; | ||||||
|  |  | ||||||
|     // Number of erasable blocks on the device. |     // Number of erasable blocks on the device. | ||||||
|     lfs_size_t block_count; |     lfs_size_t block_count; | ||||||
|  |  | ||||||
|     // Number of blocks to lookahead during block allocation. A larger |     // Number of erase cycles before we should move data to another block. | ||||||
|     // lookahead reduces the number of passes required to allocate a block. |     // May be zero, in which case no block-level wear-leveling is performed. | ||||||
|     // The lookahead buffer requires only 1 bit per block so it can be quite |     uint32_t block_cycles; | ||||||
|     // large with little ram impact. Should be a multiple of 32. |  | ||||||
|     lfs_size_t lookahead; |  | ||||||
|  |  | ||||||
|     // Optional, statically allocated read buffer. Must be read sized. |     // Size of block caches. Each cache buffers a portion of a block in RAM. | ||||||
|  |     // The littlefs needs a read cache, a program cache, and one additional | ||||||
|  |     // cache per file. Larger caches can improve performance by storing more | ||||||
|  |     // data and reducing the number of disk accesses. Must be a multiple of | ||||||
|  |     // the read and program sizes, and a factor of the block size. | ||||||
|  |     lfs_size_t cache_size; | ||||||
|  |  | ||||||
|  |     // Size of the lookahead buffer in bytes. A larger lookahead buffer | ||||||
|  |     // increases the number of blocks found during an allocation pass. The | ||||||
|  |     // lookahead buffer is stored as a compact bitmap, so each byte of RAM | ||||||
|  |     // can track 8 blocks. Must be a multiple of 4. | ||||||
|  |     lfs_size_t lookahead_size; | ||||||
|  |  | ||||||
|  |     // Optional statically allocated read buffer. Must be cache_size. | ||||||
|  |     // By default lfs_malloc is used to allocate this buffer. | ||||||
|     void *read_buffer; |     void *read_buffer; | ||||||
|  |  | ||||||
|     // Optional, statically allocated program buffer. Must be program sized. |     // Optional statically allocated program buffer. Must be cache_size. | ||||||
|  |     // By default lfs_malloc is used to allocate this buffer. | ||||||
|     void *prog_buffer; |     void *prog_buffer; | ||||||
|  |  | ||||||
|     // Optional, statically allocated lookahead buffer. Must be 1 bit per |     // Optional statically allocated lookahead buffer. Must be lookahead_size | ||||||
|     // lookahead block. |     // and aligned to a 64-bit boundary. By default lfs_malloc is used to | ||||||
|  |     // allocate this buffer. | ||||||
|     void *lookahead_buffer; |     void *lookahead_buffer; | ||||||
|  |  | ||||||
|     // Optional, statically allocated buffer for files. Must be program sized. |     // Optional upper limit on length of file names in bytes. No downside for | ||||||
|     // If enabled, only one file may be opened at a time. |     // larger names except the size of the info struct which is controlled by | ||||||
|     void *file_buffer; |     // the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in | ||||||
| }; |     // superblock and must be respected by other littlefs drivers. | ||||||
|  |     lfs_size_t name_max; | ||||||
|  |  | ||||||
| // Optional configuration provided during lfs_file_opencfg |     // Optional upper limit on files in bytes. No downside for larger files | ||||||
| struct lfs_file_config { |     // but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored | ||||||
|     // Optional, statically allocated buffer for files. Must be program sized. |     // in superblock and must be respected by other littlefs drivers. | ||||||
|     // If NULL, malloc will be used by default. |     lfs_size_t file_max; | ||||||
|     void *buffer; |  | ||||||
|  |     // Optional upper limit on custom attributes in bytes. No downside for | ||||||
|  |     // larger attributes size but must be <= LFS_ATTR_MAX. Defaults to | ||||||
|  |     // LFS_ATTR_MAX when zero. | ||||||
|  |     lfs_size_t attr_max; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // File info structure | // File info structure | ||||||
| @@ -185,108 +242,149 @@ struct lfs_info { | |||||||
|     // Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR |     // Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR | ||||||
|     uint8_t type; |     uint8_t type; | ||||||
|  |  | ||||||
|     // Size of the file, only valid for REG files |     // Size of the file, only valid for REG files. Limited to 32-bits. | ||||||
|     lfs_size_t size; |     lfs_size_t size; | ||||||
|  |  | ||||||
|     // Name of the file stored as a null-terminated string |     // Name of the file stored as a null-terminated string. Limited to | ||||||
|  |     // LFS_NAME_MAX+1, which can be changed by redefining LFS_NAME_MAX to | ||||||
|  |     // reduce RAM. LFS_NAME_MAX is stored in superblock and must be | ||||||
|  |     // respected by other littlefs drivers. | ||||||
|     char name[LFS_NAME_MAX+1]; |     char name[LFS_NAME_MAX+1]; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | // Custom attribute structure, used to describe custom attributes | ||||||
|  | // committed atomically during file writes. | ||||||
|  | struct lfs_attr { | ||||||
|  |     // 8-bit type of attribute, provided by user and used to | ||||||
|  |     // identify the attribute | ||||||
|  |     uint8_t type; | ||||||
|  |  | ||||||
| /// littlefs data structures /// |     // Pointer to buffer containing the attribute | ||||||
| typedef struct lfs_entry { |     void *buffer; | ||||||
|     lfs_off_t off; |  | ||||||
|  |  | ||||||
|     struct lfs_disk_entry { |     // Size of attribute in bytes, limited to LFS_ATTR_MAX | ||||||
|         uint8_t type; |     lfs_size_t size; | ||||||
|         uint8_t elen; | }; | ||||||
|         uint8_t alen; |  | ||||||
|         uint8_t nlen; |  | ||||||
|         union { |  | ||||||
|             struct { |  | ||||||
|                 lfs_block_t head; |  | ||||||
|                 lfs_size_t size; |  | ||||||
|             } file; |  | ||||||
|             lfs_block_t dir[2]; |  | ||||||
|         } u; |  | ||||||
|     } d; |  | ||||||
| } lfs_entry_t; |  | ||||||
|  |  | ||||||
|  | // Optional configuration provided during lfs_file_opencfg | ||||||
|  | struct lfs_file_config { | ||||||
|  |     // Optional statically allocated file buffer. Must be cache_size. | ||||||
|  |     // By default lfs_malloc is used to allocate this buffer. | ||||||
|  |     void *buffer; | ||||||
|  |  | ||||||
|  |     // Optional list of custom attributes related to the file. If the file | ||||||
|  |     // is opened with read access, these attributes will be read from disk | ||||||
|  |     // during the open call. If the file is opened with write access, the | ||||||
|  |     // attributes will be written to disk every file sync or close. This | ||||||
|  |     // write occurs atomically with update to the file's contents. | ||||||
|  |     // | ||||||
|  |     // Custom attributes are uniquely identified by an 8-bit type and limited | ||||||
|  |     // to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller | ||||||
|  |     // than the buffer, it will be padded with zeros. If the stored attribute | ||||||
|  |     // is larger, then it will be silently truncated. If the attribute is not | ||||||
|  |     // found, it will be created implicitly. | ||||||
|  |     struct lfs_attr *attrs; | ||||||
|  |  | ||||||
|  |     // Number of custom attributes in the list | ||||||
|  |     lfs_size_t attr_count; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /// internal littlefs data structures /// | ||||||
| typedef struct lfs_cache { | typedef struct lfs_cache { | ||||||
|     lfs_block_t block; |     lfs_block_t block; | ||||||
|     lfs_off_t off; |     lfs_off_t off; | ||||||
|  |     lfs_size_t size; | ||||||
|     uint8_t *buffer; |     uint8_t *buffer; | ||||||
| } lfs_cache_t; | } lfs_cache_t; | ||||||
|  |  | ||||||
|  | typedef struct lfs_mdir { | ||||||
|  |     lfs_block_t pair[2]; | ||||||
|  |     uint32_t rev; | ||||||
|  |     lfs_off_t off; | ||||||
|  |     uint32_t etag; | ||||||
|  |     uint16_t count; | ||||||
|  |     bool erased; | ||||||
|  |     bool split; | ||||||
|  |     lfs_block_t tail[2]; | ||||||
|  | } lfs_mdir_t; | ||||||
|  |  | ||||||
|  | // littlefs directory type | ||||||
|  | typedef struct lfs_dir { | ||||||
|  |     struct lfs_dir *next; | ||||||
|  |     uint16_t id; | ||||||
|  |     uint8_t type; | ||||||
|  |     lfs_mdir_t m; | ||||||
|  |  | ||||||
|  |     lfs_off_t pos; | ||||||
|  |     lfs_block_t head[2]; | ||||||
|  | } lfs_dir_t; | ||||||
|  |  | ||||||
|  | // littlefs file type | ||||||
| typedef struct lfs_file { | typedef struct lfs_file { | ||||||
|     struct lfs_file *next; |     struct lfs_file *next; | ||||||
|     lfs_block_t pair[2]; |     uint16_t id; | ||||||
|     lfs_off_t poff; |     uint8_t type; | ||||||
|  |     lfs_mdir_t m; | ||||||
|  |  | ||||||
|     lfs_block_t head; |     struct lfs_ctz { | ||||||
|     lfs_size_t size; |         lfs_block_t head; | ||||||
|  |         lfs_size_t size; | ||||||
|  |     } ctz; | ||||||
|  |  | ||||||
|     const struct lfs_file_config *cfg; |  | ||||||
|     uint32_t flags; |     uint32_t flags; | ||||||
|     lfs_off_t pos; |     lfs_off_t pos; | ||||||
|     lfs_block_t block; |     lfs_block_t block; | ||||||
|     lfs_off_t off; |     lfs_off_t off; | ||||||
|     lfs_cache_t cache; |     lfs_cache_t cache; | ||||||
|  |  | ||||||
|  |     const struct lfs_file_config *cfg; | ||||||
| } lfs_file_t; | } lfs_file_t; | ||||||
|  |  | ||||||
| typedef struct lfs_dir { |  | ||||||
|     struct lfs_dir *next; |  | ||||||
|     lfs_block_t pair[2]; |  | ||||||
|     lfs_off_t off; |  | ||||||
|  |  | ||||||
|     lfs_block_t head[2]; |  | ||||||
|     lfs_off_t pos; |  | ||||||
|  |  | ||||||
|     struct lfs_disk_dir { |  | ||||||
|         uint32_t rev; |  | ||||||
|         lfs_size_t size; |  | ||||||
|         lfs_block_t tail[2]; |  | ||||||
|     } d; |  | ||||||
| } lfs_dir_t; |  | ||||||
|  |  | ||||||
| typedef struct lfs_superblock { | typedef struct lfs_superblock { | ||||||
|     lfs_off_t off; |     uint32_t version; | ||||||
|  |     lfs_size_t block_size; | ||||||
|     struct lfs_disk_superblock { |     lfs_size_t block_count; | ||||||
|         uint8_t type; |     lfs_size_t name_max; | ||||||
|         uint8_t elen; |     lfs_size_t file_max; | ||||||
|         uint8_t alen; |     lfs_size_t attr_max; | ||||||
|         uint8_t nlen; |  | ||||||
|         lfs_block_t root[2]; |  | ||||||
|         uint32_t block_size; |  | ||||||
|         uint32_t block_count; |  | ||||||
|         uint32_t version; |  | ||||||
|         char magic[8]; |  | ||||||
|     } d; |  | ||||||
| } lfs_superblock_t; | } lfs_superblock_t; | ||||||
|  |  | ||||||
| typedef struct lfs_free { | // The littlefs filesystem type | ||||||
|     lfs_block_t off; |  | ||||||
|     lfs_block_t size; |  | ||||||
|     lfs_block_t i; |  | ||||||
|     lfs_block_t ack; |  | ||||||
|     uint32_t *buffer; |  | ||||||
| } lfs_free_t; |  | ||||||
|  |  | ||||||
| // The littlefs type |  | ||||||
| typedef struct lfs { | typedef struct lfs { | ||||||
|     const struct lfs_config *cfg; |  | ||||||
|  |  | ||||||
|     lfs_block_t root[2]; |  | ||||||
|     lfs_file_t *files; |  | ||||||
|     lfs_dir_t *dirs; |  | ||||||
|  |  | ||||||
|     lfs_cache_t rcache; |     lfs_cache_t rcache; | ||||||
|     lfs_cache_t pcache; |     lfs_cache_t pcache; | ||||||
|  |  | ||||||
|     lfs_free_t free; |     lfs_block_t root[2]; | ||||||
|     bool deorphaned; |     struct lfs_mlist { | ||||||
|     bool moving; |         struct lfs_mlist *next; | ||||||
|  |         uint16_t id; | ||||||
|  |         uint8_t type; | ||||||
|  |         lfs_mdir_t m; | ||||||
|  |     } *mlist; | ||||||
|  |     uint32_t seed; | ||||||
|  |  | ||||||
|  |     struct lfs_gstate { | ||||||
|  |         uint32_t tag; | ||||||
|  |         lfs_block_t pair[2]; | ||||||
|  |     } gstate, gpending, gdelta; | ||||||
|  |  | ||||||
|  |     struct lfs_free { | ||||||
|  |         lfs_block_t off; | ||||||
|  |         lfs_block_t size; | ||||||
|  |         lfs_block_t i; | ||||||
|  |         lfs_block_t ack; | ||||||
|  |         uint32_t *buffer; | ||||||
|  |     } free; | ||||||
|  |  | ||||||
|  |     const struct lfs_config *cfg; | ||||||
|  |     lfs_size_t name_max; | ||||||
|  |     lfs_size_t file_max; | ||||||
|  |     lfs_size_t attr_max; | ||||||
|  |  | ||||||
|  | #ifdef LFS_MIGRATE | ||||||
|  |     struct lfs1 *lfs1; | ||||||
|  | #endif | ||||||
| } lfs_t; | } lfs_t; | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -339,6 +437,38 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath); | |||||||
| // Returns a negative error code on failure. | // Returns a negative error code on failure. | ||||||
| int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info); | int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info); | ||||||
|  |  | ||||||
|  | // Get a custom attribute | ||||||
|  | // | ||||||
|  | // Custom attributes are uniquely identified by an 8-bit type and limited | ||||||
|  | // to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller than | ||||||
|  | // the buffer, it will be padded with zeros. If the stored attribute is larger, | ||||||
|  | // then it will be silently truncated. If no attribute is found, the error | ||||||
|  | // LFS_ERR_NOATTR is returned and the buffer is filled with zeros. | ||||||
|  | // | ||||||
|  | // Returns the size of the attribute, or a negative error code on failure. | ||||||
|  | // Note, the returned size is the size of the attribute on disk, irrespective | ||||||
|  | // of the size of the buffer. This can be used to dynamically allocate a buffer | ||||||
|  | // or check for existance. | ||||||
|  | lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, | ||||||
|  |         uint8_t type, void *buffer, lfs_size_t size); | ||||||
|  |  | ||||||
|  | // Set custom attributes | ||||||
|  | // | ||||||
|  | // Custom attributes are uniquely identified by an 8-bit type and limited | ||||||
|  | // to LFS_ATTR_MAX bytes. If an attribute is not found, it will be | ||||||
|  | // implicitly created. | ||||||
|  | // | ||||||
|  | // 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); | ||||||
|  |  | ||||||
|  | // 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); | ||||||
|  |  | ||||||
|  |  | ||||||
| /// File operations /// | /// File operations /// | ||||||
|  |  | ||||||
| @@ -473,7 +603,15 @@ lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir); | |||||||
| int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); | int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); | ||||||
|  |  | ||||||
|  |  | ||||||
| /// Miscellaneous littlefs specific operations /// | /// Filesystem-level filesystem operations | ||||||
|  |  | ||||||
|  | // Finds the current size of the filesystem | ||||||
|  | // | ||||||
|  | // Note: Result is best effort. If files share COW structures, the returned | ||||||
|  | // size may be larger than the filesystem actually is. | ||||||
|  | // | ||||||
|  | // Returns the number of allocated blocks, or a negative error code on failure. | ||||||
|  | lfs_ssize_t lfs_fs_size(lfs_t *lfs); | ||||||
|  |  | ||||||
| // Traverse through all blocks in use by the filesystem | // Traverse through all blocks in use by the filesystem | ||||||
| // | // | ||||||
| @@ -482,16 +620,22 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); | |||||||
| // blocks are in use or how much of the storage is available. | // blocks are in use or how much of the storage is available. | ||||||
| // | // | ||||||
| // Returns a negative error code on failure. | // Returns a negative error code on failure. | ||||||
| int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); | int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); | ||||||
|  |  | ||||||
| // Prunes any recoverable errors that may have occured in the filesystem | #ifdef LFS_MIGRATE | ||||||
|  | // Attempts to migrate a previous version of littlefs | ||||||
| // | // | ||||||
| // Not needed to be called by user unless an operation is interrupted | // Behaves similarly to the lfs_format function. Attempts to mount | ||||||
| // but the filesystem is still mounted. This is already called on first | // the previous version of littlefs and update the filesystem so it can be | ||||||
| // allocation. | // mounted with the current version of littlefs. | ||||||
|  | // | ||||||
|  | // Requires a littlefs object and config struct. This clobbers the littlefs | ||||||
|  | // object, and does not leave the filesystem mounted. The config struct must | ||||||
|  | // be zeroed for defaults and backwards compatibility. | ||||||
| // | // | ||||||
| // Returns a negative error code on failure. | // Returns a negative error code on failure. | ||||||
| int lfs_deorphan(lfs_t *lfs); | int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|  |  | ||||||
|  |  | ||||||
| // Software CRC implementation with small lookup table | // Software CRC implementation with small lookup table | ||||||
| void lfs_crc(uint32_t *restrict crc, const void *buffer, size_t size) { | uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) { | ||||||
|     static const uint32_t rtable[16] = { |     static const uint32_t rtable[16] = { | ||||||
|         0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, |         0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, | ||||||
|         0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, |         0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, | ||||||
| @@ -22,9 +22,11 @@ void lfs_crc(uint32_t *restrict crc, const void *buffer, size_t size) { | |||||||
|     const uint8_t *data = buffer; |     const uint8_t *data = buffer; | ||||||
|  |  | ||||||
|     for (size_t i = 0; i < size; i++) { |     for (size_t i = 0; i < size; i++) { | ||||||
|         *crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 0)) & 0xf]; |         crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 0)) & 0xf]; | ||||||
|         *crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 4)) & 0xf]; |         crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     return crc; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										44
									
								
								lfs_util.h
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								lfs_util.h
									
									
									
									
									
								
							| @@ -11,8 +11,8 @@ | |||||||
| // LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h). | // LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h). | ||||||
| // | // | ||||||
| // If LFS_CONFIG is used, none of the default utils will be emitted and must be | // If LFS_CONFIG is used, none of the default utils will be emitted and must be | ||||||
| // provided by the config file. To start I would suggest copying lfs_util.h and | // provided by the config file. To start, I would suggest copying lfs_util.h | ||||||
| // modifying as needed. | // and modifying as needed. | ||||||
| #ifdef LFS_CONFIG | #ifdef LFS_CONFIG | ||||||
| #define LFS_STRINGIZE(x) LFS_STRINGIZE2(x) | #define LFS_STRINGIZE(x) LFS_STRINGIZE2(x) | ||||||
| #define LFS_STRINGIZE2(x) #x | #define LFS_STRINGIZE2(x) #x | ||||||
| @@ -23,6 +23,7 @@ | |||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  | #include <inttypes.h> | ||||||
|  |  | ||||||
| #ifndef LFS_NO_MALLOC | #ifndef LFS_NO_MALLOC | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| @@ -87,6 +88,15 @@ static inline uint32_t lfs_min(uint32_t a, uint32_t b) { | |||||||
|     return (a < b) ? a : b; |     return (a < b) ? a : b; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Align to nearest multiple of a size | ||||||
|  | static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) { | ||||||
|  |     return a - (a % alignment); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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 next smallest power of 2 less than or equal to a | ||||||
| static inline uint32_t lfs_npw2(uint32_t a) { | static inline uint32_t lfs_npw2(uint32_t a) { | ||||||
| #if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) | #if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) | ||||||
| @@ -130,7 +140,7 @@ static inline int lfs_scmp(uint32_t a, uint32_t b) { | |||||||
|     return (int)(unsigned)(a - b); |     return (int)(unsigned)(a - b); | ||||||
| } | } | ||||||
|  |  | ||||||
| // Convert from 32-bit little-endian to native order | // Convert between 32-bit little-endian and native order | ||||||
| static inline uint32_t lfs_fromle32(uint32_t a) { | static inline uint32_t lfs_fromle32(uint32_t a) { | ||||||
| #if !defined(LFS_NO_INTRINSICS) && ( \ | #if !defined(LFS_NO_INTRINSICS) && ( \ | ||||||
|     (defined(  BYTE_ORDER  ) &&   BYTE_ORDER   ==   ORDER_LITTLE_ENDIAN  ) || \ |     (defined(  BYTE_ORDER  ) &&   BYTE_ORDER   ==   ORDER_LITTLE_ENDIAN  ) || \ | ||||||
| @@ -150,15 +160,39 @@ static inline uint32_t lfs_fromle32(uint32_t a) { | |||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| // Convert to 32-bit little-endian from native order |  | ||||||
| static inline uint32_t lfs_tole32(uint32_t a) { | static inline uint32_t lfs_tole32(uint32_t a) { | ||||||
|     return lfs_fromle32(a); |     return lfs_fromle32(a); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Convert between 32-bit big-endian and native order | ||||||
|  | static inline uint32_t lfs_frombe32(uint32_t a) { | ||||||
|  | #if !defined(LFS_NO_INTRINSICS) && ( \ | ||||||
|  |     (defined(  BYTE_ORDER  ) &&   BYTE_ORDER   ==   ORDER_LITTLE_ENDIAN  ) || \ | ||||||
|  |     (defined(__BYTE_ORDER  ) && __BYTE_ORDER   == __ORDER_LITTLE_ENDIAN  ) || \ | ||||||
|  |     (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) | ||||||
|  |     return __builtin_bswap32(a); | ||||||
|  | #elif !defined(LFS_NO_INTRINSICS) && ( \ | ||||||
|  |     (defined(  BYTE_ORDER  ) &&   BYTE_ORDER   ==   ORDER_BIG_ENDIAN  ) || \ | ||||||
|  |     (defined(__BYTE_ORDER  ) && __BYTE_ORDER   == __ORDER_BIG_ENDIAN  ) || \ | ||||||
|  |     (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) | ||||||
|  |     return a; | ||||||
|  | #else | ||||||
|  |     return (((uint8_t*)&a)[0] << 24) | | ||||||
|  |            (((uint8_t*)&a)[1] << 16) | | ||||||
|  |            (((uint8_t*)&a)[2] <<  8) | | ||||||
|  |            (((uint8_t*)&a)[3] <<  0); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline uint32_t lfs_tobe32(uint32_t a) { | ||||||
|  |     return lfs_frombe32(a); | ||||||
|  | } | ||||||
|  |  | ||||||
| // Calculate CRC-32 with polynomial = 0x04c11db7 | // Calculate CRC-32 with polynomial = 0x04c11db7 | ||||||
| void lfs_crc(uint32_t *crc, const void *buffer, size_t size); | uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size); | ||||||
|  |  | ||||||
| // Allocate memory, only used if buffers are not provided to littlefs | // Allocate memory, only used if buffers are not provided to littlefs | ||||||
|  | // Note, memory must be 64-bit aligned | ||||||
| static inline void *lfs_malloc(size_t size) { | static inline void *lfs_malloc(size_t size) { | ||||||
| #ifndef LFS_NO_MALLOC | #ifndef LFS_NO_MALLOC | ||||||
|     return malloc(size); |     return malloc(size); | ||||||
|   | |||||||
							
								
								
									
										44
									
								
								tests/corrupt.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										44
									
								
								tests/corrupt.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | #!/usr/bin/env python | ||||||
|  |  | ||||||
|  | import struct | ||||||
|  | import sys | ||||||
|  | import os | ||||||
|  | import argparse | ||||||
|  |  | ||||||
|  | def corrupt(block): | ||||||
|  |     with open(block, 'r+b') as file: | ||||||
|  |         # skip rev | ||||||
|  |         file.read(4) | ||||||
|  |  | ||||||
|  |         # go to last commit | ||||||
|  |         tag = 0xffffffff | ||||||
|  |         while True: | ||||||
|  |             try: | ||||||
|  |                 ntag, = struct.unpack('>I', file.read(4)) | ||||||
|  |             except struct.error: | ||||||
|  |                 break | ||||||
|  |  | ||||||
|  |             tag ^= ntag | ||||||
|  |             size = (tag & 0x3ff) if (tag & 0x3ff) != 0x3ff else 0 | ||||||
|  |             file.seek(size, os.SEEK_CUR) | ||||||
|  |  | ||||||
|  |         # lob off last 3 bytes | ||||||
|  |         file.seek(-(size + 3), os.SEEK_CUR) | ||||||
|  |         file.truncate() | ||||||
|  |  | ||||||
|  | def main(args): | ||||||
|  |     if args.n or not args.blocks: | ||||||
|  |         with open('blocks/.history', 'rb') as file: | ||||||
|  |             for i in range(int(args.n or 1)): | ||||||
|  |                 last, = struct.unpack('<I', file.read(4)) | ||||||
|  |                 args.blocks.append('blocks/%x' % last) | ||||||
|  |  | ||||||
|  |     for block in args.blocks: | ||||||
|  |         print 'corrupting %s' % block | ||||||
|  |         corrupt(block) | ||||||
|  |  | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     parser = argparse.ArgumentParser() | ||||||
|  |     parser.add_argument('-n') | ||||||
|  |     parser.add_argument('blocks', nargs='*') | ||||||
|  |     main(parser.parse_args()) | ||||||
							
								
								
									
										112
									
								
								tests/debug.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										112
									
								
								tests/debug.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,112 @@ | |||||||
|  | #!/usr/bin/env python2 | ||||||
|  |  | ||||||
|  | import struct | ||||||
|  | import binascii | ||||||
|  |  | ||||||
|  | TYPES = { | ||||||
|  |     (0x700, 0x400): 'splice', | ||||||
|  |     (0x7ff, 0x401): 'create', | ||||||
|  |     (0x7ff, 0x4ff): 'delete', | ||||||
|  |     (0x700, 0x000): 'name', | ||||||
|  |     (0x7ff, 0x001): 'name reg', | ||||||
|  |     (0x7ff, 0x002): 'name dir', | ||||||
|  |     (0x7ff, 0x0ff): 'name superblock', | ||||||
|  |     (0x700, 0x200): 'struct', | ||||||
|  |     (0x7ff, 0x200): 'struct dir', | ||||||
|  |     (0x7ff, 0x202): 'struct ctz', | ||||||
|  |     (0x7ff, 0x201): 'struct inline', | ||||||
|  |     (0x700, 0x300): 'userattr', | ||||||
|  |     (0x700, 0x600): 'tail', | ||||||
|  |     (0x7ff, 0x600): 'tail soft', | ||||||
|  |     (0x7ff, 0x601): 'tail hard', | ||||||
|  |     (0x700, 0x700): 'gstate', | ||||||
|  |     (0x7ff, 0x7ff): 'gstate move', | ||||||
|  |     (0x700, 0x500): 'crc', | ||||||
|  | } | ||||||
|  |  | ||||||
|  | def typeof(type): | ||||||
|  |     for prefix in range(12): | ||||||
|  |         mask = 0x7ff & ~((1 << prefix)-1) | ||||||
|  |         if (mask, type & mask) in TYPES: | ||||||
|  |             return TYPES[mask, type & mask] + ( | ||||||
|  |                 ' %0*x' % (prefix/4, type & ((1 << prefix)-1)) | ||||||
|  |                 if prefix else '') | ||||||
|  |     else: | ||||||
|  |         return '%02x' % type | ||||||
|  |  | ||||||
|  | def main(*blocks): | ||||||
|  |     # find most recent block | ||||||
|  |     file = None | ||||||
|  |     rev = None | ||||||
|  |     crc = None | ||||||
|  |     versions = [] | ||||||
|  |  | ||||||
|  |     for block in blocks: | ||||||
|  |         try: | ||||||
|  |             nfile = open(block, 'rb') | ||||||
|  |             ndata = nfile.read(4) | ||||||
|  |             ncrc = binascii.crc32(ndata) | ||||||
|  |             nrev, = struct.unpack('<I', ndata) | ||||||
|  |  | ||||||
|  |             assert rev != nrev | ||||||
|  |             if not file or ((rev - nrev) & 0x80000000): | ||||||
|  |                 file = nfile | ||||||
|  |                 rev = nrev | ||||||
|  |                 crc = ncrc | ||||||
|  |  | ||||||
|  |             versions.append((nrev, '%s (rev %d)' % (block, nrev))) | ||||||
|  |         except (IOError, struct.error): | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |     if not file: | ||||||
|  |         print 'Bad metadata pair {%s}' % ', '.join(blocks) | ||||||
|  |         return 1 | ||||||
|  |  | ||||||
|  |     print "--- %s ---" % ', '.join(v for _,v in sorted(versions, reverse=True)) | ||||||
|  |  | ||||||
|  |     # go through each tag, print useful information | ||||||
|  |     print "%-4s  %-8s  %-14s  %3s %4s  %s" % ( | ||||||
|  |         'off', 'tag', 'type', 'id', 'len', 'dump') | ||||||
|  |  | ||||||
|  |     tag = 0xffffffff | ||||||
|  |     off = 4 | ||||||
|  |     while True: | ||||||
|  |         try: | ||||||
|  |             data = file.read(4) | ||||||
|  |             crc = binascii.crc32(data, crc) | ||||||
|  |             ntag, = struct.unpack('>I', data) | ||||||
|  |         except struct.error: | ||||||
|  |             break | ||||||
|  |  | ||||||
|  |         tag ^= ntag | ||||||
|  |         off += 4 | ||||||
|  |  | ||||||
|  |         type = (tag & 0x7ff00000) >> 20 | ||||||
|  |         id   = (tag & 0x000ffc00) >> 10 | ||||||
|  |         size = (tag & 0x000003ff) >> 0 | ||||||
|  |         iscrc = (type & 0x700) == 0x500 | ||||||
|  |  | ||||||
|  |         data = file.read(size if size != 0x3ff else 0) | ||||||
|  |         if iscrc: | ||||||
|  |             crc = binascii.crc32(data[:4], crc) | ||||||
|  |         else: | ||||||
|  |             crc = binascii.crc32(data, crc) | ||||||
|  |  | ||||||
|  |         print '%04x: %08x  %-15s %3s %4s  %-23s  %-8s' % ( | ||||||
|  |             off, tag, | ||||||
|  |             typeof(type) + (' bad!' if iscrc and ~crc else ''), | ||||||
|  |             id if id != 0x3ff else '.', | ||||||
|  |             size if size != 0x3ff else 'x', | ||||||
|  |             ' '.join('%02x' % ord(c) for c in data[:8]), | ||||||
|  |             ''.join(c if c >= ' ' and c <= '~' else '.' for c in data[:8])) | ||||||
|  |  | ||||||
|  |         off += size if size != 0x3ff else 0 | ||||||
|  |         if iscrc: | ||||||
|  |             crc = 0 | ||||||
|  |             tag ^= (type & 1) << 31 | ||||||
|  |  | ||||||
|  |     return 0 | ||||||
|  |  | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     import sys | ||||||
|  |     sys.exit(main(*sys.argv[1:])) | ||||||
| @@ -7,7 +7,7 @@ import os | |||||||
| import re | import re | ||||||
|  |  | ||||||
| def main(): | def main(): | ||||||
|     with open('blocks/config') as file: |     with open('blocks/.config') as file: | ||||||
|         s = struct.unpack('<LLLL', file.read()) |         s = struct.unpack('<LLLL', file.read()) | ||||||
|         print 'read_size: %d' % s[0] |         print 'read_size: %d' % s[0] | ||||||
|         print 'prog_size: %d' % s[1] |         print 'prog_size: %d' % s[1] | ||||||
| @@ -18,7 +18,7 @@ def main(): | |||||||
|         os.path.getsize(os.path.join('blocks', f)) |         os.path.getsize(os.path.join('blocks', f)) | ||||||
|         for f in os.listdir('blocks') if re.match('\d+', f)) |         for f in os.listdir('blocks') if re.match('\d+', f)) | ||||||
|  |  | ||||||
|     with open('blocks/stats') as file: |     with open('blocks/.stats') as file: | ||||||
|         s = struct.unpack('<QQQ', file.read()) |         s = struct.unpack('<QQQ', file.read()) | ||||||
|         print 'read_count: %d' % s[0] |         print 'read_count: %d' % s[0] | ||||||
|         print 'prog_count: %d' % s[1] |         print 'prog_count: %d' % s[1] | ||||||
|   | |||||||
| @@ -66,7 +66,7 @@ uintmax_t test; | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifndef LFS_PROG_SIZE | #ifndef LFS_PROG_SIZE | ||||||
| #define LFS_PROG_SIZE 16 | #define LFS_PROG_SIZE LFS_READ_SIZE | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifndef LFS_BLOCK_SIZE | #ifndef LFS_BLOCK_SIZE | ||||||
| @@ -77,8 +77,16 @@ uintmax_t test; | |||||||
| #define LFS_BLOCK_COUNT 1024 | #define LFS_BLOCK_COUNT 1024 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifndef LFS_LOOKAHEAD | #ifndef LFS_BLOCK_CYCLES | ||||||
| #define LFS_LOOKAHEAD 128 | #define LFS_BLOCK_CYCLES 1024 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifndef LFS_CACHE_SIZE | ||||||
|  | #define LFS_CACHE_SIZE 64 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifndef LFS_LOOKAHEAD_SIZE | ||||||
|  | #define LFS_LOOKAHEAD_SIZE 16 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| const struct lfs_config cfg = {{ | const struct lfs_config cfg = {{ | ||||||
| @@ -88,11 +96,13 @@ const struct lfs_config cfg = {{ | |||||||
|     .erase = &lfs_emubd_erase, |     .erase = &lfs_emubd_erase, | ||||||
|     .sync  = &lfs_emubd_sync, |     .sync  = &lfs_emubd_sync, | ||||||
|  |  | ||||||
|     .read_size   = LFS_READ_SIZE, |     .read_size      = LFS_READ_SIZE, | ||||||
|     .prog_size   = LFS_PROG_SIZE, |     .prog_size      = LFS_PROG_SIZE, | ||||||
|     .block_size  = LFS_BLOCK_SIZE, |     .block_size     = LFS_BLOCK_SIZE, | ||||||
|     .block_count = LFS_BLOCK_COUNT, |     .block_count    = LFS_BLOCK_COUNT, | ||||||
|     .lookahead   = LFS_LOOKAHEAD, |     .block_cycles   = LFS_BLOCK_CYCLES, | ||||||
|  |     .cache_size     = LFS_CACHE_SIZE, | ||||||
|  |     .lookahead_size = LFS_LOOKAHEAD_SIZE, | ||||||
| }}; | }}; | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ def generate(test): | |||||||
|         template = file.read() |         template = file.read() | ||||||
|  |  | ||||||
|     lines = [] |     lines = [] | ||||||
|     for line in re.split('(?<=[;{}])\n', test.read()): |     for line in re.split('(?<=(?:.;| [{}]))\n', test.read()): | ||||||
|         match = re.match('(?: *\n)*( *)(.*)=>(.*);', line, re.DOTALL | re.MULTILINE) |         match = re.match('(?: *\n)*( *)(.*)=>(.*);', line, re.DOTALL | re.MULTILINE) | ||||||
|         if match: |         if match: | ||||||
|             tab, test, expect = match.groups() |             tab, test, expect = match.groups() | ||||||
|   | |||||||
| @@ -194,76 +194,125 @@ tests/test.py << TEST | |||||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; |     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||||
|     memcmp(buffer, "exhaustion", size) => 0; |     memcmp(buffer, "exhaustion", size) => 0; | ||||||
|     lfs_file_close(&lfs, &file[0]) => 0; |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     lfs_remove(&lfs, "exhaustion") => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
|  |  | ||||||
| echo "--- Dir exhaustion test ---" | echo "--- Dir exhaustion test ---" | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_remove(&lfs, "exhaustion") => 0; |  | ||||||
|  |  | ||||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); |     // find out max file size | ||||||
|  |     lfs_mkdir(&lfs, "exhaustiondir") => 0; | ||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||||
|             i < (cfg.block_count-6)*(cfg.block_size-8); |     int count = 0; | ||||||
|             i += size) { |     int err; | ||||||
|  |     while (true) { | ||||||
|  |         err = lfs_file_write(&lfs, &file[0], buffer, size); | ||||||
|  |         if (err < 0) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         count += 1; | ||||||
|  |     } | ||||||
|  |     err => LFS_ERR_NOSPC; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|  |     lfs_remove(&lfs, "exhaustion") => 0; | ||||||
|  |     lfs_remove(&lfs, "exhaustiondir") => 0; | ||||||
|  |  | ||||||
|  |     // see if dir fits with max file size | ||||||
|  |     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||||
|  |     for (int i = 0; i < count; i++) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
|     lfs_file_close(&lfs, &file[0]) => 0; |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|     lfs_mkdir(&lfs, "exhaustiondir") => 0; |     lfs_mkdir(&lfs, "exhaustiondir") => 0; | ||||||
|     lfs_remove(&lfs, "exhaustiondir") => 0; |     lfs_remove(&lfs, "exhaustiondir") => 0; | ||||||
|  |     lfs_remove(&lfs, "exhaustion") => 0; | ||||||
|  |  | ||||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_APPEND); |     // see if dir fits with > max file size | ||||||
|     size = strlen("blahblahblahblah"); |     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     for (int i = 0; i < count+1; i++) { | ||||||
|     for (lfs_size_t i = 0; |  | ||||||
|             i < (cfg.block_size-8); |  | ||||||
|             i += size) { |  | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
|     lfs_file_close(&lfs, &file[0]) => 0; |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|     lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; |     lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; | ||||||
|  |  | ||||||
|  |     lfs_remove(&lfs, "exhaustion") => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
|  |  | ||||||
| echo "--- Chained dir exhaustion test ---" | echo "--- Chained dir exhaustion test ---" | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_remove(&lfs, "exhaustion") => 0; |  | ||||||
|  |  | ||||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); |     // find out max file size | ||||||
|  |     lfs_mkdir(&lfs, "exhaustiondir") => 0; | ||||||
|  |     for (int i = 0; i < 10; i++) { | ||||||
|  |         sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i); | ||||||
|  |         lfs_mkdir(&lfs, (char*)buffer) => 0; | ||||||
|  |     } | ||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||||
|             i < (cfg.block_count-24)*(cfg.block_size-8); |     int count = 0; | ||||||
|             i += size) { |     int err; | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |     while (true) { | ||||||
|  |         err = lfs_file_write(&lfs, &file[0], buffer, size); | ||||||
|  |         if (err < 0) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         count += 1; | ||||||
|     } |     } | ||||||
|  |     err => LFS_ERR_NOSPC; | ||||||
|     lfs_file_close(&lfs, &file[0]) => 0; |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|     for (int i = 0; i < 9; i++) { |     lfs_remove(&lfs, "exhaustion") => 0; | ||||||
|  |     lfs_remove(&lfs, "exhaustiondir") => 0; | ||||||
|  |     for (int i = 0; i < 10; i++) { | ||||||
|  |         sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i); | ||||||
|  |         lfs_remove(&lfs, (char*)buffer) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // see that chained dir fails | ||||||
|  |     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||||
|  |     for (int i = 0; i < count+1; i++) { | ||||||
|  |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|  |     } | ||||||
|  |     lfs_file_sync(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < 10; i++) { | ||||||
|         sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i); |         sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i); | ||||||
|         lfs_mkdir(&lfs, (char*)buffer) => 0; |         lfs_mkdir(&lfs, (char*)buffer) => 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; |     lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; | ||||||
|  |  | ||||||
|     lfs_remove(&lfs, "exhaustion") => 0; |     // shorten file to try a second chained dir | ||||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); |     while (true) { | ||||||
|     size = strlen("blahblahblahblah"); |         err = lfs_mkdir(&lfs, "exhaustiondir"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |         if (err != LFS_ERR_NOSPC) { | ||||||
|     for (lfs_size_t i = 0; |             break; | ||||||
|             i < (cfg.block_count-26)*(cfg.block_size-8); |         } | ||||||
|             i += size) { |  | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_ssize_t filesize = lfs_file_size(&lfs, &file[0]); | ||||||
|     } |         filesize > 0 => true; | ||||||
|     lfs_file_close(&lfs, &file[0]) => 0; |  | ||||||
|  |         lfs_file_truncate(&lfs, &file[0], filesize - size) => 0; | ||||||
|  |         lfs_file_sync(&lfs, &file[0]) => 0; | ||||||
|  |     } | ||||||
|  |     err => 0; | ||||||
|  |  | ||||||
|     lfs_mkdir(&lfs, "exhaustiondir") => 0; |  | ||||||
|     lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC; |     lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC; | ||||||
|  |  | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
|  |  | ||||||
| echo "--- Split dir test ---" | echo "--- Split dir test ---" | ||||||
| @@ -274,28 +323,38 @@ TEST | |||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|     // create one block whole for half a directory |     // create one block hole for half a directory | ||||||
|     lfs_file_open(&lfs, &file[0], "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0; |     lfs_file_open(&lfs, &file[0], "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||||
|     lfs_file_write(&lfs, &file[0], (void*)"hi", 2) => 2; |     for (lfs_size_t i = 0; i < cfg.block_size; i += 2) { | ||||||
|  |         memcpy(&buffer[i], "hi", 2); | ||||||
|  |     } | ||||||
|  |     lfs_file_write(&lfs, &file[0], buffer, cfg.block_size) => cfg.block_size; | ||||||
|     lfs_file_close(&lfs, &file[0]) => 0; |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); |     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     for (lfs_size_t i = 0; | ||||||
|             i < (cfg.block_count-6)*(cfg.block_size-8); |             i < (cfg.block_count-4)*(cfg.block_size-8); | ||||||
|             i += size) { |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
|     lfs_file_close(&lfs, &file[0]) => 0; |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|  |     // remount to force reset of lookahead | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|     // open hole |     // open hole | ||||||
|     lfs_remove(&lfs, "bump") => 0; |     lfs_remove(&lfs, "bump") => 0; | ||||||
|  |  | ||||||
|     lfs_mkdir(&lfs, "splitdir") => 0; |     lfs_mkdir(&lfs, "splitdir") => 0; | ||||||
|     lfs_file_open(&lfs, &file[0], "splitdir/bump", |     lfs_file_open(&lfs, &file[0], "splitdir/bump", | ||||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; |             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||||
|     lfs_file_write(&lfs, &file[0], buffer, size) => LFS_ERR_NOSPC; |     for (lfs_size_t i = 0; i < cfg.block_size; i += 2) { | ||||||
|  |         memcpy(&buffer[i], "hi", 2); | ||||||
|  |     } | ||||||
|  |     lfs_file_write(&lfs, &file[0], buffer, 2*cfg.block_size) => LFS_ERR_NOSPC; | ||||||
|     lfs_file_close(&lfs, &file[0]) => 0; |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| @@ -314,7 +373,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     for (lfs_size_t i = 0; | ||||||
|             i < ((cfg.block_count-4)/2)*(cfg.block_size-8); |             i < ((cfg.block_count-2)/2)*(cfg.block_size-8); | ||||||
|             i += size) { |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
| @@ -325,7 +384,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     for (lfs_size_t i = 0; | ||||||
|             i < ((cfg.block_count-4+1)/2)*(cfg.block_size-8); |             i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); | ||||||
|             i += size) { |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
| @@ -333,7 +392,6 @@ tests/test.py << TEST | |||||||
|  |  | ||||||
|     // remount to force reset of lookahead |     // remount to force reset of lookahead | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
|  |  | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|     // rewrite one file |     // rewrite one file | ||||||
| @@ -343,7 +401,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     for (lfs_size_t i = 0; | ||||||
|             i < ((cfg.block_count-4)/2)*(cfg.block_size-8); |             i < ((cfg.block_count-2)/2)*(cfg.block_size-8); | ||||||
|             i += size) { |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
| @@ -357,7 +415,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     for (lfs_size_t i = 0; | ||||||
|             i < ((cfg.block_count-4+1)/2)*(cfg.block_size-8); |             i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); | ||||||
|             i += size) { |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
| @@ -377,7 +435,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     for (lfs_size_t i = 0; | ||||||
|             i < ((cfg.block_count-4)/2)*(cfg.block_size-8); |             i < ((cfg.block_count-2)/2)*(cfg.block_size-8); | ||||||
|             i += size) { |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
| @@ -388,7 +446,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     for (lfs_size_t i = 0; | ||||||
|             i < ((cfg.block_count-4+1)/2)*(cfg.block_size-8); |             i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); | ||||||
|             i += size) { |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
| @@ -396,7 +454,6 @@ tests/test.py << TEST | |||||||
|  |  | ||||||
|     // remount to force reset of lookahead |     // remount to force reset of lookahead | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
|  |  | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|     // rewrite one file with a hole of one block |     // rewrite one file with a hole of one block | ||||||
| @@ -406,7 +463,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     for (lfs_size_t i = 0; | ||||||
|             i < ((cfg.block_count-4)/2 - 1)*(cfg.block_size-8); |             i < ((cfg.block_count-2)/2 - 1)*(cfg.block_size-8); | ||||||
|             i += size) { |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
|   | |||||||
							
								
								
									
										286
									
								
								tests/test_attrs.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										286
									
								
								tests/test_attrs.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,286 @@ | |||||||
|  | #!/bin/bash | ||||||
|  | set -eu | ||||||
|  |  | ||||||
|  | echo "=== Attr tests ===" | ||||||
|  | rm -rf blocks | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_format(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_mkdir(&lfs, "hello") => 0; | ||||||
|  |     lfs_file_open(&lfs, &file[0], "hello/hello", | ||||||
|  |             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||||
|  |     lfs_file_write(&lfs, &file[0], "hello", strlen("hello")) | ||||||
|  |             => strlen("hello"); | ||||||
|  |     lfs_file_close(&lfs, &file[0]); | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Set/get attribute ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_setattr(&lfs, "hello", 'A', "aaaa",   4) => 0; | ||||||
|  |     lfs_setattr(&lfs, "hello", 'B', "bbbbbb", 6) => 0; | ||||||
|  |     lfs_setattr(&lfs, "hello", 'C', "ccccc",  5) => 0; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'A', buffer,    4) => 4; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'B', buffer+4,  6) => 6; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; | ||||||
|  |     memcmp(buffer,    "aaaa",   4) => 0; | ||||||
|  |     memcmp(buffer+4,  "bbbbbb", 6) => 0; | ||||||
|  |     memcmp(buffer+10, "ccccc",  5) => 0; | ||||||
|  |  | ||||||
|  |     lfs_setattr(&lfs, "hello", 'B', "", 0) => 0; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'A', buffer,    4) => 4; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'B', buffer+4,  6) => 0; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; | ||||||
|  |     memcmp(buffer,    "aaaa",         4) => 0; | ||||||
|  |     memcmp(buffer+4,  "\0\0\0\0\0\0", 6) => 0; | ||||||
|  |     memcmp(buffer+10, "ccccc",        5) => 0; | ||||||
|  |  | ||||||
|  |     lfs_removeattr(&lfs, "hello", 'B') => 0; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'A', buffer,    4) => 4; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'B', buffer+4,  6) => LFS_ERR_NOATTR; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; | ||||||
|  |     memcmp(buffer,    "aaaa",         4) => 0; | ||||||
|  |     memcmp(buffer+4,  "\0\0\0\0\0\0", 6) => 0; | ||||||
|  |     memcmp(buffer+10, "ccccc",        5) => 0; | ||||||
|  |  | ||||||
|  |     lfs_setattr(&lfs, "hello", 'B', "dddddd", 6) => 0; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'A', buffer,    4) => 4; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'B', buffer+4,  6) => 6; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; | ||||||
|  |     memcmp(buffer,    "aaaa",   4) => 0; | ||||||
|  |     memcmp(buffer+4,  "dddddd", 6) => 0; | ||||||
|  |     memcmp(buffer+10, "ccccc",  5) => 0; | ||||||
|  |  | ||||||
|  |     lfs_setattr(&lfs, "hello", 'B', "eee", 3) => 0; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'A', buffer,    4) => 4; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'B', buffer+4,  6) => 3; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; | ||||||
|  |     memcmp(buffer,    "aaaa",      4) => 0; | ||||||
|  |     memcmp(buffer+4,  "eee\0\0\0", 6) => 0; | ||||||
|  |     memcmp(buffer+10, "ccccc",     5) => 0; | ||||||
|  |  | ||||||
|  |     lfs_setattr(&lfs, "hello", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC; | ||||||
|  |     lfs_setattr(&lfs, "hello", 'B', "fffffffff", 9) => 0; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'A', buffer,    4) => 4; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'B', buffer+4,  6) => 9; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; | ||||||
|  |  | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'A', buffer,    4) => 4; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'B', buffer+4,  9) => 9; | ||||||
|  |     lfs_getattr(&lfs, "hello", 'C', buffer+13, 5) => 5; | ||||||
|  |     memcmp(buffer,    "aaaa",      4) => 0; | ||||||
|  |     memcmp(buffer+4,  "fffffffff", 9) => 0; | ||||||
|  |     memcmp(buffer+13, "ccccc",     5) => 0; | ||||||
|  |  | ||||||
|  |     lfs_file_open(&lfs, &file[0], "hello/hello", LFS_O_RDONLY) => 0; | ||||||
|  |     lfs_file_read(&lfs, &file[0], buffer, sizeof(buffer)) => strlen("hello"); | ||||||
|  |     memcmp(buffer, "hello", strlen("hello")) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]); | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Set/get root attribute ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_setattr(&lfs, "/", 'A', "aaaa",   4) => 0; | ||||||
|  |     lfs_setattr(&lfs, "/", 'B', "bbbbbb", 6) => 0; | ||||||
|  |     lfs_setattr(&lfs, "/", 'C', "ccccc",  5) => 0; | ||||||
|  |     lfs_getattr(&lfs, "/", 'A', buffer,    4) => 4; | ||||||
|  |     lfs_getattr(&lfs, "/", 'B', buffer+4,  6) => 6; | ||||||
|  |     lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; | ||||||
|  |     memcmp(buffer,    "aaaa",   4) => 0; | ||||||
|  |     memcmp(buffer+4,  "bbbbbb", 6) => 0; | ||||||
|  |     memcmp(buffer+10, "ccccc",  5) => 0; | ||||||
|  |  | ||||||
|  |     lfs_setattr(&lfs, "/", 'B', "", 0) => 0; | ||||||
|  |     lfs_getattr(&lfs, "/", 'A', buffer,    4) => 4; | ||||||
|  |     lfs_getattr(&lfs, "/", 'B', buffer+4,  6) => 0; | ||||||
|  |     lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; | ||||||
|  |     memcmp(buffer,    "aaaa",         4) => 0; | ||||||
|  |     memcmp(buffer+4,  "\0\0\0\0\0\0", 6) => 0; | ||||||
|  |     memcmp(buffer+10, "ccccc",        5) => 0; | ||||||
|  |  | ||||||
|  |     lfs_removeattr(&lfs, "/", 'B') => 0; | ||||||
|  |     lfs_getattr(&lfs, "/", 'A', buffer,    4) => 4; | ||||||
|  |     lfs_getattr(&lfs, "/", 'B', buffer+4,  6) => LFS_ERR_NOATTR; | ||||||
|  |     lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; | ||||||
|  |     memcmp(buffer,    "aaaa",         4) => 0; | ||||||
|  |     memcmp(buffer+4,  "\0\0\0\0\0\0", 6) => 0; | ||||||
|  |     memcmp(buffer+10, "ccccc",        5) => 0; | ||||||
|  |  | ||||||
|  |     lfs_setattr(&lfs, "/", 'B', "dddddd", 6) => 0; | ||||||
|  |     lfs_getattr(&lfs, "/", 'A', buffer,    4) => 4; | ||||||
|  |     lfs_getattr(&lfs, "/", 'B', buffer+4,  6) => 6; | ||||||
|  |     lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; | ||||||
|  |     memcmp(buffer,    "aaaa",   4) => 0; | ||||||
|  |     memcmp(buffer+4,  "dddddd", 6) => 0; | ||||||
|  |     memcmp(buffer+10, "ccccc",  5) => 0; | ||||||
|  |  | ||||||
|  |     lfs_setattr(&lfs, "/", 'B', "eee", 3) => 0; | ||||||
|  |     lfs_getattr(&lfs, "/", 'A', buffer,    4) => 4; | ||||||
|  |     lfs_getattr(&lfs, "/", 'B', buffer+4,  6) => 3; | ||||||
|  |     lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; | ||||||
|  |     memcmp(buffer,    "aaaa",      4) => 0; | ||||||
|  |     memcmp(buffer+4,  "eee\0\0\0", 6) => 0; | ||||||
|  |     memcmp(buffer+10, "ccccc",     5) => 0; | ||||||
|  |  | ||||||
|  |     lfs_setattr(&lfs, "/", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC; | ||||||
|  |     lfs_setattr(&lfs, "/", 'B', "fffffffff", 9) => 0; | ||||||
|  |     lfs_getattr(&lfs, "/", 'A', buffer,    4) => 4; | ||||||
|  |     lfs_getattr(&lfs, "/", 'B', buffer+4,  6) => 9; | ||||||
|  |     lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_getattr(&lfs, "/", 'A', buffer,    4) => 4; | ||||||
|  |     lfs_getattr(&lfs, "/", 'B', buffer+4,  9) => 9; | ||||||
|  |     lfs_getattr(&lfs, "/", 'C', buffer+13, 5) => 5; | ||||||
|  |     memcmp(buffer,    "aaaa",      4) => 0; | ||||||
|  |     memcmp(buffer+4,  "fffffffff", 9) => 0; | ||||||
|  |     memcmp(buffer+13, "ccccc",     5) => 0; | ||||||
|  |  | ||||||
|  |     lfs_file_open(&lfs, &file[0], "hello/hello", LFS_O_RDONLY) => 0; | ||||||
|  |     lfs_file_read(&lfs, &file[0], buffer, sizeof(buffer)) => strlen("hello"); | ||||||
|  |     memcmp(buffer, "hello", strlen("hello")) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]); | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Set/get file attribute ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     struct lfs_attr attrs1[] = { | ||||||
|  |         {'A', buffer,    4}, | ||||||
|  |         {'B', buffer+4,  6}, | ||||||
|  |         {'C', buffer+10, 5}, | ||||||
|  |     }; | ||||||
|  |     struct lfs_file_config cfg1 = {.attrs=attrs1, .attr_count=3}; | ||||||
|  |  | ||||||
|  |     lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0; | ||||||
|  |     memcpy(buffer,    "aaaa",   4); | ||||||
|  |     memcpy(buffer+4,  "bbbbbb", 6); | ||||||
|  |     memcpy(buffer+10, "ccccc",  5); | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     memset(buffer, 0, 15); | ||||||
|  |     lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     memcmp(buffer,    "aaaa",   4) => 0; | ||||||
|  |     memcmp(buffer+4,  "bbbbbb", 6) => 0; | ||||||
|  |     memcmp(buffer+10, "ccccc",  5) => 0; | ||||||
|  |  | ||||||
|  |     attrs1[1].size = 0; | ||||||
|  |     lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     memset(buffer, 0, 15); | ||||||
|  |     attrs1[1].size = 6; | ||||||
|  |     lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     memcmp(buffer,    "aaaa",         4) => 0; | ||||||
|  |     memcmp(buffer+4,  "\0\0\0\0\0\0", 6) => 0; | ||||||
|  |     memcmp(buffer+10, "ccccc",        5) => 0; | ||||||
|  |  | ||||||
|  |     attrs1[1].size = 6; | ||||||
|  |     lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0; | ||||||
|  |     memcpy(buffer+4,  "dddddd", 6); | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     memset(buffer, 0, 15); | ||||||
|  |     attrs1[1].size = 6; | ||||||
|  |     lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     memcmp(buffer,    "aaaa",   4) => 0; | ||||||
|  |     memcmp(buffer+4,  "dddddd", 6) => 0; | ||||||
|  |     memcmp(buffer+10, "ccccc",  5) => 0; | ||||||
|  |  | ||||||
|  |     attrs1[1].size = 3; | ||||||
|  |     lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0; | ||||||
|  |     memcpy(buffer+4,  "eee", 3); | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     memset(buffer, 0, 15); | ||||||
|  |     attrs1[1].size = 6; | ||||||
|  |     lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     memcmp(buffer,    "aaaa",      4) => 0; | ||||||
|  |     memcmp(buffer+4,  "eee\0\0\0", 6) => 0; | ||||||
|  |     memcmp(buffer+10, "ccccc",     5) => 0; | ||||||
|  |  | ||||||
|  |     attrs1[0].size = LFS_ATTR_MAX+1; | ||||||
|  |     lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) | ||||||
|  |         => LFS_ERR_NOSPC; | ||||||
|  |  | ||||||
|  |     struct lfs_attr attrs2[] = { | ||||||
|  |         {'A', buffer,    4}, | ||||||
|  |         {'B', buffer+4,  9}, | ||||||
|  |         {'C', buffer+13, 5}, | ||||||
|  |     }; | ||||||
|  |     struct lfs_file_config cfg2 = {.attrs=attrs2, .attr_count=3}; | ||||||
|  |     lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDWR, &cfg2) => 0; | ||||||
|  |     memcpy(buffer+4,  "fffffffff", 9); | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     attrs1[0].size = 4; | ||||||
|  |     lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     struct lfs_attr attrs2[] = { | ||||||
|  |         {'A', buffer,    4}, | ||||||
|  |         {'B', buffer+4,  9}, | ||||||
|  |         {'C', buffer+13, 5}, | ||||||
|  |     }; | ||||||
|  |     struct lfs_file_config cfg2 = {.attrs=attrs2, .attr_count=3}; | ||||||
|  |  | ||||||
|  |     lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg2) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     memcmp(buffer,    "aaaa",      4) => 0; | ||||||
|  |     memcmp(buffer+4,  "fffffffff", 9) => 0; | ||||||
|  |     memcmp(buffer+13, "ccccc",     5) => 0; | ||||||
|  |  | ||||||
|  |     lfs_file_open(&lfs, &file[0], "hello/hello", LFS_O_RDONLY) => 0; | ||||||
|  |     lfs_file_read(&lfs, &file[0], buffer, sizeof(buffer)) => strlen("hello"); | ||||||
|  |     memcmp(buffer, "hello", strlen("hello")) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]); | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Deferred file attributes ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     struct lfs_attr attrs1[] = { | ||||||
|  |         {'B', "gggg", 4}, | ||||||
|  |         {'C', "",     0}, | ||||||
|  |         {'D', "hhhh", 4}, | ||||||
|  |     }; | ||||||
|  |     struct lfs_file_config cfg1 = {.attrs=attrs1, .attr_count=3}; | ||||||
|  |  | ||||||
|  |     lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0; | ||||||
|  |  | ||||||
|  |     lfs_getattr(&lfs, "hello/hello", 'B', buffer,    9) => 9; | ||||||
|  |     lfs_getattr(&lfs, "hello/hello", 'C', buffer+9,  9) => 5; | ||||||
|  |     lfs_getattr(&lfs, "hello/hello", 'D', buffer+18, 9) => LFS_ERR_NOATTR; | ||||||
|  |     memcmp(buffer,    "fffffffff",          9) => 0; | ||||||
|  |     memcmp(buffer+9,  "ccccc\0\0\0\0",      9) => 0; | ||||||
|  |     memcmp(buffer+18, "\0\0\0\0\0\0\0\0\0", 9) => 0; | ||||||
|  |  | ||||||
|  |     lfs_file_sync(&lfs, &file[0]) => 0; | ||||||
|  |     lfs_getattr(&lfs, "hello/hello", 'B', buffer,    9) => 4; | ||||||
|  |     lfs_getattr(&lfs, "hello/hello", 'C', buffer+9,  9) => 0; | ||||||
|  |     lfs_getattr(&lfs, "hello/hello", 'D', buffer+18, 9) => 4; | ||||||
|  |     memcmp(buffer,    "gggg\0\0\0\0\0",     9) => 0; | ||||||
|  |     memcmp(buffer+9,  "\0\0\0\0\0\0\0\0\0", 9) => 0; | ||||||
|  |     memcmp(buffer+18, "hhhh\0\0\0\0\0",     9) => 0; | ||||||
|  |  | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Results ---" | ||||||
|  | tests/stats.py | ||||||
| @@ -71,24 +71,25 @@ echo "--- Sanity check ---" | |||||||
| rm -rf blocks | rm -rf blocks | ||||||
| lfs_mktree | lfs_mktree | ||||||
| lfs_chktree | lfs_chktree | ||||||
|  | BLOCKS="$(ls blocks | grep -vw '[01]')" | ||||||
|  |  | ||||||
| echo "--- Block corruption ---" | echo "--- Block corruption ---" | ||||||
| for i in {0..33} | for b in $BLOCKS | ||||||
| do  | do  | ||||||
|     rm -rf blocks |     rm -rf blocks | ||||||
|     mkdir blocks |     mkdir blocks | ||||||
|     ln -s /dev/zero blocks/$(printf '%x' $i) |     ln -s /dev/zero blocks/$b | ||||||
|     lfs_mktree |     lfs_mktree | ||||||
|     lfs_chktree |     lfs_chktree | ||||||
| done | done | ||||||
|  |  | ||||||
| echo "--- Block persistance ---" | echo "--- Block persistance ---" | ||||||
| for i in {0..33} | for b in $BLOCKS | ||||||
| do  | do  | ||||||
|     rm -rf blocks |     rm -rf blocks | ||||||
|     mkdir blocks |     mkdir blocks | ||||||
|     lfs_mktree |     lfs_mktree | ||||||
|     chmod a-w blocks/$(printf '%x' $i) |     chmod a-w blocks/$b || true | ||||||
|     lfs_mktree |     lfs_mktree | ||||||
|     lfs_chktree |     lfs_chktree | ||||||
| done | done | ||||||
| @@ -96,7 +97,7 @@ done | |||||||
| echo "--- Big region corruption ---" | echo "--- Big region corruption ---" | ||||||
| rm -rf blocks | rm -rf blocks | ||||||
| mkdir blocks | mkdir blocks | ||||||
| for i in {2..255} | for i in {2..512} | ||||||
| do | do | ||||||
|     ln -s /dev/zero blocks/$(printf '%x' $i) |     ln -s /dev/zero blocks/$(printf '%x' $i) | ||||||
| done | done | ||||||
| @@ -106,7 +107,7 @@ lfs_chktree | |||||||
| echo "--- Alternating corruption ---" | echo "--- Alternating corruption ---" | ||||||
| rm -rf blocks | rm -rf blocks | ||||||
| mkdir blocks | mkdir blocks | ||||||
| for i in {2..511..2} | for i in {2..1024..2} | ||||||
| do | do | ||||||
|     ln -s /dev/zero blocks/$(printf '%x' $i) |     ln -s /dev/zero blocks/$(printf '%x' $i) | ||||||
| done | done | ||||||
|   | |||||||
| @@ -43,11 +43,11 @@ tests/test.py << TEST | |||||||
|     strcmp(info.name, "..") => 0; |     strcmp(info.name, "..") => 0; | ||||||
|     info.type => LFS_TYPE_DIR; |     info.type => LFS_TYPE_DIR; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "potato") => 0; |  | ||||||
|     info.type => LFS_TYPE_DIR; |  | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |  | ||||||
|     strcmp(info.name, "burito") => 0; |     strcmp(info.name, "burito") => 0; | ||||||
|     info.type => LFS_TYPE_REG; |     info.type => LFS_TYPE_REG; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "potato") => 0; | ||||||
|  |     info.type => LFS_TYPE_DIR; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| @@ -85,10 +85,10 @@ tests/test.py << TEST | |||||||
|     strcmp(info.name, "baked") => 0; |     strcmp(info.name, "baked") => 0; | ||||||
|     info.type => LFS_TYPE_DIR; |     info.type => LFS_TYPE_DIR; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "sweet") => 0; |     strcmp(info.name, "fried") => 0; | ||||||
|     info.type => LFS_TYPE_DIR; |     info.type => LFS_TYPE_DIR; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "fried") => 0; |     strcmp(info.name, "sweet") => 0; | ||||||
|     info.type => LFS_TYPE_DIR; |     info.type => LFS_TYPE_DIR; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
| @@ -100,7 +100,7 @@ tests/test.py << TEST | |||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_mkdir(&lfs, "cactus") => 0; |     lfs_mkdir(&lfs, "cactus") => 0; | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |     for (int i = 0; i < $LARGESIZE; i++) { | ||||||
|         sprintf((char*)buffer, "cactus/test%d", i); |         sprintf((char*)buffer, "cactus/test%03d", i); | ||||||
|         lfs_mkdir(&lfs, (char*)buffer) => 0; |         lfs_mkdir(&lfs, (char*)buffer) => 0; | ||||||
|     } |     } | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| @@ -115,7 +115,7 @@ tests/test.py << TEST | |||||||
|     strcmp(info.name, "..") => 0; |     strcmp(info.name, "..") => 0; | ||||||
|     info.type => LFS_TYPE_DIR; |     info.type => LFS_TYPE_DIR; | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |     for (int i = 0; i < $LARGESIZE; i++) { | ||||||
|         sprintf((char*)buffer, "test%d", i); |         sprintf((char*)buffer, "test%03d", i); | ||||||
|         lfs_dir_read(&lfs, &dir[0], &info) => 1; |         lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|         strcmp(info.name, (char*)buffer) => 0; |         strcmp(info.name, (char*)buffer) => 0; | ||||||
|         info.type => LFS_TYPE_DIR; |         info.type => LFS_TYPE_DIR; | ||||||
| @@ -208,10 +208,10 @@ tests/test.py << TEST | |||||||
|     strcmp(info.name, "baked") => 0; |     strcmp(info.name, "baked") => 0; | ||||||
|     info.type => LFS_TYPE_DIR; |     info.type => LFS_TYPE_DIR; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "sweet") => 0; |     strcmp(info.name, "fried") => 0; | ||||||
|     info.type => LFS_TYPE_DIR; |     info.type => LFS_TYPE_DIR; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "fried") => 0; |     strcmp(info.name, "sweet") => 0; | ||||||
|     info.type => LFS_TYPE_DIR; |     info.type => LFS_TYPE_DIR; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
| @@ -241,10 +241,10 @@ tests/test.py << TEST | |||||||
|     strcmp(info.name, "baked") => 0; |     strcmp(info.name, "baked") => 0; | ||||||
|     info.type => LFS_TYPE_DIR; |     info.type => LFS_TYPE_DIR; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "sweet") => 0; |     strcmp(info.name, "fried") => 0; | ||||||
|     info.type => LFS_TYPE_DIR; |     info.type => LFS_TYPE_DIR; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "fried") => 0; |     strcmp(info.name, "sweet") => 0; | ||||||
|     info.type => LFS_TYPE_DIR; |     info.type => LFS_TYPE_DIR; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
| @@ -273,10 +273,10 @@ tests/test.py << TEST | |||||||
|     strcmp(info.name, "baked") => 0; |     strcmp(info.name, "baked") => 0; | ||||||
|     info.type => LFS_TYPE_DIR; |     info.type => LFS_TYPE_DIR; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "sweet") => 0; |     strcmp(info.name, "fried") => 0; | ||||||
|     info.type => LFS_TYPE_DIR; |     info.type => LFS_TYPE_DIR; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "fried") => 0; |     strcmp(info.name, "sweet") => 0; | ||||||
|     info.type => LFS_TYPE_DIR; |     info.type => LFS_TYPE_DIR; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
| @@ -330,8 +330,8 @@ echo "--- Multi-block rename ---" | |||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |     for (int i = 0; i < $LARGESIZE; i++) { | ||||||
|         sprintf((char*)buffer, "cactus/test%d", i); |         sprintf((char*)buffer, "cactus/test%03d", i); | ||||||
|         sprintf((char*)wbuffer, "cactus/tedd%d", i); |         sprintf((char*)wbuffer, "cactus/tedd%03d", i); | ||||||
|         lfs_rename(&lfs, (char*)buffer, (char*)wbuffer) => 0; |         lfs_rename(&lfs, (char*)buffer, (char*)wbuffer) => 0; | ||||||
|     } |     } | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| @@ -346,7 +346,7 @@ tests/test.py << TEST | |||||||
|     strcmp(info.name, "..") => 0; |     strcmp(info.name, "..") => 0; | ||||||
|     info.type => LFS_TYPE_DIR; |     info.type => LFS_TYPE_DIR; | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |     for (int i = 0; i < $LARGESIZE; i++) { | ||||||
|         sprintf((char*)buffer, "tedd%d", i); |         sprintf((char*)buffer, "tedd%03d", i); | ||||||
|         lfs_dir_read(&lfs, &dir[0], &info) => 1; |         lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|         strcmp(info.name, (char*)buffer) => 0; |         strcmp(info.name, (char*)buffer) => 0; | ||||||
|         info.type => LFS_TYPE_DIR; |         info.type => LFS_TYPE_DIR; | ||||||
| @@ -361,7 +361,7 @@ tests/test.py << TEST | |||||||
|     lfs_remove(&lfs, "cactus") => LFS_ERR_NOTEMPTY; |     lfs_remove(&lfs, "cactus") => LFS_ERR_NOTEMPTY; | ||||||
|  |  | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |     for (int i = 0; i < $LARGESIZE; i++) { | ||||||
|         sprintf((char*)buffer, "cactus/tedd%d", i); |         sprintf((char*)buffer, "cactus/tedd%03d", i); | ||||||
|         lfs_remove(&lfs, (char*)buffer) => 0; |         lfs_remove(&lfs, (char*)buffer) => 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -390,7 +390,7 @@ tests/test.py << TEST | |||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_mkdir(&lfs, "prickly-pear") => 0; |     lfs_mkdir(&lfs, "prickly-pear") => 0; | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |     for (int i = 0; i < $LARGESIZE; i++) { | ||||||
|         sprintf((char*)buffer, "prickly-pear/test%d", i); |         sprintf((char*)buffer, "prickly-pear/test%03d", i); | ||||||
|         lfs_file_open(&lfs, &file[0], (char*)buffer, |         lfs_file_open(&lfs, &file[0], (char*)buffer, | ||||||
|                 LFS_O_WRONLY | LFS_O_CREAT) => 0; |                 LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||||
|         size = 6; |         size = 6; | ||||||
| @@ -410,7 +410,7 @@ tests/test.py << TEST | |||||||
|     strcmp(info.name, "..") => 0; |     strcmp(info.name, "..") => 0; | ||||||
|     info.type => LFS_TYPE_DIR; |     info.type => LFS_TYPE_DIR; | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |     for (int i = 0; i < $LARGESIZE; i++) { | ||||||
|         sprintf((char*)buffer, "test%d", i); |         sprintf((char*)buffer, "test%03d", i); | ||||||
|         lfs_dir_read(&lfs, &dir[0], &info) => 1; |         lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|         strcmp(info.name, (char*)buffer) => 0; |         strcmp(info.name, (char*)buffer) => 0; | ||||||
|         info.type => LFS_TYPE_REG; |         info.type => LFS_TYPE_REG; | ||||||
| @@ -424,8 +424,8 @@ echo "--- Multi-block rename with files ---" | |||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |     for (int i = 0; i < $LARGESIZE; i++) { | ||||||
|         sprintf((char*)buffer, "prickly-pear/test%d", i); |         sprintf((char*)buffer, "prickly-pear/test%03d", i); | ||||||
|         sprintf((char*)wbuffer, "prickly-pear/tedd%d", i); |         sprintf((char*)wbuffer, "prickly-pear/tedd%03d", i); | ||||||
|         lfs_rename(&lfs, (char*)buffer, (char*)wbuffer) => 0; |         lfs_rename(&lfs, (char*)buffer, (char*)wbuffer) => 0; | ||||||
|     } |     } | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| @@ -440,7 +440,7 @@ tests/test.py << TEST | |||||||
|     strcmp(info.name, "..") => 0; |     strcmp(info.name, "..") => 0; | ||||||
|     info.type => LFS_TYPE_DIR; |     info.type => LFS_TYPE_DIR; | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |     for (int i = 0; i < $LARGESIZE; i++) { | ||||||
|         sprintf((char*)buffer, "tedd%d", i); |         sprintf((char*)buffer, "tedd%03d", i); | ||||||
|         lfs_dir_read(&lfs, &dir[0], &info) => 1; |         lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|         strcmp(info.name, (char*)buffer) => 0; |         strcmp(info.name, (char*)buffer) => 0; | ||||||
|         info.type => LFS_TYPE_REG; |         info.type => LFS_TYPE_REG; | ||||||
| @@ -456,7 +456,7 @@ tests/test.py << TEST | |||||||
|     lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOTEMPTY; |     lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOTEMPTY; | ||||||
|  |  | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |     for (int i = 0; i < $LARGESIZE; i++) { | ||||||
|         sprintf((char*)buffer, "prickly-pear/tedd%d", i); |         sprintf((char*)buffer, "prickly-pear/tedd%03d", i); | ||||||
|         lfs_remove(&lfs, (char*)buffer) => 0; |         lfs_remove(&lfs, (char*)buffer) => 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										221
									
								
								tests/test_entries.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										221
									
								
								tests/test_entries.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,221 @@ | |||||||
|  | #!/bin/bash | ||||||
|  | set -eu | ||||||
|  |  | ||||||
|  | # Note: These tests are intended for 512 byte inline size at different | ||||||
|  | # inline sizes they should still pass, but won't be testing anything | ||||||
|  |  | ||||||
|  | echo "=== Entry tests ===" | ||||||
|  | rm -rf blocks | ||||||
|  | function read_file { | ||||||
|  | cat << TEST | ||||||
|  |  | ||||||
|  |     size = $2; | ||||||
|  |     lfs_file_open(&lfs, &file[0], "$1", LFS_O_RDONLY) => 0; | ||||||
|  |     lfs_file_read(&lfs, &file[0], rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  | TEST | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function write_file { | ||||||
|  | cat << TEST | ||||||
|  |  | ||||||
|  |     size = $2; | ||||||
|  |     lfs_file_open(&lfs, &file[0], "$1", | ||||||
|  |             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs_file_write(&lfs, &file[0], wbuffer, size) => size; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  | TEST | ||||||
|  | } | ||||||
|  |  | ||||||
|  | echo "--- Entry grow test ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_format(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     $(write_file "hi0" 20) | ||||||
|  |     $(write_file "hi1" 20) | ||||||
|  |     $(write_file "hi2" 20) | ||||||
|  |     $(write_file "hi3" 20) | ||||||
|  |  | ||||||
|  |     $(read_file "hi1" 20) | ||||||
|  |     $(write_file "hi1" 200) | ||||||
|  |  | ||||||
|  |     $(read_file "hi0" 20) | ||||||
|  |     $(read_file "hi1" 200) | ||||||
|  |     $(read_file "hi2" 20) | ||||||
|  |     $(read_file "hi3" 20) | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Entry shrink test ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_format(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     $(write_file "hi0" 20) | ||||||
|  |     $(write_file "hi1" 200) | ||||||
|  |     $(write_file "hi2" 20) | ||||||
|  |     $(write_file "hi3" 20) | ||||||
|  |  | ||||||
|  |     $(read_file "hi1" 200) | ||||||
|  |     $(write_file "hi1" 20) | ||||||
|  |  | ||||||
|  |     $(read_file "hi0" 20) | ||||||
|  |     $(read_file "hi1" 20) | ||||||
|  |     $(read_file "hi2" 20) | ||||||
|  |     $(read_file "hi3" 20) | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Entry spill test ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_format(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     $(write_file "hi0" 200) | ||||||
|  |     $(write_file "hi1" 200) | ||||||
|  |     $(write_file "hi2" 200) | ||||||
|  |     $(write_file "hi3" 200) | ||||||
|  |  | ||||||
|  |     $(read_file "hi0" 200) | ||||||
|  |     $(read_file "hi1" 200) | ||||||
|  |     $(read_file "hi2" 200) | ||||||
|  |     $(read_file "hi3" 200) | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Entry push spill test ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_format(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     $(write_file "hi0" 200) | ||||||
|  |     $(write_file "hi1" 20) | ||||||
|  |     $(write_file "hi2" 200) | ||||||
|  |     $(write_file "hi3" 200) | ||||||
|  |  | ||||||
|  |     $(read_file "hi1" 20) | ||||||
|  |     $(write_file "hi1" 200) | ||||||
|  |  | ||||||
|  |     $(read_file "hi0" 200) | ||||||
|  |     $(read_file "hi1" 200) | ||||||
|  |     $(read_file "hi2" 200) | ||||||
|  |     $(read_file "hi3" 200) | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Entry push spill two test ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_format(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     $(write_file "hi0" 200) | ||||||
|  |     $(write_file "hi1" 20) | ||||||
|  |     $(write_file "hi2" 200) | ||||||
|  |     $(write_file "hi3" 200) | ||||||
|  |     $(write_file "hi4" 200) | ||||||
|  |  | ||||||
|  |     $(read_file "hi1" 20) | ||||||
|  |     $(write_file "hi1" 200) | ||||||
|  |  | ||||||
|  |     $(read_file "hi0" 200) | ||||||
|  |     $(read_file "hi1" 200) | ||||||
|  |     $(read_file "hi2" 200) | ||||||
|  |     $(read_file "hi3" 200) | ||||||
|  |     $(read_file "hi4" 200) | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Entry drop test ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_format(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     $(write_file "hi0" 200) | ||||||
|  |     $(write_file "hi1" 200) | ||||||
|  |     $(write_file "hi2" 200) | ||||||
|  |     $(write_file "hi3" 200) | ||||||
|  |  | ||||||
|  |     lfs_remove(&lfs, "hi1") => 0; | ||||||
|  |     lfs_stat(&lfs, "hi1", &info) => LFS_ERR_NOENT; | ||||||
|  |     $(read_file "hi0" 200) | ||||||
|  |     $(read_file "hi2" 200) | ||||||
|  |     $(read_file "hi3" 200) | ||||||
|  |  | ||||||
|  |     lfs_remove(&lfs, "hi2") => 0; | ||||||
|  |     lfs_stat(&lfs, "hi2", &info) => LFS_ERR_NOENT; | ||||||
|  |     $(read_file "hi0" 200) | ||||||
|  |     $(read_file "hi3" 200) | ||||||
|  |  | ||||||
|  |     lfs_remove(&lfs, "hi3") => 0; | ||||||
|  |     lfs_stat(&lfs, "hi3", &info) => LFS_ERR_NOENT; | ||||||
|  |     $(read_file "hi0" 200) | ||||||
|  |  | ||||||
|  |     lfs_remove(&lfs, "hi0") => 0; | ||||||
|  |     lfs_stat(&lfs, "hi0", &info) => LFS_ERR_NOENT; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Create too big ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_format(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     memset(buffer, 'm', 200); | ||||||
|  |     buffer[200] = '\0'; | ||||||
|  |  | ||||||
|  |     size = 400; | ||||||
|  |     lfs_file_open(&lfs, &file[0], (char*)buffer, | ||||||
|  |             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs_file_write(&lfs, &file[0], wbuffer, size) => size; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|  |     size = 400; | ||||||
|  |     lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0; | ||||||
|  |     lfs_file_read(&lfs, &file[0], rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Resize too big ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_format(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     memset(buffer, 'm', 200); | ||||||
|  |     buffer[200] = '\0'; | ||||||
|  |  | ||||||
|  |     size = 40; | ||||||
|  |     lfs_file_open(&lfs, &file[0], (char*)buffer, | ||||||
|  |             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs_file_write(&lfs, &file[0], wbuffer, size) => size; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|  |     size = 40; | ||||||
|  |     lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0; | ||||||
|  |     lfs_file_read(&lfs, &file[0], rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|  |     size = 400; | ||||||
|  |     lfs_file_open(&lfs, &file[0], (char*)buffer, | ||||||
|  |             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs_file_write(&lfs, &file[0], wbuffer, size) => size; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|  |     size = 400; | ||||||
|  |     lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0; | ||||||
|  |     lfs_file_read(&lfs, &file[0], rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Results ---" | ||||||
|  | tests/stats.py | ||||||
| @@ -29,7 +29,7 @@ tests/test.py << TEST | |||||||
| TEST | TEST | ||||||
|  |  | ||||||
| w_test() { | w_test() { | ||||||
| tests/test.py << TEST | tests/test.py ${4:-} << TEST | ||||||
|     size = $1; |     size = $1; | ||||||
|     lfs_size_t chunk = 31; |     lfs_size_t chunk = 31; | ||||||
|     srand(0); |     srand(0); | ||||||
| @@ -115,21 +115,21 @@ tests/test.py << TEST | |||||||
|     info.type => LFS_TYPE_REG; |     info.type => LFS_TYPE_REG; | ||||||
|     info.size => strlen("Hello World!\n"); |     info.size => strlen("Hello World!\n"); | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "smallavacado") => 0; |     strcmp(info.name, "largeavacado") => 0; | ||||||
|     info.type => LFS_TYPE_REG; |     info.type => LFS_TYPE_REG; | ||||||
|     info.size => $SMALLSIZE; |     info.size => $LARGESIZE; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "mediumavacado") => 0; |     strcmp(info.name, "mediumavacado") => 0; | ||||||
|     info.type => LFS_TYPE_REG; |     info.type => LFS_TYPE_REG; | ||||||
|     info.size => $MEDIUMSIZE; |     info.size => $MEDIUMSIZE; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "largeavacado") => 0; |  | ||||||
|     info.type => LFS_TYPE_REG; |  | ||||||
|     info.size => $LARGESIZE; |  | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |  | ||||||
|     strcmp(info.name, "noavacado") => 0; |     strcmp(info.name, "noavacado") => 0; | ||||||
|     info.type => LFS_TYPE_REG; |     info.type => LFS_TYPE_REG; | ||||||
|     info.size => 0; |     info.size => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "smallavacado") => 0; | ||||||
|  |     info.type => LFS_TYPE_REG; | ||||||
|  |     info.size => $SMALLSIZE; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
|   | |||||||
| @@ -9,39 +9,40 @@ tests/test.py << TEST | |||||||
|     lfs_format(&lfs, &cfg) => 0; |     lfs_format(&lfs, &cfg) => 0; | ||||||
| TEST | TEST | ||||||
|  |  | ||||||
| echo "--- Invalid superblocks ---" |  | ||||||
| ln -f -s /dev/zero blocks/0 |  | ||||||
| ln -f -s /dev/zero blocks/1 |  | ||||||
| tests/test.py << TEST |  | ||||||
|     lfs_format(&lfs, &cfg) => LFS_ERR_CORRUPT; |  | ||||||
| TEST |  | ||||||
| rm blocks/0 blocks/1 |  | ||||||
|  |  | ||||||
| echo "--- Basic mounting ---" | echo "--- Basic mounting ---" | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_format(&lfs, &cfg) => 0; |     lfs_format(&lfs, &cfg) => 0; | ||||||
| TEST |  | ||||||
| tests/test.py << TEST |  | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
|  |  | ||||||
| echo "--- Invalid mount ---" | echo "--- Invalid superblocks ---" | ||||||
|  | ln -f -s /dev/zero blocks/0 | ||||||
|  | ln -f -s /dev/zero blocks/1 | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_format(&lfs, &cfg) => 0; |     lfs_format(&lfs, &cfg) => LFS_ERR_NOSPC; | ||||||
| TEST | TEST | ||||||
| rm blocks/0 blocks/1 | rm blocks/0 blocks/1 | ||||||
|  |  | ||||||
|  | echo "--- Invalid mount ---" | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; |     lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||||
| TEST | TEST | ||||||
|  |  | ||||||
| echo "--- Valid corrupt mount ---" | echo "--- Expanding superblock ---" | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_format(&lfs, &cfg) => 0; |     lfs_format(&lfs, &cfg) => 0; | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     for (int i = 0; i < 100; i++) { | ||||||
|  |         lfs_mkdir(&lfs, "dummy") => 0; | ||||||
|  |         lfs_remove(&lfs, "dummy") => 0; | ||||||
|  |     } | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
| rm blocks/0 |  | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_mkdir(&lfs, "dummy") => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
|  |  | ||||||
|   | |||||||
| @@ -59,7 +59,7 @@ tests/test.py << TEST | |||||||
|     lfs_rename(&lfs, "b/hello", "c/hello") => 0; |     lfs_rename(&lfs, "b/hello", "c/hello") => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
| rm -v blocks/7 | tests/corrupt.py -n 1 | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_dir_open(&lfs, &dir[0], "b") => 0; |     lfs_dir_open(&lfs, &dir[0], "b") => 0; | ||||||
| @@ -86,8 +86,7 @@ tests/test.py << TEST | |||||||
|     lfs_rename(&lfs, "c/hello", "d/hello") => 0; |     lfs_rename(&lfs, "c/hello", "d/hello") => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
| rm -v blocks/8 | tests/corrupt.py -n 2 | ||||||
| rm -v blocks/a |  | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_dir_open(&lfs, &dir[0], "c") => 0; |     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||||
| @@ -108,6 +107,32 @@ tests/test.py << TEST | |||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
|  |  | ||||||
|  | echo "--- Move file after corrupt ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_rename(&lfs, "c/hello", "d/hello") => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "d") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "hello") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
| echo "--- Move dir ---" | echo "--- Move dir ---" | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
| @@ -140,7 +165,7 @@ tests/test.py << TEST | |||||||
|     lfs_rename(&lfs, "b/hi", "c/hi") => 0; |     lfs_rename(&lfs, "b/hi", "c/hi") => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
| rm -v blocks/7 | tests/corrupt.py -n 1 | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_dir_open(&lfs, &dir[0], "b") => 0; |     lfs_dir_open(&lfs, &dir[0], "b") => 0; | ||||||
| @@ -156,8 +181,6 @@ tests/test.py << TEST | |||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "..") => 0; |     strcmp(info.name, "..") => 0; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "hello") => 0; |  | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |  | ||||||
|     strcmp(info.name, "hi") => 0; |     strcmp(info.name, "hi") => 0; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| @@ -169,8 +192,7 @@ tests/test.py << TEST | |||||||
|     lfs_rename(&lfs, "c/hi", "d/hi") => 0; |     lfs_rename(&lfs, "c/hi", "d/hi") => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
| rm -v blocks/9 | tests/corrupt.py -n 2 | ||||||
| rm -v blocks/a |  | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_dir_open(&lfs, &dir[0], "c") => 0; |     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||||
| @@ -179,8 +201,6 @@ tests/test.py << TEST | |||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "..") => 0; |     strcmp(info.name, "..") => 0; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "hello") => 0; |  | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |  | ||||||
|     strcmp(info.name, "hi") => 0; |     strcmp(info.name, "hi") => 0; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
| @@ -189,6 +209,36 @@ tests/test.py << TEST | |||||||
|     strcmp(info.name, ".") => 0; |     strcmp(info.name, ".") => 0; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "..") => 0; |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "hello") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Move dir after corrupt ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_rename(&lfs, "c/hi", "d/hi") => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "d") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "hello") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "hi") => 0; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
| @@ -199,27 +249,73 @@ tests/test.py << TEST | |||||||
|  |  | ||||||
|     lfs_dir_open(&lfs, &dir[0], "a/hi") => LFS_ERR_NOENT; |     lfs_dir_open(&lfs, &dir[0], "a/hi") => LFS_ERR_NOENT; | ||||||
|     lfs_dir_open(&lfs, &dir[0], "b/hi") => LFS_ERR_NOENT; |     lfs_dir_open(&lfs, &dir[0], "b/hi") => LFS_ERR_NOENT; | ||||||
|     lfs_dir_open(&lfs, &dir[0], "d/hi") => LFS_ERR_NOENT; |     lfs_dir_open(&lfs, &dir[0], "c/hi") => LFS_ERR_NOENT; | ||||||
|  |  | ||||||
|     lfs_dir_open(&lfs, &dir[0], "c/hi") => 0; |     lfs_dir_open(&lfs, &dir[0], "d/hi") => 0; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, ".") => 0; |     strcmp(info.name, ".") => 0; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "..") => 0; |     strcmp(info.name, "..") => 0; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "hola") => 0; |  | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |  | ||||||
|     strcmp(info.name, "bonjour") => 0; |     strcmp(info.name, "bonjour") => 0; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "hola") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, "ohayo") => 0; |     strcmp(info.name, "ohayo") => 0; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
|  |  | ||||||
|     lfs_dir_open(&lfs, &dir[0], "a/hello") => LFS_ERR_NOENT; |     lfs_dir_open(&lfs, &dir[0], "a/hello") => LFS_ERR_NOENT; | ||||||
|     lfs_dir_open(&lfs, &dir[0], "b/hello") => LFS_ERR_NOENT; |     lfs_dir_open(&lfs, &dir[0], "b/hello") => LFS_ERR_NOENT; | ||||||
|     lfs_dir_open(&lfs, &dir[0], "d/hello") => LFS_ERR_NOENT; |     lfs_dir_open(&lfs, &dir[0], "c/hello") => LFS_ERR_NOENT; | ||||||
|  |  | ||||||
|     lfs_file_open(&lfs, &file[0], "c/hello", LFS_O_RDONLY) => 0; |     lfs_file_open(&lfs, &file[0], "d/hello", LFS_O_RDONLY) => 0; | ||||||
|  |     lfs_file_read(&lfs, &file[0], buffer, 5) => 5; | ||||||
|  |     memcmp(buffer, "hola\n", 5) => 0; | ||||||
|  |     lfs_file_read(&lfs, &file[0], buffer, 8) => 8; | ||||||
|  |     memcmp(buffer, "bonjour\n", 8) => 0; | ||||||
|  |     lfs_file_read(&lfs, &file[0], buffer, 6) => 6; | ||||||
|  |     memcmp(buffer, "ohayo\n", 6) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Move state stealing ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs_remove(&lfs, "b") => 0; | ||||||
|  |     lfs_remove(&lfs, "c") => 0; | ||||||
|  |  | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "a/hi") => LFS_ERR_NOENT; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "b") => LFS_ERR_NOENT; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "c") => LFS_ERR_NOENT; | ||||||
|  |  | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "d/hi") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "bonjour") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "hola") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "ohayo") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
|  |  | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "a/hello") => LFS_ERR_NOENT; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "b") => LFS_ERR_NOENT; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "c") => LFS_ERR_NOENT; | ||||||
|  |  | ||||||
|  |     lfs_file_open(&lfs, &file[0], "d/hello", LFS_O_RDONLY) => 0; | ||||||
|     lfs_file_read(&lfs, &file[0], buffer, 5) => 5; |     lfs_file_read(&lfs, &file[0], buffer, 5) => 5; | ||||||
|     memcmp(buffer, "hola\n", 5) => 0; |     memcmp(buffer, "hola\n", 5) => 0; | ||||||
|     lfs_file_read(&lfs, &file[0], buffer, 8) => 8; |     lfs_file_read(&lfs, &file[0], buffer, 8) => 8; | ||||||
|   | |||||||
| @@ -15,25 +15,29 @@ tests/test.py << TEST | |||||||
|     lfs_mkdir(&lfs, "parent/child") => 0; |     lfs_mkdir(&lfs, "parent/child") => 0; | ||||||
|     lfs_remove(&lfs, "parent/orphan") => 0; |     lfs_remove(&lfs, "parent/orphan") => 0; | ||||||
| TEST | TEST | ||||||
| # remove most recent file, this should be the update to the previous | # corrupt most recent commit, this should be the update to the previous | ||||||
| # linked-list entry and should orphan the child | # linked-list entry and should orphan the child | ||||||
| rm -v blocks/8 | tests/corrupt.py | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; |  | ||||||
|     unsigned before = 0; |  | ||||||
|     lfs_traverse(&lfs, test_count, &before) => 0; |  | ||||||
|     test_log("before", before); |  | ||||||
|  |  | ||||||
|     lfs_deorphan(&lfs) => 0; |  | ||||||
|  |  | ||||||
|     lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; |     lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; | ||||||
|     unsigned after = 0; |     lfs_ssize_t before = lfs_fs_size(&lfs); | ||||||
|     lfs_traverse(&lfs, test_count, &after) => 0; |     before => 8; | ||||||
|     test_log("after", after); |  | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; | ||||||
|  |     lfs_ssize_t orphaned = lfs_fs_size(&lfs); | ||||||
|  |     orphaned => 8; | ||||||
|  |  | ||||||
|  |     lfs_mkdir(&lfs, "parent/otherchild") => 0; | ||||||
|  |  | ||||||
|  |     lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; | ||||||
|  |     lfs_ssize_t deorphaned = lfs_fs_size(&lfs); | ||||||
|  |     deorphaned => 8; | ||||||
|  |  | ||||||
|     int diff = before - after; |  | ||||||
|     diff => 2; |  | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
|  |  | ||||||
|   | |||||||
| @@ -128,6 +128,14 @@ tests/test.py << TEST | |||||||
|     lfs_mkdir(&lfs, "/") => LFS_ERR_EXIST; |     lfs_mkdir(&lfs, "/") => LFS_ERR_EXIST; | ||||||
|     lfs_file_open(&lfs, &file[0], "/", LFS_O_WRONLY | LFS_O_CREAT) |     lfs_file_open(&lfs, &file[0], "/", LFS_O_WRONLY | LFS_O_CREAT) | ||||||
|         => LFS_ERR_ISDIR; |         => LFS_ERR_ISDIR; | ||||||
|  |  | ||||||
|  |     // more corner cases | ||||||
|  |     lfs_remove(&lfs, "") => LFS_ERR_INVAL; | ||||||
|  |     lfs_remove(&lfs, ".") => LFS_ERR_INVAL; | ||||||
|  |     lfs_remove(&lfs, "..") => LFS_ERR_INVAL; | ||||||
|  |     lfs_remove(&lfs, "/") => LFS_ERR_INVAL; | ||||||
|  |     lfs_remove(&lfs, "//") => LFS_ERR_INVAL; | ||||||
|  |     lfs_remove(&lfs, "./") => LFS_ERR_INVAL; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
|  |  | ||||||
| @@ -139,5 +147,55 @@ tests/test.py << TEST | |||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
|  |  | ||||||
|  | echo "--- Superblock conflict test ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_mkdir(&lfs, "littlefs") => 0; | ||||||
|  |     lfs_remove(&lfs, "littlefs") => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Max path test ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     memset(buffer, 'w', LFS_NAME_MAX+1); | ||||||
|  |     buffer[LFS_NAME_MAX+2] = '\0'; | ||||||
|  |     lfs_mkdir(&lfs, (char*)buffer) => LFS_ERR_NAMETOOLONG; | ||||||
|  |     lfs_file_open(&lfs, &file[0], (char*)buffer, | ||||||
|  |             LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_NAMETOOLONG; | ||||||
|  |  | ||||||
|  |     memcpy(buffer, "coffee/", strlen("coffee/")); | ||||||
|  |     memset(buffer+strlen("coffee/"), 'w', LFS_NAME_MAX+1); | ||||||
|  |     buffer[strlen("coffee/")+LFS_NAME_MAX+2] = '\0'; | ||||||
|  |     lfs_mkdir(&lfs, (char*)buffer) => LFS_ERR_NAMETOOLONG; | ||||||
|  |     lfs_file_open(&lfs, &file[0], (char*)buffer, | ||||||
|  |             LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_NAMETOOLONG; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Really big path test ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     memset(buffer, 'w', LFS_NAME_MAX); | ||||||
|  |     buffer[LFS_NAME_MAX+1] = '\0'; | ||||||
|  |     lfs_mkdir(&lfs, (char*)buffer) => 0; | ||||||
|  |     lfs_remove(&lfs, (char*)buffer) => 0; | ||||||
|  |     lfs_file_open(&lfs, &file[0], (char*)buffer, | ||||||
|  |             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     lfs_remove(&lfs, (char*)buffer) => 0; | ||||||
|  |  | ||||||
|  |     memcpy(buffer, "coffee/", strlen("coffee/")); | ||||||
|  |     memset(buffer+strlen("coffee/"), 'w', LFS_NAME_MAX); | ||||||
|  |     buffer[strlen("coffee/")+LFS_NAME_MAX+1] = '\0'; | ||||||
|  |     lfs_mkdir(&lfs, (char*)buffer) => 0; | ||||||
|  |     lfs_remove(&lfs, (char*)buffer) => 0; | ||||||
|  |     lfs_file_open(&lfs, &file[0], (char*)buffer, | ||||||
|  |             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     lfs_remove(&lfs, (char*)buffer) => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
| echo "--- Results ---" | echo "--- Results ---" | ||||||
| tests/stats.py | tests/stats.py | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ tests/test.py << TEST | |||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_mkdir(&lfs, "hello") => 0; |     lfs_mkdir(&lfs, "hello") => 0; | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |     for (int i = 0; i < $LARGESIZE; i++) { | ||||||
|         sprintf((char*)buffer, "hello/kitty%d", i); |         sprintf((char*)buffer, "hello/kitty%03d", i); | ||||||
|         lfs_file_open(&lfs, &file[0], (char*)buffer, |         lfs_file_open(&lfs, &file[0], (char*)buffer, | ||||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; |                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; | ||||||
|  |  | ||||||
| @@ -39,7 +39,7 @@ tests/test.py << TEST | |||||||
|     lfs_soff_t pos; |     lfs_soff_t pos; | ||||||
|     int i; |     int i; | ||||||
|     for (i = 0; i < $SMALLSIZE; i++) { |     for (i = 0; i < $SMALLSIZE; i++) { | ||||||
|         sprintf((char*)buffer, "kitty%d", i); |         sprintf((char*)buffer, "kitty%03d", i); | ||||||
|         lfs_dir_read(&lfs, &dir[0], &info) => 1; |         lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|         strcmp(info.name, (char*)buffer) => 0; |         strcmp(info.name, (char*)buffer) => 0; | ||||||
|         pos = lfs_dir_tell(&lfs, &dir[0]); |         pos = lfs_dir_tell(&lfs, &dir[0]); | ||||||
| @@ -47,12 +47,12 @@ tests/test.py << TEST | |||||||
|     pos >= 0 => 1; |     pos >= 0 => 1; | ||||||
|  |  | ||||||
|     lfs_dir_seek(&lfs, &dir[0], pos) => 0; |     lfs_dir_seek(&lfs, &dir[0], pos) => 0; | ||||||
|     sprintf((char*)buffer, "kitty%d", i); |     sprintf((char*)buffer, "kitty%03d", i); | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, (char*)buffer) => 0; |     strcmp(info.name, (char*)buffer) => 0; | ||||||
|  |  | ||||||
|     lfs_dir_rewind(&lfs, &dir[0]) => 0; |     lfs_dir_rewind(&lfs, &dir[0]) => 0; | ||||||
|     sprintf((char*)buffer, "kitty%d", 0); |     sprintf((char*)buffer, "kitty%03d", 0); | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, ".") => 0; |     strcmp(info.name, ".") => 0; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
| @@ -61,7 +61,7 @@ tests/test.py << TEST | |||||||
|     strcmp(info.name, (char*)buffer) => 0; |     strcmp(info.name, (char*)buffer) => 0; | ||||||
|  |  | ||||||
|     lfs_dir_seek(&lfs, &dir[0], pos) => 0; |     lfs_dir_seek(&lfs, &dir[0], pos) => 0; | ||||||
|     sprintf((char*)buffer, "kitty%d", i); |     sprintf((char*)buffer, "kitty%03d", i); | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, (char*)buffer) => 0; |     strcmp(info.name, (char*)buffer) => 0; | ||||||
|  |  | ||||||
| @@ -81,7 +81,7 @@ tests/test.py << TEST | |||||||
|     lfs_soff_t pos; |     lfs_soff_t pos; | ||||||
|     int i; |     int i; | ||||||
|     for (i = 0; i < $MEDIUMSIZE; i++) { |     for (i = 0; i < $MEDIUMSIZE; i++) { | ||||||
|         sprintf((char*)buffer, "kitty%d", i); |         sprintf((char*)buffer, "kitty%03d", i); | ||||||
|         lfs_dir_read(&lfs, &dir[0], &info) => 1; |         lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|         strcmp(info.name, (char*)buffer) => 0; |         strcmp(info.name, (char*)buffer) => 0; | ||||||
|         pos = lfs_dir_tell(&lfs, &dir[0]); |         pos = lfs_dir_tell(&lfs, &dir[0]); | ||||||
| @@ -89,12 +89,12 @@ tests/test.py << TEST | |||||||
|     pos >= 0 => 1; |     pos >= 0 => 1; | ||||||
|  |  | ||||||
|     lfs_dir_seek(&lfs, &dir[0], pos) => 0; |     lfs_dir_seek(&lfs, &dir[0], pos) => 0; | ||||||
|     sprintf((char*)buffer, "kitty%d", i); |     sprintf((char*)buffer, "kitty%03d", i); | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, (char*)buffer) => 0; |     strcmp(info.name, (char*)buffer) => 0; | ||||||
|  |  | ||||||
|     lfs_dir_rewind(&lfs, &dir[0]) => 0; |     lfs_dir_rewind(&lfs, &dir[0]) => 0; | ||||||
|     sprintf((char*)buffer, "kitty%d", 0); |     sprintf((char*)buffer, "kitty%03d", 0); | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, ".") => 0; |     strcmp(info.name, ".") => 0; | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
| @@ -103,7 +103,7 @@ tests/test.py << TEST | |||||||
|     strcmp(info.name, (char*)buffer) => 0; |     strcmp(info.name, (char*)buffer) => 0; | ||||||
|  |  | ||||||
|     lfs_dir_seek(&lfs, &dir[0], pos) => 0; |     lfs_dir_seek(&lfs, &dir[0], pos) => 0; | ||||||
|     sprintf((char*)buffer, "kitty%d", i); |     sprintf((char*)buffer, "kitty%03d", i); | ||||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|     strcmp(info.name, (char*)buffer) => 0; |     strcmp(info.name, (char*)buffer) => 0; | ||||||
|  |  | ||||||
| @@ -114,7 +114,7 @@ TEST | |||||||
| echo "--- Simple file seek ---" | echo "--- Simple file seek ---" | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDONLY) => 0; |     lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDONLY) => 0; | ||||||
|  |  | ||||||
|     lfs_soff_t pos; |     lfs_soff_t pos; | ||||||
|     size = strlen("kittycatcat"); |     size = strlen("kittycatcat"); | ||||||
| @@ -163,7 +163,7 @@ TEST | |||||||
| echo "--- Large file seek ---" | echo "--- Large file seek ---" | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDONLY) => 0; |     lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDONLY) => 0; | ||||||
|  |  | ||||||
|     lfs_soff_t pos; |     lfs_soff_t pos; | ||||||
|     size = strlen("kittycatcat"); |     size = strlen("kittycatcat"); | ||||||
| @@ -212,7 +212,7 @@ TEST | |||||||
| echo "--- Simple file seek and write ---" | echo "--- Simple file seek and write ---" | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0; |     lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDWR) => 0; | ||||||
|  |  | ||||||
|     lfs_soff_t pos; |     lfs_soff_t pos; | ||||||
|     size = strlen("kittycatcat"); |     size = strlen("kittycatcat"); | ||||||
| @@ -253,7 +253,7 @@ TEST | |||||||
| echo "--- Large file seek and write ---" | echo "--- Large file seek and write ---" | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0; |     lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDWR) => 0; | ||||||
|  |  | ||||||
|     lfs_soff_t pos; |     lfs_soff_t pos; | ||||||
|     size = strlen("kittycatcat"); |     size = strlen("kittycatcat"); | ||||||
| @@ -296,7 +296,7 @@ TEST | |||||||
| echo "--- Boundary seek and write ---" | echo "--- Boundary seek and write ---" | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0; |     lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDWR) => 0; | ||||||
|  |  | ||||||
|     size = strlen("hedgehoghog"); |     size = strlen("hedgehoghog"); | ||||||
|     const lfs_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019}; |     const lfs_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019}; | ||||||
| @@ -324,7 +324,7 @@ TEST | |||||||
| echo "--- Out-of-bounds seek ---" | echo "--- Out-of-bounds seek ---" | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0; |     lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDWR) => 0; | ||||||
|  |  | ||||||
|     size = strlen("kittycatcat"); |     size = strlen("kittycatcat"); | ||||||
|     lfs_file_size(&lfs, &file[0]) => $LARGESIZE*size; |     lfs_file_size(&lfs, &file[0]) => $LARGESIZE*size; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user