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