Ver código fonte

Initial commit of progress, minimal formatting niave free list

Christopher Haster 8 anos atrás
pai
commit
160299d35c
5 arquivos alterados com 344 adições e 7 exclusões
  1. 11 2
      emubd/lfs_emubd.c
  2. 13 0
      emubd/lfs_emubd.h
  3. 280 0
      lfs.c
  4. 32 0
      lfs.h
  5. 8 5
      lfs_bd.h

+ 11 - 2
emubd/lfs_emubd.c

@@ -18,6 +18,7 @@
 // Block device emulated on existing filesystem
 lfs_error_t lfs_emubd_create(lfs_emubd_t *emu, const char *path) {
     memset(&emu->info, 0, sizeof(emu->info));
+    memset(&emu->stats, 0, sizeof(emu->stats));
 
     // Allocate buffer for creating children files
     size_t pathlen = strlen(path);
@@ -64,8 +65,8 @@ lfs_error_t lfs_emubd_read(lfs_emubd_t *emu, uint8_t *buffer,
         return -EINVAL;
     }
 
-    // Default to some arbitrary value for debugging
-    memset(buffer, 0xcc, size);
+    // Zero out buffer for debugging
+    memset(buffer, 0, size);
 
     // Iterate over blocks until enough data is read
     while (size > 0) {
@@ -100,6 +101,7 @@ lfs_error_t lfs_emubd_read(lfs_emubd_t *emu, uint8_t *buffer,
         off = 0;
     }
 
+    emu->stats.read_count += 1;
     return 0;
 }
 
@@ -148,6 +150,7 @@ lfs_error_t lfs_emubd_write(lfs_emubd_t *emu, const uint8_t *buffer,
         off = 0;
     }
 
+    emu->stats.write_count += 1;
     return 0;
 }
 
@@ -175,6 +178,7 @@ lfs_error_t lfs_emubd_erase(lfs_emubd_t *emu,
         off = 0;
     }
 
+    emu->stats.erase_count += 1;
     return 0;
 }
 
@@ -188,6 +192,11 @@ lfs_error_t lfs_emubd_info(lfs_emubd_t *emu, struct lfs_bd_info *info) {
     return 0;
 }
 
+lfs_error_t 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,

+ 13 - 0
emubd/lfs_emubd.h

@@ -11,11 +11,19 @@
 #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;
+};
+
 // The emu bd state
 typedef struct lfs_emubd {
     char *path;
     char *child;
     struct lfs_bd_info info;
+    struct lfs_bd_stats stats;
 } lfs_emubd_t;
 
 
@@ -50,6 +58,11 @@ lfs_error_t lfs_emubd_sync(lfs_emubd_t *bd);
 // Any unknown information may be left unmodified
 lfs_error_t 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);
+
 // Block device operations
 extern const struct lfs_bd_ops lfs_emubd_ops;
 

+ 280 - 0
lfs.c

