mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 16:14:16 +01:00 
			
		
		
		
	Compare commits
	
		
			112 Commits
		
	
	
		
			v2.2.1
			...
			v2-alpha-w
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 227936b043 | ||
|  | 8e501d5922 | ||
|  | a3abfc1fe8 | ||
|  | a326e47710 | ||
|  | ed49ea492a | ||
|  | 791891ae3b | ||
|  | 42ead30339 | ||
|  | 9819ba24e8 | ||
|  | 4264b28aa3 | ||
|  | 533b3bf686 | ||
|  | 24eaf86b0e | ||
|  | 67bbee56f5 | ||
|  | efc491facb | ||
|  | fd9e609eb7 | ||
|  | 88f15a8d41 | ||
|  | 2da9392fbf | ||
|  | 5422566b6a | ||
|  | e0e289207a | ||
|  | f96e8c130d | ||
|  | 78b460a907 | ||
|  | a64ff4fb95 | ||
|  | 4f0d248e09 | ||
|  | 7ffbe81bb2 | ||
|  | 2f73652937 | ||
|  | c3480c8c92 | ||
|  | 2bcec45c1b | ||
|  | a98affc951 | ||
|  | e7c4465bf5 | ||
|  | 597249eea0 | ||
|  | 67cf4e10f4 | ||
|  | 14469059bc | ||
|  | 3bb37d4aaf | ||
|  | a703859615 | ||
|  | 10f063571a | ||
|  | 7f41aa05a9 | ||
|  | b8290e306c | ||
|  | c74bcec184 | ||
|  | 868099836d | ||
|  | c3c15d7636 | ||
|  | 3a70619f41 | ||
|  | 22e3d33ada | ||
|  | 439a76e3e1 | ||
|  | 236d46ad17 | ||
|  | f1719e3310 | ||
|  | 37cf030445 | ||
|  | 7ec75a55f2 | ||
|  | dcfe94412a | ||
|  | 6a99f6c8fc | ||
|  | e6d49feb4a | ||
|  | f6347db3da | ||
|  | 48cc58e25c | ||
|  | 92d957ab40 | ||
|  | 5152b973f0 | ||
|  | 43e6aadef6 | ||
|  | 0276e00fac | ||
|  | 5b9755f141 | ||
|  | f5488f7192 | ||
|  | 2947006660 | ||
|  | d5f0e236b8 | ||
|  | 0d3350665e | ||
|  | 5064cf82fd | ||
|  | ff13345e96 | ||
|  | 82948861d2 | ||
|  | e9bf206c54 | ||
|  | 2223bef125 | ||
|  | d56f326dfb | ||
|  | 09b69c5b2a | ||
|  | a848b0ebbd | ||
|  | 6cda0442ea | ||
|  | 0e0f015fbd | ||
|  | fbd25ac533 | ||
|  | de0b719b56 | ||
|  | ca9e43158d | ||
|  | cd045ed2eb | ||
|  | 39267fd8b2 | ||
|  | 6c2c7e3f3d | ||
|  | 2759f012c0 | ||
|  | f4d6ca5552 | ||
|  | 57fbc52cfc | ||
|  | c8323112ee | ||
|  | 7d3c2be49a | ||
|  | 0e4eb788fe | ||
|  | 6af43aec5b | ||
|  | 9af404db09 | ||
|  | 0347416b89 | ||
|  | eb70143469 | ||
|  | 2aee22aa49 | ||
|  | a82ea60658 | ||
|  | 7c0f32dc0b | ||
|  | e48a2c488b | ||
|  | f37fa75d66 | ||
|  | bba71b23f4 | ||
|  | e4a35b78e7 | ||
|  | b56d82ff34 | ||
|  | ab9750f5ed | ||
|  | 2f32222914 | ||
|  | 7ad2d58ed0 | ||
|  | 689159e31d | ||
|  | 9a97a97e4c | ||
|  | 345f7f3235 | ||
|  | a418c2068d | ||
|  | d7ed7a41e9 | ||
|  | 960e152261 | ||
|  | 28a5a27bb9 | ||
|  | 72475f64f6 | ||
|  | 8773d7c81f | ||
|  | d636299daf | ||
|  | 2d6a37f775 | ||
|  | f58408c974 | ||
|  | e1f05ee046 | ||
|  | f54ad304fc | ||
|  | 2a738463b3 | 
| @@ -18,10 +18,11 @@ script: | ||||
|   - make test QUIET=1 | ||||
|  | ||||
|   # 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=512    -DLFS_PROG_SIZE=512" | ||||
|   - make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=1      -DLFS_CACHE_SIZE=4" | ||||
|   - 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 clean test QUIET=1 CFLAGS+="-DLFS_INLINE_MAX=0" | ||||
|   - make clean test QUIET=1 CFLAGS+="-DLFS_NO_INTRINSICS" | ||||
|  | ||||
|   # compile and find the code size with the smallest configuration | ||||
| @@ -102,7 +103,7 @@ jobs: | ||||
|         - NAME=littlefs-fuse | ||||
|       install: | ||||
|         - 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 | ||||
|         - gcc --version | ||||
|       before_script: | ||||
|   | ||||
							
								
								
									
										3
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								Makefile
									
									
									
									
									
								
							| @@ -37,7 +37,8 @@ size: $(OBJ) | ||||
