refuse.c revision 1.24 1 /* $NetBSD: refuse.c,v 1.24 2007/02/18 22:42:33 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.24 2007/02/18 22:42:33 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 fuse *fuse;
548 const char *path_src = PCNPATH(pcn_src);
549 const char *path_dest = PCNPATH(pcn_targ);
550 int ret;
551
552 fuse = (struct fuse *)pu->pu_privdata;
553 if (fuse->op.rename == NULL) {
554 return ENOSYS;
555 }
556
557 ret = fuse->op.rename(path_src, path_dest);
558
559 if (ret == 0) {
560 }
561
562 return -ret;
563 }
564
565 /* create a link in the file system */
566 /* ARGSUSED1 */
567 static int
568 puffs_fuse_node_link(struct puffs_cc *pcc, void *opc, void *targ,
569 const struct puffs_cn *pcn)
570 {
571 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
572 struct puffs_node *pn = targ;
573 struct fuse *fuse;
574 int ret;
575
576 fuse = (struct fuse *)pu->pu_privdata;
577 if (fuse->op.link == NULL) {
578 return ENOSYS;
579 }
580
581 /* wrap up return code */
582 ret = (*fuse->op.link)(PNPATH(pn), PCNPATH(pcn));
583
584 return -ret;
585 }
586
587 /*
588 * fuse's regular interface provides chmod(), chown(), utimes()
589 * and truncate() + some variations, so try to fit the square block
590 * in the circle hole and the circle block .... something like that
591 */
592 /* ARGSUSED3 */
593 static int
594 puffs_fuse_node_setattr(struct puffs_cc *pcc, void *opc,
595 const struct vattr *va, const struct puffs_cred *pcr, pid_t pid)
596 {
597 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
598 struct puffs_node *pn = opc;
599 struct refusenode *rn = pn->pn_data;
600 struct fuse *fuse;
601 const char *path = PNPATH(pn);
602 mode_t mode;
603 uid_t uid;
604 gid_t gid;
605 int error, ret;
606
607 fuse = (struct fuse *)pu->pu_privdata;
608
609 error = 0;
610
611 mode = va->va_mode;
612 uid = va->va_uid;
613 gid = va->va_gid;
614
615 if (mode != (mode_t)PUFFS_VNOVAL) {
616 ret = 0;
617
618 if (fuse->op.chmod == NULL) {
619 error = -ENOSYS;
620 } else {
621 ret = fuse->op.chmod(path, mode);
622 if (ret)
623 error = ret;
624 }
625 }
626 if (uid != (uid_t)PUFFS_VNOVAL || gid != (gid_t)PUFFS_VNOVAL) {
627 ret = 0;
628
629 if (fuse->op.chown == NULL) {
630 error = -ENOSYS;
631 } else {
632 ret = fuse->op.chown(path, uid, gid);
633 if (ret)
634 error = ret;
635 }
636 }
637 if (va->va_atime.tv_sec != (time_t)PUFFS_VNOVAL
638 || va->va_mtime.tv_sec != (long)PUFFS_VNOVAL) {
639 ret = 0;
640
641 if (fuse->op.utimens) {
642 struct timespec tv[2];
643
644 tv[0].tv_sec = va->va_atime.tv_sec;
645 tv[0].tv_nsec = va->va_atime.tv_nsec;
646 tv[1].tv_sec = va->va_mtime.tv_sec;
647 tv[1].tv_nsec = va->va_mtime.tv_nsec;
648
649 ret = fuse->op.utimens(path, tv);
650 } else if (fuse->op.utime) {
651 struct utimbuf timbuf;
652
653 timbuf.actime = va->va_atime.tv_sec;
654 timbuf.modtime = va->va_mtime.tv_sec;
655
656 ret = fuse->op.utime(path, &timbuf);
657 } else {
658 error = -ENOSYS;
659 }
660
661 if (ret)
662 error = ret;
663 }
664 if (va->va_size != (u_quad_t)PUFFS_VNOVAL) {
665 ret = 0;
666
667 if (fuse->op.truncate) {
668 ret = fuse->op.truncate(path, (off_t)va->va_size);
669 } else if (fuse->op.ftruncate) {
670 ret = fuse->op.ftruncate(path, (off_t)va->va_size,
671 &rn->file_info);
672 } else {
673 error = -ENOSYS;
674 }
675
676 if (ret)
677 error = ret;
678 }
679
680 return -error;
681 }
682
683 /* ARGSUSED2 */
684 static int
685 puffs_fuse_node_open(struct puffs_cc *pcc, void *opc, int flags,
686 const struct puffs_cred *cred, pid_t pid)
687 {
688 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
689 struct puffs_node *pn = opc;
690 struct refusenode *rn = pn->pn_data;
691 struct fuse *fuse;
692 struct stat st;
693 const char *path = PNPATH(pn);
694 int ret;
695
696 fuse = (struct fuse *)pu->pu_privdata;
697 if (fuse->op.open == NULL) {
698 return ENOSYS;
699 }
700
701 /* examine type - if directory, return 0 rather than open */
702 ret = (fuse->op.getattr == NULL) ?
703 stat(path, &st) :
704 (*fuse->op.getattr)(path, &st);
705 if (ret == 0 && (st.st_mode & S_IFMT) == S_IFDIR) {
706 return 0;
707 }
708
709 if (strcmp(path, "/") == 0) {
710 return 0;
711 }
712
713 ret = (*fuse->op.open)(path, &rn->file_info);
714
715 if (ret == 0) {
716 rn->flags |= RN_OPEN;
717 }
718
719 return -ret;
720 }
721
722 /* read some more from the file */
723 /* ARGSUSED5 */
724 static int
725 puffs_fuse_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
726 off_t offset, size_t *resid, const struct puffs_cred *pcr,
727 int ioflag)
728 {
729 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
730 struct puffs_node *pn = opc;
731 struct refusenode *rn = pn->pn_data;
732 struct fuse *fuse;
733 const char *path = PNPATH(pn);
734 int ret;
735
736 fuse = (struct fuse *)pu->pu_privdata;
737 if (fuse->op.read == NULL) {
738 return ENOSYS;
739 }
740
741 ret = (*fuse->op.read)(path, (char *)buf, *resid, offset,
742 &rn->file_info);
743
744 if (ret > 0) {
745 *resid -= ret;
746 ret = 0;
747 }
748
749 return -ret;
750 }
751
752 /* write to the file */
753 /* ARGSUSED0 */
754 static int
755 puffs_fuse_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
756 off_t offset, size_t *resid, const struct puffs_cred *pcr,
757 int ioflag)
758 {
759 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
760 struct puffs_node *pn = opc;
761 struct refusenode *rn = pn->pn_data;
762 struct fuse *fuse;
763 const char *path = PNPATH(pn);
764 int ret;
765
766 fuse = (struct fuse *)pu->pu_privdata;
767 if (fuse->op.write == NULL) {
768 return ENOSYS;
769 }
770
771 if (ioflag & PUFFS_IO_APPEND)
772 offset = pn->pn_va.va_size;
773
774 ret = (*fuse->op.write)(path, (char *)buf, *resid, offset,
775 &rn->file_info);
776
777 if (ret > 0) {
778 *resid -= ret;
779 if (offset + ret > pn->pn_va.va_size)
780 pn->pn_va.va_size = offset + ret;
781 ret = 0;
782 }
783
784 return -ret;
785 }
786
787
788 /* ARGSUSED3 */
789 static int
790 puffs_fuse_node_readdir(struct puffs_cc *pcc, void *opc,
791 struct dirent *dent, const struct puffs_cred *pcr, off_t *readoff,
792 size_t *reslen)
793 {
794 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
795 struct puffs_node *pn = opc;
796 struct refusenode *rn = pn->pn_data;
797 struct fuse *fuse;
798 const char *path = PNPATH(pn);
799 struct fuse_dirh deh;
800 int ret;
801
802 fuse = (struct fuse *)pu->pu_privdata;
803 if (fuse->op.readdir == NULL && fuse->op.getdir == NULL) {
804 return ENOSYS;
805 }
806
807 /* XXX: how to handle this??? */
808 if (*readoff != 0) {
809 return 0;
810 }
811
812 deh.dent = dent;
813 deh.reslen = *reslen;
814 deh.readoff = *readoff;
815
816 if (fuse->op.readdir)
817 ret = fuse->op.readdir(path, &deh, puffs_fuse_fill_dir,
818 *readoff, &rn->file_info);
819 else
820 ret = fuse->op.getdir(path, &deh, puffs_fuse_dirfil);
821 *reslen = deh.reslen;
822 *readoff = 1;
823
824 if (ret == 0) {
825 }
826
827 return -ret;
828 }
829
830 /* ARGSUSED2 */
831 static int
832 puffs_fuse_node_reclaim(struct puffs_cc *pcc, void *opc, pid_t pid)
833 {
834 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
835 struct puffs_node *pn = opc;
836 struct refusenode *rn = pn->pn_data;
837 struct fuse *fuse;
838 const char *path = PNPATH(pn);
839 int ret;
840
841 fuse = (struct fuse *)pu->pu_privdata;
842
843 if (rn->flags & RN_OPEN) {
844 if (pn->pn_va.va_type == VDIR) {
845 if (fuse->op.releasedir == NULL)
846 ret = -ENOSYS;
847
848 ret = fuse->op.releasedir(path, &rn->file_info);
849 } else {
850 if (fuse->op.release == NULL)
851 return ENOSYS;
852
853 ret = fuse->op.release(path, &rn->file_info);
854 }
855 }
856
857 nukern(pn);
858
859 /*
860 * value ignored by the kernel, but we might as well
861 * return something for debugging purposes
862 */
863 return -ret;
864 }
865
866 /* ARGSUSED1 */
867 static int
868 puffs_fuse_fs_unmount(struct puffs_cc *pcc, int flags, pid_t pid)
869 {
870 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
871 struct fuse *fuse;
872
873 fuse = (struct fuse *)pu->pu_privdata;
874 if (fuse->op.destroy == NULL) {
875 return 0;
876 }
877 (*fuse->op.destroy)(fuse);
878 return 0;
879 }
880
881 /* ARGSUSED0 */
882 static int
883 puffs_fuse_fs_sync(struct puffs_cc *pcc, int flags,
884 const struct puffs_cred *cr, pid_t pid)
885 {
886 return 0;
887 }
888
889 /* ARGSUSED2 */
890 static int
891 puffs_fuse_fs_statvfs(struct puffs_cc *pcc, struct statvfs *svfsb, pid_t pid)
892 {
893 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
894 struct fuse *fuse;
895 int ret;
896
897 fuse = (struct fuse *)pu->pu_privdata;
898 if (fuse->op.statfs == NULL) {
899 if ((ret = statvfs(PNPATH(pu->pu_pn_root), svfsb)) == -1) {
900 return errno;
901 }
902 } else {
903 ret = (*fuse->op.statfs)(PNPATH(pu->pu_pn_root), svfsb);
904 }
905
906 return ret;
907 }
908
909
910
911
912 /* End of puffs_fuse operations */
913
914 /* ARGSUSED3 */
915 int
916 fuse_main_real(int argc, char **argv, const struct fuse_operations *ops,
917 size_t size, void *userdata)
918 {
919 struct puffs_usermount *pu;
920 struct puffs_pathobj *po_root;
921 struct puffs_ops *pops;
922 struct statvfs svfsb;
923 struct fuse *fuse;
924 char name[64];
925 char *slash;
926 int ret;
927
928 /* initialise the puffs operations structure */
929 PUFFSOP_INIT(pops);
930
931 PUFFSOP_SET(pops, puffs_fuse, fs, sync);
932 PUFFSOP_SET(pops, puffs_fuse, fs, statvfs);
933 PUFFSOP_SET(pops, puffs_fuse, fs, unmount);
934
935 /*
936 * XXX: all of these don't possibly need to be
937 * unconditionally set
938 */
939 PUFFSOP_SET(pops, puffs_fuse, node, lookup);
940 PUFFSOP_SET(pops, puffs_fuse, node, getattr);
941 PUFFSOP_SET(pops, puffs_fuse, node, setattr);
942 PUFFSOP_SET(pops, puffs_fuse, node, readdir);
943 PUFFSOP_SET(pops, puffs_fuse, node, readlink);
944 PUFFSOP_SET(pops, puffs_fuse, node, mknod);
945 PUFFSOP_SET(pops, puffs_fuse, node, create);
946 PUFFSOP_SET(pops, puffs_fuse, node, remove);
947 PUFFSOP_SET(pops, puffs_fuse, node, mkdir);
948 PUFFSOP_SET(pops, puffs_fuse, node, rmdir);
949 PUFFSOP_SET(pops, puffs_fuse, node, symlink);
950 PUFFSOP_SET(pops, puffs_fuse, node, rename);
951 PUFFSOP_SET(pops, puffs_fuse, node, link);
952 PUFFSOP_SET(pops, puffs_fuse, node, open);
953 PUFFSOP_SET(pops, puffs_fuse, node, read);
954 PUFFSOP_SET(pops, puffs_fuse, node, write);
955 PUFFSOP_SET(pops, puffs_fuse, node, reclaim);
956
957 NEW(struct fuse, fuse, "fuse_main_real", exit(EXIT_FAILURE));
958
959 /* copy fuse ops to their own stucture */
960 (void) memcpy(&fuse->op, ops, sizeof(fuse->op));
961
962 fcon.fuse = fuse;
963 fcon.private_data = userdata;
964
965 /* whilst this (assigning the pu_privdata in the puffs
966 * usermount struct to be the fuse struct) might seem like
967 * we are chasing our tail here, the logic is as follows:
968 + the operation wrapper gets called with the puffs
969 calling conventions
970 + we need to fix up args first
971 + then call the fuse user-supplied operation
972 + then we fix up any values on return that we need to
973 + and fix up any nodes, etc
974 * so we need to be able to get at the fuse ops from within the
975 * puffs_usermount struct
976 */
977 if ((slash = strrchr(*argv, '/')) == NULL) {
978 slash = *argv;
979 } else {
980 slash += 1;
981 }
982 (void) snprintf(name, sizeof(name), "refuse:%s", slash);
983 pu = puffs_mount(pops, argv[argc - 1], MNT_NODEV | MNT_NOSUID,
984 name, fuse,
985 PUFFS_FLAG_BUILDPATH
986 | PUFFS_FLAG_OPDUMP
987 | PUFFS_KFLAG_NOCACHE,
988 0);
989 if (pu == NULL) {
990 err(EXIT_FAILURE, "puffs_mount");
991 }
992
993 fuse->pu = pu;
994 pu->pu_pn_root = puffs_pn_new(pu, NULL);
995 po_root = puffs_getrootpathobj(pu);
996 po_root->po_path = strdup("/");
997 po_root->po_len = 1;
998
999 if (fuse->op.init)
1000 fcon.private_data = fuse->op.init(NULL); /* XXX */
1001
1002 statvfs(argv[argc - 1], &svfsb); /* XXX - not really the correct dir */
1003 if (puffs_start(pu, pu->pu_pn_root, &svfsb) == -1) {
1004 err(EXIT_FAILURE, "puffs_start");
1005 }
1006
1007 ret = puffs_mainloop(fuse->pu, PUFFSLOOP_NODAEMON);
1008
1009 (void) free(po_root->po_path);
1010 FREE(fuse);
1011 return ret;
1012 }
1013
1014 /* ARGSUSED0 */
1015 int
1016 fuse_opt_parse(struct fuse_args *args, void *data,
1017 const struct fuse_opt *opts, fuse_opt_proc_t proc)
1018 {
1019 return 0;
1020 }
1021
1022 /* XXX: threads */
1023 struct fuse_context *
1024 fuse_get_context()
1025 {
1026
1027 return &fcon;
1028 }
1029
1030 void
1031 fuse_exit(struct fuse *f)
1032 {
1033
1034 puffs_exit(f->pu, 1);
1035 }
1036