mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 16:14:16 +01:00 
			
		
		
		
	Fixed move handling when caught in update
This commit is contained in:
		
							
								
								
									
										282
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										282
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -430,7 +430,7 @@ static inline bool lfs_pairsync( | ||||
|     (((uint32_t)(type) << 22) | ((uint32_t)(id) << 12) | (uint32_t)(size)) | ||||
|  | ||||
| #define LFS_MKATTR(type, id, buffer, size, next) \ | ||||
|     &(lfs_mattr_t){(next), LFS_MKTAG(type, id, size), (buffer)} | ||||
|     &(const lfs_mattr_t){(next), LFS_MKTAG(type, id, size), (buffer)} | ||||
|  | ||||
| static inline bool lfs_tagisvalid(uint32_t tag) { | ||||
|     return !(tag & 0x80000000); | ||||
| @@ -485,7 +485,7 @@ struct lfs_diskoff { | ||||
| }; | ||||
|  | ||||
| static int32_t lfs_commitget(lfs_t *lfs, lfs_block_t block, lfs_off_t off, | ||||
|         uint32_t tag, uint32_t getmask, uint32_t gettag, int32_t difftag, | ||||
|         uint32_t tag, uint32_t getmask, uint32_t gettag, int32_t getdiff, | ||||
|         void *buffer, bool stopatcommit) { | ||||
|     // iterate over dir block backwards (for faster lookups) | ||||
|     while (off > sizeof(tag)) { | ||||
| @@ -495,10 +495,10 @@ static int32_t lfs_commitget(lfs_t *lfs, lfs_block_t block, lfs_off_t off, | ||||
|         if (lfs_tagtype(tag) == LFS_TYPE_CRC && stopatcommit) { | ||||
|             break; | ||||
|         } else if (lfs_tagtype(tag) == LFS_TYPE_DELETE) { | ||||
|             if (lfs_tagid(tag) <= lfs_tagid(gettag + difftag)) { | ||||
|                 difftag += LFS_MKTAG(0, 1, 0); | ||||
|             if (lfs_tagid(tag) <= lfs_tagid(gettag + getdiff)) { | ||||
|                 getdiff += LFS_MKTAG(0, 1, 0); | ||||
|             } | ||||
|         } else if ((tag & getmask) == ((gettag + difftag) & getmask)) { | ||||
|         } else if ((tag & getmask) == ((gettag + getdiff) & getmask)) { | ||||
|             if (buffer) { | ||||
|                 lfs_size_t diff = lfs_min( | ||||
|                         lfs_tagsize(gettag), lfs_tagsize(tag)); | ||||
| @@ -512,7 +512,7 @@ static int32_t lfs_commitget(lfs_t *lfs, lfs_block_t block, lfs_off_t off, | ||||
|                         lfs_tagsize(gettag) - diff); | ||||
|             } | ||||
|  | ||||
|             return tag - difftag; | ||||
|             return tag - getdiff; | ||||
|         } | ||||
|  | ||||
|         uint32_t ntag; | ||||
| @@ -926,14 +926,67 @@ relocate: | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int lfs_dir_commit(lfs_t *lfs, | ||||
|         lfs_mdir_t *dir, const lfs_mattr_t *attrs) { | ||||
|     while (true) { | ||||
|         if (!dir->erased) { | ||||
|             // not erased, must compact | ||||
|             goto compact; | ||||
| static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, | ||||
|         const lfs_mattr_t *attrs) { | ||||
|     bool canceling = (lfs_paircmp(dir->pair, lfs->globals.move.pair) == 0); | ||||
|     lfs_mattr_t cancel; | ||||
|     if (canceling) { | ||||
|         // Wait, we have the move? Just cancel this out here | ||||
|         // We need to, or else the move can become outdated | ||||
|         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; | ||||
|  | ||||
|         cancel.tag = LFS_MKTAG(LFS_TYPE_DELETE, lfs->globals.move.id, 0); | ||||
|         cancel.next = attrs; | ||||
|         attrs = &cancel; | ||||
|     } | ||||
|  | ||||
|     // calculate new directory size | ||||
|     uint32_t deletetag = 0xffffffff; | ||||
|     for (const lfs_mattr_t *a = attrs; a; a = a->next) { | ||||
|         if (lfs_tagid(a->tag) < 0x3ff && lfs_tagid(a->tag) >= dir->count) { | ||||
|             dir->count = lfs_tagid(a->tag)+1; | ||||
|         } | ||||
|  | ||||
|         if (lfs_tagtype(a->tag) == LFS_TYPE_DELETE) { | ||||
|             LFS_ASSERT(dir->count > 0); | ||||
|             dir->count -= 1; | ||||
|             deletetag = a->tag; | ||||
|  | ||||
|             if (dir->count == 0) { | ||||
|                 // should we actually drop the directory block? | ||||
|                 lfs_mdir_t pdir; | ||||
|                 int err = lfs_pred(lfs, dir->pair, &pdir); | ||||
|                 if (err && err != LFS_ERR_NOENT) { | ||||
|                     return err; | ||||
|                 } | ||||
|  | ||||
|                 if (err != LFS_ERR_NOENT && pdir.split) { | ||||
|                     // steal tail and global state | ||||
|                     pdir.split = dir->split; | ||||
|                     pdir.tail[0] = dir->tail[0]; | ||||
|                     pdir.tail[1] = dir->tail[1]; | ||||
|                     lfs_globalsxor(&lfs->diff, &dir->locals); | ||||
|                     return lfs_dir_commit(lfs, &pdir, | ||||
|                             LFS_MKATTR(LFS_TYPE_TAIL + pdir.split, 0x3ff, | ||||
|                                 pdir.tail, sizeof(pdir.tail), | ||||
|                             NULL)); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (!dir->erased) { | ||||
| compact: | ||||
|         // fall back to compaction | ||||
|         lfs->pcache.block = 0xffffffff; | ||||
|         int err = lfs_dir_compact(lfs, dir, attrs, dir, 0, dir->count); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|     } else { | ||||
|         // try to commit | ||||
|         struct lfs_commit commit = { | ||||
|             .block = dir->pair[0], | ||||
|             .off = dir->off, | ||||
| @@ -945,7 +998,20 @@ static int lfs_dir_commit(lfs_t *lfs, | ||||
|         }; | ||||
|  | ||||
|         for (const lfs_mattr_t *a = attrs; a; a = a->next) { | ||||
|             int err = lfs_commitattr(lfs, &commit, a->tag, a->buffer); | ||||
|             if (lfs_tagtype(a->tag) != LFS_TYPE_DELETE) { | ||||
|                 int err = lfs_commitattr(lfs, &commit, a->tag, a->buffer); | ||||
|                 if (err) { | ||||
|                     if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { | ||||
|                         goto compact; | ||||
|                     } | ||||
|                     return err; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (lfs_tagisvalid(deletetag)) { | ||||
|             // special case for deletes, since order matters | ||||
|             int err = lfs_commitattr(lfs, &commit, deletetag, NULL); | ||||
|             if (err) { | ||||
|                 if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { | ||||
|                     goto compact; | ||||
| @@ -976,15 +1042,13 @@ static int lfs_dir_commit(lfs_t *lfs, | ||||
|         // successful commit, update globals | ||||
|         lfs_globalsxor(&dir->locals, &lfs->diff); | ||||
|         lfs->diff = (lfs_globals_t){0}; | ||||
|         break; | ||||
|     } | ||||
|  | ||||
| compact: | ||||
|         lfs->pcache.block = 0xffffffff; | ||||
|         err = lfs_dir_compact(lfs, dir, attrs, dir, 0, dir->count); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|         break; | ||||
|     // update globals that are affected | ||||
|     if (canceling) { | ||||
|         lfs->globals.move.pair[0] = 0xffffffff; | ||||
|         lfs->globals.move.pair[1] = 0xffffffff; | ||||
|         lfs->globals.move.id = 0x3ff; | ||||
|     } | ||||
|  | ||||
|     // update any directories that are affected | ||||
| @@ -992,10 +1056,24 @@ compact: | ||||
|     for (lfs_dir_t *d = lfs->dirs; d; d = d->next) { | ||||
|         if (lfs_paircmp(d->m.pair, dir->pair) == 0) { | ||||
|             d->m = *dir; | ||||
|             if (d->id > lfs_tagid(deletetag)) { | ||||
|                 d->id -= 1; | ||||
|                 d->pos -= 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     for (lfs_file_t *f = lfs->files; f; f = f->next) { | ||||
|         if (lfs_paircmp(f->pair, dir->pair) == 0) { | ||||
|             if (f->id == lfs_tagid(deletetag)) { | ||||
|                 f->pair[0] = 0xffffffff; | ||||
|                 f->pair[1] = 0xffffffff; | ||||
|             } else if (f->id > lfs_tagid(deletetag)) { | ||||
|                 f->id -= 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // TODO what if we relocated the block containing the move? | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -1114,6 +1192,7 @@ static int32_t lfs_dir_find(lfs_t *lfs, | ||||
|                         return err; | ||||
|                     } | ||||
|                 } else if (lfs_tagtype(tag) == LFS_TYPE_DELETE) { | ||||
|                     LFS_ASSERT(tempdir.count > 0); | ||||
|                     tempdir.count -= 1; | ||||
|  | ||||
|                     if (lfs_tagid(tag) == lfs_tagid(tempfoundtag)) { | ||||
| @@ -1174,77 +1253,15 @@ static int lfs_dir_fetch(lfs_t *lfs, | ||||
|  | ||||
| static int32_t lfs_dir_get(lfs_t *lfs, lfs_mdir_t *dir, | ||||
|         uint32_t getmask, uint32_t gettag, void *buffer) { | ||||
|     int32_t difftag = 0; | ||||
|     int32_t getdiff = 0; | ||||
|     if (lfs_paircmp(dir->pair, lfs->globals.move.pair) == 0 && | ||||
|             lfs_tagid(gettag) <= lfs->globals.move.id) { | ||||
|         // synthetic moves | ||||
|         difftag = LFS_MKTAG(0, 1, 0); | ||||
|         getdiff = LFS_MKTAG(0, 1, 0); | ||||
|     } | ||||
|  | ||||
|     return lfs_commitget(lfs, dir->pair[0], dir->off, dir->etag, | ||||
|             getmask, gettag, difftag, buffer, false); | ||||
| } | ||||
|  | ||||
| static int lfs_dir_append(lfs_t *lfs, lfs_mdir_t *dir, uint16_t *id) { | ||||
|     *id = dir->count; | ||||
|     dir->count += 1; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int lfs_dir_delete(lfs_t *lfs, lfs_mdir_t *dir, uint16_t id) { | ||||
|     dir->count -= 1; | ||||
|  | ||||
|     // check if we should drop the directory block | ||||
|     if (dir->count == 0) { | ||||
|         lfs_mdir_t pdir; | ||||
|         int err = lfs_pred(lfs, dir->pair, &pdir); | ||||
|         if (err && err != LFS_ERR_NOENT) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         if (err != LFS_ERR_NOENT && pdir.split) { | ||||
|             // steal tail and global state | ||||
|             pdir.split = dir->split; | ||||
|             pdir.tail[0] = dir->tail[0]; | ||||
|             pdir.tail[1] = dir->tail[1]; | ||||
|             lfs_globalsxor(&lfs->diff, &dir->locals); | ||||
|  | ||||
|             return lfs_dir_commit(lfs, &pdir, | ||||
|                     LFS_MKATTR(LFS_TYPE_TAIL + pdir.split, 0x3ff, | ||||
|                         pdir.tail, sizeof(pdir.tail), | ||||
|                     NULL)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     int err = lfs_dir_commit(lfs, dir, | ||||
|             LFS_MKATTR(LFS_TYPE_DELETE, id, NULL, 0, | ||||
|             NULL)); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // shift over any dirs/files that are affected | ||||
|     for (lfs_dir_t *d = lfs->dirs; d; d = d->next) { | ||||
|         if (lfs_paircmp(d->m.pair, dir->pair) == 0) { | ||||
|             if (d->id > id) { | ||||
|                 d->id -= 1; | ||||
|                 d->pos -= 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     for (lfs_file_t *f = lfs->files; f; f = f->next) { | ||||
|         if (lfs_paircmp(f->pair, dir->pair) == 0) { | ||||
|             if (f->id == id) { | ||||
|                 f->pair[0] = 0xffffffff; | ||||
|                 f->pair[1] = 0xffffffff; | ||||
|             } else if (f->id > id) { | ||||
|                 f->id -= 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
|             getmask, gettag, getdiff, buffer, false); | ||||
| } | ||||
|  | ||||
| static int32_t lfs_dir_lookup(lfs_t *lfs, lfs_mdir_t *dir, const char **path) { | ||||
| @@ -1411,12 +1428,7 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { | ||||
|     } | ||||
|  | ||||
|     // get next slot and commit | ||||
|     uint16_t id; | ||||
|     err = lfs_dir_append(lfs, &cwd, &id); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     uint16_t id = cwd.count; | ||||
|     cwd.tail[0] = dir.pair[0]; | ||||
|     cwd.tail[1] = dir.pair[1]; | ||||
|     err = lfs_dir_commit(lfs, &cwd, | ||||
| @@ -1802,15 +1814,10 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||
|         } | ||||
|  | ||||
|         // get next slot and create entry to remember name | ||||
|         uint16_t id; | ||||
|         int err = lfs_dir_append(lfs, &cwd, &id); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         // TODO do we need to make file registered to list to catch updates from this commit? ie if id/cwd change | ||||
|         // TODO don't use inline struct? just leave it out? | ||||
|         err = lfs_dir_commit(lfs, &cwd, | ||||
|         uint16_t id = cwd.count; | ||||
|         int err = lfs_dir_commit(lfs, &cwd, | ||||
|                 LFS_MKATTR(LFS_TYPE_REG, id, path, nlen, | ||||
|                 LFS_MKATTR(LFS_TYPE_INLINESTRUCT, id, NULL, 0, | ||||
|                 NULL))); | ||||
| @@ -2056,7 +2063,7 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { | ||||
|         if (!(file->flags & LFS_F_INLINE)) { | ||||
|             int err = lfs_dir_commit(lfs, &cwd, | ||||
|                     LFS_MKATTR(LFS_TYPE_CTZSTRUCT, file->id, | ||||
|                             &file->ctz.head, 2*sizeof(uint32_t), | ||||
|                         &file->ctz.head, sizeof(file->ctz), | ||||
|                     file->attrs)); | ||||
|             if (err) { | ||||
|                 return err; | ||||
| @@ -2505,7 +2512,9 @@ int lfs_remove(lfs_t *lfs, const char *path) { | ||||
|     } | ||||
|  | ||||
|     // delete the entry | ||||
|     err = lfs_dir_delete(lfs, &cwd, lfs_tagid(tag)); | ||||
|     err = lfs_dir_commit(lfs, &cwd, | ||||
|             LFS_MKATTR(LFS_TYPE_DELETE, lfs_tagid(tag), NULL, 0, | ||||
|             NULL)); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
| @@ -2516,8 +2525,11 @@ int lfs_remove(lfs_t *lfs, const char *path) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         // steal state | ||||
|         // TODO test for global state stealing? | ||||
|         cwd.tail[0] = dir.tail[0]; | ||||
|         cwd.tail[1] = dir.tail[1]; | ||||
|         lfs_globalsxor(&lfs->diff, &dir.locals); | ||||
|         err = lfs_dir_commit(lfs, &cwd, | ||||
|                 LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, | ||||
|                     cwd.tail, sizeof(cwd.tail), | ||||
| @@ -2591,10 +2603,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|         } | ||||
|  | ||||
|         // get next id | ||||
|         int err = lfs_dir_append(lfs, &newcwd, &newid); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|         newid = newcwd.count; | ||||
|     } | ||||
|  | ||||
|     // create move to fix later | ||||
| @@ -2614,10 +2623,13 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // clean up after ourselves | ||||
|     err = lfs_fixmove(lfs); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     // let commit clean up after move (if we're different! otherwise move | ||||
|     // logic already fixed it for us) | ||||
|     if (lfs_paircmp(oldcwd.pair, newcwd.pair) != 0) { | ||||
|         err = lfs_dir_commit(lfs, &oldcwd, NULL); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (prevtag != LFS_ERR_NOENT && lfs_tagtype(prevtag) == LFS_TYPE_DIR) { | ||||
| @@ -2626,12 +2638,11 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         // steal global state | ||||
|         // steal state | ||||
|         // TODO test for global state stealing? | ||||
|         lfs_globalsxor(&lfs->diff, &prevdir.locals); | ||||
|  | ||||
|         newcwd.tail[0] = prevdir.tail[0]; | ||||
|         newcwd.tail[1] = prevdir.tail[1]; | ||||
|         lfs_globalsxor(&lfs->diff, &prevdir.locals); | ||||
|         err = lfs_dir_commit(lfs, &newcwd, | ||||
|                 LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, | ||||
|                     newcwd.tail, sizeof(newcwd.tail), | ||||
| @@ -2859,7 +2870,6 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|         .name_size   = lfs->name_size, | ||||
|     }; | ||||
|  | ||||
|     dir.count += 1; | ||||
|     err = lfs_dir_commit(lfs, &dir, | ||||
|             LFS_MKATTR(LFS_TYPE_SUPERBLOCK, 0, &superblock, sizeof(superblock), | ||||
|             LFS_MKATTR(LFS_TYPE_DIRSTRUCT, 0, lfs->root, sizeof(lfs->root), | ||||
| @@ -3250,35 +3260,6 @@ int lfs_scan(lfs_t *lfs) { | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     lfs->globals.move.pair[0] = 0xffffffff; | ||||
|     lfs->globals.move.pair[1] = 0xffffffff; | ||||
|     lfs->globals.move.id      = 0x3ff; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_deorphan(lfs_t *lfs) { | ||||
|     lfs->deorphaned = true; | ||||
|     if (lfs_pairisnull(lfs->root)) { // TODO rm me? | ||||
| @@ -3287,7 +3268,20 @@ int lfs_deorphan(lfs_t *lfs) { | ||||
|  | ||||
|     // Fix bad moves | ||||
|     if (!lfs_pairisnull(lfs->globals.move.pair)) { | ||||
|         int err = lfs_fixmove(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); | ||||
|  | ||||
|         // 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; | ||||
|         } | ||||
|  | ||||
|         // rely on cancel logic inside commit | ||||
|         err = lfs_dir_commit(lfs, &movedir, NULL); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user