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