mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 16:14:16 +01:00 
			
		
		
		
	Added root entry and expanding superblocks
Expanding superblocks has been on my wishlist for a while. The basic
idea is that instead of maintaining a fixed offset blocks {0, 1} to the
the root directory (1 pointer), we maintain a dynamically sized
linked-list of superblocks that point to the actual root. If the number
of writes to the root exceeds some value, we increase the size of the
superblock linked-list.
This can leverage existing metadata-pair operations. The revision count for
metadata-pairs provides some knowledge on how much wear we've put on the
superblock, and the threaded linked-list can also be reused for this
purpose. This means superblock expansion is both optional and cheap to
implement.
Expanding superblocks helps both extremely small and extremely large filesystem
(extreme being relative of course). On the small end, we can actually
collapse the superblock into the root directory and drop the hard requirement
of 4-blocks for the superblock. On the large end, our superblock will
now last longer than the rest of the filesystem. Each time we expand,
the number of cycles until the superblock dies is increased by a power.
Before we were stuck with this layout:
level  cycles  limit    layout
1      E^2     390 MiB  s0 -> root
Now we expand every time a fixed offset is exceeded:
level  cycles  limit    layout
0      E       4 KiB    s0+root
1      E^2     390 MiB  s0 -> root
2      E^3     37 TiB   s0 -> s1 -> root
3      E^4     3.6 EiB  s0 -> s1 -> s2 -> root
...
Where the cycles are the number of cycles before death, and the limit is
the worst-case size a filesystem where early superblock death becomes a
concern (all writes to root using this formula: E^|s| = E*B, E = erase
cycles = 100000, B = block count, assuming 4096 byte blocks).
Note we can also store copies of the superblock entry on the expanded
superblocks. This may help filesystem recover tools in the future.
			
			
This commit is contained in:
		
							
								
								
									
										200
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										200
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -377,6 +377,25 @@ static void lfs_ctz_tole32(struct lfs_ctz *ctz) { | |||||||
|     ctz->size = lfs_tole32(ctz->size); |     ctz->size = lfs_tole32(ctz->size); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static inline void lfs_superblock_fromle32(lfs_superblock_t *superblock) { | ||||||
|  |     superblock->version     = lfs_fromle32(superblock->version); | ||||||
|  |     superblock->block_size  = lfs_fromle32(superblock->block_size); | ||||||
|  |     superblock->block_count = lfs_fromle32(superblock->block_count); | ||||||
|  |     superblock->inline_max  = lfs_fromle32(superblock->inline_max); | ||||||
|  |     superblock->attr_max    = lfs_fromle32(superblock->attr_max); | ||||||
|  |     superblock->name_max    = lfs_fromle32(superblock->name_max); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline void lfs_superblock_tole32(lfs_superblock_t *superblock) { | ||||||
|  |     superblock->version     = lfs_tole32(superblock->version); | ||||||
|  |     superblock->block_size  = lfs_tole32(superblock->block_size); | ||||||
|  |     superblock->block_count = lfs_tole32(superblock->block_count); | ||||||
|  |     superblock->inline_max  = lfs_tole32(superblock->inline_max); | ||||||
|  |     superblock->attr_max    = lfs_tole32(superblock->attr_max); | ||||||
|  |     superblock->name_max    = lfs_tole32(superblock->name_max); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /// Entry tag operations /// | /// Entry tag operations /// | ||||||
| #define LFS_MKTAG(type, id, size) \ | #define LFS_MKTAG(type, id, size) \ | ||||||
| @@ -524,15 +543,15 @@ static int lfs_commit_move(lfs_t *lfs, struct lfs_commit *commit, | |||||||
|  |  | ||||||
| static int lfs_commit_attr(lfs_t *lfs, struct lfs_commit *commit, | static int lfs_commit_attr(lfs_t *lfs, struct lfs_commit *commit, | ||||||
|         uint32_t tag, const void *buffer) { |         uint32_t tag, const void *buffer) { | ||||||
|     if (lfs_tag_type(tag) == LFS_FROM_ATTRS) { |     if (lfs_tag_subtype(tag) == LFS_FROM_MOVE) { | ||||||
|         // special case for custom attributes |  | ||||||
|         return lfs_commit_attrs(lfs, commit, |  | ||||||
|                 lfs_tag_id(tag), buffer); |  | ||||||
|     } else if (lfs_tag_type(tag) == LFS_FROM_MOVE) { |  | ||||||
|         // special case for moves |         // special case for moves | ||||||
|         return lfs_commit_move(lfs, commit, |         return lfs_commit_move(lfs, commit, | ||||||
|                 lfs_tag_size(tag), lfs_tag_id(tag), |                 lfs_tag_size(tag), lfs_tag_id(tag), | ||||||
|                 buffer, NULL); |                 buffer, NULL); | ||||||
|  |     } else if (lfs_tag_subtype(tag) == LFS_FROM_ATTRS) { | ||||||
|  |         // special case for custom attributes | ||||||
|  |         return lfs_commit_attrs(lfs, commit, | ||||||
|  |                 lfs_tag_id(tag), buffer); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // check if we fit |     // check if we fit | ||||||
| @@ -628,7 +647,8 @@ static int lfs_commit_move(lfs_t *lfs, struct lfs_commit *commit, | |||||||
|             tag |= 0x80000000; |             tag |= 0x80000000; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (lfs_tag_type(tag) == LFS_TYPE_DELETE && lfs_tag_id(tag) <= fromid) { |         if (lfs_tag_type(tag) == LFS_TYPE_DELETE && | ||||||
|  |                 lfs_tag_id(tag) <= fromid) { | ||||||
|             // something was deleted, we need to move around it |             // something was deleted, we need to move around it | ||||||
|             fromid += 1; |             fromid += 1; | ||||||
|         } else if (lfs_tag_id(tag) != fromid) { |         } else if (lfs_tag_id(tag) != fromid) { | ||||||
| @@ -667,8 +687,8 @@ static int lfs_commit_globals(lfs_t *lfs, struct lfs_commit *commit, | |||||||
|  |  | ||||||
|     lfs_global_xor(locals, &lfs->locals); |     lfs_global_xor(locals, &lfs->locals); | ||||||
|     int err = lfs_commit_attr(lfs, commit, |     int err = lfs_commit_attr(lfs, commit, | ||||||
|             LFS_MKTAG(LFS_TYPE_GLOBALS + locals->s.deorphaned, |             LFS_MKTAG(LFS_TYPE_GLOBALS + locals->s.deorphaned, 0x3ff, 10), | ||||||
|                 0x3ff, sizeof(lfs_global_t)), locals); |             locals); | ||||||
|     lfs_global_xor(locals, &lfs->locals); |     lfs_global_xor(locals, &lfs->locals); | ||||||
|     return err; |     return err; | ||||||
| } | } | ||||||
| @@ -726,7 +746,7 @@ static int lfs_commit_crc(lfs_t *lfs, struct lfs_commit *commit) { | |||||||
|  |  | ||||||
| // internal dir operations | // internal dir operations | ||||||
| static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) { | static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) { | ||||||
|     // allocate pair of dir blocks (backwards, so we write to block 1 first) |     // allocate pair of dir blocks (backwards, so we write block 1 first) | ||||||
|     for (int i = 0; i < 2; i++) { |     for (int i = 0; i < 2; i++) { | ||||||
|         int err = lfs_alloc(lfs, &dir->pair[(i+1)%2]); |         int err = lfs_alloc(lfs, &dir->pair[(i+1)%2]); | ||||||
|         if (err) { |         if (err) { | ||||||
| @@ -756,10 +776,9 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) { | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int32_t lfs_dir_find(lfs_t *lfs, | static int32_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||||
|         lfs_mdir_t *dir, const lfs_block_t pair[2], |         lfs_mdir_t *dir, const lfs_block_t pair[2], | ||||||
|         uint32_t findmask, uint32_t findtag, |         uint32_t findmask, uint32_t findtag, const void *findbuffer) { | ||||||
|         const void *findbuffer) { |  | ||||||
|     dir->pair[0] = pair[0]; |     dir->pair[0] = pair[0]; | ||||||
|     dir->pair[1] = pair[1]; |     dir->pair[1] = pair[1]; | ||||||
|     int32_t foundtag = LFS_ERR_NOENT; |     int32_t foundtag = LFS_ERR_NOENT; | ||||||
| @@ -887,7 +906,7 @@ static int32_t lfs_dir_find(lfs_t *lfs, | |||||||
|                 } else if (lfs_tag_subtype(tag) == LFS_TYPE_GLOBALS) { |                 } else if (lfs_tag_subtype(tag) == LFS_TYPE_GLOBALS) { | ||||||
|                     templocals.s.deorphaned = (lfs_tag_type(tag) & 1); |                     templocals.s.deorphaned = (lfs_tag_type(tag) & 1); | ||||||
|                     err = lfs_bd_read(lfs, dir->pair[0], off+sizeof(tag), |                     err = lfs_bd_read(lfs, dir->pair[0], off+sizeof(tag), | ||||||
|                             &templocals, sizeof(templocals)); |                             &templocals, 10); | ||||||
|                     if (err) { |                     if (err) { | ||||||
|                         if (err == LFS_ERR_CORRUPT) { |                         if (err == LFS_ERR_CORRUPT) { | ||||||
|                             dir->erased = false; |                             dir->erased = false; | ||||||
| @@ -906,7 +925,7 @@ static int32_t lfs_dir_find(lfs_t *lfs, | |||||||
|                     } |                     } | ||||||
|                 } else if ((tag & findmask) == (findtag & findmask)) { |                 } else if ((tag & findmask) == (findtag & findmask)) { | ||||||
|                     int res = lfs_bd_cmp(lfs, dir->pair[0], off+sizeof(tag), |                     int res = lfs_bd_cmp(lfs, dir->pair[0], off+sizeof(tag), | ||||||
|                             findbuffer, lfs_tag_size(tag)); |                             findbuffer, lfs_tag_size(findtag)); | ||||||
|                     if (res < 0) { |                     if (res < 0) { | ||||||
|                         if (res == LFS_ERR_CORRUPT) { |                         if (res == LFS_ERR_CORRUPT) { | ||||||
|                             dir->erased = false; |                             dir->erased = false; | ||||||
| @@ -953,7 +972,8 @@ static int32_t lfs_dir_find(lfs_t *lfs, | |||||||
|  |  | ||||||
| static int lfs_dir_fetch(lfs_t *lfs, | static int lfs_dir_fetch(lfs_t *lfs, | ||||||
|         lfs_mdir_t *dir, const lfs_block_t pair[2]) { |         lfs_mdir_t *dir, const lfs_block_t pair[2]) { | ||||||
|     int32_t res = lfs_dir_find(lfs, dir, pair, 0xffffffff, 0xffffffff, NULL); |     int32_t res = lfs_dir_fetchmatch(lfs, dir, pair, | ||||||
|  |             0xffffffff, 0xffffffff, NULL); | ||||||
|     if (res < 0 && res != LFS_ERR_NOENT) { |     if (res < 0 && res != LFS_ERR_NOENT) { | ||||||
|         return res; |         return res; | ||||||
|     } |     } | ||||||
| @@ -961,6 +981,23 @@ static int lfs_dir_fetch(lfs_t *lfs, | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int32_t lfs_dir_find(lfs_t *lfs, | ||||||
|  |         lfs_mdir_t *dir, const lfs_block_t pair[2], bool fs, | ||||||
|  |         uint32_t findmask, uint32_t findtag, const void *findbuffer) { | ||||||
|  |     dir->split = true; | ||||||
|  |     dir->tail[0] = pair[0]; | ||||||
|  |     dir->tail[1] = pair[1]; | ||||||
|  |     while ((dir->split || fs) && !lfs_pair_isnull(dir->tail)) { | ||||||
|  |         int32_t tag = lfs_dir_fetchmatch(lfs, dir, dir->tail, | ||||||
|  |                 findmask, findtag, findbuffer); | ||||||
|  |         if (tag != LFS_ERR_NOENT) { | ||||||
|  |             return tag; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return LFS_ERR_NOENT; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int32_t lfs_dir_get(lfs_t *lfs, lfs_mdir_t *dir, | static int32_t lfs_dir_get(lfs_t *lfs, lfs_mdir_t *dir, | ||||||
|         uint32_t getmask, uint32_t gettag, void *buffer) { |         uint32_t getmask, uint32_t gettag, void *buffer) { | ||||||
|     int32_t getdiff = 0; |     int32_t getdiff = 0; | ||||||
| @@ -979,6 +1016,8 @@ static int lfs_dir_compact(lfs_t *lfs, | |||||||
|         lfs_mdir_t *source, uint16_t begin, uint16_t end) { |         lfs_mdir_t *source, uint16_t begin, uint16_t end) { | ||||||
|     // save some state in case block is bad |     // save some state in case block is bad | ||||||
|     const lfs_block_t oldpair[2] = {dir->pair[1], dir->pair[0]}; |     const lfs_block_t oldpair[2] = {dir->pair[1], dir->pair[0]}; | ||||||
|  |     int16_t ack; | ||||||
|  |     bool expanding = false; | ||||||
|     bool relocated = false; |     bool relocated = false; | ||||||
|  |  | ||||||
|     // There's nothing special about our global delta, so feed it back |     // There's nothing special about our global delta, so feed it back | ||||||
| @@ -989,9 +1028,25 @@ static int lfs_dir_compact(lfs_t *lfs, | |||||||
|     // increment revision count |     // increment revision count | ||||||
|     dir->rev += 1; |     dir->rev += 1; | ||||||
|  |  | ||||||
|  |     if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0 && | ||||||
|  |             dir->rev % 16 == 0) { | ||||||
|  |         // we're writing too much to the superblock, should we expand? | ||||||
|  |         lfs_ssize_t res = lfs_fs_size(lfs); | ||||||
|  |         if (res < 0) { | ||||||
|  |             return res; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // do we have enough space to expand? | ||||||
|  |         if (res < lfs->cfg->block_count/2) { | ||||||
|  |             expanding = (lfs_pair_cmp(dir->pair, lfs->root) != 0); | ||||||
|  |             ack = 0; | ||||||
|  |             goto split; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     while (true) { |     while (true) { | ||||||
|         // last complete id |         // last complete id | ||||||
|         int16_t ack = -1; |         ack = -1; | ||||||
|         dir->count = end - begin; |         dir->count = end - begin; | ||||||
|  |  | ||||||
|         if (true) { |         if (true) { | ||||||
| @@ -1111,7 +1166,7 @@ split: | |||||||
|         tail.tail[0] = dir->tail[0]; |         tail.tail[0] = dir->tail[0]; | ||||||
|         tail.tail[1] = dir->tail[1]; |         tail.tail[1] = dir->tail[1]; | ||||||
|  |  | ||||||
|         err = lfs_dir_compact(lfs, &tail, attrs, dir, ack+1, end); |         err = lfs_dir_compact(lfs, &tail, attrs, dir, ack+1-expanding, end); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
| @@ -1390,27 +1445,12 @@ nextname: | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // find entry matching name |         // find entry matching name | ||||||
|         while (true) { |         tag = lfs_dir_find(lfs, dir, pair, false, 0x7c000fff, | ||||||
|             tag = lfs_dir_find(lfs, dir, pair, 0x7c000fff, |  | ||||||
|                 LFS_MKTAG(LFS_TYPE_NAME, 0, namelen), name); |                 LFS_MKTAG(LFS_TYPE_NAME, 0, namelen), name); | ||||||
|             if (tag < 0 && tag != LFS_ERR_NOENT) { |         if (tag < 0) { | ||||||
|             return tag; |             return tag; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|             if (tag != LFS_ERR_NOENT) { |  | ||||||
|                 // found it |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (!dir->split) { |  | ||||||
|                 // couldn't find it |  | ||||||
|                 return LFS_ERR_NOENT; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             pair[0] = dir->tail[0]; |  | ||||||
|             pair[1] = dir->tail[1]; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // to next name |         // to next name | ||||||
|         name += namelen; |         name += namelen; | ||||||
|     } |     } | ||||||
| @@ -2721,24 +2761,6 @@ int lfs_setattr(lfs_t *lfs, const char *path, | |||||||
|  |  | ||||||
|  |  | ||||||
| /// Filesystem operations /// | /// Filesystem operations /// | ||||||
| static inline void lfs_superblockfromle32(lfs_superblock_t *superblock) { |  | ||||||
|     superblock->version     = lfs_fromle32(superblock->version); |  | ||||||
|     superblock->block_size  = lfs_fromle32(superblock->block_size); |  | ||||||
|     superblock->block_count = lfs_fromle32(superblock->block_count); |  | ||||||
|     superblock->inline_max  = lfs_fromle32(superblock->inline_max); |  | ||||||
|     superblock->attr_max    = lfs_fromle32(superblock->attr_max); |  | ||||||
|     superblock->name_max    = lfs_fromle32(superblock->name_max); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline void lfs_superblocktole32(lfs_superblock_t *superblock) { |  | ||||||
|     superblock->version     = lfs_tole32(superblock->version); |  | ||||||
|     superblock->block_size  = lfs_tole32(superblock->block_size); |  | ||||||
|     superblock->block_count = lfs_tole32(superblock->block_count); |  | ||||||
|     superblock->inline_max  = lfs_tole32(superblock->inline_max); |  | ||||||
|     superblock->attr_max    = lfs_tole32(superblock->attr_max); |  | ||||||
|     superblock->name_max    = lfs_tole32(superblock->name_max); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { | static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { | ||||||
|     lfs->cfg = cfg; |     lfs->cfg = cfg; | ||||||
|     int err = 0; |     int err = 0; | ||||||
| @@ -2859,30 +2881,13 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|     lfs->free.i = 0; |     lfs->free.i = 0; | ||||||
|     lfs_alloc_ack(lfs); |     lfs_alloc_ack(lfs); | ||||||
|  |  | ||||||
|     // create superblock dir |     // create root dir | ||||||
|     lfs_mdir_t dir; |  | ||||||
|     err = lfs_dir_alloc(lfs, &dir); |  | ||||||
|     if (err) { |  | ||||||
|         goto cleanup; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // write root directory |  | ||||||
|     lfs_mdir_t root; |     lfs_mdir_t root; | ||||||
|     err = lfs_dir_alloc(lfs, &root); |     err = lfs_dir_alloc(lfs, &root); | ||||||
|     if (err) { |     if (err) { | ||||||
|         goto cleanup; |         goto cleanup; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     err = lfs_dir_commit(lfs, &root, NULL); |  | ||||||
|     if (err) { |  | ||||||
|         goto cleanup; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs->root[0] = root.pair[0]; |  | ||||||
|     lfs->root[1] = root.pair[1]; |  | ||||||
|     dir.tail[0] = lfs->root[0]; |  | ||||||
|     dir.tail[1] = lfs->root[1]; |  | ||||||
|  |  | ||||||
|     // write one superblock |     // write one superblock | ||||||
|     lfs_superblock_t superblock = { |     lfs_superblock_t superblock = { | ||||||
|         .magic = {"littlefs"}, |         .magic = {"littlefs"}, | ||||||
| @@ -2895,20 +2900,17 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|         .inline_max  = lfs->inline_max, |         .inline_max  = lfs->inline_max, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     lfs_superblocktole32(&superblock); |     lfs_superblock_tole32(&superblock); | ||||||
|     lfs_pair_tole32(lfs->root); |     err = lfs_dir_commit(lfs, &root, | ||||||
|     err = lfs_dir_commit(lfs, &dir, |  | ||||||
|             LFS_MKATTR(LFS_TYPE_SUPERBLOCK, 0, &superblock, sizeof(superblock), |             LFS_MKATTR(LFS_TYPE_SUPERBLOCK, 0, &superblock, sizeof(superblock), | ||||||
|             LFS_MKATTR(LFS_TYPE_DIRSTRUCT, 0, lfs->root, sizeof(lfs->root), |             LFS_MKATTR(LFS_TYPE_ROOT, 1, NULL, 0, | ||||||
|             NULL))); |             NULL))); | ||||||
|     lfs_pair_fromle32(lfs->root); |  | ||||||
|     lfs_superblockfromle32(&superblock); |  | ||||||
|     if (err) { |     if (err) { | ||||||
|         goto cleanup; |         goto cleanup; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // sanity check that fetch works |     // sanity check that fetch works | ||||||
|     err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); |     err = lfs_dir_fetch(lfs, &root, (const lfs_block_t[2]){0, 1}); | ||||||
|     if (err) { |     if (err) { | ||||||
|         goto cleanup; |         goto cleanup; | ||||||
|     } |     } | ||||||
| @@ -2931,27 +2933,34 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|     lfs_alloc_ack(lfs); |     lfs_alloc_ack(lfs); | ||||||
|  |  | ||||||
|     // load superblock |     // load superblock | ||||||
|     lfs_mdir_t superdir; |     lfs_mdir_t root; | ||||||
|     err = lfs_dir_fetch(lfs, &superdir, (const lfs_block_t[2]){0, 1}); |     err = lfs_dir_fetch(lfs, &root, (const lfs_block_t[2]){0, 1}); | ||||||
|     if (err) { |     if (err) { | ||||||
|         goto cleanup; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     lfs_superblock_t superblock; |     lfs_superblock_t superblock; | ||||||
|     int32_t res = lfs_dir_get(lfs, &superdir, 0x7ffff000, |     int32_t res = lfs_dir_get(lfs, &root, 0x7fc00000, | ||||||
|             LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, sizeof(superblock)), |             LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, sizeof(superblock)), | ||||||
|             &superblock); |             &superblock); | ||||||
|     if (res < 0) { |     if (res < 0) { | ||||||
|         err = res; |         err = res; | ||||||
|         goto cleanup; |         goto cleanup; | ||||||
|     } |     } | ||||||
|     lfs_superblockfromle32(&superblock); |     lfs_superblock_fromle32(&superblock); | ||||||
|  |  | ||||||
|     if (memcmp(superblock.magic, "littlefs", 8) != 0) { |     // find root | ||||||
|         LFS_ERROR("Invalid superblock \"%.8s\"", superblock.magic); |     int32_t tag = lfs_dir_find(lfs, | ||||||
|         return LFS_ERR_INVAL; |             &root, (const lfs_block_t[2]){0, 1}, false, 0x7fc00000, | ||||||
|  |             LFS_MKTAG(LFS_TYPE_ROOT, 0, 0), NULL); | ||||||
|  |     if (tag < 0) { | ||||||
|  |         return tag; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     lfs->root[0] = root.pair[0]; | ||||||
|  |     lfs->root[1] = root.pair[1]; | ||||||
|  |  | ||||||
|  |     // check version | ||||||
|     uint16_t major_version = (0xffff & (superblock.version >> 16)); |     uint16_t major_version = (0xffff & (superblock.version >> 16)); | ||||||
|     uint16_t minor_version = (0xffff & (superblock.version >>  0)); |     uint16_t minor_version = (0xffff & (superblock.version >>  0)); | ||||||
|     if ((major_version != LFS_DISK_VERSION_MAJOR || |     if ((major_version != LFS_DISK_VERSION_MAJOR || | ||||||
| @@ -2962,15 +2971,6 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|         goto cleanup; |         goto cleanup; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     res = lfs_dir_get(lfs, &superdir, 0x7ffff000, |  | ||||||
|             LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, sizeof(lfs->root)), |  | ||||||
|             &lfs->root); |  | ||||||
|     if (res < 0) { |  | ||||||
|         err = res; |  | ||||||
|         goto cleanup; |  | ||||||
|     } |  | ||||||
|     lfs_pair_fromle32(lfs->root); |  | ||||||
|  |  | ||||||
|     // check superblock configuration |     // check superblock configuration | ||||||
|     if (superblock.attr_max) { |     if (superblock.attr_max) { | ||||||
|         if (superblock.attr_max > lfs->attr_max) { |         if (superblock.attr_max > lfs->attr_max) { | ||||||
| @@ -3145,17 +3145,12 @@ static int32_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], | |||||||
|     lfs_block_t child[2] = {pair[0], pair[1]}; |     lfs_block_t child[2] = {pair[0], pair[1]}; | ||||||
|     lfs_pair_tole32(child); |     lfs_pair_tole32(child); | ||||||
|     for (int i = 0; i < 2; i++) { |     for (int i = 0; i < 2; i++) { | ||||||
|         // iterate over all directory directory entries |         int32_t tag = lfs_dir_find(lfs, parent, | ||||||
|         parent->tail[0] = 0; |                 (const lfs_block_t[2]){0, 1}, true, 0x7fc00fff, | ||||||
|         parent->tail[1] = 1; |                 LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, sizeof(child)), child); | ||||||
|         while (!lfs_pair_isnull(parent->tail)) { |  | ||||||
|             int32_t tag = lfs_dir_find(lfs, parent, parent->tail, 0x7fc00fff, |  | ||||||
|                     LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, sizeof(child)), |  | ||||||
|                     child); |  | ||||||
|         if (tag != LFS_ERR_NOENT) { |         if (tag != LFS_ERR_NOENT) { | ||||||
|             return tag; |             return tag; | ||||||
|         } |         } | ||||||
|         } |  | ||||||
|  |  | ||||||
|         lfs_pair_swap(child); |         lfs_pair_swap(child); | ||||||
|     } |     } | ||||||
| @@ -3190,6 +3185,7 @@ static int lfs_fs_relocate(lfs_t *lfs, | |||||||
|  |  | ||||||
|     if (tag != LFS_ERR_NOENT) { |     if (tag != LFS_ERR_NOENT) { | ||||||
|         // update disk, this creates a desync |         // update disk, this creates a desync | ||||||
|  |         lfs_global_deorphaned(lfs, false); | ||||||
|         lfs_pair_tole32(newpair); |         lfs_pair_tole32(newpair); | ||||||
|         int err = lfs_dir_commit(lfs, &parent, |         int err = lfs_dir_commit(lfs, &parent, | ||||||
|                 &(lfs_mattr_t){.tag=tag, .buffer=newpair}); |                 &(lfs_mattr_t){.tag=tag, .buffer=newpair}); | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -93,7 +93,8 @@ enum lfs_type { | |||||||
|  |  | ||||||
|     // internally used types |     // internally used types | ||||||
|     LFS_TYPE_USER           = 0x100, |     LFS_TYPE_USER           = 0x100, | ||||||
|     LFS_TYPE_SUPERBLOCK     = 0x010, |     LFS_TYPE_SUPERBLOCK     = 0x011, | ||||||
|  |     LFS_TYPE_ROOT           = 0x012, | ||||||
|     LFS_TYPE_NAME           = 0x000, |     LFS_TYPE_NAME           = 0x000, | ||||||
|     LFS_TYPE_DELETE         = 0x030, |     LFS_TYPE_DELETE         = 0x030, | ||||||
|     LFS_TYPE_STRUCT         = 0x040, |     LFS_TYPE_STRUCT         = 0x040, | ||||||
| @@ -110,8 +111,9 @@ enum lfs_type { | |||||||
|     // internal chip sources |     // internal chip sources | ||||||
|     LFS_FROM_REGION         = 0x000, |     LFS_FROM_REGION         = 0x000, | ||||||
|     LFS_FROM_DISK           = 0x200, |     LFS_FROM_DISK           = 0x200, | ||||||
|     LFS_FROM_MOVE           = 0x021, |     LFS_FROM_MOVE           = 0x050, | ||||||
|     LFS_FROM_ATTRS          = 0x022, |     LFS_FROM_ATTRS          = 0x060, | ||||||
|  |     LFS_FROM_SUPERBLOCK     = 0x070, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // File open flags | // File open flags | ||||||
|   | |||||||
| @@ -206,7 +206,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     for (lfs_size_t i = 0; | ||||||
|             i < (cfg.block_count-6)*(cfg.block_size-8); |             i < (cfg.block_count-4)*(cfg.block_size-8); | ||||||
|             i += size) { |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
| @@ -286,7 +286,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     for (lfs_size_t i = 0; | ||||||
|             i < (cfg.block_count-6)*(cfg.block_size-8); |             i < (cfg.block_count-4)*(cfg.block_size-8); | ||||||
|             i += size) { |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
| @@ -320,7 +320,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     for (lfs_size_t i = 0; | ||||||
|             i < ((cfg.block_count-4)/2)*(cfg.block_size-8); |             i < ((cfg.block_count-2)/2)*(cfg.block_size-8); | ||||||
|             i += size) { |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
| @@ -331,7 +331,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     for (lfs_size_t i = 0; | ||||||
|             i < ((cfg.block_count-4+1)/2)*(cfg.block_size-8); |             i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); | ||||||
|             i += size) { |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
| @@ -349,7 +349,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     for (lfs_size_t i = 0; | ||||||
|             i < ((cfg.block_count-4)/2)*(cfg.block_size-8); |             i < ((cfg.block_count-2)/2)*(cfg.block_size-8); | ||||||
|             i += size) { |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
| @@ -363,7 +363,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     for (lfs_size_t i = 0; | ||||||
|             i < ((cfg.block_count-4+1)/2)*(cfg.block_size-8); |             i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); | ||||||
|             i += size) { |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
| @@ -383,7 +383,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     for (lfs_size_t i = 0; | ||||||
|             i < ((cfg.block_count-4)/2)*(cfg.block_size-8); |             i < ((cfg.block_count-2)/2)*(cfg.block_size-8); | ||||||
|             i += size) { |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
| @@ -394,7 +394,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     for (lfs_size_t i = 0; | ||||||
|             i < ((cfg.block_count-4+1)/2)*(cfg.block_size-8); |             i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); | ||||||
|             i += size) { |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
| @@ -412,7 +412,7 @@ tests/test.py << TEST | |||||||
|     size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs_size_t i = 0; |     for (lfs_size_t i = 0; | ||||||
|             i < ((cfg.block_count-4)/2 - 1)*(cfg.block_size-8); |             i < ((cfg.block_count-2)/2 - 1)*(cfg.block_size-8); | ||||||
|             i += size) { |             i += size) { | ||||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; |         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -9,14 +9,6 @@ tests/test.py << TEST | |||||||
|     lfs_format(&lfs, &cfg) => 0; |     lfs_format(&lfs, &cfg) => 0; | ||||||
| TEST | TEST | ||||||
|  |  | ||||||
| echo "--- Invalid superblocks ---" |  | ||||||
| ln -f -s /dev/zero blocks/0 |  | ||||||
| ln -f -s /dev/zero blocks/1 |  | ||||||
| tests/test.py << TEST |  | ||||||
|     lfs_format(&lfs, &cfg) => LFS_ERR_CORRUPT; |  | ||||||
| TEST |  | ||||||
| rm blocks/0 blocks/1 |  | ||||||
|  |  | ||||||
| echo "--- Basic mounting ---" | echo "--- Basic mounting ---" | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_format(&lfs, &cfg) => 0; |     lfs_format(&lfs, &cfg) => 0; | ||||||
| @@ -26,14 +18,34 @@ tests/test.py << TEST | |||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
|  |  | ||||||
| echo "--- Invalid mount ---" | echo "--- Invalid superblocks ---" | ||||||
|  | ln -f -s /dev/zero blocks/0 | ||||||
|  | ln -f -s /dev/zero blocks/1 | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_format(&lfs, &cfg) => 0; |     lfs_format(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||||
| TEST | TEST | ||||||
| rm -f blocks/0 blocks/1 | rm blocks/0 blocks/1 | ||||||
|  |  | ||||||
|  | echo "--- Invalid mount ---" | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; |     lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||||
| TEST | TEST | ||||||
|  |  | ||||||
|  | echo "--- 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 | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_mkdir(&lfs, "dummy") => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
| echo "--- Results ---" | echo "--- Results ---" | ||||||
| tests/stats.py | tests/stats.py | ||||||
|   | |||||||
| @@ -59,7 +59,7 @@ tests/test.py << TEST | |||||||
|     lfs_rename(&lfs, "b/hello", "c/hello") => 0; |     lfs_rename(&lfs, "b/hello", "c/hello") => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
| tests/corrupt.py blocks/{6,7} | tests/corrupt.py blocks/{4,5} | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_dir_open(&lfs, &dir[0], "b") => 0; |     lfs_dir_open(&lfs, &dir[0], "b") => 0; | ||||||
| @@ -86,8 +86,8 @@ tests/test.py << TEST | |||||||
|     lfs_rename(&lfs, "c/hello", "d/hello") => 0; |     lfs_rename(&lfs, "c/hello", "d/hello") => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
|  | tests/corrupt.py blocks/{6,7} | ||||||
| tests/corrupt.py blocks/{8,9} | tests/corrupt.py blocks/{8,9} | ||||||
| tests/corrupt.py blocks/{a,b} |  | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_dir_open(&lfs, &dir[0], "c") => 0; |     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||||
| @@ -166,7 +166,7 @@ tests/test.py << TEST | |||||||
|     lfs_rename(&lfs, "b/hi", "c/hi") => 0; |     lfs_rename(&lfs, "b/hi", "c/hi") => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
| tests/corrupt.py blocks/{6,7} | tests/corrupt.py blocks/{4,5} | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_dir_open(&lfs, &dir[0], "b") => 0; |     lfs_dir_open(&lfs, &dir[0], "b") => 0; | ||||||
| @@ -193,8 +193,8 @@ tests/test.py << TEST | |||||||
|     lfs_rename(&lfs, "c/hi", "d/hi") => 0; |     lfs_rename(&lfs, "c/hi", "d/hi") => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
|  | tests/corrupt.py blocks/{6,7} | ||||||
| tests/corrupt.py blocks/{8,9} | tests/corrupt.py blocks/{8,9} | ||||||
| tests/corrupt.py blocks/{a,b} |  | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_dir_open(&lfs, &dir[0], "c") => 0; |     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||||
|   | |||||||
| @@ -17,26 +17,26 @@ tests/test.py << TEST | |||||||
| TEST | TEST | ||||||
| # corrupt most recent commit, this should be the update to the previous | # corrupt most recent commit, this should be the update to the previous | ||||||
| # linked-list entry and should orphan the child | # linked-list entry and should orphan the child | ||||||
| tests/corrupt.py blocks/{8,9} | tests/corrupt.py blocks/{6,7} | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|     lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; |     lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; | ||||||
|     lfs_ssize_t before = lfs_fs_size(&lfs); |     lfs_ssize_t before = lfs_fs_size(&lfs); | ||||||
|     before => 10; |     before => 8; | ||||||
|  |  | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|     lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; |     lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; | ||||||
|     lfs_ssize_t orphaned = lfs_fs_size(&lfs); |     lfs_ssize_t orphaned = lfs_fs_size(&lfs); | ||||||
|     orphaned => 10; |     orphaned => 8; | ||||||
|  |  | ||||||
|     lfs_mkdir(&lfs, "parent/otherchild") => 0; |     lfs_mkdir(&lfs, "parent/otherchild") => 0; | ||||||
|  |  | ||||||
|     lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; |     lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; | ||||||
|     lfs_ssize_t deorphaned = lfs_fs_size(&lfs); |     lfs_ssize_t deorphaned = lfs_fs_size(&lfs); | ||||||
|     deorphaned => 10; |     deorphaned => 8; | ||||||
|  |  | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user