|  | ||||
| .SUFFIXES: | ||||
| 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 | ||||
| test_%: tests/test_%.sh | ||||
|  | ||||
|   | ||||
| @@ -19,6 +19,40 @@ | ||||
| #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 (int 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 (int 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 | ||||
| int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { | ||||
|     lfs_emubd_t *emu = cfg->context; | ||||
| @@ -46,20 +80,39 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { | ||||
|     } | ||||
|  | ||||
|     // 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"); | ||||
|     if (!f) { | ||||
|         return -errno; | ||||
|         memset(&emu->stats, 0, sizeof(emu->stats)); | ||||
|     } else { | ||||
|         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; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f); | ||||
|     if (res < 1) { | ||||
|         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) { | ||||
|             return -errno; | ||||
|         } | ||||
|  | ||||
|     err = fclose(f); | ||||
|     if (err) { | ||||
|         return -errno; | ||||
|         err = fclose(f); | ||||
|         if (err) { | ||||
|             return -errno; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| @@ -161,6 +214,13 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         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; | ||||
|     return 0; | ||||
| } | ||||
| @@ -206,13 +266,15 @@ int lfs_emubd_sync(const struct lfs_config *cfg) { | ||||
|     lfs_emubd_t *emu = cfg->context; | ||||
|  | ||||
|     // 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"); | ||||
|     if (!f) { | ||||
|         return -errno; | ||||
|     } | ||||
|  | ||||
|     lfs_emubd_tole32(emu); | ||||
|     size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f); | ||||
|     lfs_emubd_fromle32(emu); | ||||
|     if (res < 1) { | ||||
|         return -errno; | ||||
|     } | ||||
| @@ -222,13 +284,33 @@ int lfs_emubd_sync(const struct lfs_config *cfg) { | ||||
|         return -errno; | ||||
|     } | ||||
|  | ||||
|     snprintf(emu->child, LFS_NAME_MAX, "stats"); | ||||
|     snprintf(emu->child, LFS_NAME_MAX, ".stats"); | ||||
|     f = fopen(emu->path, "w"); | ||||
|     if (!f) { | ||||
|         return -errno; | ||||
|     } | ||||
|  | ||||
|     lfs_emubd_tole32(emu); | ||||
|     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) { | ||||
|         return -errno; | ||||
|     } | ||||
|   | ||||
| @@ -45,6 +45,10 @@ typedef struct lfs_emubd { | ||||
|         uint64_t erase_count; | ||||
|     } stats; | ||||
|  | ||||
|     struct { | ||||
|         lfs_block_t blocks[4]; | ||||
|     } history; | ||||
|  | ||||
|     struct { | ||||
|         uint32_t read_size; | ||||
|         uint32_t prog_size; | ||||
|   | ||||
							
								
								
									
										373
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										373
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -21,14 +21,14 @@ extern "C" | ||||
| // Software library version | ||||
| // Major (top-nibble), incremented on backwards incompatible changes | ||||
| // Minor (bottom-nibble), incremented on feature additions | ||||
| #define LFS_VERSION 0x00010006 | ||||
| #define LFS_VERSION 0x00020000 | ||||
| #define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) | ||||
| #define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >>  0)) | ||||
|  | ||||
| // Version of On-disk data structures | ||||
| // Major (top-nibble), incremented on backwards incompatible changes | ||||
| // 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_MINOR (0xffff & (LFS_DISK_VERSION >>  0)) | ||||
|  | ||||
| @@ -44,51 +44,97 @@ typedef int32_t  lfs_soff_t; | ||||
|  | ||||
| typedef uint32_t lfs_block_t; | ||||
|  | ||||
| // Max name size in bytes | ||||
| // Maximum size of all attributes per file in bytes, may be redefined but a | ||||
| // a smaller LFS_ATTR_MAX has no benefit. Stored in 12-bits and limited | ||||
| // to <= 0xfff. Stored in superblock and must be respected by other | ||||
| // littlefs drivers. | ||||
| #ifndef LFS_ATTR_MAX | ||||
| #define LFS_ATTR_MAX 0xfff | ||||
| #endif | ||||
|  | ||||
| // Maximum name size in bytes, may be redefined to reduce the size of the | ||||
| // info struct. Limited to <= LFS_ATTR_MAX. Stored in superblock and must | ||||
| // be respected by other littlefs drivers. | ||||
| #ifndef LFS_NAME_MAX | ||||
| #define LFS_NAME_MAX 255 | ||||
| #define LFS_NAME_MAX 0xff | ||||
| #endif | ||||
|  | ||||
| // Maximum inline file size in bytes. Large inline files require a larger | ||||
| // cache size, but if a file can be inline it does not need its own data | ||||
| // block. Limited to <= LFS_ATTR_MAX and <= cache_size. Stored in superblock | ||||
| // and must be respected by other littlefs drivers. | ||||
| #ifndef LFS_INLINE_MAX | ||||
| #define LFS_INLINE_MAX 0xfff | ||||
| #endif | ||||
|  | ||||
| // Possible error codes, these are negative to allow | ||||
| // valid positive return values | ||||
| enum lfs_error { | ||||
|     LFS_ERR_OK       = 0,    // No error | ||||
|     LFS_ERR_IO       = -5,   // Error during device operation | ||||
|     LFS_ERR_CORRUPT  = -52,  // Corrupted | ||||
|     LFS_ERR_NOENT    = -2,   // No directory entry | ||||
|     LFS_ERR_EXIST    = -17,  // Entry already exists | ||||
|     LFS_ERR_NOTDIR   = -20,  // Entry is not a dir | ||||
|     LFS_ERR_ISDIR    = -21,  // Entry is a dir | ||||
|     LFS_ERR_NOTEMPTY = -39,  // Dir is not empty | ||||
|     LFS_ERR_BADF     = -9,   // Bad file number | ||||
|     LFS_ERR_INVAL    = -22,  // Invalid parameter | ||||
|     LFS_ERR_NOSPC    = -28,  // No space left on device | ||||
|     LFS_ERR_NOMEM    = -12,  // No more memory available | ||||
|     LFS_ERR_OK          = 0,    // No error | ||||
|     LFS_ERR_IO          = -5,   // Error during device operation | ||||
|     LFS_ERR_CORRUPT     = -84,  // Corrupted | ||||
|     LFS_ERR_NOENT       = -2,   // No directory entry | ||||
|     LFS_ERR_EXIST       = -17,  // Entry already exists | ||||
|     LFS_ERR_NOTDIR      = -20,  // Entry is not a dir | ||||
|     LFS_ERR_ISDIR       = -21,  // Entry is a dir | ||||
|     LFS_ERR_NOTEMPTY    = -39,  // Dir is not empty | ||||
|     LFS_ERR_BADF        = -9,   // Bad file number | ||||
|     LFS_ERR_INVAL       = -22,  // Invalid parameter | ||||
|     LFS_ERR_NOSPC       = -28,  // No space left on device | ||||
|     LFS_ERR_NOMEM       = -12,  // No more memory available | ||||
|     LFS_ERR_NAMETOOLONG = -36,  // File name too long | ||||
| }; | ||||
|  | ||||
| // File types | ||||
| enum lfs_type { | ||||
|     LFS_TYPE_REG        = 0x11, | ||||
|     LFS_TYPE_DIR        = 0x22, | ||||
|     LFS_TYPE_SUPERBLOCK = 0x2e, | ||||
|     // file types | ||||
|     LFS_TYPE_REG            = 0x004, | ||||
|     LFS_TYPE_DIR            = 0x005, | ||||
|  | ||||
|     // internally used types | ||||
|     LFS_TYPE_USER           = 0x100, | ||||
|     LFS_TYPE_SUPERBLOCK     = 0x002, | ||||
|     LFS_TYPE_ROOT           = 0x003, | ||||
|     LFS_TYPE_CHILD          = 0x001, | ||||
|     LFS_TYPE_NAME           = 0x000, | ||||
|     LFS_TYPE_DELETE         = 0x060, | ||||
|     LFS_TYPE_STRUCT         = 0x020, | ||||
|     LFS_TYPE_GLOBALS        = 0x0e0, | ||||
|     LFS_TYPE_TAIL           = 0x080, | ||||
|     LFS_TYPE_SOFTTAIL       = 0x080, | ||||
|     LFS_TYPE_HARDTAIL       = 0x081, | ||||
|     LFS_TYPE_CRC            = 0x0a0, | ||||
|  | ||||
|     LFS_TYPE_DIRSTRUCT      = 0x020, | ||||
|     LFS_TYPE_INLINESTRUCT   = 0x021, | ||||
|     LFS_TYPE_CTZSTRUCT      = 0x022, | ||||
|  | ||||
|     // internal chip sources | ||||
|     LFS_FROM_REGION         = 0x000, | ||||
|     LFS_FROM_DISK           = 0x200, | ||||
|     LFS_FROM_MOVE           = 0x040, | ||||
|     LFS_FROM_COMPACT        = 0x041, | ||||
|     LFS_FROM_SPLIT          = 0x042, | ||||
|     LFS_FROM_ATTRS          = 0x043, | ||||
| }; | ||||
|  | ||||
| // File open flags | ||||
| enum lfs_open_flags { | ||||
|     // open flags | ||||
|     LFS_O_RDONLY = 1,        // Open a file as read only | ||||
|     LFS_O_WRONLY = 2,        // Open a file as write only | ||||
|     LFS_O_RDWR   = 3,        // Open a file as read and write | ||||
|     LFS_O_CREAT  = 0x0100,   // Create a file if it does not exist | ||||
|     LFS_O_EXCL   = 0x0200,   // Fail if a file already exists | ||||
|     LFS_O_TRUNC  = 0x0400,   // Truncate the existing file to zero size | ||||
|     LFS_O_APPEND = 0x0800,   // Move to end of file on every write | ||||
|     LFS_O_RDONLY = 1,         // Open a file as read only | ||||
|     LFS_O_WRONLY = 2,         // Open a file as write only | ||||
|     LFS_O_RDWR   = 3,         // Open a file as read and write | ||||
|     LFS_O_CREAT  = 0x0100,    // Create a file if it does not exist | ||||
|     LFS_O_EXCL   = 0x0200,    // Fail if a file already exists | ||||
|     LFS_O_TRUNC  = 0x0400,    // Truncate the existing file to zero size | ||||
|     LFS_O_APPEND = 0x0800,    // Move to end of file on every write | ||||
|  | ||||
|     // internally used flags | ||||
|     LFS_F_DIRTY   = 0x10000, // File does not match storage | ||||
|     LFS_F_WRITING = 0x20000, // File has been written since last flush | ||||
|     LFS_F_READING = 0x40000, // File has been read since last flush | ||||
|     LFS_F_ERRED   = 0x80000, // An error occured during write | ||||
|     LFS_F_DIRTY   = 0x010000, // File does not match storage | ||||
|     LFS_F_WRITING = 0x020000, // File has been written since last flush | ||||
|     LFS_F_READING = 0x040000, // File has been read since last flush | ||||
|     LFS_F_ERRED   = 0x080000, // An error occured during write | ||||
|     LFS_F_INLINE  = 0x100000, // Currently inlined in directory entry | ||||
| }; | ||||
|  | ||||
| // File seek flags | ||||
| @@ -126,26 +172,34 @@ struct lfs_config { | ||||
|     // are propogated to the user. | ||||
|     int (*sync)(const struct lfs_config *c); | ||||
|  | ||||
|     // Minimum size of a block read. This determines the size of read buffers. | ||||
|     // This may be larger than the physical read size to improve performance | ||||
|     // by caching more of the block device. | ||||
|     // Minimum size of a block read. All read operations will be a | ||||
|     // multiple of this value. | ||||
|     lfs_size_t read_size; | ||||
|  | ||||
|     // Minimum size of a block program. This determines the size of program | ||||
|     // buffers. This may be larger than the physical program size to improve | ||||
|     // performance by caching more of the block device. | ||||
|     // Must be a multiple of the read size. | ||||
|     // Minimum size of a block program. All program operations will be a | ||||
|     // multiple of this value. | ||||
|     lfs_size_t prog_size; | ||||
|  | ||||
|     // Size of an erasable block. This does not impact ram consumption and | ||||
|     // may be larger than the physical erase size. However, this should be | ||||
|     // kept small as each file currently takes up an entire block. | ||||
|     // Must be a multiple of the program size. | ||||
|     // Must be a multiple of the read, program, and cache sizes. | ||||
|     lfs_size_t block_size; | ||||
|  | ||||
|     // Number of erasable blocks on the device. | ||||
|     lfs_size_t block_count; | ||||
|  | ||||
|     // Number of erase cycles before we should move data to another block. | ||||
|     // May be zero to never move data, in which case no block-level | ||||
|     // wear-leveling is performed. | ||||
|     uint32_t block_cycles; | ||||
|  | ||||
|     // Size of block caches. Each cache buffers a portion of a block in RAM. | ||||
|     // This determines the size of the read cache, the program cache, and a | ||||
|     // cache per file. Larger caches can improve performance by storing more | ||||
|     // data. Must be a multiple of the read and program sizes. | ||||
|     lfs_size_t cache_size; | ||||
|  | ||||
|     // Number of blocks to lookahead during block allocation. A larger | ||||
|     // lookahead reduces the number of passes required to allocate a block. | ||||
|     // The lookahead buffer requires only 1 bit per block so it can be quite | ||||
| @@ -162,16 +216,24 @@ struct lfs_config { | ||||
|     // lookahead block. | ||||
|     void *lookahead_buffer; | ||||
|  | ||||
|     // Optional, statically allocated buffer for files. Must be program sized. | ||||
|     // If enabled, only one file may be opened at a time. | ||||
|     void *file_buffer; | ||||
| }; | ||||
|     // Optional upper limit on file attributes in bytes. No downside for larger | ||||
|     // attributes size but must be less than LFS_ATTR_MAX. Defaults to | ||||
|     // LFS_ATTR_MAX when zero.Stored in superblock and must be respected by | ||||
|     // other littlefs drivers. | ||||
|     lfs_size_t attr_max; | ||||
|  | ||||
| // Optional configuration provided during lfs_file_opencfg | ||||
| struct lfs_file_config { | ||||
|     // Optional, statically allocated buffer for files. Must be program sized. | ||||
|     // If NULL, malloc will be used by default. | ||||
|     void *buffer; | ||||
|     // Optional upper limit on length of file names in bytes. No downside for | ||||
|     // larger names except the size of the info struct which is controlled by | ||||
|     // 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 upper limit on inlined files in bytes. Large inline files | ||||
|     // require a larger cache size, but if a file can be inlined it does not | ||||
|     // need its own data block. Must be smaller than cache_size and less than | ||||
|     // LFS_INLINE_MAX. Defaults to min(LFS_INLINE_MAX, read_size) when zero. | ||||
|     // Stored in superblock and must be respected by other littlefs drivers. | ||||
|     lfs_size_t inline_max; | ||||
| }; | ||||
|  | ||||
| // File info structure | ||||
| @@ -186,77 +248,130 @@ struct lfs_info { | ||||
|     char name[LFS_NAME_MAX+1]; | ||||
| }; | ||||
|  | ||||
| // Custom attribute structure | ||||
| struct lfs_attr { | ||||
|     // 8-bit type of attribute, provided by user and used to | ||||
|     // identify the attribute | ||||
|     uint8_t type; | ||||
|  | ||||
|     // Pointer to buffer containing the attribute | ||||
|     void *buffer; | ||||
|  | ||||
|     // Size of attribute in bytes, limited to LFS_ATTR_MAX | ||||
|     lfs_size_t size; | ||||
|  | ||||
|     // Pointer to next attribute in linked list | ||||
|     struct lfs_attr *next; | ||||
| }; | ||||
|  | ||||
| // Optional configuration provided during lfs_file_opencfg | ||||
| struct lfs_file_config { | ||||
|     // Optional, statically allocated buffer for files. Must be program sized. | ||||
|     // If NULL, malloc will be used by default. | ||||
|     void *buffer; | ||||
|  | ||||
|     // Optional, linked list of custom attributes related to the file. If the | ||||
|     // file is opened with read access, the attributes will be read from | ||||
|     // 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; | ||||
| }; | ||||
|  | ||||
|  | ||||
| /// littlefs data structures /// | ||||
| typedef struct lfs_entry { | ||||
|     lfs_off_t off; | ||||
|  | ||||
|     struct lfs_disk_entry { | ||||
|         uint8_t type; | ||||
|         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; | ||||
| typedef struct lfs_mattr { | ||||
|     int32_t tag; | ||||
|     const void *buffer; | ||||
|     const struct lfs_mattr *next; | ||||
| } lfs_mattr_t; | ||||
|  | ||||
| typedef struct lfs_cache { | ||||
|     lfs_block_t block; | ||||
|     lfs_off_t off; | ||||
|     lfs_size_t size; | ||||
|     uint8_t *buffer; | ||||
| } lfs_cache_t; | ||||
|  | ||||
| typedef union lfs_global { | ||||
|     uint32_t u32[3]; | ||||
|     struct { | ||||
|         lfs_block_t movepair[2]; | ||||
|         uint16_t moveid; | ||||
|         uint8_t deorphaned; | ||||
|     } l; | ||||
|     struct { | ||||
|         lfs_block_t movepair[2]; | ||||
|         uint16_t moveid; | ||||
|         uint8_t orphans; | ||||
|     } g; | ||||
| } lfs_global_t; | ||||
|  | ||||
| typedef struct lfs_mdir { | ||||
|     lfs_block_t pair[2]; | ||||
|     uint32_t rev; | ||||
|     uint32_t etag; | ||||
|     lfs_off_t off; | ||||
|     uint16_t count; | ||||
|     bool erased; | ||||
|     bool split; | ||||
|     lfs_block_t tail[2]; | ||||
|     lfs_global_t locals; | ||||
| } lfs_mdir_t; | ||||
|  | ||||
| typedef struct lfs_mlist { | ||||
|     struct lfs_mlist *next; | ||||
|     uint16_t id; | ||||
|     uint8_t type; | ||||
|     lfs_mdir_t m; | ||||
| } lfs_mlist_t; | ||||
|  | ||||
| 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; | ||||
|  | ||||
| typedef struct lfs_file { | ||||
|     struct lfs_file *next; | ||||
|     lfs_block_t pair[2]; | ||||
|     lfs_off_t poff; | ||||
|     uint16_t id; | ||||
|     uint8_t type; | ||||
|     lfs_mdir_t m; | ||||
|  | ||||
|     lfs_block_t head; | ||||
|     lfs_size_t size; | ||||
|     struct lfs_ctz { | ||||
|         lfs_block_t head; | ||||
|         lfs_size_t size; | ||||
|     } ctz; | ||||
|  | ||||
|     const struct lfs_file_config *cfg; | ||||
|     uint32_t flags; | ||||
|     lfs_off_t pos; | ||||
|     lfs_block_t block; | ||||
|     lfs_off_t off; | ||||
|     lfs_cache_t cache; | ||||
|  | ||||
|     const struct lfs_file_config *cfg; | ||||
| } 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 { | ||||
|     lfs_off_t off; | ||||
|     char magic[8]; | ||||
|     uint32_t version; | ||||
|  | ||||
|     struct lfs_disk_superblock { | ||||
|         uint8_t type; | ||||
|         uint8_t elen; | ||||
|         uint8_t alen; | ||||
|         uint8_t nlen; | ||||
|         lfs_block_t root[2]; | ||||
|         uint32_t block_size; | ||||
|         uint32_t block_count; | ||||
|         uint32_t version; | ||||
|         char magic[8]; | ||||
|     } d; | ||||
|     lfs_size_t block_size; | ||||
|     lfs_size_t block_count; | ||||
|  | ||||
|     lfs_size_t attr_max; | ||||
|     lfs_size_t name_max; | ||||
|     lfs_size_t inline_max; | ||||
| } lfs_superblock_t; | ||||
|  | ||||
| typedef struct lfs_free { | ||||
| @@ -269,17 +384,23 @@ typedef struct lfs_free { | ||||
|  | ||||
| // The littlefs type | ||||
| 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 pcache; | ||||
|  | ||||
|     lfs_block_t root[2]; | ||||
|     lfs_mlist_t *mlist; | ||||
|     uint32_t seed; | ||||
|  | ||||
|     lfs_global_t globals; | ||||
|     lfs_global_t locals; | ||||
|     lfs_free_t free; | ||||
|     bool deorphaned; | ||||
|  | ||||
|     const struct lfs_config *cfg; | ||||
|     lfs_size_t block_size; | ||||
|     lfs_size_t block_count; | ||||
|     lfs_size_t attr_max; | ||||
|     lfs_size_t name_max; | ||||
|     lfs_size_t inline_max; | ||||
| } lfs_t; | ||||
|  | ||||
|  | ||||
| @@ -332,6 +453,31 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath); | ||||
| // Returns a negative error code on failure. | ||||
| 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. | ||||
| // | ||||
| // 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, and setting the size of an attribute to zero deletes | ||||
| // the attribute. | ||||
| // | ||||
| // 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); | ||||
|  | ||||
|  | ||||
| /// File operations /// | ||||
|  | ||||
| @@ -466,7 +612,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); | ||||
|  | ||||
|  | ||||
| /// 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 | ||||
| // | ||||
| @@ -475,16 +629,7 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); | ||||
| // blocks are in use or how much of the storage is available. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); | ||||
|  | ||||
| // Prunes any recoverable errors that may have occured in the filesystem | ||||
| // | ||||
| // Not needed to be called by user unless an operation is interrupted | ||||
| // but the filesystem is still mounted. This is already called on first | ||||
| // allocation. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_deorphan(lfs_t *lfs); | ||||
| int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); | ||||
|  | ||||
|  | ||||
| #ifdef __cplusplus | ||||
|   | ||||
| @@ -11,7 +11,7 @@ | ||||
|  | ||||
|  | ||||
| // Software CRC implementation with small lookup table | ||||
| void lfs_crc(uint32_t *restrict crc, const void *buffer, size_t size) { | ||||
| uint32_t lfs_crc32(uint32_t crc, const void *buffer, size_t size) { | ||||
|     static const uint32_t rtable[16] = { | ||||
|         0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, | ||||
|         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; | ||||
|  | ||||
|     for (size_t i = 0; i < size; i++) { | ||||
|         *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] >> 0)) & 0xf]; | ||||
|         crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf]; | ||||
|     } | ||||
|  | ||||
|     return crc; | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										41
									
								
								lfs_util.h
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								lfs_util.h
									
									
									
									
									
								
							| @@ -11,8 +11,8 @@ | ||||
