mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 16:14:16 +01:00 
			
		
		
		
	WIP crazy new idea work in progress
passing non-reentrant tests already!
This commit is contained in:
		
							
								
								
									
										259
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										259
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -241,6 +241,13 @@ static inline int lfs_pair_cmp( | ||||
|              paira[0] == pairb[1] || paira[1] == pairb[0]); | ||||
| } | ||||
|  | ||||
| static inline int lfs_pair_realcmp( | ||||
|         const lfs_block_t paira[2], | ||||
|         const lfs_block_t pairb[2]) { | ||||
|     return !((paira[0] == pairb[0] && paira[1] == pairb[1]) || | ||||
|              (paira[0] == pairb[1] && paira[1] == pairb[0])); | ||||
| } | ||||
|  | ||||
| static inline bool lfs_pair_sync( | ||||
|         const lfs_block_t paira[2], | ||||
|         const lfs_block_t pairb[2]) { | ||||
| @@ -423,6 +430,7 @@ int lfs_fs_traverseraw(lfs_t *lfs, | ||||
|         int (*cb)(void *data, lfs_block_t block), void *data, | ||||
|         bool includeorphans); | ||||
| static int lfs_fs_forceconsistency(lfs_t *lfs); | ||||
| static int lfs_fs_deorphan(lfs_t *lfs); | ||||
| static int lfs_deinit(lfs_t *lfs); | ||||
| #ifdef LFS_MIGRATE | ||||
| static int lfs1_traverse(lfs_t *lfs, | ||||
| @@ -808,6 +816,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|                 if (err == LFS_ERR_CORRUPT) { | ||||
|                     // can't continue? | ||||
|                     dir->erased = false; | ||||
|                     dir->first = false; | ||||
|                     break; | ||||
|                 } | ||||
|                 return err; | ||||
| @@ -820,9 +829,11 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|             if (!lfs_tag_isvalid(tag)) { | ||||
|                 dir->erased = (lfs_tag_type1(ptag) == LFS_TYPE_CRC && | ||||
|                         dir->off % lfs->cfg->prog_size == 0); | ||||
|                 dir->first = false; | ||||
|                 break; | ||||
|             } else if (off + lfs_tag_dsize(tag) > lfs->cfg->block_size) { | ||||
|                 dir->erased = false; | ||||
|                 dir->first = false; | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
| @@ -837,6 +848,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|                 if (err) { | ||||
|                     if (err == LFS_ERR_CORRUPT) { | ||||
|                         dir->erased = false; | ||||
|                         dir->first = false; | ||||
|                         break; | ||||
|                     } | ||||
|                     return err; | ||||
| @@ -845,6 +857,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|  | ||||
|                 if (crc != dcrc) { | ||||
|                     dir->erased = false; | ||||
|                     dir->first = false; | ||||
|                     break; | ||||
|                 } | ||||
|  | ||||
| @@ -880,6 +893,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|                 if (err) { | ||||
|                     if (err == LFS_ERR_CORRUPT) { | ||||
|                         dir->erased = false; | ||||
|                         dir->first = false; | ||||
|                         break; | ||||
|                     } | ||||
|                     return err; | ||||
| @@ -913,6 +927,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|                 if (err) { | ||||
|                     if (err == LFS_ERR_CORRUPT) { | ||||
|                         dir->erased = false; | ||||
|                         dir->first = false; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
| @@ -935,6 +950,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|                     if (err) { | ||||
|                         if (err == LFS_ERR_CORRUPT) { | ||||
|                             dir->erased = false; | ||||
|                             dir->first = false; | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
| @@ -948,6 +964,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|                 if (res < 0) { | ||||
|                     if (res == LFS_ERR_CORRUPT) { | ||||
|                         dir->erased = false; | ||||
|                         dir->first = false; | ||||
|                         break; | ||||
|                     } | ||||
|                     return res; | ||||
| @@ -1419,6 +1436,7 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) { | ||||
|     dir->branch[0] = LFS_BLOCK_NULL; | ||||
|     dir->branch[1] = LFS_BLOCK_NULL; | ||||
|     dir->erased = false; | ||||
|     dir->first = true; | ||||
|     dir->split = false; | ||||
|  | ||||
|     // don't write out yet, let caller take care of that | ||||
| @@ -1442,6 +1460,7 @@ static int lfs_dir_droptail(lfs_t *lfs, lfs_mdir_t *dir, lfs_mdir_t *tail) { | ||||
| //    } | ||||
|  | ||||
|     // steal tail's tail | ||||
|     // TODO does this tail update cause problems | ||||
|     lfs_pair_tole32(tail->tail); | ||||
|     err = lfs_dir_commit(lfs, dir, LFS_MKATTRS( | ||||
|             {LFS_MKTAG(LFS_TYPE_TAIL + tail->split, 0x3ff, 8), tail->tail})); | ||||
| @@ -1863,6 +1882,7 @@ relocate: | ||||
|  | ||||
| #if 1 | ||||
|         // find parent? | ||||
|         // TODO do we need this if parent.tail == oldpair? The answer is no but how to organize | ||||
|         ptag = lfs_fs_parent(lfs, oldpair, &parent, false); | ||||
|         if (ptag < 0 && ptag != LFS_ERR_NOENT) { | ||||
|             return ptag; | ||||
| @@ -1880,17 +1900,56 @@ relocate: | ||||
|         } | ||||
|  | ||||
|         // Have parent? Didn't give up? This is when we need to reinsert | ||||
|         // ourself in the threaded linked-list. This evenutally creates an | ||||
|         // ourself in the threaded linked-list. This eventually 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) { | ||||
|                 (lfs_pair_cmp(parent.tail, oldpair) != 0 || !lfs_pair_sync(parent.tail, oldpair))) { // TODO word this so much better | ||||
|             if (lfs_pair_isnull(lfs->relocate_tail)) { | ||||
|                 // not relocating yet | ||||
|                 dir->tail[0] = parent.tail[0]; | ||||
|                 dir->tail[1] = parent.tail[1]; | ||||
|             printf("tail? %x %x\n", dir->tail[0], dir->tail[1]); | ||||
|             } else { | ||||
|                 printf("HEEEEEEEEEEEEY\n"); | ||||
|                 // already relocating, we need to update the last dir in our | ||||
|                 // tail of new pairs | ||||
|                 lfs_mdir_t relocatedir; | ||||
|                 err = lfs_dir_fetch(lfs, &relocatedir, lfs->relocate_end); | ||||
|                 if (err) { | ||||
|                     return err; | ||||
|                 } | ||||
|  | ||||
|                 LFS_ASSERT(!lfs->relocate_do_hack); | ||||
|                 bool oldhack = lfs->relocate_do_hack; | ||||
|                 lfs->relocate_do_hack = true; | ||||
|                 err = lfs_dir_commit(lfs, &relocatedir, LFS_MKATTRS( | ||||
|                         {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), parent.tail})); | ||||
|                 lfs->relocate_do_hack = oldhack; | ||||
|                 if (err) { | ||||
|                     // TODO do we need to clean up anything? | ||||
|                     return err; | ||||
|                 } | ||||
|  | ||||
|                 dir->tail[0] = lfs->relocate_tail[0]; | ||||
|                 dir->tail[1] = lfs->relocate_tail[1]; | ||||
|             } | ||||
|              | ||||
|  | ||||
|  | ||||
| //            // TODO implement this | ||||
| //            if (!lfs_pair_isnull(lfs->relocate_tail)) { | ||||
| //                printf("HEEEEEY\n"); | ||||
| //            } | ||||
| //            //LFS_ASSERT(lfs->relocate_len == 0); | ||||
| //            // TODO AH BUT WHAT IF WE'RE RELOCATING ALREADY | ||||
| //            dir->tail[0] = parent.tail[0]; | ||||
| //            dir->tail[1] = parent.tail[1]; | ||||
| //            printf("tail? %x %x\n", dir->tail[0], dir->tail[1]); | ||||
| //            lfs->relocate_tail[0] = dir->pair[0]; | ||||
| //            lfs->relocate_tail[1] = dir->pair[1]; | ||||
|         } | ||||
| #elif 0 // TODO rm me | ||||
|         // find parent? | ||||
| @@ -1941,6 +2000,21 @@ relocate: | ||||
|     } | ||||
|  | ||||
|     if (relocated) { | ||||
|         // TODO hm | ||||
|         if (lfs_pair_isnull(lfs->relocate_tail)) { | ||||
|             // TODO do this before? | ||||
|             lfs->relocate_end[0] = dir->pair[0]; | ||||
|             lfs->relocate_end[1] = dir->pair[1]; | ||||
|         } | ||||
|         lfs->relocate_tail[0] = dir->pair[0]; | ||||
|         lfs->relocate_tail[1] = dir->pair[1]; | ||||
|  | ||||
| //        if (!dir->first) { | ||||
| //            // TODO is this the best way to force dir updates to be on one block? | ||||
| //            dir->pair[0] = dir->pair[0]; | ||||
| //            dir->pair[1] = oldpair[1]; | ||||
| //        } | ||||
| // | ||||
|         // update references if we relocated | ||||
|         LFS_DEBUG("Relocating %"PRIx32" %"PRIx32" -> %"PRIx32" %"PRIx32, | ||||
|                 oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); | ||||
| @@ -1948,6 +2022,17 @@ relocate: | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         // TODO well this is the hackiest thing I've done in a while, | ||||
|         // needed because we may be changed during relocate (of course!) | ||||
|         // | ||||
|         // TODO we should be inserted into mlist | ||||
|         // TODO should mlist be reworked? | ||||
|         printf("refetch {%#x, %#x}\n", dir->pair[0], dir->pair[1]); | ||||
|         err = lfs_dir_fetch(lfs, dir, dir->pair); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| @@ -2135,6 +2220,12 @@ compact: | ||||
|         } | ||||
|     } | ||||
|  | ||||
| //    printf("dir before: {%#x, %#x}, mlist before: ", olddir.pair[0], olddir.pair[1]); | ||||
| //    for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { | ||||
| //        printf("{%#x, %#x} ", d->m.pair[0], d->m.pair[1]); | ||||
| //    } | ||||
| //    printf("\n"); | ||||
|  | ||||
|     // this complicated bit of logic is for fixing up any active | ||||
|     // metadata-pairs that we may have affected | ||||
|     // | ||||
| @@ -2143,7 +2234,9 @@ compact: | ||||
|     // we need to copy the pair so they don't get clobbered if we refetch | ||||
|     // our mdir. | ||||
|     for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { | ||||
|         if (&d->m != dir && lfs_pair_cmp(d->m.pair, olddir.pair) == 0) { | ||||
|         if (&d->m != dir && (lfs_pair_realcmp(d->m.pair, olddir.pair) == 0 || | ||||
|                 lfs_pair_realcmp(d->m.pair, dir->pair) == 0)) { // TODO hmm, this was updated in lfs_fs_relocate? is this double work? what do we do with olddir?? | ||||
|             //printf("hey {%#x, %#x} -> {%#x, %#x}\n", d->m.pair[0], d->m.pair[1], dir->pair[0], dir->pair[1]); | ||||
|             d->m = *dir; | ||||
|             for (int i = 0; i < attrcount; i++) { | ||||
|                 if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE && | ||||
| @@ -2168,7 +2261,8 @@ compact: | ||||
|     } | ||||
|  | ||||
|     for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { | ||||
|         if (lfs_pair_cmp(d->m.pair, olddir.pair) == 0) { | ||||
|         if (lfs_pair_realcmp(d->m.pair, olddir.pair) == 0 || | ||||
|                 lfs_pair_realcmp(d->m.pair, dir->pair) == 0) { // TODO hm, see above | ||||
|             while (d->id >= d->m.count && !lfs_pair_isnull(d->m.branch)) { | ||||
| //                 | ||||
| // | ||||
| @@ -2202,6 +2296,18 @@ compact: | ||||
|         } | ||||
|     } | ||||
|  | ||||
| //    printf("dir after:  {%#x, %#x}, mlist after:  ", dir->pair[0], dir->pair[1]); | ||||
| //    for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { | ||||
| //        printf("{%#x, %#x} ", d->m.pair[0], d->m.pair[1]); | ||||
| //    } | ||||
| //    printf("\n"); | ||||
|  | ||||
|     if (!lfs->relocate_do_hack) { | ||||
|         lfs->relocate_tail[0] = LFS_BLOCK_NULL; | ||||
|         lfs->relocate_tail[1] = LFS_BLOCK_NULL; | ||||
|         lfs->relocate_end[0] = LFS_BLOCK_NULL; // TODO need these? | ||||
|         lfs->relocate_end[1] = LFS_BLOCK_NULL; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -2293,6 +2399,10 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { | ||||
|     lfs_pair_tole32(bpair); | ||||
|     lfs_pair_tole32(dir.pair); | ||||
|     // TODO need to change endianness of tail? | ||||
|     lfs->relocate_tail[0] = dir.pair[0]; // TODO | ||||
|     lfs->relocate_tail[1] = dir.pair[1]; | ||||
|     lfs->relocate_end[0] = dir.pair[0]; // TODO | ||||
|     lfs->relocate_end[1] = dir.pair[1]; | ||||
|     err = lfs_dir_commit(lfs, &cwd.m, LFS_MKATTRS( | ||||
|             {LFS_MKTAG(LFS_TYPE_CREATE, id, 0), NULL}, | ||||
|             {LFS_MKTAG(LFS_TYPE_DIR, id, nlen), path}, | ||||
| @@ -3683,6 +3793,15 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|     // let commit clean up after move (if we're different! otherwise move | ||||
|     // logic already fixed it for us) | ||||
|     if (!samepair && lfs_gstate_hasmove(&lfs->gstate)) { | ||||
|         // fetch again | ||||
|         // TODO should this be in mlist? | ||||
|         err = lfs_dir_fetch(lfs, &oldcwd, oldcwd.pair); | ||||
|         if (err) { | ||||
|             lfs->mlist = prevdir.next; | ||||
|             LFS_TRACE("lfs_rename -> %d", err); | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         // prep gstate and delete move id | ||||
|         lfs_fs_prepmove(lfs, 0x3ff, NULL); | ||||
|         err = lfs_dir_commit(lfs, &oldcwd, LFS_MKATTRS( | ||||
| @@ -3893,6 +4012,11 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     // setup default state | ||||
|     lfs->root[0] = LFS_BLOCK_NULL; | ||||
|     lfs->root[1] = LFS_BLOCK_NULL; | ||||
|     lfs->relocate_tail[0] = LFS_BLOCK_NULL; | ||||
|     lfs->relocate_tail[1] = LFS_BLOCK_NULL; | ||||
|     lfs->relocate_end[0] = LFS_BLOCK_NULL; | ||||
|     lfs->relocate_end[1] = LFS_BLOCK_NULL; | ||||
|     lfs->relocate_do_hack = false; | ||||
|     lfs->mlist = NULL; | ||||
|     lfs->seed = 0; | ||||
|     lfs->gdisk = (lfs_gstate_t){0}; | ||||
| @@ -4270,10 +4394,33 @@ int lfs_fs_traverse(lfs_t *lfs, | ||||
|  | ||||
| static int lfs_fs_pred(lfs_t *lfs, | ||||
|         const lfs_block_t pair[2], lfs_mdir_t *pdir) { | ||||
|     // iterate over our relocation chain | ||||
|     // TODO combine these loops? | ||||
|     pdir->tail[0] = lfs->relocate_tail[0]; | ||||
|     pdir->tail[1] = lfs->relocate_tail[1]; | ||||
|     lfs_block_t cycle = 0; | ||||
|     while (!lfs_pair_isnull(pdir->tail) && | ||||
|             lfs_pair_cmp(pdir->tail, lfs->relocate_end) != 0) { | ||||
|         if (cycle >= lfs->cfg->block_count/2) { | ||||
|             // loop detected | ||||
|             return LFS_ERR_CORRUPT; | ||||
|         } | ||||
|         cycle += 1; | ||||
|  | ||||
|         if (lfs_pair_cmp(pdir->tail, pair) == 0) { | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         int err = lfs_dir_fetch(lfs, pdir, pdir->tail); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // iterate over all directory directory entries | ||||
|     pdir->tail[0] = 0; | ||||
|     pdir->tail[1] = 1; | ||||
|     lfs_block_t cycle = 0; | ||||
|     cycle = 0; | ||||
|     while (!lfs_pair_isnull(pdir->tail)) { | ||||
|         if (cycle >= lfs->cfg->block_count/2) { | ||||
|             // loop detected | ||||
| @@ -4357,6 +4504,12 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], | ||||
| 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]) { | ||||
|     printf("mlist before: "); | ||||
|     for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { | ||||
|         printf("{%#x, %#x} ", d->m.pair[0], d->m.pair[1]); | ||||
|     } | ||||
|     printf("\n"); | ||||
|  | ||||
|     // update internal root | ||||
|     if (lfs_pair_cmp(oldpair, lfs->root) == 0) { | ||||
|         LFS_DEBUG("Relocating root %"PRIx32" %"PRIx32, | ||||
| @@ -4365,6 +4518,18 @@ static int lfs_fs_relocate(lfs_t *lfs, | ||||
|         lfs->root[1] = newpair[1]; | ||||
|     } | ||||
|  | ||||
|     // update relocate chain if needed | ||||
|     if (lfs_pair_cmp(oldpair, lfs->relocate_tail) == 0) { | ||||
|         lfs->relocate_tail[0] = newpair[0]; | ||||
|         lfs->relocate_tail[1] = newpair[1]; | ||||
|     } | ||||
|  | ||||
|     if (lfs_pair_cmp(oldpair, lfs->relocate_end) == 0) { | ||||
|         lfs->relocate_end[0] = newpair[0]; | ||||
|         lfs->relocate_end[1] = newpair[1]; | ||||
|     } | ||||
|  | ||||
|     // TODO is this doing double work???? | ||||
|     // update internally tracked dirs | ||||
|     for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { | ||||
|         if (lfs_pair_cmp(oldpair, d->m.pair) == 0) { | ||||
| @@ -4379,6 +4544,12 @@ static int lfs_fs_relocate(lfs_t *lfs, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     printf("mlist after: "); | ||||
|     for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { | ||||
|         printf("{%#x, %#x} ", d->m.pair[0], d->m.pair[1]); | ||||
|     } | ||||
|     printf("\n"); | ||||
|  | ||||
|     bool parentispred = (lfs_pair_cmp(parent->tail, oldpair) == 0); | ||||
|  | ||||
|     // update parent if needed | ||||
| @@ -4413,42 +4584,52 @@ static int lfs_fs_relocate(lfs_t *lfs, | ||||
|         } | ||||
|  | ||||
|         // next step, clean up orphans | ||||
|         lfs_fs_preporphans(lfs, -!parentispred); | ||||
|         // TODO hm | ||||
|         //lfs_fs_preporphans(lfs, -!parentispred); | ||||
|     } | ||||
|  | ||||
|     if (!parentispred) { | ||||
|         // find pred | ||||
|         int err = lfs_fs_pred(lfs, oldpair, parent); | ||||
|         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 | ||||
|             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); | ||||
|     // TODO clean this up? | ||||
|     if (lfs_gstate_hasorphans(&lfs->gstate)) { | ||||
|         int err = lfs_fs_deorphan(lfs); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|     } | ||||
|     } | ||||
|  | ||||
| // | ||||
| //    if (!parentispred) { | ||||
| //        // find pred | ||||
| //        int err = lfs_fs_pred(lfs, oldpair, parent); | ||||
| //        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 | ||||
| //            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; | ||||
| //            } | ||||
| //        } | ||||
| //    } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -4503,12 +4684,16 @@ static int lfs_fs_deorphan(lfs_t *lfs) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     // TODO needme? | ||||
|     LFS_ASSERT(lfs_pair_isnull(lfs->relocate_tail)); | ||||
|  | ||||
|     // Fix any orphans | ||||
|     lfs_mdir_t pdir = {.split = true, .tail = {0, 1}}; | ||||
|     lfs_mdir_t dir; | ||||
|  | ||||
|     // iterate over all directory directory entries | ||||
|     while (!lfs_pair_isnull(pdir.tail)) { | ||||
|     // TODO handle this more gracefully | ||||
|     while (lfs_gstate_hasorphans(&lfs->gstate) && !lfs_pair_isnull(pdir.tail)) { | ||||
|         int err = lfs_dir_fetch(lfs, &dir, pdir.tail); | ||||
|         if (err) { | ||||
|             return err; | ||||
|   | ||||
							
								
								
									
										7
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -117,7 +117,8 @@ enum lfs_type { | ||||
|     // internal chip sources | ||||
|     LFS_FROM_NOOP           = 0x000, | ||||
|     LFS_FROM_MOVE           = 0x101, | ||||
|     LFS_FROM_USERATTRS      = 0x102, | ||||
|     LFS_FROM_DROP           = 0x102, | ||||
|     LFS_FROM_USERATTRS      = 0x103, | ||||
| }; | ||||
|  | ||||
| // File open flags | ||||
| @@ -311,6 +312,7 @@ typedef struct lfs_mdir { | ||||
|     uint32_t etag; | ||||
|     uint16_t count; | ||||
|     bool erased; | ||||
|     bool first; // TODO come on | ||||
|     bool split; | ||||
|     lfs_block_t tail[2]; | ||||
|     lfs_block_t branch[2]; | ||||
| @@ -368,6 +370,9 @@ typedef struct lfs { | ||||
|     lfs_cache_t pcache; | ||||
|  | ||||
|     lfs_block_t root[2]; | ||||
|     lfs_block_t relocate_tail[2]; | ||||
|     lfs_block_t relocate_end[2]; | ||||
|     bool relocate_do_hack; // TODO fixme | ||||
|     struct lfs_mlist { | ||||
|         struct lfs_mlist *next; | ||||
|         uint16_t id; | ||||
|   | ||||
| @@ -326,7 +326,7 @@ def main(args): | ||||
|         mdir.rev, | ||||
|         ' (was %s)' % ', '.join('%d' % m.rev for m in mdir.pair[1:]) | ||||
|         if len(mdir.pair) > 1 else '', | ||||
|         ' (corrupted)' if not mdir else '')) | ||||
|         ' (corrupted!)' if not mdir else '')) | ||||
|     if args.all: | ||||
|         mdir.dump_all(truncate=not args.no_truncate) | ||||
|     elif args.log: | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import sys | ||||
| import json | ||||
| import io | ||||
| import itertools as it | ||||
| import collections as c | ||||
| from readmdir import Tag, MetadataPair | ||||
|  | ||||
| def popc(x): | ||||
| @@ -13,7 +14,7 @@ def popc(x): | ||||
| def ctz(x): | ||||
|     return len(bin(x)) - len(bin(x).rstrip('0')) | ||||
|  | ||||
| def dumpentries(args, mdir, f): | ||||
| def dumpentries(args, mdir, mdirs, f): | ||||
|     for k, id_ in enumerate(mdir.ids): | ||||
|         name = mdir[Tag('name', id_, 0)] | ||||
|         struct_ = mdir[Tag('struct', id_, 0)] | ||||
| @@ -22,8 +23,10 @@ def dumpentries(args, mdir, f): | ||||
|             id_, name.typerepr(), | ||||
|             json.dumps(name.data.decode('utf8'))) | ||||
|         if struct_.is_('dirstruct'): | ||||
|             desc += " dir {%#x, %#x}" % struct.unpack( | ||||
|                 '<II', struct_.data[:8].ljust(8, b'\xff')) | ||||
|             pair = struct.unpack('<II', struct_.data[:8].ljust(8, b'\xff')) | ||||
|             desc += " dir {%#x, %#x}%s" % ( | ||||
|                 pair[0], pair[1], | ||||
|                 '?' if frozenset(pair) not in mdirs else '') | ||||
|         if struct_.is_('ctzstruct'): | ||||
|             desc += " ctz {%#x} size %d" % struct.unpack( | ||||
|                 '<II', struct_.data[:8].ljust(8, b'\xff')) | ||||
| @@ -93,19 +96,15 @@ def dumpentries(args, mdir, f): | ||||
|  | ||||
| def main(args): | ||||
|     with open(args.disk, 'rb') as f: | ||||
|         dirs = [] | ||||
|         superblock = None | ||||
|         gstate = b'' | ||||
|         mdirs = [] | ||||
|         gstate = b'\0\0\0\0\0\0\0\0\0\0\0\0' | ||||
|         mdirs = c.OrderedDict() | ||||
|         cycle = False | ||||
|         tail = (args.block1, args.block2) | ||||
|         hard = False | ||||
|         while True: | ||||
|             for m in it.chain((m for d in dirs for m in d), mdirs): | ||||
|                 if set(m.blocks) == set(tail): | ||||
|         while tail: | ||||
|             if frozenset(tail) in mdirs: | ||||
|                 # cycle detected | ||||
|                     cycle = m.blocks | ||||
|             if cycle: | ||||
|                 cycle = tail | ||||
|                 break | ||||
|  | ||||
|             # load mdir | ||||
| @@ -128,6 +127,13 @@ def main(args): | ||||
|             except KeyError: | ||||
|                 mdir.tail = None | ||||
|  | ||||
|             try: | ||||
|                 mdir.branch = mdir[Tag('branch', 0, 0)] | ||||
|                 if mdir.branch.size != 8 or mdir.branch.data == 8*b'\xff': | ||||
|                     mdir.branch = None | ||||
|             except KeyError: | ||||
|                 mdir.branch = None | ||||
|  | ||||
|             # have superblock? | ||||
|             try: | ||||
|                 nsuperblock = mdir[ | ||||
| @@ -144,41 +150,55 @@ def main(args): | ||||
|             except KeyError: | ||||
|                 pass | ||||
|  | ||||
|             # add to directories | ||||
|             mdirs.append(mdir) | ||||
|             if mdir.tail is None or not mdir.tail.is_('hardtail'): | ||||
|                 dirs.append(mdirs) | ||||
|                 mdirs = [] | ||||
|             # add to metadata-pairs | ||||
|             mdirs[frozenset(mdir.blocks)] = mdir | ||||
|             tail = (struct.unpack('<II', mdir.tail.data) | ||||
|                 if mdir.tail else None) | ||||
|  | ||||
|             if mdir.tail is None: | ||||
|                 break | ||||
|  | ||||
|             tail = struct.unpack('<II', mdir.tail.data) | ||||
|             hard = mdir.tail.is_('hardtail') | ||||
|  | ||||
|     # find paths | ||||
|     dirtable = {} | ||||
|     for dir in dirs: | ||||
|         dirtable[frozenset(dir[0].blocks)] = dir | ||||
|  | ||||
|     pending = [("/", dirs[0])] | ||||
|     # derive paths and build directories | ||||
|     dirs = {} | ||||
|     rogue = {} | ||||
|     pending = [('/', (args.block1, args.block2))] | ||||
|     while pending: | ||||
|         path, dir = pending.pop(0) | ||||
|         for mdir in dir: | ||||
|         path, branch = pending.pop(0) | ||||
|         dir = [] | ||||
|         while branch and frozenset(branch) in mdirs: | ||||
|             mdir = mdirs[frozenset(branch)] | ||||
|             dir.append(mdir) | ||||
|  | ||||
|             for tag in mdir.tags: | ||||
|                 if tag.is_('dir'): | ||||
|                     try: | ||||
|                         npath = tag.data.decode('utf8') | ||||
|                         npath = path + '/' + tag.data.decode('utf8') | ||||
|                         npath = npath.replace('//', '/') | ||||
|                         dirstruct = mdir[Tag('dirstruct', tag.id, 0)] | ||||
|                         nblocks = struct.unpack('<II', dirstruct.data) | ||||
|                         nmdir = dirtable[frozenset(nblocks)] | ||||
|                         pending.append(((path + '/' + npath), nmdir)) | ||||
|                         npair = struct.unpack('<II', dirstruct.data) | ||||
|                         pending.append((npath, npair)) | ||||
|                     except KeyError: | ||||
|                         pass | ||||
|  | ||||
|         dir[0].path = path.replace('//', '/') | ||||
|             branch = (struct.unpack('<II', mdir.branch.data) | ||||
|                 if mdir.branch else None) | ||||
|  | ||||
|     # dump tree | ||||
|         if not dir: | ||||
|             rogue[path] = branch | ||||
|         else: | ||||
|             dirs[path] = dir | ||||
|  | ||||
|     # also find orphans | ||||
|     not_orphans = {frozenset(mdir.blocks) | ||||
|         for dir in dirs.values() | ||||
|         for mdir in dir} | ||||
|     orphans = [] | ||||
|     for pair, mdir in mdirs.items(): | ||||
|         if pair not in not_orphans: | ||||
|             if len(orphans) > 0 and (pair == frozenset( | ||||
|                     struct.unpack('<II', orphans[-1][-1].tail.data))): | ||||
|                 orphans[-1].append(mdir) | ||||
|             else: | ||||
|                 orphans.append([mdir]) | ||||
|  | ||||
|     # print littlefs + version info | ||||
|     version = ('?', '?') | ||||
|     if superblock: | ||||
|         version = tuple(reversed( | ||||
| @@ -187,24 +207,30 @@ def main(args): | ||||
|         "data (truncated, if it fits)" | ||||
|         if not any([args.no_truncate, args.tags, args.log, args.all]) else "")) | ||||
|  | ||||
|     if gstate: | ||||
|     # print gstate | ||||
|     print("gstate 0x%s" % ''.join('%02x' % c for c in gstate)) | ||||
|     tag = Tag(struct.unpack('<I', gstate[0:4].ljust(4, b'\xff'))[0]) | ||||
|     blocks = struct.unpack('<II', gstate[4:4+8].ljust(8, b'\xff')) | ||||
|     if tag.size or not tag.isvalid: | ||||
|         print("  orphans >=%d" % max(tag.size, 1)) | ||||
|     if tag.type: | ||||
|             print("  move dir {%#x, %#x} id %d" % ( | ||||
|                 blocks[0], blocks[1], tag.id)) | ||||
|         print("  move dir {%#x, %#x}%s id %d" % ( | ||||
|             blocks[0], blocks[1], | ||||
|             '?' if frozenset(blocks) not in mdirs else '', | ||||
|             tag.id)) | ||||
|  | ||||
|     for i, dir in enumerate(dirs): | ||||
|         print("dir %s" % (json.dumps(dir[0].path) | ||||
|             if hasattr(dir[0], 'path') else '(orphan)')) | ||||
|     # print dir info | ||||
|     for path, dir in it.chain( | ||||
|             sorted(dirs.items()), | ||||
|             zip(it.repeat(None), orphans)): | ||||
|         print("dir %s" % json.dumps(path) if path else "orphaned") | ||||
|  | ||||
|         for j, mdir in enumerate(dir): | ||||
|             print("mdir {%#x, %#x} rev %d%s" % ( | ||||
|                 mdir.blocks[0], mdir.blocks[1], mdir.rev, | ||||
|                 ' (corrupted)' if not mdir else '')) | ||||
|             print("mdir {%#x, %#x} rev %d (was %d)%s%s" % ( | ||||
|                 mdir.blocks[0], mdir.blocks[1], mdir.rev, mdir.pair[1].rev, | ||||
|                 ' (corrupted!)' if not mdir else '', | ||||
|                 ' -> {%#x, %#x}' % struct.unpack('<II', mdir.tail.data) | ||||
|                 if mdir.tail else '')) | ||||
|  | ||||
|             f = io.StringIO() | ||||
|             if args.tags: | ||||
| @@ -214,21 +240,27 @@ def main(args): | ||||
|             elif args.all: | ||||
|                 mdir.dump_all(f, truncate=not args.no_truncate) | ||||
|             else: | ||||
|                 dumpentries(args, mdir, f) | ||||
|                 dumpentries(args, mdir, mdirs, f) | ||||
|  | ||||
|             lines = list(filter(None, f.getvalue().split('\n'))) | ||||
|             for k, line in enumerate(lines): | ||||
|                 print("%s %s" % ( | ||||
|                     ' ' if i == len(dirs)-1 and j == len(dir)-1 else | ||||
|                     ' ' if j == len(dir)-1 else | ||||
|                     'v' if k == len(lines)-1 else | ||||
|                     '.' if j == len(dir)-1 else | ||||
|                     '|', | ||||
|                     '|' if path else '.', | ||||
|                     line)) | ||||
|  | ||||
|     if cycle: | ||||
|         print("*** cycle detected! -> {%#x, %#x} ***" % (cycle[0], cycle[1])) | ||||
|     for path, pair in rogue.items(): | ||||
|         print("*** couldn't find dir %s {%#x, %#x}! ***" % ( | ||||
|             json.dumps(path), pair[0], pair[1])) | ||||
|  | ||||
|     if cycle: | ||||
|         print("*** cycle detected {%#x, %#x}! ***" % ( | ||||
|             cycle[0], cycle[1])) | ||||
|  | ||||
|     if cycle: | ||||
|         return 3 | ||||
|     elif rogue: | ||||
|         return 2 | ||||
|     elif not all(mdir for dir in dirs for mdir in dir): | ||||
|         return 1 | ||||
|   | ||||
| @@ -231,7 +231,7 @@ class TestCase: | ||||
|                 ncmd.extend(['-ex', 'r']) | ||||
|                 if failure.assert_: | ||||
|                     ncmd.extend(['-ex', 'up 2']) | ||||
|             elif gdb == 'start': | ||||
|             elif gdb == 'start' or isinstance(gdb, int): | ||||
|                 ncmd.extend([ | ||||
|                     '-ex', 'b %s:%d' % (self.suite.path, self.code_lineno), | ||||
|                     '-ex', 'r']) | ||||
| @@ -329,7 +329,9 @@ class ReentrantTestCase(TestCase): | ||||
|                 persist = 'noerase' | ||||
|  | ||||
|             # exact cycle we should drop into debugger? | ||||
|             if gdb and failure and failure.cycleno == cycles: | ||||
|             if gdb and failure and ( | ||||
|                     failure.cycleno == cycles or | ||||
|                     (isinstance(gdb, int) and gdb == cycles)): | ||||
|                 return super().test(gdb=gdb, persist=persist, cycles=cycles, | ||||
|                     failure=failure, **args) | ||||
|  | ||||
| @@ -760,7 +762,8 @@ if __name__ == "__main__": | ||||
|         help="Store disk image in a file.") | ||||
|     parser.add_argument('-b', '--build', action='store_true', | ||||
|         help="Only build the tests, do not execute.") | ||||
|     parser.add_argument('-g', '--gdb', choices=['init', 'start', 'assert'], | ||||
|     parser.add_argument('-g', '--gdb', metavar='{init,start,assert},CYCLE', | ||||
|         type=lambda n: n if n in {'init', 'start', 'assert'} else int(n, 0), | ||||
|         nargs='?', const='assert', | ||||
|         help="Drop into gdb on test failure.") | ||||
|     parser.add_argument('--no-internal', action='store_true', | ||||
|   | ||||
		Reference in New Issue
	
	Block a user