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