refuse.c revision 1.36 1 /* $NetBSD: refuse.c,v 1.36 2007/02/26 13:52:16 pooka Exp $ */
2
3 /*
4 * Copyright 2007 Alistair Crooks. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote
15 * products derived from this software without specific prior written
16 * permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
24 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 #if !defined(lint)
33 __RCSID("$NetBSD: refuse.c,v 1.36 2007/02/26 13:52:16 pooka Exp $");
34 #endif /* !lint */
35
36 #include <assert.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <fuse.h>
40 #include <unistd.h>
41
42 #include "defs.h"
43
44 typedef uint64_t fuse_ino_t;
45
46 struct fuse_config {
47 uid_t uid;
48 gid_t gid;
49 mode_t umask;
50 double entry_timeout;
51 double negative_timeout;
52 double attr_timeout;
53 double ac_attr_timeout;
54 int ac_attr_timeout_set;
55 int debug;
56 int hard_remove;
57 int use_ino;
58 int readdir_ino;
59 int set_mode;
60 int set_uid;
61 int set_gid;
62 int direct_io;
63 int kernel_cache;
64 int auto_cache;
65 int intr;
66 int intr_signal;
67 };
68
69 /* this is the private fuse structure */
70 struct fuse {
71 struct fuse_session *se; /* fuse session pointer */
72 struct fuse_operations op; /* switch table of operations */
73 int compat; /* compat level -
74 * not used in puffs_fuse */
75 struct node **name_table;
76 size_t name_table_size;
77 struct node **id_table;
78 size_t id_table_size;
79 fuse_ino_t ctr;
80 unsigned int generation;
81 unsigned int hidectr;
82 pthread_mutex_t lock;
83 pthread_rwlock_t tree_lock;
84 void *user_data;
85 struct fuse_config conf;
86 int intr_installed;
87 struct puffs_usermount *pu;
88 };
89
90 struct puffs_fuse_dirh {
91 void *dbuf;
92 struct dirent *d;
93
94 size_t reslen;
95 size_t bufsize;
96 };
97
98 struct refusenode {
99 struct fuse_file_info file_info;
100 struct puffs_fuse_dirh dirh;
101 int opencount;
102 int flags;
103 };
104 #define RN_ROOT 0x01
105 #define RN_OPEN 0x02 /* XXX: could just use opencount */
106
107 static int fuse_setattr(struct fuse *, struct puffs_node *,
108 const char *, const struct vattr *);
109
110 static struct puffs_node *
111 newrn(struct puffs_usermount *pu)
112 {
113 struct puffs_node *pn;
114 struct refusenode *rn;
115
116 rn = malloc(sizeof(struct refusenode));
117 if (!rn)
118 abort(); /*XXX*/
119
120 memset(rn, 0, sizeof(struct refusenode));
121 pn = puffs_pn_new(pu, rn);
122
123 return pn;
124 }
125
126 static void
127 nukern(struct puffs_node *pn)
128 {
129 struct refusenode *rn = pn->pn_data;
130
131 free(rn->dirh.dbuf);
132 free(rn);
133 puffs_pn_put(pn);
134 }
135
136 static ino_t fakeino = 3;
137
138 /*
139 * XXX: do this otherwise if/when we grow thread support
140 *
141 * XXX2: does not consistently supply uid, gid or pid currently
142 */
143 static struct fuse_context fcon;
144
145 #define DIR_CHUNKSIZE 4096
146 static int
147 fill_dirbuf(struct puffs_fuse_dirh *dh, const char *name, ino_t dino,
148 uint8_t dtype)
149 {
150
151 /* initial? */
152 if (dh->bufsize == 0) {
153 dh->dbuf = malloc(DIR_CHUNKSIZE);
154 if (dh->dbuf == NULL)
155 err(1, "fill_dirbuf");
156 dh->d = dh->dbuf;
157 dh->reslen = dh->bufsize = DIR_CHUNKSIZE;
158 }
159
160 if (puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen))
161 return 0;
162
163 /* try to increase buffer space */
164 dh->dbuf = realloc(dh->dbuf, dh->bufsize + DIR_CHUNKSIZE);
165 if (dh->dbuf == NULL)
166 err(1, "fill_dirbuf realloc");
167 dh->d = (void *)((uint8_t *)dh->dbuf + (dh->bufsize - dh->reslen));
168 dh->reslen += DIR_CHUNKSIZE;
169 dh->bufsize += DIR_CHUNKSIZE;
170
171 return !puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen);
172 }
173
174 /* ARGSUSED3 */
175 /* XXX: I have no idea how "off" is supposed to be used */
176 static int
177 puffs_fuse_fill_dir(void *buf, const char *name,
178 const struct stat *stbuf, off_t off)
179 {
180 struct puffs_fuse_dirh *deh = buf;
181 ino_t dino;
182 uint8_t dtype;
183
184 if (stbuf == NULL) {
185 dtype = DT_UNKNOWN;
186 dino = fakeino++;
187 } else {
188 dtype = puffs_vtype2dt(puffs_mode2vt(stbuf->st_mode));
189 dino = stbuf->st_ino;
190 }
191
192 return fill_dirbuf(deh, name, dino, dtype);
193 }
194
195 static int
196 puffs_fuse_dirfil(fuse_dirh_t h, const char *name, int type, ino_t ino)
197 {
198 ino_t dino;
199 int dtype;
200
201 if (type == 0)
202 dtype = DT_UNKNOWN;
203 else
204 dtype = type;
205
206 if (ino)
207 dino = ino;
208 else
209 dino = fakeino++;
210
211 return fill_dirbuf(h, name, dino, dtype);
212 }
213
214 int
215 fuse_opt_add_arg(struct fuse_args *args, const char *arg)
216 {
217 char **oldargv;
218 int oldargc;
219
220 if (args->allocated) {
221 RENEW(char *, args->argv, args->argc + 1,
222 "fuse_opt_add_arg1", return 0);
223 } else {
224 oldargv = args->argv;
225 oldargc = args->argc;
226 NEWARRAY(char *, args->argv, oldargc + 1,
227 "fuse_opt_add_arg2", return 0);
228 (void) memcpy(args->argv, oldargv, oldargc * sizeof(char *));
229 args->allocated = 1;
230 }
231 args->argv[args->argc++] = strdup(arg);
232 return 1;
233 }
234
235 void
236 fuse_opt_free_args(struct fuse_args *args)
237 {
238 if (args && args->argv) {
239 int i;
240 for (i = 0; i < args->argc; i++)
241 FREE(args->argv[i]);
242 FREE(args->argv);
243 }
244 }
245
246 #define FUSE_ERR_UNLINK(fuse, file) if (fuse->op.unlink) fuse->op.unlink(file)
247 #define FUSE_ERR_RMDIR(fuse, dir) if (fuse->op.rmdir) fuse->op.rmdir(dir)
248
249 /* ARGSUSED1 */
250 static int
251 fuse_getattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
252 struct vattr *va)
253 {
254 struct stat st;
255 int ret;
256
257 if (fuse->op.getattr == NULL) {
258 return ENOSYS;
259 }
260
261 /* wrap up return code */
262 ret = (*fuse->op.getattr)(path, &st);
263
264 if (ret == 0) {
265 puffs_stat2vattr(va, &st);
266 }
267
268 return -ret;
269 }
270
271 static int
272 fuse_setattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
273 const struct vattr *va)
274 {
275 struct refusenode *rn = pn->pn_data;
276 mode_t mode;
277 uid_t uid;
278 gid_t gid;
279 int error, ret;
280
281 error = 0;
282
283 mode = va->va_mode;
284 uid = va->va_uid;
285 gid = va->va_gid;
286
287 if (mode != (mode_t)PUFFS_VNOVAL) {
288 ret = 0;
289
290 if (fuse->op.chmod == NULL) {
291 error = -ENOSYS;
292 } else {
293 ret = fuse->op.chmod(path, mode);
294 if (ret)
295 error = ret;
296 }
297 }
298 if (uid != (uid_t)PUFFS_VNOVAL || gid != (gid_t)PUFFS_VNOVAL) {
299 ret = 0;
300
301 if (fuse->op.chown == NULL) {
302 error = -ENOSYS;
303 } else {
304 ret = fuse->op.chown(path, uid, gid);
305 if (ret)
306 error = ret;
307 }
308 }
309 if (va->va_atime.tv_sec != (time_t)PUFFS_VNOVAL
310 || va->va_mtime.tv_sec != (long)PUFFS_VNOVAL) {
311 ret = 0;
312
313 if (fuse->op.utimens) {
314 struct timespec tv[2];
315
316 tv[0].tv_sec = va->va_atime.tv_sec;
317 tv[0].tv_nsec = va->va_atime.tv_nsec;
318 tv[1].tv_sec = va->va_mtime.tv_sec;
319 tv[1].tv_nsec = va->va_mtime.tv_nsec;
320
321 ret = fuse->op.utimens(path, tv);
322 } else if (fuse->op.utime) {
323 struct utimbuf timbuf;
324
325 timbuf.actime = va->va_atime.tv_sec;
326 timbuf.modtime = va->va_mtime.tv_sec;
327
328 ret = fuse->op.utime(path, &timbuf);
329 } else {
330 error = -ENOSYS;
331 }
332
333 if (ret)
334 error = ret;
335 }
336 if (va->va_size != (u_quad_t)PUFFS_VNOVAL) {
337 ret = 0;
338
339 if (fuse->op.truncate) {
340 ret = fuse->op.truncate(path, (off_t)va->va_size);
341 } else if (fuse->op.ftruncate) {
342 ret = fuse->op.ftruncate(path, (off_t)va->va_size,
343 &rn->file_info);
344 } else {
345 error = -ENOSYS;
346 }
347
348 if (ret)
349 error = ret;
350 }
351 /* XXX: no reflection with reality */
352 puffs_setvattr(&pn->pn_va, va);
353
354 return -error;
355
356 }
357
358 static int
359 fuse_newnode(struct puffs_usermount *pu, const char *path,
360 const struct vattr *va, struct fuse_file_info *fi, void **newnode)
361 {
362 struct vattr newva;
363 struct fuse *fuse;
364 struct puffs_node *pn;
365 struct refusenode *rn;
366
367 fuse = (struct fuse *)pu->pu_privdata;
368
369 /* fix up nodes */
370 pn = newrn(pu);
371 if (pn == NULL) {
372 if (va->va_type == VDIR) {
373 FUSE_ERR_RMDIR(fuse, path);
374 } else {
375 FUSE_ERR_UNLINK(fuse, path);
376 }
377 return ENOMEM;
378 }
379 fuse_setattr(fuse, pn, path, va);
380 if (fuse_getattr(fuse, pn, path, &newva) == 0)
381 puffs_setvattr(&pn->pn_va, &newva);
382
383 rn = pn->pn_data;
384 if (fi)
385 memcpy(&rn->file_info, fi, sizeof(struct fuse_file_info));
386
387 *newnode = pn;
388
389 return 0;
390 }
391
392
393 /* operation wrappers start here */
394
395 /* lookup the path */
396 /* ARGSUSED1 */
397 static int
398 puffs_fuse_node_lookup(struct puffs_cc *pcc, void *opc, void **newnode,
399 enum vtype *newtype, voff_t *newsize, dev_t *newrdev,
400 const struct puffs_cn *pcn)
401 {
402 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
403 struct puffs_node *pn_res;
404 struct stat st;
405 struct fuse *fuse;
406 const char *path = PCNPATH(pcn);
407 int ret;
408
409 fuse = (struct fuse *)pu->pu_privdata;
410 ret = fuse->op.getattr(path, &st);
411
412 if (ret != 0) {
413 return -ret;
414 }
415
416 /* XXX: fiXXXme unconst */
417 pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp,
418 __UNCONST(&pcn->pcn_po_full));
419 if (pn_res == NULL) {
420 pn_res = newrn(pu);
421 if (pn_res == NULL)
422 return errno;
423 puffs_stat2vattr(&pn_res->pn_va, &st);
424 }
425
426 *newnode = pn_res;
427 *newtype = pn_res->pn_va.va_type;
428 *newsize = pn_res->pn_va.va_size;
429 *newrdev = pn_res->pn_va.va_rdev;
430
431 return 0;
432 }
433
434 /* get attributes for the path name */
435 /* ARGSUSED3 */
436 static int
437 puffs_fuse_node_getattr(struct puffs_cc *pcc, void *opc, struct vattr *va,
438 const struct puffs_cred *pcr, pid_t pid)
439 {
440 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
441 struct puffs_node *pn = opc;
442 struct fuse *fuse;
443 const char *path = PNPATH(pn);
444
445 fuse = (struct fuse *)pu->pu_privdata;
446 return fuse_getattr(fuse, pn, path, va);
447 }
448
449 /* read the contents of the symbolic link */
450 /* ARGSUSED2 */
451 static int
452 puffs_fuse_node_readlink(struct puffs_cc *pcc, void *opc,
453 const struct puffs_cred *cred, char *linkname, size_t *linklen)
454 {
455 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
456 struct puffs_node *pn = opc;
457 struct fuse *fuse;
458 const char *path = PNPATH(pn), *p;
459 int ret;
460
461 fuse = (struct fuse *)pu->pu_privdata;
462 if (fuse->op.readlink == NULL) {
463 return ENOSYS;
464 }
465
466 /* wrap up return code */
467 ret = (*fuse->op.readlink)(path, linkname, *linklen);
468
469 if (ret == 0) {
470 p = memchr(linkname, '\0', *linklen);
471 if (!p)
472 return EINVAL;
473
474 *linklen = p - linkname;
475 }
476
477 return -ret;
478 }
479
480 /* make the special node */
481 /* ARGSUSED1 */
482 static int
483 puffs_fuse_node_mknod(struct puffs_cc *pcc, void *opc, void **newnode,
484 const struct puffs_cn *pcn, const struct vattr *va)
485 {
486 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
487 struct fuse *fuse;
488 mode_t mode = va->va_mode;
489 const char *path = PCNPATH(pcn);
490 int ret;
491
492 fuse = (struct fuse *)pu->pu_privdata;
493 if (fuse->op.mknod == NULL) {
494 return ENOSYS;
495 }
496
497 /* wrap up return code */
498 ret = (*fuse->op.mknod)(path, mode, va->va_rdev);
499
500 if (ret == 0) {
501 ret = fuse_newnode(pu, path, va, NULL, newnode);
502 }
503
504 return -ret;
505 }
506
507 /* make a directory */
508 /* ARGSUSED1 */
509 static int
510 puffs_fuse_node_mkdir(struct puffs_cc *pcc, void *opc, void **newnode,
511 const struct puffs_cn *pcn, const struct vattr *va)
512 {
513 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
514 struct fuse *fuse;
515 mode_t mode = va->va_mode;
516 const char *path = PCNPATH(pcn);
517 int ret;
518
519 fuse = (struct fuse *)pu->pu_privdata;
520 if (fuse->op.mkdir == NULL) {
521 return ENOSYS;
522 }
523
524 /* wrap up return code */
525 ret = (*fuse->op.mkdir)(path, mode);
526
527 if (ret == 0) {
528 ret = fuse_newnode(pu, path, va, NULL, newnode);
529 }
530
531 return -ret;
532 }
533
534 /*
535 * create a regular file
536 *
537 * since linux/fuse sports using mknod for creating regular files
538 * instead of having a separate call for it in some versions, if
539 * we don't have create, just jump to op->mknod.
540 */
541 /*ARGSUSED1*/
542 static int
543 puffs_fuse_node_create(struct puffs_cc *pcc, void *opc, void **newnode,
544 const struct puffs_cn *pcn, const struct vattr *va)
545 {
546 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
547 struct fuse *fuse;
548 struct fuse_file_info fi;
549 mode_t mode = va->va_mode;
550 const char *path = PCNPATH(pcn);
551 int ret, created;
552
553 fuse = (struct fuse *)pu->pu_privdata;
554
555 created = 0;
556 if (fuse->op.create) {
557 ret = fuse->op.create(path, mode, &fi);
558 if (ret == 0)
559 created = 1;
560
561 } else if (fuse->op.mknod) {
562 fcon.uid = va->va_uid; /*XXX*/
563 fcon.gid = va->va_gid; /*XXX*/
564
565 ret = fuse->op.mknod(path, mode | S_IFREG, 0);
566
567 } else {
568 ret = -ENOSYS;
569 }
570
571 if (ret == 0) {
572 ret = fuse_newnode(pu, path, va, &fi, newnode);
573
574 /* sweet.. create also open the file */
575 if (created) {
576 struct puffs_node *pn;
577 struct refusenode *rn;
578
579 pn = *newnode;
580 rn = pn->pn_data;
581 rn->flags |= RN_OPEN;
582 rn->opencount++;
583 }
584 }
585
586 return -ret;
587 }
588
589 /* remove the directory entry */
590 /* ARGSUSED1 */
591 static int
592 puffs_fuse_node_remove(struct puffs_cc *pcc, void *opc, void *targ,
593 const struct puffs_cn *pcn)
594 {
595 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
596 struct puffs_node *pn_targ = targ;
597 struct fuse *fuse;
598 const char *path = PNPATH(pn_targ);
599 int ret;
600
601 fuse = (struct fuse *)pu->pu_privdata;
602 if (fuse->op.unlink == NULL) {
603 return ENOSYS;
604 }
605
606 /* wrap up return code */
607 ret = (*fuse->op.unlink)(path);
608
609 return -ret;
610 }
611
612 /* remove the directory */
613 /* ARGSUSED1 */
614 static int
615 puffs_fuse_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ,
616 const struct puffs_cn *pcn)
617 {
618 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
619 struct puffs_node *pn_targ = targ;
620 struct fuse *fuse;
621 const char *path = PNPATH(pn_targ);
622 int ret;
623
624 fuse = (struct fuse *)pu->pu_privdata;
625 if (fuse->op.rmdir == NULL) {
626 return ENOSYS;
627 }
628
629 /* wrap up return code */
630 ret = (*fuse->op.rmdir)(path);
631
632 return -ret;
633 }
634
635 /* create a symbolic link */
636 /* ARGSUSED1 */
637 static int
638 puffs_fuse_node_symlink(struct puffs_cc *pcc, void *opc, void **newnode,
639 const struct puffs_cn *pcn_src, const struct vattr *va,
640 const char *link_target)
641 {
642 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
643 struct fuse *fuse;
644 const char *path = PCNPATH(pcn_src);
645 int ret;
646
647 fuse = (struct fuse *)pu->pu_privdata;
648 if (fuse->op.symlink == NULL) {
649 return ENOSYS;
650 }
651
652 /* wrap up return code */
653 ret = fuse->op.symlink(link_target, path);
654
655 if (ret == 0) {
656 ret = fuse_newnode(pu, path, va, NULL, newnode);
657 }
658
659 return -ret;
660 }
661
662 /* rename a directory entry */
663 /* ARGSUSED1 */
664 static int
665 puffs_fuse_node_rename(struct puffs_cc *pcc, void *opc, void *src,
666 const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
667 const struct puffs_cn *pcn_targ)
668 {
669 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
670 struct fuse *fuse;
671 const char *path_src = PCNPATH(pcn_src);
672 const char *path_dest = PCNPATH(pcn_targ);
673 int ret;
674
675 fuse = (struct fuse *)pu->pu_privdata;
676 if (fuse->op.rename == NULL) {
677 return ENOSYS;
678 }
679
680 ret = fuse->op.rename(path_src, path_dest);
681
682 if (ret == 0) {
683 }
684
685 return -ret;
686 }
687
688 /* create a link in the file system */
689 /* ARGSUSED1 */
690 static int
691 puffs_fuse_node_link(struct puffs_cc *pcc, void *opc, void *targ,
692 const struct puffs_cn *pcn)
693 {
694 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
695 struct puffs_node *pn = targ;
696 struct fuse *fuse;
697 int ret;
698
699 fuse = (struct fuse *)pu->pu_privdata;
700 if (fuse->op.link == NULL) {
701 return ENOSYS;
702 }
703
704 /* wrap up return code */
705 ret = (*fuse->op.link)(PNPATH(pn), PCNPATH(pcn));
706
707 return -ret;
708 }
709
710 /*
711 * fuse's regular interface provides chmod(), chown(), utimes()
712 * and truncate() + some variations, so try to fit the square block
713 * in the circle hole and the circle block .... something like that
714 */
715 /* ARGSUSED3 */
716 static int
717 puffs_fuse_node_setattr(struct puffs_cc *pcc, void *opc,
718 const struct vattr *va, const struct puffs_cred *pcr, pid_t pid)
719 {
720 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
721 struct puffs_node *pn = opc;
722 struct fuse *fuse;
723 const char *path = PNPATH(pn);
724
725 fuse = (struct fuse *)pu->pu_privdata;
726
727 return fuse_setattr(fuse, pn, path, va);
728 }
729
730 /* ARGSUSED2 */
731 static int
732 puffs_fuse_node_open(struct puffs_cc *pcc, void *opc, int mode,
733 const struct puffs_cred *cred, pid_t pid)
734 {
735 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
736 struct puffs_node *pn = opc;
737 struct refusenode *rn = pn->pn_data;
738 struct fuse_file_info *fi = &rn->file_info;
739 struct fuse *fuse;
740 const char *path = PNPATH(pn);
741
742 fuse = (struct fuse *)pu->pu_privdata;
743
744 /* if open, don't open again, lest risk nuking file private info */
745 if (rn->flags & RN_OPEN) {
746 rn->opencount++;
747 return 0;
748 }
749
750 fi->flags = mode & ~(O_CREAT | O_EXCL | O_TRUNC);
751 if (pn->pn_va.va_type == VDIR) {
752 if (fuse->op.opendir)
753 fuse->op.opendir(path, fi);
754 } else {
755 if (fuse->op.open)
756 fuse->op.open(path, fi);
757 }
758
759 rn->flags |= RN_OPEN;
760 rn->opencount++;
761
762 return 0;
763 }
764
765 /* ARGSUSED2 */
766 static int
767 puffs_fuse_node_close(struct puffs_cc *pcc, void *opc, int fflag,
768 const struct puffs_cred *pcr, pid_t pid)
769 {
770 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
771 struct puffs_node *pn = opc;
772 struct refusenode *rn = pn->pn_data;
773 struct fuse *fuse;
774 struct fuse_file_info *fi;
775 const char *path = PNPATH(pn);
776 int ret;
777
778 fuse = (struct fuse *)pu->pu_privdata;
779 fi = &rn->file_info;
780 ret = 0;
781
782 if (rn->flags & RN_OPEN) {
783 if (pn->pn_va.va_type == VDIR) {
784 if (fuse->op.releasedir)
785 ret = fuse->op.releasedir(path, fi);
786 } else {
787 if (fuse->op.release)
788 ret = fuse->op.release(path, fi);
789 }
790 }
791 rn->flags &= ~RN_OPEN;
792 rn->opencount--;
793
794 return ret;
795 }
796
797 /* read some more from the file */
798 /* ARGSUSED5 */
799 static int
800 puffs_fuse_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
801 off_t offset, size_t *resid, const struct puffs_cred *pcr,
802 int ioflag)
803 {
804 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
805 struct puffs_node *pn = opc;
806 struct refusenode *rn = pn->pn_data;
807 struct fuse *fuse;
808 const char *path = PNPATH(pn);
809 size_t maxread;
810 int ret;
811
812 fuse = (struct fuse *)pu->pu_privdata;
813 if (fuse->op.read == NULL) {
814 return ENOSYS;
815 }
816
817 maxread = *resid;
818 if (maxread > pn->pn_va.va_size - offset) {
819 /*LINTED*/
820 maxread = pn->pn_va.va_size - offset;
821 }
822 if (maxread == 0)
823 return 0;
824
825 ret = (*fuse->op.read)(path, (char *)buf, maxread, offset,
826 &rn->file_info);
827
828 if (ret > 0) {
829 *resid -= ret;
830 ret = 0;
831 }
832
833 return -ret;
834 }
835
836 /* write to the file */
837 /* ARGSUSED0 */
838 static int
839 puffs_fuse_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
840 off_t offset, size_t *resid, const struct puffs_cred *pcr,
841 int ioflag)
842 {
843 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
844 struct puffs_node *pn = opc;
845 struct refusenode *rn = pn->pn_data;
846 struct fuse *fuse;
847 const char *path = PNPATH(pn);
848 int ret;
849
850 fuse = (struct fuse *)pu->pu_privdata;
851 if (fuse->op.write == NULL) {
852 return ENOSYS;
853 }
854
855 if (ioflag & PUFFS_IO_APPEND)
856 offset = pn->pn_va.va_size;
857
858 ret = (*fuse->op.write)(path, (char *)buf, *resid, offset,
859 &rn->file_info);
860
861 if (ret > 0) {
862 if (offset + ret > pn->pn_va.va_size)
863 pn->pn_va.va_size = offset + ret;
864 *resid -= ret;
865 ret = 0;
866 }
867
868 return -ret;
869 }
870
871
872 /* ARGSUSED3 */
873 static int
874 puffs_fuse_node_readdir(struct puffs_cc *pcc, void *opc,
875 struct dirent *dent, const struct puffs_cred *pcr, off_t *readoff,
876 size_t *reslen)
877 {
878 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
879 struct puffs_node *pn = opc;
880 struct refusenode *rn = pn->pn_data;
881 struct puffs_fuse_dirh *dirh;
882 struct fuse *fuse;
883 struct dirent *fromdent;
884 const char *path = PNPATH(pn);
885 int ret;
886
887 fuse = (struct fuse *)pu->pu_privdata;
888 if (fuse->op.readdir == NULL && fuse->op.getdir == NULL) {
889 return ENOSYS;
890 }
891
892 if (pn->pn_va.va_type != VDIR)
893 return ENOTDIR;
894
895 dirh = &rn->dirh;
896
897 /*
898 * if we are starting from the beginning, slurp entire directory
899 * into our buffers
900 */
901 if (*readoff == 0) {
902 /* free old buffers */
903 free(dirh->dbuf);
904 memset(dirh, 0, sizeof(struct puffs_fuse_dirh));
905
906 if (fuse->op.readdir)
907 ret = fuse->op.readdir(path, dirh, puffs_fuse_fill_dir,
908 0, &rn->file_info);
909 else
910 ret = fuse->op.getdir(path, dirh, puffs_fuse_dirfil);
911 if (ret)
912 return -ret;
913 }
914
915 /* now, stuff results into the kernel buffers */
916 while (*readoff < dirh->bufsize - dirh->reslen) {
917 /*LINTED*/
918 fromdent = (struct dirent *)((uint8_t *)dirh->dbuf + *readoff);
919
920 if (*reslen < _DIRENT_SIZE(fromdent))
921 break;
922
923 memcpy(dent, fromdent, _DIRENT_SIZE(fromdent));
924 *readoff += _DIRENT_SIZE(fromdent);
925 *reslen -= _DIRENT_SIZE(fromdent);
926
927 dent = _DIRENT_NEXT(dent);
928 }
929
930 return 0;
931 }
932
933 /* ARGSUSED */
934 static int
935 puffs_fuse_node_reclaim(struct puffs_cc *pcc, void *opc, pid_t pid)
936 {
937 struct puffs_node *pn = opc;
938
939 nukern(pn);
940
941 return 0;
942 }
943
944 /* ARGSUSED1 */
945 static int
946 puffs_fuse_fs_unmount(struct puffs_cc *pcc, int flags, pid_t pid)
947 {
948 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
949 struct fuse *fuse;
950
951 fuse = (struct fuse *)pu->pu_privdata;
952 if (fuse->op.destroy == NULL) {
953 return 0;
954 }
955 (*fuse->op.destroy)(fuse);
956 return 0;
957 }
958
959 /* ARGSUSED0 */
960 static int
961 puffs_fuse_fs_sync(struct puffs_cc *pcc, int flags,
962 const struct puffs_cred *cr, pid_t pid)
963 {
964 return 0;
965 }
966
967 /* ARGSUSED2 */
968 static int
969 puffs_fuse_fs_statvfs(struct puffs_cc *pcc, struct statvfs *svfsb, pid_t pid)
970 {
971 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
972 struct fuse *fuse;
973 int ret;
974
975 fuse = (struct fuse *)pu->pu_privdata;
976 if (fuse->op.statfs == NULL) {
977 if ((ret = statvfs(PNPATH(pu->pu_pn_root), svfsb)) == -1) {
978 return errno;
979 }
980 } else {
981 ret = (*fuse->op.statfs)(PNPATH(pu->pu_pn_root), svfsb);
982 }
983
984 return ret;
985 }
986
987
988 /* End of puffs_fuse operations */
989
990 /* ARGSUSED3 */
991 int
992 fuse_main_real(int argc, char **argv, const struct fuse_operations *ops,
993 size_t size, void *userdata)
994 {
995 struct puffs_usermount *pu;
996 struct puffs_pathobj *po_root;
997 struct puffs_ops *pops;
998 struct refusenode *rn_root;
999 struct statvfs svfsb;
1000 struct stat st;
1001 struct fuse *fuse;
1002 char name[64];
1003 char *slash;
1004 int ret;
1005
1006 /* initialise the puffs operations structure */
1007 PUFFSOP_INIT(pops);
1008
1009 PUFFSOP_SET(pops, puffs_fuse, fs, sync);
1010 PUFFSOP_SET(pops, puffs_fuse, fs, statvfs);
1011 PUFFSOP_SET(pops, puffs_fuse, fs, unmount);
1012
1013 /*
1014 * XXX: all of these don't possibly need to be
1015 * unconditionally set
1016 */
1017 PUFFSOP_SET(pops, puffs_fuse, node, lookup);
1018 PUFFSOP_SET(pops, puffs_fuse, node, getattr);
1019 PUFFSOP_SET(pops, puffs_fuse, node, setattr);
1020 PUFFSOP_SET(pops, puffs_fuse, node, readdir);
1021 PUFFSOP_SET(pops, puffs_fuse, node, readlink);
1022 PUFFSOP_SET(pops, puffs_fuse, node, mknod);
1023 PUFFSOP_SET(pops, puffs_fuse, node, create);
1024 PUFFSOP_SET(pops, puffs_fuse, node, remove);
1025 PUFFSOP_SET(pops, puffs_fuse, node, mkdir);
1026 PUFFSOP_SET(pops, puffs_fuse, node, rmdir);
1027 PUFFSOP_SET(pops, puffs_fuse, node, symlink);
1028 PUFFSOP_SET(pops, puffs_fuse, node, rename);
1029 PUFFSOP_SET(pops, puffs_fuse, node, link);
1030 PUFFSOP_SET(pops, puffs_fuse, node, open);
1031 PUFFSOP_SET(pops, puffs_fuse, node, close);
1032 PUFFSOP_SET(pops, puffs_fuse, node, read);
1033 PUFFSOP_SET(pops, puffs_fuse, node, write);
1034 PUFFSOP_SET(pops, puffs_fuse, node, reclaim);
1035
1036 NEW(struct fuse, fuse, "fuse_main_real", exit(EXIT_FAILURE));
1037
1038 /* copy fuse ops to their own stucture */
1039 (void) memcpy(&fuse->op, ops, sizeof(fuse->op));
1040
1041 fcon.fuse = fuse;
1042 fcon.private_data = userdata;
1043
1044 /* whilst this (assigning the pu_privdata in the puffs
1045 * usermount struct to be the fuse struct) might seem like
1046 * we are chasing our tail here, the logic is as follows:
1047 + the operation wrapper gets called with the puffs
1048 calling conventions
1049 + we need to fix up args first
1050 + then call the fuse user-supplied operation
1051 + then we fix up any values on return that we need to
1052 + and fix up any nodes, etc
1053 * so we need to be able to get at the fuse ops from within the
1054 * puffs_usermount struct
1055 */
1056 if ((slash = strrchr(*argv, '/')) == NULL) {
1057 slash = *argv;
1058 } else {
1059 slash += 1;
1060 }
1061 (void) snprintf(name, sizeof(name), "refuse:%s", slash);
1062 pu = puffs_mount(pops, argv[argc - 1], MNT_NODEV | MNT_NOSUID,
1063 name, fuse,
1064 PUFFS_FLAG_BUILDPATH
1065 | PUFFS_FLAG_OPDUMP
1066 | PUFFS_KFLAG_NOCACHE,
1067 0);
1068 if (pu == NULL) {
1069 err(EXIT_FAILURE, "puffs_mount");
1070 }
1071
1072 fuse->pu = pu;
1073 pu->pu_pn_root = newrn(pu);
1074 rn_root = pu->pu_pn_root->pn_data;
1075 rn_root->flags |= RN_ROOT;
1076
1077 po_root = puffs_getrootpathobj(pu);
1078 po_root->po_path = strdup("/");
1079 po_root->po_len = 1;
1080
1081 /* sane defaults */
1082 puffs_vattr_null(&pu->pu_pn_root->pn_va);
1083 pu->pu_pn_root->pn_va.va_type = VDIR;
1084 pu->pu_pn_root->pn_va.va_mode = 0755;
1085 if (fuse->op.getattr)
1086 if (fuse->op.getattr(po_root->po_path, &st) == 0)
1087 puffs_stat2vattr(&pu->pu_pn_root->pn_va, &st);
1088 assert(pu->pu_pn_root->pn_va.va_type == VDIR);
1089
1090 if (fuse->op.init)
1091 fcon.private_data = fuse->op.init(NULL); /* XXX */
1092
1093 statvfs(argv[argc - 1], &svfsb); /* XXX - not really the correct dir */
1094 if (puffs_start(pu, pu->pu_pn_root, &svfsb) == -1) {
1095 err(EXIT_FAILURE, "puffs_start");
1096 }
1097
1098 ret = puffs_mainloop(fuse->pu, PUFFSLOOP_NODAEMON);
1099
1100 (void) free(po_root->po_path);
1101 FREE(fuse);
1102 return ret;
1103 }
1104
1105 /* ARGSUSED0 */
1106 int
1107 fuse_opt_parse(struct fuse_args *args, void *data,
1108 const struct fuse_opt *opts, fuse_opt_proc_t proc)
1109 {
1110 return 0;
1111 }
1112
1113 /* XXX: threads */
1114 struct fuse_context *
1115 fuse_get_context()
1116 {
1117
1118 return &fcon;
1119 }
1120
1121 void
1122 fuse_exit(struct fuse *f)
1123 {
1124
1125 puffs_exit(f->pu, 1);
1126 }
1127
1128 /*
1129 * XXX: obviously not the most perfect of functions, but needs some
1130 * puffs tweaking for a better tomorrow
1131 */
1132 /*ARGSUSED*/
1133 void
1134 fuse_unmount(const char *mp)
1135 {
1136
1137 return;
1138 }
1139