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