mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	Switched back to simple deorphan-step on directory remove
Originally I tried to reuse the indirect delete to accomplish truely atomic directory removes, however this fell apart when it came to implementing directory removes as a side-effect of renames. A single indirect-delete simply can't handle renames with removes as a side effects. When copying an entry to its destination, we need to atomically delete both the old entry, and the source of our copy. We can't delete both with only a single indirect-delete. It is possible to accomplish this with two indirect-deletes, but this is such an uncommon case that it's really not worth supporting efficiently due to how expensive globals are. I also dropped indirect-deletes for normal directory removes. I may add it back later, but at the moment it's extra code cost for that's not traveled very often. As a result, restructured the indirect delete handling to be a bit more generic, now with a multipurpose lfs_globals_t struct instead of the delete specific lfs_entry_t struct. Also worked on integrating xored-globals, now with several primitive global operations to manage fetching/updating globals on disk.
This commit is contained in:
		
							
								
								
									
										364
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										364
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -267,6 +267,8 @@ static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2], | |||||||
| static int lfs_moved(lfs_t *lfs, lfs_mdir_t *fromdir, uint16_t fromid); | static int lfs_moved(lfs_t *lfs, lfs_mdir_t *fromdir, uint16_t fromid); | ||||||
| static int lfs_relocate(lfs_t *lfs, | static int lfs_relocate(lfs_t *lfs, | ||||||
|         const lfs_block_t oldpair[2], const lfs_block_t newpair[2]); |         const lfs_block_t oldpair[2], const lfs_block_t newpair[2]); | ||||||
|  | int lfs_scan(lfs_t *lfs); | ||||||
|  | int lfs_fixmove(lfs_t *lfs); | ||||||
| int lfs_deorphan(lfs_t *lfs); | int lfs_deorphan(lfs_t *lfs); | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -458,6 +460,22 @@ static inline lfs_size_t lfs_tag_size(lfs_tag_t tag) { | |||||||
|     return tag & 0x00000fff; |     return tag & 0x00000fff; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // operations on globals | ||||||
|  | static lfs_globals_t lfs_globals_xor( | ||||||
|  |         const lfs_globals_t *a, const lfs_globals_t *b) { | ||||||
|  |     lfs_globals_t res; | ||||||
|  |     res.move.pair[0] = a->move.pair[0] ^ b->move.pair[0]; | ||||||
|  |     res.move.pair[1] = a->move.pair[1] ^ b->move.pair[1]; | ||||||
|  |     res.move.id = a->move.id ^ b->move.id; | ||||||
|  |     return res; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static bool lfs_globals_iszero(const lfs_globals_t *a) { | ||||||
|  |     return (a->move.pair[0] == 0 && a->move.pair[1] == 0 && a->move.id == 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // commit logic | ||||||
| struct lfs_commit { | struct lfs_commit { | ||||||
|     lfs_block_t block; |     lfs_block_t block; | ||||||
|     lfs_off_t off; |     lfs_off_t off; | ||||||
| @@ -701,6 +719,23 @@ static int lfs_commit_move(lfs_t *lfs, struct lfs_commit *commit, | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int lfs_commit_globals(lfs_t *lfs, struct lfs_commit *commit, | ||||||
|  |         const lfs_globals_t *source, const lfs_globals_t *diff) { | ||||||
|  |     lfs_globals_t res = lfs_globals_xor(source, diff); | ||||||
|  |  | ||||||
|  |     if (!lfs_globals_iszero(&res)) { | ||||||
|  |         int err = lfs_commit_commit(lfs, commit, (lfs_mattr_t){ | ||||||
|  |                 lfs_mktag(LFS_TYPE_IDELETE, | ||||||
|  |                     res.move.id, sizeof(res.move.pair)), | ||||||
|  |                     .u.buffer=res.move.pair}); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir, | static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir, | ||||||
|         bool split, const lfs_block_t tail[2]) { |         bool split, const lfs_block_t tail[2]) { | ||||||
|     // allocate pair of dir blocks (backwards, so we write to block 1 first) |     // allocate pair of dir blocks (backwards, so we write to block 1 first) | ||||||
| @@ -727,7 +762,7 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir, | |||||||
|     dir->tail[1] = tail[1]; |     dir->tail[1] = tail[1]; | ||||||
|     dir->erased = false; |     dir->erased = false; | ||||||
|     dir->split = split; |     dir->split = split; | ||||||
|     dir->idelete = (lfs_entry_t){{0, 0}, 0}; |     dir->globals = (lfs_globals_t){0}; | ||||||
|  |  | ||||||
|     // don't write out yet, let caller take care of that |     // don't write out yet, let caller take care of that | ||||||
|     return 0; |     return 0; | ||||||
| @@ -764,7 +799,7 @@ static int lfs_dir_fetchwith(lfs_t *lfs, | |||||||
|         dir->tail[1] = 0xffffffff; |         dir->tail[1] = 0xffffffff; | ||||||
|         dir->count = 0; |         dir->count = 0; | ||||||
|         dir->split = false; |         dir->split = false; | ||||||
|         dir->idelete = (lfs_entry_t){{0, 0}, 0}; |         dir->globals = (lfs_globals_t){0}; | ||||||
|         dir->moveid = -1; |         dir->moveid = -1; | ||||||
|  |  | ||||||
|         dir->rev = lfs_tole32(rev[0]); |         dir->rev = lfs_tole32(rev[0]); | ||||||
| @@ -786,9 +821,12 @@ static int lfs_dir_fetchwith(lfs_t *lfs, | |||||||
|  |  | ||||||
|             // next commit not yet programmed |             // next commit not yet programmed | ||||||
|             if (lfs_tag_type(ptag) == LFS_TYPE_CRC && !lfs_tag_valid(tag)) { |             if (lfs_tag_type(ptag) == LFS_TYPE_CRC && !lfs_tag_valid(tag)) { | ||||||
|                 if (lfs_paircmp(dir->pair, lfs->idelete.pair) == 0 && cb) { |                 // synthetic move | ||||||
|  |                 if (lfs_paircmp(dir->pair, lfs->globals.move.pair) == 0 | ||||||
|  |                         && cb) { | ||||||
|                     int err = cb(lfs, data, (lfs_mattr_t){ |                     int err = cb(lfs, data, (lfs_mattr_t){ | ||||||
|                             lfs_mktag(LFS_STRUCT_DELETE, lfs->idelete.id, 0)}); |                             lfs_mktag(LFS_STRUCT_DELETE, | ||||||
|  |                                 lfs->globals.move.id, 0)}); | ||||||
|                     if (err) { |                     if (err) { | ||||||
|                         return err; |                         return err; | ||||||
|                     } |                     } | ||||||
| @@ -818,12 +856,13 @@ static int lfs_dir_fetchwith(lfs_t *lfs, | |||||||
|                         // try other block |                         // try other block | ||||||
|                         break; |                         break; | ||||||
|                     } else { |                     } else { | ||||||
|  |                         // snythetic move | ||||||
|                         // TODO combine with above? |                         // TODO combine with above? | ||||||
|                         if (lfs_paircmp(dir->pair, lfs->idelete.pair) == 0 |                         if (lfs_paircmp(dir->pair, lfs->globals.move.pair) == 0 | ||||||
|                                 && cb) { |                                 && cb) { | ||||||
|                             int err = cb(lfs, data, (lfs_mattr_t){ |                             int err = cb(lfs, data, (lfs_mattr_t){ | ||||||
|                                     lfs_mktag(LFS_STRUCT_DELETE, |                                     lfs_mktag(LFS_STRUCT_DELETE, | ||||||
|                                         lfs->idelete.id, 0)}); |                                         lfs->globals.move.id, 0)}); | ||||||
|                             if (err) { |                             if (err) { | ||||||
|                                 return err; |                                 return err; | ||||||
|                             } |                             } | ||||||
| @@ -855,8 +894,10 @@ static int lfs_dir_fetchwith(lfs_t *lfs, | |||||||
|                         return err; |                         return err; | ||||||
|                     } |                     } | ||||||
|                 } else if (lfs_tag_type(tag) == LFS_TYPE_IDELETE) { |                 } else if (lfs_tag_type(tag) == LFS_TYPE_IDELETE) { | ||||||
|  |                     temp.globals.move.id = lfs_tag_id(tag); | ||||||
|                     err = lfs_bd_read(lfs, temp.pair[0], off+sizeof(tag), |                     err = lfs_bd_read(lfs, temp.pair[0], off+sizeof(tag), | ||||||
|                             &temp.idelete, sizeof(temp.idelete)); |                             &temp.globals.move.pair, | ||||||
|  |                             sizeof(temp.globals.move.pair)); | ||||||
|                     if (err) { |                     if (err) { | ||||||
|                         return err; |                         return err; | ||||||
|                     } |                     } | ||||||
| @@ -864,7 +905,7 @@ static int lfs_dir_fetchwith(lfs_t *lfs, | |||||||
|                     // TODO handle moves correctly? |                     // TODO handle moves correctly? | ||||||
|                     temp.moveid = lfs_tag_id(tag); |                     temp.moveid = lfs_tag_id(tag); | ||||||
|                 } else { |                 } else { | ||||||
|                     if (lfs_tag_id(tag) < 0x3ff && |                     if (lfs_tag_scope(tag) <= LFS_SCOPE_ENTRY && | ||||||
|                             lfs_tag_id(tag) >= temp.count) { |                             lfs_tag_id(tag) >= temp.count) { | ||||||
|                         temp.count = lfs_tag_id(tag)+1; |                         temp.count = lfs_tag_id(tag)+1; | ||||||
|                     } |                     } | ||||||
| @@ -918,9 +959,10 @@ static int lfs_dir_traverse(lfs_t *lfs, lfs_mdir_t *dir, | |||||||
|     lfs_off_t off = dir->off; |     lfs_off_t off = dir->off; | ||||||
|     lfs_tag_t tag = dir->etag; |     lfs_tag_t tag = dir->etag; | ||||||
|  |  | ||||||
|     if (lfs_paircmp(dir->pair, lfs->idelete.pair) == 0) { |     // synthetic move | ||||||
|  |     if (lfs_paircmp(dir->pair, lfs->globals.move.pair) == 0) { | ||||||
|         int err = cb(lfs, data, (lfs_mattr_t){ |         int err = cb(lfs, data, (lfs_mattr_t){ | ||||||
|                 lfs_mktag(LFS_STRUCT_DELETE, lfs->idelete.id, 0)}); |                 lfs_mktag(LFS_STRUCT_DELETE, lfs->globals.move.id, 0)}); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
| @@ -1031,29 +1073,29 @@ static int lfs_dir_compact(lfs_t *lfs, lfs_mdir_t *dir, lfs_mattrlist_t *list, | |||||||
|                 ack = id; |                 ack = id; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             // reopen the reserved space at the end | ||||||
|  |             // TODO can I just commit these first? | ||||||
|             commit.end = lfs->cfg->block_size - 2*sizeof(uint32_t); |             commit.end = lfs->cfg->block_size - 2*sizeof(uint32_t); | ||||||
|             if (!lfs_pairisnull(dir->tail)) { |  | ||||||
|                 // TODO le32 |             if (!relocated) { | ||||||
|                 err = lfs_commit_commit(lfs, &commit, (lfs_mattr_t){ |                 err = lfs_commit_globals(lfs, &commit, | ||||||
|                         lfs_mktag(LFS_STRUCT_TAIL + dir->split*0x8, |                         &dir->globals, &lfs->diff); | ||||||
|                             0x3ff, sizeof(dir->tail)), |  | ||||||
|                         .u.buffer=dir->tail}); |  | ||||||
|                 if (err) { |                 if (err) { | ||||||
|                     if (err == LFS_ERR_CORRUPT) { |                     if (err == LFS_ERR_NOSPC) { | ||||||
|  |                         goto split; | ||||||
|  |                     } else if (err == LFS_ERR_CORRUPT) { | ||||||
|                         goto relocate; |                         goto relocate; | ||||||
|                     } |                     } | ||||||
|                     return err; |                     return err; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (!(dir->idelete.pair[0] == 0 && |             if (!lfs_pairisnull(dir->tail)) { | ||||||
|                   dir->idelete.pair[0] == 0 && |  | ||||||
|                   dir->idelete.id == 0)) { |  | ||||||
|                 // TODO le32 |                 // TODO le32 | ||||||
|                 err = lfs_commit_commit(lfs, &commit, (lfs_mattr_t){ |                 err = lfs_commit_commit(lfs, &commit, (lfs_mattr_t){ | ||||||
|                         lfs_mktag(LFS_TYPE_IDELETE, |                         lfs_mktag(LFS_STRUCT_TAIL + dir->split*0x8, | ||||||
|                             0x3ff, sizeof(dir->idelete)), |                             0x3ff, sizeof(dir->tail)), | ||||||
|                         .u.buffer=&dir->idelete}); |                         .u.buffer=dir->tail}); | ||||||
|                 if (err) { |                 if (err) { | ||||||
|                     if (err == LFS_ERR_CORRUPT) { |                     if (err == LFS_ERR_CORRUPT) { | ||||||
|                         goto relocate; |                         goto relocate; | ||||||
| @@ -1131,6 +1173,9 @@ relocate: | |||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |     } else { | ||||||
|  |         lfs->globals = lfs_globals_xor(&lfs->globals, &lfs->diff); | ||||||
|  |         lfs->diff = (lfs_globals_t){0}; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| @@ -1162,6 +1207,16 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, lfs_mattrlist_t *list) { | |||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         if (!lfs_globals_iszero(&lfs->diff)) { | ||||||
|  |             err = lfs_commit_globals(lfs, &commit, &dir->globals, &lfs->diff); | ||||||
|  |             if (err) { | ||||||
|  |                 if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { | ||||||
|  |                     goto compact; | ||||||
|  |                 } | ||||||
|  |                 return err; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         err = lfs_commit_crc(lfs, &commit); |         err = lfs_commit_crc(lfs, &commit); | ||||||
|         if (err) { |         if (err) { | ||||||
|             if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { |             if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { | ||||||
| @@ -1173,6 +1228,8 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, lfs_mattrlist_t *list) { | |||||||
|         // successful commit, lets update dir |         // successful commit, lets update dir | ||||||
|         dir->off = commit.off; |         dir->off = commit.off; | ||||||
|         dir->etag = commit.ptag; |         dir->etag = commit.ptag; | ||||||
|  |         lfs->globals = lfs_globals_xor(&lfs->globals, &lfs->diff); | ||||||
|  |         lfs->diff = (lfs_globals_t){0}; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
| compact: | compact: | ||||||
| @@ -1212,9 +1269,13 @@ static int lfs_dir_delete(lfs_t *lfs, lfs_mdir_t *dir, uint16_t id) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (res && pdir.split) { |         if (res && pdir.split) { | ||||||
|  |             // steal tail, and global state | ||||||
|             pdir.split = dir->split; |             pdir.split = dir->split; | ||||||
|             pdir.tail[0] = dir->tail[0]; |             pdir.tail[0] = dir->tail[0]; | ||||||
|             pdir.tail[1] = dir->tail[1]; |             pdir.tail[1] = dir->tail[1]; | ||||||
|  |             lfs->diff = dir->globals; | ||||||
|  |             lfs->globals = lfs_globals_xor(&lfs->globals, &dir->globals); | ||||||
|  |  | ||||||
|             int err = lfs_dir_commit(lfs, &pdir, &(lfs_mattrlist_t){ |             int err = lfs_dir_commit(lfs, &pdir, &(lfs_mattrlist_t){ | ||||||
|                     {lfs_mktag(LFS_STRUCT_TAIL + pdir.split*0x8, |                     {lfs_mktag(LFS_STRUCT_TAIL + pdir.split*0x8, | ||||||
|                         0x3ff, sizeof(pdir.tail)), |                         0x3ff, sizeof(pdir.tail)), | ||||||
| @@ -2661,17 +2722,50 @@ int lfs_remove(lfs_t *lfs, const char *path) { | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     lfs_mdir_t dir; | ||||||
|     if (lfs_tag_subtype(attr.tag) == LFS_TYPE_DIR) { |     if (lfs_tag_subtype(attr.tag) == LFS_TYPE_DIR) { | ||||||
|         lfs_mdir_t dir; |  | ||||||
|         // must be empty before removal |         // must be empty before removal | ||||||
|         err = lfs_dir_fetch(lfs, &dir, attr.u.pair); |         err = lfs_dir_fetch(lfs, &dir, attr.u.pair); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // TODO lfs_dir_empty? | ||||||
|         if (dir.count > 0 || dir.split) { |         if (dir.count > 0 || dir.split) { | ||||||
|             return LFS_ERR_NOTEMPTY; |             return LFS_ERR_NOTEMPTY; | ||||||
|         } |         } | ||||||
|  | // | ||||||
|  | //        // unlink from tail chain and create move to fix | ||||||
|  | //        lfs->diff.move.pair[0] = cwd.pair[0] ^ lfs->globals.move.pair[0]; | ||||||
|  | //        lfs->diff.move.pair[1] = cwd.pair[1] ^ lfs->globals.move.pair[1]; | ||||||
|  | //        lfs->diff.move.id      = id          ^ lfs->globals.move.id; | ||||||
|  | // | ||||||
|  | //        // xor over our child's global state | ||||||
|  | //        lfs->diff = lfs_globals_xor(&lfs->diff, &dir.globals); | ||||||
|  | //        lfs->globals = lfs_globals_xor(&lfs->globals, &dir.globals); | ||||||
|  | // | ||||||
|  | //        // find pred and remove | ||||||
|  | //        // TODO handle dropped block? | ||||||
|  | //        lfs_mdir_t pred; | ||||||
|  | //        int res = lfs_pred(lfs, dir.pair, &pred); | ||||||
|  | //        if (res < 0) { | ||||||
|  | //            return res; | ||||||
|  | //        } | ||||||
|  | // | ||||||
|  | //        LFS_ASSERT(res); // must have pred | ||||||
|  | //        pred.tail[0] = dir.tail[0]; | ||||||
|  | //        pred.tail[1] = dir.tail[1]; | ||||||
|  | //        err = lfs_dir_commit(lfs, &pred, &(lfs_mattrlist_t){ | ||||||
|  | //                {lfs_mktag(LFS_TYPE_SOFTTAIL, 0x3ff, sizeof(pred.tail)), | ||||||
|  | //                    .u.buffer=pred.tail}}); | ||||||
|  | //        if (err) { | ||||||
|  | //            return err; | ||||||
|  | //        } | ||||||
|  | // | ||||||
|  | //        // mark global state to clear move entry | ||||||
|  | //        lfs->diff.move.pair[0] = 0xffffffff ^ lfs->globals.move.pair[0]; | ||||||
|  | //        lfs->diff.move.pair[1] = 0xffffffff ^ lfs->globals.move.pair[1]; | ||||||
|  | //        lfs->diff.move.id      = 0x3ff      ^ lfs->globals.move.id; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // delete the entry |     // delete the entry | ||||||
| @@ -2680,31 +2774,28 @@ int lfs_remove(lfs_t *lfs, const char *path) { | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // if we were a directory, find pred, replace tail | //    // if we were a directory, fix the move we just created | ||||||
|     // TODO can this just deorphan? |  | ||||||
|     if (lfs_tag_subtype(attr.tag) == LFS_TYPE_DIR) { |  | ||||||
|         err = lfs_deorphan(lfs); |  | ||||||
|         if (err) { |  | ||||||
|             return err; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| //    if (lfs_tag_subtype(attr.tag) == LFS_TYPE_DIR) { | //    if (lfs_tag_subtype(attr.tag) == LFS_TYPE_DIR) { | ||||||
| //        int res = lfs_pred(lfs, dir.pair, &cwd); | //        err = lfs_deorphan(lfs); | ||||||
| //        if (res < 0) { |  | ||||||
| //            return res; |  | ||||||
| //        } |  | ||||||
| // |  | ||||||
| //        LFS_ASSERT(res); // must have pred |  | ||||||
| //        cwd.tail[0] = dir.tail[0]; |  | ||||||
| //        cwd.tail[1] = dir.tail[1]; |  | ||||||
| // |  | ||||||
| //        err = lfs_dir_commit(lfs, &cwd, NULL, 0); |  | ||||||
| //        if (err) { | //        if (err) { | ||||||
| //            return err; | //            return err; | ||||||
| //        } | //        } | ||||||
| //    } | //    } | ||||||
|  |  | ||||||
|  |     if (lfs_tag_subtype(attr.tag) == LFS_TYPE_DIR) { | ||||||
|  |         int res = lfs_pred(lfs, dir.pair, &cwd); | ||||||
|  |         if (res < 0) { | ||||||
|  |             return res; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         LFS_ASSERT(res); // must have pred | ||||||
|  |         cwd.tail[0] = dir.tail[0]; | ||||||
|  |         cwd.tail[1] = dir.tail[1]; | ||||||
|  |         err = lfs_dir_commit(lfs, &cwd, &(lfs_mattrlist_t){ | ||||||
|  |                 {lfs_mktag(LFS_TYPE_SOFTTAIL, 0x3ff, sizeof(cwd.tail)), | ||||||
|  |                     .u.buffer=cwd.tail}}); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -2741,8 +2832,10 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool prevexists = (err != LFS_ERR_NOENT); |     bool prevexists = (err != LFS_ERR_NOENT); | ||||||
|     bool samepair = (lfs_paircmp(oldcwd.pair, newcwd.pair) == 0); |     //bool samepair = (lfs_paircmp(oldcwd.pair, newcwd.pair) == 0); | ||||||
|  |  | ||||||
|     lfs_mattr_t prevattr; |     lfs_mattr_t prevattr; | ||||||
|  |     lfs_mdir_t prevdir; | ||||||
|     if (prevexists) { |     if (prevexists) { | ||||||
|         // get prev entry, check that we have same type |         // get prev entry, check that we have same type | ||||||
|         err = lfs_dir_getentry(lfs, &newcwd, 0x703ff000, |         err = lfs_dir_getentry(lfs, &newcwd, 0x703ff000, | ||||||
| @@ -2756,7 +2849,6 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (lfs_tag_subtype(prevattr.tag) == LFS_TYPE_DIR) { |         if (lfs_tag_subtype(prevattr.tag) == LFS_TYPE_DIR) { | ||||||
|             lfs_mdir_t prevdir; |  | ||||||
|             // must be empty before removal |             // must be empty before removal | ||||||
|             err = lfs_dir_fetch(lfs, &prevdir, prevattr.u.pair); |             err = lfs_dir_fetch(lfs, &prevdir, prevattr.u.pair); | ||||||
|             if (err) { |             if (err) { | ||||||
| @@ -2781,19 +2873,24 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // mark as moving |     // create move to fix later | ||||||
|     //printf("RENAME MOVE %d %d %d\n", oldcwd.pair[0], oldcwd.pair[1], oldid); |     lfs->diff.move.pair[0] = oldcwd.pair[0] ^ lfs->globals.move.pair[0]; | ||||||
|     err = lfs_dir_commit(lfs, &oldcwd, &(lfs_mattrlist_t){ |     lfs->diff.move.pair[1] = oldcwd.pair[1] ^ lfs->globals.move.pair[1]; | ||||||
|             {lfs_mktag(LFS_STRUCT_MOVE, oldid, 0)}}); |     lfs->diff.move.id      = oldid          ^ lfs->globals.move.id; | ||||||
|     if (err) { |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (samepair) { |  | ||||||
|         // update pair if newcwd == oldcwd |  | ||||||
|         newcwd = oldcwd; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  | //    // mark as moving | ||||||
|  | //    //printf("RENAME MOVE %d %d %d\n", oldcwd.pair[0], oldcwd.pair[1], oldid); | ||||||
|  | //    err = lfs_dir_commit(lfs, &oldcwd, &(lfs_mattrlist_t){ | ||||||
|  | //            {lfs_mktag(LFS_STRUCT_MOVE, oldid, 0)}}); | ||||||
|  | //    if (err) { | ||||||
|  | //        return err; | ||||||
|  | //    } | ||||||
|  | // | ||||||
|  | //    if (samepair) { | ||||||
|  | //        // update pair if newcwd == oldcwd | ||||||
|  | //        newcwd = oldcwd; | ||||||
|  | //    } | ||||||
|  | // | ||||||
| // TODO check that all complaints are fixed | // TODO check that all complaints are fixed | ||||||
| //    // move to new location | //    // move to new location | ||||||
| //    // TODO NAME????? | //    // TODO NAME????? | ||||||
| @@ -2813,6 +2910,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | |||||||
| //        return err; | //        return err; | ||||||
| //    } | //    } | ||||||
|  |  | ||||||
|  |     // move over all attributes | ||||||
|     err = lfs_dir_commit(lfs, &newcwd, &(lfs_mattrlist_t){ |     err = lfs_dir_commit(lfs, &newcwd, &(lfs_mattrlist_t){ | ||||||
|             {lfs_mktag(LFS_STRUCT_NAME | lfs_tag_subtype(oldattr.tag), |             {lfs_mktag(LFS_STRUCT_NAME | lfs_tag_subtype(oldattr.tag), | ||||||
|                 newid, strlen(newpath)), |                 newid, strlen(newpath)), | ||||||
| @@ -2823,28 +2921,53 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (samepair) { |     // clean up after ourselves | ||||||
|         // update pair if newcwd == oldcwd |     err = lfs_fixmove(lfs); | ||||||
|         oldcwd = newcwd; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // remove old entry |  | ||||||
|     //printf("RENAME DELETE %d %d %d\n", oldcwd.pair[0], oldcwd.pair[1], oldid); |  | ||||||
|     err = lfs_dir_delete(lfs, &oldcwd, oldid); |  | ||||||
|     if (err) { |     if (err) { | ||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // if we were a directory, find pred, replace tail |  | ||||||
|     // TODO can this just deorphan? |  | ||||||
|     if (prevexists && lfs_tag_subtype(prevattr.tag) == LFS_TYPE_DIR) { |     if (prevexists && lfs_tag_subtype(prevattr.tag) == LFS_TYPE_DIR) { | ||||||
|         err = lfs_deorphan(lfs); |         int res = lfs_pred(lfs, prevdir.pair, &newcwd); | ||||||
|         if (err) { |         if (res < 0) { | ||||||
|             return err; |             return res; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         LFS_ASSERT(res); // must have pred | ||||||
|  |         newcwd.tail[0] = prevdir.tail[0]; | ||||||
|  |         newcwd.tail[1] = prevdir.tail[1]; | ||||||
|  |         err = lfs_dir_commit(lfs, &newcwd, &(lfs_mattrlist_t){ | ||||||
|  |                 {lfs_mktag(LFS_TYPE_SOFTTAIL, 0x3ff, sizeof(newcwd.tail)), | ||||||
|  |                     .u.buffer=newcwd.tail}}); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|  |      | ||||||
|  |  | ||||||
|  | //    if (samepair) { | ||||||
|  | //        // update pair if newcwd == oldcwd | ||||||
|  | //        oldcwd = newcwd; | ||||||
|  | //    } | ||||||
|  | // | ||||||
|  | //    err = fix | ||||||
|  | // | ||||||
|  | //    // remove old entry | ||||||
|  | //    //printf("RENAME DELETE %d %d %d\n", oldcwd.pair[0], oldcwd.pair[1], oldid); | ||||||
|  | //    err = lfs_dir_delete(lfs, &oldcwd, oldid); | ||||||
|  | //    if (err) { | ||||||
|  | //        return err; | ||||||
|  | //    } | ||||||
|  | // | ||||||
|  | //    // if we were a directory, find pred, replace tail | ||||||
|  | //    // TODO can this just deorphan? | ||||||
|  | //    if (prevexists && lfs_tag_subtype(prevattr.tag) == LFS_TYPE_DIR) { | ||||||
|  | //        err = lfs_deorphan(lfs); | ||||||
|  | //        if (err) { | ||||||
|  | //            return err; | ||||||
|  | //        } | ||||||
|  | //    } | ||||||
|  | // | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| //int lfs_getattrs(lfs_t *lfs, const char *path, | //int lfs_getattrs(lfs_t *lfs, const char *path, | ||||||
| @@ -2954,7 +3077,17 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|     lfs->files = NULL; |     lfs->files = NULL; | ||||||
|     lfs->dirs = NULL; |     lfs->dirs = NULL; | ||||||
|     lfs->deorphaned = false; |     lfs->deorphaned = false; | ||||||
|     lfs->idelete = (lfs_entry_t){{0xffffffff, 0xffffffff}, 0xffff}; |     lfs->globals.move.pair[0] = 0xffffffff; | ||||||
|  |     lfs->globals.move.pair[1] = 0xffffffff; | ||||||
|  |     lfs->globals.move.id = 0x3ff; | ||||||
|  |     lfs->diff = (lfs_globals_t){0}; | ||||||
|  |  | ||||||
|  |     // scan for any global updates | ||||||
|  |     // TODO rm me? need to grab any inits | ||||||
|  |     int err = lfs_scan(lfs); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| @@ -3122,6 +3255,11 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|     lfs->root[0] = superblock.root[0]; |     lfs->root[0] = superblock.root[0]; | ||||||
|     lfs->root[1] = superblock.root[1]; |     lfs->root[1] = superblock.root[1]; | ||||||
|  |  | ||||||
|  |     err = lfs_scan(lfs); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -3601,15 +3739,13 @@ static int lfs_relocate(lfs_t *lfs, | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int lfs_deorphan(lfs_t *lfs) { | int lfs_scan(lfs_t *lfs) { | ||||||
|     lfs->deorphaned = true; |     if (lfs_pairisnull(lfs->root)) { // TODO rm me | ||||||
|     if (lfs_pairisnull(lfs->root)) { |  | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     lfs_mdir_t pdir = {.split = true}; |  | ||||||
|     lfs_mdir_t dir = {.tail = {0, 1}}; |     lfs_mdir_t dir = {.tail = {0, 1}}; | ||||||
|     lfs_entry_t idelete = {{0xffffffff, 0xffffffff}, 0xffff}; |     lfs_globals_t globals = {{{0xffffffff, 0xffffffff}, 0x3ff}}; | ||||||
|  |  | ||||||
|     // iterate over all directory directory entries |     // iterate over all directory directory entries | ||||||
|     while (!lfs_pairisnull(dir.tail)) { |     while (!lfs_pairisnull(dir.tail)) { | ||||||
| @@ -3619,9 +3755,71 @@ int lfs_deorphan(lfs_t *lfs) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // xor together indirect deletes |         // xor together indirect deletes | ||||||
|         idelete.pair[0] ^= dir.idelete.pair[0]; |         globals = lfs_globals_xor(&globals, &dir.globals); | ||||||
|         idelete.pair[1] ^= dir.idelete.pair[1]; |     } | ||||||
|         idelete.id      ^= dir.idelete.id; |  | ||||||
|  |     // update littlefs with globals | ||||||
|  |     lfs->globals = globals; | ||||||
|  |     lfs->diff = (lfs_globals_t){0}; | ||||||
|  |     if (!lfs_pairisnull(lfs->globals.move.pair)) { | ||||||
|  |         LFS_DEBUG("Found move %d %d %d", | ||||||
|  |                 lfs->globals.move.pair[0], | ||||||
|  |                 lfs->globals.move.pair[1], | ||||||
|  |                 lfs->globals.move.id); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs_fixmove(lfs_t *lfs) { | ||||||
|  |     LFS_DEBUG("Fixing move %d %d %d", // TODO move to just deorphan | ||||||
|  |             lfs->globals.move.pair[0], | ||||||
|  |             lfs->globals.move.pair[1], | ||||||
|  |             lfs->globals.move.id); | ||||||
|  |  | ||||||
|  |     // mark global state to clear move entry | ||||||
|  |     lfs->diff.move.pair[0] = 0xffffffff ^ lfs->globals.move.pair[0]; | ||||||
|  |     lfs->diff.move.pair[1] = 0xffffffff ^ lfs->globals.move.pair[1]; | ||||||
|  |     lfs->diff.move.id      = 0x3ff      ^ lfs->globals.move.id; | ||||||
|  |  | ||||||
|  |     // fetch and delete the moved entry | ||||||
|  |     lfs_mdir_t movedir; | ||||||
|  |     int err = lfs_dir_fetch(lfs, &movedir, lfs->globals.move.pair); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     err = lfs_dir_delete(lfs, &movedir, lfs->globals.move.id); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs_deorphan(lfs_t *lfs) { | ||||||
|  |     lfs->deorphaned = true; | ||||||
|  |     if (lfs_pairisnull(lfs->root)) { // TODO rm me? | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Fix bad moves | ||||||
|  |     if (!lfs_pairisnull(lfs->globals.move.pair)) { | ||||||
|  |         int err = lfs_fixmove(lfs); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs_mdir_t pdir = {.split = true}; | ||||||
|  |     lfs_mdir_t dir = {.tail = {0, 1}}; | ||||||
|  |  | ||||||
|  |     // iterate over all directory directory entries | ||||||
|  |     while (!lfs_pairisnull(dir.tail)) { | ||||||
|  |         int err = lfs_dir_fetch(lfs, &dir, dir.tail); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         // check head blocks for orphans |         // check head blocks for orphans | ||||||
|         if (!pdir.split) { |         if (!pdir.split) { | ||||||
| @@ -3671,7 +3869,7 @@ int lfs_deorphan(lfs_t *lfs) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // check entries for moves |         // check entries for moves | ||||||
|         if (dir.moveid >= 0) { |         //if (dir.moveid >= 0) { | ||||||
| // TODO moves and stuff | // TODO moves and stuff | ||||||
|                     // TODO need to load entry to find it |                     // TODO need to load entry to find it | ||||||
| //                    // found moved entry | //                    // found moved entry | ||||||
| @@ -3698,15 +3896,11 @@ int lfs_deorphan(lfs_t *lfs) { | |||||||
| //                            return err; | //                            return err; | ||||||
| //                        } | //                        } | ||||||
| //                    } | //                    } | ||||||
|         } |         //} | ||||||
|  |  | ||||||
|         memcpy(&pdir, &dir, sizeof(pdir)); |         memcpy(&pdir, &dir, sizeof(pdir)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // update littlefs with current move |  | ||||||
|     // TODO do this here? needs to be before reads also |  | ||||||
|     lfs->idelete = idelete; |  | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| /* | /* | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -129,7 +129,7 @@ enum lfs_type { | |||||||
|     // internal sources |     // internal sources | ||||||
|     LFS_FROM_REGION     = 0x000, |     LFS_FROM_REGION     = 0x000, | ||||||
|     LFS_FROM_DISK       = 0x200, |     LFS_FROM_DISK       = 0x200, | ||||||
|     LFS_FROM_MOVE       = 0x004, |     LFS_FROM_MOVE       = 0x0ff, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // File open flags | // File open flags | ||||||
| @@ -296,10 +296,17 @@ typedef struct lfs_mattrlist { | |||||||
|     struct lfs_mattrlist *next; |     struct lfs_mattrlist *next; | ||||||
| } lfs_mattrlist_t; | } lfs_mattrlist_t; | ||||||
|  |  | ||||||
| typedef struct lfs_entry { | //typedef struct lfs_entry { | ||||||
|     lfs_block_t pair[2]; | //    lfs_block_t pair[2]; | ||||||
|     uint16_t id; | //    uint16_t id; | ||||||
| } lfs_entry_t; | //} lfs_entry_t; | ||||||
|  |  | ||||||
|  | typedef struct lfs_globals { | ||||||
|  |     struct lfs_move { | ||||||
|  |         lfs_block_t pair[2]; | ||||||
|  |         uint16_t id; | ||||||
|  |     } move; | ||||||
|  | } lfs_globals_t; | ||||||
|  |  | ||||||
| typedef struct lfs_mdir { | typedef struct lfs_mdir { | ||||||
|     lfs_block_t pair[2]; |     lfs_block_t pair[2]; | ||||||
| @@ -310,7 +317,7 @@ typedef struct lfs_mdir { | |||||||
|     uint16_t count; |     uint16_t count; | ||||||
|     bool erased; |     bool erased; | ||||||
|     bool split; |     bool split; | ||||||
|     lfs_entry_t idelete; |     lfs_globals_t globals; | ||||||
|     bool stop_at_commit; // TODO hmmm |     bool stop_at_commit; // TODO hmmm | ||||||
|     uint16_t moveid; // TODO rm me |     uint16_t moveid; // TODO rm me | ||||||
| } lfs_mdir_t; | } lfs_mdir_t; | ||||||
| @@ -380,8 +387,8 @@ typedef struct lfs { | |||||||
|  |  | ||||||
|     lfs_free_t free; |     lfs_free_t free; | ||||||
|     bool deorphaned; |     bool deorphaned; | ||||||
|     lfs_entry_t idelete; |     lfs_globals_t globals; | ||||||
|     lfs_entry_t diff; |     lfs_globals_t diff; | ||||||
|  |  | ||||||
|     lfs_size_t inline_size; |     lfs_size_t inline_size; | ||||||
|     lfs_size_t attrs_size; |     lfs_size_t attrs_size; | ||||||
|   | |||||||
| @@ -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 | ||||||
| truncate -s-7 blocks/6 | truncate -s-11 blocks/6 | ||||||
| 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 | ||||||
| truncate -s-7 blocks/8 | truncate -s-11 blocks/8 | ||||||
| truncate -s-7 blocks/a | truncate -s-11 blocks/a | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|     lfs_dir_open(&lfs, &dir[0], "c") => 0; |     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||||
| @@ -108,6 +108,32 @@ tests/test.py << TEST | |||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
| TEST | TEST | ||||||
|  |  | ||||||
|  | echo "--- Move file after corrupt ---" | ||||||
|  | tests/test.py -s << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_rename(&lfs, "c/hello", "d/hello") => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "d") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "hello") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
| echo "--- Move dir ---" | echo "--- Move dir ---" | ||||||
| tests/test.py << TEST | tests/test.py << TEST | ||||||
|     lfs_mount(&lfs, &cfg) => 0; |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user