@@ -0,0 +1,280 @@
+/*
+ * The little filesystem
+ *
+ * Copyright (c) 2017 Christopher Haster
+ * Distributed under the MIT license
+ */
+#include "lfs.h"
+
+#include <string.h>
+#include <stdbool.h>
+
+
+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,
+    };
+
+    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;
+}
+
+lfs_error_t lfs_create(lfs_t *lfs, lfs_bd_t *bd, const struct lfs_bd_ops *ops) {
+    // TODO rm me, for debugging
+    memset(lfs, 0, sizeof(lfs_t));
+
+    lfs->bd = bd;
+    lfs->ops = ops;
+
+    lfs_error_t err = lfs->ops->info(lfs->bd, &lfs->info);
+    if (err) {
+        return err;
+    }
+    
+    return 0;
+}
+
+static lfs_off_t lfs_calc_irem(lfs_t *lfs, lfs_size_t isize) {
+    lfs_size_t icount = lfs->info.erase_size/4;
+
+    if (isize <= icount) {
+        return isize;
+    } else {
+        return ((isize-2) % (icount-1)) + 1;
+    }
+}
+
+static lfs_off_t lfs_calc_ioff(lfs_t *lfs, lfs_size_t ioff) {
+    lfs_size_t icount = lfs->info.erase_size/4;
+
+    if (ioff < icount) {
+        return ioff;
+    } else {
+        return ((ioff-1) % (icount-1)) + 1;
+    }
+}
+
+static lfs_error_t lfs_ifind(lfs_t *lfs, lfs_ino_t head,
+        lfs_size_t isize, lfs_off_t ioff, lfs_ino_t *ino) {
+    if (ioff >= isize) {
+        return -15;
+    } else if (isize == 1) {
+        *ino = head;
+        return 0;
+    }
+
+    lfs_off_t ilookback = isize - ioff;
+    lfs_off_t irealoff = lfs_calc_ioff(lfs, ioff);
+
+    while (true) {
+        lfs_size_t irem = lfs_calc_irem(lfs, isize);
+        if (ilookback <= irem) {
+            return lfs->ops->read(lfs->bd, (void*)ino,
+                    head, 4*irealoff, 4);
+        }
+
+        lfs_error_t err = lfs->ops->read(lfs->bd, (void*)&head, head, 0, 4);
+        if (err) {
+            return err;
+        }
+        ilookback -= irem;
+        isize -= irem;
+    }
+}
+
+static lfs_error_t lfs_alloc(lfs_t *lfs, lfs_ino_t *ino) {
+    lfs_error_t err = lfs_ifind(lfs, lfs->free.head,
+            lfs->free.rev[1], lfs->free.rev[0], ino);
+    if (err) {
+        return err;
+    }
+
+    err = lfs->ops->erase(lfs->bd, *ino, 0, lfs->info.erase_size);
+    if (err) {
+        return err;
+    }
+
+    lfs->free.rev[0] += 1;
+    return 0;
+}
+
+static lfs_error_t lfs_free(lfs_t *lfs, lfs_ino_t ino) {
+    // TODO handle overflow?
+    if (lfs->free.rev[1] == 0) {
+        lfs->free.head = ino;
+        lfs->free.rev[1]++;
+        lfs->free.off = lfs->info.erase_size;
+        return 0;
+    }
+
+    if (lfs->free.off == lfs->info.erase_size || !lfs->free.head) {
+        lfs_ino_t nhead = 0;
+        lfs_error_t err = lfs_alloc(lfs, &nhead);
+        if (err) {
+            return err;
+        }
+
+        if (lfs->free.off == lfs->info.erase_size) {
+            err = lfs->ops->write(lfs->bd, (void*)&lfs->free.head, nhead, 0, 4);
+            if (err) {
+                return err;
+            }
+        } else {
+            for (lfs_off_t i = 0; i < lfs->free.off; i += 4) {
+                lfs_ino_t ino;
+                lfs_error_t err = lfs->ops->read(lfs->bd, (void*)&ino,
+                        lfs->free.phead, i, 4);
+                if (err) {
+                    return err;
+                }
+
+                err = lfs->ops->write(lfs->bd, (void*)&ino,
+                        nhead, i, 4);
+                if (err) {
+                    return err;
+                }
+            }
+        }
+
+        lfs->free.head = nhead;
+        lfs->free.off = 4;
+    }
+
+    lfs_error_t err = lfs->ops->write(lfs->bd, (void*)&ino,
+            lfs->free.head, lfs->free.off, 4);
+    if (err) {
+        return err;
+    }
+
+    lfs->free.off += 4;
+    lfs->free.rev[1] += 1;
+    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);
+    if (err) {
+        return err;
+    }
+
+    err = lfs->ops->erase(lfs->bd, 0, 0, info.erase_size);
+    if (err) {
+        return err;
+    }
+
+    // TODO erase what could be misinterpreted (pairs of blocks)
+
+    {   // Create free list
+        lfs->free.rev[0] = 0;
+        lfs->free.rev[1] = 0;
+        lfs->free.phead = 0;
+        lfs->free.head = 0;
+        lfs->free.off = 0;
+
+        lfs_size_t block_count = lfs->info.total_size / lfs->info.erase_size;
+        for (lfs_ino_t i = 4; i < block_count; i++) {
+            lfs_error_t err = lfs_free(lfs, i);
+            if (err) {
+                return err;
+            }
+        }
+    }
+
+    {
+        // Write root directory
+        struct __attribute__((packed)) {
+            lfs_word_t rev;
+            lfs_size_t len;
+            lfs_ino_t  tail[2];
+            lfs_word_t free_rev[2];
+            lfs_ino_t  free_ino;
+        } header = {1, 0, {0, 0},
+            {lfs->free.rev[0], lfs->free.rev[1]}, lfs->free.head};
+        err = lfs->ops->write(lfs->bd, (void*)&header, 2, 0, sizeof(header));
+        if (err) {
+            return err;
+        }
+
+        uint32_t crc = lfs_crc((void*)&header, sizeof(header), 0xffffffff);
+
+        for (lfs_size_t i = sizeof(header); i < info.erase_size-4; i += 4) {
+            uint32_t data;
+            err = lfs->ops->read(lfs->bd, (void*)&data, 2, i, 4);
+            if (err) {
+                return err;
+            }
+
+            crc = lfs_crc((void*)&data, 4, crc);
+        }
+
+        err = lfs->ops->write(lfs->bd, (void*)&crc, 2, info.erase_size-4, 4);
+        if (err) {
+            return err;
+        }
+    }
+
+    {
+        // Write superblock
+        struct __attribute__((packed)) {
+            lfs_word_t rev;
+            lfs_word_t len;
+            lfs_word_t tail[2];
+            lfs_word_t free_head;
+            lfs_word_t free_end;
+            lfs_ino_t  free_ino;
+            char magic[4];
+            struct lfs_bd_info info;
+        } header = {1, 0, {2, 3}, 0, 0, 0, {"lfs"}, info};
+        err = lfs->ops->write(lfs->bd, (void*)&header, 0, 0, sizeof(header));
+        if (err) {
+            return err;
+        }
+
+        uint32_t crc = lfs_crc((void*)&header, sizeof(header), 0xffffffff);
+
+        for (lfs_size_t i = sizeof(header); i < info.erase_size-4; i += 4) {
+            uint32_t data;
+            err = lfs->ops->read(lfs->bd, (void*)&data, 0, i, 4);
+            if (err) {
+                return err;
+            }
+
+            crc = lfs_crc((void*)&data, 4, crc);
+        }
+
+        err = lfs->ops->write(lfs->bd, (void*)&crc, 0, info.erase_size-4, 4);
+        if (err) {
+            return err;
+        }
+    }
+
+
+    // Sanity check
+    uint32_t crc = 0xffffffff;
+    for (lfs_size_t i = 0; i < info.erase_size; i += 4) {
+        uint32_t data;
+        err = lfs->ops->read(lfs->bd, (void*)&data, 0, i, 4);
+        if (err) {
+            return err;
+        }
+
+        crc = lfs_crc((void*)&data, 4, crc);
+    }
+
+    uint32_t data;
+    err = lfs->ops->read(lfs->bd, (void*)&data, 0, info.erase_size-4, 4);
+    if (err) {
+        return err;
+    }
+
+    return crc;
+}
+

