refuse.c revision 1.30 1 /* $NetBSD: refuse.c,v 1.30 2007/02/20 14:51:52 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.30 2007/02/20 14:51:52 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 refusenode {
91 struct fuse_file_info file_info;
92 int flags;
93 };
94 #define RN_ROOT 0x01
95 #define RN_OPEN 0x02
96
97 static int fuse_setattr(struct fuse *, struct puffs_node *,
98 const char *, const struct vattr *);
99
100 static struct puffs_node *
101 newrn(struct puffs_usermount *pu)
102 {
103 struct puffs_node *pn;
104 struct refusenode *rn;
105
106 rn = malloc(sizeof(struct refusenode));
107 if (!rn)
108 abort(); /*XXX*/
109
110 memset(rn, 0, sizeof(struct refusenode));
111 pn = puffs_pn_new(pu, rn);
112
113 return pn;
114 }
115
116 static void
117 nukern(struct puffs_node *pn)
118 {
119
120 free(pn->pn_data);
121 puffs_pn_put(pn);
122 }
123
124 static ino_t fakeino = 3;
125
126 /*
127 * XXX: do this otherwise if/when we grow thread support
128 *
129 * XXX2: does not consistently supply uid, gid or pid currently
130 */
131 static struct fuse_context fcon;
132
133
134 /* XXX: rethinkme */
135 struct fuse_dirh {
136 struct dirent *dent;
137 size_t reslen;
138 off_t readoff;
139 };
140
141 /* ARGSUSED2 */
142 static int
143 puffs_fuse_fill_dir(void *buf, const char *name,
144 const struct stat *stbuf, off_t off)
145 {
146 struct fuse_dirh *deh = buf;
147 ino_t dino;
148 uint8_t dtype;
149
150 if (stbuf == NULL) {
151 dtype = DT_UNKNOWN;
152 dino = fakeino++;
153 } else {
154 dtype = puffs_vtype2dt(puffs_mode2vt(stbuf->st_mode));
155 dino = stbuf->st_ino;
156 }
157
158 return !puffs_nextdent(&deh->dent, name, dino, dtype, &deh->reslen);
159 }
160
161 static int
162 puffs_fuse_dirfil(fuse_dirh_t h, const char *name, int type, ino_t ino)
163 {
164 ino_t dino;
165 int dtype;
166
167 if (type == 0)
168 dtype = DT_UNKNOWN;
169 else
170 dtype = type;
171
172 if (ino)
173 dino = ino;
174 else
175 dino = fakeino++;
176
177 return !puffs_nextdent(&h->dent, name, dino, dtype, &h->reslen);
178 }
179
180 int
181 fuse_opt_add_arg(struct fuse_args *args, const char *arg)
182 {
183 char **oldargv;
184 int oldargc;
185
186 if (args->allocated) {
187 RENEW(char *, args->argv, args->argc + 1,
188 "fuse_opt_add_arg1", return 0);
189 } else {
190 oldargv = args->argv;
191 oldargc = args->argc;
192 NEWARRAY(char *, args->argv, oldargc + 1,
193 "fuse_opt_add_arg2", return 0);
194 (void) memcpy(args->argv, oldargv, oldargc * sizeof(char *));
195 args->allocated = 1;
196 }
197 args->argv[args->argc++] = strdup(arg);
198 return 1;
199 }
200
201 void
202 fuse_opt_free_args(struct fuse_args *args)
203 {
204 if (args && args->argv) {
205 int i;
206 for (i = 0; i < args->argc; i++)
207 FREE(args->argv[i]);
208 FREE(args->argv);
209 }
210 }
211
212 #define FUSE_ERR_UNLINK(fuse, file) if (fuse->op.unlink) fuse->op.unlink(file)
213 #define FUSE_ERR_RMDIR(fuse, dir) if (fuse->op.rmdir) fuse->op.rmdir(dir)
214
215 /* ARGSUSED1 */
216 static int
217 fuse_getattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
218 struct vattr *va)
219 {
220 struct stat st;
221 int ret;
222
223 if (fuse->op.getattr == NULL) {
224 return ENOSYS;
225 }
226
227 /* wrap up return code */
228 ret = (*fuse->op.getattr)(path, &st);
229
230 if (ret == 0) {
231 puffs_stat2vattr(va, &st);
232 }
233
234 return -ret;
235 }
236
237 static int
238 fuse_setattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
239 const struct vattr *va)
240 {
241 struct refusenode *rn = pn->pn_data;
242 mode_t mode;
243 uid_t uid;
244 gid_t gid;
245 int error, ret;
246
247 error = 0;
248
249 mode = va->va_mode;
250 uid = va->va_uid;
251 gid = va->va_gid;
252
253 if (mode != (mode_t)PUFFS_VNOVAL) {
254 ret = 0;
255
256 if (fuse->op.chmod == NULL) {
257 error = -ENOSYS;
258 } else {
259 ret = fuse->op.chmod(path, mode);
260 if (ret)
261 error = ret;
262 }
263 }
264 if (uid != (uid_t)PUFFS_VNOVAL || gid != (gid_t)PUFFS_VNOVAL) {
265 ret = 0;
266
267 if (fuse->op.chown == NULL) {
268 error = -ENOSYS;
269 } else {
270 ret = fuse->op.chown(path, uid, gid);
271 if (ret)
272 error = ret;
273 }
274 }
275 if (va->va_atime.tv_sec != (time_t)PUFFS_VNOVAL
276 || va->va_mtime.tv_sec != (long)PUFFS_VNOVAL) {
277 ret = 0;
278
279 if (fuse->op.utimens) {
280 struct timespec tv[2];
281
282 tv[0].tv_sec = va->va_atime.tv_sec;
283 tv[0].tv_nsec = va->va_atime.tv_nsec;
284 tv[1].tv_sec = va->va_mtime.tv_sec;
285 tv[1].tv_nsec = va->va_mtime.tv_nsec;
286
287 ret = fuse->op.utimens(path, tv);
288 } else if (fuse->op.utime) {
289 struct utimbuf timbuf;
290
291 timbuf.actime = va->va_atime.tv_sec;
292 timbuf.modtime = va->va_mtime.tv_sec;
293
294 ret = fuse->op.utime(path, &timbuf);
295 } else {
296 error = -ENOSYS;
297 }
298
299 if (ret)
300 error = ret;
301 }
302 if (va->va_size != (u_quad_t)PUFFS_VNOVAL) {
303 ret = 0;
304
305 if (fuse->op.truncate) {
306 ret = fuse->op.truncate(path, (off_t)va->va_size);
307 } else if (fuse->op.ftruncate) {
308 ret = fuse->op.ftruncate(path, (off_t)va->va_size,
309 &rn->file_info);
310 } else {
311 error = -ENOSYS;
312 }
313
314 if (ret)
315 error = ret;
316 }
317 /* XXX: no reflection with reality */
318 puffs_setvattr(&pn->pn_va, va);
319
320 return -error;
321
322 }
323
324 static int
325 fuse_newnode(struct puffs_usermount *pu, const char *path,
326 const struct vattr *va, struct fuse_file_info *fi, void **newnode)
327 {
328 struct vattr newva;
329 struct fuse *fuse;
330 struct puffs_node *pn;
331 struct refusenode *rn;
332
333 fuse = (struct fuse *)pu->pu_privdata;
334
335 /* fix up nodes */
336 pn = newrn(pu);
337 if (pn == NULL) {
338 if (va->va_type == VDIR) {
339 FUSE_ERR_RMDIR(fuse, path);
340 } else {
341 FUSE_ERR_UNLINK(fuse, path);
342 }
343 return ENOMEM;
344 }
345 fuse_setattr(fuse, pn, path, va);
346 if (fuse_getattr(fuse, pn, path, &newva) == 0)
347 puffs_setvattr(&pn->pn_va, &newva);
348
349 rn = pn->pn_data;
350 if (fi)
351 memcpy(&rn->file_info, fi, sizeof(struct fuse_file_info));
352
353 *newnode = pn;
354
355 return 0;
356 }
357
358
359 /* operation wrappers start here */
360
361 /* lookup the path */
362 /* ARGSUSED1 */
363 static int
364 puffs_fuse_node_lookup(struct puffs_cc *pcc, void *opc, void **newnode,
365 enum vtype *newtype, voff_t *newsize, dev_t *newrdev,
366 const struct puffs_cn *pcn)
367 {
368 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
369 struct puffs_node *pn_res;
370 struct stat st;
371 struct fuse *fuse;
372 const char *path = PCNPATH(pcn);
373 int ret;
374
375 fuse = (struct fuse *)pu->pu_privdata;
376 ret = fuse->op.getattr(path, &st);
377
378 if (ret != 0) {
379 return -ret;
380 }
381
382 /* XXX: fiXXXme unconst */
383 pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp,
384 __UNCONST(&pcn->pcn_po_full));
385 if (pn_res == NULL) {
386 pn_res = newrn(pu);
387 if (pn_res == NULL)
388 return errno;
389 puffs_stat2vattr(&pn_res->pn_va, &st);
390 }
391
392 *newnode = pn_res;
393 *newtype = pn_res->pn_va.va_type;
394 *newsize = pn_res->pn_va.va_size;
395 *newrdev = pn_res->pn_va.va_rdev;
396
397 return 0;
398 }
399
400 /* get attributes for the path name */
401 /* ARGSUSED3 */
402 static int
403 puffs_fuse_node_getattr(struct puffs_cc *pcc, void *opc, struct vattr *va,
404 const struct puffs_cred *pcr, pid_t pid)
405 {
406 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
407 struct puffs_node *pn = opc;
408 struct fuse *fuse;
409 const char *path = PNPATH(pn);
410
411 fuse = (struct fuse *)pu->pu_privdata;
412 return fuse_getattr(fuse, pn, path, va);
413 }
414
415 /* read the contents of the symbolic link */
416 /* ARGSUSED2 */
417 static int
418 puffs_fuse_node_readlink(struct puffs_cc *pcc, void *opc,
419 const struct puffs_cred *cred, char *linkname, size_t *linklen)
420 {
421 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
422 struct puffs_node *pn = opc;
423 struct fuse *fuse;
424 const char *path = PNPATH(pn), *p;
425 int ret;
426
427 fuse = (struct fuse *)pu->pu_privdata;
428 if (fuse->op.readlink == NULL) {
429 return ENOSYS;
430 }
431
432 /* wrap up return code */
433 ret = (*fuse->op.readlink)(path, linkname, *linklen);
434
435 if (ret == 0) {
436 p = memchr(linkname, '\0', *linklen);
437 if (!p)
438 return EINVAL;
439
440 *linklen = p - linkname;
441 }
442
443 return -ret;
444 }
445
446 /* make the special node */
447 /* ARGSUSED1 */
448 static int
449 puffs_fuse_node_mknod(struct puffs_cc *pcc, void *opc, void **newnode,
450 const struct puffs_cn *pcn, const struct vattr *va)
451 {
452 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
453 struct fuse *fuse;
454 mode_t mode = va->va_mode;
455 const char *path = PCNPATH(pcn);
456 int ret;
457
458 fuse = (struct fuse *)pu->pu_privdata;
459 if (fuse->op.mknod == NULL) {
460 return ENOSYS;
461 }
462
463 /* wrap up return code */
464 ret = (*fuse->op.mknod)(path, mode, va->va_rdev);
465
466 if (ret == 0) {
467 ret = fuse_newnode(pu, path, va, NULL, newnode);
468 }
469
470 return -ret;
471 }
472
473 /* make a directory */
474 /* ARGSUSED1 */
475 static int
476 puffs_fuse_node_mkdir(struct puffs_cc *pcc, void *opc, void **newnode,
477 const struct puffs_cn *pcn, const struct vattr *va)
478 {
479 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
480 struct fuse *fuse;
481 mode_t mode = va->va_mode;
482 const char *path = PCNPATH(pcn);
483 int ret;
484
485 fuse = (struct fuse *)pu->pu_privdata;
486 if (fuse->op.mkdir == NULL) {
487 return ENOSYS;
488 }
489
490 /* wrap up return code */
491 ret = (*fuse->op.mkdir)(path, mode);
492
493 if (ret == 0) {
494 ret = fuse_newnode(pu, path, va, NULL, newnode);
495 }
496
497 return -ret;
498 }
499
500 /*
501 * create a regular file
502 *
503 * since linux/fuse sports using mknod for creating regular files
504 * instead of having a separate call for it in some versions, if
505 * we don't have create, just jump to op->mknod.
506 */
507 /*ARGSUSED1*/
508 static int
509 puffs_fuse_node_create(struct puffs_cc *pcc, void *opc, void **newnode,
510 const struct puffs_cn *pcn, const struct vattr *va)
511 {
512 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
513 struct fuse *fuse;
514 struct fuse_file_info fi;
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
521 if (fuse->op.create) {
522 ret = fuse->op.create(path, mode, &fi);
523
524 } else if (fuse->op.mknod) {
525 fcon.uid = va->va_uid; /*XXX*/
526 fcon.gid = va->va_gid; /*XXX*/
527
528 ret = fuse->op.mknod(path, mode | S_IFREG, 0);
529
530 } else {
531 ret = -ENOSYS;
532 }
533
534 if (ret == 0) {
535 ret = fuse_newnode(pu, path, va, &fi, newnode);
536 }
537
538 return -ret;
539 }
540
541 /* remove the directory entry */
542 /* ARGSUSED1 */
543 static int
544 puffs_fuse_node_remove(struct puffs_cc *pcc, void *opc, void *targ,
545 const struct puffs_cn *pcn)
546 {
547 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
548 struct puffs_node *pn_targ = targ;
549 struct fuse *fuse;
550 const char *path = PNPATH(pn_targ);
551 int ret;
552
553 fuse = (struct fuse *)pu->pu_privdata;
554 if (fuse->op.unlink == NULL) {
555 return ENOSYS;
556 }
557
558 /* wrap up return code */
559 ret = (*fuse->op.unlink)(path);
560
561 return -ret;
562 }
563
564 /* remove the directory */
565 /* ARGSUSED1 */
566 static int
567 puffs_fuse_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ,
568 const struct puffs_cn *pcn)
569 {
570 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
571 struct puffs_node *pn_targ = targ;
572 struct fuse *fuse;
573 const char *path = PNPATH(pn_targ);
574 int ret;
575
576 fuse = (struct fuse *)pu->pu_privdata;
577 if (fuse->op.rmdir == NULL) {
578 return ENOSYS;
579 }
580
581 /* wrap up return code */
582 ret = (*fuse->op.rmdir)(path);
583
584 return -ret;
585 }
586
587 /* create a symbolic link */
588 /* ARGSUSED1 */
589 static int
590 puffs_fuse_node_symlink(struct puffs_cc *pcc, void *opc, void **newnode,
591 const struct puffs_cn *pcn_src, const struct vattr *va,
592 const char *link_target)
593 {
594 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
595 struct fuse *fuse;
596 const char *path = PCNPATH(pcn_src);
597 int ret;
598
599 fuse = (struct fuse *)pu->pu_privdata;
600 if (fuse->op.symlink == NULL) {
601 return ENOSYS;
602 }
603
604 /* wrap up return code */
605 ret = (*fuse->op.symlink)(path, link_target);
606 /* XXX - check I haven't transposed these args */
607
608 if (ret == 0) {
609 ret = fuse_newnode(pu, path, va, NULL, newnode);
610 }
611
612 return -ret;
613 }
614
615 /* rename a directory entry */
616 /* ARGSUSED1 */
617 static int
618 puffs_fuse_node_rename(struct puffs_cc *pcc, void *opc, void *src,
619 const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
620 const struct puffs_cn *pcn_targ)
621 {
622 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
623 struct fuse *fuse;
624 const char *path_src = PCNPATH(pcn_src);
625 const char *path_dest = PCNPATH(pcn_targ);
626 int ret;
627
628 fuse = (struct fuse *)pu->pu_privdata;
629 if (fuse->op.rename == NULL) {
630 return ENOSYS;
631 }
632
633 ret = fuse->op.rename(path_src, path_dest);
634
635 if (ret == 0) {
636 }
637
638 return -ret;
639 }
640
641 /* create a link in the file system */
642 /* ARGSUSED1 */
643 static int
644 puffs_fuse_node_link(struct puffs_cc *pcc, void *opc, void *targ,
645 const struct puffs_cn *pcn)
646 {
647 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
648 struct puffs_node *pn = targ;
649 struct fuse *fuse;
650 int ret;
651
652 fuse = (struct fuse *)pu->pu_privdata;
653 if (fuse->op.link == NULL) {
654 return ENOSYS;
655 }
656
657 /* wrap up return code */
658 ret = (*fuse->op.link)(PNPATH(pn), PCNPATH(pcn));
659
660 return -ret;
661 }
662
663 /*
664 * fuse's regular interface provides chmod(), chown(), utimes()
665 * and truncate() + some variations, so try to fit the square block
666 * in the circle hole and the circle block .... something like that
667 */
668 /* ARGSUSED3 */
669 static int
670 puffs_fuse_node_setattr(struct puffs_cc *pcc, void *opc,
671 const struct vattr *va, const struct puffs_cred *pcr, pid_t pid)
672 {
673 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
674 struct puffs_node *pn = opc;
675 struct fuse *fuse;
676 const char *path = PNPATH(pn);
677
678 fuse = (struct fuse *)pu->pu_privdata;
679
680 return fuse_setattr(fuse, pn, path, va);
681 }
682
683 /* ARGSUSED2 */
684 static int
685 puffs_fuse_node_open(struct puffs_cc *pcc, void *opc, int flags,
686 const struct puffs_cred *cred, pid_t pid)
687 {
688 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
689 struct puffs_node *pn = opc;
690 struct refusenode *rn = pn->pn_data;
691 struct fuse *fuse;
692 const char *path = PNPATH(pn);
693 int ret;
694
695 fuse = (struct fuse *)pu->pu_privdata;
696
697 /* if open, don't open again, lest risk nuking file private info */
698 if (rn->flags & RN_OPEN)
699 return 0;
700
701 if (pn->pn_va.va_type == VDIR) {
702 if (fuse->op.opendir)
703 ret = fuse->op.opendir(path, &rn->file_info);
704 else
705 ret = ENOSYS;
706 } else {
707 if (fuse->op.open)
708 ret = fuse->op.open(path, &rn->file_info);
709 else
710 ret = ENOSYS;
711 }
712
713 if (ret == 0) {
714 rn->flags |= RN_OPEN;
715 }
716
717 return -ret;
718 }
719
720 /* read some more from the file */
721 /* ARGSUSED5 */
722 static int
723 puffs_fuse_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
724 off_t offset, size_t *resid, const struct puffs_cred *pcr,
725 int ioflag)
726 {
727 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
728 struct puffs_node *pn = opc;
729 struct refusenode *rn = pn->pn_data;
730 struct fuse *fuse;
731 const char *path = PNPATH(pn);
732 size_t maxread;
733 int ret;
734
735 fuse = (struct fuse *)pu->pu_privdata;
736 if (fuse->op.read == NULL) {
737 return ENOSYS;
738 }
739
740 maxread = *resid;
741 if (maxread > pn->pn_va.va_size - offset) {
742 /*LINTED*/
743 maxread = pn->pn_va.va_size - offset;
744 }
745 if (maxread == 0)
746 return 0;
747
748 ret = (*fuse->op.read)(path, (char *)buf, maxread, offset,
749 &rn->file_info);
750
751 if (ret > 0) {
752 *resid -= ret;
753 ret = 0;
754 }
755
756 return -ret;
757 }
758
759 /* write to the file */
760 /* ARGSUSED0 */
761 static int
762 puffs_fuse_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
763 off_t offset, size_t *resid, const struct puffs_cred *pcr,
764 int ioflag)
765 {
766 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
767 struct puffs_node *pn = opc;
768 struct refusenode *rn = pn->pn_data;
769 struct fuse *fuse;
770 const char *path = PNPATH(pn);
771 int ret;
772
773 fuse = (struct fuse *)pu->pu_privdata;
774 if (fuse->op.write == NULL) {
775 return ENOSYS;
776 }
777
778 if (ioflag & PUFFS_IO_APPEND)
779 offset = pn->pn_va.va_size;
780
781 ret = (*fuse->op.write)(path, (char *)buf, *resid, offset,
782 &rn->file_info);
783
784 if (ret > 0) {
785 if (offset + ret > pn->pn_va.va_size)
786 pn->pn_va.va_size = offset + ret;
787 *resid -= ret;
788 ret = 0;
789 }
790
791 return -ret;
792 }
793
794
795 /* ARGSUSED3 */
796 static int
797 puffs_fuse_node_readdir(struct puffs_cc *pcc, void *opc,
798 struct dirent *dent, const struct puffs_cred *pcr, off_t *readoff,
799 size_t *reslen)
800 {
801 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
802 struct puffs_node *pn = opc;
803 struct refusenode *rn = pn->pn_data;
804 struct fuse *fuse;
805 const char *path = PNPATH(pn);
806 struct fuse_dirh deh;
807 int ret;
808
809 fuse = (struct fuse *)pu->pu_privdata;
810 if (fuse->op.readdir == NULL && fuse->op.getdir == NULL) {
811 return ENOSYS;
812 }
813
814 /* XXX: how to handle this??? */
815 if (*readoff != 0) {
816 return 0;
817 }
818
819 deh.dent = dent;
820 deh.reslen = *reslen;
821 deh.readoff = *readoff;
822
823 if (fuse->op.readdir)
824 ret = fuse->op.readdir(path, &deh, puffs_fuse_fill_dir,
825 *readoff, &rn->file_info);
826 else
827 ret = fuse->op.getdir(path, &deh, puffs_fuse_dirfil);
828 *reslen = deh.reslen;
829 *readoff = 1;
830
831 if (ret == 0) {
832 }
833
834 return -ret;
835 }
836
837 /* ARGSUSED2 */
838 static int
839 puffs_fuse_node_inactive(struct puffs_cc *pcc, void *opc, pid_t pid,
840 int *refcount)
841 {
842 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
843 struct puffs_node *pn = opc;
844 struct refusenode *rn = pn->pn_data;
845 struct fuse *fuse;
846 const char *path = PNPATH(pn);
847
848 *refcount = 1; /* safe default */
849 fuse = (struct fuse *)pu->pu_privdata;
850
851 if (rn && rn->flags & RN_OPEN) {
852 if (pn->pn_va.va_type == VDIR) {
853 if (fuse->op.releasedir)
854 fuse->op.releasedir(path, &rn->file_info);
855 } else {
856 if (fuse->op.release)
857 fuse->op.release(path, &rn->file_info);
858 }
859 }
860 if (rn)
861 rn->flags &= ~RN_OPEN;
862
863 return 0;
864 }
865
866 /* ARGSUSED */
867 static int
868 puffs_fuse_node_reclaim(struct puffs_cc *pcc, void *opc, pid_t pid)
869 {
870 struct puffs_node *pn = opc;
871
872 nukern(pn);
873
874 return 0;
875 }
876
877 /* ARGSUSED1 */
878 static int
879 puffs_fuse_fs_unmount(struct puffs_cc *pcc, int flags, pid_t pid)
880 {
881 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
882 struct fuse *fuse;
883
884 fuse = (struct fuse *)pu->pu_privdata;
885 if (fuse->op.destroy == NULL) {
886 return 0;
887 }
888 (*fuse->op.destroy)(fuse);
889 return 0;
890 }
891
892 /* ARGSUSED0 */
893 static int
894 puffs_fuse_fs_sync(struct puffs_cc *pcc, int flags,
895 const struct puffs_cred *cr, pid_t pid)
896 {
897 return 0;
898 }
899
900 /* ARGSUSED2 */
901 static int
902 puffs_fuse_fs_statvfs(struct puffs_cc *pcc, struct statvfs *svfsb, pid_t pid)
903 {
904 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
905 struct fuse *fuse;
906 int ret;
907
908 fuse = (struct fuse *)pu->pu_privdata;
909 if (fuse->op.statfs == NULL) {
910 if ((ret = statvfs(PNPATH(pu->pu_pn_root), svfsb)) == -1) {
911 return errno;
912 }
913 } else {
914 ret = (*fuse->op.statfs)(PNPATH(pu->pu_pn_root), svfsb);
915 }
916
917 return ret;
918 }
919
920
921 /* End of puffs_fuse operations */
922
923 /* ARGSUSED3 */
924 int
925 fuse_main_real(int argc, char **argv, const struct fuse_operations *ops,
926 size_t size, void *userdata)
927 {
928 struct puffs_usermount *pu;
929 struct puffs_pathobj *po_root;
930 struct puffs_ops *pops;
931 struct refusenode *rn_root;
932 struct statvfs svfsb;
933 struct stat st;
934 struct fuse *fuse;
935 char name[64];
936 char *slash;
937 int ret;
938
939 /* initialise the puffs operations structure */
940 PUFFSOP_INIT(pops);
941
942 PUFFSOP_SET(pops, puffs_fuse, fs, sync);
943 PUFFSOP_SET(pops, puffs_fuse, fs, statvfs);
944 PUFFSOP_SET(pops, puffs_fuse, fs, unmount);
945
946 /*
947 * XXX: all of these don't possibly need to be
948 * unconditionally set
949 */
950 PUFFSOP_SET(pops, puffs_fuse, node, lookup);
951 PUFFSOP_SET(pops, puffs_fuse, node, getattr);
952 PUFFSOP_SET(pops, puffs_fuse, node, setattr);
953 PUFFSOP_SET(pops, puffs_fuse, node, readdir);
954 PUFFSOP_SET(pops, puffs_fuse, node, readlink);
955 PUFFSOP_SET(pops, puffs_fuse, node, mknod);
956 PUFFSOP_SET(pops, puffs_fuse, node, create);
957 PUFFSOP_SET(pops, puffs_fuse, node, remove);
958 PUFFSOP_SET(pops, puffs_fuse, node, mkdir);
959 PUFFSOP_SET(pops, puffs_fuse, node, rmdir);
960 PUFFSOP_SET(pops, puffs_fuse, node, symlink);
961 PUFFSOP_SET(pops, puffs_fuse, node, rename);
962 PUFFSOP_SET(pops, puffs_fuse, node, link);
963 PUFFSOP_SET(pops, puffs_fuse, node, open);
964 PUFFSOP_SET(pops, puffs_fuse, node, read);
965 PUFFSOP_SET(pops, puffs_fuse, node, write);
966 PUFFSOP_SET(pops, puffs_fuse, node, inactive);
967 PUFFSOP_SET(pops, puffs_fuse, node, reclaim);
968
969 NEW(struct fuse, fuse, "fuse_main_real", exit(EXIT_FAILURE));
970
971 /* copy fuse ops to their own stucture */
972 (void) memcpy(&fuse->op, ops, sizeof(fuse->op));
973
974 fcon.fuse = fuse;
975 fcon.private_data = userdata;
976
977 /* whilst this (assigning the pu_privdata in the puffs
978 * usermount struct to be the fuse struct) might seem like
979 * we are chasing our tail here, the logic is as follows:
980 + the operation wrapper gets called with the puffs
981 calling conventions
982 + we need to fix up args first
983 + then call the fuse user-supplied operation
984 + then we fix up any values on return that we need to
985 + and fix up any nodes, etc
986 * so we need to be able to get at the fuse ops from within the
987 * puffs_usermount struct
988 */
989 if ((slash = strrchr(*argv, '/')) == NULL) {
990 slash = *argv;
991 } else {
992 slash += 1;
993 }
994 (void) snprintf(name, sizeof(name), "refuse:%s", slash);
995 pu = puffs_mount(pops, argv[argc - 1], MNT_NODEV | MNT_NOSUID,
996 name, fuse,
997 PUFFS_FLAG_BUILDPATH
998 | PUFFS_FLAG_OPDUMP
999 | PUFFS_KFLAG_NOCACHE,
1000 0);
1001 if (pu == NULL) {
1002 err(EXIT_FAILURE, "puffs_mount");
1003 }
1004
1005 fuse->pu = pu;
1006 pu->pu_pn_root = newrn(pu);
1007 rn_root = pu->pu_pn_root->pn_data;
1008 rn_root->flags |= RN_ROOT;
1009
1010 po_root = puffs_getrootpathobj(pu);
1011 po_root->po_path = strdup("/");
1012 po_root->po_len = 1;
1013
1014 /* sane defaults */
1015 puffs_vattr_null(&pu->pu_pn_root->pn_va);
1016 pu->pu_pn_root->pn_va.va_type = VDIR;
1017 pu->pu_pn_root->pn_va.va_mode = 0755;
1018 if (fuse->op.getattr)
1019 if (fuse->op.getattr(po_root->po_path, &st) == 0)
1020 puffs_stat2vattr(&pu->pu_pn_root->pn_va, &st);
1021 assert(pu->pu_pn_root->pn_va.va_type == VDIR);
1022
1023 if (fuse->op.init)
1024 fcon.private_data = fuse->op.init(NULL); /* XXX */
1025
1026 statvfs(argv[argc - 1], &svfsb); /* XXX - not really the correct dir */
1027 if (puffs_start(pu, pu->pu_pn_root, &svfsb) == -1) {
1028 err(EXIT_FAILURE, "puffs_start");
1029 }
1030
1031 ret = puffs_mainloop(fuse->pu, PUFFSLOOP_NODAEMON);
1032
1033 (void) free(po_root->po_path);
1034 FREE(fuse);
1035 return ret;
1036 }
1037
1038 /* ARGSUSED0 */
1039 int
1040 fuse_opt_parse(struct fuse_args *args, void *data,
1041 const struct fuse_opt *opts, fuse_opt_proc_t proc)
1042 {
1043 return 0;
1044 }
1045
1046 /* XXX: threads */
1047 struct fuse_context *
1048 fuse_get_context()
1049 {
1050
1051 return &fcon;
1052 }
1053
1054 void
1055 fuse_exit(struct fuse *f)
1056 {
1057
1058 puffs_exit(f->pu, 1);
1059 }
1060
1061 /*
1062 * XXX: obviously not the most perfect of functions, but needs some
1063 * puffs tweaking for a better tomorrow
1064 */
1065 void
1066 fuse_unmount(const char *mp)
1067 {
1068
1069 return;
1070 }
1071