chiark / gitweb /
Superblock loading and saving.
authorDan Sheppard <dan.sheppard.circle@gmail.com>
Fri, 11 Apr 2025 14:36:51 +0000 (15:36 +0100)
committerDan Sheppard <dan.sheppard.circle@gmail.com>
Fri, 11 Apr 2025 14:36:51 +0000 (15:36 +0100)
(Untested, no logging).

14 files changed:
.vscode/settings.json
Makefile
constants.h
coquet.c
coquet.h
design.txt
main.c
sha2.c
superblock.c [new file with mode: 0644]
superblock.h [new file with mode: 0644]
unix.c [moved from posix.c with 68% similarity]
util.c
util.h
vfs.h

index c662aec146b7a404b7ad17dd1defa4f836d4a270..4b4779a788a5b16bc979d69f6581de36179b3732 100644 (file)
@@ -8,6 +8,7 @@
         "NURS"
     ],
     "files.associations": {
-        "coquet.h": "c"
+        "coquet.h": "c",
+        "superblock.h": "c"
     }
 }
\ No newline at end of file
index 5b6ba648146e0c47755129a4949439614c05ce32..cf2674c3c623a5e6e9ff6120aa581c5c6f930ab0 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ TARGET = coquet
 LIBS =
 CC = gcc
 CFLAGS = -g -Wall --std=c99
-FILES = main.c posix.c util.c coquet.c
+FILES = main.c unix.c util.c coquet.c superblock.c sha2.c
 
 .PHONY: default all clean
 
index 3d77ef7f65bcdcfea46a6bbf25d30e7ec47b1117..4a7fc0594239c4d9c55cb8db98303e8dd35e3db6 100644 (file)
@@ -7,22 +7,26 @@
 #define COQUET_FILE_OLD  1
 #define COQUET_FILE_TMP  2
 
+/* remember to update messages */
 #define COQUET_RET_OK       0
 #define COQUET_RET_VFSERR   1
 #define COQUET_RET_HEAPERR  2
 #define COQUET_RET_LOCKED   3
-#define COQUET_UNUSED_ERROR 4
-#define COQUET_LAST_ERROR   4
+#define COQUET_RET_CORRUPT  4
+#define COQUET_RET_EXISTS   5
+#define COQUET_UNUSED_ERROR 6
+#define COQUET_LAST_ERROR   6
 
 #define COQUET_CMODE_OPEN   0
 #define COQUET_CMODE_CREATE 1
 #define COQUET_CMODE_EITHER 2
 
-#define COQUET_LOCK_WRITE  0
-#define COQUET_LOCK_ALIVE  1
-#define COQUET_LOCK_NURS_A 2
-#define COQUET_LOCK_NURS_B 3
-#define COQUET_LOCK_LAST   3
+#define COQUET_LOCK_WRITE      0
+#define COQUET_LOCK_ALIVE      1
+#define COQUET_LOCK_NURS_A     2
+#define COQUET_LOCK_NURS_B     3
+#define COQUET_LOCK_SUPERBLOCK 4
+#define COQUET_LOCK_LAST       4
 
 #define COQUET_LMODE_EXCL  0
 #define COQUET_LMODE_SHARE 1
@@ -30,4 +34,7 @@
 
 typedef int bool_t;
 
+struct coquet;
+typedef struct coquet coquet_t;
+
 #endif
index 4400d3ef7cf7fe0cb2e856860ee38498a2c99f86..220b976bc0e945dd5fdcd9f4b81d736eb19e5d2d 100644 (file)
--- a/coquet.c
+++ b/coquet.c
@@ -5,7 +5,7 @@
 #include "coquet.h"
 #include "util.h"
 
-extern vfs_t vfs_posix;
+extern vfs_t vfs_unix;
 
 /* VFS null is used as a placeholder prior to initialisation, so that
  * subsequent tidies can be clean.
@@ -26,7 +26,7 @@ vfs_t vfs_null = {
 static int init_vfs(coquet_t *cq, char *basename) {
     int r;
 
-    cq->vfs_funcs = vfs_posix;
+    cq->vfs_funcs = vfs_unix;
 
     /* Allocate memory */
     cq->vfs_data = (cq->vfs_funcs.make)();
