Kaynağa Gözat

Restructured the major interfaces of the filesystem

Christopher Haster 8 yıl önce
ebeveyn
işleme
84a57642e5
8 değiştirilmiş dosya ile 470 ekleme ve 433 silme
  1. 43 41
      emubd/lfs_emubd.c
  2. 14 13
      emubd/lfs_emubd.h
  3. 288 291
      lfs.c
  4. 45 42
      lfs.h
  5. 10 10
      lfs_bd.h
  6. 5 36
      lfs_config.h
  7. 27 0
      lfs_util.c
  8. 38 0
      lfs_util.h

+ 43 - 41
emubd/lfs_emubd.c

@@ -17,7 +17,7 @@
 
 
 // Block device emulated on existing filesystem
-lfs_error_t lfs_emubd_create(lfs_emubd_t *emu, const char *path) {
+int lfs_emubd_create(lfs_emubd_t *emu, const char *path) {
     memset(&emu->info, 0, sizeof(emu->info));
     memset(&emu->stats, 0, sizeof(emu->stats));
 
@@ -41,8 +41,8 @@ lfs_error_t lfs_emubd_create(lfs_emubd_t *emu, const char *path) {
         return err;
     }
 
-    emu->info.read_size = lfs_cfg_getu(&cfg,  "read_size",  0);
-    emu->info.write_size = lfs_cfg_getu(&cfg, "write_size", 0);
+    emu->info.read_size  = lfs_cfg_getu(&cfg, "read_size",  0);
+    emu->info.prog_size  = lfs_cfg_getu(&cfg, "prog_size",  0);
     emu->info.erase_size = lfs_cfg_getu(&cfg, "erase_size", 0);
     emu->info.total_size = lfs_cfg_getu(&cfg, "total_size", 0);
 
@@ -55,23 +55,24 @@ void lfs_emubd_destroy(lfs_emubd_t *emu) {
     free(emu->path);
 }
 
-lfs_error_t lfs_emubd_read(lfs_emubd_t *emu, uint8_t *buffer,
-        lfs_ino_t ino, lfs_off_t off, lfs_size_t size) {
+int lfs_emubd_read(lfs_emubd_t *emu, lfs_block_t block,
+        lfs_off_t off, lfs_size_t size, void *buffer) {
+    uint8_t *data = buffer;
 
     // Check if read is valid
     if (!(off % emu->info.read_size == 0 &&
          size % emu->info.read_size == 0 &&
-         ((lfs_lsize_t)ino*emu->info.erase_size + off + size
+         ((uint64_t)block*emu->info.erase_size + off + size
           < emu->info.total_size))) {
         return -EINVAL;
     }
 
     // Zero out buffer for debugging
-    memset(buffer, 0, size);
+    memset(data, 0, size);
 
     // Iterate over blocks until enough data is read
     while (size > 0) {
-        snprintf(emu->child, LFS_NAME_MAX, "%d", ino);
+        snprintf(emu->child, LFS_NAME_MAX, "%d", block);
         size_t count = lfs_min(emu->info.erase_size - off, size);
 
         FILE *f = fopen(emu->path, "rb");
@@ -85,7 +86,7 @@ lfs_error_t lfs_emubd_read(lfs_emubd_t *emu, uint8_t *buffer,
                 return -errno;
             }
 
-            size_t res = fread(buffer, 1, count, f);
+            size_t res = fread(data, 1, count, f);
             if (res < count && !feof(f)) {
                 return -errno;
             }
@@ -97,8 +98,8 @@ lfs_error_t lfs_emubd_read(lfs_emubd_t *emu, uint8_t *buffer,
         }
 
         size -= count;
-        buffer += count;
-        ino += 1;
+        data += count;
+        block += 1;
         off = 0;
     }
 
@@ -106,20 +107,21 @@ lfs_error_t lfs_emubd_read(lfs_emubd_t *emu, uint8_t *buffer,
     return 0;
 }
 
-lfs_error_t lfs_emubd_write(lfs_emubd_t *emu, const uint8_t *buffer,
-        lfs_ino_t ino, lfs_off_t off, lfs_size_t size) {
+int lfs_emubd_prog(lfs_emubd_t *emu, lfs_block_t block,
+        lfs_off_t off, lfs_size_t size, const void *buffer) {
+    const uint8_t *data = buffer;
 
     // Check if write is valid
-    if (!(off % emu->info.write_size == 0 &&
-         size % emu->info.write_size == 0 &&
-         ((lfs_lsize_t)ino*emu->info.erase_size + off + size
+    if (!(off % emu->info.prog_size == 0 &&
+         size % emu->info.prog_size == 0 &&
+         ((uint64_t)block*emu->info.erase_size + off + size
           < emu->info.total_size))) {
         return -EINVAL;
     }
 
     // Iterate over blocks until enough data is read
     while (size > 0) {
-        snprintf(emu->child, LFS_NAME_MAX, "%d", ino);
+        snprintf(emu->child, LFS_NAME_MAX, "%d", block);
         size_t count = lfs_min(emu->info.erase_size - off, size);
 
         FILE *f = fopen(emu->path, "r+b");
@@ -135,7 +137,7 @@ lfs_error_t lfs_emubd_write(lfs_emubd_t *emu, const uint8_t *buffer,
             return -errno;
         }
 
-        size_t res = fwrite(buffer, 1, count, f);
+        size_t res = fwrite(data, 1, count, f);
         if (res < count) {
             return -errno;
         }
@@ -146,29 +148,29 @@ lfs_error_t lfs_emubd_write(lfs_emubd_t *emu, const uint8_t *buffer,
         }
 
         size -= count;
-        buffer += count;
-        ino += 1;
+        data += count;
+        block += 1;
         off = 0;
     }
 
-    emu->stats.write_count += 1;
+    emu->stats.prog_count += 1;
     return 0;
 }
 
-lfs_error_t lfs_emubd_erase(lfs_emubd_t *emu,
-        lfs_ino_t ino, lfs_off_t off, lfs_size_t size) {
+int lfs_emubd_erase(lfs_emubd_t *emu, lfs_block_t block,
+        lfs_off_t off, lfs_size_t size) {
 
     // Check if erase is valid
     if (!(off % emu->info.erase_size == 0 &&
          size % emu->info.erase_size == 0 &&
-         ((lfs_lsize_t)ino*emu->info.erase_size + off + size
+         ((uint64_t)block*emu->info.erase_size + off + size
           < emu->info.total_size))) {
         return -EINVAL;
     }
 
     // Iterate and erase blocks
     while (size > 0) {
-        snprintf(emu->child, LFS_NAME_MAX, "%d", ino);
+        snprintf(emu->child, LFS_NAME_MAX, "%d", block);
         struct stat st;
         int err = stat(emu->path, &st);
         if (err && errno != ENOENT) {
@@ -183,7 +185,7 @@ lfs_error_t lfs_emubd_erase(lfs_emubd_t *emu,
         }
 
         size -= emu->info.erase_size;
-        ino += 1;
+        block += 1;
         off = 0;
     }
 
@@ -191,49 +193,49 @@ lfs_error_t lfs_emubd_erase(lfs_emubd_t *emu,
     return 0;
 }
 
-lfs_error_t lfs_emubd_sync(lfs_emubd_t *emu) {
+int lfs_emubd_sync(lfs_emubd_t *emu) {
     // Always in sync
     return 0;
 }
 
-lfs_error_t lfs_emubd_info(lfs_emubd_t *emu, struct lfs_bd_info *info) {
+int lfs_emubd_info(lfs_emubd_t *emu, struct lfs_bd_info *info) {
     *info = emu->info;
     return 0;
 }
 
-lfs_error_t lfs_emubd_stats(lfs_emubd_t *emu, struct lfs_bd_stats *stats) {
+int lfs_emubd_stats(lfs_emubd_t *emu, struct lfs_bd_stats *stats) {
     *stats = emu->stats;
     return 0;
 }
 
 
 // Wrappers for void*s
-static lfs_error_t lfs_emubd_bd_read(void *bd, uint8_t *buffer,
-        lfs_ino_t ino, lfs_off_t off, lfs_size_t size) {
-    return lfs_emubd_read((lfs_emubd_t*)bd, buffer, ino, off, size);
+static int lfs_emubd_bd_read(void *bd, lfs_block_t block,
+        lfs_off_t off, lfs_size_t size, void *buffer) {
+    return lfs_emubd_read((lfs_emubd_t*)bd, block, off, size, buffer);
 }
 
-static lfs_error_t lfs_emubd_bd_write(void *bd, const uint8_t *buffer,
-        lfs_ino_t ino, lfs_off_t off, lfs_size_t size) {
-    return lfs_emubd_write((lfs_emubd_t*)bd, buffer, ino, off, size);
+static int lfs_emubd_bd_prog(void *bd, lfs_block_t block,
+        lfs_off_t off, lfs_size_t size, const void *buffer) {
+    return lfs_emubd_prog((lfs_emubd_t*)bd, block, off, size, buffer);
 }
 
-static lfs_error_t lfs_emubd_bd_erase(void *bd,
-        lfs_ino_t ino, lfs_off_t off, lfs_size_t size) {
-    return lfs_emubd_erase((lfs_emubd_t*)bd, ino, off, size);
+static int lfs_emubd_bd_erase(void *bd, lfs_block_t block,
+        lfs_off_t off, lfs_size_t size) {
+    return lfs_emubd_erase((lfs_emubd_t*)bd, block, off, size);
 }
 
-static lfs_error_t lfs_emubd_bd_sync(void *bd) {
+static int lfs_emubd_bd_sync(void *bd) {
     return lfs_emubd_sync((lfs_emubd_t*)bd);
 }
 
-static lfs_error_t lfs_emubd_bd_info(void *bd, struct lfs_bd_info *info) {
+static int lfs_emubd_bd_info(void *bd, struct lfs_bd_info *info) {
     return lfs_emubd_info((lfs_emubd_t*)bd, info);
 }
 
 const struct lfs_bd_ops lfs_emubd_ops = {
     .read = lfs_emubd_bd_read,
-    .write = lfs_emubd_bd_write,
+    .prog = lfs_emubd_bd_prog,
     .erase = lfs_emubd_bd_erase,
     .sync = lfs_emubd_bd_sync,
     .info = lfs_emubd_bd_info,

+ 14 - 13
emubd/lfs_emubd.h

@@ -8,14 +8,15 @@
 #define LFS_EMUBD_H
 
 #include "lfs_config.h"
+#include "lfs_util.h"
 #include "lfs_bd.h"
 
 
 // Stats for debugging and optimization
 struct lfs_bd_stats {
-    lfs_lword_t read_count;
-    lfs_lword_t write_count;
-    lfs_lword_t erase_count;
+    uint64_t read_count;
+    uint64_t prog_count;
+    uint64_t erase_count;
 };
 
 // The emu bd state
@@ -28,40 +29,40 @@ typedef struct lfs_emubd {
 
 
 // Create a block device using path for the directory to store blocks
-lfs_error_t lfs_emubd_create(lfs_emubd_t *emu, const char *path);
+int lfs_emubd_create(lfs_emubd_t *emu, const char *path);
 
 // Clean up memory associated with emu block device
 void lfs_emubd_destroy(lfs_emubd_t *emu);
 
 // Read a block
-lfs_error_t lfs_emubd_read(lfs_emubd_t *bd, uint8_t *buffer,
-        lfs_ino_t ino, lfs_off_t off, lfs_size_t size);
+int lfs_emubd_read(lfs_emubd_t *bd, lfs_block_t block,
+        lfs_off_t off, lfs_size_t size, void *buffer);
 
 // Program a block
 //
 // The block must have previously been erased.
-lfs_error_t lfs_emubd_write(lfs_emubd_t *bd, const uint8_t *buffer,
-        lfs_ino_t ino, lfs_off_t off, lfs_size_t size);
+int lfs_emubd_prog(lfs_emubd_t *bd, lfs_block_t block,
+        lfs_off_t off, lfs_size_t size, const void *buffer);
 
 // Erase a block
 //
 // A block must be erased before being programmed. The
 // state of an erased block is undefined.
-lfs_error_t lfs_emubd_erase(lfs_emubd_t *bd,
-        lfs_ino_t ino, lfs_off_t off, lfs_size_t size);
+int lfs_emubd_erase(lfs_emubd_t *bd, lfs_block_t block,
+        lfs_off_t off, lfs_size_t size);
 
 // Sync the block device
-lfs_error_t lfs_emubd_sync(lfs_emubd_t *bd);
+int lfs_emubd_sync(lfs_emubd_t *bd);
 
 // Get a description of the block device
 //
 // Any unknown information may be left unmodified
-lfs_error_t lfs_emubd_info(lfs_emubd_t *bd, struct lfs_bd_info *info);
+int lfs_emubd_info(lfs_emubd_t *bd, struct lfs_bd_info *info);
 
 // Get stats of operations on the block device
 //
 // Used for debugging and optimizations
-lfs_error_t lfs_emubd_stats(lfs_emubd_t *bd, struct lfs_bd_stats *stats);
+int lfs_emubd_stats(lfs_emubd_t *bd, struct lfs_bd_stats *stats);
 
 // Block device operations
 extern const struct lfs_bd_ops lfs_emubd_ops;

+ 288 - 291
lfs.c

@@ -5,62 +5,107 @@
  * Distributed under the MIT license
  */
 #include "lfs.h"
+#include "lfs_util.h"
 
 #include <string.h>
 #include <stdbool.h>
 
 
-static int lfs_diff(uint32_t a, uint32_t b) {
-    return (int)(unsigned)(a - b);
+/// Block device operations ///
+static int lfs_bd_info(lfs_t *lfs, struct lfs_bd_info *info) {
+    return lfs->bd_ops->info(lfs->bd, info);
 }
 
-static uint32_t lfs_crc(const uint8_t *data, lfs_size_t size, uint32_t crc) {
-    static const uint32_t rtable[16] = {
-        0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
-        0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
-        0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
-        0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
-    };
+static int lfs_bd_read(lfs_t *lfs, lfs_block_t block,
+        lfs_off_t off, lfs_size_t size, void *buffer) {
+    return lfs->bd_ops->read(lfs->bd, block, off, size, buffer);
+}
 
-    for (lfs_size_t i = 0; i < size; i++) {
-        crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 0)) & 0xf];
-        crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf];
-    }
+static int lfs_bd_prog(lfs_t *lfs, lfs_block_t block,
+        lfs_off_t off, lfs_size_t size, const void *buffer) {
+    return lfs->bd_ops->prog(lfs->bd, block, off, size, buffer);
+}
+
+static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block,
+        lfs_off_t off, lfs_size_t size) {
+    return lfs->bd_ops->erase(lfs->bd, block, off, size);
+}
 
-    return crc;
+static int lfs_bd_sync(lfs_t *lfs) {
+    return lfs->bd_ops->sync(lfs->bd);
 }
 
-static lfs_error_t lfs_bd_cmp(lfs_t *lfs,
-        lfs_ino_t ino, lfs_off_t off, lfs_size_t size, const void *d) {
-    const uint8_t *data = d;
+static int lfs_bd_cmp(lfs_t *lfs, lfs_block_t block,
+        lfs_off_t off, lfs_size_t size, const void *buffer) {
+    const uint8_t *data = buffer;
 
-    for (int i = 0; i < size; i++) {
+    while (off < size) {
         uint8_t c;
-        int err = lfs->ops->read(lfs->bd, (void*)&c, ino, off + i, 1);
+        int err = lfs_bd_read(lfs, block, off, 1, &c);
         if (err) {
             return err;
         }
 
-        if (c != data[i]) {
+        if (c != *data) {
             return false;
         }
+
+        data += 1;
+        off += 1;
     }
 
     return true;
 }
 
+static int lfs_bd_crc(lfs_t *lfs, lfs_block_t block,
+        lfs_off_t off, lfs_size_t size, uint32_t *crc) {
+    while (off < size) {
+        uint8_t c;
+        int err = lfs_bd_read(lfs, block, off, 1, &c);
+        if (err) {
+            return err;
+        }
+
+        *crc = lfs_crc(&c, 1, *crc);
+        off += 1;
+    }
+
+    return 0;
+}
+
+
+/// Block allocator ///
+
+static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
+    if (lfs->free.begin != lfs->free.end) {
+        *block = lfs->free.begin;
+        lfs->free.begin += 1;
+        return 0;
+    }
+
+    // TODO find next stride of free blocks
+    // TODO verify no strides exist where begin > current begin
+    // note: begin = 0 is invalid (superblock)
+    return LFS_ERROR_NO_SPACE;
+}
+
+static int lfs_alloc_erased(lfs_t *lfs, lfs_block_t *block) {
+    int err = lfs_alloc(lfs, block);
+    if (err) {
+        return err;
+    }
 
+    return lfs_bd_erase(lfs, *block, 0, lfs->block_size);
+}
 
-static lfs_error_t lfs_alloc(lfs_t *lfs, lfs_ino_t *ino);
 
+/// Index list operations ///
 
 // Next index offset
-static lfs_off_t lfs_inext(lfs_t *lfs, lfs_off_t ioff) {
+static lfs_off_t lfs_index_next(lfs_t *lfs, lfs_off_t ioff) {
     ioff += 1;
-
-    lfs_size_t wcount = lfs->info.erase_size/4;
-    while (ioff % wcount == 0) {
-        ioff += lfs_min(lfs_ctz(ioff/wcount + 1), wcount-1) + 1;
+    while (ioff % lfs->words == 0) {
+        ioff += lfs_min(lfs_ctz(ioff/lfs->words + 1), lfs->words-1) + 1;
     }
 
     return ioff;
@@ -68,28 +113,26 @@ static lfs_off_t lfs_inext(lfs_t *lfs, lfs_off_t ioff) {
 
 static lfs_off_t lfs_toindex(lfs_t *lfs, lfs_off_t off) {
     lfs_off_t i = 0;
-    while (off > 512) {
-        i = lfs_inext(lfs, i);
-        off -= 512;
+    while (off > lfs->block_size) {
+        i = lfs_index_next(lfs, i);
+        off -= lfs->block_size;
     }
 
     return i;
 }
 
 // Find index in index chain given its index offset
-static lfs_error_t lfs_ifind_block(lfs_t *lfs, lfs_ino_t head,
-        lfs_size_t icount, lfs_off_t ioff, lfs_ino_t *block) {
-    lfs_size_t wcount = lfs->info.erase_size/4;
-    lfs_off_t iitarget = ioff / wcount;
-    lfs_off_t iicurrent = (icount-1) / wcount;
+static int lfs_index_find(lfs_t *lfs, lfs_block_t head,
+        lfs_size_t icount, lfs_off_t ioff, lfs_block_t *block) {
+    lfs_off_t iitarget = ioff / lfs->words;
+    lfs_off_t iicurrent = (icount-1) / lfs->words;
 
     while (iitarget != iicurrent) {
         lfs_size_t skip = lfs_min(
-                lfs_min(lfs_ctz(iicurrent+1), wcount-1),
+                lfs_min(lfs_ctz(iicurrent+1), lfs->words-1),
                 lfs_npw2((iitarget ^ iicurrent)+1)-1);
         
-        lfs_error_t err = lfs->ops->read(lfs->bd, (void*)&head,
-                head, 4*skip, 4);
+        int err = lfs_bd_read(lfs, head, 4*skip, 4, &head);
         if (err) {
             return err;
         }
@@ -97,46 +140,34 @@ static lfs_error_t lfs_ifind_block(lfs_t *lfs, lfs_ino_t head,
         iicurrent -= 1 << skip;
     }
 
-    *block = head;
-    return 0;
-}
-
-static lfs_error_t lfs_ifind(lfs_t *lfs, lfs_ino_t head,
-        lfs_size_t icount, lfs_off_t ioff, lfs_ino_t *ino) {
-    lfs_size_t wcount = lfs->info.erase_size/4;
-    int err = lfs_ifind_block(lfs, head, icount, ioff, &head);
-    if (err) {
-        return err;
-    }
-
-    return lfs->ops->read(lfs->bd, (void*)ino, head, 4*(ioff % wcount), 4);
+    return lfs_bd_read(lfs, head, 4*(ioff % lfs->words), 4, block);
 }
 
 // Append index to index chain, updates head and icount
-static lfs_error_t lfs_iappend(lfs_t *lfs, lfs_ino_t *headp,
-        lfs_size_t *icountp, lfs_ino_t ino) {
-    lfs_ino_t head = *headp;
+static int lfs_index_append(lfs_t *lfs, lfs_block_t *headp,
+        lfs_size_t *icountp, lfs_block_t block) {
+    lfs_block_t head = *headp;
     lfs_size_t ioff = *icountp - 1;
-    lfs_size_t wcount = lfs->info.erase_size/4;
 
     ioff += 1;
 
-    while (ioff % wcount == 0) {
-        lfs_ino_t nhead;
-        lfs_error_t err = lfs_alloc(lfs, &nhead);
+    while (ioff % lfs->words == 0) {
+        lfs_block_t nhead;
+        int err = lfs_alloc_erased(lfs, &nhead);
         if (err) {
             return err;
         }
 
-        lfs_off_t skips = lfs_min(lfs_ctz(ioff/wcount + 1), wcount-2) + 1;
+        lfs_off_t skips = lfs_min(
+                lfs_ctz(ioff/lfs->words + 1), lfs->words-2) + 1;
         for (lfs_off_t i = 0; i < skips; i++) {
-            err = lfs->ops->write(lfs->bd, (void*)&head, nhead, 4*i, 4);
+            err = lfs_bd_prog(lfs, nhead, 4*i, 4, &head);
             if (err) {
                 return err;
             }
 
             if (head && i != skips-1) {
-                err = lfs->ops->read(lfs->bd, (void*)&head, head, 4*i, 4);
+                err = lfs_bd_read(lfs, head, 4*i, 4, &head);
                 if (err) {
                     return err;
                 }
@@ -147,8 +178,7 @@ static lfs_error_t lfs_iappend(lfs_t *lfs, lfs_ino_t *headp,
         head = nhead;
     }
 
-    lfs_error_t err = lfs->ops->write(lfs->bd, (void*)&ino,
-            head, 4*(ioff % wcount), 4);
+    int err = lfs_bd_prog(lfs, head, 4*(ioff % lfs->words), 4, &block);
     if (err) {
         return err;
     }
@@ -158,35 +188,13 @@ static lfs_error_t lfs_iappend(lfs_t *lfs, lfs_ino_t *headp,
     return 0;
 }
 
-// Memory managment
-static lfs_error_t lfs_alloc(lfs_t *lfs, lfs_ino_t *ino) {
-    if (lfs->free.d.begin != lfs->free.d.end) {
-        *ino = lfs->free.d.begin;
-        lfs->free.d.begin += 1;
 
-        return lfs->ops->erase(lfs->bd, *ino, 0, lfs->info.erase_size);
-    }
+/// Metadata pair operations ///
 
-    // TODO find next stride of free blocks
-    // TODO verify no strides exist where begin > current begin
-    // note: begin = 0 is invalid (superblock)
-    return LFS_ERROR_NO_SPACE;
-}
-
-lfs_error_t lfs_check(lfs_t *lfs, lfs_ino_t block) {
-    uint32_t crc = 0xffffffff;
-
-    for (lfs_size_t i = 0; i < lfs->info.erase_size; i += 4) {
-        uint32_t data;
-        int err = lfs->ops->read(lfs->bd, (void*)&data, block, i, 4);
-        if (err) {
-            return err;
-        }
-
-        crc = lfs_crc((void*)&data, 4, crc);
-    }
-
-    return (crc != 0) ? LFS_ERROR_CORRUPT : LFS_ERROR_OK;
+static inline void lfs_swap(lfs_block_t pair[2]) {
+    lfs_block_t t = pair[0];
+    pair[0] = pair[1];
+    pair[1] = t;
 }
 
 struct lfs_fetch_region {
@@ -195,43 +203,44 @@ struct lfs_fetch_region {
     void *data;
 };
 
-lfs_error_t lfs_pair_fetch(lfs_t *lfs, lfs_ino_t pair[2],
+static int lfs_pair_fetch(lfs_t *lfs, lfs_block_t pair[2],
         int count, const struct lfs_fetch_region *regions) {
     int checked = 0;
     int rev = 0;
     for (int i = 0; i < 2; i++) {
         uint32_t nrev;
-        int err = lfs->ops->read(lfs->bd, (void*)&nrev,
-                pair[0], 0, 4);
+        int err = lfs_bd_read(lfs, pair[1], 0, 4, &nrev);
         if (err) {
             return err;
         }
 
-        // TODO diff these
-        if (checked > 0 && lfs_diff(nrev, rev) < 0) {
+        if (checked > 0 && lfs_scmp(nrev, rev) < 0) {
             continue;
         }
 
-        err = lfs_check(lfs, pair[0]);
-        if (err == LFS_ERROR_CORRUPT) {
-            lfs_swap(&pair[0], &pair[1]);
-            continue;
-        } else if (err) {
+        uint32_t crc = 0xffffffff;
+        err = lfs_bd_crc(lfs, pair[1], 0, lfs->block_size, &crc);
+        if (err) {
             return err;
         }
 
+        if (crc != 0) {
+            lfs_swap(pair);
+        }
+
         checked += 1;
         rev = nrev;
-        lfs_swap(&pair[0], &pair[1]);
+        lfs_swap(pair);
     }
 
     if (checked == 0) {
+        LFS_ERROR("Corrupted metadata pair at %d %d", pair[0], pair[1]);
         return LFS_ERROR_CORRUPT;
     }
 
     for (int i = 0; i < count; i++) {
-        int err = lfs->ops->read(lfs->bd, regions[i].data,
-                pair[1], regions[i].off, regions[i].size);
+        int err = lfs_bd_read(lfs, pair[0],
+                regions[i].off, regions[i].size, regions[i].data);
         if (err) {
             return err;
         }
@@ -246,21 +255,20 @@ struct lfs_commit_region {
     const void *data;
 };
 
-lfs_error_t lfs_pair_commit(lfs_t *lfs, lfs_ino_t pair[2],
+static int lfs_pair_commit(lfs_t *lfs, lfs_block_t pair[2],
         int count, const struct lfs_commit_region *regions) {
     uint32_t crc = 0xffffffff;
-    int err = lfs->ops->erase(lfs->bd,
-            pair[0], 0, lfs->info.erase_size);
+    int err = lfs_bd_erase(lfs, pair[1], 0, lfs->block_size);
     if (err) {
         return err;
     }
 
     lfs_off_t off = 0;
-    while (off < lfs->info.erase_size - 4) {
+    while (off < lfs->block_size - 4) {
         if (count > 0 && regions[0].off == off) {
             crc = lfs_crc(regions[0].data, regions[0].size, crc);
-            int err = lfs->ops->write(lfs->bd, regions[0].data,
-                    pair[0], off, regions[0].size);
+            int err = lfs_bd_prog(lfs, pair[1],
+                    off, regions[0].size, regions[0].data);
             if (err) {
                 return err;
             }
@@ -271,34 +279,39 @@ lfs_error_t lfs_pair_commit(lfs_t *lfs, lfs_ino_t pair[2],
         } else {
             // TODO faster strides?
             uint8_t data;
-            int err = lfs->ops->read(lfs->bd, (void*)&data,
-                    pair[1], off, sizeof(data));
+            int err = lfs_bd_read(lfs, pair[0], off, 1, &data);
             if (err) {
                 return err;
             }
 
-            crc = lfs_crc((void*)&data, sizeof(data), crc);
-            err = lfs->ops->write(lfs->bd, (void*)&data,
-                    pair[0], off, sizeof(data));
+            crc = lfs_crc((void*)&data, 1, crc);
+            err = lfs_bd_prog(lfs, pair[1], off, 1, &data);
             if (err) {
                 return err;
             }
 
-            off += sizeof(data);
+            off += 1;
         }
     }
 
-    err = lfs->ops->write(lfs->bd, (void*)&crc,
-            pair[0], lfs->info.erase_size-4, 4);
+    err = lfs_bd_prog(lfs, pair[1], lfs->block_size-4, 4, &crc);
     if (err) {
         return err;
     }
 
-    lfs_swap(&pair[0], &pair[1]);
+    err = lfs_bd_sync(lfs);
+    if (err) {
+        return err;
+    }
+
+    lfs_swap(pair);
     return 0;
 }
 
-lfs_error_t lfs_dir_make(lfs_t *lfs, lfs_dir_t *dir, lfs_ino_t parent[2]) {
+
+/// Directory operations ///
+
+static int lfs_dir_create(lfs_t *lfs, lfs_dir_t *dir, lfs_block_t parent[2]) {
     // Allocate pair of dir blocks
     for (int i = 0; i < 2; i++) {
         int err = lfs_alloc(lfs, &dir->pair[i]);
@@ -309,89 +322,77 @@ lfs_error_t lfs_dir_make(lfs_t *lfs, lfs_dir_t *dir, lfs_ino_t parent[2]) {
 
     // Rather than clobbering one of the blocks we just pretend
     // the revision may be valid
-    int err = lfs->ops->read(lfs->bd, (void*)&dir->d.rev, dir->pair[1], 0, 4);
+    int err = lfs_bd_read(lfs, dir->pair[0], 0, 4, &dir->d.rev);
     if (err) {
         return err;
     }
     dir->d.rev += 1;
 
+    // Calculate total size
+    dir->d.size = sizeof(dir->d);
+    if (parent) {
+        dir->d.size += sizeof(struct lfs_disk_entry);
+    }
+
     // Other defaults
-    dir->i = sizeof(struct lfs_disk_dir);
-    dir->d.size = sizeof(struct lfs_disk_dir);
+    dir->off = dir->d.size;
     dir->d.tail[0] = 0;
     dir->d.tail[1] = 0;
-    dir->d.free = lfs->free.d;
-
-    if (parent) {
-        // Create '..' entry
-        lfs_entry_t entry = {
-            .d.type = LFS_TYPE_DIR,
-            .d.len = sizeof(entry.d) + 2,
-            .d.u.dir[0] = parent[0],
-            .d.u.dir[1] = parent[1],
-        };
-
-        dir->d.size += entry.d.len;
-
-        // Write out to memory
-        return lfs_pair_commit(lfs, dir->pair,
-            3, (struct lfs_commit_region[3]){
-                {0, sizeof(dir->d), &dir->d},
-                {sizeof(dir->d), sizeof(entry.d), &entry.d},
-                {sizeof(dir->d)+sizeof(entry.d), 2, ".."},
-            });
-    } else {
-        return lfs_pair_commit(lfs, dir->pair,
-            1, (struct lfs_commit_region[1]){
-                {0, sizeof(dir->d), &dir->d},
-            });
-    }
+    dir->d.free = lfs->free;
+
+    // Write out to memory
+    return lfs_pair_commit(lfs, dir->pair,
+        1 + (parent ? 2 : 0), (struct lfs_commit_region[]){
+            {0, sizeof(dir->d), &dir->d},
+            {sizeof(dir->d), sizeof(struct lfs_disk_entry),
+             &(struct lfs_disk_entry){
+                .type     = LFS_TYPE_DIR,
+                .len      = 12+2,
+                .u.dir[0] = parent ? parent[0] : 0,
+                .u.dir[1] = parent ? parent[1] : 0,
+            }},
+            {sizeof(dir->d)+sizeof(struct lfs_disk_entry), 2, ".."},
+        });
 }
 
-lfs_error_t lfs_dir_fetch(lfs_t *lfs, lfs_dir_t *dir, lfs_ino_t pair[2]) {
+static int lfs_dir_fetch(lfs_t *lfs, lfs_dir_t *dir, lfs_block_t pair[2]) {
     dir->pair[0] = pair[0];
     dir->pair[1] = pair[1];
-    dir->i = sizeof(dir->d);
+    dir->off = sizeof(dir->d);
 
-    int err = lfs_pair_fetch(lfs, dir->pair,
+    return lfs_pair_fetch(lfs, dir->pair,
         1, (struct lfs_fetch_region[1]) {
             {0, sizeof(dir->d), &dir->d}
         });
-
-    if (err == LFS_ERROR_CORRUPT) {
-        LFS_ERROR("Corrupted dir at %d %d", pair[0], pair[1]);
-    }
-
-    return err;
 }
 
-lfs_error_t lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
+static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
     while (true) {
         // TODO iterate down list
         entry->dir[0] = dir->pair[0];
         entry->dir[1] = dir->pair[1];
-        entry->off = dir->i;
+        entry->off = dir->off;
 
-        if (dir->d.size - dir->i < sizeof(entry->d)) {
+        if (dir->d.size - dir->off < sizeof(entry->d)) {
             return LFS_ERROR_NO_ENTRY;
         }
 
-        int err = lfs->ops->read(lfs->bd, (void*)&entry->d,
-                dir->pair[1], dir->i, sizeof(entry->d));
+        int err = lfs_bd_read(lfs, dir->pair[0], dir->off,
+                sizeof(entry->d), &entry->d);
         if (err) {
             return err;
         }
 
-        dir->i += entry->d.len;
+        dir->off += entry->d.len;
 
         // Skip any unknown entries
-        if (entry->d.type == 1 || entry->d.type == 2) {
+        if ((entry->d.type & 0xf) == 1 || (entry->d.type & 0xf) == 2) {
             return 0;
         }
     }
 }
 
-lfs_error_t lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
+static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
         const char *path, lfs_entry_t *entry) {
     // TODO follow directories
     lfs_size_t pathlen = strcspn(path, "/");
@@ -405,7 +406,7 @@ lfs_error_t lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
             continue;
         }
 
-        int ret = lfs_bd_cmp(lfs, entry->dir[1],
+        int ret = lfs_bd_cmp(lfs, entry->dir[0],
                 entry->off + sizeof(entry->d), pathlen, path);
         if (ret < 0) {
             return ret;
@@ -418,44 +419,22 @@ lfs_error_t lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
     }
 }
 
-lfs_error_t lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir,
-        const char *path, lfs_entry_t *entry, uint16_t len) {
+static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
+        const char *path, lfs_entry_t *entry) {
     int err = lfs_dir_find(lfs, dir, path, entry);
     if (err != LFS_ERROR_NO_ENTRY) {
         return err ? err : LFS_ERROR_EXISTS;
     }
 
     // Check if we fit
-    if (dir->d.size + len > lfs->info.erase_size - 4) {
+    if (dir->d.size + strlen(path) > lfs->block_size - 4) {
         return -1; // TODO make fit
     }
 
     return 0;
 }
 
-lfs_error_t lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
-    int err = lfs_dir_fetch(lfs, dir, lfs->cwd);
-    if (err) {
-        return err;
-    }
-    
-    lfs_entry_t entry;
-    err = lfs_dir_find(lfs, dir, path, &entry);
-    if (err) {
-        return err;
-    } else if (entry.d.type != LFS_TYPE_DIR) {
-        return LFS_ERROR_NOT_DIR;
-    }
-
-    return lfs_dir_fetch(lfs, dir, entry.d.u.dir);
-}
-
-lfs_error_t lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir) {
-    // Do nothing, dir is always synchronized
-    return 0;
-}
-
-lfs_error_t lfs_mkdir(lfs_t *lfs, const char *path) {
+int lfs_mkdir(lfs_t *lfs, const char *path) {
     // Allocate entry for directory
     lfs_dir_t cwd;
     int err = lfs_dir_fetch(lfs, &cwd, lfs->cwd);
@@ -464,27 +443,26 @@ lfs_error_t lfs_mkdir(lfs_t *lfs, const char *path) {
     }
 
     lfs_entry_t entry;
-    err = lfs_dir_alloc(lfs, &cwd, path,
-            &entry, sizeof(entry.d)+strlen(path));
+    err = lfs_dir_append(lfs, &cwd, path, &entry);
     if (err) {
         return err;
     }
 
     // Build up new directory
     lfs_dir_t dir;
-    err = lfs_dir_make(lfs, &dir, cwd.pair); // TODO correct parent?
+    err = lfs_dir_create(lfs, &dir, cwd.pair);
     if (err) {
         return err;
     }
 
-    entry.d.type = 2;
+    entry.d.type = LFS_TYPE_DIR;
     entry.d.len = sizeof(entry.d) + strlen(path);
     entry.d.u.dir[0] = dir.pair[0];
     entry.d.u.dir[1] = dir.pair[1];
 
     cwd.d.rev += 1;
     cwd.d.size += entry.d.len;
-    cwd.d.free = lfs->free.d;
+    cwd.d.free = lfs->free;
 
     return lfs_pair_commit(lfs, entry.dir,
         3, (struct lfs_commit_region[3]) {
@@ -494,7 +472,32 @@ lfs_error_t lfs_mkdir(lfs_t *lfs, const char *path) {
         });
 }
 
-lfs_error_t lfs_file_open(lfs_t *lfs, lfs_file_t *file,
+int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
+    int err = lfs_dir_fetch(lfs, dir, lfs->cwd);
+    if (err) {
+        return err;
+    }
+    
+    lfs_entry_t entry;
+    err = lfs_dir_find(lfs, dir, path, &entry);
+    if (err) {
+        return err;
+    } else if (entry.d.type != LFS_TYPE_DIR) {
+        return LFS_ERROR_NOT_DIR;
+    }
+
+    return lfs_dir_fetch(lfs, dir, entry.d.u.dir);
+}
+
+int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir) {
+    // Do nothing, dir is always synchronized
+    return 0;
+}
+
+
+/// File operations ///
+
+int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
         const char *path, int flags) {
     // Allocate entry for file if it doesn't exist
     // TODO check open files
@@ -505,8 +508,7 @@ lfs_error_t lfs_file_open(lfs_t *lfs, lfs_file_t *file,
     }
 
     if (flags & LFS_O_CREAT) {
-        err = lfs_dir_alloc(lfs, &cwd, path,
-                &file->entry, sizeof(file->entry.d)+strlen(path));
+        err = lfs_dir_append(lfs, &cwd, path, &file->entry);
         if (err && err != LFS_ERROR_EXISTS) {
             return err;
         }
@@ -534,15 +536,17 @@ lfs_error_t lfs_file_open(lfs_t *lfs, lfs_file_t *file,
 
         cwd.d.rev += 1;
         cwd.d.size += file->entry.d.len;
-        cwd.d.free = lfs->free.d;
+        cwd.d.free = lfs->free;
 
         return lfs_pair_commit(lfs, file->entry.dir,
             3, (struct lfs_commit_region[3]) {
                 {0, sizeof(cwd.d), &cwd.d},
-                {file->entry.off, sizeof(file->entry.d),
-                        &file->entry.d},
+                {file->entry.off,
+                 sizeof(file->entry.d),
+                 &file->entry.d},
                 {file->entry.off+sizeof(file->entry.d),
-                        file->entry.d.len-sizeof(file->entry.d), path}
+                 file->entry.d.len-sizeof(file->entry.d),
+                 path}
             });
     } else {
         file->head = file->entry.d.u.file.head;
@@ -554,10 +558,10 @@ lfs_error_t lfs_file_open(lfs_t *lfs, lfs_file_t *file,
 
         // TODO do this lazily in write?
         // TODO cow the head i/d block
-        if (file->size < lfs->info.erase_size) {
+        if (file->size < lfs->block_size) {
             file->wblock = file->head;
         } else {
-            int err = lfs_ifind(lfs, file->head, file->windex,
+            int err = lfs_index_find(lfs, file->head, file->windex,
                     file->windex, &file->wblock);
             if (err) {
                 return err;
@@ -568,7 +572,7 @@ lfs_error_t lfs_file_open(lfs_t *lfs, lfs_file_t *file,
     }
 }
 
-lfs_error_t lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
+int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
     // Store file
     lfs_dir_t cwd;
     int err = lfs_dir_fetch(lfs, &cwd, file->entry.dir);
@@ -580,7 +584,7 @@ lfs_error_t lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
     file->entry.d.u.file.size = file->size;
 
     cwd.d.rev += 1;
-    cwd.d.free = lfs->free.d;
+    cwd.d.free = lfs->free;
 
     return lfs_pair_commit(lfs, file->entry.dir,
         3, (struct lfs_commit_region[3]) {
@@ -595,10 +599,10 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
     lfs_size_t nsize = size;
 
     while (nsize > 0) {
-        lfs_off_t woff = file->size % lfs->info.erase_size;
+        lfs_off_t woff = file->size % lfs->block_size;
 
         if (file->size == 0) {
-            int err = lfs_alloc(lfs, &file->wblock);
+            int err = lfs_alloc_erased(lfs, &file->wblock);
             if (err) {
                 return err;
             }
@@ -606,21 +610,20 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
             file->head = file->wblock;
             file->windex = 0;
         } else if (woff == 0) {
-            // TODO check that 2 blocks are available
-            // TODO check for available blocks for backing up scratch files?
-            int err = lfs_alloc(lfs, &file->wblock);
+            int err = lfs_alloc_erased(lfs, &file->wblock);
             if (err) {
                 return err;
             }
 
-            err = lfs_iappend(lfs, &file->head, &file->windex, file->wblock);
+            err = lfs_index_append(lfs, &file->head,
+                    &file->windex, file->wblock);
             if (err) {
                 return err;
             }
         }
 
-        lfs_size_t diff = lfs_min(nsize, lfs->info.erase_size - woff);
-        int err = lfs->ops->write(lfs->bd, data, file->wblock, woff, diff);
+        lfs_size_t diff = lfs_min(nsize, lfs->block_size - woff);
+        int err = lfs_bd_prog(lfs, file->wblock, woff, diff, data);
         if (err) {
             return err;
         }
@@ -639,25 +642,25 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
     lfs_size_t nsize = size;
 
     while (nsize > 0 && file->roff < file->size) {
-        lfs_off_t roff = file->roff % lfs->info.erase_size;
+        lfs_off_t roff = file->roff % lfs->block_size;
 
         // TODO cache index blocks
-        if (file->size < lfs->info.erase_size) {
+        if (file->size < lfs->block_size) {
             file->rblock = file->head;
         } else if (roff == 0) {
-            int err = lfs_ifind(lfs, file->head, file->windex,
+            int err = lfs_index_find(lfs, file->head, file->windex,
                     file->rindex, &file->rblock);
             if (err) {
                 return err;
             }
 
-            file->rindex = lfs_inext(lfs, file->rindex);
+            file->rindex = lfs_index_next(lfs, file->rindex);
         }
 
         lfs_size_t diff = lfs_min(
                 lfs_min(nsize, file->size-file->roff),
-                lfs->info.erase_size - roff);
-        int err = lfs->ops->read(lfs->bd, data, file->rblock, roff, diff);
+                lfs->block_size - roff);
+        int err = lfs_bd_read(lfs, file->rblock, roff, diff, data);
         if (err) {
             return err;
         }
@@ -671,112 +674,106 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
 }
 
 
-
-// Little filesystem operations
-lfs_error_t lfs_create(lfs_t *lfs, lfs_bd_t *bd, const struct lfs_bd_ops *ops) {
+/// Generic filesystem operations ///
+int lfs_format(lfs_t *lfs, lfs_bd_t *bd, const struct lfs_bd_ops *bd_ops) {
     lfs->bd = bd;
-    lfs->ops = ops;
+    lfs->bd_ops = bd_ops;
 
-    lfs_error_t err = lfs->ops->info(lfs->bd, &lfs->info);
-    if (err) {
-        return err;
-    }
-    
-    return 0;
-}
-
-lfs_error_t lfs_format(lfs_t *lfs) {
     struct lfs_bd_info info;
-    lfs_error_t err = lfs->ops->info(lfs->bd, &info);
+    int err = lfs_bd_info(lfs, &info);
     if (err) {
         return err;
     }
 
-    err = lfs->ops->erase(lfs->bd, 0, 0, 3*info.erase_size);
-    if (err) {
-        return err;
-    }
-
-    // TODO make sure that erase clobbered blocks
+    lfs->read_size  = info.read_size;
+    lfs->prog_size  = info.prog_size;
+    lfs->block_size = info.erase_size;
+    lfs->block_count = info.total_size / info.erase_size;
+    lfs->words = info.erase_size / sizeof(uint32_t);
 
-    {
-        lfs_size_t block_count = lfs->info.total_size / lfs->info.erase_size;
+    // Create free list
+    lfs->free.begin = 2;
+    lfs->free.end = lfs->block_count;
 
-        // Create free list
-        lfs->free = (lfs_free_t){
-            .d.begin = 2,
-            .d.end = block_count,
-        };
+    // Write root directory
+    lfs_dir_t root;
+    err = lfs_dir_create(lfs, &root, 0);
+    if (err) {
+        return err;
     }
+    lfs->cwd[0] = root.pair[0];
+    lfs->cwd[1] = root.pair[1];
+
+    // Write superblocks
+    lfs_superblock_t superblock = {
+        .pair = {0, 1},
+        .d.rev = 1,
+        .d.size = sizeof(superblock),
+        .d.root = {lfs->cwd[0], lfs->cwd[1]},
+        .d.magic = {"littlefs"},
+        .d.block_size  = lfs->block_size,
+        .d.block_count = lfs->block_count,
+    };
 
-    {
-        // Write root directory
-        lfs_dir_t root;
-        int err = lfs_dir_make(lfs, &root, 0);
+    for (int i = 0; i < 2; i++) {
+        int err = lfs_pair_commit(lfs, superblock.pair,
+                1, (struct lfs_commit_region[]){
+                    {0, sizeof(superblock.d), &superblock.d}
+                });
         if (err) {
+            LFS_ERROR("Failed to write superblock at %d", superblock.pair[1]);
             return err;
         }
 
-        lfs->cwd[0] = root.pair[0];
-        lfs->cwd[1] = root.pair[1];
-    }
-
-    {
-        // Write superblocks
-        lfs_superblock_t superblock = {
-            .pair = {0, 1},
-            .d.rev = 1,
-            .d.size = sizeof(struct lfs_disk_superblock),
-            .d.root = {lfs->cwd[0], lfs->cwd[1]},
-            .d.magic = {"littlefs"},
-            .d.block_size = info.erase_size,
-            .d.block_count = info.total_size / info.erase_size,
-        };
-
-        for (int i = 0; i < 2; i++) {
-            lfs_ino_t block = superblock.pair[0];
-            int err = lfs_pair_commit(lfs, superblock.pair,
-                    1, (struct lfs_commit_region[1]){
-                        {0, sizeof(superblock.d), &superblock.d}
-                    });
-
-            err = lfs_check(lfs, block);
-            if (err) {
-                LFS_ERROR("Failed to write superblock at %d", block);
-                return err;
-            }
+        uint32_t crc = 0xffffffff;
+        err = lfs_bd_crc(lfs, superblock.pair[0], 0, lfs->block_size, &crc);
+        if (err || crc != 0) {
+            LFS_ERROR("Failed to write superblock at %d", superblock.pair[0]);
+            return err ? err : LFS_ERROR_CORRUPT;
         }
     }
 
     return 0;
 }
 
-lfs_error_t lfs_mount(lfs_t *lfs) {
+int lfs_mount(lfs_t *lfs, lfs_bd_t *bd, const struct lfs_bd_ops *bd_ops) {
+    lfs->bd = bd;
+    lfs->bd_ops = bd_ops;
+
     struct lfs_bd_info info;
-    lfs_error_t err = lfs->ops->info(lfs->bd, &info);
+    int err = lfs_bd_info(lfs, &info);
     if (err) {
         return err;
     }
 
-    lfs_superblock_t superblock;
-    err = lfs_pair_fetch(lfs,
-            (lfs_ino_t[2]){0, 1},
-            1, (struct lfs_fetch_region[1]){
+    lfs->read_size  = info.read_size;
+    lfs->prog_size  = info.prog_size;
+    lfs->block_size = info.erase_size;
+    lfs->block_count = info.total_size / info.erase_size;
+    lfs->words = info.erase_size / sizeof(uint32_t);
+
+    lfs_superblock_t superblock = {
+        .pair = {0, 1},
+    };
+    err = lfs_pair_fetch(lfs, superblock.pair,
+            1, (struct lfs_fetch_region[]){
                 {0, sizeof(superblock.d), &superblock.d}
             });
 
     if ((err == LFS_ERROR_CORRUPT ||
             memcmp(superblock.d.magic, "littlefs", 8) != 0)) {
-        LFS_ERROR("Invalid superblock at %d %d\n", 0, 1);
+        LFS_ERROR("Invalid superblock at %d %d\n",
+                superblock.pair[0], superblock.pair[1]);
         return LFS_ERROR_CORRUPT;
     }
 
-    printf("superblock %d %d\n",
-            superblock.d.block_size,
-            superblock.d.block_count);
-
     lfs->cwd[0] = superblock.d.root[0];
     lfs->cwd[1] = superblock.d.root[1];
 
     return err;
 }
+
+int lfs_unmount(lfs_t *lfs) {
+    // No nothing for now
+    return 0;
+}

+ 45 - 42
lfs.h

@@ -38,63 +38,61 @@ enum lfs_open_flags {
     LFS_O_SYNC   = 0x1000,
 };
 
-typedef struct lfs_free {
-    lfs_disk_struct lfs_disk_free {
-        lfs_word_t begin;
-        lfs_word_t end;
-    } d;
-} lfs_free_t;
-
-typedef struct lfs_dir {
-    lfs_ino_t pair[2];
-    lfs_off_t i;
-
-    lfs_disk_struct lfs_disk_dir {
-        lfs_word_t rev;
-        lfs_size_t size;
-        lfs_ino_t tail[2];
 
-        struct lfs_disk_free free;
-    } d;
-} lfs_dir_t;
 
 typedef struct lfs_entry {
-    lfs_ino_t dir[2];
+    lfs_block_t dir[2];
     lfs_off_t off;
 
-    lfs_disk_struct lfs_disk_entry {
+    struct lfs_disk_entry {
         uint16_t type;
         uint16_t len;
         union {
-            lfs_disk_struct {
-                lfs_ino_t head;
+            struct {
+                lfs_block_t head;
                 lfs_size_t size;
             } file;
-            lfs_ino_t dir[2];
+            lfs_block_t dir[2];
         } u;
     } d;
 } lfs_entry_t;
 
 typedef struct lfs_file {
-    lfs_ino_t head;
+    lfs_block_t head;
     lfs_size_t size;
 
-    lfs_ino_t wblock;
-    lfs_word_t windex;
+    lfs_block_t wblock;
+    uint32_t windex;
 
-    lfs_ino_t rblock;
-    lfs_word_t rindex;
+    lfs_block_t rblock;
+    uint32_t rindex;
     lfs_off_t roff;
 
     struct lfs_entry entry;
 } lfs_file_t;
 
+typedef struct lfs_dir {
+    lfs_block_t pair[2];
+    lfs_off_t off;
+
+    struct lfs_disk_dir {
+        uint32_t rev;
+        lfs_size_t size;
+        lfs_block_t tail[2];
+
+        struct lfs_disk_free {
+            uint32_t begin;
+            uint32_t end;
+        } free;
+    } d;
+} lfs_dir_t;
+
 typedef struct lfs_superblock {
-    lfs_ino_t pair[2];
-    lfs_disk_struct lfs_disk_superblock {
-        lfs_word_t rev;
+    lfs_block_t pair[2];
+    struct lfs_disk_superblock {
+        uint32_t rev;
         uint32_t size;
-        lfs_ino_t root[2];
+        lfs_block_t root[2];
         char magic[8];
         uint32_t block_size;
         uint32_t block_count;
@@ -104,23 +102,28 @@ typedef struct lfs_superblock {
 // Little filesystem type
 typedef struct lfs {
     lfs_bd_t *bd;
-    const struct lfs_bd_ops *ops;
+    const struct lfs_bd_ops *bd_ops;
+
+    lfs_block_t cwd[2];
+    struct lfs_disk_free free;
 
-    lfs_ino_t cwd[2];
-    lfs_free_t free;
-    struct lfs_bd_info info;
+    lfs_size_t read_size;   // size of read
+    lfs_size_t prog_size;   // size of program
+    lfs_size_t block_size;  // size of erase (block size)
+    lfs_size_t block_count; // number of erasable blocks
+    lfs_size_t words;       // number of 32-bit words that can fit in a block
 } lfs_t;
 
 // Functions
-lfs_error_t lfs_create(lfs_t *lfs, lfs_bd_t *bd, const struct lfs_bd_ops *bd_ops);
-lfs_error_t lfs_format(lfs_t *lfs);
-lfs_error_t lfs_mount(lfs_t *lfs);
+int lfs_format(lfs_t *lfs, lfs_bd_t *bd, const struct lfs_bd_ops *bd_ops);
+int lfs_mount(lfs_t *lfs, lfs_bd_t *bd, const struct lfs_bd_ops *bd_ops);
+int lfs_unmount(lfs_t *lfs);
 
-lfs_error_t lfs_mkdir(lfs_t *lfs, const char *path);
+int lfs_mkdir(lfs_t *lfs, const char *path);
 
-lfs_error_t lfs_file_open(lfs_t *lfs, lfs_file_t *file,
+int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
         const char *path, int flags);
-lfs_error_t lfs_file_close(lfs_t *lfs, lfs_file_t *file);
+int lfs_file_close(lfs_t *lfs, lfs_file_t *file);
 lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
         const void *buffer, lfs_size_t size);
 lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,

+ 10 - 10
lfs_bd.h

@@ -16,10 +16,10 @@ typedef void lfs_bd_t;
 // Description of block devices
 struct lfs_bd_info {
     lfs_size_t read_size;   // Size of readable block
-    lfs_size_t write_size;  // Size of programmable block
+    lfs_size_t prog_size;   // Size of programmable block
     lfs_size_t erase_size;  // Size of erase block
 
-    lfs_lsize_t total_size; // Total size of the device
+    uint64_t total_size; // Total size of the device
 };
 
 // Block device operations
@@ -30,29 +30,29 @@ struct lfs_bd_info {
 // block device
 struct lfs_bd_ops {
     // Read a block
-    lfs_error_t (*read)(lfs_bd_t *bd, uint8_t *buffer,
-            lfs_ino_t ino, lfs_off_t off, lfs_size_t size);
+    int (*read)(lfs_bd_t *bd, lfs_block_t block,
+            lfs_off_t off, lfs_size_t size, void *buffer);
 
     // Program a block
     //
     // The block must have previously been erased.
-    lfs_error_t (*write)(lfs_bd_t *bd, const uint8_t *buffer,
-            lfs_ino_t ino, lfs_off_t off, lfs_size_t size);
+    int (*prog)(lfs_bd_t *bd, lfs_block_t block,
+            lfs_off_t off, lfs_size_t size, const void *buffer);
 
     // Erase a block
     //
     // A block must be erased before being programmed. The
     // state of an erased block is undefined.
-    lfs_error_t (*erase)(lfs_bd_t *bd,
-            lfs_ino_t ino, lfs_off_t off, lfs_size_t size);
+    int (*erase)(lfs_bd_t *bd, lfs_block_t block,
+            lfs_off_t off, lfs_size_t size);
 
     // Sync the block device
-    lfs_error_t (*sync)(lfs_bd_t *bd);
+    int (*sync)(lfs_bd_t *bd);
 
     // Get a description of the block device
     //
     // Any unknown information may be left as zero
-    lfs_error_t (*info)(lfs_bd_t *bd, struct lfs_bd_info *info);
+    int (*info)(lfs_bd_t *bd, struct lfs_bd_info *info);
 };
 
 

+ 5 - 36
lfs_config.h

@@ -10,48 +10,17 @@
 #include <stdint.h>
 
 // Type definitions
-typedef uint64_t lfs_lword_t;
-typedef uint32_t lfs_word_t;
-typedef uint16_t lfs_hword_t;
+typedef uint32_t lfs_size_t;
+typedef uint32_t lfs_off_t;
 
-typedef lfs_word_t lfs_size_t;
-typedef int32_t lfs_ssize_t;
-typedef lfs_word_t lfs_off_t;
-typedef int lfs_error_t;
+typedef int32_t  lfs_ssize_t;
+typedef int32_t  lfs_soff_t;
 
-typedef lfs_lword_t lfs_lsize_t;
-typedef lfs_word_t  lfs_ino_t;
-typedef lfs_hword_t lfs_ioff_t;
+typedef uint32_t lfs_block_t;
 
 // Maximum length of file name
 #define LFS_NAME_MAX 255
 
-// Builtin functions
-static inline lfs_word_t lfs_max(lfs_word_t a, lfs_word_t b) {
-    return (a > b) ? a : b;
-}
-
-static inline lfs_word_t lfs_min(lfs_word_t a, lfs_word_t b) {
-    return (a < b) ? a : b;
-}
-
-static inline lfs_word_t lfs_ctz(lfs_word_t a) {
-    return __builtin_ctz(a);
-}
-
-static inline lfs_word_t lfs_npw2(lfs_word_t a) {
-    return 32 - __builtin_clz(a-1);
-}
-
-static inline void lfs_swap(lfs_word_t *a, lfs_word_t *b) {
-    lfs_word_t temp = *a;
-    *a = *b;
-    *b = temp;
-}
-
-// Attributes
-#define lfs_disk_struct struct __attribute__((packed))
-
 // Logging operations
 #include <stdio.h>
 #define LFS_ERROR(fmt, ...) printf("Error: " fmt "\n", __VA_ARGS__)

+ 27 - 0
lfs_util.c

@@ -0,0 +1,27 @@
+/*
+ * lfs util functions
+ *
+ * Copyright (c) 2017 Christopher Haster
+ * Distributed under the MIT license
+ */
+#include "lfs_util.h"
+
+
+uint32_t lfs_crc(const void *buffer, lfs_size_t size, uint32_t crc) {
+    static const uint32_t rtable[16] = {
+        0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
+        0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+        0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
+        0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
+    };
+
+    const uint8_t *data = buffer;
+
+    for (lfs_size_t i = 0; i < size; i++) {
+        crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 0)) & 0xf];
+        crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf];
+    }
+
+    return crc;
+}
+

+ 38 - 0
lfs_util.h

@@ -0,0 +1,38 @@
+/*
+ * lfs utility functions
+ *
+ * Copyright (c) 2017 Christopher Haster
+ * Distributed under the MIT license
+ */
+#ifndef LFS_UTIL_H
+#define LFS_UTIL_H
+
+#include "lfs_config.h"
+
+
+// Builtin functions
+static inline uint32_t lfs_max(uint32_t a, uint32_t b) {
+    return (a > b) ? a : b;
+}
+
+static inline uint32_t lfs_min(uint32_t a, uint32_t b) {
+    return (a < b) ? a : b;
+}
+
+static inline uint32_t lfs_ctz(uint32_t a) {
+    return __builtin_ctz(a);
+}
+
+static inline uint32_t lfs_npw2(uint32_t a) {
+    return 32 - __builtin_clz(a-1);
+}
+
+static inline int lfs_scmp(uint32_t a, uint32_t b) {
+    return (int)(unsigned)(a - b);
+}
+
+uint32_t lfs_crc(const void *buffer, lfs_size_t size, uint32_t crc);
+
+
+
+#endif