refuse.c revision 1.56 1 /* $NetBSD: refuse.c,v 1.56 2007/05/17 01:55:43 christos 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.56 2007/05/17 01:55:43 christos 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 if ((rn = malloc(sizeof(*rn))) == NULL)
122 err(1, "newrn");
123 pn = puffs_pn_new(pu, rn);
124
125 return pn;
126 }
127
128 static void
129 nukern(struct puffs_node *pn)
130 {
131 struct refusenode *rn = pn->pn_data;
132
133 free(rn->dirh.dbuf);
134 free(rn);
135 puffs_pn_put(pn);
136 }
137
138 static ino_t fakeino = 3;
139
140 /*
141 * XXX: do this otherwise if/when we grow thread support
142 *
143 * XXX2: does not consistently supply uid, gid or pid currently
144 */
145 static struct fuse_context fcon;
146
147 #define DIR_CHUNKSIZE 4096
148 static int
149 fill_dirbuf(struct puffs_fuse_dirh *dh, const char *name, ino_t dino,
150 uint8_t dtype)
151 {
152
153 /* initial? */
154 if (dh->bufsize == 0) {
155 if ((dh->dbuf = malloc(DIR_CHUNKSIZE)) == 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 void
224 set_refuse_mount_name(char **argv, char *name, size_t size)
225 {
226 char *slash;
227
228 if (argv == NULL || *argv == NULL) {
229 (void) strlcpy(name, "refuse", size);
230 } else {
231 if ((slash = strrchr(*argv, '/')) == NULL) {
232 slash = *argv;
233 } else {
234 slash += 1;
235 }
236 if (strncmp(*argv, "refuse:", 7) == 0) {
237 /* we've already done this */
238 (void) strlcpy(name, *argv, size);
239 } else {
240 (void) snprintf(name, size, "refuse:%s", slash);
241 }
242 }
243 }
244
245
246 /* this function exposes struct fuse to userland */
247 struct fuse *
248 fuse_setup(int argc, char **argv, const struct fuse_operations *ops,
249 size_t size, char **mountpoint, int *multithreaded, int *fd)
250 {
251 struct fuse_chan *fc;
252 struct fuse_args *args;
253 struct fuse *fuse;
254 char name[64];
255 int i;
256
257 /* whilst this (assigning the pu_privdata in the puffs
258 * usermount struct to be the fuse struct) might seem like
259 * we are chasing our tail here, the logic is as follows:
260 + the operation wrapper gets called with the puffs
261 calling conventions
262 + we need to fix up args first
263 + then call the fuse user-supplied operation
264 + then we fix up any values on return that we need to
265 + and fix up any nodes, etc
266 * so we need to be able to get at the fuse ops from within the
267 * puffs_usermount struct
268 */
269 set_refuse_mount_name(argv, name, sizeof(name));
270
271 /* stuff name into fuse_args */
272 args = fuse_opt_deep_copy_args(argc, argv);
273 if (args->argc > 0) {
274 free(args->argv[0]);
275 }
276 if ((args->argv[0] = strdup(name)) == NULL)
277 err(1, "fuse_setup");
278
279 for (i = argc - 1 ; i > 0 && *argv[i] == '-' ; --i) {
280 }
281
282 fc = fuse_mount(*mountpoint = argv[i], args);
283 fuse = fuse_new(fc, args, ops, size, NULL);
284
285 fuse_opt_free_args(args);
286 free(args);
287
288 /* XXX - wait for puffs to become multi-threaded */
289 if (multithreaded) {
290 *multithreaded = 0;
291 }
292
293 /* XXX - this is unused */
294 if (fd) {
295 *fd = 0;
296 }
297
298 return fuse;
299 }
300
301 #define FUSE_ERR_UNLINK(fuse, file) if (fuse->op.unlink) fuse->op.unlink(file)
302 #define FUSE_ERR_RMDIR(fuse, dir) if (fuse->op.rmdir) fuse->op.rmdir(dir)
303
304 /* ARGSUSED1 */
305 static int
306 fuse_getattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
307 struct vattr *va)
308 {
309 struct stat st;
310 int ret;
311
312 if (fuse->op.getattr == NULL) {
313 return ENOSYS;
314 }
315
316 /* wrap up return code */
317 ret = (*fuse->op.getattr)(path, &st);
318
319 if (ret == 0) {
320 puffs_stat2vattr(va, &st);
321 }
322
323 return -ret;
324 }
325
326 static int
327 fuse_setattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
328 const struct vattr *va)
329 {
330 struct refusenode *rn = pn->pn_data;
331 mode_t mode;
332 uid_t uid;
333 gid_t gid;
334 int error, ret;
335
336 error = 0;
337
338 mode = va->va_mode;
339 uid = va->va_uid;
340 gid = va->va_gid;
341
342 if (mode != (mode_t)PUFFS_VNOVAL) {
343 ret = 0;
344
345 if (fuse->op.chmod == NULL) {
346 error = -ENOSYS;
347 } else {
348 ret = fuse->op.chmod(path, mode);
349 if (ret)
350 error = ret;
351 }
352 }
353 if (uid != (uid_t)PUFFS_VNOVAL || gid != (gid_t)PUFFS_VNOVAL) {
354 ret = 0;
355
356 if (fuse->op.chown == NULL) {
357 error = -ENOSYS;
358 } else {
359 ret = fuse->op.chown(path, uid, gid);
360 if (ret)
361 error = ret;
362 }
363 }
364 if (va->va_atime.tv_sec != (time_t)PUFFS_VNOVAL
365 || va->va_mtime.tv_sec != (long)PUFFS_VNOVAL) {
366 ret = 0;
367
368 if (fuse->op.utimens) {
369 struct timespec tv[2];
370
371 tv[0].tv_sec = va->va_atime.tv_sec;
372 tv[0].tv_nsec = va->va_atime.tv_nsec;
373 tv[1].tv_sec = va->va_mtime.tv_sec;
374 tv[1].tv_nsec = va->va_mtime.tv_nsec;
375
376 ret = fuse->op.utimens(path, tv);
377 } else if (fuse->op.utime) {
378 struct utimbuf timbuf;
379
380 timbuf.actime = va->va_atime.tv_sec;
381 timbuf.modtime = va->va_mtime.tv_sec;
382
383 ret = fuse->op.utime(path, &timbuf);
384 } else {
385 error = -ENOSYS;
386 }
387
388 if (ret)
389 error = ret;
390 }
391 if (va->va_size != (u_quad_t)PUFFS_VNOVAL) {
392 ret = 0;
393
394 if (fuse->op.truncate) {
395 ret = fuse->op.truncate(path, (off_t)va->va_size);
396 } else if (fuse->op.ftruncate) {
397 ret = fuse->op.ftruncate(path, (off_t)va->va_size,
398 &rn->file_info);
399 } else {
400 error = -ENOSYS;
401 }
402
403 if (ret)
404 error = ret;
405 }
406 /* XXX: no reflection with reality */
407 puffs_setvattr(&pn->pn_va, va);
408
409 return -error;
410
411 }
412
413 static int
414 fuse_newnode(struct puffs_usermount *pu, const char *path,
415 const struct vattr *va, struct fuse_file_info *fi, void **newnode)
416 {
417 struct vattr newva;
418 struct fuse *fuse;
419 struct puffs_node *pn;
420 struct refusenode *rn;
421
422 fuse = puffs_getspecific(pu);
423
424 /* fix up nodes */
425 pn = newrn(pu);
426 if (pn == NULL) {
427 if (va->va_type == VDIR) {
428 FUSE_ERR_RMDIR(fuse, path);
429 } else {
430 FUSE_ERR_UNLINK(fuse, path);
431 }
432 return ENOMEM;
433 }
434 fuse_setattr(fuse, pn, path, va);
435 if (fuse_getattr(fuse, pn, path, &newva) == 0)
436 puffs_setvattr(&pn->pn_va, &newva);
437
438 rn = pn->pn_data;
439 if (fi)
440 memcpy(&rn->file_info, fi, sizeof(struct fuse_file_info));
441
442 *newnode = pn;
443
444 return 0;
445 }
446
447
448 /* operation wrappers start here */
449
450 /* lookup the path */
451 /* ARGSUSED1 */
452 static int
453 puffs_fuse_node_lookup(struct puffs_cc *pcc, void *opc, void **newnode,
454 enum vtype *newtype, voff_t *newsize, dev_t *newrdev,
455 const struct puffs_cn *pcn)
456 {
457 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
458 struct puffs_node *pn_res;
459 struct stat st;
460 struct fuse *fuse;
461 const char *path = PCNPATH(pcn);
462 int ret;
463
464 fuse = puffs_getspecific(pu);
465 ret = fuse->op.getattr(path, &st);
466
467 if (ret != 0) {
468 return -ret;
469 }
470
471 /* XXX: fiXXXme unconst */
472 pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp,
473 __UNCONST(&pcn->pcn_po_full));
474 if (pn_res == NULL) {
475 pn_res = newrn(pu);
476 if (pn_res == NULL)
477 return errno;
478 puffs_stat2vattr(&pn_res->pn_va, &st);
479 }
480
481 *newnode = pn_res;
482 *newtype = pn_res->pn_va.va_type;
483 *newsize = pn_res->pn_va.va_size;
484 *newrdev = pn_res->pn_va.va_rdev;
485
486 return 0;
487 }
488
489 /* get attributes for the path name */
490 /* ARGSUSED3 */
491 static int
492 puffs_fuse_node_getattr(struct puffs_cc *pcc, void *opc, struct vattr *va,
493 const struct puffs_cred *pcr, pid_t pid)
494 {
495 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
496 struct puffs_node *pn = opc;
497 struct fuse *fuse;
498 const char *path = PNPATH(pn);
499
500 fuse = puffs_getspecific(pu);
501 return fuse_getattr(fuse, pn, path, va);
502 }
503
504 /* read the contents of the symbolic link */
505 /* ARGSUSED2 */
506 static int
507 puffs_fuse_node_readlink(struct puffs_cc *pcc, void *opc,
508 const struct puffs_cred *cred, char *linkname, size_t *linklen)
509 {
510 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
511 struct puffs_node *pn = opc;
512 struct fuse *fuse;
513 const char *path = PNPATH(pn), *p;
514 int ret;
515
516 fuse = puffs_getspecific(pu);
517 if (fuse->op.readlink == NULL) {
518 return ENOSYS;
519 }
520
521 /* wrap up return code */
522 ret = (*fuse->op.readlink)(path, linkname, *linklen);
523
524 if (ret == 0) {
525 p = memchr(linkname, '\0', *linklen);
526 if (!p)
527 return EINVAL;
528
529 *linklen = p - linkname;
530 }
531
532 return -ret;
533 }
534
535 /* make the special node */
536 /* ARGSUSED1 */
537 static int
538 puffs_fuse_node_mknod(struct puffs_cc *pcc, void *opc, void **newnode,
539 const struct puffs_cn *pcn, const struct vattr *va)
540 {
541 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
542 struct fuse *fuse;
543 mode_t mode;
544 const char *path = PCNPATH(pcn);
545 int ret;
546
547 fuse = puffs_getspecific(pu);
548 if (fuse->op.mknod == NULL) {
549 return ENOSYS;
550 }
551
552 /* wrap up return code */
553 mode = puffs_addvtype2mode(va->va_mode, va->va_type);
554 ret = (*fuse->op.mknod)(path, mode, va->va_rdev);
555
556 if (ret == 0) {
557 ret = fuse_newnode(pu, path, va, NULL, newnode);
558 }
559
560 return -ret;
561 }
562
563 /* make a directory */
564 /* ARGSUSED1 */
565 static int
566 puffs_fuse_node_mkdir(struct puffs_cc *pcc, void *opc, void **newnode,
567 const struct puffs_cn *pcn, const struct vattr *va)
568 {
569 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
570 struct fuse *fuse;
571 mode_t mode = va->va_mode;
572 const char *path = PCNPATH(pcn);
573 int ret;
574
575 fuse = puffs_getspecific(pu);
576 if (fuse->op.mkdir == NULL) {
577 return ENOSYS;
578 }
579
580 /* wrap up return code */
581 ret = (*fuse->op.mkdir)(path, mode);
582
583 if (ret == 0) {
584 ret = fuse_newnode(pu, path, va, NULL, newnode);
585 }
586
587 return -ret;
588 }
589
590 /*
591 * create a regular file
592 *
593 * since linux/fuse sports using mknod for creating regular files
594 * instead of having a separate call for it in some versions, if
595 * we don't have create, just jump to op->mknod.
596 */
597 /*ARGSUSED1*/
598 static int
599 puffs_fuse_node_create(struct puffs_cc *pcc, void *opc, void **newnode,
600 const struct puffs_cn *pcn, const struct vattr *va)
601 {
602 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
603 struct fuse *fuse;
604 struct fuse_file_info fi;
605 mode_t mode = va->va_mode;
606 const char *path = PCNPATH(pcn);
607 int ret, created;
608
609 fuse = puffs_getspecific(pu);
610
611 created = 0;
612 if (fuse->op.create) {
613 ret = fuse->op.create(path, mode, &fi);
614 if (ret == 0)
615 created = 1;
616
617 } else if (fuse->op.mknod) {
618 fcon.uid = va->va_uid; /*XXX*/
619 fcon.gid = va->va_gid; /*XXX*/
620
621 ret = fuse->op.mknod(path, mode | S_IFREG, 0);
622
623 } else {
624 ret = -ENOSYS;
625 }
626
627 if (ret == 0) {
628 ret = fuse_newnode(pu, path, va, &fi, newnode);
629
630 /* sweet.. create also open the file */
631 if (created) {
632 struct puffs_node *pn;
633 struct refusenode *rn;
634
635 pn = *newnode;
636 rn = pn->pn_data;
637 rn->flags |= RN_OPEN;
638 rn->opencount++;
639 }
640 }
641
642 return -ret;
643 }
644
645 /* remove the directory entry */
646 /* ARGSUSED1 */
647 static int
648 puffs_fuse_node_remove(struct puffs_cc *pcc, void *opc, void *targ,
649 const struct puffs_cn *pcn)
650 {
651 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
652 struct puffs_node *pn_targ = targ;
653 struct fuse *fuse;
654 const char *path = PNPATH(pn_targ);
655 int ret;
656
657 fuse = puffs_getspecific(pu);
658 if (fuse->op.unlink == NULL) {
659 return ENOSYS;
660 }
661
662 /* wrap up return code */
663 ret = (*fuse->op.unlink)(path);
664
665 return -ret;
666 }
667
668 /* remove the directory */
669 /* ARGSUSED1 */
670 static int
671 puffs_fuse_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ,
672 const struct puffs_cn *pcn)
673 {
674 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
675 struct puffs_node *pn_targ = targ;
676 struct fuse *fuse;
677 const char *path = PNPATH(pn_targ);
678 int ret;
679
680 fuse = puffs_getspecific(pu);
681 if (fuse->op.rmdir == NULL) {
682 return ENOSYS;
683 }
684
685 /* wrap up return code */
686 ret = (*fuse->op.rmdir)(path);
687
688 return -ret;
689 }
690
691 /* create a symbolic link */
692 /* ARGSUSED1 */
693 static int
694 puffs_fuse_node_symlink(struct puffs_cc *pcc, void *opc, void **newnode,
695 const struct puffs_cn *pcn_src, const struct vattr *va,
696 const char *link_target)
697 {
698 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
699 struct fuse *fuse;
700 const char *path = PCNPATH(pcn_src);
701 int ret;
702
703 fuse = puffs_getspecific(pu);
704 if (fuse->op.symlink == NULL) {
705 return ENOSYS;
706 }
707
708 /* wrap up return code */
709 ret = fuse->op.symlink(link_target, path);
710
711 if (ret == 0) {
712 ret = fuse_newnode(pu, path, va, NULL, newnode);
713 }
714
715 return -ret;
716 }
717
718 /* rename a directory entry */
719 /* ARGSUSED1 */
720 static int
721 puffs_fuse_node_rename(struct puffs_cc *pcc, void *opc, void *src,
722 const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
723 const struct puffs_cn *pcn_targ)
724 {
725 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
726 struct fuse *fuse;
727 const char *path_src = PCNPATH(pcn_src);
728 const char *path_dest = PCNPATH(pcn_targ);
729 int ret;
730
731 fuse = puffs_getspecific(pu);
732 if (fuse->op.rename == NULL) {
733 return ENOSYS;
734 }
735
736 ret = fuse->op.rename(path_src, path_dest);
737
738 if (ret == 0) {
739 }
740
741 return -ret;
742 }
743
744 /* create a link in the file system */
745 /* ARGSUSED1 */
746 static int
747 puffs_fuse_node_link(struct puffs_cc *pcc, void *opc, void *targ,
748 const struct puffs_cn *pcn)
749 {
750 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
751 struct puffs_node *pn = targ;
752 struct fuse *fuse;
753 int ret;
754
755 fuse = puffs_getspecific(pu);
756 if (fuse->op.link == NULL) {
757 return ENOSYS;
758 }
759
760 /* wrap up return code */
761 ret = (*fuse->op.link)(PNPATH(pn), PCNPATH(pcn));
762
763 return -ret;
764 }
765
766 /*
767 * fuse's regular interface provides chmod(), chown(), utimes()
768 * and truncate() + some variations, so try to fit the square block
769 * in the circle hole and the circle block .... something like that
770 */
771 /* ARGSUSED3 */
772 static int
773 puffs_fuse_node_setattr(struct puffs_cc *pcc, void *opc,
774 const struct vattr *va, const struct puffs_cred *pcr, pid_t pid)
775 {
776 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
777 struct puffs_node *pn = opc;
778 struct fuse *fuse;
779 const char *path = PNPATH(pn);
780
781 fuse = puffs_getspecific(pu);
782
783 return fuse_setattr(fuse, pn, path, va);
784 }
785
786 /* ARGSUSED2 */
787 static int
788 puffs_fuse_node_open(struct puffs_cc *pcc, void *opc, int mode,
789 const struct puffs_cred *cred, pid_t pid)
790 {
791 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
792 struct puffs_node *pn = opc;
793 struct refusenode *rn = pn->pn_data;
794 struct fuse_file_info *fi = &rn->file_info;
795 struct fuse *fuse;
796 const char *path = PNPATH(pn);
797
798 fuse = puffs_getspecific(pu);
799
800 /* if open, don't open again, lest risk nuking file private info */
801 if (rn->flags & RN_OPEN) {
802 rn->opencount++;
803 return 0;
804 }
805
806 /* OFLAGS(), need to convert FREAD/FWRITE to O_RD/WR */
807 fi->flags = (mode & ~(O_CREAT | O_EXCL | O_TRUNC)) - 1;
808
809 if (pn->pn_va.va_type == VDIR) {
810 if (fuse->op.opendir)
811 fuse->op.opendir(path, fi);
812 } else {
813 if (fuse->op.open)
814 fuse->op.open(path, fi);
815 }
816
817 rn->flags |= RN_OPEN;
818 rn->opencount++;
819
820 return 0;
821 }
822
823 /* ARGSUSED2 */
824 static int
825 puffs_fuse_node_close(struct puffs_cc *pcc, void *opc, int fflag,
826 const struct puffs_cred *pcr, pid_t pid)
827 {
828 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
829 struct puffs_node *pn = opc;
830 struct refusenode *rn = pn->pn_data;
831 struct fuse *fuse;
832 struct fuse_file_info *fi;
833 const char *path = PNPATH(pn);
834 int ret;
835
836 fuse = puffs_getspecific(pu);
837 fi = &rn->file_info;
838 ret = 0;
839
840 if (rn->flags & RN_OPEN) {
841 if (pn->pn_va.va_type == VDIR) {
842 if (fuse->op.releasedir)
843 ret = fuse->op.releasedir(path, fi);
844 } else {
845 if (fuse->op.release)
846 ret = fuse->op.release(path, fi);
847 }
848 }
849 rn->flags &= ~RN_OPEN;
850 rn->opencount--;
851
852 return ret;
853 }
854
855 /* read some more from the file */
856 /* ARGSUSED5 */
857 static int
858 puffs_fuse_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
859 off_t offset, size_t *resid, const struct puffs_cred *pcr,
860 int ioflag)
861 {
862 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
863 struct puffs_node *pn = opc;
864 struct refusenode *rn = pn->pn_data;
865 struct fuse *fuse;
866 const char *path = PNPATH(pn);
867 size_t maxread;
868 int ret;
869
870 fuse = puffs_getspecific(pu);
871 if (fuse->op.read == NULL) {
872 return ENOSYS;
873 }
874
875 maxread = *resid;
876 if (maxread > pn->pn_va.va_size - offset) {
877 /*LINTED*/
878 maxread = pn->pn_va.va_size - offset;
879 }
880 if (maxread == 0)
881 return 0;
882
883 ret = (*fuse->op.read)(path, (char *)buf, maxread, offset,
884 &rn->file_info);
885
886 if (ret > 0) {
887 *resid -= ret;
888 ret = 0;
889 }
890
891 return -ret;
892 }
893
894 /* write to the file */
895 /* ARGSUSED0 */
896 static int
897 puffs_fuse_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
898 off_t offset, size_t *resid, const struct puffs_cred *pcr,
899 int ioflag)
900 {
901 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
902 struct puffs_node *pn = opc;
903 struct refusenode *rn = pn->pn_data;
904 struct fuse *fuse;
905 const char *path = PNPATH(pn);
906 int ret;
907
908 fuse = puffs_getspecific(pu);
909 if (fuse->op.write == NULL) {
910 return ENOSYS;
911 }
912
913 if (ioflag & PUFFS_IO_APPEND)
914 offset = pn->pn_va.va_size;
915
916 ret = (*fuse->op.write)(path, (char *)buf, *resid, offset,
917 &rn->file_info);
918
919 if (ret > 0) {
920 if (offset + ret > pn->pn_va.va_size)
921 pn->pn_va.va_size = offset + ret;
922 *resid -= ret;
923 ret = 0;
924 }
925
926 return -ret;
927 }
928
929
930 /* ARGSUSED3 */
931 static int
932 puffs_fuse_node_readdir(struct puffs_cc *pcc, void *opc, struct dirent *dent,
933 off_t *readoff, size_t *reslen, const struct puffs_cred *pcr,
934 int *eofflag, off_t *cookies, size_t *ncookies)
935 {
936 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
937 struct puffs_node *pn = opc;
938 struct refusenode *rn = pn->pn_data;
939 struct puffs_fuse_dirh *dirh;
940 struct fuse *fuse;
941 struct dirent *fromdent;
942 const char *path = PNPATH(pn);
943 int ret;
944
945 fuse = puffs_getspecific(pu);
946 if (fuse->op.readdir == NULL && fuse->op.getdir == NULL) {
947 return ENOSYS;
948 }
949
950 if (pn->pn_va.va_type != VDIR)
951 return ENOTDIR;
952
953 dirh = &rn->dirh;
954
955 /*
956 * if we are starting from the beginning, slurp entire directory
957 * into our buffers
958 */
959 if (*readoff == 0) {
960 /* free old buffers */
961 free(dirh->dbuf);
962 memset(dirh, 0, sizeof(struct puffs_fuse_dirh));
963
964 if (fuse->op.readdir)
965 ret = fuse->op.readdir(path, dirh, puffs_fuse_fill_dir,
966 0, &rn->file_info);
967 else
968 ret = fuse->op.getdir(path, dirh, puffs_fuse_dirfil);
969 if (ret)
970 return -ret;
971 }
972
973 /* now, stuff results into the kernel buffers */
974 while (*readoff < dirh->bufsize - dirh->reslen) {
975 /*LINTED*/
976 fromdent = (struct dirent *)((uint8_t *)dirh->dbuf + *readoff);
977
978 if (*reslen < _DIRENT_SIZE(fromdent))
979 break;
980
981 memcpy(dent, fromdent, _DIRENT_SIZE(fromdent));
982 *readoff += _DIRENT_SIZE(fromdent);
983 *reslen -= _DIRENT_SIZE(fromdent);
984
985 dent = _DIRENT_NEXT(dent);
986 }
987
988 return 0;
989 }
990
991 /* ARGSUSED */
992 static int
993 puffs_fuse_node_reclaim(struct puffs_cc *pcc, void *opc, pid_t pid)
994 {
995 struct puffs_node *pn = opc;
996
997 nukern(pn);
998
999 return 0;
1000 }
1001
1002 /* ARGSUSED1 */
1003 static int
1004 puffs_fuse_fs_unmount(struct puffs_cc *pcc, int flags, pid_t pid)
1005 {
1006 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
1007 struct fuse *fuse;
1008
1009 fuse = puffs_getspecific(pu);
1010 if (fuse->op.destroy == NULL) {
1011 return 0;
1012 }
1013 (*fuse->op.destroy)(fuse);
1014 return 0;
1015 }
1016
1017 /* ARGSUSED0 */
1018 static int
1019 puffs_fuse_fs_sync(struct puffs_cc *pcc, int flags,
1020 const struct puffs_cred *cr, pid_t pid)
1021 {
1022 return 0;
1023 }
1024
1025 /* ARGSUSED2 */
1026 static int
1027 puffs_fuse_fs_statvfs(struct puffs_cc *pcc, struct statvfs *svfsb, pid_t pid)
1028 {
1029 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
1030 struct fuse *fuse;
1031 int ret;
1032
1033 fuse = puffs_getspecific(pu);
1034 if (fuse->op.statfs == NULL) {
1035 if ((ret = statvfs(PNPATH(puffs_getroot(pu)), svfsb)) == -1) {
1036 return errno;
1037 }
1038 } else {
1039 ret = fuse->op.statfs(PNPATH(puffs_getroot(pu)), svfsb);
1040 }
1041
1042 return ret;
1043 }
1044
1045
1046 /* End of puffs_fuse operations */
1047 /* ARGSUSED3 */
1048 int
1049 fuse_main_real(int argc, char **argv, const struct fuse_operations *ops,
1050 size_t size, void *userdata)
1051 {
1052 struct fuse *fuse;
1053 char *mountpoint;
1054 int multithreaded;
1055 int fd;
1056
1057 fuse = fuse_setup(argc, argv, ops, size, &mountpoint, &multithreaded,
1058 &fd);
1059
1060 return fuse_loop(fuse);
1061 }
1062
1063 /*
1064 * XXX: just defer the operation until fuse_new() when we have more
1065 * info on our hands. The real beef is why's this separate in fuse in
1066 * the first place?
1067 */
1068 /* ARGSUSED1 */
1069 struct fuse_chan *
1070 fuse_mount(const char *dir, struct fuse_args *args)
1071 {
1072 struct fuse_chan *fc;
1073 char name[64];
1074
1075 if ((fc = malloc(sizeof(*fc))) == NULL)
1076 err(1, "fuse_mount");
1077
1078 if ((fc->dir = strdup(dir)) == NULL)
1079 err(1, "fuse_mount");
1080
1081 /*
1082 * we need to deep copy the args struct - some fuse file
1083 * systems "clean up" the argument vector for "security
1084 * reasons"
1085 */
1086 fc->args = fuse_opt_deep_copy_args(args->argc, args->argv);
1087
1088 if (args->argc > 0) {
1089 set_refuse_mount_name(args->argv, name, sizeof(name));
1090 if ((args->argv[0] = strdup(name)) == NULL)
1091 err(1, "fuse_mount");
1092 }
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 char name[64];
1111 char *argv0;
1112 extern int puffs_fakecc;
1113
1114 if ((fuse = malloc(sizeof(*fuse))) == NULL)
1115 err(1, "fuse_new");
1116
1117 /* copy fuse ops to their own stucture */
1118 (void) memcpy(&fuse->op, ops, sizeof(fuse->op));
1119
1120 fcon.fuse = fuse;
1121 fcon.private_data = userdata;
1122
1123 fuse->fc = fc;
1124
1125 /* initialise the puffs operations structure */
1126 PUFFSOP_INIT(pops);
1127
1128 PUFFSOP_SET(pops, puffs_fuse, fs, sync);
1129 PUFFSOP_SET(pops, puffs_fuse, fs, statvfs);
1130 PUFFSOP_SET(pops, puffs_fuse, fs, unmount);
1131
1132 /*
1133 * XXX: all of these don't possibly need to be
1134 * unconditionally set
1135 */
1136 PUFFSOP_SET(pops, puffs_fuse, node, lookup);
1137 PUFFSOP_SET(pops, puffs_fuse, node, getattr);
1138 PUFFSOP_SET(pops, puffs_fuse, node, setattr);
1139 PUFFSOP_SET(pops, puffs_fuse, node, readdir);
1140 PUFFSOP_SET(pops, puffs_fuse, node, readlink);
1141 PUFFSOP_SET(pops, puffs_fuse, node, mknod);
1142 PUFFSOP_SET(pops, puffs_fuse, node, create);
1143 PUFFSOP_SET(pops, puffs_fuse, node, remove);
1144 PUFFSOP_SET(pops, puffs_fuse, node, mkdir);
1145 PUFFSOP_SET(pops, puffs_fuse, node, rmdir);
1146 PUFFSOP_SET(pops, puffs_fuse, node, symlink);
1147 PUFFSOP_SET(pops, puffs_fuse, node, rename);
1148 PUFFSOP_SET(pops, puffs_fuse, node, link);
1149 PUFFSOP_SET(pops, puffs_fuse, node, open);
1150 PUFFSOP_SET(pops, puffs_fuse, node, close);
1151 PUFFSOP_SET(pops, puffs_fuse, node, read);
1152 PUFFSOP_SET(pops, puffs_fuse, node, write);
1153 PUFFSOP_SET(pops, puffs_fuse, node, reclaim);
1154
1155 argv0 = (*args->argv[0] == 0x0) ? fc->args->argv[0] : args->argv[0];
1156 set_refuse_mount_name(&argv0, name, sizeof(name));
1157
1158 puffs_fakecc = 1; /* XXX */
1159 pu = puffs_mount(pops, fc->dir, MNT_NODEV | MNT_NOSUID,
1160 name, fuse,
1161 PUFFS_FLAG_BUILDPATH
1162 | PUFFS_FLAG_HASHPATH
1163 | PUFFS_FLAG_OPDUMP
1164 | PUFFS_KFLAG_NOCACHE);
1165 if (pu == NULL) {
1166 err(EXIT_FAILURE, "puffs_mount: directory \"%s\"", fc->dir);
1167 }
1168 fc->pu = pu;
1169
1170 pn_root = newrn(pu);
1171 puffs_setroot(pu, pn_root);
1172 rn_root = pn_root->pn_data;
1173 rn_root->flags |= RN_ROOT;
1174
1175 po_root = puffs_getrootpathobj(pu);
1176 if ((po_root->po_path = strdup("/")) == NULL)
1177 err(1, "fuse_new");
1178 po_root->po_len = 1;
1179
1180 /* sane defaults */
1181 puffs_vattr_null(&pn_root->pn_va);
1182 pn_root->pn_va.va_type = VDIR;
1183 pn_root->pn_va.va_mode = 0755;
1184 if (fuse->op.getattr)
1185 if (fuse->op.getattr(po_root->po_path, &st) == 0)
1186 puffs_stat2vattr(&pn_root->pn_va, &st);
1187 assert(pn_root->pn_va.va_type == VDIR);
1188
1189 if (fuse->op.init)
1190 fcon.private_data = fuse->op.init(NULL); /* XXX */
1191
1192 puffs_zerostatvfs(&svfsb);
1193 if (puffs_start(pu, pn_root, &svfsb) == -1) {
1194 err(EXIT_FAILURE, "puffs_start");
1195 }
1196
1197 return fuse;
1198 }
1199
1200 int
1201 fuse_loop(struct fuse *fuse)
1202 {
1203
1204 return puffs_mainloop(fuse->fc->pu, PUFFSLOOP_NODAEMON);
1205 }
1206
1207 void
1208 fuse_destroy(struct fuse *fuse)
1209 {
1210
1211
1212 /* XXXXXX: missing stuff */
1213 free(fuse);
1214 }
1215
1216 /* XXX: threads */
1217 struct fuse_context *
1218 fuse_get_context()
1219 {
1220
1221 return &fcon;
1222 }
1223
1224 void
1225 fuse_exit(struct fuse *fuse)
1226 {
1227
1228 puffs_exit(fuse->fc->pu, 1);
1229 }
1230
1231 /*
1232 * XXX: obviously not the most perfect of functions, but needs some
1233 * puffs tweaking for a better tomorrow
1234 */
1235 /*ARGSUSED*/
1236 void
1237 fuse_unmount(const char *mp, struct fuse_chan *fc)
1238 {
1239
1240 puffs_exit(fc->pu, 1);
1241 }
1242
1243 /*ARGSUSED*/
1244 void
1245 fuse_unmount_compat22(const char *mp)
1246 {
1247
1248 return;
1249 }
1250
1251 /* The next function "exposes" struct fuse to userland. Not much
1252 * that we can do about this, as we're conforming to a defined
1253 * interface. */
1254
1255 void
1256 fuse_teardown(struct fuse *fuse, char *mountpoint)
1257 {
1258 fuse_unmount(mountpoint, fuse->fc);
1259 fuse_destroy(fuse);
1260 }
1261