@@ -91,7 +91,10 @@ char * error_strings[COQUET_UNUSED_ERROR+1] = {
     /* COQUET_RET_VFS */      "Unknown VFS Error",
     /* COQUET_RET_HEAPERR */  "Heap allocation failed",
     /* COQUET_RET_LOCKED */   "Locked",
-    /* COQUET_UNUSED_ERROR */ "No such error code"
+    /* COQUET_RET_CORRUPT */  "File corrupt",
+    /* COQUET_RET_EXISTS */   "File already exists",
+    /* COQUET_UNUSED_ERROR */ "No such error code",
+
 };
 
 /* Return error string for most recent error. Returned string is owned by
index 45ca3b49a6d4f10e8408f6f7d18a69c01d1b5c8e..4cd6547ec2424051f82d0fe3c123127f2e00139f 100644 (file)
--- a/coquet.h
+++ b/coquet.h
@@ -2,10 +2,13 @@
 #define COQUET_H
 
 #include "constants.h"
+#include "superblock.h"
 #include "vfs.h"
 
 typedef struct coquet {
-    
+    /* superblock stuff */
+    uint64_t global_iv[GLOBAL_IV_LEN];    
+
     /* VFS */
     vfs_t vfs_funcs;
     void *vfs_data;
index df2559439ba59c5c8484abb4d04f1b284cfe1ec3..3be25002c5e18a427da77741557b2b473cbcf2ee 100644 (file)
@@ -17,7 +17,10 @@ coquet is also designed to support arbitrary user-created queries with little da
 
 coquet *does* make changes to the database file other than at the end, making it *not* append-only. However, it is guaranteed that these changes are such that at any moment any other process can read or copy the database file, even without locking it first, and receive a valid database state at some moment in time.
 
-The non-append actions relate to the nursery-area, a journal to improve write efficiency in various ways.
+The non-append actions relate 
+
+1. to the nursery-area, a journal to improve write efficiency in various ways;
+2. to the superblock where an admin may update the intended parameters for the file for the database to apply when convenient.
 
 The nursery-area is at a fixed location near the start of the file. It is a write-ahead log, designed in such a way that reading its data, even *during* updates to the log, gives you the valid log state at some point in the near past, at least at the most recent commit which has been followed by a filesystem sync, perhaps also including unsynced commits -- and therefore always a valid database at that point (or later).
 
@@ -25,6 +28,8 @@ The nursery-area could have been a separate file. However, it was deemed more im
 
 As a write-ahead log, the nursery does place a performance obligation on readers. Again, given our pessimistic approach to systems administration, the default implementation does not use exotic shared memory to speed this up, but takes the performance hit.
 
+The superblock is at the very start of the file with a pair of entries, of which exactly one is valid. Superblocks include a small, increasing integer denoting the currently valid superblock, and are each protected by an HMAC. Writes to update a superblock update the inactive one of the pair and issue a sync, making it active.
+
 Filesystem Guarantees
 =====================
 
@@ -67,6 +72,7 @@ Three guarantees are achieved by locking:
 1. No two writes can be live simultaneously.
 2. An old database file cannot be deleted while operations are ongoing.
 3. A nursery half is not deleted on flushing while being read by another process.
+4. A superblock lock, ensuring no two process are updating the superblock at once.
 
 The *write lock* is exclusively acquired by a writer before writing.
 
@@ -76,6 +82,8 @@ One of the pair of *nursery locks* is acquired exclusively by writers while dele
 
 Deadlocks are avoided by having a defined order of lock acquisition.
 
+The superblock is locked during update. The lock only stops simultaneous writes: reads may run concurrently, even coexisting with other writers.
+
 File Structure
 ==============
 
diff --git a/main.c b/main.c
index 424272560180717701d29ae997f845fe15b5626f..c7436e62d356586f77bb4e7a27b4dcfcd93fdab4 100644 (file)
--- a/main.c
+++ b/main.c
@@ -2,6 +2,7 @@
 #include <stdlib.h>
 #include "coquet.h"
 #include "vfs.h"
