Browse Source

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.
Christopher Haster 8 years ago
parent
commit
11664160a8
1 changed files with 60 additions and 39 deletions
  1. 60 39
      lfs.c

+ 60 - 39
lfs.c

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