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