mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	WIP initial commit of branch idea to repair non-DAG trees
This commit is contained in:
		
							
								
								
									
										518
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										518
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -279,6 +279,10 @@ static inline bool lfs_tag_isdelete(lfs_tag_t tag) { | |||||||
|     return ((int32_t)(tag << 22) >> 22) == -1; |     return ((int32_t)(tag << 22) >> 22) == -1; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static inline bool lfs_tag_isattr(lfs_tag_t tag) { | ||||||
|  |     return !(tag & 0x40000000); | ||||||
|  | } | ||||||
|  |  | ||||||
| static inline uint16_t lfs_tag_type1(lfs_tag_t tag) { | static inline uint16_t lfs_tag_type1(lfs_tag_t tag) { | ||||||
|     return (tag & 0x70000000) >> 20; |     return (tag & 0x70000000) >> 20; | ||||||
| } | } | ||||||
| @@ -411,8 +415,9 @@ static void lfs_fs_prepmove(lfs_t *lfs, | |||||||
| static int lfs_fs_pred(lfs_t *lfs, const lfs_block_t dir[2], | static int lfs_fs_pred(lfs_t *lfs, const lfs_block_t dir[2], | ||||||
|         lfs_mdir_t *pdir); |         lfs_mdir_t *pdir); | ||||||
| static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t dir[2], | static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t dir[2], | ||||||
|         lfs_mdir_t *parent); |         lfs_mdir_t *parent, bool mustbesync); | ||||||
| static int lfs_fs_relocate(lfs_t *lfs, | static int lfs_fs_relocate(lfs_t *lfs, | ||||||
|  |         lfs_stag_t ptag, lfs_mdir_t *parent, | ||||||
|         const lfs_block_t oldpair[2], lfs_block_t newpair[2]); |         const lfs_block_t oldpair[2], lfs_block_t newpair[2]); | ||||||
| int lfs_fs_traverseraw(lfs_t *lfs, | int lfs_fs_traverseraw(lfs_t *lfs, | ||||||
|         int (*cb)(void *data, lfs_block_t block), void *data, |         int (*cb)(void *data, lfs_block_t block), void *data, | ||||||
| @@ -892,11 +897,11 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | |||||||
|                 if (tag == (LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) | |                 if (tag == (LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) | | ||||||
|                         (LFS_MKTAG(0, 0x3ff, 0) & tempbesttag))) { |                         (LFS_MKTAG(0, 0x3ff, 0) & tempbesttag))) { | ||||||
|                     tempbesttag |= 0x80000000; |                     tempbesttag |= 0x80000000; | ||||||
|                 } else if (tempbesttag != -1 && |                 } else if (tempbesttag != -1 && lfs_tag_isattr(tempbesttag) && | ||||||
|                         lfs_tag_id(tag) <= lfs_tag_id(tempbesttag)) { |                         lfs_tag_id(tag) <= lfs_tag_id(tempbesttag)) { | ||||||
|                     tempbesttag += LFS_MKTAG(0, lfs_tag_splice(tag), 0); |                     tempbesttag += LFS_MKTAG(0, lfs_tag_splice(tag), 0); | ||||||
|                 } |                 } | ||||||
|             } else if (lfs_tag_type1(tag) == LFS_TYPE_TAIL) { |             } else if ((lfs_tag_type3(tag) & 0x7fe) == LFS_TYPE_TAIL) { | ||||||
|                 tempsplit = (lfs_tag_chunk(tag) & 1); |                 tempsplit = (lfs_tag_chunk(tag) & 1); | ||||||
|  |  | ||||||
|                 err = lfs_bd_read(lfs, |                 err = lfs_bd_read(lfs, | ||||||
| @@ -945,7 +950,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | |||||||
|             if (lfs_gstate_hasmovehere(&lfs->gdisk, dir->pair)) { |             if (lfs_gstate_hasmovehere(&lfs->gdisk, dir->pair)) { | ||||||
|                 if (lfs_tag_id(lfs->gdisk.tag) == lfs_tag_id(besttag)) { |                 if (lfs_tag_id(lfs->gdisk.tag) == lfs_tag_id(besttag)) { | ||||||
|                     besttag |= 0x80000000; |                     besttag |= 0x80000000; | ||||||
|                 } else if (besttag != -1 && |                 } else if (besttag != -1 && lfs_tag_isattr(besttag) && | ||||||
|                         lfs_tag_id(lfs->gdisk.tag) < lfs_tag_id(besttag)) { |                         lfs_tag_id(lfs->gdisk.tag) < lfs_tag_id(besttag)) { | ||||||
|                     besttag -= LFS_MKTAG(0, 1, 0); |                     besttag -= LFS_MKTAG(0, 1, 0); | ||||||
|                 } |                 } | ||||||
| @@ -1075,8 +1080,7 @@ static lfs_stag_t lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir, | |||||||
|  |  | ||||||
|     // default to root dir |     // default to root dir | ||||||
|     lfs_stag_t tag = LFS_MKTAG(LFS_TYPE_DIR, 0x3ff, 0); |     lfs_stag_t tag = LFS_MKTAG(LFS_TYPE_DIR, 0x3ff, 0); | ||||||
|     dir->tail[0] = lfs->root[0]; |     lfs_block_t npair[2] = {lfs->root[0], lfs->root[1]}; | ||||||
|     dir->tail[1] = lfs->root[1]; |  | ||||||
|  |  | ||||||
|     while (true) { |     while (true) { | ||||||
| nextname: | nextname: | ||||||
| @@ -1131,16 +1135,17 @@ nextname: | |||||||
|         // grab the entry data |         // grab the entry data | ||||||
|         if (lfs_tag_id(tag) != 0x3ff) { |         if (lfs_tag_id(tag) != 0x3ff) { | ||||||
|             lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0x3ff, 0), |             lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0x3ff, 0), | ||||||
|                     LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), dir->tail); |                     LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), npair); | ||||||
|             if (res < 0) { |             if (res < 0) { | ||||||
|                 return res; |                 return res; | ||||||
|             } |             } | ||||||
|             lfs_pair_fromle32(dir->tail); |             lfs_pair_fromle32(npair); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // find entry matching name |         // find entry matching name | ||||||
|         while (true) { |         while (true) { | ||||||
|             tag = lfs_dir_fetchmatch(lfs, dir, dir->tail, |             // TODO also find tail? | ||||||
|  |             tag = lfs_dir_fetchmatch(lfs, dir, npair, | ||||||
|                     LFS_MKTAG(0x780, 0, 0), |                     LFS_MKTAG(0x780, 0, 0), | ||||||
|                     LFS_MKTAG(LFS_TYPE_NAME, 0, namelen), |                     LFS_MKTAG(LFS_TYPE_NAME, 0, namelen), | ||||||
|                      // are we last name? |                      // are we last name? | ||||||
| @@ -1155,7 +1160,23 @@ nextname: | |||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             // TODO compact npair -> dir->tail? | ||||||
|  |             lfs_stag_t ttag = 0; | ||||||
|  |             npair[0] = dir->tail[0]; | ||||||
|  |             npair[1] = dir->tail[1]; | ||||||
|             if (!dir->split) { |             if (!dir->split) { | ||||||
|  |                 ttag = lfs_dir_get(lfs, dir, | ||||||
|  |                         LFS_MKTAG(0x7ff, 0, 0), | ||||||
|  |                         LFS_MKTAG(LFS_TYPE_BRANCH, 0, sizeof(npair)), | ||||||
|  |                         npair); | ||||||
|  |                 if (ttag < 0 && ttag != LFS_ERR_NOENT) { | ||||||
|  |                     return ttag; | ||||||
|  |                 } | ||||||
|  |                 lfs_pair_fromle32(npair); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // ttag may be NOENT or may be marked null explicitly | ||||||
|  |             if (ttag == LFS_ERR_NOENT || lfs_pair_isnull(npair)) { | ||||||
|                 return LFS_ERR_NOENT; |                 return LFS_ERR_NOENT; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -1374,10 +1395,22 @@ static int lfs_dir_drop(lfs_t *lfs, lfs_mdir_t *dir, lfs_mdir_t *tail) { | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // steal tail |     // steal tail's branch | ||||||
|  |     lfs_block_t tbpair[2]; | ||||||
|  |     lfs_stag_t tbtag = lfs_dir_get(lfs, tail, | ||||||
|  |             LFS_MKTAG(0x7ff, 0, 0), | ||||||
|  |             LFS_MKTAG(LFS_TYPE_BRANCH, 0, 8), tbpair); | ||||||
|  |     if (tbtag < 0 && tbtag != LFS_ERR_NOENT) { | ||||||
|  |         return tbtag; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // steal tail's tail | ||||||
|     lfs_pair_tole32(tail->tail); |     lfs_pair_tole32(tail->tail); | ||||||
|     err = lfs_dir_commit(lfs, dir, LFS_MKATTRS( |     err = lfs_dir_commit(lfs, dir, LFS_MKATTRS( | ||||||
|             {LFS_MKTAG(LFS_TYPE_TAIL + tail->split, 0x3ff, 8), tail->tail})); |             {LFS_MKTAG(LFS_TYPE_TAIL + tail->split, 0x3ff, 8), tail->tail}, | ||||||
|  |             {LFS_MKTAG_IF_ELSE(tbtag != LFS_ERR_NOENT, | ||||||
|  |                 LFS_TYPE_BRANCH, 0x3ff, sizeof(tbpair), | ||||||
|  |                 LFS_TYPE_BRANCH, 0x3ff, 0x3ff), tbpair})); | ||||||
|     lfs_pair_fromle32(tail->tail); |     lfs_pair_fromle32(tail->tail); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return err; |         return err; | ||||||
| @@ -1386,6 +1419,54 @@ static int lfs_dir_drop(lfs_t *lfs, lfs_mdir_t *dir, lfs_mdir_t *tail) { | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // TODO consolidate? | ||||||
|  | static int lfs_dir_dropbranch(lfs_t *lfs, | ||||||
|  |         lfs_mdir_t *dir, lfs_stag_t btag, lfs_mdir_t *branch) { | ||||||
|  |     // steal state | ||||||
|  |     int err = lfs_dir_getgstate(lfs, branch, &lfs->gdelta); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // steal branch's branch | ||||||
|  |     lfs_block_t bbpair[2]; | ||||||
|  |     lfs_stag_t bbtag = lfs_dir_get(lfs, branch, | ||||||
|  |             LFS_MKTAG(0x7ff, 0, 0), | ||||||
|  |             LFS_MKTAG(LFS_TYPE_BRANCH, 0, 8), bbpair); | ||||||
|  |     if (bbtag < 0 && bbtag != LFS_ERR_NOENT) { | ||||||
|  |         return bbtag; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // TODO need mlist? | ||||||
|  |     // TODO endianness? | ||||||
|  |     // mark fs as orphaned | ||||||
|  |     lfs_fs_preporphans(lfs, +1); | ||||||
|  |  | ||||||
|  |     // delete branch | ||||||
|  |     err = lfs_dir_commit(lfs, dir, LFS_MKATTRS( | ||||||
|  |             //{LFS_MKTAG(LFS_TYPE_TAIL + branch->split, 0x3ff, 8), branch->tail}, | ||||||
|  |             {LFS_MKTAG_IF_ELSE(bbtag != LFS_ERR_NOENT, | ||||||
|  |                 LFS_TYPE_BRANCH, 0x3ff, sizeof(bbpair), | ||||||
|  |                 LFS_TYPE_BRANCH, 0x3ff, 0x3ff), bbpair})); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // find and clean up orphan | ||||||
|  |     err = lfs_fs_pred(lfs, branch->pair, dir); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs_fs_preporphans(lfs, -1); | ||||||
|  |     err = lfs_dir_drop(lfs, dir, branch); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int lfs_dir_split(lfs_t *lfs, | static int lfs_dir_split(lfs_t *lfs, | ||||||
|         lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, |         lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, | ||||||
|         lfs_mdir_t *source, uint16_t split, uint16_t end) { |         lfs_mdir_t *source, uint16_t split, uint16_t end) { | ||||||
| @@ -1444,6 +1525,8 @@ static int lfs_dir_compact(lfs_t *lfs, | |||||||
|     const lfs_block_t oldpair[2] = {dir->pair[0], dir->pair[1]}; |     const lfs_block_t oldpair[2] = {dir->pair[0], dir->pair[1]}; | ||||||
|     bool relocated = false; |     bool relocated = false; | ||||||
|     bool tired = false; |     bool tired = false; | ||||||
|  |     lfs_stag_t ptag = LFS_ERR_NOENT; | ||||||
|  |     lfs_mdir_t parent; | ||||||
|  |  | ||||||
|     // should we split? |     // should we split? | ||||||
|     while (end - begin > 1) { |     while (end - begin > 1) { | ||||||
| @@ -1592,7 +1675,7 @@ static int lfs_dir_compact(lfs_t *lfs, | |||||||
|             if (!lfs_pair_isnull(dir->tail)) { |             if (!lfs_pair_isnull(dir->tail)) { | ||||||
|                 lfs_pair_tole32(dir->tail); |                 lfs_pair_tole32(dir->tail); | ||||||
|                 err = lfs_dir_commitattr(lfs, &commit, |                 err = lfs_dir_commitattr(lfs, &commit, | ||||||
|                         LFS_MKTAG(LFS_TYPE_TAIL + dir->split, 0x3ff, 8), |                         LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), | ||||||
|                         dir->tail); |                         dir->tail); | ||||||
|                 lfs_pair_fromle32(dir->tail); |                 lfs_pair_fromle32(dir->tail); | ||||||
|                 if (err) { |                 if (err) { | ||||||
| @@ -1601,6 +1684,37 @@ static int lfs_dir_compact(lfs_t *lfs, | |||||||
|                     } |                     } | ||||||
|                     return err; |                     return err; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  |                 // Also commit branch or turn branch+tail into explicit branch | ||||||
|  |                 // and tail. This helps handling of relocations. You may wonder | ||||||
|  |                 // why we even have the branch+tail type, and that is simply | ||||||
|  |                 // because of backwards compatibility. | ||||||
|  |                 // | ||||||
|  |                 // Note we don't need to convert to/from le32 because we commit | ||||||
|  |                 // it straight to disk | ||||||
|  |                 lfs_stag_t btag = 0; | ||||||
|  |                 lfs_block_t bpair[2] = {dir->tail[0], dir->tail[1]}; | ||||||
|  |                 if (!dir->split) { | ||||||
|  |                     btag = lfs_dir_get(lfs, source, | ||||||
|  |                             LFS_MKTAG(0x7ff, 0, 0), | ||||||
|  |                             LFS_MKTAG(LFS_TYPE_BRANCH, 0, sizeof(bpair)), | ||||||
|  |                             bpair); | ||||||
|  |                     if (btag < 0 && btag != LFS_ERR_NOENT) { | ||||||
|  |                         return btag; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (btag != LFS_ERR_NOENT) { | ||||||
|  |                     err = lfs_dir_commitattr(lfs, &commit, | ||||||
|  |                             LFS_MKTAG(LFS_TYPE_BRANCH, 0x3ff, 8), | ||||||
|  |                             bpair); | ||||||
|  |                     if (err) { | ||||||
|  |                         if (err == LFS_ERR_CORRUPT) { | ||||||
|  |                             goto relocate; | ||||||
|  |                         } | ||||||
|  |                         return err; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             // bring over gstate? |             // bring over gstate? | ||||||
| @@ -1645,6 +1759,7 @@ static int lfs_dir_compact(lfs_t *lfs, | |||||||
|             dir->count = end - begin; |             dir->count = end - begin; | ||||||
|             dir->off = commit.off; |             dir->off = commit.off; | ||||||
|             dir->etag = commit.ptag; |             dir->etag = commit.ptag; | ||||||
|  |             dir->split = false; // converted to branch | ||||||
|             // update gstate |             // update gstate | ||||||
|             lfs->gdelta = (lfs_gstate_t){0}; |             lfs->gdelta = (lfs_gstate_t){0}; | ||||||
|             if (!relocated) { |             if (!relocated) { | ||||||
| @@ -1663,15 +1778,85 @@ relocate: | |||||||
|  |  | ||||||
|         // can't relocate superblock, filesystem is now frozen |         // can't relocate superblock, filesystem is now frozen | ||||||
|         if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) { |         if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) { | ||||||
|             LFS_WARN("Superblock %"PRIx32" has become unwritable", dir->pair[1]); |             LFS_WARN("Superblock %"PRIx32" has become unwritable", | ||||||
|  |                     dir->pair[1]); | ||||||
|             return LFS_ERR_NOSPC; |             return LFS_ERR_NOSPC; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | #if 1 | ||||||
|  |         // find parent? | ||||||
|  |         ptag = lfs_fs_parent(lfs, oldpair, &parent, false); | ||||||
|  |         if (ptag < 0 && ptag != LFS_ERR_NOENT) { | ||||||
|  |             return ptag; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // allocate new pair | ||||||
|  |         int err = lfs_alloc(lfs, &dir->pair[1]); | ||||||
|  |         if (err) { | ||||||
|  |             if (err == LFS_ERR_NOSPC && tired) { | ||||||
|  |                 // fix pending move in this pair? t because of wear-leveling | ||||||
|  |                 relocated = false; | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Have parent? Didn't give up? This is when we need to reinsert | ||||||
|  |         // ourself in the threaded linked-list. This evenutally creates an | ||||||
|  |         // orphan, but we can clean that up. We need to reinsert to avoid | ||||||
|  |         // issues with cycles in non-DAG trees. | ||||||
|  |         // | ||||||
|  |         // Note if parent's tail == us we can, and must, clean ourselves up | ||||||
|  |         // without an orphan. | ||||||
|  |         if (ptag != LFS_ERR_NOENT && | ||||||
|  |                 lfs_pair_cmp(parent.tail, oldpair) != 0) { | ||||||
|  |             dir->tail[0] = parent.tail[0]; | ||||||
|  |             dir->tail[1] = parent.tail[1]; | ||||||
|  |             printf("tail? %x %x\n", dir->tail[0], dir->tail[1]); | ||||||
|  |         } | ||||||
|  | #elif 0 // TODO rm me | ||||||
|  |         // find parent? | ||||||
|  |         ptag = lfs_fs_parent(lfs, oldpair, &parent); | ||||||
|  |         if (ptag < 0 && ptag != LFS_ERR_NOENT) { | ||||||
|  |             return ptag; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // allocate new pair | ||||||
|  |         lfs_mdir_t temp; | ||||||
|  |         int err = lfs_dir_alloc(lfs, &temp); | ||||||
|  |         if (err) { | ||||||
|  |             if (err == LFS_ERR_NOSPC && tired) { | ||||||
|  |                 // fix pending move in this pair? t because of wear-leveling | ||||||
|  |                 relocated = false; | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         dir->pair[0] = temp.pair[0]; | ||||||
|  |         dir->pair[1] = temp.pair[1]; | ||||||
|  |         dir->rev = temp.rev; | ||||||
|  |  | ||||||
|  |         // Have parent? Didn't give up? This is when we need to reinsert | ||||||
|  |         // ourself in the threaded linked-list. This evenutally creates an | ||||||
|  |         // orphan, but we can clean that up. We need to reinsert to avoid | ||||||
|  |         // issues with cycles in non-DAG trees. | ||||||
|  |         // | ||||||
|  |         // Note if parent's tail == us we can, and must, clean ourselves up | ||||||
|  |         // without an orphan. | ||||||
|  |         if (ptag != LFS_ERR_NOENT && | ||||||
|  |                 lfs_pair_cmp(parent.tail, oldpair) != 0) { | ||||||
|  |             dir->tail[0] = parent.tail[0]; | ||||||
|  |             dir->tail[1] = parent.tail[1]; | ||||||
|  |             printf("tail? %x %x\n", dir->tail[0], dir->tail[1]); | ||||||
|  |         } | ||||||
|  | #else | ||||||
|         // relocate half of pair |         // relocate half of pair | ||||||
|         int err = lfs_alloc(lfs, &dir->pair[1]); |         int err = lfs_alloc(lfs, &dir->pair[1]); | ||||||
|         if (err && (err != LFS_ERR_NOSPC || !tired)) { |         if (err && (err != LFS_ERR_NOSPC || !tired)) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|         tired = false; |         tired = false; | ||||||
|         continue; |         continue; | ||||||
| @@ -1681,7 +1866,7 @@ relocate: | |||||||
|         // update references if we relocated |         // update references if we relocated | ||||||
|         LFS_DEBUG("Relocating %"PRIx32" %"PRIx32" -> %"PRIx32" %"PRIx32, |         LFS_DEBUG("Relocating %"PRIx32" %"PRIx32" -> %"PRIx32" %"PRIx32, | ||||||
|                 oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); |                 oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); | ||||||
|         int err = lfs_fs_relocate(lfs, oldpair, dir->pair); |         int err = lfs_fs_relocate(lfs, ptag, &parent, oldpair, dir->pair); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
| @@ -1720,7 +1905,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, | |||||||
|             LFS_ASSERT(dir->count > 0); |             LFS_ASSERT(dir->count > 0); | ||||||
|             dir->count -= 1; |             dir->count -= 1; | ||||||
|             hasdelete = true; |             hasdelete = true; | ||||||
|         } else if (lfs_tag_type1(attrs[i].tag) == LFS_TYPE_TAIL) { |         } else if ((lfs_tag_type3(attrs[i].tag) & 0x7fe) == LFS_TYPE_TAIL) { | ||||||
|             dir->tail[0] = ((lfs_block_t*)attrs[i].buffer)[0]; |             dir->tail[0] = ((lfs_block_t*)attrs[i].buffer)[0]; | ||||||
|             dir->tail[1] = ((lfs_block_t*)attrs[i].buffer)[1]; |             dir->tail[1] = ((lfs_block_t*)attrs[i].buffer)[1]; | ||||||
|             dir->split = (lfs_tag_chunk(attrs[i].tag) & 1); |             dir->split = (lfs_tag_chunk(attrs[i].tag) & 1); | ||||||
| @@ -1730,6 +1915,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, | |||||||
|  |  | ||||||
|     // should we actually drop the directory block? |     // should we actually drop the directory block? | ||||||
|     if (hasdelete && dir->count == 0) { |     if (hasdelete && dir->count == 0) { | ||||||
|  |         //printf("hit zero %x %x\n", dir->pair[0], dir->pair[1]); | ||||||
|         lfs_mdir_t pdir; |         lfs_mdir_t pdir; | ||||||
|         int err = lfs_fs_pred(lfs, dir->pair, &pdir); |         int err = lfs_fs_pred(lfs, dir->pair, &pdir); | ||||||
|         if (err && err != LFS_ERR_NOENT) { |         if (err && err != LFS_ERR_NOENT) { | ||||||
| @@ -1737,12 +1923,36 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, | |||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // TODO should this actually be handled in _lfs_remove_?? | ||||||
|  |         //printf("hmm1 %d %d\n", err, pdir.split); | ||||||
|         if (err != LFS_ERR_NOENT && pdir.split) { |         if (err != LFS_ERR_NOENT && pdir.split) { | ||||||
|             err = lfs_dir_drop(lfs, &pdir, dir); |             err = lfs_dir_drop(lfs, &pdir, dir); // TODO drop needs to handle branches | ||||||
|             if (err) { |             if (err) { | ||||||
|                 *dir = olddir; |                 *dir = olddir; | ||||||
|                 return err; |                 return err; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // ok, we couldn't find branch+tail combo, but what about explicit | ||||||
|  |         // branch and tail? | ||||||
|  |         lfs_stag_t ptag = lfs_fs_parent(lfs, dir->pair, &pdir, false); | ||||||
|  |         if (ptag < 0 && ptag != LFS_ERR_NOENT) { | ||||||
|  |             *dir = olddir; | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         //printf("hmm2 %x (%08x) %x %x\n", lfs_tag_type3(ptag), ptag, pdir.pair[0], pdir.pair[1]); | ||||||
|  |         if (lfs_tag_type3(ptag) == LFS_TYPE_BRANCH) { | ||||||
|  |             //printf("oh hey! it's a branch %x\n", ptag); | ||||||
|  |             err = lfs_dir_dropbranch(lfs, &pdir, ptag, dir); | ||||||
|  |             if (err) { | ||||||
|  |                 *dir = olddir; | ||||||
|  |                 return err; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return 0; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -1822,8 +2032,11 @@ compact: | |||||||
|         // fall back to compaction |         // fall back to compaction | ||||||
|         lfs_cache_drop(lfs, &lfs->pcache); |         lfs_cache_drop(lfs, &lfs->pcache); | ||||||
|  |  | ||||||
|  |         // TODO do we need to copy olddir? we happen to have one handy, but is this ok? | ||||||
|  |         // TODO should we just flatten dir_compact into here? this is complicated | ||||||
|  |         lfs_mdir_t temp = *dir; // TODO rm me? | ||||||
|         int err = lfs_dir_compact(lfs, dir, attrs, attrcount, |         int err = lfs_dir_compact(lfs, dir, attrs, attrcount, | ||||||
|                 dir, 0, dir->count); |                 &temp, 0, dir->count); | ||||||
|         if (err) { |         if (err) { | ||||||
|             *dir = olddir; |             *dir = olddir; | ||||||
|             return err; |             return err; | ||||||
| @@ -1864,10 +2077,30 @@ compact: | |||||||
|  |  | ||||||
|     for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { |     for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { | ||||||
|         if (lfs_pair_cmp(d->m.pair, olddir.pair) == 0) { |         if (lfs_pair_cmp(d->m.pair, olddir.pair) == 0) { | ||||||
|             while (d->id >= d->m.count && d->m.split) { |             while (d->id >= d->m.count) { | ||||||
|                 // we split and id is on tail now |                 // we split and id is on tail now | ||||||
|  |                 // TODO should I make this a common function? | ||||||
|  |                 // TODO use softtail? | ||||||
|  |                 lfs_stag_t ttag = 0; | ||||||
|  |                 lfs_block_t npair[2] = {d->m.tail[0], d->m.tail[1]}; | ||||||
|  |                 if (!d->m.split) { | ||||||
|  |                     ttag = lfs_dir_get(lfs, &d->m, | ||||||
|  |                             LFS_MKTAG(0x7ff, 0, 0), | ||||||
|  |                             LFS_MKTAG(LFS_TYPE_BRANCH, 0, sizeof(npair)), | ||||||
|  |                             npair); | ||||||
|  |                     if (ttag < 0 && ttag != LFS_ERR_NOENT) { | ||||||
|  |                         return ttag; | ||||||
|  |                     } | ||||||
|  |                     lfs_pair_fromle32(npair); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // ttag may be NOENT or may be marked null explicitly | ||||||
|  |                 if (ttag == LFS_ERR_NOENT || lfs_pair_isnull(npair)) { | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 d->id -= d->m.count; |                 d->id -= d->m.count; | ||||||
|                 int err = lfs_dir_fetch(lfs, &d->m, d->m.tail); |                 int err = lfs_dir_fetch(lfs, &d->m, npair); | ||||||
|                 if (err) { |                 if (err) { | ||||||
|                     return err; |                     return err; | ||||||
|                 } |                 } | ||||||
| @@ -1914,61 +2147,65 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // find end of list | //    // find end of list | ||||||
|     lfs_mdir_t pred = cwd.m; | //    lfs_mdir_t pred = cwd.m; | ||||||
|     while (pred.split) { | //    while (pred.split) { | ||||||
|         err = lfs_dir_fetch(lfs, &pred, pred.tail); | //        err = lfs_dir_fetch(lfs, &pred, pred.tail); | ||||||
|         if (err) { | //        if (err) { | ||||||
|             LFS_TRACE("lfs_mkdir -> %d", err); | //            LFS_TRACE("lfs_mkdir -> %d", err); | ||||||
|             return err; | //            return err; | ||||||
|         } | //        } | ||||||
|     } | //    } | ||||||
|  |  | ||||||
|     // setup dir |     // setup dir | ||||||
|     lfs_pair_tole32(pred.tail); |     lfs_pair_tole32(cwd.m.tail); | ||||||
|     err = lfs_dir_commit(lfs, &dir, LFS_MKATTRS( |     err = lfs_dir_commit(lfs, &dir, LFS_MKATTRS( | ||||||
|             {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), pred.tail})); |             {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), cwd.m.tail})); | ||||||
|     lfs_pair_fromle32(pred.tail); |     lfs_pair_fromle32(cwd.m.tail); | ||||||
|     if (err) { |     if (err) { | ||||||
|         LFS_TRACE("lfs_mkdir -> %d", err); |         LFS_TRACE("lfs_mkdir -> %d", err); | ||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // current block end of list? | //    // current block end of list? | ||||||
|     if (cwd.m.split) { | //    if (cwd.m.split) { | ||||||
|         // update tails, this creates a desync | //        // update tails, this creates a desync | ||||||
|         lfs_fs_preporphans(lfs, +1); | //        lfs_fs_preporphans(lfs, +1); | ||||||
|  | // | ||||||
|         // it's possible our predecessor has to be relocated, and if | //        // it's possible our predecessor has to be relocated, and if | ||||||
|         // our parent is our predecessor's predecessor, this could have | //        // our parent is our predecessor's predecessor, this could have | ||||||
|         // caused our parent to go out of date, fortunately we can hook | //        // caused our parent to go out of date, fortunately we can hook | ||||||
|         // ourselves into littlefs to catch this | //        // ourselves into littlefs to catch this | ||||||
|         cwd.type = 0; | //        cwd.type = 0; | ||||||
|         cwd.id = 0; | //        cwd.id = 0; | ||||||
|         lfs->mlist = &cwd; | //        lfs->mlist = &cwd; | ||||||
|  | // | ||||||
|         lfs_pair_tole32(dir.pair); | //        lfs_pair_tole32(dir.pair); | ||||||
|         err = lfs_dir_commit(lfs, &pred, LFS_MKATTRS( | //        err = lfs_dir_commit(lfs, &pred, LFS_MKATTRS( | ||||||
|                 {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir.pair})); | //                {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir.pair})); | ||||||
|         lfs_pair_fromle32(dir.pair); | //        lfs_pair_fromle32(dir.pair); | ||||||
|         if (err) { | //        if (err) { | ||||||
|             lfs->mlist = cwd.next; | //            lfs->mlist = cwd.next; | ||||||
|             LFS_TRACE("lfs_mkdir -> %d", err); | //            LFS_TRACE("lfs_mkdir -> %d", err); | ||||||
|             return err; | //            return err; | ||||||
|         } | //        } | ||||||
|  | // | ||||||
|         lfs->mlist = cwd.next; | //        lfs->mlist = cwd.next; | ||||||
|         lfs_fs_preporphans(lfs, -1); | //        lfs_fs_preporphans(lfs, -1); | ||||||
|     } | //    } | ||||||
|  |  | ||||||
|     // now insert into our parent block |     // now insert into our parent block | ||||||
|  |     lfs_block_t bpair[2] = {cwd.m.tail[0], cwd.m.tail[1]}; | ||||||
|  |     lfs_pair_tole32(bpair); | ||||||
|     lfs_pair_tole32(dir.pair); |     lfs_pair_tole32(dir.pair); | ||||||
|  |     // TODO need to change endianness of tail? | ||||||
|     err = lfs_dir_commit(lfs, &cwd.m, LFS_MKATTRS( |     err = lfs_dir_commit(lfs, &cwd.m, LFS_MKATTRS( | ||||||
|             {LFS_MKTAG(LFS_TYPE_CREATE, id, 0), NULL}, |             {LFS_MKTAG(LFS_TYPE_CREATE, id, 0), NULL}, | ||||||
|             {LFS_MKTAG(LFS_TYPE_DIR, id, nlen), path}, |             {LFS_MKTAG(LFS_TYPE_DIR, id, nlen), path}, | ||||||
|             {LFS_MKTAG(LFS_TYPE_DIRSTRUCT, id, 8), dir.pair}, |             {LFS_MKTAG(LFS_TYPE_DIRSTRUCT, id, 8), dir.pair}, | ||||||
|             {LFS_MKTAG_IF(!cwd.m.split, |             {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir.pair}, //TODO something with endianness? | ||||||
|                 LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir.pair})); |             {LFS_MKTAG_IF(cwd.m.split, // TODO split now means, _exclusively_, has branch+tail combo. Need to rename/redoc? | ||||||
|  |                 LFS_TYPE_BRANCH, 0x3ff, 8), bpair})); | ||||||
|     lfs_pair_fromle32(dir.pair); |     lfs_pair_fromle32(dir.pair); | ||||||
|     if (err) { |     if (err) { | ||||||
|         LFS_TRACE("lfs_mkdir -> %d", err); |         LFS_TRACE("lfs_mkdir -> %d", err); | ||||||
| @@ -2066,12 +2303,28 @@ int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) { | |||||||
|  |  | ||||||
|     while (true) { |     while (true) { | ||||||
|         if (dir->id == dir->m.count) { |         if (dir->id == dir->m.count) { | ||||||
|  |             lfs_stag_t ttag = 0; | ||||||
|  |             lfs_block_t npair[2] = {dir->m.tail[0], dir->m.tail[1]}; | ||||||
|  |             // TODO remove mlist from mkdir! | ||||||
|             if (!dir->m.split) { |             if (!dir->m.split) { | ||||||
|  |                 ttag = lfs_dir_get(lfs, &dir->m, | ||||||
|  |                         LFS_MKTAG(0x7ff, 0, 0), | ||||||
|  |                         LFS_MKTAG(LFS_TYPE_BRANCH, 0, sizeof(npair)), | ||||||
|  |                         npair); | ||||||
|  |                 if (ttag < 0 && ttag != LFS_ERR_NOENT) { | ||||||
|  |                     return ttag; | ||||||
|  |                 } | ||||||
|  |                 lfs_pair_fromle32(npair); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // ttag may be NOENT or may be marked null explicitly | ||||||
|  |             if (ttag == LFS_ERR_NOENT || lfs_pair_isnull(npair)) { | ||||||
|                 LFS_TRACE("lfs_dir_read -> %d", false); |                 LFS_TRACE("lfs_dir_read -> %d", false); | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             int err = lfs_dir_fetch(lfs, &dir->m, dir->m.tail); |             // fetch next dir | ||||||
|  |             int err = lfs_dir_fetch(lfs, &dir->m, npair); | ||||||
|             if (err) { |             if (err) { | ||||||
|                 LFS_TRACE("lfs_dir_read -> %d", err); |                 LFS_TRACE("lfs_dir_read -> %d", err); | ||||||
|                 return err; |                 return err; | ||||||
| @@ -2121,12 +2374,26 @@ int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) { | |||||||
|         off -= diff; |         off -= diff; | ||||||
|  |  | ||||||
|         if (dir->id == dir->m.count) { |         if (dir->id == dir->m.count) { | ||||||
|  |             lfs_stag_t ttag = 0; | ||||||
|  |             lfs_block_t npair[2] = {dir->m.tail[0], dir->m.tail[1]}; // TODO reuse dir tail? | ||||||
|             if (!dir->m.split) { |             if (!dir->m.split) { | ||||||
|  |                 ttag = lfs_dir_get(lfs, &dir->m, | ||||||
|  |                         LFS_MKTAG(0x7ff, 0, 0), | ||||||
|  |                         LFS_MKTAG(LFS_TYPE_BRANCH, 0, sizeof(npair)), | ||||||
|  |                         npair); | ||||||
|  |                 if (ttag < 0 && ttag != LFS_ERR_NOENT) { | ||||||
|  |                     return ttag; | ||||||
|  |                 } | ||||||
|  |                 lfs_pair_fromle32(npair); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // ttag may be NOENT or may be marked null explicitly | ||||||
|  |             if (ttag == LFS_ERR_NOENT || lfs_pair_isnull(npair)) { | ||||||
|                 LFS_TRACE("lfs_dir_seek -> %d", LFS_ERR_INVAL); |                 LFS_TRACE("lfs_dir_seek -> %d", LFS_ERR_INVAL); | ||||||
|                 return LFS_ERR_INVAL; |                 return LFS_ERR_INVAL; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             err = lfs_dir_fetch(lfs, &dir->m, dir->m.tail); |             err = lfs_dir_fetch(lfs, &dir->m, npair); | ||||||
|             if (err) { |             if (err) { | ||||||
|                 LFS_TRACE("lfs_dir_seek -> %d", err); |                 LFS_TRACE("lfs_dir_seek -> %d", err); | ||||||
|                 return err; |                 return err; | ||||||
| @@ -3929,6 +4196,7 @@ static int lfs_fs_pred(lfs_t *lfs, | |||||||
| struct lfs_fs_parent_match { | struct lfs_fs_parent_match { | ||||||
|     lfs_t *lfs; |     lfs_t *lfs; | ||||||
|     const lfs_block_t pair[2]; |     const lfs_block_t pair[2]; | ||||||
|  |     const bool mustbesync; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static int lfs_fs_parent_match(void *data, | static int lfs_fs_parent_match(void *data, | ||||||
| @@ -3936,7 +4204,12 @@ static int lfs_fs_parent_match(void *data, | |||||||
|     struct lfs_fs_parent_match *find = data; |     struct lfs_fs_parent_match *find = data; | ||||||
|     lfs_t *lfs = find->lfs; |     lfs_t *lfs = find->lfs; | ||||||
|     const struct lfs_diskoff *disk = buffer; |     const struct lfs_diskoff *disk = buffer; | ||||||
|     (void)tag; |  | ||||||
|  |     // is tag dirstruct or branch? can't check this with masking unfortunately | ||||||
|  |     if (lfs_tag_type3(tag) != LFS_TYPE_DIRSTRUCT && | ||||||
|  |             lfs_tag_type3(tag) != LFS_TYPE_BRANCH) { | ||||||
|  |         return LFS_CMP_LT; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     lfs_block_t child[2]; |     lfs_block_t child[2]; | ||||||
|     int err = lfs_bd_read(lfs, |     int err = lfs_bd_read(lfs, | ||||||
| @@ -3947,11 +4220,11 @@ static int lfs_fs_parent_match(void *data, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     lfs_pair_fromle32(child); |     lfs_pair_fromle32(child); | ||||||
|     return (lfs_pair_cmp(child, find->pair) == 0) ? LFS_CMP_EQ : LFS_CMP_LT; |     return (lfs_pair_cmp(child, find->pair) == 0 && !(find->mustbesync && !lfs_pair_sync(child, find->pair))) ? LFS_CMP_EQ : LFS_CMP_LT; | ||||||
| } | } | ||||||
|  |  | ||||||
| static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], | static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], | ||||||
|         lfs_mdir_t *parent) { |         lfs_mdir_t *parent, bool mustbesync) { | ||||||
|     // use fetchmatch with callback to find pairs |     // use fetchmatch with callback to find pairs | ||||||
|     parent->tail[0] = 0; |     parent->tail[0] = 0; | ||||||
|     parent->tail[1] = 1; |     parent->tail[1] = 1; | ||||||
| @@ -3964,11 +4237,14 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], | |||||||
|         cycle += 1; |         cycle += 1; | ||||||
|  |  | ||||||
|         lfs_stag_t tag = lfs_dir_fetchmatch(lfs, parent, parent->tail, |         lfs_stag_t tag = lfs_dir_fetchmatch(lfs, parent, parent->tail, | ||||||
|                 LFS_MKTAG(0x7ff, 0, 0x3ff), |                 // we need to match both dirstruct and branch, which are | ||||||
|  |                 // unfortunately very far Hamming distance wise, so we basically | ||||||
|  |                 // just handle this in lfs_fs_parent_match | ||||||
|  |                 LFS_MKTAG(0x300, 0, 0x3ff), | ||||||
|                 LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8), |                 LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8), | ||||||
|                 NULL, |                 NULL, | ||||||
|                 lfs_fs_parent_match, &(struct lfs_fs_parent_match){ |                 lfs_fs_parent_match, &(struct lfs_fs_parent_match){ | ||||||
|                     lfs, {pair[0], pair[1]}}); |                     lfs, {pair[0], pair[1]}, mustbesync}); | ||||||
|         if (tag && tag != LFS_ERR_NOENT) { |         if (tag && tag != LFS_ERR_NOENT) { | ||||||
|             return tag; |             return tag; | ||||||
|         } |         } | ||||||
| @@ -3978,6 +4254,7 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], | |||||||
| } | } | ||||||
|  |  | ||||||
| static int lfs_fs_relocate(lfs_t *lfs, | static int lfs_fs_relocate(lfs_t *lfs, | ||||||
|  |         lfs_stag_t ptag, lfs_mdir_t *parent, | ||||||
|         const lfs_block_t oldpair[2], lfs_block_t newpair[2]) { |         const lfs_block_t oldpair[2], lfs_block_t newpair[2]) { | ||||||
|     // update internal root |     // update internal root | ||||||
|     if (lfs_pair_cmp(oldpair, lfs->root) == 0) { |     if (lfs_pair_cmp(oldpair, lfs->root) == 0) { | ||||||
| @@ -4001,73 +4278,74 @@ static int lfs_fs_relocate(lfs_t *lfs, | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // find parent |     bool parentispred = (lfs_pair_cmp(parent->tail, oldpair) == 0); | ||||||
|     lfs_mdir_t parent; |  | ||||||
|     lfs_stag_t tag = lfs_fs_parent(lfs, oldpair, &parent); |  | ||||||
|     if (tag < 0 && tag != LFS_ERR_NOENT) { |  | ||||||
|         return tag; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (tag != LFS_ERR_NOENT) { |     // update parent if needed | ||||||
|         // update disk, this creates a desync |     if (ptag != LFS_ERR_NOENT) { | ||||||
|         lfs_fs_preporphans(lfs, +1); |         // update disk, this creates an orphan | ||||||
|  |         lfs_fs_preporphans(lfs, +!parentispred); | ||||||
|  |  | ||||||
|         // fix pending move in this pair? this looks like an optimization but |         // fix pending move in this pair? this looks like an optimization but | ||||||
|         // is in fact _required_ since relocating may outdate the move. |         // is in fact _required_ since relocating may outdate the move. | ||||||
|         uint16_t moveid = 0x3ff; |         uint16_t moveid = 0x3ff; | ||||||
|         if (lfs_gstate_hasmovehere(&lfs->gstate, parent.pair)) { |         if (lfs_gstate_hasmovehere(&lfs->gstate, parent->pair)) { | ||||||
|             moveid = lfs_tag_id(lfs->gstate.tag); |             moveid = lfs_tag_id(lfs->gstate.tag); | ||||||
|             LFS_DEBUG("Fixing move while relocating " |             LFS_DEBUG("Fixing move while relocating " | ||||||
|                     "%"PRIx32" %"PRIx32" %"PRIx16"\n", |                     "%"PRIx32" %"PRIx32" %"PRIx16"\n", | ||||||
|                     parent.pair[0], parent.pair[1], moveid); |                     parent->pair[0], parent->pair[1], moveid); | ||||||
|             lfs_fs_prepmove(lfs, 0x3ff, NULL); |             lfs_fs_prepmove(lfs, 0x3ff, NULL); | ||||||
|             if (moveid < lfs_tag_id(tag)) { |             if (moveid < lfs_tag_id(ptag)) { | ||||||
|                 tag -= LFS_MKTAG(0, 1, 0); |                 ptag -= LFS_MKTAG(0, 1, 0); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // commit new parent and thread into linked-list atomically | ||||||
|         lfs_pair_tole32(newpair); |         lfs_pair_tole32(newpair); | ||||||
|         int err = lfs_dir_commit(lfs, &parent, LFS_MKATTRS( |         int err = lfs_dir_commit(lfs, parent, LFS_MKATTRS( | ||||||
|                 {LFS_MKTAG_IF(moveid != 0x3ff, |                 {LFS_MKTAG_IF(moveid != 0x3ff, | ||||||
|                     LFS_TYPE_DELETE, moveid, 0)}, |                     LFS_TYPE_DELETE, moveid, 0)}, | ||||||
|                 {tag, newpair})); |                 {ptag, newpair}, | ||||||
|  |                 {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), newpair})); | ||||||
|         lfs_pair_fromle32(newpair); |         lfs_pair_fromle32(newpair); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // next step, clean up orphans |         // next step, clean up orphans | ||||||
|         lfs_fs_preporphans(lfs, -1); |         lfs_fs_preporphans(lfs, -!parentispred); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // find pred |     if (!parentispred) { | ||||||
|     int err = lfs_fs_pred(lfs, oldpair, &parent); |         // find pred | ||||||
|     if (err && err != LFS_ERR_NOENT) { |         int err = lfs_fs_pred(lfs, oldpair, parent); | ||||||
|         return err; |         if (err && err != LFS_ERR_NOENT) { | ||||||
|     } |             return err; | ||||||
|  |  | ||||||
|     // if we can't find dir, it must be new |  | ||||||
|     if (err != LFS_ERR_NOENT) { |  | ||||||
|         // fix pending move in this pair? this looks like an optimization but |  | ||||||
|         // is in fact _required_ since relocating may outdate the move. |  | ||||||
|         uint16_t moveid = 0x3ff; |  | ||||||
|         if (lfs_gstate_hasmovehere(&lfs->gstate, parent.pair)) { |  | ||||||
|             moveid = lfs_tag_id(lfs->gstate.tag); |  | ||||||
|             LFS_DEBUG("Fixing move while relocating " |  | ||||||
|                     "%"PRIx32" %"PRIx32" %"PRIx16"\n", |  | ||||||
|                     parent.pair[0], parent.pair[1], moveid); |  | ||||||
|             lfs_fs_prepmove(lfs, 0x3ff, NULL); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // replace bad pair, either we clean up desync, or no desync occured |         // if we can't find dir, it must be new | ||||||
|         lfs_pair_tole32(newpair); |         if (err != LFS_ERR_NOENT) { | ||||||
|         err = lfs_dir_commit(lfs, &parent, LFS_MKATTRS( |             // fix pending move in this pair? this looks like an optimization | ||||||
|                 {LFS_MKTAG_IF(moveid != 0x3ff, |             // but is in fact _required_ since relocating may outdate the move. | ||||||
|                     LFS_TYPE_DELETE, moveid, 0)}, |             uint16_t moveid = 0x3ff; | ||||||
|                 {LFS_MKTAG(LFS_TYPE_TAIL + parent.split, 0x3ff, 8), newpair})); |             if (lfs_gstate_hasmovehere(&lfs->gstate, parent->pair)) { | ||||||
|         lfs_pair_fromle32(newpair); |                 moveid = lfs_tag_id(lfs->gstate.tag); | ||||||
|         if (err) { |                 LFS_DEBUG("Fixing move while relocating " | ||||||
|             return err; |                         "%"PRIx32" %"PRIx32" %"PRIx16"\n", | ||||||
|  |                         parent->pair[0], parent->pair[1], moveid); | ||||||
|  |                 lfs_fs_prepmove(lfs, 0x3ff, NULL); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // replace bad pair, either we clean up desync, or no desync occured | ||||||
|  |             lfs_pair_tole32(newpair); | ||||||
|  |             err = lfs_dir_commit(lfs, parent, LFS_MKATTRS( | ||||||
|  |                     {LFS_MKTAG_IF(moveid != 0x3ff, | ||||||
|  |                         LFS_TYPE_DELETE, moveid, 0)}, | ||||||
|  |                     {LFS_MKTAG(LFS_TYPE_TAIL + parent->split, 0x3ff, 8), | ||||||
|  |                         newpair})); | ||||||
|  |             lfs_pair_fromle32(newpair); | ||||||
|  |             if (err) { | ||||||
|  |                 return err; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -4139,12 +4417,26 @@ static int lfs_fs_deorphan(lfs_t *lfs) { | |||||||
|         if (!pdir.split) { |         if (!pdir.split) { | ||||||
|             // check if we have a parent |             // check if we have a parent | ||||||
|             lfs_mdir_t parent; |             lfs_mdir_t parent; | ||||||
|             lfs_stag_t tag = lfs_fs_parent(lfs, pdir.tail, &parent); |             lfs_stag_t tag = lfs_fs_parent(lfs, pdir.tail, &parent, true); | ||||||
|             if (tag < 0 && tag != LFS_ERR_NOENT) { |             if (tag < 0 && tag != LFS_ERR_NOENT) { | ||||||
|                 return tag; |                 return tag; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             lfs_block_t pair[2]; | ||||||
|  |             lfs_stag_t res = lfs_dir_get(lfs, &parent, | ||||||
|  |                     LFS_MKTAG(0x7ff, 0x3ff, 0), tag, pair); | ||||||
|  |             if (res < 0) { | ||||||
|  |                 return res; | ||||||
|  |             } | ||||||
|  |             lfs_pair_fromle32(pair); | ||||||
|  |  | ||||||
|  |             // Half-orphans can also be true orphans if there is a better | ||||||
|  |             // child. Currently the child must be our successor. | ||||||
|             if (tag == LFS_ERR_NOENT) { |             if (tag == LFS_ERR_NOENT) { | ||||||
|  | // || !lfs_pair_sync(pair, pdir.tail)) { | ||||||
|  | // TODO only do this if half-sync and found better pair? | ||||||
|  | //                    (!lfs_pair_sync(pair, pdir.tail) && | ||||||
|  | //                        lfs_pair_cmp(pdir.tail, dir.tail) == 0)) { | ||||||
|                 // we are an orphan |                 // we are an orphan | ||||||
|                 LFS_DEBUG("Fixing orphan %"PRIx32" %"PRIx32, |                 LFS_DEBUG("Fixing orphan %"PRIx32" %"PRIx32, | ||||||
|                         pdir.tail[0], pdir.tail[1]); |                         pdir.tail[0], pdir.tail[1]); | ||||||
| @@ -4158,14 +4450,6 @@ static int lfs_fs_deorphan(lfs_t *lfs) { | |||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             lfs_block_t pair[2]; |  | ||||||
|             lfs_stag_t res = lfs_dir_get(lfs, &parent, |  | ||||||
|                     LFS_MKTAG(0x7ff, 0x3ff, 0), tag, pair); |  | ||||||
|             if (res < 0) { |  | ||||||
|                 return res; |  | ||||||
|             } |  | ||||||
|             lfs_pair_fromle32(pair); |  | ||||||
|  |  | ||||||
|             if (!lfs_pair_sync(pair, pdir.tail)) { |             if (!lfs_pair_sync(pair, pdir.tail)) { | ||||||
|                 // we have desynced |                 // we have desynced | ||||||
|                 LFS_DEBUG("Fixing half-orphan " |                 LFS_DEBUG("Fixing half-orphan " | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -111,6 +111,7 @@ enum lfs_type { | |||||||
|     LFS_TYPE_INLINESTRUCT   = 0x201, |     LFS_TYPE_INLINESTRUCT   = 0x201, | ||||||
|     LFS_TYPE_SOFTTAIL       = 0x600, |     LFS_TYPE_SOFTTAIL       = 0x600, | ||||||
|     LFS_TYPE_HARDTAIL       = 0x601, |     LFS_TYPE_HARDTAIL       = 0x601, | ||||||
|  |     LFS_TYPE_BRANCH         = 0x681, | ||||||
|     LFS_TYPE_MOVESTATE      = 0x7ff, |     LFS_TYPE_MOVESTATE      = 0x7ff, | ||||||
|  |  | ||||||
|     // internal chip sources |     // internal chip sources | ||||||
|   | |||||||
| @@ -18,9 +18,10 @@ TAG_TYPES = { | |||||||
|     'ctzstruct':    (0x7ff, 0x202), |     'ctzstruct':    (0x7ff, 0x202), | ||||||
|     'inlinestruct': (0x7ff, 0x201), |     'inlinestruct': (0x7ff, 0x201), | ||||||
|     'userattr':     (0x700, 0x300), |     'userattr':     (0x700, 0x300), | ||||||
|     'tail':         (0x700, 0x600), |     'tail':         (0x700, 0x600), # TODO rename these? | ||||||
|     'softtail':     (0x7ff, 0x600), |     'softtail':     (0x7ff, 0x600), | ||||||
|     'hardtail':     (0x7ff, 0x601), |     'hardtail':     (0x7ff, 0x601), | ||||||
|  |     'branch':       (0x7ff, 0x681), | ||||||
|     'gstate':       (0x700, 0x700), |     'gstate':       (0x700, 0x700), | ||||||
|     'movestate':    (0x7ff, 0x7ff), |     'movestate':    (0x7ff, 0x7ff), | ||||||
|     'crc':          (0x700, 0x500), |     'crc':          (0x700, 0x500), | ||||||
| @@ -103,7 +104,7 @@ class Tag: | |||||||
|  |  | ||||||
|     def mkmask(self): |     def mkmask(self): | ||||||
|         return Tag( |         return Tag( | ||||||
|             0x700 if self.isunique else 0x7ff, |             0x780 if self.is_('tail') else 0x700 if self.isunique else 0x7ff, # TODO best way? | ||||||
|             0x3ff if self.isattr else 0, |             0x3ff if self.isattr else 0, | ||||||
|             0) |             0) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -246,6 +246,8 @@ code = ''' | |||||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; |                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||||
|         lfs_file_close(&lfs, &file) => 0; |         lfs_file_close(&lfs, &file) => 0; | ||||||
|     } |     } | ||||||
|  |     // TODO rm me | ||||||
|  |     lfs_mkdir(&lfs, "a") => 0; | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
|  |  | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
| @@ -256,6 +258,9 @@ code = ''' | |||||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; |     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||||
|     assert(info.type == LFS_TYPE_DIR); |     assert(info.type == LFS_TYPE_DIR); | ||||||
|     assert(strcmp(info.name, "..") == 0); |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "a") == 0); | ||||||
|     for (int i = 0; i < N; i++) { |     for (int i = 0; i < N; i++) { | ||||||
|         sprintf(path, "file%03d", i); |         sprintf(path, "file%03d", i); | ||||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; |         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user