Fixed move handling when caught in update

This commit is contained in:
Christopher Haster
2018-07-17 18:31:30 -05:00
parent f1719e3310
commit 236d46ad17

282
lfs.c
View File

@@ -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;
}