Эх сурвалжийг харах

Initial commit of block device interface and emulated block device

Christopher Haster 8 жил өмнө
parent
commit
02156cb47d
7 өөрчлөгдсөн 471 нэмэгдсэн , 43 устгасан
  1. 49 0
      Makefile
  2. 36 30
      emubd/lfs_cfg.c
  3. 13 13
      emubd/lfs_cfg.h
  4. 223 0
      emubd/lfs_emubd.c
  5. 57 0
      emubd/lfs_emubd.h
  6. 56 0
      lfs_bd.h
  7. 37 0
      lfs_config.h

+ 49 - 0
Makefile

@@ -0,0 +1,49 @@
+TARGET = lfs
+
+CC = gcc
+AR = ar
+SIZE = size
+
+SRC += $(wildcard *.c emubd/*.c)
+OBJ := $(SRC:.c=.o)
+DEP := $(SRC:.c=.d)
+ASM := $(SRC:.c=.s)
+
+ifdef DEBUG
+CFLAGS += -O0 -g3
+else
+CFLAGS += -O2
+endif
+ifdef WORD
+CFLAGS += -m$(WORD)
+endif
+CFLAGS += -I.
+CFLAGS += -std=c99 -Wall -pedantic
+
+
+all: $(TARGET)
+
+asm: $(ASM)
+
+size: $(OBJ)
+	$(SIZE) -t $^
+
+-include $(DEP)
+
+$(TARGET): $(OBJ)
+	$(CC) $(CFLAGS) $^ $(LFLAGS) -o $@
+
+%.a: $(OBJ)
+	$(AR) rcs $@ $^
+
+%.o: %.c
+	$(CC) -c -MMD $(CFLAGS) $< -o $@
+
+%.s: %.c
+	$(CC) -S $(CFLAGS) $< -o $@
+
+clean:
+	rm -f $(TARGET)
+	rm -f $(OBJ)
+	rm -f $(DEP)
+	rm -f $(ASM)

+ 36 - 30
emu/cfg.c → emubd/lfs_cfg.c

@@ -1,4 +1,10 @@
-#include "cfg.h"
+/*
+ * Simple config parser
+ *
+ * Copyright (c) 2017 Christopher Haster
+ * Distributed under the MIT license
+ */
+#include "emubd/lfs_cfg.h"
 
 #include <stdlib.h>
 #include <errno.h>
@@ -6,7 +12,7 @@
 #include <stdio.h>
 
 
-static int cfg_buffer(cfg_t *cfg, char c) {
+static int lfs_cfg_buffer(lfs_cfg_t *cfg, char c) {
     // Amortize double
     if (cfg->blen == cfg->bsize) {
         size_t nsize = cfg->bsize * 2;
@@ -26,16 +32,16 @@ static int cfg_buffer(cfg_t *cfg, char c) {
     return 0;
 }
 
-static int cfg_attr(cfg_t *cfg, unsigned key, unsigned val) {
+static int lfs_cfg_attr(lfs_cfg_t *cfg, unsigned key, unsigned val) {
     // Amortize double
     if (cfg->len == cfg->size) {
         size_t nsize = cfg->size * 2;
-        struct cfg_attr *nattrs = malloc(nsize*sizeof(struct cfg_attr));
+        struct lfs_cfg_attr *nattrs = malloc(nsize*sizeof(struct lfs_cfg_attr));
         if (!nattrs) {
             return -ENOMEM;
         }
 
-        memcpy(nattrs, cfg->attrs, cfg->size*sizeof(struct cfg_attr));
+        memcpy(nattrs, cfg->attrs, cfg->size*sizeof(struct lfs_cfg_attr));
         free(cfg->attrs);
         cfg->attrs = nattrs;
         cfg->size = nsize;
@@ -50,14 +56,14 @@ static int cfg_attr(cfg_t *cfg, unsigned key, unsigned val) {
     }
 
     memmove(&cfg->attrs[i+1], &cfg->attrs[i],
-        (cfg->size - i)*sizeof(struct cfg_attr));
+        (cfg->size - i)*sizeof(struct lfs_cfg_attr));
     cfg->attrs[i].key = key;
     cfg->attrs[i].val = val;
     cfg->len += 1;
     return 0;
 }
 
-static bool cfg_match(FILE *f, const char *matches) {
+static bool lfs_cfg_match(FILE *f, const char *matches) {
     char c = getc(f);
     ungetc(c, f);
 
@@ -70,11 +76,11 @@ static bool cfg_match(FILE *f, const char *matches) {
     return false;
 }
 
-int cfg_create(cfg_t *cfg, const char *filename) {
+int lfs_cfg_create(lfs_cfg_t *cfg, const char *filename) {
     // start with some initial space
     cfg->len = 0;
     cfg->size = 4;
-    cfg->attrs = malloc(cfg->size*sizeof(struct cfg_attr));
+    cfg->attrs = malloc(cfg->size*sizeof(struct lfs_cfg_attr));
 
     cfg->blen = 0;
     cfg->bsize = 16;
@@ -88,42 +94,42 @@ int cfg_create(cfg_t *cfg, const char *filename) {
     while (!feof(f)) {
         int err;
 
-        while (cfg_match(f, " \t\v\f")) {
+        while (lfs_cfg_match(f, " \t\v\f")) {
             fgetc(f);
         }
 
-        if (!cfg_match(f, "#\r\n")) {
+        if (!lfs_cfg_match(f, "#\r\n")) {
             unsigned key = cfg->blen;
-            while (!cfg_match(f, " \t\v\f:#") && !feof(f)) {
-                if ((err = cfg_buffer(cfg, fgetc(f)))) {
+            while (!lfs_cfg_match(f, " \t\v\f:#") && !feof(f)) {
+                if ((err = lfs_cfg_buffer(cfg, fgetc(f)))) {
                     return err;
                 }
             }
-            if ((err = cfg_buffer(cfg, 0))) {
+            if ((err = lfs_cfg_buffer(cfg, 0))) {
                 return err;
             }
 
-            while (cfg_match(f, " \t\v\f")) {
+            while (lfs_cfg_match(f, " \t\v\f")) {
                 fgetc(f);
             }
 
-            if (cfg_match(f, ":")) {
+            if (lfs_cfg_match(f, ":")) {
                 fgetc(f);
-                while (cfg_match(f, " \t\v\f")) {
+                while (lfs_cfg_match(f, " \t\v\f")) {
                     fgetc(f);
                 }
 
                 unsigned val = cfg->blen;
-                while (!cfg_match(f, " \t\v\f#\r\n") && !feof(f)) {
-                    if ((err = cfg_buffer(cfg, fgetc(f)))) {
+                while (!lfs_cfg_match(f, " \t\v\f#\r\n") && !feof(f)) {
+                    if ((err = lfs_cfg_buffer(cfg, fgetc(f)))) {
                         return err;
                     }
                 }
-                if ((err = cfg_buffer(cfg, 0))) {
+                if ((err = lfs_cfg_buffer(cfg, 0))) {
                     return err;
                 }
 
-                if ((err = cfg_attr(cfg, key, val))) {
+                if ((err = lfs_cfg_attr(cfg, key, val))) {
                     return err;
                 }
             } else {
@@ -131,7 +137,7 @@ int cfg_create(cfg_t *cfg, const char *filename) {
             }
         }
 
-        while (!cfg_match(f, "\r\n") && !feof(f)) {
+        while (!lfs_cfg_match(f, "\r\n") && !feof(f)) {
             fgetc(f);
         }
         fgetc(f);
@@ -140,15 +146,15 @@ int cfg_create(cfg_t *cfg, const char *filename) {
     return 0;
 }
 
-void cfg_destroy(cfg_t *cfg) {
+void lfs_cfg_destroy(lfs_cfg_t *cfg) {
     free(cfg->attrs);
 }
 
-bool cfg_has(cfg_t *cfg, const char *key) {
-    return cfg_get(cfg, key, 0);
+bool lfs_cfg_has(lfs_cfg_t *cfg, const char *key) {
+    return lfs_cfg_get(cfg, key, 0);
 }
 
-const char *cfg_get(cfg_t *cfg, const char *key, const char *def) {
+const char *lfs_cfg_get(lfs_cfg_t *cfg, const char *key, const char *def) {
     // binary search for attribute
     int lo = 0;
     int hi = cfg->len-1;
@@ -168,8 +174,8 @@ const char *cfg_get(cfg_t *cfg, const char *key, const char *def) {
     return def;
 }
 
-ssize_t cfg_geti(cfg_t *cfg, const char *key, ssize_t def) {
-    const char *val = cfg_get(cfg, key, 0);
+ssize_t lfs_cfg_geti(lfs_cfg_t *cfg, const char *key, ssize_t def) {
+    const char *val = lfs_cfg_get(cfg, key, 0);
     if (!val) {
         return def;
     }
@@ -179,8 +185,8 @@ ssize_t cfg_geti(cfg_t *cfg, const char *key, ssize_t def) {
     return (end == val) ? def : res;
 }
 
-size_t cfg_getu(cfg_t *cfg, const char *key, size_t def) {
-    const char *val = cfg_get(cfg, key, 0);
+size_t lfs_cfg_getu(lfs_cfg_t *cfg, const char *key, size_t def) {
+    const char *val = lfs_cfg_get(cfg, key, 0);
     if (!val) {
         return def;
     }

+ 13 - 13
emu/cfg.h → emubd/lfs_cfg.h

@@ -1,11 +1,11 @@
 /*
  * Simple config parser
  *
- * Copyright (c) 2016 Christopher Haster
+ * Copyright (c) 2017 Christopher Haster
  * Distributed under the MIT license
  */
-#ifndef CFG_H
-#define CFG_H
+#ifndef LFS_CFG_H
+#define LFS_CFG_H
 
 #include <stddef.h>
 #include <stdbool.h>
@@ -26,7 +26,7 @@
 // huh: yeah_that's_basically_it # basically it
 
 // Internal config structure
-typedef struct cfg {
+typedef struct lfs_cfg {
     size_t len;
     size_t size;
 
@@ -34,41 +34,41 @@ typedef struct cfg {
     size_t bsize;
     char *buf;
 
-    struct cfg_attr {
+    struct lfs_cfg_attr {
         unsigned key;
         unsigned val;
     } *attrs;
-} cfg_t;
+} lfs_cfg_t;
 
 
 
 // Creates a cfg object and reads in the cfg file from the filename
 //
-// If the cfg_read fails, returns a negative value from the underlying
+// If the lfs_cfg_read fails, returns a negative value from the underlying
 // stdio functions
-int cfg_create(cfg_t *cfg, const char *filename);
+int lfs_cfg_create(lfs_cfg_t *cfg, const char *filename);
 
 // Destroys the cfg object and frees any used memory
-void cfg_destroy(cfg_t *cfg);
+void lfs_cfg_destroy(lfs_cfg_t *cfg);
 
 // Checks if a cfg attribute exists
-bool cfg_has(cfg_t *cfg, const char *key);
+bool lfs_cfg_has(lfs_cfg_t *cfg, const char *key);
 
 // Retrieves a cfg attribute as a null-terminated string
 //
 // If the attribute does not exist, returns the string passed as def
-const char *cfg_get(cfg_t *cfg, const char *key, const char *def);
+const char *lfs_cfg_get(lfs_cfg_t *cfg, const char *key, const char *def);
 
 // Retrieves a cfg attribute parsed as an int
 //
 // If the attribute does not exist or can't be parsed, returns the
 // integer passed as def
-ssize_t cfg_geti(cfg_t *cfg, const char *name, ssize_t def);
+ssize_t lfs_cfg_geti(lfs_cfg_t *cfg, const char *name, ssize_t def);
 
 // Retrieves a cfg attribute parsed as an unsigned int
 //
 // If the attribute does not exist or can't be parsed, returns the
 // integer passed as def
-size_t cfg_getu(cfg_t *cfg, const char *name, size_t def);
+size_t lfs_cfg_getu(lfs_cfg_t *cfg, const char *name, size_t def);
 
 #endif

+ 223 - 0
emubd/lfs_emubd.c

@@ -0,0 +1,223 @@
+/*
+ * Block device emulated on standard files
+ *
+ * Copyright (c) 2017 Christopher Haster
+ * Distributed under the MIT license
+ */
+#include "emubd/lfs_emubd.h"
+#include "emubd/lfs_cfg.h"
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <dirent.h>
+
+
+// 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));
+
+    // Allocate buffer for creating children files
+    size_t pathlen = strlen(path);
+    emu->path = malloc(pathlen + 1 + LFS_NAME_MAX + 1);
+    if (!emu->path) {
+        return -ENOMEM;
+    }
+
+    strcpy(emu->path, path);
+    emu->path[pathlen] = '/';
+    emu->path[pathlen + 1 + LFS_NAME_MAX] = '\0';
+    emu->child = &emu->path[pathlen+1];
+    strncpy(emu->child, "config", LFS_NAME_MAX);
+
+    // Load config, erroring if it doesn't exist
+    lfs_cfg_t cfg;
+    int err = lfs_cfg_create(&cfg, emu->path);
+    if (err) {
+        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.erase_size = lfs_cfg_getu(&cfg, "erase_size", 0);
+    emu->info.total_size = lfs_cfg_getu(&cfg, "total_size", 0);
+
+    lfs_cfg_destroy(&cfg);
+
+    return 0;
+}
+
+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) {
+
+    // 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
+          < emu->info.total_size))) {
+        return -EINVAL;
+    }
+
+    // Default to some arbitrary value for debugging
+    memset(buffer, 0xcc, size);
+
+    // Iterate over blocks until enough data is read
+    while (size > 0) {
+        snprintf(emu->child, LFS_NAME_MAX, "%d", ino);
+        size_t count = lfs_min(emu->info.erase_size - off, size);
+
+        FILE *f = fopen(emu->path, "rb");
+        if (!f && errno != ENOENT) {
+            return -errno;
+        }
+
+        if (f) {
+            int err = fseek(f, off, SEEK_SET);
+            if (err) {
+                return -errno;
+            }
+
+            size_t res = fread(buffer, 1, count, f);
+            if (res < count && !feof(f)) {
+                return -errno;
+            }
+
+            err = fclose(f);
+            if (err) {
+                return -errno;
+            }
+        }
+
+        size -= count;
+        buffer += count;
+        ino += 1;
+        off = 0;
+    }
+
+    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) {
+
+    // 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
+          < 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);
+        size_t count = lfs_min(emu->info.erase_size - off, size);
+
+        FILE *f = fopen(emu->path, "r+b");
+        if (!f && errno == ENOENT) {
+            f = fopen(emu->path, "w+b");
+            if (!f) {
+                return -errno;
+            }
+        }
+
+        int err = fseek(f, off, SEEK_SET);
+        if (err) {
+            return -errno;
+        }
+
+        size_t res = fwrite(buffer, 1, count, f);
+        if (res < count) {
+            return -errno;
+        }
+
+        err = fclose(f);
+        if (err) {
+            return -errno;
+        }
+
+        size -= count;
+        buffer += count;
+        ino += 1;
+        off = 0;
+    }
+
+    return 0;
+}
+
+lfs_error_t lfs_emubd_erase(lfs_emubd_t *emu,
+        lfs_ino_t ino, 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
+          < emu->info.total_size))) {
+        return -EINVAL;
+    }
+
+    // Iterate and erase blocks
+    while (size > 0) {
+        snprintf(emu->child, LFS_NAME_MAX, "%d", ino);
+        int err = unlink(emu->path);
+        if (err && errno != ENOENT) {
+            return -errno;
+        }
+
+        size -= emu->info.erase_size;
+        ino += 1;
+        off = 0;
+    }
+
+    return 0;
+}
+
+lfs_error_t 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) {
+    *info = emu->info;
+    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 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 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 lfs_error_t 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) {
+    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,
+    .erase = lfs_emubd_bd_erase,
+    .sync = lfs_emubd_bd_sync,
+    .info = lfs_emubd_bd_info,
+};
+

+ 57 - 0
emubd/lfs_emubd.h

@@ -0,0 +1,57 @@
+/*
+ * Block device emulated on standard files
+ *
+ * Copyright (c) 2017 Christopher Haster
+ * Distributed under the MIT license
+ */
+#ifndef LFS_EMUBD_H
+#define LFS_EMUBD_H
+
+#include "lfs_config.h"
+#include "lfs_bd.h"
+
+
+// The emu bd state
+typedef struct lfs_emubd {
+    char *path;
+    char *child;
+    struct lfs_bd_info info;
+} lfs_emubd_t;
+
+
+// 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);
+
+// 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);
+
+// 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);
+
+// 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);
+
+// Sync the block device
+lfs_error_t 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);
+
+// Block device operations
+extern const struct lfs_bd_ops lfs_emubd_ops;
+
+
+#endif

+ 56 - 0
lfs_bd.h

@@ -0,0 +1,56 @@
+/*
+ * Block device interface
+ *
+ * Copyright (c) 2017 Christopher Haster
+ * Distributed under the MIT license
+ */
+#ifndef LFS_BD_H
+#define LFS_BD_H
+
+#include "lfs_config.h"
+
+
+// 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 erase_size;  // Size of erase block
+
+    lfs_lsize_t total_size; // Total size of the device
+};
+
+// Block device operations
+//
+// The little file system takes in a pointer to an opaque type
+// and this struct, all operations are passed the opaque pointer
+// which can be used to reference any state associated with the
+// block device
+struct lfs_bd_ops {
+    // Read a block
+    lfs_error_t (*read)(void *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_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_ino_t ino, lfs_off_t off, lfs_size_t size);
+
+    // Sync the block device
+    lfs_error_t (*sync)(void *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);
+};
+
+
+#endif

+ 37 - 0
lfs_config.h

@@ -0,0 +1,37 @@
+/*
+ * Configuration and type definitions
+ *
+ * Copyright (c) 2017 Christopher Haster
+ * Distributed under the MIT license
+ */
+#ifndef LFS_CONFIG_H
+#define LFS_CONFIG_H
+
+#include <stdint.h>
+
+// Type definitions
+typedef uint64_t lfs_lword_t;
+typedef uint32_t lfs_word_t;
+typedef uint16_t lfs_hword_t;
+
+typedef lfs_word_t lfs_size_t;
+typedef lfs_word_t lfs_off_t;
+typedef int lfs_error_t;
+
+typedef lfs_lword_t lfs_lsize_t;
+typedef lfs_word_t  lfs_ino_t;
+typedef lfs_hword_t lfs_ioff_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;
+}
+
+#endif