1 /* $NetBSD: refuse.c,v 1.96 2012/12/30 10:04:22 tron Exp $ */
4 * Copyright © 2007 Alistair Crooks. All rights reserved.
5 * Copyright © 2007 Antti Kantee. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote
16 * products derived from this software without specific prior written
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
34 __RCSID("$NetBSD: refuse.c,v 1.96 2012/12/30 10:04:22 tron Exp $");
37 #include <sys/types.h>
48 #ifdef MULTITHREADED_REFUSE
52 typedef uint64_t fuse_ino_t
;
59 double negative_timeout
;
61 double ac_attr_timeout
;
62 int ac_attr_timeout_set
;
79 struct fuse_args
*args
;
80 struct puffs_usermount
*pu
;
84 /* this is the private fuse structure */
86 struct fuse_chan
*fc
; /* fuse channel pointer */
87 struct fuse_operations op
; /* switch table of operations */
88 int compat
; /* compat level -
89 * not used in puffs_fuse */
90 struct node
**name_table
;
91 size_t name_table_size
;
92 struct node
**id_table
;
95 unsigned int generation
;
99 pthread_rwlock_t tree_lock
;
100 #endif /* !defined(__minix) */
102 struct fuse_config conf
;
106 struct puffs_fuse_dirh
{
115 struct fuse_file_info file_info
;
116 struct puffs_fuse_dirh dirh
;
121 #define RN_OPEN 0x02 /* XXX: could just use opencount */
123 static int fuse_setattr(struct fuse
*, struct puffs_node
*,
124 const char *, const struct vattr
*);
126 static struct puffs_node
*
127 newrn(struct puffs_usermount
*pu
)
129 struct puffs_node
*pn
;
130 struct refusenode
*rn
;
132 if ((rn
= calloc(1, sizeof(*rn
))) == NULL
) {
133 err(EXIT_FAILURE
, "newrn");
135 pn
= puffs_pn_new(pu
, rn
);
141 nukern(struct puffs_node
*pn
)
143 struct refusenode
*rn
= pn
->pn_data
;
150 /* XXX - not threadsafe */
151 static ino_t fakeino
= 3;
153 /***************** start of pthread context routines ************************/
156 * Notes on fuse_context:
157 * we follow fuse's lead and use the pthread specific information to hold
158 * a reference to the fuse_context structure for this thread.
160 #ifdef MULTITHREADED_REFUSE
161 static pthread_mutex_t context_mutex
= PTHREAD_MUTEX_INITIALIZER
;
162 static pthread_key_t context_key
;
163 static unsigned long context_refc
;
166 /* return the fuse_context struct related to this thread */
167 struct fuse_context
*
168 fuse_get_context(void)
170 #ifdef MULTITHREADED_REFUSE
171 struct fuse_context
*ctxt
;
173 if ((ctxt
= pthread_getspecific(context_key
)) == NULL
) {
174 if ((ctxt
= calloc(1, sizeof(struct fuse_context
))) == NULL
) {
177 pthread_setspecific(context_key
, ctxt
);
181 static struct fuse_context fcon
;
187 /* used as a callback function */
188 #ifdef MULTITHREADED_REFUSE
190 free_context(void *ctxt
)
197 * Create the pthread key. The reason for the complexity is to
198 * enable use of multiple fuse instances within a single process.
201 create_context_key(void)
203 #ifdef MULTITHREADED_REFUSE
206 rv
= pthread_mutex_lock(&context_mutex
);
209 if (context_refc
== 0) {
210 if (pthread_key_create(&context_key
, free_context
) != 0) {
211 warnx("create_context_key: pthread_key_create failed");
212 pthread_mutex_unlock(&context_mutex
);
217 pthread_mutex_unlock(&context_mutex
);
225 delete_context_key(void)
227 #ifdef MULTITHREADED_REFUSE
228 pthread_mutex_lock(&context_mutex
);
229 /* If we are the last fuse instances using the key, delete it */
230 if (--context_refc
== 0) {
231 free(pthread_getspecific(context_key
));
232 pthread_key_delete(context_key
);
234 pthread_mutex_unlock(&context_mutex
);
238 /* set the uid and gid of the calling process in the current fuse context */
240 set_fuse_context_uid_gid(const struct puffs_cred
*cred
)
242 struct fuse_context
*fusectx
;
246 fusectx
= fuse_get_context();
247 if (puffs_cred_getuid(cred
, &uid
) == 0) {
250 if (puffs_cred_getgid(cred
, &gid
) == 0) {
255 /* set the pid of the calling process in the current fuse context */
257 set_fuse_context_pid(struct puffs_usermount
*pu
)
259 struct puffs_cc
*pcc
= puffs_cc_getcc(pu
);
260 struct fuse_context
*fusectx
;
262 fusectx
= fuse_get_context();
263 puffs_cc_getcaller(pcc
, &fusectx
->pid
, NULL
);
266 /***************** end of pthread context routines ************************/
268 #define DIR_CHUNKSIZE 4096
270 fill_dirbuf(struct puffs_fuse_dirh
*dh
, const char *name
, ino_t dino
,
275 if (dh
->bufsize
== 0) {
276 if ((dh
->dbuf
= calloc(1, DIR_CHUNKSIZE
)) == NULL
) {
280 dh
->reslen
= dh
->bufsize
= DIR_CHUNKSIZE
;
283 if (puffs_nextdent(&dh
->d
, name
, dino
, dtype
, &dh
->reslen
)) {
287 /* try to increase buffer space */
288 dh
->dbuf
= realloc(dh
->dbuf
, dh
->bufsize
+ DIR_CHUNKSIZE
);
289 if (dh
->dbuf
== NULL
) {
292 dh
->d
= (void *)((uint8_t *)dh
->dbuf
+ (dh
->bufsize
- dh
->reslen
));
293 dh
->reslen
+= DIR_CHUNKSIZE
;
294 dh
->bufsize
+= DIR_CHUNKSIZE
;
296 return !puffs_nextdent(&dh
->d
, name
, dino
, dtype
, &dh
->reslen
);
300 /* XXX: I have no idea how "off" is supposed to be used */
302 puffs_fuse_fill_dir(void *buf
, const char *name
,
303 const struct stat
*stbuf
, off_t off
)
305 struct puffs_fuse_dirh
*deh
= buf
;
313 dtype
= puffs_vtype2dt(puffs_mode2vt(stbuf
->st_mode
));
314 dino
= stbuf
->st_ino
;
317 * Some FUSE file systems like to always use 0 as the
318 * inode number. Our readdir() doesn't like to show
319 * directory entries with inode number 0 ==> workaround.
326 return fill_dirbuf(deh
, name
, dino
, dtype
);
330 puffs_fuse_dirfil(fuse_dirh_t h
, const char *name
, int type
, ino_t ino
)
335 if ((dtype
= type
) == 0) {
339 dino
= (ino
) ? ino
: fakeino
++;
341 return fill_dirbuf(h
, name
, dino
, dtype
);
344 /* place the refuse file system name into `name' */
346 set_refuse_mount_name(char **argv
, char *name
, size_t size
)
350 if (argv
== NULL
|| *argv
== NULL
) {
351 (void) strlcpy(name
, "refuse", size
);
353 if ((slash
= strrchr(*argv
, '/')) == NULL
) {
358 if (strncmp(*argv
, "refuse:", 7) == 0) {
359 /* we've already done this */
360 (void) strlcpy(name
, *argv
, size
);
362 (void) snprintf(name
, size
, "refuse:%s", slash
);
368 /* this function exposes struct fuse to userland */
370 fuse_setup_real(int argc
, char **argv
, const struct fuse_operations
*ops
,
371 size_t size
, char **mountpoint
, int *multithreaded
, int *fd
,
374 struct fuse_chan
*fc
;
375 struct fuse_args
*args
;
380 /* set up the proper name */
381 set_refuse_mount_name(argv
, name
, sizeof(name
));
383 /* grab the pthread context key */
384 if (!create_context_key()) {
388 /* stuff name into fuse_args */
389 args
= fuse_opt_deep_copy_args(argc
, argv
);
390 if (args
->argc
> 0) {
393 if ((args
->argv
[0] = strdup(name
)) == NULL
) {
394 fuse_opt_free_args(args
);
398 /* count back from the end over arguments starting with '-' */
399 for (i
= argc
- 1 ; i
> 0 && *argv
[i
] == '-' ; --i
) {
402 fc
= fuse_mount(*mountpoint
= argv
[i
], args
);
403 fuse
= fuse_new(fc
, args
, ops
, size
, user_data
);
405 fuse_opt_free_args(args
);
408 /* XXX - wait for puffs to become multi-threaded */
413 /* XXX - this is unused */
426 fuse_setup(int argc
, char **argv
, const struct fuse_operations
*ops
,
427 size_t size
, char **mountpoint
, int *multithreaded
, int *fd
)
429 return fuse_setup_real(argc
, argv
, ops
, size
, mountpoint
,
430 multithreaded
, fd
, NULL
);
434 fuse_setup26(int argc
, char **argv
, const struct fuse_operations
*ops
,
435 size_t size
, char **mountpoint
, int *multithreaded
, void *user_data
)
437 return fuse_setup_real(argc
, argv
, ops
, size
, mountpoint
,
438 multithreaded
, NULL
, user_data
);
441 #define FUSE_ERR_UNLINK(fuse, file) if (fuse->op.unlink) fuse->op.unlink(file)
442 #define FUSE_ERR_RMDIR(fuse, dir) if (fuse->op.rmdir) fuse->op.rmdir(dir)
446 fuse_getattr(struct fuse
*fuse
, struct puffs_node
*pn
, const char *path
,
452 if (fuse
->op
.getattr
== NULL
) {
456 /* wrap up return code */
457 memset(&st
, 0, sizeof(st
));
458 ret
= (*fuse
->op
.getattr
)(path
, &st
);
461 if (st
.st_blksize
== 0)
462 st
.st_blksize
= DEV_BSIZE
;
463 puffs_stat2vattr(va
, &st
);
469 /* utility function to set various elements of the attribute */
471 fuse_setattr(struct fuse
*fuse
, struct puffs_node
*pn
, const char *path
,
472 const struct vattr
*va
)
474 struct refusenode
*rn
= pn
->pn_data
;
486 if (mode
!= (mode_t
)PUFFS_VNOVAL
) {
489 if (fuse
->op
.chmod
== NULL
) {
492 ret
= fuse
->op
.chmod(path
, mode
);
497 if (uid
!= (uid_t
)PUFFS_VNOVAL
|| gid
!= (gid_t
)PUFFS_VNOVAL
) {
500 if (fuse
->op
.chown
== NULL
) {
503 ret
= fuse
->op
.chown(path
, uid
, gid
);
508 if (va
->va_atime
.tv_sec
!= (time_t)PUFFS_VNOVAL
509 || va
->va_mtime
.tv_sec
!= (long)PUFFS_VNOVAL
) {
512 if (fuse
->op
.utimens
) {
513 struct timespec tv
[2];
515 tv
[0].tv_sec
= va
->va_atime
.tv_sec
;
516 tv
[0].tv_nsec
= va
->va_atime
.tv_nsec
;
517 tv
[1].tv_sec
= va
->va_mtime
.tv_sec
;
518 tv
[1].tv_nsec
= va
->va_mtime
.tv_nsec
;
520 ret
= fuse
->op
.utimens(path
, tv
);
521 } else if (fuse
->op
.utime
) {
522 struct utimbuf timbuf
;
524 timbuf
.actime
= va
->va_atime
.tv_sec
;
525 timbuf
.modtime
= va
->va_mtime
.tv_sec
;
527 ret
= fuse
->op
.utime(path
, &timbuf
);
535 if (va
->va_size
!= (u_quad_t
)PUFFS_VNOVAL
) {
538 if (fuse
->op
.truncate
) {
539 ret
= fuse
->op
.truncate(path
, (off_t
)va
->va_size
);
540 } else if (fuse
->op
.ftruncate
) {
541 ret
= fuse
->op
.ftruncate(path
, (off_t
)va
->va_size
,
550 /* XXX: no reflection with reality */
551 puffs_setvattr(&pn
->pn_va
, va
);
558 fuse_newnode(struct puffs_usermount
*pu
, const char *path
,
559 const struct vattr
*va
, struct fuse_file_info
*fi
,
560 struct puffs_newinfo
*pni
, struct puffs_node
**pn_new
)
562 struct puffs_node
*pn
;
563 struct refusenode
*rn
;
567 fuse
= puffs_getspecific(pu
);
572 if (va
->va_type
== VDIR
) {
573 FUSE_ERR_RMDIR(fuse
, path
);
575 FUSE_ERR_UNLINK(fuse
, path
);
579 fuse_setattr(fuse
, pn
, path
, va
);
580 if (fuse_getattr(fuse
, pn
, path
, &newva
) == 0)
581 puffs_setvattr(&pn
->pn_va
, &newva
);
585 memcpy(&rn
->file_info
, fi
, sizeof(struct fuse_file_info
));
587 puffs_newinfo_setcookie(pni
, pn
);
595 /* operation wrappers start here */
597 /* lookup the path */
600 puffs_fuse_node_lookup(struct puffs_usermount
*pu
, void *opc
,
601 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
)
603 struct puffs_node
*pn_res
;
606 const char *path
= PCNPATH(pcn
);
609 fuse
= puffs_getspecific(pu
);
611 set_fuse_context_uid_gid(pcn
->pcn_cred
);
613 ret
= fuse
->op
.getattr(path
, &st
);
619 /* XXX: fiXXXme unconst */
620 pn_res
= puffs_pn_nodewalk(pu
, puffs_path_walkcmp
,
621 __UNCONST(&pcn
->pcn_po_full
));
622 if (pn_res
== NULL
) {
626 puffs_stat2vattr(&pn_res
->pn_va
, &st
);
629 puffs_newinfo_setcookie(pni
, pn_res
);
630 puffs_newinfo_setvtype(pni
, pn_res
->pn_va
.va_type
);
631 puffs_newinfo_setsize(pni
, (voff_t
)pn_res
->pn_va
.va_size
);
632 puffs_newinfo_setrdev(pni
, pn_res
->pn_va
.va_rdev
);
637 /* get attributes for the path name */
640 puffs_fuse_node_getattr(struct puffs_usermount
*pu
, void *opc
, struct vattr
*va
,
641 const struct puffs_cred
*pcr
)
643 struct puffs_node
*pn
= opc
;
645 const char *path
= PNPATH(pn
);
647 fuse
= puffs_getspecific(pu
);
649 set_fuse_context_uid_gid(pcr
);
651 return fuse_getattr(fuse
, pn
, path
, va
);
654 /* read the contents of the symbolic link */
657 puffs_fuse_node_readlink(struct puffs_usermount
*pu
, void *opc
,
658 const struct puffs_cred
*cred
, char *linkname
, size_t *linklen
)
660 struct puffs_node
*pn
= opc
;
662 const char *path
= PNPATH(pn
), *p
;
665 fuse
= puffs_getspecific(pu
);
666 if (fuse
->op
.readlink
== NULL
) {
670 set_fuse_context_uid_gid(cred
);
672 /* wrap up return code */
673 ret
= (*fuse
->op
.readlink
)(path
, linkname
, *linklen
);
676 p
= memchr(linkname
, '\0', *linklen
);
680 *linklen
= p
- linkname
;
686 /* make the special node */
689 puffs_fuse_node_mknod(struct puffs_usermount
*pu
, void *opc
,
690 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
,
691 const struct vattr
*va
)
695 const char *path
= PCNPATH(pcn
);
698 fuse
= puffs_getspecific(pu
);
699 if (fuse
->op
.mknod
== NULL
) {
703 set_fuse_context_uid_gid(pcn
->pcn_cred
);
705 /* wrap up return code */
706 mode
= puffs_addvtype2mode(va
->va_mode
, va
->va_type
);
707 ret
= (*fuse
->op
.mknod
)(path
, mode
, va
->va_rdev
);
710 ret
= fuse_newnode(pu
, path
, va
, NULL
, pni
, NULL
);
716 /* make a directory */
719 puffs_fuse_node_mkdir(struct puffs_usermount
*pu
, void *opc
,
720 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
,
721 const struct vattr
*va
)
724 mode_t mode
= va
->va_mode
;
725 const char *path
= PCNPATH(pcn
);
728 fuse
= puffs_getspecific(pu
);
730 set_fuse_context_uid_gid(pcn
->pcn_cred
);
732 if (fuse
->op
.mkdir
== NULL
) {
736 /* wrap up return code */
737 ret
= (*fuse
->op
.mkdir
)(path
, mode
);
740 ret
= fuse_newnode(pu
, path
, va
, NULL
, pni
, NULL
);
747 * create a regular file
749 * since linux/fuse sports using mknod for creating regular files
750 * instead of having a separate call for it in some versions, if
751 * we don't have create, just jump to op->mknod.
755 puffs_fuse_node_create(struct puffs_usermount
*pu
, void *opc
,
756 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
,
757 const struct vattr
*va
)
760 struct fuse_file_info fi
;
761 struct puffs_node
*pn
;
762 mode_t mode
= va
->va_mode
;
763 const char *path
= PCNPATH(pcn
);
766 fuse
= puffs_getspecific(pu
);
768 set_fuse_context_uid_gid(pcn
->pcn_cred
);
771 if (fuse
->op
.create
) {
772 ret
= fuse
->op
.create(path
, mode
| S_IFREG
, &fi
);
776 } else if (fuse
->op
.mknod
) {
777 ret
= fuse
->op
.mknod(path
, mode
| S_IFREG
, 0);
784 ret
= fuse_newnode(pu
, path
, va
, &fi
, pni
, &pn
);
786 /* sweet.. create also open the file */
788 struct refusenode
*rn
;
791 rn
->flags
|= RN_OPEN
;
799 /* remove the directory entry */
802 puffs_fuse_node_remove(struct puffs_usermount
*pu
, void *opc
, void *targ
,
803 const struct puffs_cn
*pcn
)
805 struct puffs_node
*pn_targ
= targ
;
807 const char *path
= PNPATH(pn_targ
);
810 fuse
= puffs_getspecific(pu
);
812 set_fuse_context_uid_gid(pcn
->pcn_cred
);
814 if (fuse
->op
.unlink
== NULL
) {
818 /* wrap up return code */
819 ret
= (*fuse
->op
.unlink
)(path
);
824 /* remove the directory */
827 puffs_fuse_node_rmdir(struct puffs_usermount
*pu
, void *opc
, void *targ
,
828 const struct puffs_cn
*pcn
)
830 struct puffs_node
*pn_targ
= targ
;
832 const char *path
= PNPATH(pn_targ
);
835 fuse
= puffs_getspecific(pu
);
837 set_fuse_context_uid_gid(pcn
->pcn_cred
);
839 if (fuse
->op
.rmdir
== NULL
) {
843 /* wrap up return code */
844 ret
= (*fuse
->op
.rmdir
)(path
);
849 /* create a symbolic link */
852 puffs_fuse_node_symlink(struct puffs_usermount
*pu
, void *opc
,
853 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn_src
,
854 const struct vattr
*va
, const char *link_target
)
857 const char *path
= PCNPATH(pcn_src
);
860 fuse
= puffs_getspecific(pu
);
862 set_fuse_context_uid_gid(pcn_src
->pcn_cred
);
864 if (fuse
->op
.symlink
== NULL
) {
868 /* wrap up return code */
869 ret
= fuse
->op
.symlink(link_target
, path
);
872 ret
= fuse_newnode(pu
, path
, va
, NULL
, pni
, NULL
);
878 /* rename a directory entry */
881 puffs_fuse_node_rename(struct puffs_usermount
*pu
, void *opc
, void *src
,
882 const struct puffs_cn
*pcn_src
, void *targ_dir
, void *targ
,
883 const struct puffs_cn
*pcn_targ
)
886 const char *path_src
= PCNPATH(pcn_src
);
887 const char *path_dest
= PCNPATH(pcn_targ
);
890 fuse
= puffs_getspecific(pu
);
892 set_fuse_context_uid_gid(pcn_targ
->pcn_cred
);
894 if (fuse
->op
.rename
== NULL
) {
898 ret
= fuse
->op
.rename(path_src
, path_dest
);
906 /* create a link in the file system */
909 puffs_fuse_node_link(struct puffs_usermount
*pu
, void *opc
, void *targ
,
910 const struct puffs_cn
*pcn
)
912 struct puffs_node
*pn
= targ
;
916 fuse
= puffs_getspecific(pu
);
918 set_fuse_context_uid_gid(pcn
->pcn_cred
);
920 if (fuse
->op
.link
== NULL
) {
924 /* wrap up return code */
925 ret
= (*fuse
->op
.link
)(PNPATH(pn
), PCNPATH(pcn
));
931 * fuse's regular interface provides chmod(), chown(), utimes()
932 * and truncate() + some variations, so try to fit the square block
933 * in the circle hole and the circle block .... something like that
937 puffs_fuse_node_setattr(struct puffs_usermount
*pu
, void *opc
,
938 const struct vattr
*va
, const struct puffs_cred
*pcr
)
940 struct puffs_node
*pn
= opc
;
942 const char *path
= PNPATH(pn
);
944 fuse
= puffs_getspecific(pu
);
946 set_fuse_context_uid_gid(pcr
);
948 return fuse_setattr(fuse
, pn
, path
, va
);
953 puffs_fuse_node_open(struct puffs_usermount
*pu
, void *opc
, int mode
,
954 const struct puffs_cred
*cred
)
956 struct puffs_node
*pn
= opc
;
957 struct refusenode
*rn
= pn
->pn_data
;
958 struct fuse_file_info
*fi
= &rn
->file_info
;
960 const char *path
= PNPATH(pn
);
962 fuse
= puffs_getspecific(pu
);
964 set_fuse_context_uid_gid(cred
);
966 /* if open, don't open again, lest risk nuking file private info */
967 if (rn
->flags
& RN_OPEN
) {
972 /* OFLAGS(), need to convert FREAD/FWRITE to O_RD/WR */
973 fi
->flags
= (mode
& ~(O_CREAT
| O_EXCL
| O_TRUNC
)) - 1;
975 if (pn
->pn_va
.va_type
== VDIR
) {
976 if (fuse
->op
.opendir
)
977 fuse
->op
.opendir(path
, fi
);
980 fuse
->op
.open(path
, fi
);
983 rn
->flags
|= RN_OPEN
;
991 puffs_fuse_node_close(struct puffs_usermount
*pu
, void *opc
, int fflag
,
992 const struct puffs_cred
*pcr
)
994 struct puffs_node
*pn
= opc
;
995 struct refusenode
*rn
= pn
->pn_data
;
997 struct fuse_file_info
*fi
;
998 const char *path
= PNPATH(pn
);
1001 fuse
= puffs_getspecific(pu
);
1002 fi
= &rn
->file_info
;
1005 set_fuse_context_uid_gid(pcr
);
1007 if (rn
->flags
& RN_OPEN
) {
1008 if (pn
->pn_va
.va_type
== VDIR
) {
1009 if (fuse
->op
.releasedir
)
1010 ret
= fuse
->op
.releasedir(path
, fi
);
1012 if (fuse
->op
.release
)
1013 ret
= fuse
->op
.release(path
, fi
);
1016 rn
->flags
&= ~RN_OPEN
;
1022 /* read some more from the file */
1025 puffs_fuse_node_read(struct puffs_usermount
*pu
, void *opc
, uint8_t *buf
,
1026 off_t offset
, size_t *resid
, const struct puffs_cred
*pcr
,
1029 struct puffs_node
*pn
= opc
;
1030 struct refusenode
*rn
= pn
->pn_data
;
1032 const char *path
= PNPATH(pn
);
1036 fuse
= puffs_getspecific(pu
);
1037 if (fuse
->op
.read
== NULL
) {
1041 set_fuse_context_uid_gid(pcr
);
1044 if (maxread
> pn
->pn_va
.va_size
- offset
) {
1046 maxread
= pn
->pn_va
.va_size
- offset
;
1051 ret
= (*fuse
->op
.read
)(path
, (char *)buf
, maxread
, offset
,
1062 /* write to the file */
1065 puffs_fuse_node_write(struct puffs_usermount
*pu
, void *opc
, uint8_t *buf
,
1066 off_t offset
, size_t *resid
, const struct puffs_cred
*pcr
,
1069 struct puffs_node
*pn
= opc
;
1070 struct refusenode
*rn
= pn
->pn_data
;
1072 const char *path
= PNPATH(pn
);
1075 fuse
= puffs_getspecific(pu
);
1076 if (fuse
->op
.write
== NULL
) {
1080 set_fuse_context_uid_gid(pcr
);
1082 if (ioflag
& PUFFS_IO_APPEND
)
1083 offset
= pn
->pn_va
.va_size
;
1085 ret
= (*fuse
->op
.write
)(path
, (char *)buf
, *resid
, offset
,
1089 if ((uint64_t)(offset
+ ret
) > pn
->pn_va
.va_size
)
1090 pn
->pn_va
.va_size
= offset
+ ret
;
1092 ret
= (*resid
== 0) ? 0 : ENOSPC
;
1103 puffs_fuse_node_readdir(struct puffs_usermount
*pu
, void *opc
,
1104 struct dirent
*dent
, off_t
*readoff
, size_t *reslen
,
1105 const struct puffs_cred
*pcr
, int *eofflag
,
1106 off_t
*cookies
, size_t *ncookies
)
1108 struct puffs_node
*pn
= opc
;
1109 struct refusenode
*rn
= pn
->pn_data
;
1110 struct puffs_fuse_dirh
*dirh
;
1112 struct dirent
*fromdent
;
1113 const char *path
= PNPATH(pn
);
1116 fuse
= puffs_getspecific(pu
);
1117 if (fuse
->op
.readdir
== NULL
&& fuse
->op
.getdir
== NULL
) {
1121 set_fuse_context_uid_gid(pcr
);
1123 if (pn
->pn_va
.va_type
!= VDIR
)
1129 * if we are starting from the beginning, slurp entire directory
1132 if (*readoff
== 0) {
1133 /* free old buffers */
1135 memset(dirh
, 0, sizeof(struct puffs_fuse_dirh
));
1137 if (fuse
->op
.readdir
)
1138 ret
= fuse
->op
.readdir(path
, dirh
, puffs_fuse_fill_dir
,
1141 ret
= fuse
->op
.getdir(path
, dirh
, puffs_fuse_dirfil
);
1146 /* Both op.readdir and op.getdir read full directory */
1149 /* now, stuff results into the kernel buffers */
1150 while (*readoff
< (off_t
)(dirh
->bufsize
- dirh
->reslen
)) {
1152 fromdent
= (struct dirent
*)((uint8_t *)dirh
->dbuf
+ *readoff
);
1154 if (*reslen
< _DIRENT_SIZE(fromdent
))
1157 memcpy(dent
, fromdent
, _DIRENT_SIZE(fromdent
));
1158 *readoff
+= _DIRENT_SIZE(fromdent
);
1159 *reslen
-= _DIRENT_SIZE(fromdent
);
1161 dent
= _DIRENT_NEXT(dent
);
1169 puffs_fuse_node_reclaim(struct puffs_usermount
*pu
, void *opc
)
1171 struct puffs_node
*pn
= opc
;
1179 puffs_fuse_fs_unmount(struct puffs_usermount
*pu
, int flags
)
1183 fuse
= puffs_getspecific(pu
);
1184 if (fuse
->op
.destroy
== NULL
) {
1187 (*fuse
->op
.destroy
)(fuse
);
1193 puffs_fuse_fs_sync(struct puffs_usermount
*pu
, int flags
,
1194 const struct puffs_cred
*cr
)
1196 set_fuse_context_uid_gid(cr
);
1202 puffs_fuse_fs_statvfs(struct puffs_usermount
*pu
, struct statvfs
*svfsb
)
1207 fuse
= puffs_getspecific(pu
);
1208 if (fuse
->op
.statfs
== NULL
) {
1209 if ((ret
= statvfs(PNPATH(puffs_getroot(pu
)), svfsb
)) == -1) {
1213 ret
= fuse
->op
.statfs(PNPATH(puffs_getroot(pu
)), svfsb
);
1220 /* End of puffs_fuse operations */
1223 fuse_main_real(int argc
, char **argv
, const struct fuse_operations
*ops
,
1224 size_t size
, void *userdata
)
1231 fuse
= fuse_setup_real(argc
, argv
, ops
, size
, &mountpoint
,
1232 &multithreaded
, &fd
, userdata
);
1234 return fuse_loop(fuse
);
1238 * XXX: just defer the operation until fuse_new() when we have more
1239 * info on our hands. The real beef is why's this separate in fuse in
1244 fuse_mount(const char *dir
, struct fuse_args
*args
)
1246 struct fuse_chan
*fc
;
1249 if ((fc
= calloc(1, sizeof(*fc
))) == NULL
) {
1250 err(EXIT_FAILURE
, "fuse_mount");
1254 if ((fc
->dir
= strdup(dir
)) == NULL
) {
1255 err(EXIT_FAILURE
, "fuse_mount");
1259 * we need to deep copy the args struct - some fuse file
1260 * systems "clean up" the argument vector for "security
1263 fc
->args
= fuse_opt_deep_copy_args(args
->argc
, args
->argv
);
1265 if (args
->argc
> 0) {
1266 set_refuse_mount_name(args
->argv
, name
, sizeof(name
));
1267 if ((args
->argv
[0] = strdup(name
)) == NULL
)
1268 err(1, "fuse_mount");
1276 fuse_new(struct fuse_chan
*fc
, struct fuse_args
*args
,
1277 const struct fuse_operations
*ops
, size_t size
, void *userdata
)
1279 struct puffs_usermount
*pu
;
1280 struct fuse_context
*fusectx
;
1281 struct puffs_pathobj
*po_root
;
1282 struct puffs_node
*pn_root
;
1283 struct puffs_ops
*pops
;
1284 struct refusenode
*rn_root
;
1285 struct statvfs svfsb
;
1288 extern int puffs_fakecc
;
1292 if ((fuse
= calloc(1, sizeof(*fuse
))) == NULL
) {
1293 err(EXIT_FAILURE
, "fuse_new");
1296 /* copy fuse ops to their own structure */
1297 (void) memcpy(&fuse
->op
, ops
, sizeof(fuse
->op
));
1299 fusectx
= fuse_get_context();
1300 fusectx
->fuse
= fuse
;
1304 fusectx
->private_data
= userdata
;
1308 if (fuse
->op
.init
!= NULL
)
1309 fusectx
->private_data
= fuse
->op
.init(NULL
); /* XXX */
1311 /* initialise the puffs operations structure */
1314 PUFFSOP_SET(pops
, puffs_fuse
, fs
, sync
);
1315 PUFFSOP_SET(pops
, puffs_fuse
, fs
, statvfs
);
1316 PUFFSOP_SET(pops
, puffs_fuse
, fs
, unmount
);
1319 * XXX: all of these don't possibly need to be
1320 * unconditionally set
1322 PUFFSOP_SET(pops
, puffs_fuse
, node
, lookup
);
1323 PUFFSOP_SET(pops
, puffs_fuse
, node
, getattr
);
1324 PUFFSOP_SET(pops
, puffs_fuse
, node
, setattr
);
1325 PUFFSOP_SET(pops
, puffs_fuse
, node
, readdir
);
1326 PUFFSOP_SET(pops
, puffs_fuse
, node
, readlink
);
1327 PUFFSOP_SET(pops
, puffs_fuse
, node
, mknod
);
1328 PUFFSOP_SET(pops
, puffs_fuse
, node
, create
);
1329 PUFFSOP_SET(pops
, puffs_fuse
, node
, remove
);
1330 PUFFSOP_SET(pops
, puffs_fuse
, node
, mkdir
);
1331 PUFFSOP_SET(pops
, puffs_fuse
, node
, rmdir
);
1332 PUFFSOP_SET(pops
, puffs_fuse
, node
, symlink
);
1333 PUFFSOP_SET(pops
, puffs_fuse
, node
, rename
);
1334 PUFFSOP_SET(pops
, puffs_fuse
, node
, link
);
1335 PUFFSOP_SET(pops
, puffs_fuse
, node
, open
);
1336 PUFFSOP_SET(pops
, puffs_fuse
, node
, close
);
1337 PUFFSOP_SET(pops
, puffs_fuse
, node
, read
);
1338 PUFFSOP_SET(pops
, puffs_fuse
, node
, write
);
1339 PUFFSOP_SET(pops
, puffs_fuse
, node
, reclaim
);
1341 argv0
= (*args
->argv
[0] == 0x0) ? fc
->args
->argv
[0] : args
->argv
[0];
1342 set_refuse_mount_name(&argv0
, name
, sizeof(name
));
1344 puffs_fakecc
= 1; /* XXX */
1345 pu
= puffs_init(pops
, _PATH_PUFFS
, name
, fuse
,
1346 PUFFS_FLAG_BUILDPATH
1347 | PUFFS_FLAG_HASHPATH
1348 | PUFFS_KFLAG_NOCACHE
);
1350 err(EXIT_FAILURE
, "puffs_init");
1354 pn_root
= newrn(pu
);
1355 puffs_setroot(pu
, pn_root
);
1356 rn_root
= pn_root
->pn_data
;
1357 rn_root
->flags
|= RN_ROOT
;
1359 po_root
= puffs_getrootpathobj(pu
);
1360 if ((po_root
->po_path
= strdup("/")) == NULL
)
1362 po_root
->po_len
= 1;
1363 puffs_path_buildhash(pu
, po_root
);
1366 puffs_vattr_null(&pn_root
->pn_va
);
1367 pn_root
->pn_va
.va_type
= VDIR
;
1368 pn_root
->pn_va
.va_mode
= 0755;
1369 if (fuse
->op
.getattr
)
1370 if (fuse
->op
.getattr(po_root
->po_path
, &st
) == 0)
1371 puffs_stat2vattr(&pn_root
->pn_va
, &st
);
1372 assert(pn_root
->pn_va
.va_type
== VDIR
);
1374 puffs_set_prepost(pu
, set_fuse_context_pid
, NULL
);
1376 puffs_zerostatvfs(&svfsb
);
1377 if (puffs_mount(pu
, fc
->dir
, MNT_NODEV
| MNT_NOSUID
, pn_root
) == -1) {
1378 err(EXIT_FAILURE
, "puffs_mount: directory \"%s\"", fc
->dir
);
1385 fuse_loop(struct fuse
*fuse
)
1388 return puffs_mainloop(fuse
->fc
->pu
);
1392 fuse_destroy(struct fuse
*fuse
)
1396 * TODO: needs to assert the fs is quiescent, i.e. no other
1400 delete_context_key();
1401 /* XXXXXX: missing stuff */
1406 fuse_exit(struct fuse
*fuse
)
1409 /* XXX: puffs_exit() is WRONG */
1410 if (fuse
->fc
->dead
== 0)
1411 puffs_exit(fuse
->fc
->pu
, 1);
1416 * XXX: obviously not the most perfect of functions, but needs some
1417 * puffs tweaking for a better tomorrow
1421 fuse_unmount(const char *mp
, struct fuse_chan
*fc
)
1424 /* XXX: puffs_exit() is WRONG */
1426 puffs_exit(fc
->pu
, 1);
1432 fuse_unmount_compat22(const char *mp
)
1438 /* The next function "exposes" struct fuse to userland. Not much
1439 * that we can do about this, as we're conforming to a defined
1443 fuse_teardown(struct fuse
*fuse
, char *mountpoint
)
1445 fuse_unmount(mountpoint
, fuse
->fc
);