| // 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 | ||||
| // provided by the config file. To start I would suggest copying lfs_util.h and | ||||
| // modifying as needed. | ||||
| // provided by the config file. To start, I would suggest copying lfs_util.h | ||||
| // and modifying as needed. | ||||
| #ifdef LFS_CONFIG | ||||
| #define LFS_STRINGIZE(x) LFS_STRINGIZE2(x) | ||||
| #define LFS_STRINGIZE2(x) #x | ||||
| @@ -23,6 +23,7 @@ | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| #include <string.h> | ||||
| #include <inttypes.h> | ||||
|  | ||||
| #ifndef LFS_NO_MALLOC | ||||
| #include <stdlib.h> | ||||
| @@ -87,6 +88,15 @@ static inline uint32_t lfs_min(uint32_t a, uint32_t 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 | ||||
| static inline uint32_t lfs_npw2(uint32_t a) { | ||||
| #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); | ||||
| } | ||||
|  | ||||
| // 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) { | ||||
| #if !defined(LFS_NO_INTRINSICS) && ( \ | ||||
|     (defined(  BYTE_ORDER  ) &&   BYTE_ORDER   ==   ORDER_LITTLE_ENDIAN  ) || \ | ||||
| @@ -150,13 +160,34 @@ static inline uint32_t lfs_fromle32(uint32_t a) { | ||||
| #endif | ||||
| } | ||||
|  | ||||
| // Convert to 32-bit little-endian from native order | ||||
| static inline uint32_t lfs_tole32(uint32_t a) { | ||||
|     return lfs_fromle32(a); | ||||
| } | ||||
|  | ||||
| // Convert between 16-bit little-endian and native order | ||||
| static inline uint16_t lfs_fromle16(uint16_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 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 __builtin_bswap16(a); | ||||
| #else | ||||
|     return (((uint8_t*)&a)[0] <<  0) | | ||||
|            (((uint8_t*)&a)[1] <<  8); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static inline uint16_t lfs_tole16(uint16_t a) { | ||||
|     return lfs_fromle16(a); | ||||
| } | ||||
|  | ||||
| // Calculate CRC-32 with polynomial = 0x04c11db7 | ||||
| void lfs_crc(uint32_t *crc, const void *buffer, size_t size); | ||||
| uint32_t lfs_crc32(uint32_t crc, const void *buffer, size_t size); | ||||
|  | ||||
| // Allocate memory, only used if buffers are not provided to littlefs | ||||
| static inline void *lfs_malloc(size_t size) { | ||||
|   | ||||
							
								
								
									
										43
									
								
								tests/corrupt.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										43
									
								
								tests/corrupt.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| #!/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 | ||||
|             file.seek(tag & 0xfff, os.SEEK_CUR) | ||||
|  | ||||
|         # lob off last 3 bytes | ||||
|         file.seek(-((tag & 0xfff) + 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()) | ||||
							
								
								
									
										107
									
								
								tests/debug.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										107
									
								
								tests/debug.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| #!/usr/bin/env python2 | ||||
|  | ||||
| import struct | ||||
| import binascii | ||||
|  | ||||
| TYPES = { | ||||
|     (0x1ff, 0x004): 'reg', | ||||
|     (0x1ff, 0x005): 'dir', | ||||
|     (0x1ff, 0x002): 'superblock', | ||||
|     (0x1ff, 0x003): 'root', | ||||
|     (0x1ff, 0x001): 'child', | ||||
|     (0x1ff, 0x060): 'delete', | ||||
|     (0x1f0, 0x0e0): 'globals', | ||||
|     (0x1ff, 0x080): 'tail soft', | ||||
|     (0x1ff, 0x081): 'tail hard', | ||||
|     (0x1f0, 0x0a0): 'crc', | ||||
|     (0x1ff, 0x020): 'struct dir', | ||||
|     (0x1ff, 0x021): 'struct inline', | ||||
|     (0x1ff, 0x022): 'struct ctz', | ||||
|     (0x100, 0x100): 'attr', | ||||
| } | ||||
|  | ||||
| def typeof(type): | ||||
|     for prefix in range(9): | ||||
|         mask = 0x1ff & ~((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  %3s  %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 & 0x7fc00000) >> 22 | ||||
|         id   = (tag & 0x003ff000) >> 12 | ||||
|         size = (tag & 0x00000fff) >> 0 | ||||
|         iscrc = (type & 0x1f0) == 0x0f0 | ||||
|  | ||||
|         data = file.read(size) | ||||
|         if iscrc: | ||||
|             crc = binascii.crc32(data[:4], crc) | ||||
|         else: | ||||
|             crc = binascii.crc32(data, crc) | ||||
|  | ||||
|         print '%04x: %08x  %-14s  %3s  %3d  %-23s  %-8s' % ( | ||||
|             off, tag, | ||||
|             typeof(type) + (' bad!' if iscrc and ~crc else ''), | ||||
|             id if id != 0x3ff else '.', size, | ||||
|             ' '.join('%02x' % ord(c) for c in data[:8]), | ||||
|             ''.join(c if c >= ' ' and c <= '~' else '.' for c in data[:8])) | ||||
|  | ||||
|         off += tag & 0xfff | ||||
|         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 | ||||
|  | ||||
| def main(): | ||||
|     with open('blocks/config') as file: | ||||
|     with open('blocks/.config') as file: | ||||
|         s = struct.unpack('<LLLL', file.read()) | ||||
|         print 'read_size: %d' % s[0] | ||||
|         print 'prog_size: %d' % s[1] | ||||
| @@ -18,7 +18,7 @@ def main(): | ||||
|         os.path.getsize(os.path.join('blocks', 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()) | ||||
|         print 'read_count: %d' % s[0] | ||||
|         print 'prog_count: %d' % s[1] | ||||
|   | ||||
| @@ -66,7 +66,7 @@ uintmax_t test; | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_PROG_SIZE | ||||
| #define LFS_PROG_SIZE 16 | ||||
| #define LFS_PROG_SIZE LFS_READ_SIZE | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_BLOCK_SIZE | ||||
| @@ -77,6 +77,14 @@ uintmax_t test; | ||||
| #define LFS_BLOCK_COUNT 1024 | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_BLOCK_CYCLES | ||||
| #define LFS_BLOCK_CYCLES 1024 | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_CACHE_SIZE | ||||
| #define LFS_CACHE_SIZE 64 | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_LOOKAHEAD | ||||
| #define LFS_LOOKAHEAD 128 | ||||
| #endif | ||||
| @@ -88,11 +96,13 @@ const struct lfs_config cfg = {{ | ||||
|     .erase = &lfs_emubd_erase, | ||||
|     .sync  = &lfs_emubd_sync, | ||||
|  | ||||
|     .read_size   = LFS_READ_SIZE, | ||||
|     .prog_size   = LFS_PROG_SIZE, | ||||
|     .block_size  = LFS_BLOCK_SIZE, | ||||
|     .block_count = LFS_BLOCK_COUNT, | ||||
|     .lookahead   = LFS_LOOKAHEAD, | ||||
|     .read_size    = LFS_READ_SIZE, | ||||
|     .prog_size    = LFS_PROG_SIZE, | ||||
|     .block_size   = LFS_BLOCK_SIZE, | ||||
|     .block_count  = LFS_BLOCK_COUNT, | ||||
|     .block_cycles = LFS_BLOCK_CYCLES, | ||||
|     .cache_size   = LFS_CACHE_SIZE, | ||||
|     .lookahead    = LFS_LOOKAHEAD, | ||||
| }}; | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -10,7 +10,7 @@ def generate(test): | ||||
|         template = file.read() | ||||
|  | ||||
|     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) | ||||
|         if match: | ||||
|             tab, test, expect = match.groups() | ||||
|   | ||||
| @@ -194,55 +194,98 @@ tests/test.py << TEST | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     memcmp(buffer, "exhaustion", size) => 0; | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_remove(&lfs, "exhaustion") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Dir exhaustion test ---" | ||||
| tests/test.py << TEST | ||||
|     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"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|             i < (cfg.block_count-6)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     int count = 0; | ||||
|     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_close(&lfs, &file[0]) => 0; | ||||
|  | ||||
|     lfs_mkdir(&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); | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|             i < (cfg.block_size-8); | ||||
|             i += size) { | ||||
|     // 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+1; i++) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|  | ||||
|     lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; | ||||
|  | ||||
|     lfs_remove(&lfs, "exhaustion") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Chained dir exhaustion test ---" | ||||
| tests/test.py << TEST | ||||
|     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 < 9; i++) { | ||||
|         sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i); | ||||
|         lfs_mkdir(&lfs, (char*)buffer) => 0; | ||||
|     } | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|             i < (cfg.block_count-24)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     int count = 0; | ||||
|     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; | ||||
|     for (int i = 0; i < 9; 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_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_sync(&lfs, &file[0]) => 0; | ||||
|  | ||||
|     for (int i = 0; i < 9; i++) { | ||||
|         sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i); | ||||
| @@ -251,19 +294,25 @@ tests/test.py << TEST | ||||
|  | ||||
|     lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; | ||||
|  | ||||
|     lfs_remove(&lfs, "exhaustion") => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|             i < (cfg.block_count-26)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     // shorten file to try a second chained dir | ||||
|     while (true) { | ||||
|         err = lfs_mkdir(&lfs, "exhaustiondir"); | ||||
|         if (err != LFS_ERR_NOSPC) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         lfs_ssize_t filesize = lfs_file_size(&lfs, &file[0]); | ||||
|         filesize > 0 => true; | ||||
|  | ||||
|         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_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Split dir test ---" | ||||
| @@ -274,28 +323,38 @@ TEST | ||||
| tests/test.py << TEST | ||||
|     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_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_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     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) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|  | ||||
|     // remount to force reset of lookahead | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     // open hole | ||||
|     lfs_remove(&lfs, "bump") => 0; | ||||
|  | ||||
|     lfs_mkdir(&lfs, "splitdir") => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "splitdir/bump", | ||||
|             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_unmount(&lfs) => 0; | ||||
| @@ -314,7 +373,7 @@ tests/test.py << TEST | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     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) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     } | ||||
| @@ -325,7 +384,7 @@ tests/test.py << TEST | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     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) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     } | ||||
| @@ -333,7 +392,6 @@ tests/test.py << TEST | ||||
|  | ||||
|     // remount to force reset of lookahead | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     // rewrite one file | ||||
| @@ -343,7 +401,7 @@ tests/test.py << TEST | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     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) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     } | ||||
| @@ -357,7 +415,7 @@ tests/test.py << TEST | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     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) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     } | ||||
| @@ -377,7 +435,7 @@ tests/test.py << TEST | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     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) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     } | ||||
| @@ -388,7 +446,7 @@ tests/test.py << TEST | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     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) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     } | ||||
| @@ -396,7 +454,6 @@ tests/test.py << TEST | ||||
|  | ||||
|     // remount to force reset of lookahead | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     // rewrite one file with a hole of one block | ||||
| @@ -406,7 +463,7 @@ tests/test.py << TEST | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     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) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     } | ||||
|   | ||||
							
								
								
									
										262
									
								
								tests/test_attrs.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										262
									
								
								tests/test_attrs.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,262 @@ | ||||
