refuse.c revision 1.37 1 /* $NetBSD: refuse.c,v 1.37 2007/02/26 15:09:19 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.37 2007/02/26 15:09:19 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 /* OFLAGS(), need to convert FREAD/FWRITE to O_RD/WR */
751 fi->flags = (mode & ~(O_CREAT | O_EXCL | O_TRUNC)) - 1;
752
753 if (pn->pn_va.va_type == VDIR) {
754 if (fuse->op.opendir)
755 fuse->op.opendir(path, fi);
756 } else {
757 if (fuse->op.open)
758 fuse->op.open(path, fi);
759 }
760
761 rn->flags |= RN_OPEN;
762 rn->opencount++;
763
764 return 0;
765 }
766
767 /* ARGSUSED2 */
768 static int
769 puffs_fuse_node_close(struct puffs_cc *pcc, void *opc, int fflag,
770 const struct puffs_cred *pcr, pid_t pid)
771 {
772 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
773 struct puffs_node *pn = opc;
774 struct refusenode *rn = pn->pn_data;
775 struct fuse *fuse;
776 struct fuse_file_info *fi;
777 const char *path = PNPATH(pn);
778 int ret;
779
780 fuse = (struct fuse *)pu->pu_privdata;
781 fi = &rn->file_info;
782 ret = 0;
783
784 if (rn->flags & RN_OPEN) {
785 if (pn->pn_va.va_type == VDIR) {
786 if (fuse->op.releasedir)
787 ret = fuse->op.releasedir(path, fi);
788 } else {
789 if (fuse->op.release)
790 ret = fuse->op.release(path, fi);
791 }
792 }
793 rn->flags &= ~RN_OPEN;
794 rn->opencount--;
795
796 return ret;
797 }
798
799 /* read some more from the file */
800 /* ARGSUSED5 */
801 static int
802 puffs_fuse_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
803 off_t offset, size_t *resid, const struct puffs_cred *pcr,
804 int ioflag)
805 {
806 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
807 struct puffs_node *pn = opc;
808 struct refusenode *rn = pn->pn_data;
809 struct fuse *fuse;
810 const char *path = PNPATH(pn);
811 size_t maxread;
812 int ret;
813
814 fuse = (struct fuse *)pu->pu_privdata;
815 if (fuse->op.read == NULL) {
816 return ENOSYS;
817 }
818
819 maxread = *resid;
820 if (maxread > pn->pn_va.va_size - offset) {
821 /*LINTED*/
822 maxread = pn->pn_va.va_size - offset;
823 }
824 if (maxread == 0)
825 return 0;
826
827 ret = (*fuse->op.read)(path, (char *)buf, maxread, offset,
828 &rn->file_info);
829
830 if (ret > 0) {
831 *resid -= ret;
832 ret = 0;
833 }
834
835 return -ret;
836 }
837
838 /* write to the file */
839 /* ARGSUSED0 */
840 static int
841 puffs_fuse_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
842 off_t offset, size_t *resid, const struct puffs_cred *pcr,
843 int ioflag)
844 {
845 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
846 struct puffs_node *pn = opc;
847 struct refusenode *rn = pn->pn_data;
848 struct fuse *fuse;
849 const char *path = PNPATH(pn);
850 int ret;
851
852 fuse = (struct fuse *)pu->pu_privdata;
853 if (fuse->op.write == NULL) {
854 return ENOSYS;
855 }
856
857 if (ioflag & PUFFS_IO_APPEND)
858 offset = pn->pn_va.va_size;
859
860 ret = (*fuse->op.write)(path, (char *)buf, *resid, offset,
861 &rn->file_info);
862
863 if (ret > 0) {
864 if (offset + ret > pn->pn_va.va_size)
865 pn->pn_va.va_size = offset + ret;
866 *resid -= ret;
867 ret = 0;
868 }
869
870 return -ret;
871 }
872
873
874 /* ARGSUSED3 */
875 static int
876 puffs_fuse_node_readdir(struct puffs_cc *pcc, void *opc,
877 struct dirent *dent, const struct puffs_cred *pcr, off_t *readoff,
878 size_t *reslen)
879 {
880 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
881 struct puffs_node *pn = opc;
882 struct refusenode *rn = pn->pn_data;
883 struct puffs_fuse_dirh *dirh;
884 struct fuse *fuse;
885 struct dirent *fromdent;
886 const char *path = PNPATH(pn);
887 int ret;
888
889 fuse = (struct fuse *)pu->pu_privdata;
890 if (fuse->op.readdir == NULL && fuse->op.getdir == NULL) {
891 return ENOSYS;
892 }
893
894 if (pn->pn_va.va_type != VDIR)
895 return ENOTDIR;
896
897 dirh = &rn->dirh;
898
899 /*
900 * if we are starting from the beginning, slurp entire directory
901 * into our buffers
902 */
903 if (*readoff == 0) {
904 /* free old buffers */
905 free(dirh->dbuf);
906 memset(dirh, 0, sizeof(struct puffs_fuse_dirh));
907
908 if (fuse->op.readdir)
909 ret = fuse->op.readdir(path, dirh, puffs_fuse_fill_dir,
910 0, &rn->file_info);
911 else
912 ret = fuse->op.getdir(path, dirh, puffs_fuse_dirfil);
913 if (ret)
914 return -ret;
915 }
916
917 /* now, stuff results into the kernel buffers */
918 while (*readoff < dirh->bufsize - dirh->reslen) {
919 /*LINTED*/
920 fromdent = (struct dirent *)((uint8_t *)dirh->dbuf + *readoff);
921
922 if (*reslen < _DIRENT_SIZE(fromdent))
923 break;
924
925 memcpy(dent, fromdent, _DIRENT_SIZE(fromdent));
926 *readoff += _DIRENT_SIZE(fromdent);
927 *reslen -= _DIRENT_SIZE(fromdent);
928
929 dent = _DIRENT_NEXT(dent);
930 }
931
932 return 0;
933 }
934
935 /* ARGSUSED */
936 static int
937 puffs_fuse_node_reclaim(struct puffs_cc *pcc, void *opc, pid_t pid)
938 {
939 struct puffs_node *pn = opc;
940
941 nukern(pn);
942
943 return 0;
944 }
945
946 /* ARGSUSED1 */
947 static int
948 puffs_fuse_fs_unmount(struct puffs_cc *pcc, int flags, pid_t pid)
949 {
950 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
951 struct fuse *fuse;
952
953 fuse = (struct fuse *)pu->pu_privdata;
954 if (fuse->op.destroy == NULL) {
955 return 0;
956 }
957 (*fuse->op.destroy)(fuse);
958 return 0;
959 }
960
961 /* ARGSUSED0 */
962 static int
963 puffs_fuse_fs_sync(struct puffs_cc *pcc, int flags,
964 const struct puffs_cred *cr, pid_t pid)
965 {
966 return 0;
967 }
968
969 /* ARGSUSED2 */
970 static int
971 puffs_fuse_fs_statvfs(struct puffs_cc *pcc, struct statvfs *svfsb, pid_t pid)
972 {
973 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
974 struct fuse *fuse;
975 int ret;
976
977 fuse = (struct fuse *)pu->pu_privdata;
978 if (fuse->op.statfs == NULL) {
979 if ((ret = statvfs(PNPATH(pu->pu_pn_root), svfsb)) == -1) {
980 return errno;
981 }
982 } else {
983 ret = (*fuse->op.statfs)(PNPATH(pu->pu_pn_root), svfsb);
984 }
985
986 return ret;
987 }
988
989
990 /* End of puffs_fuse operations */
991
992 /* ARGSUSED3 */
993 int
994 fuse_main_real(int argc, char **argv, const struct fuse_operations *ops,
995 size_t size, void *userdata)
996 {
997 struct puffs_usermount *pu;
998 struct puffs_pathobj *po_root;
999 struct puffs_ops *pops;
1000 struct refusenode *rn_root;
1001 struct statvfs svfsb;
1002 struct stat st;
1003 struct fuse *fuse;
1004 char name[64];
1005 char *slash;
1006 int ret;
1007
1008 /* initialise the puffs operations structure */
1009 PUFFSOP_INIT(pops);
1010
1011 PUFFSOP_SET(pops, puffs_fuse, fs, sync);
1012 PUFFSOP_SET(pops, puffs_fuse, fs, statvfs);
1013 PUFFSOP_SET(pops, puffs_fuse, fs, unmount);
1014
1015 /*
1016 * XXX: all of these don't possibly need to be
1017 * unconditionally set
1018 */
1019 PUFFSOP_SET(pops, puffs_fuse, node, lookup);
1020 PUFFSOP_SET(pops, puffs_fuse, node, getattr);
1021 PUFFSOP_SET(pops, puffs_fuse, node, setattr);
1022 PUFFSOP_SET(pops, puffs_fuse, node, readdir);
1023 PUFFSOP_SET(pops, puffs_fuse, node, readlink);
1024 PUFFSOP_SET(pops, puffs_fuse, node, mknod);
1025 PUFFSOP_SET(pops, puffs_fuse, node, create);
1026 PUFFSOP_SET(pops, puffs_fuse, node, remove);
1027 PUFFSOP_SET(pops, puffs_fuse, node, mkdir);
1028 PUFFSOP_SET(pops, puffs_fuse, node, rmdir);
1029 PUFFSOP_SET(pops, puffs_fuse, node, symlink);
1030 PUFFSOP_SET(pops, puffs_fuse, node, rename);
1031 PUFFSOP_SET(pops, puffs_fuse, node, link);
1032 PUFFSOP_SET(pops, puffs_fuse, node, open);
1033 PUFFSOP_SET(pops, puffs_fuse, node, close);
1034 PUFFSOP_SET(pops, puffs_fuse, node, read);
1035 PUFFSOP_SET(pops, puffs_fuse, node, write);
1036 PUFFSOP_SET(pops, puffs_fuse, node, reclaim);
1037
1038 NEW(struct fuse, fuse, "fuse_main_real", exit(EXIT_FAILURE));
1039
1040 /* copy fuse ops to their own stucture */
1041 (void) memcpy(&fuse->op, ops, sizeof(fuse->op));
1042
1043 fcon.fuse = fuse;
1044 fcon.private_data = userdata;
1045
1046 /* whilst this (assigning the pu_privdata in the puffs
1047 * usermount struct to be the fuse struct) might seem like
1048 * we are chasing our tail here, the logic is as follows:
1049 + the operation wrapper gets called with the puffs
1050 calling conventions
1051 + we need to fix up args first
1052 + then call the fuse user-supplied operation
1053 + then we fix up any values on return that we need to
1054 + and fix up any nodes, etc
1055 * so we need to be able to get at the fuse ops from within the
1056 * puffs_usermount struct
1057 */
1058 if ((slash = strrchr(*argv, '/')) == NULL) {
1059 slash = *argv;
1060 } else {
1061 slash += 1;
1062 }
1063 (void) snprintf(name, sizeof(name), "refuse:%s", slash);
1064 pu = puffs_mount(pops, argv[argc - 1], MNT_NODEV | MNT_NOSUID,
1065 name, fuse,
1066 PUFFS_FLAG_BUILDPATH
1067 | PUFFS_FLAG_OPDUMP
1068 | PUFFS_KFLAG_NOCACHE,
1069 0);
1070 if (pu == NULL) {
1071 err(EXIT_FAILURE, "puffs_mount");
1072 }
1073
1074 fuse->pu = pu;
1075 pu->pu_pn_root = newrn(pu);
1076 rn_root = pu->pu_pn_root->pn_data;
1077 rn_root->flags |= RN_ROOT;
1078
1079 po_root = puffs_getrootpathobj(pu);
1080 po_root->po_path = strdup("/");
1081 po_root->po_len = 1;
1082
1083 /* sane defaults */
1084 puffs_vattr_null(&pu->pu_pn_root->pn_va);
1085 pu->pu_pn_root->pn_va.va_type = VDIR;
1086 pu->pu_pn_root->pn_va.va_mode = 0755;
1087 if (fuse->op.getattr)
1088 if (fuse->op.getattr(po_root->po_path, &st) == 0)
1089 puffs_stat2vattr(&pu->pu_pn_root->pn_va, &st);
1090 assert(pu->pu_pn_root->pn_va.va_type == VDIR);
1091
1092 if (fuse->op.init)
1093 fcon.private_data = fuse->op.init(NULL); /* XXX */
1094
1095 statvfs(argv[argc - 1], &svfsb); /* XXX - not really the correct dir */
1096 if (puffs_start(pu, pu->pu_pn_root, &svfsb) == -1) {
1097 err(EXIT_FAILURE, "puffs_start");
1098 }
1099
1100 ret = puffs_mainloop(fuse->pu, PUFFSLOOP_NODAEMON);
1101
1102 (void) free(po_root->po_path);
1103 FREE(fuse);
1104 return ret;
1105 }
1106
1107 /* ARGSUSED0 */
1108 int
1109 fuse_opt_parse(struct fuse_args *args, void *data,
1110 const struct fuse_opt *opts, fuse_opt_proc_t proc)
1111 {
1112 return 0;
1113 }
1114
1115 /* XXX: threads */
1116 struct fuse_context *
1117 fuse_get_context()
1118 {
1119
1120 return &fcon;
1121 }
1122
1123 void
1124 fuse_exit(struct fuse *f)
1125 {
1126
1127 puffs_exit(f->pu, 1);
1128 }
1129
1130 /*
1131 * XXX: obviously not the most perfect of functions, but needs some
1132 * puffs tweaking for a better tomorrow
1133 */
1134 /*ARGSUSED*/
1135 void
1136 fuse_unmount(const char *mp)
1137 {
1138
1139 return;
1140 }
1141