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