+ 32 - 0
lfs.h

@@ -0,0 +1,32 @@
+/*
+ * The little filesystem
+ *
+ * Copyright (c) 2017 Christopher Haster
+ * Distributed under the MIT license
+ */
+#ifndef LFS_H
+#define LFS_H
+
+#include "lfs_config.h"
+#include "lfs_bd.h"
+
+struct lfs_free_list {
+    lfs_word_t rev[2];
+    lfs_ino_t phead;
+    lfs_ino_t head;
+    lfs_ino_t tip;
+    lfs_off_t off;
+};
+
+typedef struct lfs {
+    lfs_bd_t *bd;
+    const struct lfs_bd_ops *ops;
+
+    struct lfs_free_list free;
+    struct lfs_bd_info info;
+} lfs_t;
+
+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);
+
+#endif

+ 8 - 5
lfs_bd.h

@@ -10,6 +10,9 @@
 #include "lfs_config.h"
 
 
+// Opaque type for block devices
+typedef void lfs_bd_t;
+
 // Description of block devices
 struct lfs_bd_info {
     lfs_size_t read_size;   // Size of readable block
@@ -27,29 +30,29 @@ struct lfs_bd_info {
 // block device
 struct lfs_bd_ops {
     // Read a block
-    lfs_error_t (*read)(void *bd, uint8_t *buffer,
+    lfs_error_t (*read)(lfs_bd_t *bd, uint8_t *buffer,
             lfs_ino_t ino, lfs_off_t off, lfs_size_t size);
 
     // Program a block
     //
     // The block must have previously been erased.
-    lfs_error_t (*write)(void *bd, const uint8_t *buffer,
+    lfs_error_t (*write)(lfs_bd_t *bd, const uint8_t *buffer,
             lfs_ino_t ino, lfs_off_t off, lfs_size_t size);
 
     // Erase a block
     //
     // A block must be erased before being programmed. The
     // state of an erased block is undefined.
-    lfs_error_t (*erase)(void *bd,
+    lfs_error_t (*erase)(lfs_bd_t *bd,
             lfs_ino_t ino, lfs_off_t off, lfs_size_t size);
 
     // Sync the block device
-    lfs_error_t (*sync)(void *bd);
+    lfs_error_t (*sync)(lfs_bd_t *bd);
 
     // Get a description of the block device
     //
     // Any unknown information may be left as zero
-    lfs_error_t (*info)(void *bd, struct lfs_bd_info *info);
+    lfs_error_t (*info)(lfs_bd_t *bd, struct lfs_bd_info *info);
 };