+#include "superblock.h"
 
 void bail(coquet_t * cq, int error_code) {
     char *msg;
@@ -22,28 +23,18 @@ void bail(coquet_t * cq, int error_code) {
 
 int main() {
     coquet_t cq;
-    int i,r;
-    char buf[10];
+    int r;
+    struct cq_super super;
 
     r = coquet_init(&cq,"test");
     bail(&cq,r);
-    r = (cq.vfs_funcs.open)(cq.vfs_data,COQUET_FILE_MAIN,1);
+    r = (cq.vfs_funcs.open)(cq.vfs_data,COQUET_FILE_MAIN,COQUET_CMODE_EITHER);
     bail(&cq,r);
-    r = (cq.vfs_funcs.write)(cq.vfs_data,COQUET_FILE_MAIN,"hello",20,5);
-    bail(&cq,r);
-    r = (cq.vfs_funcs.read)(cq.vfs_data,COQUET_FILE_MAIN,buf,18,10);
-    bail(&cq,r);
-    r = (cq.vfs_funcs.lock)(cq.vfs_data,COQUET_LOCK_ALIVE,COQUET_LMODE_EXCL,1);
-    bail(&cq,r);
-    for(i=0;i<10;i++) {
-        printf("%d %d\n",i,buf[i]);
-    }
-    r = (cq.vfs_funcs.lock)(cq.vfs_data,COQUET_LOCK_ALIVE,COQUET_LMODE_UN,1);
+    r = cq_super_load(&cq,&super,1);
     bail(&cq,r);
+    cq_super_save(&cq,&super,1);
     r = (cq.vfs_funcs.close)(cq.vfs_data,COQUET_FILE_MAIN);
     bail(&cq,r);
-    r = (cq.vfs_funcs.delete)(cq.vfs_data,COQUET_FILE_MAIN);
-    bail(&cq,r);
     r = coquet_finish(&cq);
     bail(&cq,r);
     return 0;
diff --git a/sha2.c b/sha2.c
index 5d0cb7d3434650c7d9eb33ee494dde588f8de5a2..e5264c84ed9728d1e858ee73dc623e6940d7a4e2 100644 (file)
--- a/sha2.c
+++ b/sha2.c
@@ -88,6 +88,7 @@ uint64_t sha512_k[80] = {
  *     m: only written by
  *          a. memcpy len=56 at pointer offset 1. Is 8*64-bits, so at offset 1 
  *             there's 7*64-bits, = 56 bytes.
+ *          b. at indexes 0 and 4 which are less than its size.
  * 
  * (2) pad_length is at most SHA2_MAX_BLOCK_LEN. It is calculated by taking
  *     block_size and subtracting positives. If this is greater than zero,
diff --git a/superblock.c b/superblock.c
new file mode 100644 (file)
index 0000000..6409fbf
--- /dev/null
@@ -0,0 +1,206 @@
+#include <stdint.h>
+#include <string.h>
+#include "coquet.h"
+#include "util.h"
+#include "sha2.h"
+#include "superblock.h"
+
+#define HASH_LEN    32
+
+/* cq_config: length = 2048
+ *
+ *                       offset    length
+ * +-------------------+
+ * | sb_hash           |    0        32
+ * | sb_serial         |   32         8
+ * +-------------------+
+ * | unused            |   40       984
+ * +-------------------+
+ * | current           | 1024       512
+ * +-------------------+
+ * | desired           | 1536       512
+ * +-------------------+
+ *
+ * cq_super_config: length = 512
+ *
+ *                       offset    length
+ * +-------------------+
+ * | global_iv         |  0           32
+ * +-------------------+
+ * | block_size        |  32           1
+ * +-------------------+
+ * | unused            |  33         478
+ * +-------------------+
+ * | reserved          | 511           1
+ * +-------------------+
+ * 
+ * The reserved byte is to allow locking to work via superblock page.
+ * Range must exist to write, hence lock code may write this byte at
+ * offset 0x00007FFF
+ */
+
+struct cq_super default_super = {
+    .current = {{0,},12},
+    .desired = {{0,},12},
+    .sb_serial = 0,
+    .sb_hash = {0,},
+    .from_b = 1
+};
+
+/* Blindly do a extraction. Note that we may not have any verification
+ * at this point, so don't trust the data yet.
+ */
+static void extract_config(uint8_t *data, struct cq_super_config *sc) {
+    memcpy(sc->global_iv,data,HASH_LEN);
+    sc->block_size = be_decode(data+32,1);
+}
+
+static int extract_half(uint8_t *data, struct cq_super *super) {
+    struct sha2_ctx_t sha2;
+    uint8_t hash[HASH_LEN],cmp[HASH_LEN];
+
+    /* extract */
+    memset(super->sb_hash,0,HASH_LEN);
+    super->sb_serial = be_decode(data+32,8);
+    extract_config(data+1024,&(super->current));
+    extract_config(data+1536,&(super->desired));
+
+    /* verify */
+    memcpy(hash,data,HASH_LEN);
+    memset(data,0,HASH_LEN);
+    sha2_init_hmac(&sha2, SHA2_VARIETY_512_256,
+        super->current.global_iv, GLOBAL_IV_LEN);
+    sha2_more(&sha2,data,HALF_BYTES);
+    sha2_finish(&sha2,cmp,HASH_LEN);
+    if(memcmp(hash,cmp,HASH_LEN)) {
+        return 0;
+    }
+
+    return 1;
+}
+
+static int super_init(coquet_t *cq, struct cq_super *super) {
+    int r;
+
+    r = (cq->vfs_funcs.random)(cq->vfs_data,
+            super->current.global_iv, GLOBAL_IV_LEN);
+    if(r != COQUET_RET_OK) {
+        return r;
+    }
+    memcpy(super->desired.global_iv,super->current.global_iv,GLOBAL_IV_LEN);    
+    return COQUET_RET_OK;
+}
+
+/* Extract a superblock into the given structure and check it. We don't 
+ * have a valid global_iv, we just have to trust the one in the block,
+ * reducing the HMAC to a simple hash in terms of guarantees. Requires the
+ * main file to be open.
+ */
+int cq_super_load(coquet_t *cq, struct cq_super *super, bool_t create) {
+    uint8_t super_bytes[SUPER_BYTES];
+    struct cq_super super_a, super_b;
+    int r,r2;
+    bool_t use_b;
+
+    r = (cq->vfs_funcs.read)
+            (cq->vfs_data,COQUET_FILE_MAIN,super_bytes,0,SUPER_BYTES);
+    if(r != COQUET_RET_OK) {
+        return r;
+    }
+
+    r = extract_half(super_bytes,&super_a);
+    r2 = extract_half(super_bytes+HALF_BYTES,&super_b);
+    switch(r*2+r2) {
+        case 3: /* both valid */
+            use_b = (super_b.sb_serial > super_a.sb_serial);
+            break;
+        case 2: /* only A valid */
+            use_b = 0;
+            break;
+        case 1: /* only B valid */
+            use_b = 1;
+            break;
+        case 0: /* nothing valid */
+            if(create) {
+                use_b = 0;
+                super_a = default_super;
+                r = super_init(cq,&super_a);
+                if(r != COQUET_RET_OK) {
+                    return r;
+                }
+            } else {
+                return COQUET_RET_CORRUPT;
+            }
+    }
+    memcpy(super, use_b?&super_b:&super_a, sizeof(struct cq_super));
+    super->from_b = use_b;
+    return COQUET_RET_OK;
+}
+
+static int lock_super(coquet_t *cq, int mode, bool_t wait) {
+    return (cq->vfs_funcs.lock)
+        (cq->vfs_data,COQUET_LOCK_SUPERBLOCK,mode,wait);
+}
+
+static void intract_config(uint8_t *data,
+                          struct cq_super_config *sc) {
+    memcpy(data,sc->global_iv,HASH_LEN);
+    be_encode(data+32,sc->block_size,1);
+}
+
+static void intract(uint8_t *data, struct cq_super *super, uint64_t serial) {
+    struct sha2_ctx_t sha2;
+    
+    /* intract */
+    memset(data,0,HALF_BYTES);
+    be_encode(data+32,serial,8);
+    intract_config(data+1024,&(super->current));
+    intract_config(data+1536,&(super->desired));
+
+    /* set hash */
+    sha2_init_hmac(&sha2, SHA2_VARIETY_512_256,
+            super->current.global_iv, GLOBAL_IV_LEN);
+    sha2_more(&sha2,data,HALF_BYTES);
+    sha2_finish(&sha2,data,HASH_LEN);
+}
+
+static int super_write(coquet_t *cq, struct cq_super *super,
+                       bool_t use_b, uint64_t serial) {
+    uint8_t half[HALF_BYTES];
+    int r;
+
+    intract(half,super,serial);
+    r = (cq->vfs_funcs.write)
+            (cq->vfs_data,COQUET_FILE_MAIN,half,
+            use_b?HALF_BYTES:0,HALF_BYTES);
+
+    return r;
+}
+
+int cq_super_save(coquet_t *cq, struct cq_super *super, bool_t wait) {
+    int r, ret;
+    struct cq_super old;
+
+    /* lock */
+    r = lock_super(cq,COQUET_LMODE_EXCL,wait);
+    if(r != COQUET_RET_OK) {
+        return r;
+    }
+
+    /* load */
+    ret = cq_super_load(cq,&old,0);
+    if(ret == COQUET_RET_OK) {
+        ret = super_write(cq,super,!old.from_b,old.sb_serial+1);
+    } else if(ret == COQUET_RET_CORRUPT) {
+        // XXX log
+        ret = super_write(cq,super,0,1);
+    }
+
+    /* unlock */
+    r = lock_super(cq,COQUET_LMODE_UN,wait);
+    if(r != COQUET_RET_OK) {
+        return r;
+    }
+
+    return ret;
+}
diff --git a/superblock.h b/superblock.h
new file mode 100644 (file)
index 0000000..1cffe05
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef COQUET_SUPERBLOCK_H
+#define COQUET_SUPERBLOCK_H
+
+#include "constants.h"
+
+#define HALF_BYTES  2048
+#define SUPER_BYTES (HALF_BYTES*2)
+#define GLOBAL_IV_LEN 32
+#define SUPERBLOCK_LAST_OFFSET (SUPER_BYTES-1)
+
+struct cq_super_config {
+    uint8_t global_iv[GLOBAL_IV_LEN]; /* (0 : GLOBAL_IV_LEN) */
+    uint8_t block_size; /* log bits (GLOBAL_IV_LEN: 1) */
+};
+
+struct cq_super {
+    struct cq_super_config current, desired;
+    uint64_t sb_serial;
+    uint8_t sb_hash[32]; /* HMAC SHA512-256 using global_iv */
+    int from_b; /* true if loaded from b, false if from a */
+};
+
+#include "coquet.h"
+
+int cq_super_save(coquet_t *cq, struct cq_super *super, bool_t wait);
+int cq_super_load(coquet_t *cq, struct cq_super *super, bool_t create);
+
+#endif
\ No newline at end of file
diff --git a/posix.c b/unix.c
similarity index 68%
rename from posix.c
rename to unix.c
index 588aa7301b77eef2f17fbe998e1fe38c02fb2027..ab48a019be70f65f6fffc0bf7b83f16f2b9f2e84 100644 (file)
--- a/posix.c
+++ b/unix.c
@@ -10,8 +10,9 @@
 #include "vfs.h"
 #include "coquet.h"
 #include "util.h"
+#include "superblock.h"
 
-struct posix_data {
+struct unix_data {
     char * filename;
     char * dirname_buf, * dirname;
     int seen_error;
@@ -19,10 +20,10 @@ struct posix_data {
     int main_fd, tmp_fd, old_fd;
 };
 
-/* Set message to be returned by posix_get_error_text. Passed string
+/* Set message to be returned by unix_get_error_text. Passed string
  * remains owned by caller.
  */
-static void set_error(struct posix_data * pd, char *error,
+static void set_error(struct unix_data * pd, char *error,
                       bool_t use_errno) {
     pd->seen_error = 1;
     if(pd->error_text != NULL) {
@@ -35,10 +36,10 @@ static void set_error(struct posix_data * pd, char *error,
     }
 }
 
-static void * posix_make() {
-    struct posix_data * pd;
+static void * unix_make() {
+    struct unix_data * pd;
 
-    pd = malloc(sizeof(struct posix_data));
+    pd = malloc(sizeof(struct unix_data));
     pd->seen_error = 0;
     pd->error_text = NULL;
     pd->filename = pd->dirname_buf = NULL;
@@ -46,8 +47,8 @@ static void * posix_make() {
     return pd;
 }
 
-static int posix_start(void * vfs_data, char *filename) {
-    struct posix_data * pd = (struct posix_data *)vfs_data;
+static int unix_start(void * vfs_data, char *filename) {
+    struct unix_data * pd = (struct unix_data *)vfs_data;
 
     pd->filename = strdup(filename);
     if(pd->filename == NULL) {
@@ -58,9 +59,18 @@ static int posix_start(void * vfs_data, char *filename) {
     return COQUET_RET_OK;
 }
 
-static int posix_finish(void *vfs_data) {
-    struct posix_data * pd = (struct posix_data *)vfs_data;
+static int unix_finish(void *vfs_data) {
+    struct unix_data * pd = (struct unix_data *)vfs_data;
 
+    if(pd->main_fd!=-1) {
+        close(pd->main_fd); /* ret delibertely ignored */
+    }
+    if(pd->tmp_fd!=-1) {
+        close(pd->tmp_fd); /* ret delibertely ignored */
+    }
+    if(pd->old_fd!=-1) {
+        close(pd->old_fd); /* ret delibertely ignored */
+    }
     if(pd->error_text) {
         free(pd->error_text);
     }
@@ -74,8 +84,8 @@ static int posix_finish(void *vfs_data) {
     return COQUET_RET_OK;
 }
 
-static char * posix_get_error_text(void * vfs_data) {
-    struct posix_data * pd = (struct posix_data *)vfs_data;
+static char * unix_get_error_text(void * vfs_data) {
+    struct unix_data * pd = (struct unix_data *)vfs_data;
 
     if(!pd->seen_error) {
         return strdup("No VFS error occurred");
@@ -86,7 +96,7 @@ static char * posix_get_error_text(void * vfs_data) {
     return strdup(pd->error_text);
 }
 
-static char * filename(struct posix_data *pd, int which_file) {
+static char * filename(struct unix_data *pd, int which_file) {
     switch(which_file) {
         case COQUET_FILE_MAIN:
             return cq_message("%s.coquet",pd->filename);
@@ -102,7 +112,7 @@ static char * filename(struct posix_data *pd, int which_file) {
     }
 }
 
-static int * file_fd(struct posix_data *pd, int which_file) {
+static int * file_fd(struct unix_data *pd, int which_file) {
     switch(which_file) {
         case COQUET_FILE_MAIN: return &pd->main_fd;
         case COQUET_FILE_OLD: return &pd->old_fd;
@@ -111,7 +121,7 @@ static int * file_fd(struct posix_data *pd, int which_file) {
     }
 }
 
-static int sync_dir(struct posix_data * pd) {
+static int sync_dir(struct unix_data * pd) {
     int r,fd;
 
     if(pd->dirname == NULL) {
@@ -140,9 +150,9 @@ static int sync_dir(struct posix_data * pd) {
     return COQUET_RET_OK;
 }
 
-static int posix_open(void * vfs_data, int which_file, 
-                      bool_t allow_create) {
-    struct posix_data * pd = (struct posix_data *)vfs_data;
+static int unix_open(void * vfs_data, int which_file, 
+                      int mode) {
+    struct unix_data * pd = (struct unix_data *)vfs_data;
     char *path;
     int r, fd, flags, *fd_field;
 
@@ -152,8 +162,14 @@ static int posix_open(void * vfs_data, int which_file,
     }
 
     flags = O_RDWR;
-    if(allow_create) {
-        flags |= O_CREAT;
+    switch(mode) {
+        case COQUET_CMODE_CREATE:
+            flags |= O_EXCL|O_CREAT;
+            break;
+        case COQUET_CMODE_EITHER:
+            flags |= O_CREAT;
+            break;
+        default:
     }
 
     fd_field = file_fd(pd,which_file);
@@ -188,8 +204,8 @@ static int posix_open(void * vfs_data, int which_file,
     return COQUET_RET_OK;
 }
 
-static int posix_close(void * vfs_data, int which_file) {
-    struct posix_data * pd = (struct posix_data *)vfs_data;
+static int unix_close(void * vfs_data, int which_file) {
+    struct unix_data * pd = (struct unix_data *)vfs_data;
     int *fd_field, r;
 
     fd_field = file_fd(pd,which_file);
@@ -208,9 +224,9 @@ static int posix_close(void * vfs_data, int which_file) {
     return COQUET_RET_OK;
 }
 
-static int posix_write(void * vfs_data, int which_file, char * data,
+static int unix_write(void * vfs_data, int which_file, uint8_t * data,
                        off_t offset, uint64_t length) {
-    struct posix_data * pd = (struct posix_data *)vfs_data;
+    struct unix_data * pd = (struct unix_data *)vfs_data;
     int *fd, r;
     off_t r_off;
 
@@ -239,9 +255,9 @@ static int posix_write(void * vfs_data, int which_file, char * data,
     return COQUET_RET_OK;
 }
 
-static int posix_read(void * vfs_data, int which_file, char * data,
+static int unix_read(void * vfs_data, int which_file, uint8_t * data,
                       uint64_t offset, uint64_t length) {
-    struct posix_data * pd = (struct posix_data *)vfs_data;
+    struct unix_data * pd = (struct unix_data *)vfs_data;
     int *fd, r;
     off_t r_off;
 
@@ -276,16 +292,16 @@ static int posix_read(void * vfs_data, int which_file, char * data,
 }
 
 /* main_fd must be open */
-static int min_size(struct posix_data *pd, off_t size) {
+static int min_size(struct unix_data *pd, off_t offset) {
     int r;
     off_t r_off;
     char buf[1] = {0};
 
-    if(size == 0) {
+    if(offset == 0) {
         return COQUET_RET_OK;
     }
 
-    r_off = lseek(pd->main_fd,size-1,SEEK_SET);
+    r_off = lseek(pd->main_fd,offset,SEEK_SET);
     if(r_off == -1) {
         set_error(pd,"seek failed",1);
         return COQUET_RET_VFSERR;
@@ -300,10 +316,10 @@ static int min_size(struct posix_data *pd, off_t size) {
     return COQUET_RET_OK;
 }
 
-#define LOCK_BLOCK 512
-static int posix_lock(void * vfs_data, int which_lock, int lock_mode,
+#define LOCK_BLOCK 128
+static int unix_lock(void * vfs_data, int which_lock, int lock_mode,
                       bool_t wait) {
-    struct posix_data * pd = (struct posix_data *)vfs_data;
+    struct unix_data * pd = (struct unix_data *)vfs_data;
     int r, op;
     struct flock flk;
 
@@ -313,7 +329,7 @@ static int posix_lock(void * vfs_data, int which_lock, int lock_mode,
     }
 
     /* Lock region needs to exist. We can append zeroes.*/
-    r = min_size(pd,(COQUET_LOCK_LAST+1)*LOCK_BLOCK);
+    r = min_size(pd,SUPERBLOCK_LAST_OFFSET);
     if(r != COQUET_RET_OK) {
         return r;
     }
@@ -351,8 +367,8 @@ static int posix_lock(void * vfs_data, int which_lock, int lock_mode,
     return COQUET_RET_OK;
 }
 
-static int posix_delete(void *vfs_data, int which_file) {
-    struct posix_data * pd = (struct posix_data *)vfs_data;
+static int unix_delete(void *vfs_data, int which_file) {
+    struct unix_data * pd = (struct unix_data *)vfs_data;
     int *fd, r;
     char *path;
 
@@ -382,8 +398,8 @@ static int posix_delete(void *vfs_data, int which_file) {
     return COQUET_RET_OK;
 }
 
-static int posix_sync(void * vfs_data, int which_file, bool_t data_only) {
-    struct posix_data * pd = (struct posix_data *)vfs_data;
+static int unix_sync(void * vfs_data, int which_file, bool_t data_only) {
+    struct unix_data * pd = (struct unix_data *)vfs_data;
     int *fd, r;
 
     fd = file_fd(pd,which_file);
@@ -405,16 +421,50 @@ static int posix_sync(void * vfs_data, int which_file, bool_t data_only) {
     return COQUET_RET_OK;
 }
 
-vfs_t vfs_posix = {
-    .make = posix_make,
-    .start = posix_start,
-    .get_error_text = posix_get_error_text,
-    .lock = posix_lock,
-    .open = posix_open,
-    .close = posix_close,
-    .write = posix_write,
-    .read = posix_read,
-    .finish = posix_finish,
-    .delete = posix_delete,
-    .sync = posix_sync
+static int unix_random(void * vfs_data, uint8_t *out, int len) {
+    struct unix_data * pd = (struct unix_data *)vfs_data;
+    int fd, r;
+
+    fd = open("/dev/urandom",O_RDONLY);
+    if(fd==-1) {
+        set_error(pd,"Failed to open /dev/urandom",1);
+        return COQUET_RET_VFSERR;
+    }
+    while(len>0) {
+        r = read(fd,out,len);
+        if(r==-1) {
+            set_error(pd,"read of /dev/urandom failed",1);
+            return COQUET_RET_VFSERR;
+        }
+        out += r;
+        len -= r;
+        if(r==0) {
+            /* EOF */
+            set_error(pd,"EOF in /dev/urandom",0);
+            return COQUET_RET_VFSERR;
+        }
+    }
+
+    r = close(fd);
+    if(r==-1) {
+        set_error(pd,"Failed to close /dev/random",1);
+        return COQUET_RET_VFSERR;
+    }
+
+    return COQUET_RET_OK;
+}
+
+vfs_t vfs_unix = {
+    .make = unix_make,
+    .start = unix_start,
+    .get_error_text = unix_get_error_text,
+    .lock = unix_lock,
+    .open = unix_open,
+    .close = unix_close,
+    .write = unix_write,
+    .read = unix_read,
+    .finish = unix_finish,
+    .delete = unix_delete,
+    .sync = unix_sync,
+    .random = unix_random
 };
diff --git a/util.c b/util.c
index 40f53461a66d6a56378de2afc84ea0b13b2a6bb5..9fd0254c3de4c584206e051cee1bebd328486c5e 100644 (file)
--- a/util.c
+++ b/util.c
@@ -35,3 +35,24 @@ char * cq_message(const char *fmt, ...) {
 
     return p;
 }
+
+uint64_t be_decode(uint8_t *data,int len) {
+    int i;
+    uint64_t out;
+
+    out = 0;
+    for(i=0;i<len;i++) {
+        out <<= 8;
+        out += *(data++);
+    }
+    return out;
+}
+
+void be_encode(uint8_t *where, uint64_t value, int len) {
+    int i;
+
+    for(i=len-1;i>=0;i--) {
+        *(where+i) = value;
+        value >>= 8;
+    }
+}
diff --git a/util.h b/util.h
index 8862a08c3a23c77e7644846526b2815702239f14..6d2f655511bc9262819a4434b136caedf31e055f 100644 (file)
--- a/util.h
+++ b/util.h
@@ -2,5 +2,7 @@
 #define COQUET_UTIL_H
 
 char * cq_message(const char *fmt, ...);
+uint64_t be_decode(uint8_t *data,int len);
+void be_encode(uint8_t *where, uint64_t value, int len);
 
 #endif
\ No newline at end of file
diff --git a/vfs.h b/vfs.h
index b2e55c332678f31220959951eec0a3562794e697..6d677dd541bb85c0d29e84d62835f23b9e7c9847 100644 (file)
--- a/vfs.h
+++ b/vfs.h
@@ -42,10 +42,12 @@ typedef struct vfs {
                 bool_t wait);
 
     /* Open the given file. which_file is drawn from COQUET_FILE_*.
-     * If allow_create is called, the file is created if not present.
+     * Open mode gives whether a file must (or can) be created.
      * The file is guaranteed not to be open when this function is called.
+     * Can also return COQUET_RET_EXISTS if file exists and
+     * COQUET_CMODE_CREATE is passed.
      */
-    int (*open)(void * vfs_data, int which_file, bool_t allow_create);
+    int (*open)(void * vfs_data, int which_file, int open_mode);
 
     /* Sync given file to disk. */
     int (*sync)(void * vfs_data, int which_file, bool_t data_only);
@@ -61,7 +63,7 @@ typedef struct vfs {
      * offset is beyond the end of the file, the file should be extended
      * to accommodate the write.
      */
-    int (*write)(void * vfs_data, int which_file, char * data,
+    int (*write)(void * vfs_data, int which_file, uint8_t * data,
                  off_t offset, uint64_t length);
 
     /* Read given data of given length at offset from to indicated file.
@@ -69,7 +71,7 @@ typedef struct vfs {
      * offset is beyond the end of the file, all zeroes must be returned.
      * File will be open before call.
      */
-    int (*read)(void * vfs_data, int which_file, char * data,
+    int (*read)(void * vfs_data, int which_file, uint8_t * data,
                 uint64_t offset, uint64_t length);
 
     /* Finish VFS layer. Release OS resources (files etc), and free the
@@ -81,6 +83,10 @@ typedef struct vfs {
      */
     int (*delete)(void * vfs_data, int which_file);
 
+    /* Return some good-quality randomness.
+     */
+    int (*random)(void * vfs_data, uint8_t *out, int len);
+
 } vfs_t;
 
 #endif