refuse.c revision 1.65 1 /* $NetBSD: refuse.c,v 1.65 2007/06/12 18:54:36 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.65 2007/06/12 18:54:36 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 = calloc(1, DIR_CHUNKSIZE)) == NULL) {
171 err(EXIT_FAILURE, "fill_dirbuf");
172 }
173 dh->d = dh->dbuf;
174 dh->reslen = dh->bufsize = DIR_CHUNKSIZE;
175 }
176
177 if (puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen)) {
178 return 0;
179 }
180
181 /* try to increase buffer space */
182 dh->dbuf = realloc(dh->dbuf, dh->bufsize + DIR_CHUNKSIZE);
183 if (dh->dbuf == NULL) {
184 err(EXIT_FAILURE, "fill_dirbuf realloc");
185 }
186 dh->d = (void *)((uint8_t *)dh->dbuf + (dh->bufsize - dh->reslen));
187 dh->reslen += DIR_CHUNKSIZE;
188 dh->bufsize += DIR_CHUNKSIZE;
189
190 return !puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen);
191 }
192
193 /* ARGSUSED3 */
194 /* XXX: I have no idea how "off" is supposed to be used */
195 static int
196 puffs_fuse_fill_dir(void *buf, const char *name,
197 const struct stat *stbuf, off_t off)
198 {
199 struct puffs_fuse_dirh *deh = buf;
200 ino_t dino;
201 uint8_t dtype;
202
203 if (stbuf == NULL) {
204 dtype = DT_UNKNOWN;
205 dino = fakeino++;
206 } else {
207 dtype = puffs_vtype2dt(puffs_mode2vt(stbuf->st_mode));
208 dino = stbuf->st_ino;
209
210 /*
211 * Some FUSE file systems like to always use 0 as the
212 * inode number. Our readdir() doesn't like to show
213 * directory entries with inode number 0 ==> workaround.
214 */
215 if (dino == 0) {
216 dino = fakeino++;
217 }
218 }
219
220 return fill_dirbuf(deh, name, dino, dtype);
221 }
222
223 static int
224 puffs_fuse_dirfil(fuse_dirh_t h, const char *name, int type, ino_t ino)
225 {
226 ino_t dino;
227 int dtype;
228
229 if ((dtype = type) == 0) {
230 dtype = DT_UNKNOWN;
231 }
232
233 dino = (ino) ? ino : fakeino++;
234
235 return fill_dirbuf(h, name, dino, dtype);
236 }
237
238 /* place the refuse file system name into `name' */
239 static void
240 set_refuse_mount_name(char **argv, char *name, size_t size)
241 {
242 char *slash;
243
244 if (argv == NULL || *argv == NULL) {
245 (void) strlcpy(name, "refuse", size);
246 } else {
247 if ((slash = strrchr(*argv, '/')) == NULL) {
248 slash = *argv;
249 } else {
250 slash += 1;
251 }
252 if (strncmp(*argv, "refuse:", 7) == 0) {
253 /* we've already done this */
254 (void) strlcpy(name, *argv, size);
255 } else {
256 (void) snprintf(name, size, "refuse:%s", slash);
257 }
258 }
259 }
260
261
262 /* this function exposes struct fuse to userland */
263 struct fuse *
264 fuse_setup(int argc, char **argv, const struct fuse_operations *ops,
265 size_t size, char **mountpoint, int *multithreaded, int *fd)
266 {
267 struct fuse_chan *fc;
268 struct fuse_args *args;
269 struct fuse *fuse;
270 char name[64];
271 int i;
272
273 set_refuse_mount_name(argv, name, sizeof(name));
274
275 /* stuff name into fuse_args */
276 args = fuse_opt_deep_copy_args(argc, argv);
277 if (args->argc > 0) {
278 free(args->argv[0]);
279 }
280 if ((args->argv[0] = strdup(name)) == NULL)
281 err(1, "fuse_setup");
282
283 /* count back from the end over arguments starting with '-' */
284 for (i = argc - 1 ; i > 0 && *argv[i] == '-' ; --i) {
285 }
286
287 fc = fuse_mount(*mountpoint = argv[i], args);
288 fuse = fuse_new(fc, args, ops, size, NULL);
289
290 fuse_opt_free_args(args);
291 free(args);
292
293 /* XXX - wait for puffs to become multi-threaded */
294 if (multithreaded) {
295 *multithreaded = 0;
296 }
297
298 /* XXX - this is unused */
299 if (fd) {
300 *fd = 0;
301 }
302
303 return fuse;
304 }
305
306 #define FUSE_ERR_UNLINK(fuse, file) if (fuse->op.unlink) fuse->op.unlink(file)
307 #define FUSE_ERR_RMDIR(fuse, dir) if (fuse->op.rmdir) fuse->op.rmdir(dir)
308
309 /* ARGSUSED1 */
310 static int
311 fuse_getattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
312 struct vattr *va)
313 {
314 struct stat st;
315 int ret;
316
317 if (fuse->op.getattr == NULL) {
318 return ENOSYS;
319 }
320
321 /* wrap up return code */
322 ret = (*fuse->op.getattr)(path, &st);
323
324 if (ret == 0) {
325 puffs_stat2vattr(va, &st);
326 }
327
328 return -ret;
329 }
330
331 static int
332 fuse_setattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
333 const struct vattr *va)
334 {
335 struct refusenode *rn = pn->pn_data;
336 mode_t mode;
337 uid_t uid;
338 gid_t gid;
339 int error, ret;
340
341 error = 0;
342
343 mode = va->va_mode;
344 uid = va->va_uid;
345 gid = va->va_gid;
346
347 if (mode != (mode_t)PUFFS_VNOVAL) {
348 ret = 0;
349
350 if (fuse->op.chmod == NULL) {
351 error = -ENOSYS;
352 } else {
353 ret = fuse->op.chmod(path, mode);
354 if (ret)
355 error = ret;
356 }
357 }
358 if (uid != (uid_t)PUFFS_VNOVAL || gid != (gid_t)PUFFS_VNOVAL) {
359 ret = 0;
360
361 if (fuse->op.chown == NULL) {
362 error = -ENOSYS;
363 } else {
364 ret = fuse->op.chown(path, uid, gid);
365 if (ret)
366 error = ret;
367 }
368 }
369 if (va->va_atime.tv_sec != (time_t)PUFFS_VNOVAL
370 || va->va_mtime.tv_sec != (long)PUFFS_VNOVAL) {
371 ret = 0;
372
373 if (fuse->op.utimens) {
374 struct timespec tv[2];
375
376 tv[0].tv_sec = va->va_atime.tv_sec;
377 tv[0].tv_nsec = va->va_atime.tv_nsec;
378 tv[1].tv_sec = va->va_mtime.tv_sec;
379 tv[1].tv_nsec = va->va_mtime.tv_nsec;
380
381 ret = fuse->op.utimens(path, tv);
382 } else if (fuse->op.utime) {
383 struct utimbuf timbuf;
384
385 timbuf.actime = va->va_atime.tv_sec;
386 timbuf.modtime = va->va_mtime.tv_sec;
387
388 ret = fuse->op.utime(path, &timbuf);
389 } else {
390 error = -ENOSYS;
391 }
392
393 if (ret)
394 error = ret;
395 }
396 if (va->va_size != (u_quad_t)PUFFS_VNOVAL) {
397 ret = 0;
398
399 if (fuse->op.truncate) {
400 ret = fuse->op.truncate(path, (off_t)va->va_size);
401 } else if (fuse->op.ftruncate) {
402 ret = fuse->op.ftruncate(path, (off_t)va->va_size,
403 &rn->file_info);
404 } else {
405 error = -ENOSYS;
406 }
407
408 if (ret)
409 error = ret;
410 }
411 /* XXX: no reflection with reality */
412 puffs_setvattr(&pn->pn_va, va);
413
414 return -error;
415
416 }
417
418 static int
419 fuse_newnode(struct puffs_usermount *pu, const char *path,
420 const struct vattr *va, struct fuse_file_info *fi, void **newnode)
421 {
422 struct vattr newva;
423 struct fuse *fuse;
424 struct puffs_node *pn;
425 struct refusenode *rn;
426
427 fuse = puffs_getspecific(pu);
428
429 /* fix up nodes */
430 pn = newrn(pu);
431 if (pn == NULL) {
432 if (va->va_type == VDIR) {
433 FUSE_ERR_RMDIR(fuse, path);
434 } else {
435 FUSE_ERR_UNLINK(fuse, path);
436 }
437 return ENOMEM;
438 }
439 fuse_setattr(fuse, pn, path, va);
440 if (fuse_getattr(fuse, pn, path, &newva) == 0)
441 puffs_setvattr(&pn->pn_va, &newva);
442
443 rn = pn->pn_data;
444 if (fi)
445 memcpy(&rn->file_info, fi, sizeof(struct fuse_file_info));
446
447 *newnode = pn;
448
449 return 0;
450 }
451
452
453 /* operation wrappers start here */
454
455 /* lookup the path */
456 /* ARGSUSED1 */
457 static int
458 puffs_fuse_node_lookup(struct puffs_cc *pcc, void *opc, void **newnode,
459 enum vtype *newtype, voff_t *newsize, dev_t *newrdev,
460 const struct puffs_cn *pcn)
461 {
462 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
463 struct puffs_node *pn_res;
464 struct stat st;
465 struct fuse *fuse;
466 const char *path = PCNPATH(pcn);
467 int ret;
468
469 fuse = puffs_getspecific(pu);
470
471 SET_FUSE_CONTEXT_UID_GID(&fcon, &pcn->pcn_cred);
472
473 ret = fuse->op.getattr(path, &st);
474
475 if (ret != 0) {
476 return -ret;
477 }
478
479 /* XXX: fiXXXme unconst */
480 pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp,
481 __UNCONST(&pcn->pcn_po_full));
482 if (pn_res == NULL) {
483 pn_res = newrn(pu);
484 if (pn_res == NULL)
485 return errno;
486 puffs_stat2vattr(&pn_res->pn_va, &st);
487 }
488
489 *newnode = pn_res;
490 *newtype = pn_res->pn_va.va_type;
491 *newsize = pn_res->pn_va.va_size;
492 *newrdev = pn_res->pn_va.va_rdev;
493
494 return 0;
495 }
496
497 /* get attributes for the path name */
498 /* ARGSUSED3 */
499 static int
500 puffs_fuse_node_getattr(struct puffs_cc *pcc, void *opc, struct vattr *va,
501 const struct puffs_cred *pcr, pid_t pid)
502 {
503 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
504 struct puffs_node *pn = opc;
505 struct fuse *fuse;
506 const char *path = PNPATH(pn);
507
508 fuse = puffs_getspecific(pu);
509
510 SET_FUSE_CONTEXT_UID_GID(&fcon, pcr);
511 SET_FUSE_CONTEXT_PID(&fcon, pid);
512
513 return fuse_getattr(fuse, pn, path, va);
514 }
515
516 /* read the contents of the symbolic link */
517 /* ARGSUSED2 */
518 static int
519 puffs_fuse_node_readlink(struct puffs_cc *pcc, void *opc,
520 const struct puffs_cred *cred, char *linkname, size_t *linklen)
521 {
522 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
523 struct puffs_node *pn = opc;
524 struct fuse *fuse;
525 const char *path = PNPATH(pn), *p;
526 int ret;
527
528 fuse = puffs_getspecific(pu);
529 if (fuse->op.readlink == NULL) {
530 return ENOSYS;
531 }
532
533 SET_FUSE_CONTEXT_UID_GID(&fcon, cred);
534
535 /* wrap up return code */
536 ret = (*fuse->op.readlink)(path, linkname, *linklen);
537
538 if (ret == 0) {
539 p = memchr(linkname, '\0', *linklen);
540 if (!p)
541 return EINVAL;
542
543 *linklen = p - linkname;
544 }
545
546 return -ret;
547 }
548
549 /* make the special node */
550 /* ARGSUSED1 */
551 static int
552 puffs_fuse_node_mknod(struct puffs_cc *pcc, void *opc, void **newnode,
553 const struct puffs_cn *pcn, const struct vattr *va)
554 {
555 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
556 struct fuse *fuse;
557 mode_t mode;
558 const char *path = PCNPATH(pcn);
559 int ret;
560
561 fuse = puffs_getspecific(pu);
562 if (fuse->op.mknod == NULL) {
563 return ENOSYS;
564 }
565
566 SET_FUSE_CONTEXT_UID_GID(&fcon, &pcn->pcn_cred);
567 SET_FUSE_CONTEXT_PID(&fcon, pcn->pcn_pid);
568
569 /* wrap up return code */
570 mode = puffs_addvtype2mode(va->va_mode, va->va_type);
571 ret = (*fuse->op.mknod)(path, mode, va->va_rdev);
572
573 if (ret == 0) {
574 ret = fuse_newnode(pu, path, va, NULL, newnode);
575 }
576
577 return -ret;
578 }
579
580 /* make a directory */
581 /* ARGSUSED1 */
582 static int
583 puffs_fuse_node_mkdir(struct puffs_cc *pcc, void *opc, void **newnode,
584 const struct puffs_cn *pcn, const struct vattr *va)
585 {
586 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
587 struct fuse *fuse;
588 mode_t mode = va->va_mode;
589 const char *path = PCNPATH(pcn);
590 int ret;
591
592 fuse = puffs_getspecific(pu);
593
594 SET_FUSE_CONTEXT_UID_GID(&fcon, &pcn->pcn_cred);
595 SET_FUSE_CONTEXT_PID(&fcon, pcn->pcn_pid);
596
597 if (fuse->op.mkdir == NULL) {
598 return ENOSYS;
599 }
600
601 /* wrap up return code */
602 ret = (*fuse->op.mkdir)(path, mode);
603
604 if (ret == 0) {
605 ret = fuse_newnode(pu, path, va, NULL, newnode);
606 }
607
608 return -ret;
609 }
610
611 /*
612 * create a regular file
613 *
614 * since linux/fuse sports using mknod for creating regular files
615 * instead of having a separate call for it in some versions, if
616 * we don't have create, just jump to op->mknod.
617 */
618 /*ARGSUSED1*/
619 static int
620 puffs_fuse_node_create(struct puffs_cc *pcc, void *opc, void **newnode,
621 const struct puffs_cn *pcn, const struct vattr *va)
622 {
623 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
624 struct fuse *fuse;
625 struct fuse_file_info fi;
626 mode_t mode = va->va_mode;
627 const char *path = PCNPATH(pcn);
628 int ret, created;
629
630 fuse = puffs_getspecific(pu);
631
632 SET_FUSE_CONTEXT_UID_GID(&fcon, &pcn->pcn_cred);
633 SET_FUSE_CONTEXT_PID(&fcon, pcn->pcn_pid);
634
635 created = 0;
636 if (fuse->op.create) {
637 ret = fuse->op.create(path, mode, &fi);
638 if (ret == 0)
639 created = 1;
640
641 } else if (fuse->op.mknod) {
642 /* XXX - no-one can remember why the context uid/gid are taken from the vattr uid/gid */
643 fcon.uid = va->va_uid; /*XXX*/
644 fcon.gid = va->va_gid; /*XXX*/
645
646 ret = fuse->op.mknod(path, mode | S_IFREG, 0);
647
648 } else {
649 ret = -ENOSYS;
650 }
651
652 if (ret == 0) {
653 ret = fuse_newnode(pu, path, va, &fi, newnode);
654
655 /* sweet.. create also open the file */
656 if (created) {
657 struct puffs_node *pn;
658 struct refusenode *rn;
659
660 pn = *newnode;
661 rn = pn->pn_data;
662 rn->flags |= RN_OPEN;
663 rn->opencount++;
664 }
665 }
666
667 return -ret;
668 }
669
670 /* remove the directory entry */
671 /* ARGSUSED1 */
672 static int
673 puffs_fuse_node_remove(struct puffs_cc *pcc, void *opc, void *targ,
674 const struct puffs_cn *pcn)
675 {
676 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
677 struct puffs_node *pn_targ = targ;
678 struct fuse *fuse;
679 const char *path = PNPATH(pn_targ);
680 int ret;
681
682 fuse = puffs_getspecific(pu);
683
684 SET_FUSE_CONTEXT_UID_GID(&fcon, &pcn->pcn_cred);
685 SET_FUSE_CONTEXT_PID(&fcon, pcn->pcn_pid);
686
687 if (fuse->op.unlink == NULL) {
688 return ENOSYS;
689 }
690
691 /* wrap up return code */
692 ret = (*fuse->op.unlink)(path);
693
694 return -ret;
695 }
696
697 /* remove the directory */
698 /* ARGSUSED1 */
699 static int
700 puffs_fuse_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ,
701 const struct puffs_cn *pcn)
702 {
703 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
704 struct puffs_node *pn_targ = targ;
705 struct fuse *fuse;
706 const char *path = PNPATH(pn_targ);
707 int ret;
708
709 fuse = puffs_getspecific(pu);
710
711 SET_FUSE_CONTEXT_UID_GID(&fcon, &pcn->pcn_cred);
712 SET_FUSE_CONTEXT_PID(&fcon, pcn->pcn_pid);
713
714 if (fuse->op.rmdir == NULL) {
715 return ENOSYS;
716 }
717
718 /* wrap up return code */
719 ret = (*fuse->op.rmdir)(path);
720
721 return -ret;
722 }
723
724 /* create a symbolic link */
725 /* ARGSUSED1 */
726 static int
727 puffs_fuse_node_symlink(struct puffs_cc *pcc, void *opc, void **newnode,
728 const struct puffs_cn *pcn_src, const struct vattr *va,
729 const char *link_target)
730 {
731 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
732 struct fuse *fuse;
733 const char *path = PCNPATH(pcn_src);
734 int ret;
735
736 fuse = puffs_getspecific(pu);
737
738 SET_FUSE_CONTEXT_UID_GID(&fcon, &pcn_src->pcn_cred);
739 SET_FUSE_CONTEXT_PID(&fcon, pcn_src->pcn_pid);
740
741 if (fuse->op.symlink == NULL) {
742 return ENOSYS;
743 }
744
745 /* wrap up return code */
746 ret = fuse->op.symlink(link_target, path);
747
748 if (ret == 0) {
749 ret = fuse_newnode(pu, path, va, NULL, newnode);
750 }
751
752 return -ret;
753 }
754
755 /* rename a directory entry */
756 /* ARGSUSED1 */
757 static int
758 puffs_fuse_node_rename(struct puffs_cc *pcc, void *opc, void *src,
759 const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
760 const struct puffs_cn *pcn_targ)
761 {
762 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
763 struct fuse *fuse;
764 const char *path_src = PCNPATH(pcn_src);
765 const char *path_dest = PCNPATH(pcn_targ);
766 int ret;
767
768 fuse = puffs_getspecific(pu);
769
770 SET_FUSE_CONTEXT_UID_GID(&fcon, &pcn_targ->pcn_cred);
771 SET_FUSE_CONTEXT_PID(&fcon, pcn_targ->pcn_pid);
772
773 if (fuse->op.rename == NULL) {
774 return ENOSYS;
775 }
776
777 ret = fuse->op.rename(path_src, path_dest);
778
779 if (ret == 0) {
780 }
781
782 return -ret;
783 }
784
785 /* create a link in the file system */
786 /* ARGSUSED1 */
787 static int
788 puffs_fuse_node_link(struct puffs_cc *pcc, void *opc, void *targ,
789 const struct puffs_cn *pcn)
790 {
791 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
792 struct puffs_node *pn = targ;
793 struct fuse *fuse;
794 int ret;
795
796 fuse = puffs_getspecific(pu);
797
798 SET_FUSE_CONTEXT_UID_GID(&fcon, &pcn->pcn_cred);
799 SET_FUSE_CONTEXT_PID(&fcon, pcn->pcn_pid);
800
801 if (fuse->op.link == NULL) {
802 return ENOSYS;
803 }
804
805 /* wrap up return code */
806 ret = (*fuse->op.link)(PNPATH(pn), PCNPATH(pcn));
807
808 return -ret;
809 }
810
811 /*
812 * fuse's regular interface provides chmod(), chown(), utimes()
813 * and truncate() + some variations, so try to fit the square block
814 * in the circle hole and the circle block .... something like that
815 */
816 /* ARGSUSED3 */
817 static int
818 puffs_fuse_node_setattr(struct puffs_cc *pcc, void *opc,
819 const struct vattr *va, const struct puffs_cred *pcr, pid_t pid)
820 {
821 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
822 struct puffs_node *pn = opc;
823 struct fuse *fuse;
824 const char *path = PNPATH(pn);
825
826 fuse = puffs_getspecific(pu);
827
828 SET_FUSE_CONTEXT_UID_GID(&fcon, pcr);
829 SET_FUSE_CONTEXT_PID(&fcon, pid);
830
831 return fuse_setattr(fuse, pn, path, va);
832 }
833
834 /* ARGSUSED2 */
835 static int
836 puffs_fuse_node_open(struct puffs_cc *pcc, void *opc, int mode,
837 const struct puffs_cred *cred, pid_t pid)
838 {
839 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
840 struct puffs_node *pn = opc;
841 struct refusenode *rn = pn->pn_data;
842 struct fuse_file_info *fi = &rn->file_info;
843 struct fuse *fuse;
844 const char *path = PNPATH(pn);
845
846 fuse = puffs_getspecific(pu);
847
848 SET_FUSE_CONTEXT_UID_GID(&fcon, cred);
849 SET_FUSE_CONTEXT_PID(&fcon, pid);
850
851 /* if open, don't open again, lest risk nuking file private info */
852 if (rn->flags & RN_OPEN) {
853 rn->opencount++;
854 return 0;
855 }
856
857 /* OFLAGS(), need to convert FREAD/FWRITE to O_RD/WR */
858 fi->flags = (mode & ~(O_CREAT | O_EXCL | O_TRUNC)) - 1;
859
860 if (pn->pn_va.va_type == VDIR) {
861 if (fuse->op.opendir)
862 fuse->op.opendir(path, fi);
863 } else {
864 if (fuse->op.open)
865 fuse->op.open(path, fi);
866 }
867
868 rn->flags |= RN_OPEN;
869 rn->opencount++;
870
871 return 0;
872 }
873
874 /* ARGSUSED2 */
875 static int
876 puffs_fuse_node_close(struct puffs_cc *pcc, void *opc, int fflag,
877 const struct puffs_cred *pcr, pid_t pid)
878 {
879 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
880 struct puffs_node *pn = opc;
881 struct refusenode *rn = pn->pn_data;
882 struct fuse *fuse;
883 struct fuse_file_info *fi;
884 const char *path = PNPATH(pn);
885 int ret;
886
887 fuse = puffs_getspecific(pu);
888 fi = &rn->file_info;
889 ret = 0;
890
891 SET_FUSE_CONTEXT_UID_GID(&fcon, pcr);
892 SET_FUSE_CONTEXT_PID(&fcon, pid);
893
894 if (rn->flags & RN_OPEN) {
895 if (pn->pn_va.va_type == VDIR) {
896 if (fuse->op.releasedir)
897 ret = fuse->op.releasedir(path, fi);
898 } else {
899 if (fuse->op.release)
900 ret = fuse->op.release(path, fi);
901 }
902 }
903 rn->flags &= ~RN_OPEN;
904 rn->opencount--;
905
906 return ret;
907 }
908
909 /* read some more from the file */
910 /* ARGSUSED5 */
911 static int
912 puffs_fuse_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
913 off_t offset, size_t *resid, const struct puffs_cred *pcr,
914 int ioflag)
915 {
916 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
917 struct puffs_node *pn = opc;
918 struct refusenode *rn = pn->pn_data;
919 struct fuse *fuse;
920 const char *path = PNPATH(pn);
921 size_t maxread;
922 int ret;
923
924 fuse = puffs_getspecific(pu);
925 if (fuse->op.read == NULL) {
926 return ENOSYS;
927 }
928
929 SET_FUSE_CONTEXT_UID_GID(&fcon, pcr);
930
931 maxread = *resid;
932 if (maxread > pn->pn_va.va_size - offset) {
933 /*LINTED*/
934 maxread = pn->pn_va.va_size - offset;
935 }
936 if (maxread == 0)
937 return 0;
938
939 ret = (*fuse->op.read)(path, (char *)buf, maxread, offset,
940 &rn->file_info);
941
942 if (ret > 0) {
943 *resid -= ret;
944 ret = 0;
945 }
946
947 return -ret;
948 }
949
950 /* write to the file */
951 /* ARGSUSED0 */
952 static int
953 puffs_fuse_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
954 off_t offset, size_t *resid, const struct puffs_cred *pcr,
955 int ioflag)
956 {
957 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
958 struct puffs_node *pn = opc;
959 struct refusenode *rn = pn->pn_data;
960 struct fuse *fuse;
961 const char *path = PNPATH(pn);
962 int ret;
963
964 fuse = puffs_getspecific(pu);
965 if (fuse->op.write == NULL) {
966 return ENOSYS;
967 }
968
969 SET_FUSE_CONTEXT_UID_GID(&fcon, pcr);
970
971 if (ioflag & PUFFS_IO_APPEND)
972 offset = pn->pn_va.va_size;
973
974 ret = (*fuse->op.write)(path, (char *)buf, *resid, offset,
975 &rn->file_info);
976
977 if (ret > 0) {
978 if (offset + ret > pn->pn_va.va_size)
979 pn->pn_va.va_size = offset + ret;
980 *resid -= ret;
981 ret = 0;
982 }
983
984 return -ret;
985 }
986
987
988 /* ARGSUSED3 */
989 static int
990 puffs_fuse_node_readdir(struct puffs_cc *pcc, void *opc, struct dirent *dent,
991 off_t *readoff, size_t *reslen, const struct puffs_cred *pcr,
992 int *eofflag, off_t *cookies, size_t *ncookies)
993 {
994 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
995 struct puffs_node *pn = opc;
996 struct refusenode *rn = pn->pn_data;
997 struct puffs_fuse_dirh *dirh;
998 struct fuse *fuse;
999 struct dirent *fromdent;
1000 const char *path = PNPATH(pn);
1001 int ret;
1002
1003 fuse = puffs_getspecific(pu);
1004 if (fuse->op.readdir == NULL && fuse->op.getdir == NULL) {
1005 return ENOSYS;
1006 }
1007
1008 SET_FUSE_CONTEXT_UID_GID(&fcon, pcr);
1009
1010 if (pn->pn_va.va_type != VDIR)
1011 return ENOTDIR;
1012
1013 dirh = &rn->dirh;
1014
1015 /*
1016 * if we are starting from the beginning, slurp entire directory
1017 * into our buffers
1018 */
1019 if (*readoff == 0) {
1020 /* free old buffers */
1021 free(dirh->dbuf);
1022 memset(dirh, 0, sizeof(struct puffs_fuse_dirh));
1023
1024 if (fuse->op.readdir)
1025 ret = fuse->op.readdir(path, dirh, puffs_fuse_fill_dir,
1026 0, &rn->file_info);
1027 else
1028 ret = fuse->op.getdir(path, dirh, puffs_fuse_dirfil);
1029 if (ret)
1030 return -ret;
1031 }
1032
1033 /* now, stuff results into the kernel buffers */
1034 while (*readoff < dirh->bufsize - dirh->reslen) {
1035 /*LINTED*/
1036 fromdent = (struct dirent *)((uint8_t *)dirh->dbuf + *readoff);
1037
1038 if (*reslen < _DIRENT_SIZE(fromdent))
1039 break;
1040
1041 memcpy(dent, fromdent, _DIRENT_SIZE(fromdent));
1042 *readoff += _DIRENT_SIZE(fromdent);
1043 *reslen -= _DIRENT_SIZE(fromdent);
1044
1045 dent = _DIRENT_NEXT(dent);
1046 }
1047
1048 return 0;
1049 }
1050
1051 /* ARGSUSED */
1052 static int
1053 puffs_fuse_node_reclaim(struct puffs_cc *pcc, void *opc, pid_t pid)
1054 {
1055 struct puffs_node *pn = opc;
1056
1057 nukern(pn);
1058
1059 SET_FUSE_CONTEXT_PID(&fcon, pid);
1060
1061 return 0;
1062 }
1063
1064 /* ARGSUSED1 */
1065 static int
1066 puffs_fuse_fs_unmount(struct puffs_cc *pcc, int flags, pid_t pid)
1067 {
1068 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
1069 struct fuse *fuse;
1070
1071 fuse = puffs_getspecific(pu);
1072 SET_FUSE_CONTEXT_PID(&fcon, pid);
1073 if (fuse->op.destroy == NULL) {
1074 return 0;
1075 }
1076 (*fuse->op.destroy)(fuse);
1077 return 0;
1078 }
1079
1080 /* ARGSUSED0 */
1081 static int
1082 puffs_fuse_fs_sync(struct puffs_cc *pcc, int flags,
1083 const struct puffs_cred *cr, pid_t pid)
1084 {
1085 SET_FUSE_CONTEXT_UID_GID(&fcon, cr);
1086 SET_FUSE_CONTEXT_PID(&fcon, pid);
1087 return 0;
1088 }
1089
1090 /* ARGSUSED2 */
1091 static int
1092 puffs_fuse_fs_statvfs(struct puffs_cc *pcc, struct statvfs *svfsb, pid_t pid)
1093 {
1094 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
1095 struct fuse *fuse;
1096 int ret;
1097
1098 fuse = puffs_getspecific(pu);
1099 SET_FUSE_CONTEXT_PID(&fcon, pid);
1100 if (fuse->op.statfs == NULL) {
1101 if ((ret = statvfs(PNPATH(puffs_getroot(pu)), svfsb)) == -1) {
1102 return errno;
1103 }
1104 } else {
1105 ret = fuse->op.statfs(PNPATH(puffs_getroot(pu)), svfsb);
1106 }
1107
1108 return ret;
1109 }
1110
1111
1112 /* End of puffs_fuse operations */
1113 /* ARGSUSED3 */
1114 int
1115 fuse_main_real(int argc, char **argv, const struct fuse_operations *ops,
1116 size_t size, void *userdata)
1117 {
1118 struct fuse *fuse;
1119 char *mountpoint;
1120 int multithreaded;
1121 int fd;
1122
1123 fuse = fuse_setup(argc, argv, ops, size, &mountpoint, &multithreaded,
1124 &fd);
1125
1126 return fuse_loop(fuse);
1127 }
1128
1129 /*
1130 * XXX: just defer the operation until fuse_new() when we have more
1131 * info on our hands. The real beef is why's this separate in fuse in
1132 * the first place?
1133 */
1134 /* ARGSUSED1 */
1135 struct fuse_chan *
1136 fuse_mount(const char *dir, struct fuse_args *args)
1137 {
1138 struct fuse_chan *fc;
1139 char name[64];
1140
1141 if ((fc = calloc(1, sizeof(*fc))) == NULL) {
1142 err(EXIT_FAILURE, "fuse_mount");
1143 }
1144 fc->dead = 0;
1145
1146 if ((fc->dir = strdup(dir)) == NULL) {
1147 err(EXIT_FAILURE, "fuse_mount");
1148 }
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 = calloc(1, sizeof(*fuse))) == NULL) {
1184 err(EXIT_FAILURE, "fuse_new");
1185 }
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