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);
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), DIRBLSIZE)) < 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, DIRBLSIZE))
423 if((sz = btget(fsd->st, &ino->data, ino->size - 1, &dent, sizeof(dent), DIRBLSIZE)) < 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, DIRBLSIZE))
434 static int setdentry(struct vcfsdata *fsd, struct inode *ino, int di, const char *name, vc_ino_t target)
439 if(strlen(name) > 255) {
440 errno = ENAMETOOLONG;
443 memset(&dent, 0, sizeof(dent));
444 strcpy(dent.name, name);
446 sz = sizeof(dent) - sizeof(dent.name) + strlen(name) + 1;
447 if((di == -1) || (di == ino->size)) {
448 if(btput(fsd->st, &ino->data, ino->size, &dent, sz, DIRBLSIZE))
453 return(btput(fsd->st, &ino->data, di, &dent, sz, DIRBLSIZE));
456 static void fusemkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
458 struct vcfsdata *fsd;
460 struct inode file, new;
461 struct btnode inotab;
462 struct fuse_entry_param e;
463 const struct fuse_ctx *ctx;
466 fsd = fuse_req_userdata(req);
467 ctx = fuse_req_ctx(req);
468 if((inoc = getinocbf(fsd, parent)) == NULL) {
469 fuse_reply_err(req, ENOENT);
472 if(inoc->inotab.d != 0) {
473 fuse_reply_err(req, EROFS);
476 if(getinode(fsd, inoc->inotab, inoc->inode, &file)) {
477 fuse_reply_err(req, errno);
480 if(!S_ISDIR(file.mode)) {
481 fuse_reply_err(req, ENOTDIR);
484 if(dirlookup(fsd, &file.data, name, NULL) != -1) {
485 fuse_reply_err(req, EEXIST);
489 memset(&new, 0, sizeof(new));
490 new.mode = S_IFDIR | mode;
491 new.mtime = new.ctime = time(NULL);
496 if(setdentry(fsd, &new, -1, ".", fsd->nextino) || setdentry(fsd, &new, -1, "..", inoc->inode)) {
497 fuse_reply_err(req, errno);
501 inotab = fsd->inotab;
502 if(setdentry(fsd, &file, -1, name, fsd->nextino)) {
503 fuse_reply_err(req, errno);
507 btmkop(ops + 0, inoc->inode, &file, sizeof(file));
508 btmkop(ops + 1, fsd->nextino, &new, sizeof(new));
509 if(btputmany(fsd->st, &inotab, ops, 2, INOBLSIZE)) {
510 fuse_reply_err(req, errno);
514 if(btput(fsd->st, &inotab, fsd->nextino, &new, sizeof(new))) {
515 fuse_reply_err(req, errno);
518 if(btput(fsd->st, &inotab, inoc->inode, &file, sizeof(file))) {
519 fuse_reply_err(req, errno);
525 memset(&e, 0, sizeof(e));
526 e.ino = cacheinode(fsd, fsd->nextino++, nilnode);
527 fillstat(&e.attr, &new);
528 fuse_reply_entry(req, &e);
531 static void fuseunlink(fuse_req_t req, fuse_ino_t parent, const char *name)
533 struct vcfsdata *fsd;
537 struct btnode inotab;
539 fsd = fuse_req_userdata(req);
540 if((inoc = getinocbf(fsd, parent)) == NULL) {
541 fuse_reply_err(req, ENOENT);
544 if(inoc->inotab.d != 0) {
545 fuse_reply_err(req, EROFS);
548 if(getinode(fsd, inoc->inotab, inoc->inode, &file)) {
549 fuse_reply_err(req, errno);
552 if(!S_ISDIR(file.mode)) {
553 fuse_reply_err(req, ENOTDIR);
556 if(dirlookup(fsd, &file.data, name, &di) == -1) {
557 fuse_reply_err(req, ENOENT);
560 inotab = fsd->inotab;
561 if(deldentry(fsd, &file, di)) {
562 fuse_reply_err(req, errno);
565 if(btput(fsd->st, &inotab, inoc->inode, &file, sizeof(file), INOBLSIZE)) {
566 fuse_reply_err(req, errno);
570 fuse_reply_err(req, 0);
573 static struct fuse_lowlevel_ops fuseops = {
574 .destroy = (void (*)(void *))fusedestroy,
575 .lookup = fuselookup,
576 .getattr = fusegetattr,
577 .readdir = fusereaddir,
580 .unlink = fuseunlink,
583 int main(int argc, char **argv)
585 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
586 struct fuse_session *fs;
587 struct fuse_chan *ch;
588 struct vcfsdata *fsd;
592 if((fsd = initvcfs(".")) == NULL)
594 if(fuse_parse_cmdline(&args, &mtpt, NULL, NULL) < 0)
596 if((fd = fuse_mount(mtpt, &args)) < 0)
598 if((fs = fuse_lowlevel_new(&args, &fuseops, sizeof(fuseops), fsd)) == NULL) {
599 fuse_unmount(mtpt, fd);
601 fprintf(stderr, "vcfs: could not initialize fuse\n");
604 fuse_set_signal_handlers(fs);
605 if((ch = fuse_kern_chan_new(fd)) == NULL) {
606 fuse_remove_signal_handlers(fs);
607 fuse_unmount(mtpt, fd);
608 fuse_session_destroy(fs);
613 fuse_session_add_chan(fs, ch);
614 err = fuse_session_loop(fs);
616 fuse_remove_signal_handlers(fs);
617 fuse_unmount(mtpt, fd);
618 fuse_session_destroy(fs);