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