Skip to content

Commit

Permalink
Fixed relocation bug when a file is closed with lingering caches
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
geky committed Jun 29, 2017
1 parent fe28ea0 commit 1166416
Showing 1 changed file with 60 additions and 39 deletions.
99 changes: 60 additions & 39 deletions lfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,50 @@ int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
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) {
if (file->flags & LFS_F_READING) {
// just drop read cache
Expand Down Expand Up @@ -1225,9 +1269,21 @@ static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) {
}

// write out what we have
int err = lfs_cache_flush(lfs, &file->cache, &lfs->rcache);
if (err) {
return err;
while (true) {
int err = lfs_cache_flush(lfs, &file->cache, &lfs->rcache);
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
Expand Down Expand Up @@ -1396,45 +1452,10 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,

break;
relocate:
LFS_DEBUG("Bad block at %d", file->block);

// just relocate what exists into new block
lfs_block_t nblock;
err = lfs_alloc(lfs, &nblock);
err = lfs_file_relocate(lfs, file);
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;
}

file->pos += diff;
Expand Down

0 comments on commit 1166416

Please sign in to comment.