refuse.c revision 1.51 1 /* $NetBSD: refuse.c,v 1.51 2007/05/03 21:02:54 agc Exp $ */
2
3 /*
4 * Copyright 2007 Alistair Crooks. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote
15 * products derived from this software without specific prior written
16 * permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
24 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 #if !defined(lint)
33 __RCSID("$NetBSD: refuse.c,v 1.51 2007/05/03 21:02:54 agc Exp $");
34 #endif /* !lint */
35
36 #include <assert.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <fuse.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 struct fuse_chan {
70 const char *dir;
71 struct fuse_args *args;
72 struct puffs_usermount *pu;
73 };
74
75 /* this is the private fuse structure */
76 struct fuse {
77 struct fuse_chan *fc; /* fuse channel pointer */
78 struct fuse_operations op; /* switch table of operations */
79 int compat; /* compat level -
80 * not used in puffs_fuse */
81 struct node **name_table;
82 size_t name_table_size;
83 struct node **id_table;
84 size_t id_table_size;
85 fuse_ino_t ctr;
86 unsigned int generation;
87 unsigned int hidectr;
88 pthread_mutex_t lock;
89 pthread_rwlock_t tree_lock;
90 void *user_data;
91 struct fuse_config conf;
92 int intr_installed;
93 };
94
95 struct puffs_fuse_dirh {
96 void *dbuf;
97 struct dirent *d;
98
99 size_t reslen;
100 size_t bufsize;
101 };
102
103 struct refusenode {
104 struct fuse_file_info file_info;
105 struct puffs_fuse_dirh dirh;
106 int opencount;
107 int flags;
108 };
109 #define RN_ROOT 0x01
110 #define RN_OPEN 0x02 /* XXX: could just use opencount */
111
112 static int fuse_setattr(struct fuse *, struct puffs_node *,
113 const char *, const struct vattr *);
114
115 static struct puffs_node *
116 newrn(struct puffs_usermount *pu)
117 {
118 struct puffs_node *pn;
119 struct refusenode *rn;
120
121 NEW(struct refusenode, rn, "newrn", exit(EXIT_FAILURE));
122 pn = puffs_pn_new(pu, rn);
123
124 return pn;
125 }
126
127 static void
128 nukern(struct puffs_node *pn)
129 {
130 struct refusenode *rn = pn->pn_data;
131
132 free(rn->dirh.dbuf);
133 free(rn);
134 puffs_pn_put(pn);
135 }
136
137 static ino_t fakeino = 3;
138
139 /*
140 * XXX: do this otherwise if/when we grow thread support
141 *
142 * XXX2: does not consistently supply uid, gid or pid currently
143 */
144 static struct fuse_context fcon;
145
146 #define DIR_CHUNKSIZE 4096
147 static int
148 fill_dirbuf(struct puffs_fuse_dirh *dh, const char *name, ino_t dino,
149 uint8_t dtype)
150 {
151
152 /* initial? */
153 if (dh->bufsize == 0) {
154 dh->dbuf = malloc(DIR_CHUNKSIZE);
155 if (dh->dbuf == NULL)
156 err(1, "fill_dirbuf");
157 dh->d = dh->dbuf;
158 dh->reslen = dh->bufsize = DIR_CHUNKSIZE;
159 }
160
161 if (puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen))
162 return 0;
163
164 /* try to increase buffer space */
165 dh->dbuf = realloc(dh->dbuf, dh->bufsize + DIR_CHUNKSIZE);
166 if (dh->dbuf == NULL)
167 err(1, "fill_dirbuf realloc");
168 dh->d = (void *)((uint8_t *)dh->dbuf + (dh->bufsize - dh->reslen));
169 dh->reslen += DIR_CHUNKSIZE;
170 dh->bufsize += DIR_CHUNKSIZE;
171
172 return !puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen);
173 }
174
175 /* ARGSUSED3 */
176 /* XXX: I have no idea how "off" is supposed to be used */
177 static int
178 puffs_fuse_fill_dir(void *buf, const char *name,
179 const struct stat *stbuf, off_t off)
180 {
181 struct puffs_fuse_dirh *deh = buf;
182 ino_t dino;
183 uint8_t dtype;
184
185 if (stbuf == NULL) {
186 dtype = DT_UNKNOWN;
187 dino = fakeino++;
188 } else {
189 dtype = puffs_vtype2dt(puffs_mode2vt(stbuf->st_mode));
190 dino = stbuf->st_ino;
191
192 /*
193 * Some FUSE file systems like to always use 0 as the
194 * inode number. Our readdir() doesn't like to show
195 * directory entries with inode number 0 ==> workaround.
196 */
197 if (dino == 0)
198 dino = fakeino++;
199 }
200
201 return fill_dirbuf(deh, name, dino, dtype);
202 }
203
204 static int
205 puffs_fuse_dirfil(fuse_dirh_t h, const char *name, int type, ino_t ino)
206 {
207 ino_t dino;
208 int dtype;
209
210 if (type == 0)
211 dtype = DT_UNKNOWN;
212 else
213 dtype = type;
214
215 if (ino)
216 dino = ino;
217 else
218 dino = fakeino++;
219
220 return fill_dirbuf(h, name, dino, dtype);
221 }
222
223 static struct fuse_args *
224 deep_copy_args(int argc, char **argv)
225 {
226 struct fuse_args *ap;
227 int i;
228
229 NEW(struct fuse_args, ap, "deep_copy_args", return NULL);
230 /* deep copy args structure into channel args */
231 ap->allocated = ((argc / 10) + 1) * 10;
232 NEWARRAY(char *, ap->argv, ap->allocated, "deep_copy_args",
233 return NULL);
234 for (i = 0 ; i < argc ; i++) {
235 ap->argv[i] = strdup(argv[i]);
236 }
237 return ap;
238 }
239
240 static void
241 free_args(struct fuse_args *ap)
242 {
243 int i;
244
245 for (i = 0 ; i < ap->argc ; i++) {
246 free(ap->argv[i]);
247 }
248 free(ap);
249 }
250
251 /* this function exposes struct fuse to userland */
252 struct fuse *
253 fuse_setup(int argc, char **argv, const struct fuse_operations *ops,
254 size_t size, char **mountpoint, int *multithreaded, int *fd)
255 {
256 struct fuse_chan *fc;
257 struct fuse_args *args;
258 struct fuse *fuse;
259 char name[64];
260 char *slash;
261
262 /* whilst this (assigning the pu_privdata in the puffs
263 * usermount struct to be the fuse struct) might seem like
264 * we are chasing our tail here, the logic is as follows:
265 + the operation wrapper gets called with the puffs
266 calling conventions
267 + we need to fix up args first
268 + then call the fuse user-supplied operation
269 + then we fix up any values on return that we need to
270 + and fix up any nodes, etc
271 * so we need to be able to get at the fuse ops from within the
272 * puffs_usermount struct
273 */
274 if (argv == NULL || *argv == NULL) {
275 (void) strlcpy(name, "refuse", sizeof(name));
276 } else {
277 if ((slash = strrchr(*argv, '/')) == NULL) {
278 slash = *argv;
279 } else {
280 slash += 1;
281 }
282 (void) snprintf(name, sizeof(name), "refuse:%s", slash);
283 }
284
285 /* stuff name into fuse_args */
286 args = deep_copy_args(argc, argv);
287 if (args->argc > 0) {
288 FREE(args->argv[0]);
289 }
290 args->argv[0] = strdup(name);
291
292 fc = fuse_mount(*mountpoint = argv[argc - 1], args);
293 fuse = fuse_new(fc, args, ops, size, NULL);
294
295 free_args(args);
296
297 /* XXX - wait for puffs to become multi-threaded */
298 if (multithreaded) {
299 *multithreaded = 0;
300 }
301
302 /* XXX - this is unused */
303 if (fd) {
304 *fd = 0;
305 }
306
307 return fuse;
308 }
309
310 #define FUSE_ERR_UNLINK(fuse, file) if (fuse->op.unlink) fuse->op.unlink(file)
311 #define FUSE_ERR_RMDIR(fuse, dir) if (fuse->op.rmdir) fuse->op.rmdir(dir)
312
313 /* ARGSUSED1 */
314 static int
315 fuse_getattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
316 struct vattr *va)
317 {
318 struct stat st;
319 int ret;
320
321 if (fuse->op.getattr == NULL) {
322 return ENOSYS;
323 }
324
325 /* wrap up return code */
326 ret = (*fuse->op.getattr)(path, &st);
327
328 if (ret == 0) {
329 puffs_stat2vattr(va, &st);
330 }
331
332 return -ret;
333 }
334
335 static int
336 fuse_setattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
337 const struct vattr *va)
338 {
339 struct refusenode *rn = pn->pn_data;
340 mode_t mode;
341 uid_t uid;
342 gid_t gid;
343 int error, ret;
344
345 error = 0;
346
347 mode = va->va_mode;
348 uid = va->va_uid;
349 gid = va->va_gid;
350
351 if (mode != (mode_t)PUFFS_VNOVAL) {
352 ret = 0;
353
354 if (fuse->op.chmod == NULL) {
355 error = -ENOSYS;
356 } else {
357 ret = fuse->op.chmod(path, mode);
358 if (ret)
359 error = ret;
360 }
361 }
362 if (uid != (uid_t)PUFFS_VNOVAL || gid != (gid_t)PUFFS_VNOVAL) {
363 ret = 0;
364
365 if (fuse->op.chown == NULL) {
366 error = -ENOSYS;
367 } else {
368 ret = fuse->op.chown(path, uid, gid);
369 if (ret)
370 error = ret;
371 }
372 }
373 if (va->va_atime.tv_sec != (time_t)PUFFS_VNOVAL
374 || va->va_mtime.tv_sec != (long)PUFFS_VNOVAL) {
375 ret = 0;
376
377 if (fuse->op.utimens) {
378 struct timespec tv[2];
379
380 tv[0].tv_sec = va->va_atime.tv_sec;
381 tv[0].tv_nsec = va->va_atime.tv_nsec;
382 tv[1].tv_sec = va->va_mtime.tv_sec;
383 tv[1].tv_nsec = va->va_mtime.tv_nsec;
384
385 ret = fuse->op.utimens(path, tv);
386 } else if (fuse->op.utime) {
387 struct utimbuf timbuf;
388
389 timbuf.actime = va->va_atime.tv_sec;
390 timbuf.modtime = va->va_mtime.tv_sec;
391
392 ret = fuse->op.utime(path, &timbuf);
393 } else {
394 error = -ENOSYS;
395 }
396
397 if (ret)
398 error = ret;
399 }
400 if (va->va_size != (u_quad_t)PUFFS_VNOVAL) {
401 ret = 0;
402
403 if (fuse->op.truncate) {
404 ret = fuse->op.truncate(path, (off_t)va->va_size);
405 } else if (fuse->op.ftruncate) {
406 ret = fuse->op.ftruncate(path, (off_t)va->va_size,
407 &rn->file_info);
408 } else {
409 error = -ENOSYS;
410 }
411
412 if (ret)
413 error = ret;
414 }
415 /* XXX: no reflection with reality */
416 puffs_setvattr(&pn->pn_va, va);
417
418 return -error;
419
420 }
421
422 static int
423 fuse_newnode(struct puffs_usermount *pu, const char *path,
424 const struct vattr *va, struct fuse_file_info *fi, void **newnode)
425 {
426 struct vattr newva;
427 struct fuse *fuse;
428 struct puffs_node *pn;
429 struct refusenode *rn;
430
431 fuse = puffs_getspecific(pu);
432
433 /* fix up nodes */
434 pn = newrn(pu);
435 if (pn == NULL) {
436 if (va->va_type == VDIR) {
437 FUSE_ERR_RMDIR(fuse, path);
438 } else {
439 FUSE_ERR_UNLINK(fuse, path);
440 }
441 return ENOMEM;
442 }
443 fuse_setattr(fuse, pn, path, va);
444 if (fuse_getattr(fuse, pn, path, &newva) == 0)
445 puffs_setvattr(&pn->pn_va, &newva);
446
447 rn = pn->pn_data;
448 if (fi)
449 memcpy(&rn->file_info, fi, sizeof(struct fuse_file_info));
450
451 *newnode = pn;
452
453 return 0;
454 }
455
456
457 /* operation wrappers start here */
458
459 /* lookup the path */
460 /* ARGSUSED1 */
461 static int
462 puffs_fuse_node_lookup(struct puffs_cc *pcc, void *opc, void **newnode,
463 enum vtype *newtype, voff_t *newsize, dev_t *newrdev,
464 const struct puffs_cn *pcn)
465 {
466 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
467 struct puffs_node *pn_res;
468 struct stat st;
469 struct fuse *fuse;
470 const char *path = PCNPATH(pcn);
471 int ret;
472
473 fuse = puffs_getspecific(pu);
474 ret = fuse->op.getattr(path, &st);
475
476 if (ret != 0) {
477 return -ret;
478 }
479
480 /* XXX: fiXXXme unconst */
481 pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp,
482 __UNCONST(&pcn->pcn_po_full));
483 if (pn_res == NULL) {
484 pn_res = newrn(pu);
485 if (pn_res == NULL)
486 return errno;
487 puffs_stat2vattr(&pn_res->pn_va, &st);
488 }
489
490 *newnode = pn_res;
491 *newtype = pn_res->pn_va.va_type;
492 *newsize = pn_res->pn_va.va_size;
493 *newrdev = pn_res->pn_va.va_rdev;
494
495 return 0;
496 }
497
498 /* get attributes for the path name */
499 /* ARGSUSED3 */
500 static int
501 puffs_fuse_node_getattr(struct puffs_cc *pcc, void *opc, struct vattr *va,
502 const struct puffs_cred *pcr, pid_t pid)
503 {
504 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
505 struct puffs_node *pn = opc;
506 struct fuse *fuse;
507 const char *path = PNPATH(pn);
508
509 fuse = puffs_getspecific(pu);
510 return fuse_getattr(fuse, pn, path, va);
511 }
512
513 /* read the contents of the symbolic link */
514 /* ARGSUSED2 */
515 static int
516 puffs_fuse_node_readlink(struct puffs_cc *pcc, void *opc,
517 const struct puffs_cred *cred, char *linkname, size_t *linklen)
518 {
519 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
520 struct puffs_node *pn = opc;
521 struct fuse *fuse;
522 const char *path = PNPATH(pn), *p;
523 int ret;
524
525 fuse = puffs_getspecific(pu);
526 if (fuse->op.readlink == NULL) {
527 return ENOSYS;
528 }
529
530 /* wrap up return code */
531 ret = (*fuse->op.readlink)(path, linkname, *linklen);
532
533 if (ret == 0) {
534 p = memchr(linkname, '\0', *linklen);
535 if (!p)
536 return EINVAL;
537
538 *linklen = p - linkname;
539 }
540
541 return -ret;
542 }
543
544 /* make the special node */
545 /* ARGSUSED1 */
546 static int
547 puffs_fuse_node_mknod(struct puffs_cc *pcc, void *opc, void **newnode,
548 const struct puffs_cn *pcn, const struct vattr *va)
549 {
550 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
551 struct fuse *fuse;
552 mode_t mode;
553 const char *path = PCNPATH(pcn);
554 int ret;
555
556 fuse = puffs_getspecific(pu);
557 if (fuse->op.mknod == NULL) {
558 return ENOSYS;
559 }
560
561 /* wrap up return code */
562 mode = puffs_addvtype2mode(va->va_mode, va->va_type);
563 ret = (*fuse->op.mknod)(path, mode, va->va_rdev);
564
565 if (ret == 0) {
566 ret = fuse_newnode(pu, path, va, NULL, newnode);
567 }
568
569 return -ret;
570 }
571
572 /* make a directory */
573 /* ARGSUSED1 */
574 static int
575 puffs_fuse_node_mkdir(struct puffs_cc *pcc, void *opc, void **newnode,
576 const struct puffs_cn *pcn, const struct vattr *va)
577 {
578 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
579 struct fuse *fuse;
580 mode_t mode = va->va_mode;
581 const char *path = PCNPATH(pcn);
582 int ret;
583
584 fuse = puffs_getspecific(pu);
585 if (fuse->op.mkdir == NULL) {
586 return ENOSYS;
587 }
588
589 /* wrap up return code */
590 ret = (*fuse->op.mkdir)(path, mode);
591
592 if (ret == 0) {
593 ret = fuse_newnode(pu, path, va, NULL, newnode);
594 }
595
596 return -ret;
597 }
598
599 /*
600 * create a regular file
601 *
602 * since linux/fuse sports using mknod for creating regular files
603 * instead of having a separate call for it in some versions, if
604 * we don't have create, just jump to op->mknod.
605 */
606 /*ARGSUSED1*/
607 static int
608 puffs_fuse_node_create(struct puffs_cc *pcc, void *opc, void **newnode,
609 const struct puffs_cn *pcn, const struct vattr *va)
610 {
611 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
612 struct fuse *fuse;
613 struct fuse_file_info fi;
614 mode_t mode = va->va_mode;
615 const char *path = PCNPATH(pcn);
616 int ret, created;
617
618 fuse = puffs_getspecific(pu);
619
620 created = 0;
621 if (fuse->op.create) {
622 ret = fuse->op.create(path, mode, &fi);
623 if (ret == 0)
624 created = 1;
625
626 } else if (fuse->op.mknod) {
627 fcon.uid = va->va_uid; /*XXX*/
628 fcon.gid = va->va_gid; /*XXX*/
629
630 ret = fuse->op.mknod(path, mode | S_IFREG, 0);
631
632 } else {
633 ret = -ENOSYS;
634 }
635
636 if (ret == 0) {
637 ret = fuse_newnode(pu, path, va, &fi, newnode);
638
639 /* sweet.. create also open the file */
640 if (created) {
641 struct puffs_node *pn;
642 struct refusenode *rn;
643
644 pn = *newnode;
645 rn = pn->pn_data;
646 rn->flags |= RN_OPEN;
647 rn->opencount++;
648 }
649 }
650
651 return -ret;
652 }
653
654 /* remove the directory entry */
655 /* ARGSUSED1 */
656 static int
657 puffs_fuse_node_remove(struct puffs_cc *pcc, void *opc, void *targ,
658 const struct puffs_cn *pcn)
659 {
660 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
661 struct puffs_node *pn_targ = targ;
662 struct fuse *fuse;
663 const char *path = PNPATH(pn_targ);
664 int ret;
665
666 fuse = puffs_getspecific(pu);
667 if (fuse->op.unlink == NULL) {
668 return ENOSYS;
669 }
670
671 /* wrap up return code */
672 ret = (*fuse->op.unlink)(path);
673
674 return -ret;
675 }
676
677 /* remove the directory */
678 /* ARGSUSED1 */
679 static int
680 puffs_fuse_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ,
681 const struct puffs_cn *pcn)
682 {
683 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
684 struct puffs_node *pn_targ = targ;
685 struct fuse *fuse;
686 const char *path = PNPATH(pn_targ);
687 int ret;
688
689 fuse = puffs_getspecific(pu);
690 if (fuse->op.rmdir == NULL) {
691 return ENOSYS;
692 }
693
694 /* wrap up return code */
695 ret = (*fuse->op.rmdir)(path);
696
697 return -ret;
698 }
699
700 /* create a symbolic link */
701 /* ARGSUSED1 */
702 static int
703 puffs_fuse_node_symlink(struct puffs_cc *pcc, void *opc, void **newnode,
704 const struct puffs_cn *pcn_src, const struct vattr *va,
705 const char *link_target)
706 {
707 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
708 struct fuse *fuse;
709 const char *path = PCNPATH(pcn_src);
710 int ret;
711
712 fuse = puffs_getspecific(pu);
713 if (fuse->op.symlink == NULL) {
714 return ENOSYS;
715 }
716
717 /* wrap up return code */
718 ret = fuse->op.symlink(link_target, path);
719
720 if (ret == 0) {
721 ret = fuse_newnode(pu, path, va, NULL, newnode);
722 }
723
724 return -ret;
725 }
726
727 /* rename a directory entry */
728 /* ARGSUSED1 */
729 static int
730 puffs_fuse_node_rename(struct puffs_cc *pcc, void *opc, void *src,
731 const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
732 const struct puffs_cn *pcn_targ)
733 {
734 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
735 struct fuse *fuse;
736 const char *path_src = PCNPATH(pcn_src);
737 const char *path_dest = PCNPATH(pcn_targ);
738 int ret;
739
740 fuse = puffs_getspecific(pu);
741 if (fuse->op.rename == NULL) {
742 return ENOSYS;
743 }
744
745 ret = fuse->op.rename(path_src, path_dest);
746
747 if (ret == 0) {
748 }
749
750 return -ret;
751 }
752
753 /* create a link in the file system */
754 /* ARGSUSED1 */
755 static int
756 puffs_fuse_node_link(struct puffs_cc *pcc, void *opc, void *targ,
757 const struct puffs_cn *pcn)
758 {
759 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
760 struct puffs_node *pn = targ;
761 struct fuse *fuse;
762 int ret;
763
764 fuse = puffs_getspecific(pu);
765 if (fuse->op.link == NULL) {
766 return ENOSYS;
767 }
768
769 /* wrap up return code */
770 ret = (*fuse->op.link)(PNPATH(pn), PCNPATH(pcn));
771
772 return -ret;
773 }
774
775 /*
776 * fuse's regular interface provides chmod(), chown(), utimes()
777 * and truncate() + some variations, so try to fit the square block
778 * in the circle hole and the circle block .... something like that
779 */
780 /* ARGSUSED3 */
781 static int
782 puffs_fuse_node_setattr(struct puffs_cc *pcc, void *opc,
783 const struct vattr *va, const struct puffs_cred *pcr, pid_t pid)
784 {
785 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
786 struct puffs_node *pn = opc;
787 struct fuse *fuse;
788 const char *path = PNPATH(pn);
789
790 fuse = puffs_getspecific(pu);
791
792 return fuse_setattr(fuse, pn, path, va);
793 }
794
795 /* ARGSUSED2 */
796 static int
797 puffs_fuse_node_open(struct puffs_cc *pcc, void *opc, int mode,
798 const struct puffs_cred *cred, pid_t pid)
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_file_info *fi = &rn->file_info;
804 struct fuse *fuse;
805 const char *path = PNPATH(pn);
806
807 fuse = puffs_getspecific(pu);
808
809 /* if open, don't open again, lest risk nuking file private info */
810 if (rn->flags & RN_OPEN) {
811 rn->opencount++;
812 return 0;
813 }
814
815 /* OFLAGS(), need to convert FREAD/FWRITE to O_RD/WR */
816 fi->flags = (mode & ~(O_CREAT | O_EXCL | O_TRUNC)) - 1;
817
818 if (pn->pn_va.va_type == VDIR) {
819 if (fuse->op.opendir)
820 fuse->op.opendir(path, fi);
821 } else {
822 if (fuse->op.open)
823 fuse->op.open(path, fi);
824 }
825
826 rn->flags |= RN_OPEN;
827 rn->opencount++;
828
829 return 0;
830 }
831
832 /* ARGSUSED2 */
833 static int
834 puffs_fuse_node_close(struct puffs_cc *pcc, void *opc, int fflag,
835 const struct puffs_cred *pcr, pid_t pid)
836 {
837 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
838 struct puffs_node *pn = opc;
839 struct refusenode *rn = pn->pn_data;
840 struct fuse *fuse;
841 struct fuse_file_info *fi;
842 const char *path = PNPATH(pn);
843 int ret;
844
845 fuse = puffs_getspecific(pu);
846 fi = &rn->file_info;
847 ret = 0;
848
849 if (rn->flags & RN_OPEN) {
850 if (pn->pn_va.va_type == VDIR) {
851 if (fuse->op.releasedir)
852 ret = fuse->op.releasedir(path, fi);
853 } else {
854 if (fuse->op.release)
855 ret = fuse->op.release(path, fi);
856 }
857 }
858 rn->flags &= ~RN_OPEN;
859 rn->opencount--;
860
861 return ret;
862 }
863
864 /* read some more from the file */
865 /* ARGSUSED5 */
866 static int
867 puffs_fuse_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
868 off_t offset, size_t *resid, const struct puffs_cred *pcr,
869 int ioflag)
870 {
871 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
872 struct puffs_node *pn = opc;
873 struct refusenode *rn = pn->pn_data;
874 struct fuse *fuse;
875 const char *path = PNPATH(pn);
876 size_t maxread;
877 int ret;
878
879 fuse = puffs_getspecific(pu);
880 if (fuse->op.read == NULL) {
881 return ENOSYS;
882 }
883
884 maxread = *resid;
885 if (maxread > pn->pn_va.va_size - offset) {
886 /*LINTED*/
887 maxread = pn->pn_va.va_size - offset;
888 }
889 if (maxread == 0)
890 return 0;
891
892 ret = (*fuse->op.read)(path, (char *)buf, maxread, offset,
893 &rn->file_info);
894
895 if (ret > 0) {
896 *resid -= ret;
897 ret = 0;
898 }
899
900 return -ret;
901 }
902
903 /* write to the file */
904 /* ARGSUSED0 */
905 static int
906 puffs_fuse_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
907 off_t offset, size_t *resid, const struct puffs_cred *pcr,
908 int ioflag)
909 {
910 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
911 struct puffs_node *pn = opc;
912 struct refusenode *rn = pn->pn_data;
913 struct fuse *fuse;
914 const char *path = PNPATH(pn);
915 int ret;
916
917 fuse = puffs_getspecific(pu);
918 if (fuse->op.write == NULL) {
919 return ENOSYS;
920 }
921
922 if (ioflag & PUFFS_IO_APPEND)
923 offset = pn->pn_va.va_size;
924
925 ret = (*fuse->op.write)(path, (char *)buf, *resid, offset,
926 &rn->file_info);
927
928 if (ret > 0) {
929 if (offset + ret > pn->pn_va.va_size)
930 pn->pn_va.va_size = offset + ret;
931 *resid -= ret;
932 ret = 0;
933 }
934
935 return -ret;
936 }
937
938
939 /* ARGSUSED3 */
940 static int
941 puffs_fuse_node_readdir(struct puffs_cc *pcc, void *opc, struct dirent *dent,
942 off_t *readoff, size_t *reslen, const struct puffs_cred *pcr,
943 int *eofflag, off_t *cookies, size_t *ncookies)
944 {
945 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
946 struct puffs_node *pn = opc;
947 struct refusenode *rn = pn->pn_data;
948 struct puffs_fuse_dirh *dirh;
949 struct fuse *fuse;
950 struct dirent *fromdent;
951 const char *path = PNPATH(pn);
952 int ret;
953
954 fuse = puffs_getspecific(pu);
955 if (fuse->op.readdir == NULL && fuse->op.getdir == NULL) {
956 return ENOSYS;
957 }
958
959 if (pn->pn_va.va_type != VDIR)
960 return ENOTDIR;
961
962 dirh = &rn->dirh;
963
964 /*
965 * if we are starting from the beginning, slurp entire directory
966 * into our buffers
967 */
968 if (*readoff == 0) {
969 /* free old buffers */
970 free(dirh->dbuf);
971 memset(dirh, 0, sizeof(struct puffs_fuse_dirh));
972
973 if (fuse->op.readdir)
974 ret = fuse->op.readdir(path, dirh, puffs_fuse_fill_dir,
975 0, &rn->file_info);
976 else
977 ret = fuse->op.getdir(path, dirh, puffs_fuse_dirfil);
978 if (ret)
979 return -ret;
980 }
981
982 /* now, stuff results into the kernel buffers */
983 while (*readoff < dirh->bufsize - dirh->reslen) {
984 /*LINTED*/
985 fromdent = (struct dirent *)((uint8_t *)dirh->dbuf + *readoff);
986
987 if (*reslen < _DIRENT_SIZE(fromdent))
988 break;
989
990 memcpy(dent, fromdent, _DIRENT_SIZE(fromdent));
991 *readoff += _DIRENT_SIZE(fromdent);
992 *reslen -= _DIRENT_SIZE(fromdent);
993
994 dent = _DIRENT_NEXT(dent);
995 }
996
997 return 0;
998 }
999
1000 /* ARGSUSED */
1001 static int
1002 puffs_fuse_node_reclaim(struct puffs_cc *pcc, void *opc, pid_t pid)
1003 {
1004 struct puffs_node *pn = opc;
1005
1006 nukern(pn);
1007
1008 return 0;
1009 }
1010
1011 /* ARGSUSED1 */
1012 static int
1013 puffs_fuse_fs_unmount(struct puffs_cc *pcc, int flags, pid_t pid)
1014 {
1015 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
1016 struct fuse *fuse;
1017
1018 fuse = puffs_getspecific(pu);
1019 if (fuse->op.destroy == NULL) {
1020 return 0;
1021 }
1022 (*fuse->op.destroy)(fuse);
1023 return 0;
1024 }
1025
1026 /* ARGSUSED0 */
1027 static int
1028 puffs_fuse_fs_sync(struct puffs_cc *pcc, int flags,
1029 const struct puffs_cred *cr, pid_t pid)
1030 {
1031 return 0;
1032 }
1033
1034 /* ARGSUSED2 */
1035 static int
1036 puffs_fuse_fs_statvfs(struct puffs_cc *pcc, struct statvfs *svfsb, pid_t pid)
1037 {
1038 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
1039 struct fuse *fuse;
1040 int ret;
1041
1042 fuse = puffs_getspecific(pu);
1043 if (fuse->op.statfs == NULL) {
1044 if ((ret = statvfs(PNPATH(puffs_getroot(pu)), svfsb)) == -1) {
1045 return errno;
1046 }
1047 } else {
1048 ret = fuse->op.statfs(PNPATH(puffs_getroot(pu)), svfsb);
1049 }
1050
1051 return ret;
1052 }
1053
1054
1055 /* End of puffs_fuse operations */
1056 /* ARGSUSED3 */
1057 int
1058 fuse_main_real(int argc, char **argv, const struct fuse_operations *ops,
1059 size_t size, void *userdata)
1060 {
1061 struct fuse *fuse;
1062 char *mountpoint;
1063 int multithreaded;
1064 int fd;
1065
1066 fuse = fuse_setup(argc, argv, ops, size, &mountpoint, &multithreaded,
1067 &fd);
1068
1069 return fuse_loop(fuse);
1070 }
1071
1072 /*
1073 * XXX: just defer the operation until fuse_new() when we have more
1074 * info on our hands. The real beef is why's this separate in fuse in
1075 * the first place?
1076 */
1077 /* ARGSUSED1 */
1078 struct fuse_chan *
1079 fuse_mount(const char *dir, struct fuse_args *args)
1080 {
1081 struct fuse_chan *fc;
1082
1083 NEW(struct fuse_chan, fc, "fuse_mount", exit(EXIT_FAILURE));
1084
1085 fc->dir = strdup(dir);
1086
1087 /*
1088 * we need to deep copy the args struct - some fuse file
1089 * systems "clean up" the argument vector for "security
1090 * reasons"
1091 */
1092 fc->args = deep_copy_args(args->argc, args->argv);
1093
1094 return fc;
1095 }
1096
1097 /* ARGSUSED1 */
1098 struct fuse *
1099 fuse_new(struct fuse_chan *fc, struct fuse_args *args,
1100 const struct fuse_operations *ops, size_t size, void *userdata)
1101 {
1102 struct puffs_usermount *pu;
1103 struct puffs_pathobj *po_root;
1104 struct puffs_node *pn_root;
1105 struct puffs_ops *pops;
1106 struct refusenode *rn_root;
1107 struct statvfs svfsb;
1108 struct stat st;
1109 struct fuse *fuse;
1110
1111 NEW(struct fuse, fuse, "fuse_new", exit(EXIT_FAILURE));
1112
1113 /* copy fuse ops to their own stucture */
1114 (void) memcpy(&fuse->op, ops, sizeof(fuse->op));
1115
1116 fcon.fuse = fuse;
1117 fcon.private_data = userdata;
1118
1119 fuse->fc = fc;
1120
1121 /* initialise the puffs operations structure */
1122 PUFFSOP_INIT(pops);
1123
1124 PUFFSOP_SET(pops, puffs_fuse, fs, sync);
1125 PUFFSOP_SET(pops, puffs_fuse, fs, statvfs);
1126 PUFFSOP_SET(pops, puffs_fuse, fs, unmount);
1127
1128 /*
1129 * XXX: all of these don't possibly need to be
1130 * unconditionally set
1131 */
1132 PUFFSOP_SET(pops, puffs_fuse, node, lookup);
1133 PUFFSOP_SET(pops, puffs_fuse, node, getattr);
1134 PUFFSOP_SET(pops, puffs_fuse, node, setattr);
1135 PUFFSOP_SET(pops, puffs_fuse, node, readdir);
1136 PUFFSOP_SET(pops, puffs_fuse, node, readlink);
1137 PUFFSOP_SET(pops, puffs_fuse, node, mknod);
1138 PUFFSOP_SET(pops, puffs_fuse, node, create);
1139 PUFFSOP_SET(pops, puffs_fuse, node, remove);
1140 PUFFSOP_SET(pops, puffs_fuse, node, mkdir);
1141 PUFFSOP_SET(pops, puffs_fuse, node, rmdir);
1142 PUFFSOP_SET(pops, puffs_fuse, node, symlink);
1143 PUFFSOP_SET(pops, puffs_fuse, node, rename);
1144 PUFFSOP_SET(pops, puffs_fuse, node, link);
1145 PUFFSOP_SET(pops, puffs_fuse, node, open);
1146 PUFFSOP_SET(pops, puffs_fuse, node, close);
1147 PUFFSOP_SET(pops, puffs_fuse, node, read);
1148 PUFFSOP_SET(pops, puffs_fuse, node, write);
1149 PUFFSOP_SET(pops, puffs_fuse, node, reclaim);
1150
1151 pu = puffs_mount(pops, fc->dir, MNT_NODEV | MNT_NOSUID,
1152 args->argv[0], fuse,
1153 PUFFS_FLAG_BUILDPATH
1154 | PUFFS_FLAG_HASHPATH
1155 | PUFFS_FLAG_OPDUMP
1156 | PUFFS_KFLAG_NOCACHE);
1157 if (pu == NULL) {
1158 err(EXIT_FAILURE, "puffs_mount");
1159 }
1160 fc->pu = pu;
1161
1162 pn_root = newrn(pu);
1163 puffs_setroot(pu, pn_root);
1164 rn_root = pn_root->pn_data;
1165 rn_root->flags |= RN_ROOT;
1166
1167 po_root = puffs_getrootpathobj(pu);
1168 po_root->po_path = strdup("/");
1169 po_root->po_len = 1;
1170
1171 /* sane defaults */
1172 puffs_vattr_null(&pn_root->pn_va);
1173 pn_root->pn_va.va_type = VDIR;
1174 pn_root->pn_va.va_mode = 0755;
1175 if (fuse->op.getattr)
1176 if (fuse->op.getattr(po_root->po_path, &st) == 0)
1177 puffs_stat2vattr(&pn_root->pn_va, &st);
1178 assert(pn_root->pn_va.va_type == VDIR);
1179
1180 if (fuse->op.init)
1181 fcon.private_data = fuse->op.init(NULL); /* XXX */
1182
1183 puffs_zerostatvfs(&svfsb);
1184 if (puffs_start(pu, pn_root, &svfsb) == -1) {
1185 err(EXIT_FAILURE, "puffs_start");
1186 }
1187
1188 return fuse;
1189 }
1190
1191 int
1192 fuse_loop(struct fuse *fuse)
1193 {
1194
1195 return puffs_mainloop(fuse->fc->pu, PUFFSLOOP_NODAEMON);
1196 }
1197
1198 void
1199 fuse_destroy(struct fuse *fuse)
1200 {
1201
1202
1203 /* XXXXXX: missing stuff */
1204 FREE(fuse);
1205 }
1206
1207 /* XXX: threads */
1208 struct fuse_context *
1209 fuse_get_context()
1210 {
1211
1212 return &fcon;
1213 }
1214
1215 void
1216 fuse_exit(struct fuse *fuse)
1217 {
1218
1219 puffs_exit(fuse->fc->pu, 1);
1220 }
1221
1222 /*
1223 * XXX: obviously not the most perfect of functions, but needs some
1224 * puffs tweaking for a better tomorrow
1225 */
1226 /*ARGSUSED*/
1227 void
1228 fuse_unmount(const char *mp, struct fuse_chan *fc)
1229 {
1230
1231 puffs_exit(fc->pu, 1);
1232 }
1233
1234 /*ARGSUSED*/
1235 void
1236 fuse_unmount_compat22(const char *mp)
1237 {
1238
1239 return;
1240 }
1241
1242 /* The next function "exposes" struct fuse to userland. Not much
1243 * that we can do about this, as we're conforming to a defined
1244 * interface. */
1245
1246 void
1247 fuse_teardown(struct fuse *fuse, char *mountpoint)
1248 {
1249 fuse_unmount(mountpoint, fuse->fc);
1250 fuse_destroy(fuse);
1251 }
1252