From d5cf535197d5090554453b6357867d782f0d1484 Mon Sep 17 00:00:00 2001 From: fredrik Date: Thu, 20 Jul 2006 13:15:32 +0000 Subject: [PATCH] Initial import. git-svn-id: svn+ssh://svn.dolda2000.com/srv/svn/repos/src/vcfs@673 959494ce-11ee-0310-bf91-de5d638817bd --- Makefile | 27 +++ blocktree.c | 298 ++++++++++++++++++++++++++++++ blocktree.h | 33 ++++ filestore.c | 360 ++++++++++++++++++++++++++++++++++++ log.c | 29 +++ log.h | 11 ++ mkfs.vc.c | 78 ++++++++ mkstore.c | 15 ++ store.c | 164 +++++++++++++++++ store.h | 42 +++++ storeget.c | 29 +++ storeput.c | 29 +++ utils.c | 51 ++++++ utils.h | 7 + vcfs.c | 592 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vcfs.h | 32 ++++ 16 files changed, 1797 insertions(+) create mode 100644 Makefile create mode 100644 blocktree.c create mode 100644 blocktree.h create mode 100644 filestore.c create mode 100644 log.c create mode 100644 log.h create mode 100644 mkfs.vc.c create mode 100644 mkstore.c create mode 100644 store.c create mode 100644 store.h create mode 100644 storeget.c create mode 100644 storeput.c create mode 100644 utils.c create mode 100644 utils.h create mode 100644 vcfs.c create mode 100644 vcfs.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..83686b5 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +CFLAGS=-g -Wall + +all: storeget storeput mkstore mkfs.vc vcfs + +storeget: storeget.o store.o filestore.o log.o utils.o + gcc $(CFLAGS) -o $@ $^ -lgcrypt + +storeput: storeput.o store.o filestore.o log.o utils.o + gcc $(CFLAGS) -o $@ $^ -lgcrypt + +mkstore: mkstore.o store.o filestore.o log.o utils.o + gcc $(CFLAGS) -o $@ $^ -lgcrypt + +mkfs.vc: mkfs.vc.o store.o filestore.o log.o blocktree.o utils.o + gcc $(CFLAGS) -o $@ $^ -lgcrypt + +vcfs: vcfs.o store.o filestore.o log.o blocktree.o utils.o + gcc $(CFLAGS) -o $@ $^ -lgcrypt -lfuse + +vcfs.o: vcfs.c + gcc -c $(CFLAGS) -o $@ $< -DFUSE_USE_VERSION=26 -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse + +%.o: %.c + gcc -c $(CFLAGS) -o $@ $< + +clean: + rm -f *.o storeget storeput mkstore mkfs.vc vcfs diff --git a/blocktree.c b/blocktree.c new file mode 100644 index 0000000..8a3c36e --- /dev/null +++ b/blocktree.c @@ -0,0 +1,298 @@ +#include +#include +#include + +#include "store.h" +#include "blocktree.h" + +#define min(a, b) (((b) < (a))?(b):(a)) + +ssize_t btget(struct store *st, struct btnode *tree, block_t bl, void *buf, size_t len) +{ + int d; + block_t c, sel; + struct btnode indir[BT_INDSZ]; + ssize_t sz; + + if(tree->d == 0) { + errno = ERANGE; + return(-1); + } + while(1) { + d = tree->d & 0x7f; + /* This check should really only be necessary on the first + * iteration, but I felt it was easier to put it in the + * loop. */ + if((bl >> (d * BT_INDBITS)) > 0) { + errno = ERANGE; + return(-1); + } + + if(d == 0) + return(storeget(st, buf, len, &tree->a)); + + /* Luckily, this is tail recursive */ + if((sz = storeget(st, indir, BT_INDBSZ, &tree->a)) < 0) + return(-1); + c = sz / sizeof(struct btnode); + sel = bl >> ((d - 1) * BT_INDBITS); + if(sel >= c) { + errno = ERANGE; + return(-1); + } + tree = &indir[sel]; + bl &= (1LL << ((d - 1) * BT_INDBITS)) - 1; + } + return(0); +} + +static int btputleaf(struct store *st, struct btnode *leaf, struct btop *op, block_t bloff) +{ + void *buf; + struct addr na; + int ret; + + buf = NULL; + if(op->buf == NULL) { + buf = op->buf = malloc(op->len); + if(op->fillfn(buf, op->len, op->pdata)) + return(-1); + } + ret = storeput(st, op->buf, op->len, &na); + if(buf != NULL) + free(buf); + if(ret) + return(-1); + leaf->d = 0x80; + leaf->a = na; + return(0); +} + +static int countops(struct btop *ops, int numops, block_t bloff, block_t maxbl) +{ + int i; + + for(i = 0; i < numops; i++) { + if(ops[i].blk - bloff >= maxbl) + break; + } + return(i); +} + +/* + * blputmany() in many ways makes the code uglier, but it saves a + * *lot* of space, since it doesn't need to store intermediary blocks. + */ +static int btputmany2(struct store *st, struct btnode *tree, struct btop *ops, int numops, block_t bloff) +{ + int i, subops, d, f, hasid; + block_t c, sel, bl, nextsz; + struct addr na; + struct btnode indir[BT_INDSZ]; + ssize_t sz; + + d = tree->d & 0x7f; + f = tree->d & 0x80; + + hasid = 0; + + for(i = 0; i < numops; ) { + bl = ops[i].blk - bloff; + + if((d == 0) && (bl == 0)) { + if(btputleaf(st, tree, ops, bloff)) + return(-1); + i++; + continue; + } + + if(f && (bl == (1LL << (d * BT_INDBITS)))) { + /* New level of indirection */ + if(hasid) { + if(storeput(st, indir, c * sizeof(struct btnode), &na)) + return(-1); + tree->a = na; + } + indir[0] = *tree; + tree->d = ++d; + f = 0; + c = 1; + hasid = 1; + } else if(d == 0) { + /* New tree */ + if(bl != 0) { + errno = ERANGE; + return(-1); + } + /* Assume that numops == largest block number + 1 -- gaps + * will be detected as errors later */ + for(bl = numops - 1; bl > 0; d++, bl <<= BT_INDBITS); + tree->d = d; + c = 0; + hasid = 1; + } else { + /* Get indirect block */ + if(!hasid) { + if((sz = storeget(st, indir, BT_INDBSZ, &tree->a)) < 0) + return(-1); + c = sz / sizeof(struct btnode); + hasid = 1; + } + } + + sel = bl >> ((d - 1) * BT_INDBITS); + if(sel > c) { + errno = ERANGE; + return(-1); + } + + if(sel == c) { + /* Append new */ + if((c > 0) && (!(indir[c - 1].d & 0x80) || ((indir[c - 1].d & 0x7f) < (d - 1)))) { + errno = ERANGE; + return(-1); + } + indir[c].d = 0; + c++; + } + nextsz = 1LL << ((d - 1) * BT_INDBITS); + subops = countops(ops + i, numops - i, bloff + (sel * nextsz), nextsz); + if(btputmany2(st, &indir[sel], ops + i, subops, bloff + (sel * nextsz))) + return(-1); + i += subops; + + if((sel == BT_INDSZ - 1) && (indir[sel].d == ((d - 1) | 0x80))) { + tree->d |= 0x80; + f = 1; + } + } + if(hasid) { + if(storeput(st, indir, c * sizeof(struct btnode), &na)) + return(-1); + tree->a = na; + } + return(0); +} + +int btputmany(struct store *st, struct btnode *tree, struct btop *ops, int numops) +{ + return(btputmany2(st, tree, ops, numops, 0)); +} + +int btput(struct store *st, struct btnode *tree, block_t bl, void *buf, size_t len) +{ + struct btop ops[1]; + + ops[0].blk = bl; + ops[0].buf = buf; + ops[0].len = len; + return(btputmany(st, tree, ops, 1)); +} + +void btmkop(struct btop *op, block_t bl, void *buf, size_t len) +{ + memset(op, 0, sizeof(*op)); + op->blk = bl; + op->buf = buf; + op->len = len; +} + +static int opcmp(const struct btop **op1, const struct btop **op2) +{ + return((*op1)->blk - (*op2)->blk); +} + +void btsortops(struct btop *ops, int numops) +{ + qsort(ops, numops, sizeof(*ops), (int (*)(const void *, const void *))opcmp); +} + +/* +Obsoleted + +int btappend(struct store *st, struct btnode *tree, void *buf, size_t len) +{ + int d, f; + struct btnode indir[BT_INDSZ]; + struct addr na; + block_t c; + ssize_t sz; + + d = tree->d & 0x7f; + f = tree->d & 0x80; + + if(f) { + if(storeput(st, buf, len, &na)) + return(-1); + indir[0] = *tree; + indir[1].d = 0x80; + indir[1].a = na; + if(storeput(st, indir, 2 * sizeof(*indir), &na)) + return(-1); + tree->d = d + 1; + tree->a = na; + return(0); + } + + if(d == 0) { + if(storeput(st, buf, len, &na)) + return(-1); + tree->d |= 0x80; + tree->a = na; + return(0); + } + + if((sz = storeget(st, indir, BT_INDBSZ, &tree->a)) < 0) + return(-1); + c = sz / sizeof(struct btnode); + if(!(indir[c - 1].d & 0x80) || ((indir[c - 1].d & 0x7f) < (d - 1))) { + if(btappend(st, &indir[c - 1], buf, len)) + return(-1); + if(storeput(st, indir, sz, &na)) + return(-1); + tree->a = na; + if((indir[c - 1].d & 0x80) && ((indir[c - 1].d & 0x7f) == (d - 1))) + tree->d |= 0x80; + return(0); + } + + if(storeput(st, buf, len, &na)) + return(-1); + indir[c].d = 0x80; + indir[c].a = na; + if(storeput(st, indir, sz + sizeof(struct btnode), &na)) + return(-1); + tree->a = na; + return(0); +} +*/ + +block_t btcount(struct store *st, struct btnode *tree) +{ + int d, f; + struct btnode indir[BT_INDSZ]; + block_t c, ret; + ssize_t sz; + + d = tree->d & 0x7f; + f = tree->d & 0x80; + + if(f) + return(1LL << (d * BT_INDBITS)); + + if(d == 0) + return(0); + + ret = 0; + while(1) { + if((sz = storeget(st, indir, BT_INDBSZ, &tree->a)) < 0) + return(-1); + c = sz / sizeof(struct btnode); + ret += (c - 1) * (1LL << ((d - 1) * BT_INDBITS)); + d = indir[c - 1].d & 0x7f; + f = indir[c - 1].d & 0x80; + if(f) + return(ret + (1LL << (d * BT_INDBITS))); + tree = &indir[c - 1]; + } +} diff --git a/blocktree.h b/blocktree.h new file mode 100644 index 0000000..c962874 --- /dev/null +++ b/blocktree.h @@ -0,0 +1,33 @@ +#ifndef _BLOCKTREE_H +#define _BLOCKTREE_H + +#include "store.h" +#include "inttypes.h" + +#define BT_INDBITS 10 +#define BT_INDSZ (1 << BT_INDBITS) +#define BT_INDBSZ (sizeof(struct btnode) * BT_INDSZ) + +typedef loff_t block_t; + +struct btnode { + u_int8_t d; + struct addr a; +}; + +struct btop { + block_t blk; + void *buf; + size_t len; + int (*fillfn)(void *buf, size_t len, void *pdata); + void *pdata; +}; + +ssize_t btget(struct store *st, struct btnode *tree, block_t bl, void *buf, size_t len); +int btputmany(struct store *st, struct btnode *tree, struct btop *ops, int numops); +int btput(struct store *st, struct btnode *tree, block_t bl, void *buf, size_t len); +block_t btcount(struct store *st, struct btnode *tree); +void btsortops(struct btop *ops, int numops); +void btmkop(struct btop *op, block_t bl, void *buf, size_t len); + +#endif diff --git a/filestore.c b/filestore.c new file mode 100644 index 0000000..51089a8 --- /dev/null +++ b/filestore.c @@ -0,0 +1,360 @@ +#define _LARGEFILE64_SOURCE +#define _XOPEN_SOURCE 500 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "store.h" +#include "log.h" + +#define LOGMAGIC "Dolda/Venti-1" +#define IDXMAGIC "Dolda/Index-1" +#define LOGENTMAGIC "\xca\xe5\x7a\x93" + +typedef loff_t idx_t; + +struct loghdr { + char magic[sizeof(LOGMAGIC)]; +}; + +struct idxhdr { + char magic[sizeof(IDXMAGIC)]; + u_int64_t size; +}; + +struct idxent { + struct addr addr; + u_int64_t l, r; + u_int64_t off; +}; + +struct logent { + u_int8_t magic[4]; + struct addr name; + u_int16_t len; + u_int8_t fl; +}; + +struct fstore { + int logfd; + int idxfd; + loff_t logsize; + idx_t idxsize; +}; + +static int release(struct fstore *fst) +{ + if(fst->logfd >= 0) { + fsync(fst->logfd); + close(fst->logfd); + } + if(fst->idxfd >= 0) { + fsync(fst->idxfd); + close(fst->idxfd); + } + free(fst); + return(0); +} + +static int releaseg(struct store *st) +{ + return(release(st->pdata)); +} + +static void hash(const void *buf, size_t len, struct addr *a) +{ + gcry_md_hash_buffer(GCRY_MD_SHA256, a->hash, buf, len); +} + +static int getidx(struct fstore *fst, idx_t i, struct idxent *ie) +{ + return(readall(fst->idxfd, ie, sizeof(*ie), sizeof(struct idxhdr) + i * sizeof(struct idxent))); +} + +static int putidx(struct fstore *fst, idx_t i, struct idxent *ie) +{ + return(writeall(fst->idxfd, ie, sizeof(*ie), sizeof(struct idxhdr) + i * sizeof(struct idxent))); +} + +static idx_t lookup(struct fstore *fst, struct addr *a, idx_t *parent) +{ + idx_t i; + struct idxent ie; + int c; + + if(fst->idxsize == 0) { + if(parent != NULL) + *parent = -1; + return(-1); + } + i = 0; + while(1) { + assert(!getidx(fst, i, &ie)); + c = addrcmp(a, &ie.addr); + if(c < 0) { + if(ie.l == 0) { + if(parent != NULL) + *parent = i; + return(-1); + } + i = ie.l; + } else if(c > 0) { + if(ie.r == 0) { + if(parent != NULL) + *parent = i; + return(-1); + } + i = ie.r; + } else { + return(i); + } + } +} + +static idx_t newindex(struct fstore *fst) +{ + size_t newsize; + idx_t ni; + struct idxent ne; + struct idxhdr ih; + + /* XXX: Thread safety */ + ni = fst->idxsize++; + newsize = sizeof(struct idxhdr) + fst->idxsize * sizeof(struct idxent); + if(ftruncate(fst->idxfd, newsize)) + return(-1); + ne.l = ne.r = 0; + assert(!putidx(fst, ni, &ne)); + assert(!readall(fst->idxfd, &ih, sizeof(ih), 0)); + ih.size = fst->idxsize; + assert(!writeall(fst->idxfd, &ih, sizeof(ih), 0)); + return(ni); +} + +static int put(struct store *st, const void *buf, size_t len, struct addr *at) +{ + struct fstore *fst; + struct addr pa; + idx_t i, pi; + struct idxent ie; + loff_t leoff; + int c; + struct logent le; + + if(len > STORE_MAXBLSZ) { + errno = E2BIG; + return(-1); + } + + fst = st->pdata; + hash(buf, len, &pa); + if(at != NULL) + memcpy(at->hash, pa.hash, 32); + + if(lookup(fst, &pa, &pi) != -1) + return(0); + + memcpy(le.magic, LOGENTMAGIC, 4); + le.name = pa; + le.len = len; + le.fl = 0; + /* XXX: Thread safety { */ + leoff = fst->logsize; + fst->logsize += sizeof(le) + len; + /* } */ + /* XXX: Handle data with embedded LOGENTMAGIC */ + writeall(fst->logfd, &le, sizeof(le), leoff); + writeall(fst->logfd, buf, len, leoff + sizeof(le)); + + i = newindex(fst); + assert(!getidx(fst, i, &ie)); + ie.addr = pa; + ie.off = leoff; + assert(!putidx(fst, i, &ie)); + if(pi != -1) { + assert(!getidx(fst, pi, &ie)); + c = addrcmp(&pa, &ie.addr); + if(c < 0) + ie.l = i; + else + ie.r = i; + assert(!putidx(fst, pi, &ie)); + } + + return(0); +} + +#define min(a, b) (((b) < (a))?(b):(a)) + +static ssize_t get(struct store *st, void *buf, size_t len, struct addr *at) +{ + idx_t i; + struct idxent ie; + struct fstore *fst; + struct logent le; + struct addr v; + char tmpbuf[STORE_MAXBLSZ]; + + fst = st->pdata; + if((i = lookup(fst, at, NULL)) == -1) { + errno = ENOENT; + return(-1); + } + assert(!getidx(fst, i, &ie)); + + if(readall(fst->logfd, &le, sizeof(le), ie.off)) { + flog(LOG_CRIT, "could not read log entry: %s", strerror(errno)); + errno = EIO; + return(-1); + } + if(memcmp(le.magic, LOGENTMAGIC, 4)) { + flog(LOG_CRIT, "invalid magic in log"); + errno = EIO; + return(-1); + } + if(addrcmp(&le.name, at)) { + flog(LOG_CRIT, "did not receive correct block from log"); + errno = EIO; + return(-1); + } + if(readall(fst->logfd, tmpbuf, le.len, ie.off + sizeof(le))) { + flog(LOG_CRIT, "could not read log data: %s", strerror(errno)); + errno = EIO; + return(-1); + } + hash(tmpbuf, le.len, &v); + if(addrcmp(&v, &le.name)) { + flog(LOG_CRIT, "log data did not verify against hash"); + errno = EIO; + return(-1); + } + if(buf != NULL) + memcpy(buf, tmpbuf, min(len, le.len)); + return(le.len); +} + +static struct storeops fstops = { + .release = releaseg, + .put = put, + .get = get, +}; + +struct store *newfstore(char *dir) +{ + struct store *st; + struct fstore *fst; + char tbuf[1024]; + struct loghdr lh; + struct idxhdr ih; + struct stat64 sb; + + fst = calloc(1, sizeof(*fst)); + fst->logfd = -1; + fst->idxfd = -1; + + snprintf(tbuf, sizeof(tbuf), "%s/log", dir); + if((fst->logfd = open(tbuf, O_RDWR | O_LARGEFILE)) < 0) { + flog(LOG_ERR, "could not open log %s: %s", tbuf, strerror(errno)); + release(fst); + return(NULL); + } + if(fstat64(fst->logfd, &sb)) { + flog(LOG_ERR, "could not stat log: %s", strerror(errno)); + release(fst); + return(NULL); + } + fst->logsize = sb.st_size; + if(readall(fst->logfd, &lh, sizeof(lh), 0)) { + flog(LOG_ERR, "could not read log header: %s", strerror(errno)); + release(fst); + return(NULL); + } + if(memcmp(lh.magic, LOGMAGIC, sizeof(LOGMAGIC))) { + flog(LOG_ERR, "invalid log magic"); + release(fst); + return(NULL); + } + + snprintf(tbuf, sizeof(tbuf), "%s/index", dir); + if((fst->idxfd = open(tbuf, O_RDWR | O_LARGEFILE)) < 0) { + flog(LOG_ERR, "could not open index %s: %s", tbuf, strerror(errno)); + release(fst); + return(NULL); + } + if(fstat64(fst->idxfd, &sb)) { + flog(LOG_ERR, "could not stat index: %s", strerror(errno)); + release(fst); + return(NULL); + } + if(readall(fst->idxfd, &ih, sizeof(ih), 0)) { + flog(LOG_ERR, "could not read index header: %s", strerror(errno)); + release(fst); + return(NULL); + } + if(memcmp(ih.magic, IDXMAGIC, sizeof(IDXMAGIC))) { + flog(LOG_ERR, "invalid index magic"); + release(fst); + return(NULL); + } + if(sb.st_size != (sizeof(struct idxhdr) + ih.size * sizeof(struct idxent))) { + flog(LOG_ERR, "invalid index size"); + release(fst); + return(NULL); + } + fst->idxsize = ih.size; + + st = newstore(&fstops); + st->pdata = fst; + return(st); +} + +int mkfstore(char *dir) +{ + char tbuf[1024]; + int fd; + struct loghdr lh; + struct idxhdr ih; + + if(access(dir, F_OK)) { + if(mkdir(dir, 0700)) { + flog(LOG_ERR, "could not create %s: %s", dir, strerror(errno)); + return(-1); + } + } + + snprintf(tbuf, sizeof(tbuf), "%s/log", dir); + if((fd = open(tbuf, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0) { + flog(LOG_ERR, "could not create log %s: %s", tbuf, strerror(errno)); + return(-1); + } + memcpy(lh.magic, LOGMAGIC, sizeof(LOGMAGIC)); + if(writeall(fd, &lh, sizeof(lh), 0)) { + flog(LOG_ERR, "could not write log header: %s", strerror(errno)); + close(fd); + return(-1); + } + close(fd); + + snprintf(tbuf, sizeof(tbuf), "%s/index", dir); + if((fd = open(tbuf, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0) { + flog(LOG_ERR, "could not create index %s: %s", tbuf, strerror(errno)); + return(-1); + } + memcpy(ih.magic, IDXMAGIC, sizeof(IDXMAGIC)); + ih.size = 0; + if(writeall(fd, &ih, sizeof(ih), 0)) { + flog(LOG_ERR, "could not write index header: %s", strerror(errno)); + close(fd); + return(-1); + } + close(fd); + return(0); +} diff --git a/log.c b/log.c new file mode 100644 index 0000000..55af6bf --- /dev/null +++ b/log.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include +#include + +#include "log.h" + +void logstderr(int level, char *msg, ...) +{ + va_list args; + + va_start(args, msg); + vfprintf(stderr, msg, args); + fputc('\n', stderr); + va_end(args); +} + +void logsyslog(int level, char *msg, ...) +{ + va_list args; + + va_start(args, msg); + vsyslog(level, msg, args); + va_end(args); +} + +void (*flog)(int, char *, ...) = logstderr; diff --git a/log.h b/log.h new file mode 100644 index 0000000..3a3ba7b --- /dev/null +++ b/log.h @@ -0,0 +1,11 @@ +#ifndef _LOG_H +#define _LOG_H + +#include + +extern void (*flog)(int level, char *msg, ...); + +void logstderr(int level, char *msg, ...); +void logsyslog(int level, char *msg, ...); + +#endif diff --git a/mkfs.vc.c b/mkfs.vc.c new file mode 100644 index 0000000..a507abe --- /dev/null +++ b/mkfs.vc.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "store.h" +#include "blocktree.h" +#include "vcfs.h" + +int main(int argc, char **argv) +{ + struct store *st; + struct inode root; + struct revrec frev; + struct dentry dots; + time_t now; + int fd; + char tbuf[1024]; + + if(argc < 2) { + fprintf(stderr, "usage; mkfs.vc DIR\n"); + exit(1); + } + if(mkfstore(argv[1])) + exit(1); + if((st = newfstore(argv[1])) == NULL) + exit(1); + + now = time(NULL); + + memset(&dots, 0, sizeof(dots)); + dots.inode = 0; + + root.mode = S_IFDIR | 0755; + root.mtime = root.ctime = now; + root.size = 2; + root.uid = getuid(); + root.gid = getgid(); + root.links = 2; + root.data.d = 0; + root.xattr.d = 0; + strcpy(dots.name, "."); + if(btput(st, &root.data, 0, &dots, sizeof(dots) - sizeof(dots.name) + 2)) { + fprintf(stderr, "mkfs.vc: could not create root directory entries: %s\n", strerror(errno)); + exit(1); + } + strcpy(dots.name, ".."); + if(btput(st, &root.data, 1, &dots, sizeof(dots) - sizeof(dots.name) + 3)) { + fprintf(stderr, "mkfs.vc: could not create root directory entries: %s\n", strerror(errno)); + exit(1); + } + + frev.ct = now; + frev.root.d = 0; + if(btput(st, &frev.root, 0, &root, sizeof(root))) { + fprintf(stderr, "mkfs.vc: could not store root directory inode: %s\n", strerror(errno)); + exit(1); + } + releasestore(st); + + snprintf(tbuf, sizeof(tbuf), "%s/revs", argv[1]); + if((fd = open(tbuf, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0) { + fprintf(stderr, "mkfs.vc: could not create revision database: %s\n", strerror(errno)); + exit(1); + } + if(writeall(fd, &frev, sizeof(frev), 0)) { + fprintf(stderr, "mkfs.vc: could not write initial revision: %s\n", strerror(errno)); + exit(1); + } + fsync(fd); + close(fd); + return(0); +} diff --git a/mkstore.c b/mkstore.c new file mode 100644 index 0000000..f50860c --- /dev/null +++ b/mkstore.c @@ -0,0 +1,15 @@ +#include +#include + +#include "store.h" + +int main(int argc, char **argv) +{ + if(argc < 2) { + fprintf(stderr, "usage: mkstore DIR\n"); + exit(1); + } + if(mkfstore(argv[1])) + exit(1); + return(0); +} diff --git a/store.c b/store.c new file mode 100644 index 0000000..8727512 --- /dev/null +++ b/store.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include + +#include "store.h" + +struct store *newstore(struct storeops *ops) +{ + struct store *new; + + new = malloc(sizeof(*new)); + new->ops = ops; + new->pdata = NULL; + new->cache = calloc(4096 * 4, sizeof(struct storecache)); + return(new); +} + +#define min(a, b) (((b) < (a))?(b):(a)) + +static ssize_t cacheget(struct store *st, struct addr *a, void *buf, size_t len) +{ + int he, i; + + he = a->hash[0] + ((a->hash[1] & 0x0f) << 8); + for(i = 0; i < 4; i++) { + if(!addrcmp(&st->cache[he * 4 + i].a, a)) + break; + } + if(i == 4) + return(-2); + if(st->cache[he * 4 + i].data != NULL) + memcpy(buf, st->cache[he * 4 + i].data, min(len, st->cache[he * 4 + i].dlen)); + return(st->cache[he * 4 + i].dlen); +} + +static void cacheput(struct store *st, struct addr *a, const void *data, ssize_t len) +{ + int he, i; + struct storecache tmp; + + he = a->hash[0] + ((a->hash[1] & 0x0f) << 8); + for(i = 0; i < 4; i++) { + if(!addrcmp(&st->cache[he * 4 + i].a, a)) + break; + } + if(i == 0) + return; + if(i < 4) { + tmp = st->cache[he * 4 + i]; + memmove(&st->cache[he * 4 + 1], &st->cache[he * 4], i); + st->cache[he * 4] = tmp; + return; + } + if(st->cache[he * 4 + 3].data != NULL) + free(st->cache[he * 4 + 3].data); + memmove(&st->cache[he * 4 + 1], &st->cache[he * 4], 3); + st->cache[he * 4].a = *a; + if(len > 0) + st->cache[he * 4].data = memcpy(malloc(len), data, len); + else + st->cache[he * 4].data = NULL; + st->cache[he * 4].dlen = len; +} + +int storeput(struct store *st, const void *buf, size_t len, struct addr *at) +{ + int ret; + struct addr na; + + ret = st->ops->put(st, buf, len, &na); + if(!ret) + cacheput(st, &na, buf, len); + if(at != NULL) + *at = na; + return(ret); +} + +ssize_t storeget(struct store *st, void *buf, size_t len, struct addr *at) +{ + ssize_t sz; + + sz = cacheget(st, at, buf, len); + if(sz != -2) { + if(sz == -1) + errno = ENOENT; + return(sz); + } + sz = st->ops->get(st, buf, len, at); + if((sz < 0) && (errno == ENOENT)) + cacheput(st, at, NULL, -1); + else if(sz >= 0) + cacheput(st, at, buf, sz); + return(sz); +} + +int releasestore(struct store *st) +{ + int err; + + if((err = st->ops->release(st)) != 0) + return(err); + free(st); + return(0); +} + +int addrcmp(struct addr *a1, struct addr *a2) +{ + return(memcmp(a1->hash, a2->hash, 32)); +} + +char *formataddr(struct addr *a) +{ + int i; + static char buf[65]; + + for(i = 0; i < 32; i++) + sprintf(buf + (i * 2), "%02x", a->hash[i]); + buf[64] = 0; + return(buf); +} + +static int hex2int(char hex) +{ + if((hex >= 'a') && (hex <= 'f')) + return(hex - 'a' + 10); + if((hex >= 'A') && (hex <= 'F')) + return(hex - 'A' + 10); + if((hex >= '0') && (hex <= '9')) + return(hex - '0'); + return(-1); +} + +int parseaddr(char *p, struct addr *a) +{ + int i, d; + + for(i = 0; i < 32; i++) { + if((d = hex2int(*p++)) < 0) + return(-1); + while(*p == ' ') + p++; + a->hash[i] = d << 4; + if((d = hex2int(*p++)) < 0) + return(-1); + while(*p == ' ') + p++; + a->hash[i] |= d; + } + if(*p != 0) + return(-1); + return(0); +} + +int niladdr(struct addr *a) +{ + int i; + + for(i = 0; i < 32; i++) { + if(a->hash[i]) + return(0); + } + return(1); +} diff --git a/store.h b/store.h new file mode 100644 index 0000000..cd03347 --- /dev/null +++ b/store.h @@ -0,0 +1,42 @@ +#ifndef _STORE_H +#define _STORE_H + +#include + +#define STORE_MAXBLSZ 65535 + +struct addr { + unsigned char hash[32]; +}; + +struct storecache { + struct addr a; + void *data; + ssize_t dlen; +}; + +struct store { + struct storeops *ops; + void *pdata; + struct storecache *cache; +}; + +struct storeops { + int (*put)(struct store *st, const void *buf, size_t len, struct addr *at); + ssize_t (*get)(struct store *st, void *buf, size_t len, struct addr *at); + int (*release)(struct store *st); +}; + +struct store *newstore(struct storeops *ops); +int storeput(struct store *st, const void *buf, size_t len, struct addr *at); +ssize_t storeget(struct store *st, void *buf, size_t len, struct addr *at); +int releasestore(struct store *st); +int addrcmp(struct addr *a1, struct addr *a2); +char *formataddr(struct addr *a); +int parseaddr(char *buf, struct addr *a); +int niladdr(struct addr *a); + +struct store *newfstore(char *dir); +int mkfstore(char *dir); + +#endif diff --git a/storeget.c b/storeget.c new file mode 100644 index 0000000..ad746e7 --- /dev/null +++ b/storeget.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include + +#include "store.h" + +char buf[STORE_MAXBLSZ]; + +int main(int argc, char **argv) +{ + struct store *st; + struct addr a; + int ret; + + if(argc < 3) { + fprintf(stderr, "usage: storeget DIR HASH\n"); + exit(1); + } + if((st = newfstore(argv[1])) == NULL) + exit(1); + parseaddr(argv[2], &a); + if((ret = storeget(st, buf, STORE_MAXBLSZ, &a)) < 0) { + perror(argv[2]); + exit(1); + } + write(1, buf, ret); + return(0); +} diff --git a/storeput.c b/storeput.c new file mode 100644 index 0000000..bb7f007 --- /dev/null +++ b/storeput.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include + +#include "store.h" + +char buf[STORE_MAXBLSZ]; + +int main(int argc, char **argv) +{ + struct store *st; + struct addr a; + int ret, o; + + if(argc < 3) { + fprintf(stderr, "usage: storeget DIR\n"); + exit(1); + } + for(o = 0; (ret = read(0, buf + o, STORE_MAXBLSZ - o)) > 0; o += ret); + if((st = newfstore(argv[1])) == NULL) + exit(1); + if((ret = storeput(st, buf, STORE_MAXBLSZ, &a)) < 0) { + perror(argv[2]); + exit(1); + } + printf("%s\n", formataddr(&a)); + return(0); +} diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..4e4f537 --- /dev/null +++ b/utils.c @@ -0,0 +1,51 @@ +#define _LARGEFILE64_SOURCE +#define _XOPEN_SOURCE 500 +#include +#include +#include + +#include "utils.h" + +int readall(int fd, void *buf, size_t len, loff_t offset) +{ + int ret; + + while(len > 0) { + /* + if(lseek(fd, offset, SEEK_SET) != offset) + return(-1); + ret = read(fd, buf, len); + */ + ret = pread64(fd, buf, len, offset); + if(ret < 0) + return(-1); + if(ret == 0) { + errno = ENODATA; + return(-1); + } + buf += ret; + len -= ret; + offset += ret; + } + return(0); +} + +int writeall(int fd, const void *buf, size_t len, loff_t offset) +{ + int ret; + + while(len > 0) { + /* + if(lseek(fd, offset, SEEK_SET) != offset) + return(-1); + ret = write(fd, buf, len); + */ + ret = pwrite64(fd, buf, len, offset); + if(ret < 0) + return(-1); + buf += ret; + len -= ret; + offset += ret; + } + return(0); +} diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..6dd998b --- /dev/null +++ b/utils.h @@ -0,0 +1,7 @@ +#ifndef _UTILS_H +#define _UTILS_H + +int readall(int fd, void *buf, size_t len, loff_t offset); +int writeall(int fd, const void *buf, size_t len, loff_t offset); + +#endif diff --git a/vcfs.c b/vcfs.c new file mode 100644 index 0000000..0a49f49 --- /dev/null +++ b/vcfs.c @@ -0,0 +1,592 @@ +#define _LARGEFILE64_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "log.h" +#include "store.h" +#include "blocktree.h" +#include "vcfs.h" + +/* XXX: The current i-numbering scheme sucks. */ + +struct btree { + struct btree *l, *r; + int h; + void *d; +}; + +struct inoc { + vc_ino_t inode; + struct btnode inotab; + fuse_ino_t cnum; +}; + +struct vcfsdata { + struct store *st; + int revfd; + vc_rev_t currev; + vc_ino_t nextino; + struct btnode inotab; + struct btree *inocbf, *inocbv; + fuse_ino_t inocser; +}; + +#define max(a, b) (((b) > (a))?(b):(a)) +static struct btnode nilnode = {0, }; + +static int btheight(struct btree *tree) +{ + if(tree == NULL) + return(0); + return(tree->h); +} + +static void btsetheight(struct btree *tree) +{ + if(tree == NULL) + return; + tree->h = max(btheight(tree->l), btheight(tree->r)) + 1; +} + +static void bbtrl(struct btree **tree); + +static void bbtrr(struct btree **tree) +{ + struct btree *m, *l, *r; + + if(btheight((*tree)->l->r) > btheight((*tree)->l->l)) + bbtrl(&(*tree)->l); + r = (*tree); + l = r->l; + m = l->r; + r->l = m; + btsetheight(r); + l->r = r; + btsetheight(l); + *tree = l; +} + +static void bbtrl(struct btree **tree) +{ + struct btree *m, *l, *r; + + if(btheight((*tree)->r->l) > btheight((*tree)->r->r)) + bbtrr(&(*tree)->r); + l = (*tree); + r = l->r; + m = r->l; + l->r = m; + btsetheight(l); + r->l = l; + btsetheight(r); + *tree = r; +} + +static int bbtreeput(struct btree **tree, void *item, int (*cmp)(void *, void *)) +{ + int c, r; + + if(*tree == NULL) { + *tree = calloc(1, sizeof(**tree)); + (*tree)->d = item; + (*tree)->h = 1; + return(1); + } + if((c = cmp(item, (*tree)->d)) < 0) + r = bbtreeput(&(*tree)->l, item, cmp); + else if(c > 0) + r = bbtreeput(&(*tree)->r, item, cmp); + else + return(0); + btsetheight(*tree); + if(btheight((*tree)->l) > btheight((*tree)->r) + 1) + bbtrr(tree); + if(btheight((*tree)->r) > btheight((*tree)->l) + 1) + bbtrl(tree); + return(r); +} + +static void *btreeget(struct btree *tree, void *key, int (*cmp)(void *, void *)) +{ + int c; + + while(1) { + if(tree == NULL) + return(NULL); + c = cmp(key, tree->d); + if(c < 0) + tree = tree->l; + else if(c > 0) + tree = tree->r; + else + return(tree->d); + } +} + +static void dstrvcfs(struct vcfsdata *fsd) +{ + releasestore(fsd->st); + fsync(fsd->revfd); + close(fsd->revfd); + free(fsd); +} + +static int inoccmpbf(struct inoc *a, struct inoc *b) +{ + return(a->cnum - b->cnum); +} + +static int inoccmpbv(struct inoc *a, struct inoc *b) +{ + if(a->inode < b->inode) + return(-1); + if(a->inode > b->inode) + return(1); + if(a->inotab.d < b->inotab.d) + return(-1); + if(a->inotab.d > b->inotab.d) + return(1); + return(addrcmp(&a->inotab.a, &b->inotab.a)); +} + +static struct inoc *getinocbf(struct vcfsdata *fsd, fuse_ino_t inode) +{ + struct inoc key; + + key.cnum = inode; + return(btreeget(fsd->inocbf, &key, (int (*)(void *, void *))inoccmpbf)); +} + +static struct inoc *getinocbv(struct vcfsdata *fsd, vc_ino_t inode, struct btnode inotab) +{ + struct inoc key; + + key.inotab = inotab; + key.inode = inode; + return(btreeget(fsd->inocbv, &key, (int (*)(void *, void *))inoccmpbv)); +} + +static fuse_ino_t cacheinode(struct vcfsdata *fsd, vc_ino_t inode, struct btnode inotab) +{ + fuse_ino_t ret; + struct inoc *inoc; + + if((inoc = getinocbv(fsd, inode, inotab)) != NULL) + return(inoc->cnum); + ret = fsd->inocser++; + inoc = calloc(1, sizeof(*inoc)); + inoc->inode = inode; + inoc->inotab = inotab; + inoc->cnum = ret; + bbtreeput(&fsd->inocbf, inoc, (int (*)(void *, void *))inoccmpbf); + bbtreeput(&fsd->inocbv, inoc, (int (*)(void *, void *))inoccmpbv); + return(ret); +} + +static struct vcfsdata *initvcfs(char *dir) +{ + struct vcfsdata *fsd; + char tbuf[1024]; + struct stat64 sb; + struct revrec cr; + + fsd = calloc(1, sizeof(*fsd)); + snprintf(tbuf, sizeof(tbuf), "%s/revs", dir); + if((fsd->revfd = open(tbuf, O_RDWR | O_LARGEFILE)) < 0) { + flog(LOG_ERR, "could not open revision database: %s", strerror(errno)); + free(fsd); + return(NULL); + } + if(fstat64(fsd->revfd, &sb)) { + flog(LOG_ERR, "could not stat revision database: %s", strerror(errno)); + close(fsd->revfd); + free(fsd); + return(NULL); + } + if(sb.st_size % sizeof(struct revrec) != 0) { + flog(LOG_ERR, "revision database has illegal size"); + close(fsd->revfd); + free(fsd); + return(NULL); + } + fsd->currev = (sb.st_size / sizeof(struct revrec)) - 1; + assert(!readall(fsd->revfd, &cr, sizeof(cr), fsd->currev * sizeof(struct revrec))); + fsd->inotab = cr.root; + if((fsd->st = newfstore(dir)) == NULL) { + close(fsd->revfd); + free(fsd); + return(NULL); + } + fsd->inocser = 1; + cacheinode(fsd, 0, nilnode); + if((fsd->nextino = btcount(fsd->st, &fsd->inotab)) < 0) { + flog(LOG_ERR, "could not count inodes: %s"); + close(fsd->revfd); + releasestore(fsd->st); + free(fsd); + return(NULL); + } + return(fsd); +} + +static vc_ino_t dirlookup(struct vcfsdata *fsd, struct btnode *dirdata, const char *name, int *di) +{ + struct dentry dent; + int i; + ssize_t sz; + + for(i = 0; ; i++) { + if((sz = btget(fsd->st, dirdata, i, &dent, sizeof(dent))) < 0) { + if(errno == ERANGE) + errno = ENOENT; + return(-1); + } + if((dent.inode >= 0) && !strncmp(dent.name, name, sizeof(dent.name))) { + if(di != NULL) + *di = i; + return(dent.inode); + } + } +} + +static void fusedestroy(struct vcfsdata *fsd) +{ + dstrvcfs(fsd); +} + +static void fillstat(struct stat *sb, struct inode *file) +{ + sb->st_mode = file->mode; + sb->st_atime = (time_t)file->mtime; + sb->st_mtime = (time_t)file->mtime; + sb->st_ctime = (time_t)file->ctime; + sb->st_size = file->size; + sb->st_uid = file->uid; + sb->st_gid = file->gid; + sb->st_nlink = file->links; +} + +static int getinode(struct vcfsdata *fsd, struct btnode inotab, vc_ino_t ino, struct inode *buf) +{ + ssize_t sz; + + if(inotab.d == 0) + inotab = fsd->inotab; + if((sz = btget(fsd->st, &inotab, ino, buf, sizeof(*buf))) < 0) + return(-1); + if(sz != sizeof(*buf)) { + flog(LOG_ERR, "illegal size for inode %i", ino); + errno = EIO; + return(-1); + } + return(0); +} + +static void fusegetattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + struct vcfsdata *fsd; + struct stat sb; + struct inoc *inoc; + struct inode file; + + fsd = fuse_req_userdata(req); + memset(&sb, 0, sizeof(sb)); + if((inoc = getinocbf(fsd, ino)) == NULL) { + fuse_reply_err(req, ENOENT); + return; + } + if(getinode(fsd, inoc->inotab, inoc->inode, &file)) { + fuse_reply_err(req, errno); + return; + } + fillstat(&sb, &file); + fuse_reply_attr(req, &sb, 0); +} + +static void fuselookup(fuse_req_t req, fuse_ino_t parent, const char *name) +{ + struct vcfsdata *fsd; + struct inode file; + struct inoc *inoc; + struct fuse_entry_param e; + vc_ino_t target; + + fsd = fuse_req_userdata(req); + if((inoc = getinocbf(fsd, parent)) == NULL) { + fuse_reply_err(req, ENOENT); + return; + } + if(getinode(fsd, inoc->inotab, inoc->inode, &file)) { + fuse_reply_err(req, errno); + return; + } + if((target = dirlookup(fsd, &file.data, name, NULL)) < 0) { + fuse_reply_err(req, errno); + return; + } + if(getinode(fsd, inoc->inotab, target, &file)) { + fuse_reply_err(req, errno); + return; + } + memset(&e, 0, sizeof(e)); + e.ino = cacheinode(fsd, target, inoc->inotab); + fillstat(&e.attr, &file); + fuse_reply_entry(req, &e); +} + +static void fusereaddir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) +{ + struct vcfsdata *fsd; + struct inoc *inoc; + struct inode file; + struct dentry dent; + struct stat sb; + ssize_t sz, osz, bsz; + char *buf; + + fsd = fuse_req_userdata(req); + if((inoc = getinocbf(fsd, ino)) == NULL) { + fuse_reply_err(req, ENOENT); + return; + } + if(getinode(fsd, inoc->inotab, inoc->inode, &file)) { + fuse_reply_err(req, errno); + return; + } + bsz = 0; + buf = NULL; + while(bsz < size) { + memset(&dent, 0, sizeof(dent)); + if((sz = btget(fsd->st, &file.data, off++, &dent, sizeof(dent))) < 0) { + if(errno == ERANGE) { + if(buf != NULL) + break; + fuse_reply_buf(req, NULL, 0); + return; + } + fuse_reply_err(req, errno); + if(buf != NULL) + free(buf); + return; + } + if(dent.inode < 0) + continue; + osz = bsz; + bsz += fuse_add_direntry(req, NULL, 0, dent.name, NULL, 0); + if(bsz > size) + break; + buf = realloc(buf, bsz); + memset(&sb, 0, sizeof(sb)); + sb.st_ino = cacheinode(fsd, dent.inode, inoc->inotab); + fuse_add_direntry(req, buf + osz, bsz - osz, dent.name, &sb, off); + } + fuse_reply_buf(req, buf, bsz); + if(buf != NULL) + free(buf); +} + +static vc_rev_t commit(struct vcfsdata *fsd, struct btnode inotab) +{ + struct revrec rr; + + rr.ct = time(NULL); + rr.root = inotab; + if(writeall(fsd->revfd, &rr, sizeof(rr), (fsd->currev + 1) * sizeof(struct revrec))) { + flog(LOG_CRIT, "could not write new revision: %s", strerror(errno)); + return(-1); + } + fsd->inotab = inotab; + return(++fsd->currev); +} + +static int deldentry(struct vcfsdata *fsd, struct inode *ino, int di) +{ + if((di < 0) || (di >= ino->size)) { + errno = ERANGE; + return(-1); + } + +} + +static int setdentry(struct vcfsdata *fsd, struct inode *ino, int di, const char *name, vc_ino_t target) +{ + struct dentry dent; + ssize_t sz; + + if(strlen(name) > 255) { + errno = ENAMETOOLONG; + return(-1); + } + memset(&dent, 0, sizeof(dent)); + strcpy(dent.name, name); + dent.inode = target; + sz = sizeof(dent) - sizeof(dent.name) + strlen(name) + 1; + if((di == -1) || (di == ino->size)) { + if(btput(fsd->st, &ino->data, ino->size, &dent, sz)) + return(-1); + ino->size++; + return(0); + } + return(btput(fsd->st, &ino->data, di, &dent, sz)); +} + +static void fusemkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode) +{ + struct vcfsdata *fsd; + struct inoc *inoc; + struct inode file, new; + struct btnode inotab; + struct fuse_entry_param e; + const struct fuse_ctx *ctx; + struct btop ops[2]; + + fsd = fuse_req_userdata(req); + ctx = fuse_req_ctx(req); + if((inoc = getinocbf(fsd, parent)) == NULL) { + fuse_reply_err(req, ENOENT); + return; + } + if(inoc->inotab.d != 0) { + fuse_reply_err(req, EROFS); + return; + } + if(getinode(fsd, inoc->inotab, inoc->inode, &file)) { + fuse_reply_err(req, errno); + return; + } + if(!S_ISDIR(file.mode)) { + fuse_reply_err(req, ENOTDIR); + return; + } + if(dirlookup(fsd, &file.data, name, NULL) != -1) { + fuse_reply_err(req, EEXIST); + return; + } + + memset(&new, 0, sizeof(new)); + new.mode = S_IFDIR | mode; + new.mtime = new.ctime = time(NULL); + new.size = 0; + new.uid = ctx->uid; + new.gid = ctx->gid; + new.links = 2; + if(setdentry(fsd, &new, -1, ".", fsd->nextino) || setdentry(fsd, &new, -1, "..", inoc->inode)) { + fuse_reply_err(req, errno); + return; + } + + inotab = fsd->inotab; + if(setdentry(fsd, &file, -1, name, fsd->nextino)) { + fuse_reply_err(req, errno); + return; + } + file.links++; + btmkop(ops + 0, inoc->inode, &file, sizeof(file)); + btmkop(ops + 1, fsd->nextino, &new, sizeof(new)); + if(btputmany(fsd->st, &inotab, ops, 2)) { + fuse_reply_err(req, errno); + return; + } + /* + if(btput(fsd->st, &inotab, fsd->nextino, &new, sizeof(new))) { + fuse_reply_err(req, errno); + return; + } + if(btput(fsd->st, &inotab, inoc->inode, &file, sizeof(file))) { + fuse_reply_err(req, errno); + return; + } + */ + commit(fsd, inotab); + + memset(&e, 0, sizeof(e)); + e.ino = cacheinode(fsd, fsd->nextino++, nilnode); + fillstat(&e.attr, &new); + fuse_reply_entry(req, &e); +} + +static void fusermdir(fuse_req_t req, fuse_ino_t parent, const char *name) +{ + struct vcfsdata *fsd; + struct inoc *inoc; + struct inode file; + int di; + + fsd = fuse_req_userdata(req); + if((inoc = getinocbf(fsd, parent)) == NULL) { + fuse_reply_err(req, ENOENT); + return; + } + if(inoc->inotab.d != 0) { + fuse_reply_err(req, EROFS); + return; + } + if(getinode(fsd, inoc->inotab, inoc->inode, &file)) { + fuse_reply_err(req, errno); + return; + } + if(!S_ISDIR(file.mode)) { + fuse_reply_err(req, ENOTDIR); + return; + } + if(dirlookup(fsd, &file.data, name, &di) == -1) { + fuse_reply_err(req, ENOENT); + return; + } +} + +static struct fuse_lowlevel_ops fuseops = { + .destroy = (void (*)(void *))fusedestroy, + .lookup = fuselookup, + .getattr = fusegetattr, + .readdir = fusereaddir, + .mkdir = fusemkdir, + .rmdir = fusermdir, +}; + +int main(int argc, char **argv) +{ + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + struct fuse_session *fs; + struct fuse_chan *ch; + struct vcfsdata *fsd; + char *mtpt; + int err, fd; + + if((fsd = initvcfs(".")) == NULL) + exit(1); + if(fuse_parse_cmdline(&args, &mtpt, NULL, NULL) < 0) + exit(1); + if((fd = fuse_mount(mtpt, &args)) < 0) + exit(1); + if((fs = fuse_lowlevel_new(&args, &fuseops, sizeof(fuseops), fsd)) == NULL) { + fuse_unmount(mtpt, fd); + close(fd); + fprintf(stderr, "vcfs: could not initialize fuse\n"); + exit(1); + } + fuse_set_signal_handlers(fs); + if((ch = fuse_kern_chan_new(fd)) == NULL) { + fuse_remove_signal_handlers(fs); + fuse_unmount(mtpt, fd); + fuse_session_destroy(fs); + close(fd); + exit(1); + } + + fuse_session_add_chan(fs, ch); + err = fuse_session_loop(fs); + + fuse_remove_signal_handlers(fs); + fuse_unmount(mtpt, fd); + fuse_session_destroy(fs); + close(fd); + return(err?1:0); +} diff --git a/vcfs.h b/vcfs.h new file mode 100644 index 0000000..8fd2727 --- /dev/null +++ b/vcfs.h @@ -0,0 +1,32 @@ +#ifndef _VCFS_H +#define _VCFS_H + +#include +#include + +#include "blocktree.h" + +typedef loff_t vc_ino_t; +typedef loff_t vc_rev_t; + +struct revrec { + u_int64_t ct; + struct btnode root; +}; + +struct inode { + u_int32_t mode; + u_int64_t mtime, ctime; + u_int64_t size; + u_int32_t uid, gid; + u_int32_t links; + struct btnode data; + struct btnode xattr; +}; + +struct dentry { + u_int64_t inode; + char name[256]; +}; + +#endif -- 2.11.0