1 #define _LARGEFILE64_SOURCE
9 #include <fuse_lowlevel.h>
14 #include "blocktree.h"
17 /* XXX: The current i-numbering scheme sucks. */
37 struct btree *inocbf, *inocbv;
41 #define max(a, b) (((b) > (a))?(b):(a))
42 static struct btnode nilnode = {0, };
44 static int btheight(struct btree *tree)
51 static void btsetheight(struct btree *tree)
55 tree->h = max(btheight(tree->l), btheight(tree->r)) + 1;
58 static void bbtrl(struct btree **tree);
60 static void bbtrr(struct btree **tree)
62 struct btree *m, *l, *r;
64 if(btheight((*tree)->l->r) > btheight((*tree)->l->l))
76 static void bbtrl(struct btree **tree)
78 struct btree *m, *l, *r;
80 if(btheight((*tree)->r->l) > btheight((*tree)->r->r))
92 static int bbtreeput(struct btree **tree, void *item, int (*cmp)(void *, void *))
97 *tree = calloc(1, sizeof(**tree));
102 if((c = cmp(item, (*tree)->d)) < 0)
103 r = bbtreeput(&(*tree)->l, item, cmp);
105 r = bbtreeput(&(*tree)->r, item, cmp);
109 if(btheight((*tree)->l) > btheight((*tree)->r) + 1)
111 if(btheight((*tree)->r) > btheight((*tree)->l) + 1)
116 static void *btreeget(struct btree *tree, void *key, int (*cmp)(void *, void *))
123 c = cmp(key, tree->d);
133 static void dstrvcfs(struct vcfsdata *fsd)
135 releasestore(fsd->st);
141 static int inoccmpbf(struct inoc *a, struct inoc *b)
143 return(a->cnum - b->cnum);
146 static int inoccmpbv(struct inoc *a, struct inoc *b)
148 if(a->inode < b->inode)
150 if(a->inode > b->inode)
152 if(a->inotab.d < b->inotab.d)
154 if(a->inotab.d > b->inotab.d)
156 return(addrcmp(&a->inotab.a, &b->inotab.a));
159 static struct inoc *getinocbf(struct vcfsdata *fsd, fuse_ino_t inode)
164 return(btreeget(fsd->inocbf, &key, (int (*)(void *, void *))inoccmpbf));
167 static struct inoc *getinocbv(struct vcfsdata *fsd, vc_ino_t inode, struct btnode inotab)
173 return(btreeget(fsd->inocbv, &key, (int (*)(void *, void *))inoccmpbv));
176 static fuse_ino_t cacheinode(struct vcfsdata *fsd, vc_ino_t inode, struct btnode inotab)
181 if((inoc = getinocbv(fsd, inode, inotab)) != NULL)
183 ret = fsd->inocser++;
184 inoc = calloc(1, sizeof(*inoc));
186 inoc->inotab = inotab;
188 bbtreeput(&fsd->inocbf, inoc, (int (*)(void *, void *))inoccmpbf);
189 bbtreeput(&fsd->inocbv, inoc, (int (*)(void *, void *))inoccmpbv);
193 static struct vcfsdata *initvcfs(char *dir)
195 struct vcfsdata *fsd;
200 fsd = calloc(1, sizeof(*fsd));
201 snprintf(tbuf, sizeof(tbuf), "%s/revs", dir);
202 if((fsd->revfd = open(tbuf, O_RDWR | O_LARGEFILE)) < 0) {
203 flog(LOG_ERR, "could not open revision database: %s", strerror(errno));
207 if(fstat64(fsd->revfd, &sb)) {
208 flog(LOG_ERR, "could not stat revision database: %s", strerror(errno));
213 if(sb.st_size % sizeof(struct revrec) != 0) {
214 flog(LOG_ERR, "revision database has illegal size");
219 fsd->currev = (sb.st_size / sizeof(struct revrec)) - 1;
220 assert(!readall(fsd->revfd, &cr, sizeof(cr), fsd->currev * sizeof(struct revrec)));
221 fsd->inotab = cr.root;
222 if((fsd->st = newfstore(dir)) == NULL) {
228 cacheinode(fsd, 0, nilnode);
229 if((fsd->nextino = btcount(fsd->st, &fsd->inotab, INOBLSIZE)) < 0) {
230 flog(LOG_ERR, "could not count inodes: %s");
232 releasestore(fsd->st);
239 static vc_ino_t dirlookup(struct vcfsdata *fsd, struct btnode *dirdata, const char *name, int *di)
246 if((sz = btget(fsd->st, dirdata, i, &dent, sizeof(dent), DIRBLSIZE)) < 0) {
251 if((dent.inode >= 0) && !strncmp(dent.name, name, sizeof(dent.name))) {
259 static void fusedestroy(struct vcfsdata *fsd)
264 static void fillstat(struct stat *sb, struct inode *file)
266 sb->st_mode = file->mode;
267 sb->st_atime = (time_t)file->mtime;
268 sb->st_mtime = (time_t)file->mtime;
269 sb->st_ctime = (time_t)file->ctime;
270 sb->st_size = file->size;
271 sb->st_uid = file->uid;
272 sb->st_gid = file->gid;
273 sb->st_nlink = file->links;
276 static int getinode(struct vcfsdata *fsd, struct btnode inotab, vc_ino_t ino, struct inode *buf)
281 inotab = fsd->inotab;
282 if((sz = btget(fsd->st, &inotab, ino, buf, sizeof(*buf), INOBLSIZE)) < 0)
284 if(sz != sizeof(*buf)) {
285 flog(LOG_ERR, "illegal size for inode %i", ino);
292 static void fusegetattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
294 struct vcfsdata *fsd;
299 fsd = fuse_req_userdata(req);
300 memset(&sb, 0, sizeof(sb));
301 if((inoc = getinocbf(fsd, ino)) == NULL) {
302 fuse_reply_err(req, ENOENT);
305 if(getinode(fsd, inoc->inotab, inoc->inode, &file)) {
306 fuse_reply_err(req, errno);
309 fillstat(&sb, &file);
311 fuse_reply_attr(req, &sb, 0);
314 static void fuselookup(fuse_req_t req, fuse_ino_t parent, const char *name)
316 struct vcfsdata *fsd;
319 struct fuse_entry_param e;
322 fsd = fuse_req_userdata(req);
323 if((inoc = getinocbf(fsd, parent)) == NULL) {
324 fuse_reply_err(req, ENOENT);
327 if(getinode(fsd, inoc->inotab, inoc->inode, &file)) {
328 fuse_reply_err(req, errno);
331 if((target = dirlookup(fsd, &file.data, name, NULL)) < 0) {
332 fuse_reply_err(req, errno);
335 if(getinode(fsd, inoc->inotab, target, &file)) {
336 fuse_reply_err(req, errno);
339 memset(&e, 0, sizeof(e));
340 e.ino = cacheinode(fsd, target, inoc->inotab);
341 fillstat(&e.attr, &file);
342 e.attr.st_ino = e.ino;
343 fuse_reply_entry(req, &e);
346 static void fusereaddir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
348 struct vcfsdata *fsd;
353 ssize_t sz, osz, bsz;
356 fsd = fuse_req_userdata(req);
357 if((inoc = getinocbf(fsd, ino)) == NULL) {
358 fuse_reply_err(req, ENOENT);
361 if(getinode(fsd, inoc->inotab, inoc->inode, &file)) {
362 fuse_reply_err(req, errno);
368 memset(&dent, 0, sizeof(dent));
369 if((sz = btget(fsd->st, &file.data, off++, &dent, sizeof(dent), DIRBLSIZE)) < 0) {
370 if(errno == ERANGE) {
373 fuse_reply_buf(req, NULL, 0);
376 fuse_reply_err(req, errno);
384 bsz += fuse_add_direntry(req, NULL, 0, dent.name, NULL, 0);
387 buf = realloc(buf, bsz);
388 memset(&sb, 0, sizeof(sb));
389 sb.st_ino = cacheinode(fsd, dent.inode, inoc->inotab);
390 fuse_add_direntry(req, buf + osz, bsz - osz, dent.name, &sb, off);
392 fuse_reply_buf(req, buf, bsz);
397 static vc_rev_t commit(struct vcfsdata *fsd, struct btnode inotab)
403 if(writeall(fsd->revfd, &rr, sizeof(rr), (fsd->currev + 1) * sizeof(struct revrec))) {
404 flog(LOG_CRIT, "could not write new revision: %s", strerror(errno));
407 fsd->inotab = inotab;
408 return(++fsd->currev);
411 static int deldentry(struct vcfsdata *fsd, struct inode *ino, int di)
417 if((di < 0) || (di >= ino->size)) {
421 if(di == ino->size - 1) {
422 if(btput(fsd->st, &ino->data, ino->size - 1, NULL, 0, DIRBLSIZE))
425 if((sz = btget(fsd->st, &ino->data, ino->size - 1, &dent, sizeof(dent), DIRBLSIZE)) < 0)
427 btmkop(ops + 0, di, &dent, sz);
428 btmkop(ops + 1, ino->size - 1, NULL, 0);
429 if(btputmany(fsd->st, &ino->data, ops, 2, DIRBLSIZE))
436 static int setdentry(struct vcfsdata *fsd, struct inode *ino, int di, const char *name, vc_ino_t target)
441 if(strlen(name) > 255) {
442 errno = ENAMETOOLONG;
445 memset(&dent, 0, sizeof(dent));
446 strcpy(dent.name, name);
448 sz = sizeof(dent) - sizeof(dent.name) + strlen(name) + 1;
449 if((di == -1) || (di == ino->size)) {
450 if(btput(fsd->st, &ino->data, ino->size, &dent, sz, DIRBLSIZE))
455 return(btput(fsd->st, &ino->data, di, &dent, sz, DIRBLSIZE));
458 static void fusemkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
460 struct vcfsdata *fsd;
462 struct inode file, new;
463 struct btnode inotab;
464 struct fuse_entry_param e;
465 const struct fuse_ctx *ctx;
468 fsd = fuse_req_userdata(req);
469 ctx = fuse_req_ctx(req);
470 if((inoc = getinocbf(fsd, parent)) == NULL) {
471 fuse_reply_err(req, ENOENT);
474 if(inoc->inotab.d != 0) {
475 fuse_reply_err(req, EROFS);
478 if(getinode(fsd, inoc->inotab, inoc->inode, &file)) {
479 fuse_reply_err(req, errno);
482 if(!S_ISDIR(file.mode)) {
483 fuse_reply_err(req, ENOTDIR);
486 if(dirlookup(fsd, &file.data, name, NULL) != -1) {
487 fuse_reply_err(req, EEXIST);
491 memset(&new, 0, sizeof(new));
492 new.mode = S_IFDIR | mode;
493 new.mtime = new.ctime = time(NULL);
498 if(setdentry(fsd, &new, -1, ".", fsd->nextino) || setdentry(fsd, &new, -1, "..", inoc->inode)) {
499 fuse_reply_err(req, errno);
503 inotab = fsd->inotab;
504 if(setdentry(fsd, &file, -1, name, fsd->nextino)) {
505 fuse_reply_err(req, errno);
509 btmkop(ops + 0, inoc->inode, &file, sizeof(file));
510 btmkop(ops + 1, fsd->nextino, &new, sizeof(new));
511 if(btputmany(fsd->st, &inotab, ops, 2, INOBLSIZE)) {
512 fuse_reply_err(req, errno);
516 if(btput(fsd->st, &inotab, fsd->nextino, &new, sizeof(new))) {
517 fuse_reply_err(req, errno);
520 if(btput(fsd->st, &inotab, inoc->inode, &file, sizeof(file))) {
521 fuse_reply_err(req, errno);
527 memset(&e, 0, sizeof(e));
528 e.ino = cacheinode(fsd, fsd->nextino++, nilnode);
529 fillstat(&e.attr, &new);
530 e.attr.st_ino = e.ino;
531 fuse_reply_entry(req, &e);
534 static void fuseunlink(fuse_req_t req, fuse_ino_t parent, const char *name)
536 struct vcfsdata *fsd;
540 struct btnode inotab;
542 fsd = fuse_req_userdata(req);
543 if((inoc = getinocbf(fsd, parent)) == NULL) {
544 fuse_reply_err(req, ENOENT);
547 if(inoc->inotab.d != 0) {
548 fuse_reply_err(req, EROFS);
551 if(getinode(fsd, inoc->inotab, inoc->inode, &file)) {
552 fuse_reply_err(req, errno);
555 if(!S_ISDIR(file.mode)) {
556 fuse_reply_err(req, ENOTDIR);
559 if(dirlookup(fsd, &file.data, name, &di) == -1) {
560 fuse_reply_err(req, ENOENT);
563 inotab = fsd->inotab;
564 if(deldentry(fsd, &file, di)) {
565 fuse_reply_err(req, errno);
568 if(btput(fsd->st, &inotab, inoc->inode, &file, sizeof(file), INOBLSIZE)) {
569 fuse_reply_err(req, errno);
573 fuse_reply_err(req, 0);
576 static struct fuse_lowlevel_ops fuseops = {
577 .destroy = (void (*)(void *))fusedestroy,
578 .lookup = fuselookup,
579 .getattr = fusegetattr,
580 .readdir = fusereaddir,
583 .unlink = fuseunlink,
586 int main(int argc, char **argv)
588 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
589 struct fuse_session *fs;
590 struct fuse_chan *ch;
591 struct vcfsdata *fsd;
595 if((fsd = initvcfs(".")) == NULL)
597 if(fuse_parse_cmdline(&args, &mtpt, NULL, NULL) < 0)
599 if((fd = fuse_mount(mtpt, &args)) < 0)
601 if((fs = fuse_lowlevel_new(&args, &fuseops, sizeof(fuseops), fsd)) == NULL) {
602 fuse_unmount(mtpt, fd);
604 fprintf(stderr, "vcfs: could not initialize fuse\n");
607 fuse_set_signal_handlers(fs);
608 if((ch = fuse_kern_chan_new(fd)) == NULL) {
609 fuse_remove_signal_handlers(fs);
610 fuse_unmount(mtpt, fd);
611 fuse_session_destroy(fs);
616 fuse_session_add_chan(fs, ch);
617 err = fuse_session_loop(fs);
619 fuse_remove_signal_handlers(fs);
620 fuse_unmount(mtpt, fd);
621 fuse_session_destroy(fs);