| #!/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_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_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 a1 = {'A', buffer,    4}; | ||||
|     struct lfs_attr b1 = {'B', buffer+4,  6, &a1}; | ||||
|     struct lfs_attr c1 = {'C', buffer+10, 5, &b1}; | ||||
|     struct lfs_file_config cfg1 = {.attrs = &c1}; | ||||
|  | ||||
|     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; | ||||
|  | ||||
|     b1.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); | ||||
|     b1.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; | ||||
|  | ||||
|     b1.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); | ||||
|     b1.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; | ||||
|  | ||||
|     b1.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); | ||||
|     b1.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; | ||||
|  | ||||
|     a1.size = LFS_ATTR_MAX+1; | ||||
|     lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) | ||||
|         => LFS_ERR_NOSPC; | ||||
|  | ||||
|     struct lfs_attr a2 = {'A', buffer,    4}; | ||||
|     struct lfs_attr b2 = {'B', buffer+4,  9, &a2}; | ||||
|     struct lfs_attr c2 = {'C', buffer+13, 5, &b2}; | ||||
|     struct lfs_file_config cfg2 = {.attrs = &c2}; | ||||
|     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; | ||||
|     a1.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 a2 = {'A', buffer,    4}; | ||||
|     struct lfs_attr b2 = {'B', buffer+4,  9, &a2}; | ||||
|     struct lfs_attr c2 = {'C', buffer+13, 5, &b2}; | ||||
|     struct lfs_file_config cfg2 = {.attrs = &c2}; | ||||
|  | ||||
|     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 a1 = {'B', "gggg", 4}; | ||||
|     struct lfs_attr b1 = {'C', "",     0, &a1}; | ||||
|     struct lfs_attr c1 = {'D', "hhhh", 4, &b1}; | ||||
|     struct lfs_file_config cfg1 = {.attrs = &c1}; | ||||
|  | ||||
|     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) => 0; | ||||
|     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 | ||||
| lfs_mktree | ||||
| lfs_chktree | ||||
| BLOCKS="$(ls blocks | grep -vw '[01]')" | ||||
|  | ||||
| echo "--- Block corruption ---" | ||||
| for i in {0..33} | ||||
| for b in $BLOCKS | ||||
| do  | ||||
|     rm -rf blocks | ||||
|     mkdir blocks | ||||
|     ln -s /dev/zero blocks/$(printf '%x' $i) | ||||
|     ln -s /dev/zero blocks/$b | ||||
|     lfs_mktree | ||||
|     lfs_chktree | ||||
| done | ||||
|  | ||||
| echo "--- Block persistance ---" | ||||
| for i in {0..33} | ||||
| for b in $BLOCKS | ||||
| do  | ||||
|     rm -rf blocks | ||||
|     mkdir blocks | ||||
|     lfs_mktree | ||||
|     chmod a-w blocks/$(printf '%x' $i) | ||||
|     chmod a-w blocks/$b | ||||
|     lfs_mktree | ||||
|     lfs_chktree | ||||
| done | ||||
| @@ -96,7 +97,7 @@ done | ||||
| echo "--- Big region corruption ---" | ||||
| rm -rf blocks | ||||
| mkdir blocks | ||||
| for i in {2..255} | ||||
| for i in {2..512} | ||||
| do | ||||
|     ln -s /dev/zero blocks/$(printf '%x' $i) | ||||
| done | ||||
| @@ -106,7 +107,7 @@ lfs_chktree | ||||
| echo "--- Alternating corruption ---" | ||||
| rm -rf blocks | ||||
| mkdir blocks | ||||
| for i in {2..511..2} | ||||
| for i in {2..1024..2} | ||||
| do | ||||
|     ln -s /dev/zero blocks/$(printf '%x' $i) | ||||
| done | ||||
|   | ||||
							
								
								
									
										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 | ||||
