From 7132ea170dc2058e299df6fe025b88c9d3640a79 Mon Sep 17 00:00:00 2001 From: Dan Sheppard Date: Fri, 11 Apr 2025 15:36:51 +0100 Subject: [PATCH] Superblock loading and saving. (Untested, no logging). --- .vscode/settings.json | 3 +- Makefile | 2 +- constants.h | 21 +++-- coquet.c | 9 +- coquet.h | 5 +- design.txt | 10 +- main.c | 21 ++--- sha2.c | 1 + superblock.c | 206 ++++++++++++++++++++++++++++++++++++++++++ superblock.h | 28 ++++++ posix.c => unix.c | 148 ++++++++++++++++++++---------- util.c | 21 +++++ util.h | 2 + vfs.h | 14 ++- 14 files changed, 409 insertions(+), 82 deletions(-) create mode 100644 superblock.c create mode 100644 superblock.h rename posix.c => unix.c (68%) diff --git a/.vscode/settings.json b/.vscode/settings.json index c662aec..4b4779a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,6 +8,7 @@ "NURS" ], "files.associations": { - "coquet.h": "c" + "coquet.h": "c", + "superblock.h": "c" } } \ No newline at end of file diff --git a/Makefile b/Makefile index 5b6ba64..cf2674c 100644 --- 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 diff --git a/constants.h b/constants.h index 3d77ef7..4a7fc05 100644 --- a/constants.h +++ b/constants.h @@ -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 diff --git a/coquet.c b/coquet.c index 4400d3e..220b976 100644 --- 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 diff --git a/coquet.h b/coquet.h index 45ca3b4..4cd6547 100644 --- 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; diff --git a/design.txt b/design.txt index df25594..3be2500 100644 --- a/design.txt +++ b/design.txt @@ -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 4242725..c7436e6 100644 --- a/main.c +++ b/main.c @@ -2,6 +2,7 @@ #include #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 5d0cb7d..e5264c8 100644 --- 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 index 0000000..6409fbf --- /dev/null +++ b/superblock.c @@ -0,0 +1,206 @@ +#include +#include +#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 index 0000000..1cffe05 --- /dev/null +++ b/superblock.h @@ -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 588aa73..ab48a01 100644 --- 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 40f5346..9fd0254 100644 --- 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=0;i--) { + *(where+i) = value; + value >>= 8; + } +} diff --git a/util.h b/util.h index 8862a08..6d2f655 100644 --- 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 b2e55c3..6d677dd 100644 --- 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 -- 2.30.2