Skip to content

Commit b46fcac

Browse files
committed
Fixed issues with finding wrong ids after bad commits
Unfortunately, the behaviour needed of lfs_dir_fetchwith is as subtle as it is important. When fetching from a block corrupted by power-loss, lfs_dir_fetch must be able to rewind any state it picks up to before the corruption. This is not limited to the directory state, but includes find results and other side-effects. This gets a bit complicated when trying to generalize littlefs's fetchwith mechanics. Being able to scan a directory block during a fetch greatly impacts the runtime of littlefs operations, but if the state is generic how do we know what to rollback to? The fix here is to leave the management of rolling back state to the fetchwith match functions, and transparently pass a CRC tag to indicate the temporary state can be saved.
1 parent cebf7aa commit b46fcac

File tree

2 files changed

+53
-29
lines changed

2 files changed

+53
-29
lines changed

lfs.c

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -721,16 +721,18 @@ static int lfs_commit_move(lfs_t *lfs, struct lfs_commit *commit,
721721

722722
static int lfs_commit_globals(lfs_t *lfs, struct lfs_commit *commit,
723723
const lfs_globals_t *source, const lfs_globals_t *diff) {
724-
lfs_globals_t res = lfs_globals_xor(source, diff);
724+
if (lfs_globals_iszero(diff)) {
725+
return 0;
726+
}
725727

726-
if (!lfs_globals_iszero(&res)) {
727-
int err = lfs_commit_commit(lfs, commit, (lfs_mattr_t){
728-
lfs_mktag(LFS_TYPE_IDELETE,
729-
res.move.id, sizeof(res.move.pair)),
730-
.u.buffer=res.move.pair});
731-
if (err) {
732-
return err;
733-
}
728+
// TODO check performance/complexity of different strategies here
729+
lfs_globals_t res = lfs_globals_xor(source, diff);
730+
int err = lfs_commit_commit(lfs, commit, (lfs_mattr_t){
731+
lfs_mktag(LFS_TYPE_IDELETE,
732+
res.move.id, sizeof(res.move.pair)),
733+
.u.buffer=res.move.pair});
734+
if (err) {
735+
return err;
734736
}
735737

736738
return 0;
@@ -878,6 +880,17 @@ static int lfs_dir_fetchwith(lfs_t *lfs,
878880
temp.etag = tag;
879881
crc = 0xffffffff;
880882
*dir = temp;
883+
884+
// TODO simplify this?
885+
if (cb) {
886+
err = cb(lfs, data, (lfs_mattr_t){
887+
(tag | 0x80000000),
888+
.u.d.block=temp.pair[0],
889+
.u.d.off=off+sizeof(tag)});
890+
if (err) {
891+
return err;
892+
}
893+
}
881894
} else {
882895
// TODO crc before callback???
883896
err = lfs_bd_crc(lfs, temp.pair[0],
@@ -1008,6 +1021,11 @@ static int lfs_dir_compact(lfs_t *lfs, lfs_mdir_t *dir, lfs_mattrlist_t *list,
10081021
const lfs_block_t oldpair[2] = {dir->pair[1], dir->pair[0]};
10091022
bool relocated = false;
10101023

1024+
// There's nothing special about our global delta, so feed it back
1025+
// into the global global delta
1026+
lfs->diff = lfs_globals_xor(&lfs->diff, &dir->globals);
1027+
dir->globals = (lfs_globals_t){0};
1028+
10111029
// increment revision count
10121030
dir->rev += 1;
10131031

@@ -1207,14 +1225,12 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, lfs_mattrlist_t *list) {
12071225
return err;
12081226
}
12091227

1210-
if (!lfs_globals_iszero(&lfs->diff)) {
1211-
err = lfs_commit_globals(lfs, &commit, &dir->globals, &lfs->diff);
1212-
if (err) {
1213-
if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
1214-
goto compact;
1215-
}
1216-
return err;
1228+
err = lfs_commit_globals(lfs, &commit, &dir->globals, &lfs->diff);
1229+
if (err) {
1230+
if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
1231+
goto compact;
12171232
}
1233+
return err;
12181234
}
12191235

12201236
err = lfs_commit_crc(lfs, &commit);
@@ -1242,6 +1258,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, lfs_mattrlist_t *list) {
12421258
}
12431259

12441260
// update any directories that are affected
1261+
// TODO what about pairs? what if we're splitting??
12451262
for (lfs_dir_t *d = lfs->dirs; d; d = d->next) {
12461263
if (lfs_paircmp(d->m.pair, dir->pair) == 0) {
12471264
d->m = *dir;
@@ -1441,6 +1458,7 @@ struct lfs_dir_find {
14411458
const char *name;
14421459
uint16_t len;
14431460
int16_t id;
1461+
int16_t tempid;
14441462
};
14451463

14461464
static int lfs_dir_findscan(lfs_t *lfs, void *p, lfs_mattr_t attr) {
@@ -1456,14 +1474,16 @@ static int lfs_dir_findscan(lfs_t *lfs, void *p, lfs_mattr_t attr) {
14561474

14571475
if (res) {
14581476
// found a match
1459-
find->id = lfs_tag_id(attr.tag);
1477+
find->tempid = lfs_tag_id(attr.tag);
14601478
}
14611479
} else if (lfs_tag_type(attr.tag) == LFS_STRUCT_DELETE) {
1462-
if (lfs_tag_id(attr.tag) == find->id) {
1463-
find->id = -1;
1464-
} else if (lfs_tag_id(attr.tag) < find->id) {
1465-
find->id -= 1;
1480+
if (lfs_tag_id(attr.tag) == find->tempid) {
1481+
find->tempid = -1;
1482+
} else if (lfs_tag_id(attr.tag) < find->tempid) {
1483+
find->tempid -= 1;
14661484
}
1485+
} else if (lfs_tag_type(attr.tag) == LFS_TYPE_CRC) {
1486+
find->id = find->tempid;
14671487
}
14681488

14691489
return 0;
@@ -1531,6 +1551,7 @@ static int lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir,
15311551
while (true) {
15321552
//printf("checking %d %d for %s\n", attr.u.pair[0], attr.u.pair[1], *path);
15331553
find.id = -1;
1554+
find.tempid = -1;
15341555
int err = lfs_dir_fetchwith(lfs, dir, attr.u.pair,
15351556
lfs_dir_findscan, &find);
15361557
if (err) {
@@ -3451,9 +3472,11 @@ static int lfs_pred(lfs_t *lfs, const lfs_block_t dir[2], lfs_mdir_t *pdir) {
34513472
*/
34523473

34533474

3475+
// TODO combine parentscan and findscan?
34543476
struct lfs_dir_parentscan {
34553477
lfs_block_t pair[2];
34563478
int16_t id;
3479+
int16_t tempid;
34573480
};
34583481

34593482
static int lfs_parentscan(lfs_t *lfs, void *p, lfs_mattr_t attr) {
@@ -3468,14 +3491,16 @@ static int lfs_parentscan(lfs_t *lfs, void *p, lfs_mattr_t attr) {
34683491

34693492
if (lfs_paircmp(attr.u.pair, parentscan->pair) == 0) {
34703493
// found a match
3471-
parentscan->id = lfs_tag_id(attr.tag);
3494+
parentscan->tempid = lfs_tag_id(attr.tag);
34723495
}
34733496
} else if (lfs_tag_struct(attr.tag) == LFS_STRUCT_DELETE) {
3474-
if (lfs_tag_id(attr.tag) == parentscan->id) {
3475-
parentscan->id = -1;
3476-
} else if (lfs_tag_id(attr.tag) < parentscan->id) {
3477-
parentscan->id -= 1;
3497+
if (lfs_tag_id(attr.tag) == parentscan->tempid) {
3498+
parentscan->tempid = -1;
3499+
} else if (lfs_tag_id(attr.tag) < parentscan->tempid) {
3500+
parentscan->tempid -= 1;
34783501
}
3502+
} else if (lfs_tag_type(attr.tag) == LFS_TYPE_CRC) {
3503+
parentscan->id = parentscan->tempid;
34793504
}
34803505

34813506
return 0;
@@ -3490,7 +3515,8 @@ static int lfs_parent(lfs_t *lfs, const lfs_block_t pair[2],
34903515
struct lfs_dir_parentscan parentscan = {
34913516
.pair[0] = pair[0],
34923517
.pair[1] = pair[1],
3493-
.id = -1
3518+
.id = -1,
3519+
.tempid = -1,
34943520
};
34953521

34963522
int err = lfs_dir_fetchwith(lfs, parent, parent->tail,

tests/test_dirs.sh

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,6 @@ tests/test.py << TEST
152152
strcmp(info.name, "..") => 0;
153153
info.type => LFS_TYPE_DIR;
154154
lfs_dir_read(&lfs, &dir[0], &info) => 1;
155-
printf("nameee \"%s\"\n", info.name);
156-
printf("expect \"%s\"\n", "burito");
157155
strcmp(info.name, "burito") => 0;
158156
info.type => LFS_TYPE_REG;
159157
lfs_dir_read(&lfs, &dir[0], &info) => 1;

0 commit comments

Comments
 (0)