Fixed relocation bug when a file is closed with lingering caches

This bug required larger cache sizes to notice, since block errors
usually get detected in the early stages of writing to files.

Since the fix here requires both lfs_file_write and lfs_file_flush
relocate file blocks, the relocation logic was moved out into a
seperate function.
This commit is contained in:
Christopher Haster
2017-06-25 14:01:33 -05:00
parent fe28ea0f93
commit 11664160a8

99
lfs.c
View File

@@ -1183,6 +1183,50 @@ int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
return err; return err;
} }
static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) {
relocate:
LFS_DEBUG("Bad block at %d", file->block);
// just relocate what exists into new block
lfs_block_t nblock;
int err = lfs_alloc(lfs, &nblock);
if (err) {
return err;
}
// either read from dirty cache or disk
for (lfs_off_t i = 0; i < file->off; i++) {
uint8_t data;
if (file->cache.block == file->block && i >= file->cache.off) {
data = file->cache.buffer[i - file->cache.off];
} else {
// just read from disk
err = lfs_bd_read(lfs, file->block, i, &data, 1);
if (err) {
return err;
}
}
err = lfs_cache_prog(lfs, &lfs->pcache, &lfs->rcache,
nblock, i, &data, 1);
if (err) {
if (err == LFS_ERR_CORRUPT) {
goto relocate;
}
return err;
}
}
// copy over new state of file
memcpy(file->cache.buffer, lfs->pcache.buffer, lfs->cfg->prog_size);
file->cache.block = lfs->pcache.block;
file->cache.off = lfs->pcache.off;
lfs->pcache.block = 0xffffffff;
file->block = nblock;
return 0;
}
static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) {
if (file->flags & LFS_F_READING) { if (file->flags & LFS_F_READING) {
// just drop read cache // just drop read cache
@@ -1225,9 +1269,21 @@ static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) {
} }
// write out what we have // write out what we have
int err = lfs_cache_flush(lfs, &file->cache, &lfs->rcache); while (true) {
if (err) { int err = lfs_cache_flush(lfs, &file->cache, &lfs->rcache);
return err; if (err) {
if (err == LFS_ERR_CORRUPT) {
goto relocate;
}
return err;
}
break;
relocate:
err = lfs_file_relocate(lfs, file);
if (err) {
return err;
}
} }
// actual file updates // actual file updates
@@ -1396,45 +1452,10 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
break; break;
relocate: relocate:
LFS_DEBUG("Bad block at %d", file->block); err = lfs_file_relocate(lfs, file);
// just relocate what exists into new block
lfs_block_t nblock;
err = lfs_alloc(lfs, &nblock);
if (err) { if (err) {
return err; return err;
} }
// either read from dirty cache or disk
for (lfs_off_t i = 0; i < file->off; i++) {
uint8_t data;
if (file->cache.block == file->block && i >= file->cache.off) {
data = file->cache.buffer[i - file->cache.off];
} else {
// just read from disk
err = lfs_bd_read(lfs, file->block, i, &data, 1);
if (err) {
return err;
}
}
err = lfs_cache_prog(lfs, &lfs->pcache, &lfs->rcache,
nblock, i, &data, 1);
if (err) {
if (err == LFS_ERR_CORRUPT) {
goto relocate;
}
return err;
}
}
// copy over new state of file
memcpy(file->cache.buffer, lfs->pcache.buffer, lfs->cfg->prog_size);
file->cache.block = lfs->pcache.block;
file->cache.off = lfs->pcache.off;
lfs->pcache.block = 0xffffffff;
file->block = nblock;
} }
file->pos += diff; file->pos += diff;