| 1 | #define _LARGEFILE64_SOURCE |
| 2 | #include <stdlib.h> |
| 3 | #include <stdio.h> |
| 4 | #include <unistd.h> |
| 5 | #include <fcntl.h> |
| 6 | #include <string.h> |
| 7 | #include <errno.h> |
| 8 | #include <assert.h> |
| 9 | #include <fuse_lowlevel.h> |
| 10 | |
| 11 | #include "utils.h" |
| 12 | #include "log.h" |
| 13 | #include "store.h" |
| 14 | #include "blocktree.h" |
| 15 | #include "vcfs.h" |
| 16 | |
| 17 | /* XXX: The current i-numbering scheme sucks. */ |
| 18 | |
| 19 | struct btree { |
| 20 | struct btree *l, *r; |
| 21 | int h; |
| 22 | void *d; |
| 23 | }; |
| 24 | |
| 25 | struct inoc { |
| 26 | vc_ino_t inode; |
| 27 | struct btnode inotab; |
| 28 | fuse_ino_t cnum; |
| 29 | }; |
| 30 | |
| 31 | struct vcfsdata { |
| 32 | struct store *st; |
| 33 | int revfd; |
| 34 | vc_rev_t currev; |
| 35 | vc_ino_t nextino; |
| 36 | struct btnode inotab; |
| 37 | struct btree *inocbf, *inocbv; |
| 38 | fuse_ino_t inocser; |
| 39 | }; |
| 40 | |
| 41 | #define max(a, b) (((b) > (a))?(b):(a)) |
| 42 | static struct btnode nilnode = {0, }; |
| 43 | |
| 44 | static int btheight(struct btree *tree) |
| 45 | { |
| 46 | if(tree == NULL) |
| 47 | return(0); |
| 48 | return(tree->h); |
| 49 | } |
| 50 | |
| 51 | static void btsetheight(struct btree *tree) |
| 52 | { |
| 53 | if(tree == NULL) |
| 54 | return; |
| 55 | tree->h = max(btheight(tree->l), btheight(tree->r)) + 1; |
| 56 | } |
| 57 | |
| 58 | static void bbtrl(struct btree **tree); |
| 59 | |
| 60 | static void bbtrr(struct btree **tree) |
| 61 | { |
| 62 | struct btree *m, *l, *r; |
| 63 | |
| 64 | if(btheight((*tree)->l->r) > btheight((*tree)->l->l)) |
| 65 | bbtrl(&(*tree)->l); |
| 66 | r = (*tree); |
| 67 | l = r->l; |
| 68 | m = l->r; |
| 69 | r->l = m; |
| 70 | btsetheight(r); |
| 71 | l->r = r; |
| 72 | btsetheight(l); |
| 73 | *tree = l; |
| 74 | } |
| 75 | |
| 76 | static void bbtrl(struct btree **tree) |
| 77 | { |
| 78 | struct btree *m, *l, *r; |
| 79 | |
| 80 | if(btheight((*tree)->r->l) > btheight((*tree)->r->r)) |
| 81 | bbtrr(&(*tree)->r); |
| 82 | l = (*tree); |
| 83 | r = l->r; |
| 84 | m = r->l; |
| 85 | l->r = m; |
| 86 | btsetheight(l); |
| 87 | r->l = l; |
| 88 | btsetheight(r); |
| 89 | *tree = r; |
| 90 | } |
| 91 | |
| 92 | static int bbtreeput(struct btree **tree, void *item, int (*cmp)(void *, void *)) |
| 93 | { |
| 94 | int c, r; |
| 95 | |
| 96 | if(*tree == NULL) { |
| 97 | *tree = calloc(1, sizeof(**tree)); |
| 98 | (*tree)->d = item; |
| 99 | (*tree)->h = 1; |
| 100 | return(1); |
| 101 | } |
| 102 | if((c = cmp(item, (*tree)->d)) < 0) |
| 103 | r = bbtreeput(&(*tree)->l, item, cmp); |
| 104 | else if(c > 0) |
| 105 | r = bbtreeput(&(*tree)->r, item, cmp); |
| 106 | else |
| 107 | return(0); |
| 108 | btsetheight(*tree); |
| 109 | if(btheight((*tree)->l) > btheight((*tree)->r) + 1) |
| 110 | bbtrr(tree); |
| 111 | if(btheight((*tree)->r) > btheight((*tree)->l) + 1) |
| 112 | bbtrl(tree); |
| 113 | return(r); |
| 114 | } |
| 115 | |
| 116 | static void *btreeget(struct btree *tree, void *key, int (*cmp)(void *, void *)) |
| 117 | { |
| 118 | int c; |
| 119 | |
| 120 | while(1) { |
| 121 | if(tree == NULL) |
| 122 | return(NULL); |
| 123 | c = cmp(key, tree->d); |
| 124 | if(c < 0) |
| 125 | tree = tree->l; |
| 126 | else if(c > 0) |
| 127 | tree = tree->r; |
| 128 | else |
| 129 | return(tree->d); |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | static void dstrvcfs(struct vcfsdata *fsd) |
| 134 | { |
| 135 | releasestore(fsd->st); |
| 136 | fsync(fsd->revfd); |
| 137 | close(fsd->revfd); |
| 138 | free(fsd); |
| 139 | } |
| 140 | |
| 141 | static int inoccmpbf(struct inoc *a, struct inoc *b) |
| 142 | { |
| 143 | return(a->cnum - b->cnum); |
| 144 | } |
| 145 | |
| 146 | static int inoccmpbv(struct inoc *a, struct inoc *b) |
| 147 | { |
| 148 | if(a->inode < b->inode) |
| 149 | return(-1); |
| 150 | if(a->inode > b->inode) |
| 151 | return(1); |
| 152 | if(a->inotab.d < b->inotab.d) |
| 153 | return(-1); |
| 154 | if(a->inotab.d > b->inotab.d) |
| 155 | return(1); |
| 156 | return(addrcmp(&a->inotab.a, &b->inotab.a)); |
| 157 | } |
| 158 | |
| 159 | static struct inoc *getinocbf(struct vcfsdata *fsd, fuse_ino_t inode) |
| 160 | { |
| 161 | struct inoc key; |
| 162 | |
| 163 | key.cnum = inode; |
| 164 | return(btreeget(fsd->inocbf, &key, (int (*)(void *, void *))inoccmpbf)); |
| 165 | } |
| 166 | |
| 167 | static struct inoc *getinocbv(struct vcfsdata *fsd, vc_ino_t inode, struct btnode inotab) |
| 168 | { |
| 169 | struct inoc key; |
| 170 | |
| 171 | key.inotab = inotab; |
| 172 | key.inode = inode; |
| 173 | return(btreeget(fsd->inocbv, &key, (int (*)(void *, void *))inoccmpbv)); |
| 174 | } |
| 175 | |
| 176 | static fuse_ino_t cacheinode(struct vcfsdata *fsd, vc_ino_t inode, struct btnode inotab) |
| 177 | { |
| 178 | fuse_ino_t ret; |
| 179 | struct inoc *inoc; |
| 180 | |
| 181 | if((inoc = getinocbv(fsd, inode, inotab)) != NULL) |
| 182 | return(inoc->cnum); |
| 183 | ret = fsd->inocser++; |
| 184 | inoc = calloc(1, sizeof(*inoc)); |
| 185 | inoc->inode = inode; |
| 186 | inoc->inotab = inotab; |
| 187 | inoc->cnum = ret; |
| 188 | bbtreeput(&fsd->inocbf, inoc, (int (*)(void *, void *))inoccmpbf); |
| 189 | bbtreeput(&fsd->inocbv, inoc, (int (*)(void *, void *))inoccmpbv); |
| 190 | return(ret); |
| 191 | } |
| 192 | |
| 193 | static struct vcfsdata *initvcfs(char *dir) |
| 194 | { |
| 195 | struct vcfsdata *fsd; |
| 196 | char tbuf[1024]; |
| 197 | struct stat64 sb; |
| 198 | struct revrec cr; |
| 199 | |
| 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)); |
| 204 | free(fsd); |
| 205 | return(NULL); |
| 206 | } |
| 207 | if(fstat64(fsd->revfd, &sb)) { |
| 208 | flog(LOG_ERR, "could not stat revision database: %s", strerror(errno)); |
| 209 | close(fsd->revfd); |
| 210 | free(fsd); |
| 211 | return(NULL); |
| 212 | } |
| 213 | if(sb.st_size % sizeof(struct revrec) != 0) { |
| 214 | flog(LOG_ERR, "revision database has illegal size"); |
| 215 | close(fsd->revfd); |
| 216 | free(fsd); |
| 217 | return(NULL); |
| 218 | } |
| 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) { |
| 223 | close(fsd->revfd); |
| 224 | free(fsd); |
| 225 | return(NULL); |
| 226 | } |
| 227 | fsd->inocser = 1; |
| 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"); |
| 231 | close(fsd->revfd); |
| 232 | releasestore(fsd->st); |
| 233 | free(fsd); |
| 234 | return(NULL); |
| 235 | } |
| 236 | return(fsd); |
| 237 | } |
| 238 | |
| 239 | static vc_ino_t dirlookup(struct vcfsdata *fsd, struct btnode *dirdata, const char *name, int *di) |
| 240 | { |
| 241 | struct dentry dent; |
| 242 | int i; |
| 243 | ssize_t sz; |
| 244 | |
| 245 | for(i = 0; ; i++) { |
| 246 | if((sz = btget(fsd->st, dirdata, i, &dent, sizeof(dent), DIRBLSIZE)) < 0) { |
| 247 | if(errno == ERANGE) |
| 248 | errno = ENOENT; |
| 249 | return(-1); |
| 250 | } |
| 251 | if((dent.inode >= 0) && !strncmp(dent.name, name, sizeof(dent.name))) { |
| 252 | if(di != NULL) |
| 253 | *di = i; |
| 254 | return(dent.inode); |
| 255 | } |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | static void fusedestroy(struct vcfsdata *fsd) |
| 260 | { |
| 261 | dstrvcfs(fsd); |
| 262 | } |
| 263 | |
| 264 | static void fillstat(struct stat *sb, struct inode *file) |
| 265 | { |
| 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; |
| 274 | } |
| 275 | |
| 276 | static int getinode(struct vcfsdata *fsd, struct btnode inotab, vc_ino_t ino, struct inode *buf) |
| 277 | { |
| 278 | ssize_t sz; |
| 279 | |
| 280 | if(inotab.d == 0) |
| 281 | inotab = fsd->inotab; |
| 282 | if((sz = btget(fsd->st, &inotab, ino, buf, sizeof(*buf), INOBLSIZE)) < 0) |
| 283 | return(-1); |
| 284 | if(sz != sizeof(*buf)) { |
| 285 | flog(LOG_ERR, "illegal size for inode %i", ino); |
| 286 | errno = EIO; |
| 287 | return(-1); |
| 288 | } |
| 289 | return(0); |
| 290 | } |
| 291 | |
| 292 | static void fusegetattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) |
| 293 | { |
| 294 | struct vcfsdata *fsd; |
| 295 | struct stat sb; |
| 296 | struct inoc *inoc; |
| 297 | struct inode file; |
| 298 | |
| 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); |
| 303 | return; |
| 304 | } |
| 305 | if(getinode(fsd, inoc->inotab, inoc->inode, &file)) { |
| 306 | fuse_reply_err(req, errno); |
| 307 | return; |
| 308 | } |
| 309 | fillstat(&sb, &file); |
| 310 | sb.st_ino = ino; |
| 311 | fuse_reply_attr(req, &sb, 0); |
| 312 | } |
| 313 | |
| 314 | static void fuselookup(fuse_req_t req, fuse_ino_t parent, const char *name) |
| 315 | { |
| 316 | struct vcfsdata *fsd; |
| 317 | struct inode file; |
| 318 | struct inoc *inoc; |
| 319 | struct fuse_entry_param e; |
| 320 | vc_ino_t target; |
| 321 | |
| 322 | fsd = fuse_req_userdata(req); |
| 323 | if((inoc = getinocbf(fsd, parent)) == NULL) { |
| 324 | fuse_reply_err(req, ENOENT); |
| 325 | return; |
| 326 | } |
| 327 | if(getinode(fsd, inoc->inotab, inoc->inode, &file)) { |
| 328 | fuse_reply_err(req, errno); |
| 329 | return; |
| 330 | } |
| 331 | if((target = dirlookup(fsd, &file.data, name, NULL)) < 0) { |
| 332 | fuse_reply_err(req, errno); |
| 333 | return; |
| 334 | } |
| 335 | if(getinode(fsd, inoc->inotab, target, &file)) { |
| 336 | fuse_reply_err(req, errno); |
| 337 | return; |
| 338 | } |
| 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); |
| 344 | } |
| 345 | |
| 346 | static void fusereaddir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) |
| 347 | { |
| 348 | struct vcfsdata *fsd; |
| 349 | struct inoc *inoc; |
| 350 | struct inode file; |
| 351 | struct dentry dent; |
| 352 | struct stat sb; |
| 353 | ssize_t sz, osz, bsz; |
| 354 | char *buf; |
| 355 | |
| 356 | fsd = fuse_req_userdata(req); |
| 357 | if((inoc = getinocbf(fsd, ino)) == NULL) { |
| 358 | fuse_reply_err(req, ENOENT); |
| 359 | return; |
| 360 | } |
| 361 | if(getinode(fsd, inoc->inotab, inoc->inode, &file)) { |
| 362 | fuse_reply_err(req, errno); |
| 363 | return; |
| 364 | } |
| 365 | bsz = 0; |
| 366 | buf = NULL; |
| 367 | while(bsz < size) { |
| 368 | memset(&dent, 0, sizeof(dent)); |
| 369 | if((sz = btget(fsd->st, &file.data, off++, &dent, sizeof(dent), DIRBLSIZE)) < 0) { |
| 370 | if(errno == ERANGE) { |
| 371 | if(buf != NULL) |
| 372 | break; |
| 373 | fuse_reply_buf(req, NULL, 0); |
| 374 | return; |
| 375 | } |
| 376 | fuse_reply_err(req, errno); |
| 377 | if(buf != NULL) |
| 378 | free(buf); |
| 379 | return; |
| 380 | } |
| 381 | if(dent.inode < 0) |
| 382 | continue; |
| 383 | osz = bsz; |
| 384 | bsz += fuse_add_direntry(req, NULL, 0, dent.name, NULL, 0); |
| 385 | if(bsz > size) |
| 386 | break; |
| 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); |
| 391 | } |
| 392 | fuse_reply_buf(req, buf, bsz); |
| 393 | if(buf != NULL) |
| 394 | free(buf); |
| 395 | } |
| 396 | |
| 397 | static vc_rev_t commit(struct vcfsdata *fsd, struct btnode inotab) |
| 398 | { |
| 399 | struct revrec rr; |
| 400 | |
| 401 | rr.ct = time(NULL); |
| 402 | rr.root = 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)); |
| 405 | return(-1); |
| 406 | } |
| 407 | fsd->inotab = inotab; |
| 408 | return(++fsd->currev); |
| 409 | } |
| 410 | |
| 411 | static int deldentry(struct vcfsdata *fsd, struct inode *ino, int di) |
| 412 | { |
| 413 | struct btop ops[2]; |
| 414 | struct dentry dent; |
| 415 | ssize_t sz; |
| 416 | |
| 417 | if((di < 0) || (di >= ino->size)) { |
| 418 | errno = ERANGE; |
| 419 | return(-1); |
| 420 | } |
| 421 | if(di == ino->size - 1) { |
| 422 | if(btput(fsd->st, &ino->data, ino->size - 1, NULL, 0, DIRBLSIZE)) |
| 423 | return(-1); |
| 424 | } else { |
| 425 | if((sz = btget(fsd->st, &ino->data, ino->size - 1, &dent, sizeof(dent), DIRBLSIZE)) < 0) |
| 426 | return(-1); |
| 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)) |
| 430 | return(-1); |
| 431 | } |
| 432 | ino->size--; |
| 433 | return(0); |
| 434 | } |
| 435 | |
| 436 | static int setdentry(struct vcfsdata *fsd, struct inode *ino, int di, const char *name, vc_ino_t target) |
| 437 | { |
| 438 | struct dentry dent; |
| 439 | ssize_t sz; |
| 440 | |
| 441 | if(strlen(name) > 255) { |
| 442 | errno = ENAMETOOLONG; |
| 443 | return(-1); |
| 444 | } |
| 445 | memset(&dent, 0, sizeof(dent)); |
| 446 | strcpy(dent.name, name); |
| 447 | dent.inode = target; |
| 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)) |
| 451 | return(-1); |
| 452 | ino->size++; |
| 453 | return(0); |
| 454 | } |
| 455 | return(btput(fsd->st, &ino->data, di, &dent, sz, DIRBLSIZE)); |
| 456 | } |
| 457 | |
| 458 | static void fusemkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode) |
| 459 | { |
| 460 | struct vcfsdata *fsd; |
| 461 | struct inoc *inoc; |
| 462 | struct inode file, new; |
| 463 | struct btnode inotab; |
| 464 | struct fuse_entry_param e; |
| 465 | const struct fuse_ctx *ctx; |
| 466 | struct btop ops[2]; |
| 467 | |
| 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); |
| 472 | return; |
| 473 | } |
| 474 | if(inoc->inotab.d != 0) { |
| 475 | fuse_reply_err(req, EROFS); |
| 476 | return; |
| 477 | } |
| 478 | if(getinode(fsd, inoc->inotab, inoc->inode, &file)) { |
| 479 | fuse_reply_err(req, errno); |
| 480 | return; |
| 481 | } |
| 482 | if(!S_ISDIR(file.mode)) { |
| 483 | fuse_reply_err(req, ENOTDIR); |
| 484 | return; |
| 485 | } |
| 486 | if(dirlookup(fsd, &file.data, name, NULL) != -1) { |
| 487 | fuse_reply_err(req, EEXIST); |
| 488 | return; |
| 489 | } |
| 490 | |
| 491 | memset(&new, 0, sizeof(new)); |
| 492 | new.mode = S_IFDIR | mode; |
| 493 | new.mtime = new.ctime = time(NULL); |
| 494 | new.size = 0; |
| 495 | new.uid = ctx->uid; |
| 496 | new.gid = ctx->gid; |
| 497 | new.links = 2; |
| 498 | if(setdentry(fsd, &new, -1, ".", fsd->nextino) || setdentry(fsd, &new, -1, "..", inoc->inode)) { |
| 499 | fuse_reply_err(req, errno); |
| 500 | return; |
| 501 | } |
| 502 | |
| 503 | inotab = fsd->inotab; |
| 504 | if(setdentry(fsd, &file, -1, name, fsd->nextino)) { |
| 505 | fuse_reply_err(req, errno); |
| 506 | return; |
| 507 | } |
| 508 | file.links++; |
| 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); |
| 513 | return; |
| 514 | } |
| 515 | /* |
| 516 | if(btput(fsd->st, &inotab, fsd->nextino, &new, sizeof(new))) { |
| 517 | fuse_reply_err(req, errno); |
| 518 | return; |
| 519 | } |
| 520 | if(btput(fsd->st, &inotab, inoc->inode, &file, sizeof(file))) { |
| 521 | fuse_reply_err(req, errno); |
| 522 | return; |
| 523 | } |
| 524 | */ |
| 525 | commit(fsd, inotab); |
| 526 | |
| 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); |
| 532 | } |
| 533 | |
| 534 | static void fuseunlink(fuse_req_t req, fuse_ino_t parent, const char *name) |
| 535 | { |
| 536 | struct vcfsdata *fsd; |
| 537 | struct inoc *inoc; |
| 538 | struct inode file; |
| 539 | int di; |
| 540 | struct btnode inotab; |
| 541 | |
| 542 | fsd = fuse_req_userdata(req); |
| 543 | if((inoc = getinocbf(fsd, parent)) == NULL) { |
| 544 | fuse_reply_err(req, ENOENT); |
| 545 | return; |
| 546 | } |
| 547 | if(inoc->inotab.d != 0) { |
| 548 | fuse_reply_err(req, EROFS); |
| 549 | return; |
| 550 | } |
| 551 | if(getinode(fsd, inoc->inotab, inoc->inode, &file)) { |
| 552 | fuse_reply_err(req, errno); |
| 553 | return; |
| 554 | } |
| 555 | if(!S_ISDIR(file.mode)) { |
| 556 | fuse_reply_err(req, ENOTDIR); |
| 557 | return; |
| 558 | } |
| 559 | if(dirlookup(fsd, &file.data, name, &di) == -1) { |
| 560 | fuse_reply_err(req, ENOENT); |
| 561 | return; |
| 562 | } |
| 563 | inotab = fsd->inotab; |
| 564 | if(deldentry(fsd, &file, di)) { |
| 565 | fuse_reply_err(req, errno); |
| 566 | return; |
| 567 | } |
| 568 | if(btput(fsd->st, &inotab, inoc->inode, &file, sizeof(file), INOBLSIZE)) { |
| 569 | fuse_reply_err(req, errno); |
| 570 | return; |
| 571 | } |
| 572 | commit(fsd, inotab); |
| 573 | fuse_reply_err(req, 0); |
| 574 | } |
| 575 | |
| 576 | static struct fuse_lowlevel_ops fuseops = { |
| 577 | .destroy = (void (*)(void *))fusedestroy, |
| 578 | .lookup = fuselookup, |
| 579 | .getattr = fusegetattr, |
| 580 | .readdir = fusereaddir, |
| 581 | .mkdir = fusemkdir, |
| 582 | .rmdir = fuseunlink, |
| 583 | .unlink = fuseunlink, |
| 584 | }; |
| 585 | |
| 586 | int main(int argc, char **argv) |
| 587 | { |
| 588 | struct fuse_args args = FUSE_ARGS_INIT(argc, argv); |
| 589 | struct fuse_session *fs; |
| 590 | struct fuse_chan *ch; |
| 591 | struct vcfsdata *fsd; |
| 592 | char *mtpt; |
| 593 | int err, fd; |
| 594 | |
| 595 | if((fsd = initvcfs(".")) == NULL) |
| 596 | exit(1); |
| 597 | if(fuse_parse_cmdline(&args, &mtpt, NULL, NULL) < 0) |
| 598 | exit(1); |
| 599 | if((fd = fuse_mount(mtpt, &args)) < 0) |
| 600 | exit(1); |
| 601 | if((fs = fuse_lowlevel_new(&args, &fuseops, sizeof(fuseops), fsd)) == NULL) { |
| 602 | fuse_unmount(mtpt, fd); |
| 603 | close(fd); |
| 604 | fprintf(stderr, "vcfs: could not initialize fuse\n"); |
| 605 | exit(1); |
| 606 | } |
| 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); |
| 612 | close(fd); |
| 613 | exit(1); |
| 614 | } |
| 615 | |
| 616 | fuse_session_add_chan(fs, ch); |
| 617 | err = fuse_session_loop(fs); |
| 618 | |
| 619 | fuse_remove_signal_handlers(fs); |
| 620 | fuse_unmount(mtpt, fd); |
| 621 | fuse_session_destroy(fs); |
| 622 | close(fd); |
| 623 | return(err?1:0); |
| 624 | } |