refuse.c revision 1.22 1 /* $NetBSD: refuse.c,v 1.22 2007/02/18 22:08:42 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.22 2007/02/18 22:08:42 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 /* ARGSUSED2 */
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 = opc;
464 struct fuse *fuse;
465 const char *path = PNPATH(pn);
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 = opc;
487 struct fuse *fuse;
488 const char *path = PNPATH(pn);
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 ret = 0;
793 }
794
795 return -ret;
796 }
797
798
799 /* ARGSUSED3 */
800 static int
801 puffs_fuse_node_readdir(struct puffs_cc *pcc, void *opc,
802 struct dirent *dent, const struct puffs_cred *pcr, off_t *readoff,
803 size_t *reslen)
804 {
805 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
806 struct puffs_node *pn = opc;
807 struct refusenode *rn = pn->pn_data;
808 struct fuse *fuse;
809 const char *path = PNPATH(pn);
810 struct fuse_dirh deh;
811 int ret;
812
813 fuse = (struct fuse *)pu->pu_privdata;
814 if (fuse->op.readdir == NULL && fuse->op.getdir == NULL) {
815 return ENOSYS;
816 }
817
818 /* XXX: how to handle this??? */
819 if (*readoff != 0) {
820 return 0;
821 }
822
823 deh.dent = dent;
824 deh.reslen = *reslen;
825 deh.readoff = *readoff;
826
827 if (fuse->op.readdir)
828 ret = fuse->op.readdir(path, &deh, puffs_fuse_fill_dir,
829 *readoff, &rn->file_info);
830 else
831 ret = fuse->op.getdir(path, &deh, puffs_fuse_dirfil);
832 *reslen = deh.reslen;
833 *readoff = 1;
834
835 if (ret == 0) {
836 }
837
838 return -ret;
839 }
840
841 /* ARGSUSED2 */
842 static int
843 puffs_fuse_node_reclaim(struct puffs_cc *pcc, void *opc, pid_t pid)
844 {
845 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
846 struct puffs_node *pn = opc;
847 struct refusenode *rn = pn->pn_data;
848 struct fuse *fuse;
849 const char *path = PNPATH(pn);
850 int ret;
851
852 fuse = (struct fuse *)pu->pu_privdata;
853
854 if (rn->flags & RN_OPEN) {
855 if (pn->pn_va.va_type == VDIR) {
856 if (fuse->op.releasedir == NULL)
857 ret = -ENOSYS;
858
859 ret = fuse->op.releasedir(path, &rn->file_info);
860 } else {
861 if (fuse->op.release == NULL)
862 return ENOSYS;
863
864 ret = fuse->op.release(path, &rn->file_info);
865 }
866 }
867
868 nukern(pn);
869
870 /*
871 * value ignored by the kernel, but we might as well
872 * return something for debugging purposes
873 */
874 return -ret;
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
922
923 /* End of puffs_fuse operations */
924
925 /* ARGSUSED3 */
926 int
927 fuse_main_real(int argc, char **argv, const struct fuse_operations *ops,
928 size_t size, void *userdata)
929 {
930 struct puffs_usermount *pu;
931 struct puffs_pathobj *po_root;
932 struct puffs_ops *pops;
933 struct statvfs svfsb;
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, reclaim);
967
968 NEW(struct fuse, fuse, "fuse_main_real", exit(EXIT_FAILURE));
969
970 /* copy fuse ops to their own stucture */
971 (void) memcpy(&fuse->op, ops, sizeof(fuse->op));
972
973 fcon.fuse = fuse;
974 fcon.private_data = userdata;
975
976 /* whilst this (assigning the pu_privdata in the puffs
977 * usermount struct to be the fuse struct) might seem like
978 * we are chasing our tail here, the logic is as follows:
979 + the operation wrapper gets called with the puffs
980 calling conventions
981 + we need to fix up args first
982 + then call the fuse user-supplied operation
983 + then we fix up any values on return that we need to
984 + and fix up any nodes, etc
985 * so we need to be able to get at the fuse ops from within the
986 * puffs_usermount struct
987 */
988 if ((slash = strrchr(*argv, '/')) == NULL) {
989 slash = *argv;
990 } else {
991 slash += 1;
992 }
993 (void) snprintf(name, sizeof(name), "refuse:%s", slash);
994 pu = puffs_mount(pops, argv[argc - 1], MNT_NODEV | MNT_NOSUID,
995 name, fuse,
996 PUFFS_FLAG_BUILDPATH | PUFFS_FLAG_OPDUMP, 0);
997 if (pu == NULL) {
998 err(EXIT_FAILURE, "puffs_mount");
999 }
1000
1001 fuse->pu = pu;
1002 pu->pu_pn_root = puffs_pn_new(pu, NULL);
1003 po_root = puffs_getrootpathobj(pu);
1004 po_root->po_path = strdup("/");
1005 po_root->po_len = 1;
1006
1007 if (fuse->op.init)
1008 fcon.private_data = fuse->op.init(NULL); /* XXX */
1009
1010 statvfs(argv[argc - 1], &svfsb); /* XXX - not really the correct dir */
1011 if (puffs_start(pu, pu->pu_pn_root, &svfsb) == -1) {
1012 err(EXIT_FAILURE, "puffs_start");
1013 }
1014
1015 ret = puffs_mainloop(fuse->pu, PUFFSLOOP_NODAEMON);
1016
1017 (void) free(po_root->po_path);
1018 FREE(fuse);
1019 return ret;
1020 }
1021
1022 /* ARGSUSED0 */
1023 int
1024 fuse_opt_parse(struct fuse_args *args, void *data,
1025 const struct fuse_opt *opts, fuse_opt_proc_t proc)
1026 {
1027 return 0;
1028 }
1029
1030 /* XXX: threads */
1031 struct fuse_context *
1032 fuse_get_context()
1033 {
1034
1035 return &fcon;
1036 }
1037
1038 void
1039 fuse_exit(struct fuse *f)
1040 {
1041
1042 puffs_exit(f->pu, 1);
1043 }
1044