From: fredrik Date: Thu, 20 Jul 2006 13:15:32 +0000 (+0000) Subject: Initial import. X-Git-Url: http://git.dolda2000.com/gitweb/?a=commitdiff_plain;h=d5cf535197d5090554453b6357867d782f0d1484;p=vcfs.git Initial import. git-svn-id: svn+ssh://svn.dolda2000.com/srv/svn/repos/src/vcfs@673 959494ce-11ee-0310-bf91-de5d638817bd --- d5cf535197d5090554453b6357867d782f0d1484 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