Bläddra i källkod

Added support for handling corrupted blocks

This provides a limited form of wear leveling. While wear is
not actually balanced across blocks, the filesystem can recover
from corrupted blocks and extend the lifetime of a device nearly
as much as dynamic wear leveling.

For use-cases where wear is important, it would be better to use
a full form of dynamic wear-leveling at the block level. (or
consider a logging filesystem).

Corrupted block handling was simply added on top of the existing
logic in place for the filesystem, so it's a bit more noodly than
it may have to be, but it gets the work done.
Christopher Haster 8 år sedan
förälder
incheckning
fd1da602d7
8 ändrade filer med 531 tillägg och 277 borttagningar
  1. 1 1
      Makefile
  2. 12 1
      emubd/lfs_emubd.c
  3. 395 266
      lfs.c
  4. 5 4
      lfs.h
  5. 9 3
      tests/template.fmt
  6. 106 0
      tests/test_corrupt.sh
  7. 1 0
      tests/test_dirs.sh
  8. 2 2
      tests/test_format.sh

+ 1 - 1
Makefile

@@ -32,7 +32,7 @@ size: $(OBJ)
 
 
 .SUFFIXES:
 .SUFFIXES:
 test: test_format test_dirs test_files test_seek test_parallel \
 test: test_format test_dirs test_files test_seek test_parallel \
-	test_alloc test_paths test_orphan
+	test_alloc test_paths test_orphan test_corrupt
 test_%: tests/test_%.sh
 test_%: tests/test_%.sh
 	./$<
 	./$<
 
 

+ 12 - 1
emubd/lfs_emubd.c

@@ -144,13 +144,24 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
         return -errno;
         return -errno;
     }
     }
 
 
+    err = fseek(f, off, SEEK_SET);
+    if (err) {
+        return -errno;
+    }
+
+    uint8_t dat;
+    res = fread(&dat, 1, 1, f);
+    if (res < 1) {
+        return -errno;
+    }
+
     err = fclose(f);
     err = fclose(f);
     if (err) {
     if (err) {
         return -errno;
         return -errno;
     }
     }
 
 
     emu->stats.prog_count += 1;
     emu->stats.prog_count += 1;
-    return 0;
+    return (dat != data[0]) ? LFS_ERR_CORRUPT : 0;
 }
 }
 
 
 int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
 int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 395 - 266
lfs.c


+ 5 - 4
lfs.h

