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)) < 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))) < 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))) < 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);
310 fuse_reply_attr(req, &sb, 0);
313 static void fuselookup(fuse_req_t req, fuse_ino_t parent, const char *name)
315 struct vcfsdata *fsd;
318 struct fuse_entry_param e;
321 fsd = fuse_req_userdata(req);
322 if((inoc = getinocbf(fsd, parent)) == NULL) {
323 fuse_reply_err(req, ENOENT);
326 if(getinode(fsd, inoc->inotab, inoc->inode, &file)) {
327 fuse_reply_err(req, errno);
330 if((target = dirlookup(fsd, &file.data, name, NULL)) < 0) {
331 fuse_reply_err(req, errno);
334 if(getinode(fsd, inoc->inotab, target, &file)) {
335 fuse_reply_err(req, errno);
338 memset(&e, 0, sizeof(e));
339 e.ino = cacheinode(fsd, target, inoc->inotab);
340 fillstat(&e.attr, &file);
341 fuse_reply_entry(req, &e);
344 static void fusereaddir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
346 struct vcfsdata *fsd;
351 ssize_t sz, osz, bsz;
354 fsd = fuse_req_userdata(req);
355 if((inoc = getinocbf(fsd, ino)) == NULL) {
356 fuse_reply_err(req, ENOENT);
359 if(getinode(fsd, inoc->inotab, inoc->inode, &file)) {
360 fuse_reply_err(req, errno);
366 memset(&dent, 0, sizeof(dent));
367 if((sz = btget(fsd->st, &file.data, off++, &dent, sizeof(dent))) < 0) {
368 if(errno == ERANGE) {
371 fuse_reply_buf(req, NULL, 0);
374 fuse_reply_err(req, errno);
382 bsz += fuse_add_direntry(req, NULL, 0, dent.name, NULL, 0);
385 buf = realloc(buf, bsz);
386 memset(&sb, 0, sizeof(sb));
387 sb.st_ino = cacheinode(fsd, dent.inode, inoc->inotab);
388 fuse_add_direntry(req, buf + osz, bsz - osz, dent.name, &sb, off);
390 fuse_reply_buf(req, buf, bsz);
395 static vc_rev_t commit(struct vcfsdata *fsd, struct btnode inotab)
401 if(writeall(fsd->revfd, &rr, sizeof(rr), (fsd->currev + 1) * sizeof(struct revrec))) {
402 flog(LOG_CRIT, "could not write new revision: %s", strerror(errno));
405 fsd->inotab = inotab;
406 return(++fsd->currev);
409 static int deldentry(struct vcfsdata *fsd, struct inode *ino, int di)
415 if((di < 0) || (di >= ino->size)) {
419 if(di == ino->size - 1) {
420 if(btput(fsd->st, &ino->data, ino->size - 1, NULL, 0))
423 if((sz = btget(fsd->st, &ino->data, ino->size - 1, &dent, sizeof(dent))) < 0)
425 btmkop(ops + 0, di, &dent, sz);
426 btmkop(ops + 1, ino->size - 1, NULL, 0);
427 if(btputmany(fsd->st, &ino->data, ops, 2))
433 static int setdentry(struct vcfsdata *fsd, struct inode *ino, int di, const char *name, vc_ino_t target)
438 if(strlen(name) > 255) {
439 errno = ENAMETOOLONG;
442 memset(&dent, 0, sizeof(dent));
443 strcpy(dent.name, name);
445 sz = sizeof(dent) - sizeof(dent.name) + strlen(name) + 1;
446 if((di == -1) || (di == ino->size)) {
447 if(btput(fsd->st, &ino->data, ino->size, &dent, sz))
452 return(btput(fsd->st, &ino->data, di, &dent, sz));
455 static void fusemkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
457 struct vcfsdata *fsd;
459 struct inode file, new;
460 struct btnode inotab;
461 struct fuse_entry_param e;
462 const struct fuse_ctx *ctx;
465 fsd = fuse_req_userdata(req);
466 ctx = fuse_req_ctx(req);
467 if((inoc = getinocbf(fsd, parent)) == NULL) {
468 fuse_reply_err(req, ENOENT);
471 if(inoc->inotab.d != 0) {
472 fuse_reply_err(req, EROFS);
475 if(getinode(fsd, inoc->inotab, inoc->inode, &file)) {
476 fuse_reply_err(req, errno);
479 if(!S_ISDIR(file.mode)) {
480 fuse_reply_err(req, ENOTDIR);
483 if(dirlookup(fsd, &file.data, name, NULL) != -1) {
484 fuse_reply_err(req, EEXIST);
488 memset(&new, 0, sizeof(new));
489 new.mode = S_IFDIR | mode;
490 new.mtime = new.ctime = time(NULL);
495 if(setdentry(fsd, &new, -1, ".", fsd->nextino) || setdentry(fsd, &new, -1, "..", inoc->inode)) {
496 fuse_reply_err(req, errno);
500 inotab = fsd->inotab;
501 if(setdentry(fsd, &file, -1, name, fsd->nextino)) {
502 fuse_reply_err(req, errno);
506 btmkop(ops + 0, inoc->inode, &file, sizeof(file));
507 btmkop(ops + 1, fsd->nextino, &new, sizeof(new));
508 if(btputmany(fsd->st, &inotab, ops, 2)) {
509 fuse_reply_err(req, errno);
513 if(btput(fsd->st, &inotab, fsd->nextino, &new, sizeof(new))) {
514 fuse_reply_err(req, errno);
517 if(btput(fsd->st, &inotab, inoc->inode, &file, sizeof(file))) {
518 fuse_reply_err(req, errno);
524 memset(&e, 0, sizeof(e));
525 e.ino = cacheinode(fsd, fsd->nextino++, nilnode);
526 fillstat(&e.attr, &new);
527 fuse_reply_entry(req, &e);
530 static void fuseunlink(fuse_req_t req, fuse_ino_t parent, const char *name)
532 struct vcfsdata *fsd;
536 struct btnode inotab;
538 fsd = fuse_req_userdata(req);
539 if((inoc = getinocbf(fsd, parent)) == NULL) {
540 fuse_reply_err(req, ENOENT);
543 if(inoc->inotab.d != 0) {
544 fuse_reply_err(req, EROFS);
547 if(getinode(fsd, inoc->inotab, inoc->inode, &file)) {
548 fuse_reply_err(req, errno);
551 if(!S_ISDIR(file.mode)) {
552 fuse_reply_err(req, ENOTDIR);
555 if(dirlookup(fsd, &file.data, name, &di) == -1) {
556 fuse_reply_err(req, ENOENT);
559 inotab = fsd->inotab;
560 if(deldentry(fsd, &file, di)) {
561 fuse_reply_err(req, errno);
564 if(btput(fsd->st, &inotab, inoc->inode, &file, sizeof(file))) {
565 fuse_reply_err(req, errno);
569 fuse_reply_err(req, 0);
572 static struct fuse_lowlevel_ops fuseops = {
573 .destroy = (void (*)(void *))fusedestroy,
574 .lookup = fuselookup,
575 .getattr = fusegetattr,
576 .readdir = fusereaddir,
579 .unlink = fuseunlink,
582 int main(int argc, char **argv)
584 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
585 struct fuse_session *fs;
586 struct fuse_chan *ch;
587 struct vcfsdata *fsd;
591 if((fsd = initvcfs(".")) == NULL)
593 if(fuse_parse_cmdline(&args, &mtpt, NULL, NULL) < 0)
595 if((fd = fuse_mount(mtpt, &args)) < 0)
597 if((fs = fuse_lowlevel_new(&args, &fuseops, sizeof(fuseops), fsd)) == NULL) {
598 fuse_unmount(mtpt, fd);
600 fprintf(stderr, "vcfs: could not initialize fuse\n");
603 fuse_set_signal_handlers(fs);
604 if((ch = fuse_kern_chan_new(fd)) == NULL) {
605 fuse_remove_signal_handlers(fs);
606 fuse_unmount(mtpt, fd);
607 fuse_session_destroy(fs);
612 fuse_session_add_chan(fs, ch);
613 err = fuse_session_loop(fs);
615 fuse_remove_signal_handlers(fs);
616 fuse_unmount(mtpt, fd);
617 fuse_session_destroy(fs);