| @@ -9,14 +9,6 @@ tests/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| 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 ---" | ||||
| tests/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| @@ -26,22 +18,32 @@ tests/test.py << TEST | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| 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 | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_format(&lfs, &cfg) => LFS_ERR_NOSPC; | ||||
| TEST | ||||
| rm blocks/0 blocks/1 | ||||
|  | ||||
| echo "--- Invalid mount ---" | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||
| TEST | ||||
|  | ||||
| echo "--- Valid corrupt mount ---" | ||||
| echo "--- Expanding superblock ---" | ||||
| tests/test.py << TEST | ||||
|     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 | ||||
| rm blocks/0 | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "dummy") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
|   | ||||
| @@ -59,7 +59,7 @@ tests/test.py << TEST | ||||
|     lfs_rename(&lfs, "b/hello", "c/hello") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| rm -v blocks/7 | ||||
| tests/corrupt.py -n 1 | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 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_unmount(&lfs) => 0; | ||||
| TEST | ||||
| rm -v blocks/8 | ||||
| rm -v blocks/a | ||||
| tests/corrupt.py -n 2 | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||
| @@ -108,6 +107,32 @@ tests/test.py << TEST | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| 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 ---" | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
| @@ -140,7 +165,7 @@ tests/test.py << TEST | ||||
|     lfs_rename(&lfs, "b/hi", "c/hi") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| rm -v blocks/7 | ||||
| tests/corrupt.py -n 1 | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 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; | ||||
|     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_unmount(&lfs) => 0; | ||||
| @@ -169,8 +192,7 @@ tests/test.py << TEST | ||||
|     lfs_rename(&lfs, "c/hi", "d/hi") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| rm -v blocks/9 | ||||
| rm -v blocks/a | ||||
| tests/corrupt.py -n 2 | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 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; | ||||
|     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_close(&lfs, &dir[0]) => 0; | ||||
| @@ -189,6 +209,36 @@ tests/test.py << TEST | ||||
|     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 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_unmount(&lfs) => 0; | ||||
| TEST | ||||
| @@ -199,9 +249,9 @@ tests/test.py << TEST | ||||
|  | ||||
|     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], "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; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
| @@ -217,9 +267,55 @@ tests/test.py << TEST | ||||
|  | ||||
|     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], "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, "hola") => 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, "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; | ||||
|     memcmp(buffer, "hola\n", 5) => 0; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, 8) => 8; | ||||
|   | ||||
| @@ -15,25 +15,29 @@ tests/test.py << TEST | ||||
|     lfs_mkdir(&lfs, "parent/child") => 0; | ||||
|     lfs_remove(&lfs, "parent/orphan") => 0; | ||||
| 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 | ||||
| rm -v blocks/8 | ||||
| tests/corrupt.py | ||||
| tests/test.py << TEST | ||||
|     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; | ||||
|     unsigned after = 0; | ||||
|     lfs_traverse(&lfs, test_count, &after) => 0; | ||||
|     test_log("after", after); | ||||
|     lfs_ssize_t before = lfs_fs_size(&lfs); | ||||
|     before => 8; | ||||
|  | ||||
|     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; | ||||
| TEST | ||||
|  | ||||
|   | ||||
| @@ -139,5 +139,23 @@ tests/test.py << TEST | ||||
|     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 "--- Results ---" | ||||
| tests/stats.py | ||||
|   | ||||
		Reference in New Issue
	
	Block a user