Fixed issue updating dir struct when extended dir chain

Like most of the lfs_dir_t functions, lfs_dir_append is responsible for
updating the lfs_dir_t struct if the underlying directory block is
moved. This property makes handling worn out blocks much easier by
removing the amount of state that needs to be considered during a
directory update.

However, extending the dir chain is a bit of a corner case. It's not
changing the old block, but callers of lfs_dir_append do assume the
"entry" will reside in "dir" after lfs_dir_append completes.

This issue only occurs when creating files, since mkdir does not use
the entry after lfs_dir_append. Unfortunately, the tests against
extending the directory chain were all made using mkdir.

Found by schouleu
This commit is contained in:
Christopher Haster
2018-02-26 12:24:27 -06:00
parent d9c36371e7
commit 9ee112a7cb
3 changed files with 95 additions and 10 deletions

20
lfs.c
View File

@@ -658,17 +658,17 @@ static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
// we need to allocate a new dir block
if (!(0x80000000 & dir->d.size)) {
lfs_dir_t newdir;
int err = lfs_dir_alloc(lfs, &newdir);
lfs_dir_t olddir = *dir;
int err = lfs_dir_alloc(lfs, dir);
if (err) {
return err;
}
newdir.d.tail[0] = dir->d.tail[0];
newdir.d.tail[1] = dir->d.tail[1];
entry->off = newdir.d.size - 4;
dir->d.tail[0] = olddir.d.tail[0];
dir->d.tail[1] = olddir.d.tail[1];
entry->off = dir->d.size - 4;
lfs_entry_tole32(&entry->d);
err = lfs_dir_commit(lfs, &newdir, (struct lfs_region[]){
err = lfs_dir_commit(lfs, dir, (struct lfs_region[]){
{entry->off, 0, &entry->d, sizeof(entry->d)},
{entry->off, 0, data, entry->d.nlen}
}, 2);
@@ -677,10 +677,10 @@ static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
return err;
}
dir->d.size |= 0x80000000;
dir->d.tail[0] = newdir.pair[0];
dir->d.tail[1] = newdir.pair[1];
return lfs_dir_commit(lfs, dir, NULL, 0);
olddir.d.size |= 0x80000000;
olddir.d.tail[0] = dir->pair[0];
olddir.d.tail[1] = dir->pair[1];
return lfs_dir_commit(lfs, &olddir, NULL, 0);
}
int err = lfs_dir_fetch(lfs, dir, dir->d.tail);