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