@@ -43,7 +43,7 @@ enum lfs_error {
 enum lfs_type {
 enum lfs_type {
     LFS_TYPE_REG        = 0x01,
     LFS_TYPE_REG        = 0x01,
     LFS_TYPE_DIR        = 0x02,
     LFS_TYPE_DIR        = 0x02,
-    LFS_TYPE_SUPERBLOCK = 0x10,
+    LFS_TYPE_SUPERBLOCK = 0x12,
 };
 };
 
 
 enum lfs_open_flags {
 enum lfs_open_flags {
@@ -193,15 +193,16 @@ typedef struct lfs_superblock {
     struct lfs_disk_superblock {
     struct lfs_disk_superblock {
         uint16_t type;
         uint16_t type;
         uint16_t len;
         uint16_t len;
+        lfs_block_t root[2];
         uint32_t version;
         uint32_t version;
         char magic[8];
         char magic[8];
         uint32_t block_size;
         uint32_t block_size;
         uint32_t block_count;
         uint32_t block_count;
-        lfs_block_t root[2];
     } d;
     } d;
 } lfs_superblock_t;
 } lfs_superblock_t;
 
 
 typedef struct lfs_free {
 typedef struct lfs_free {
+    lfs_block_t end;
     lfs_block_t start;
     lfs_block_t start;
     lfs_block_t off;
     lfs_block_t off;
     uint32_t *lookahead;
     uint32_t *lookahead;
@@ -212,8 +213,8 @@ typedef struct lfs {
     const struct lfs_config *cfg;
     const struct lfs_config *cfg;
 
 
     lfs_block_t root[2];
     lfs_block_t root[2];
-    lfs_dir_t *scratch;
     lfs_file_t *files;
     lfs_file_t *files;
+    bool deorphaned;
 
 
     lfs_cache_t rcache;
     lfs_cache_t rcache;
     lfs_cache_t pcache;
     lfs_cache_t pcache;
@@ -257,8 +258,8 @@ int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file);
 lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file);
 lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file);
 
 
 // miscellaneous lfs specific operations
 // miscellaneous lfs specific operations
-int lfs_deorphan(lfs_t *lfs);
 int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
 int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
+int lfs_deorphan(lfs_t *lfs);
 
 
 
 
 #endif
 #endif

+ 9 - 3
tests/template.fmt

@@ -13,11 +13,17 @@ void test_log(const char *s, uintmax_t v) {{
 
 
 void test_assert(const char *file, unsigned line,
 void test_assert(const char *file, unsigned line,
         const char *s, uintmax_t v, uintmax_t e) {{
         const char *s, uintmax_t v, uintmax_t e) {{
-    static const char *last[2] = {{0, 0}};
-    if (v != e || !(last[0] == s || last[1] == s)) {{
+    static const char *last[6] = {{0, 0}};
+    if (v != e || !(last[0] == s || last[1] == s ||
+            last[2] == s || last[3] == s ||
+            last[4] == s || last[5] == s)) {{
         test_log(s, v);
         test_log(s, v);
         last[0] = last[1];
         last[0] = last[1];
-        last[1] = s;
+        last[1] = last[2];
+        last[2] = last[3];
+        last[3] = last[4];
+        last[4] = last[5];
+        last[5] = s;
     }}
     }}
 
 
     if (v != e) {{
     if (v != e) {{

+ 106 - 0
tests/test_corrupt.sh

@@ -0,0 +1,106 @@
+#!/bin/bash
+set -eu
+
+echo "=== Corrupt tests ==="
+
+NAMEMULT=64
+FILEMULT=1
+
+lfs_mktree() {
+tests/test.py ${1:-} << TEST
+    lfs_format(&lfs, &cfg) => 0;
+
+    lfs_mount(&lfs, &cfg) => 0;
+    for (int i = 1; i < 10; i++) {
+        for (int j = 0; j < $NAMEMULT; j++) {
+            buffer[j] = '0'+i;
+        }
+        buffer[$NAMEMULT] = '\0';
+        lfs_mkdir(&lfs, (char*)buffer) => 0;
+
+        buffer[$NAMEMULT] = '/';
+        for (int j = 0; j < $NAMEMULT; j++) {
+            buffer[j+$NAMEMULT+1] = '0'+i;
+        }
+        buffer[2*$NAMEMULT+1] = '\0';
+        lfs_file_open(&lfs, &file[0], (char*)buffer,
+                LFS_O_WRONLY | LFS_O_CREAT) => 0;
+        
+        size = $NAMEMULT;
+        for (int j = 0; j < i*$FILEMULT; j++) {
+            lfs_file_write(&lfs, &file[0], buffer, size) => size;
+        }
+
+        lfs_file_close(&lfs, &file[0]) => 0;
+    }
+    lfs_unmount(&lfs) => 0;
+TEST
+}
+
+lfs_chktree() {
+tests/test.py ${1:-} << TEST
+    lfs_mount(&lfs, &cfg) => 0;
+    for (int i = 1; i < 10; i++) {
+        for (int j = 0; j < $NAMEMULT; j++) {
+            buffer[j] = '0'+i;
+        }
+        buffer[$NAMEMULT] = '\0';
+        lfs_stat(&lfs, (char*)buffer, &info) => 0;
+        info.type => LFS_TYPE_DIR;
+
+        buffer[$NAMEMULT] = '/';
+        for (int j = 0; j < $NAMEMULT; j++) {
+            buffer[j+$NAMEMULT+1] = '0'+i;
+        }
+        buffer[2*$NAMEMULT+1] = '\0';
+        lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0;
+        
+        size = $NAMEMULT;
+        for (int j = 0; j < i*$FILEMULT; j++) {
+            lfs_file_read(&lfs, &file[0], rbuffer, size) => size;
+            memcmp(buffer, rbuffer, size) => 0;
+        }
+
+        lfs_file_close(&lfs, &file[0]) => 0;
+    }
+    lfs_unmount(&lfs) => 0;
+TEST
+}
+
+echo "--- Sanity check ---"
+rm -rf blocks
+lfs_mktree
+lfs_chktree
+
+echo "--- Block corruption ---"
+for i in {0..33}
+do 
+    rm -rf blocks
+    mkdir blocks
+    ln -s /dev/zero blocks/$(printf '%x' $i)
+    lfs_mktree
+    lfs_chktree
+done
+
+echo "--- Big region corruption ---"
+rm -rf blocks
+mkdir blocks
+for i in {2..255}
+do
+    ln -s /dev/zero blocks/$(printf '%x' $i)
+done
+lfs_mktree
+lfs_chktree
+
+echo "--- Alternating corruption ---"
+rm -rf blocks
+mkdir blocks
+for i in {2..511..2}
+do
+    ln -s /dev/zero blocks/$(printf '%x' $i)
+done
+lfs_mktree
+lfs_chktree
+
+echo "--- Results ---"
+tests/stats.py

+ 1 - 0
tests/test_dirs.sh

@@ -124,6 +124,7 @@ tests/test.py << TEST
 TEST
 TEST
 
 
 echo "--- Directory remove ---"
 echo "--- Directory remove ---"
+# TESTING HERE
 tests/test.py << TEST
 tests/test.py << TEST
     lfs_mount(&lfs, &cfg) => 0;
     lfs_mount(&lfs, &cfg) => 0;
     lfs_remove(&lfs, "potato") => LFS_ERR_INVAL;
     lfs_remove(&lfs, "potato") => LFS_ERR_INVAL;

+ 2 - 2
tests/test_format.sh

@@ -10,8 +10,8 @@ tests/test.py << TEST
 TEST
 TEST
 
 
 echo "--- Invalid superblocks ---"
 echo "--- Invalid superblocks ---"
-ln -f -s /dev/null blocks/0
-ln -f -s /dev/null blocks/1
+ln -f -s /dev/zero blocks/0
+ln -f -s /dev/zero blocks/1
 tests/test.py << TEST
 tests/test.py << TEST
     lfs_format(&lfs, &cfg) => LFS_ERR_CORRUPT;
     lfs_format(&lfs, &cfg) => LFS_ERR_CORRUPT;
 TEST
 TEST

Vissa filer visades inte eftersom för många filer har ändrats