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