1 /* $NetBSD: refuse.c,v 1.92 2009/03/05 01:21:57 msaitoh 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.92 2009/03/05 01:21:57 msaitoh Exp $");
37 #include <sys/types.h>
49 #ifdef MULTITHREADED_REFUSE
53 typedef uint64_t fuse_ino_t
;
60 double negative_timeout
;
62 double ac_attr_timeout
;
63 int ac_attr_timeout_set
;
80 struct fuse_args
*args
;
81 struct puffs_usermount
*pu
;
85 /* this is the private fuse structure */
87 struct fuse_chan
*fc
; /* fuse channel pointer */
88 struct fuse_operations op
; /* switch table of operations */
89 int compat
; /* compat level -
90 * not used in puffs_fuse */
91 struct node
**name_table
;
92 size_t name_table_size
;
93 struct node
**id_table
;
96 unsigned int generation
;
98 #ifdef MULTITHREADED_REFUSE
100 pthread_rwlock_t tree_lock
;
103 struct fuse_config conf
;
107 struct puffs_fuse_dirh
{
116 struct fuse_file_info file_info
;
117 struct puffs_fuse_dirh dirh
;
122 #define RN_OPEN 0x02 /* XXX: could just use opencount */
124 static int fuse_setattr(struct fuse
*, struct puffs_node
*,
125 const char *, const struct vattr
*);
127 static struct puffs_node
*
128 newrn(struct puffs_usermount
*pu
)
130 struct puffs_node
*pn
;
131 struct refusenode
*rn
;
133 if ((rn
= calloc(1, sizeof(*rn
))) == NULL
) {
134 err(EXIT_FAILURE
, "newrn");
136 pn
= puffs_pn_new(pu
, rn
);
142 nukern(struct puffs_node
*pn
)
144 struct refusenode
*rn
= pn
->pn_data
;
151 /* XXX - not threadsafe */
152 static ino_t fakeino
= 3;
154 /***************** start of pthread context routines ************************/
157 * Notes on fuse_context:
158 * we follow fuse's lead and use the pthread specific information to hold
159 * a reference to the fuse_context structure for this thread.
161 #ifdef MULTITHREADED_REFUSE
162 static pthread_mutex_t context_mutex
= PTHREAD_MUTEX_INITIALIZER
;
163 static pthread_key_t context_key
;
164 static unsigned long context_refc
;
167 /* return the fuse_context struct related to this thread */
168 struct fuse_context
*
169 fuse_get_context(void)
171 #ifdef MULTITHREADED_REFUSE
172 struct fuse_context
*ctxt
;
174 if ((ctxt
= pthread_getspecific(context_key
)) == NULL
) {
175 if ((ctxt
= calloc(1, sizeof(struct fuse_context
))) == NULL
) {
178 pthread_setspecific(context_key
, ctxt
);
182 static struct fuse_context fcon
;
188 /* used as a callback function */
189 #ifdef MULTITHREADED_REFUSE
191 free_context(void *ctxt
)
198 * Create the pthread key. The reason for the complexity is to
199 * enable use of multiple fuse instances within a single process.
202 create_context_key(void)
204 #ifdef MULTITHREADED_REFUSE
207 rv
= pthread_mutex_lock(&context_mutex
);
210 if (context_refc
== 0) {
211 if (pthread_key_create(&context_key
, free_context
) != 0) {
212 warnx("create_context_key: pthread_key_create failed");
213 pthread_mutex_unlock(&context_mutex
);
218 pthread_mutex_unlock(&context_mutex
);
226 delete_context_key(void)
228 #ifdef MULTITHREADED_REFUSE
229 pthread_mutex_lock(&context_mutex
);
230 /* If we are the last fuse instances using the key, delete it */
231 if (--context_refc
== 0) {
232 free(pthread_getspecific(context_key
));
233 pthread_key_delete(context_key
);
235 pthread_mutex_unlock(&context_mutex
);
239 /* set the uid and gid of the calling process in the current fuse context */
241 set_fuse_context_uid_gid(const struct puffs_cred
*cred
)
243 struct fuse_context
*fusectx
;
247 fusectx
= fuse_get_context();
248 if (puffs_cred_getuid(cred
, &uid
) == 0) {
251 if (puffs_cred_getgid(cred
, &gid
) == 0) {
256 /* set the pid of the calling process in the current fuse context */
258 set_fuse_context_pid(struct puffs_usermount
*pu
)
260 struct puffs_cc
*pcc
= puffs_cc_getcc(pu
);
261 struct fuse_context
*fusectx
;
263 fusectx
= fuse_get_context();
264 puffs_cc_getcaller(pcc
, &fusectx
->pid
, NULL
);
267 /***************** end of pthread context routines ************************/
269 #define DIR_CHUNKSIZE 4096
271 fill_dirbuf(struct puffs_fuse_dirh
*dh
, const char *name
, ino_t dino
,
276 if (dh
->bufsize
== 0) {
277 if ((dh
->dbuf
= calloc(1, DIR_CHUNKSIZE
)) == NULL
) {
281 dh
->reslen
= dh
->bufsize
= DIR_CHUNKSIZE
;
284 if (puffs_nextdent(&dh
->d
, name
, dino
, dtype
, &dh
->reslen
)) {
288 /* try to increase buffer space */
289 dh
->dbuf
= realloc(dh
->dbuf
, dh
->bufsize
+ DIR_CHUNKSIZE
);
290 if (dh
->dbuf
== NULL
) {
293 dh
->d
= (void *)((uint8_t *)dh
->dbuf
+ (dh
->bufsize
- dh
->reslen
));
294 dh
->reslen
+= DIR_CHUNKSIZE
;
295 dh
->bufsize
+= DIR_CHUNKSIZE
;
297 return !puffs_nextdent(&dh
->d
, name
, dino
, dtype
, &dh
->reslen
);
301 /* XXX: I have no idea how "off" is supposed to be used */
303 puffs_fuse_fill_dir(void *buf
, const char *name
,
304 const struct stat
*stbuf
, off_t off
)
306 struct puffs_fuse_dirh
*deh
= buf
;
314 dtype
= puffs_vtype2dt(puffs_mode2vt(stbuf
->st_mode
));
315 dino
= stbuf
->st_ino
;
318 * Some FUSE file systems like to always use 0 as the
319 * inode number. Our readdir() doesn't like to show
320 * directory entries with inode number 0 ==> workaround.
327 return fill_dirbuf(deh
, name
, dino
, dtype
);
331 puffs_fuse_dirfil(fuse_dirh_t h
, const char *name
, int type
, ino_t ino
)
336 if ((dtype
= type
) == 0) {
340 dino
= (ino
) ? ino
: fakeino
++;
342 return fill_dirbuf(h
, name
, dino
, dtype
);
345 /* place the refuse file system name into `name' */
347 set_refuse_mount_name(char **argv
, char *name
, size_t size
)
351 if (argv
== NULL
|| *argv
== NULL
) {
352 (void) strlcpy(name
, "refuse", size
);
354 if ((slash
= strrchr(*argv
, '/')) == NULL
) {
359 if (strncmp(*argv
, "refuse:", 7) == 0) {
360 /* we've already done this */
361 (void) strlcpy(name
, *argv
, size
);
363 (void) snprintf(name
, size
, "refuse:%s", slash
);
369 /* this function exposes struct fuse to userland */
371 fuse_setup_real(int argc
, char **argv
, const struct fuse_operations
*ops
,
372 size_t size
, char **mountpoint
, int *multithreaded
, int *fd
,
375 struct fuse_chan
*fc
;
376 struct fuse_args
*args
;
381 /* set up the proper name */
382 set_refuse_mount_name(argv
, name
, sizeof(name
));
384 /* grab the pthread context key */
385 if (!create_context_key()) {
389 /* stuff name into fuse_args */
390 args
= fuse_opt_deep_copy_args(argc
, argv
);
391 if (args
->argc
> 0) {
394 if ((args
->argv
[0] = strdup(name
)) == NULL
) {
395 fuse_opt_free_args(args
);
399 /* count back from the end over arguments starting with '-' */
400 for (i
= argc
- 1 ; i
> 0 && *argv
[i
] == '-' ; --i
) {
403 fc
= fuse_mount(*mountpoint
= argv
[i
], args
);
404 fuse
= fuse_new(fc
, args
, ops
, size
, user_data
);
406 fuse_opt_free_args(args
);
409 /* XXX - wait for puffs to become multi-threaded */
414 /* XXX - this is unused */
427 fuse_setup(int argc
, char **argv
, const struct fuse_operations
*ops
,
428 size_t size
, char **mountpoint
, int *multithreaded
, int *fd
)
430 return fuse_setup_real(argc
, argv
, ops
, size
, mountpoint
,
431 multithreaded
, fd
, NULL
);
435 fuse_setup26(int argc
, char **argv
, const struct fuse_operations
*ops
,
436 size_t size
, char **mountpoint
, int *multithreaded
, void *user_data
)
438 return fuse_setup_real(argc
, argv
, ops
, size
, mountpoint
,
439 multithreaded
, NULL
, user_data
);
442 #define FUSE_ERR_UNLINK(fuse, file) if (fuse->op.unlink) fuse->op.unlink(file)
443 #define FUSE_ERR_RMDIR(fuse, dir) if (fuse->op.rmdir) fuse->op.rmdir(dir)
447 fuse_getattr(struct fuse
*fuse
, struct puffs_node
*pn
, const char *path
,
453 if (fuse
->op
.getattr
== NULL
) {
457 /* wrap up return code */
458 memset(&st
, 0, sizeof(st
));
459 ret
= (*fuse
->op
.getattr
)(path
, &st
);
462 if (st
.st_blksize
== 0)
463 st
.st_blksize
= DEV_BSIZE
;
464 puffs_stat2vattr(va
, &st
);
470 /* utility function to set various elements of the attribute */
472 fuse_setattr(struct fuse
*fuse
, struct puffs_node
*pn
, const char *path
,
473 const struct vattr
*va
)
475 struct refusenode
*rn
= pn
->pn_data
;
487 if (mode
!= (mode_t
)PUFFS_VNOVAL
) {
490 if (fuse
->op
.chmod
== NULL
) {
493 ret
= fuse
->op
.chmod(path
, mode
);
498 if (uid
!= (uid_t
)PUFFS_VNOVAL
|| gid
!= (gid_t
)PUFFS_VNOVAL
) {
501 if (fuse
->op
.chown
== NULL
) {
504 ret
= fuse
->op
.chown(path
, uid
, gid
);
509 if (va
->va_atime
.tv_sec
!= (time_t)PUFFS_VNOVAL
510 || va
->va_mtime
.tv_sec
!= (long)PUFFS_VNOVAL
) {
513 if (fuse
->op
.utimens
) {
514 struct timespec tv
[2];
516 tv
[0].tv_sec
= va
->va_atime
.tv_sec
;
517 tv
[0].tv_nsec
= va
->va_atime
.tv_nsec
;
518 tv
[1].tv_sec
= va
->va_mtime
.tv_sec
;
519 tv
[1].tv_nsec
= va
->va_mtime
.tv_nsec
;
521 ret
= fuse
->op
.utimens(path
, tv
);
522 } else if (fuse
->op
.utime
) {
523 struct utimbuf timbuf
;
525 timbuf
.actime
= va
->va_atime
.tv_sec
;
526 timbuf
.modtime
= va
->va_mtime
.tv_sec
;
528 ret
= fuse
->op
.utime(path
, &timbuf
);
536 if (va
->va_size
!= (u_quad_t
)PUFFS_VNOVAL
) {
539 if (fuse
->op
.truncate
) {
540 ret
= fuse
->op
.truncate(path
, (off_t
)va
->va_size
);
541 } else if (fuse
->op
.ftruncate
) {
542 ret
= fuse
->op
.ftruncate(path
, (off_t
)va
->va_size
,
551 /* XXX: no reflection with reality */
552 puffs_setvattr(&pn
->pn_va
, va
);
559 fuse_newnode(struct puffs_usermount
*pu
, const char *path
,
560 const struct vattr
*va
, struct fuse_file_info
*fi
,
561 struct puffs_newinfo
*pni
, struct puffs_node
**pn_new
)
563 struct puffs_node
*pn
;
564 struct refusenode
*rn
;
568 fuse
= puffs_getspecific(pu
);
573 if (va
->va_type
== VDIR
) {
574 FUSE_ERR_RMDIR(fuse
, path
);
576 FUSE_ERR_UNLINK(fuse
, path
);
580 fuse_setattr(fuse
, pn
, path
, va
);
581 if (fuse_getattr(fuse
, pn
, path
, &newva
) == 0)
582 puffs_setvattr(&pn
->pn_va
, &newva
);
586 memcpy(&rn
->file_info
, fi
, sizeof(struct fuse_file_info
));
588 puffs_newinfo_setcookie(pni
, pn
);
596 /* operation wrappers start here */
598 /* lookup the path */
601 puffs_fuse_node_lookup(struct puffs_usermount
*pu
, void *opc
,
602 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
)
604 struct puffs_node
*pn_res
;
607 const char *path
= PCNPATH(pcn
);
610 fuse
= puffs_getspecific(pu
);
612 set_fuse_context_uid_gid(pcn
->pcn_cred
);
614 ret
= fuse
->op
.getattr(path
, &st
);
620 /* XXX: fiXXXme unconst */
621 pn_res
= puffs_pn_nodewalk(pu
, puffs_path_walkcmp
,
622 __UNCONST(&pcn
->pcn_po_full
));
623 if (pn_res
== NULL
) {
627 puffs_stat2vattr(&pn_res
->pn_va
, &st
);
630 puffs_newinfo_setcookie(pni
, pn_res
);
631 puffs_newinfo_setvtype(pni
, pn_res
->pn_va
.va_type
);
632 puffs_newinfo_setsize(pni
, (voff_t
)pn_res
->pn_va
.va_size
);
633 puffs_newinfo_setrdev(pni
, pn_res
->pn_va
.va_rdev
);
638 /* get attributes for the path name */
641 puffs_fuse_node_getattr(struct puffs_usermount
*pu
, void *opc
, struct vattr
*va
,
642 const struct puffs_cred
*pcr
)
644 struct puffs_node
*pn
= opc
;
646 const char *path
= PNPATH(pn
);
648 fuse
= puffs_getspecific(pu
);
650 set_fuse_context_uid_gid(pcr
);
652 return fuse_getattr(fuse
, pn
, path
, va
);
655 /* read the contents of the symbolic link */
658 puffs_fuse_node_readlink(struct puffs_usermount
*pu
, void *opc
,
659 const struct puffs_cred
*cred
, char *linkname
, size_t *linklen
)
661 struct puffs_node
*pn
= opc
;
663 const char *path
= PNPATH(pn
), *p
;
666 fuse
= puffs_getspecific(pu
);
667 if (fuse
->op
.readlink
== NULL
) {
671 set_fuse_context_uid_gid(cred
);
673 /* wrap up return code */
674 ret
= (*fuse
->op
.readlink
)(path
, linkname
, *linklen
);
677 p
= memchr(linkname
, '\0', *linklen
);
681 *linklen
= p
- linkname
;
687 /* make the special node */
690 puffs_fuse_node_mknod(struct puffs_usermount
*pu
, void *opc
,
691 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
,
692 const struct vattr
*va
)
696 const char *path
= PCNPATH(pcn
);
699 fuse
= puffs_getspecific(pu
);
700 if (fuse
->op
.mknod
== NULL
) {
704 set_fuse_context_uid_gid(pcn
->pcn_cred
);
706 /* wrap up return code */
707 mode
= puffs_addvtype2mode(va
->va_mode
, va
->va_type
);
708 ret
= (*fuse
->op
.mknod
)(path
, mode
, va
->va_rdev
);
711 ret
= fuse_newnode(pu
, path
, va
, NULL
, pni
, NULL
);
717 /* make a directory */
720 puffs_fuse_node_mkdir(struct puffs_usermount
*pu
, void *opc
,
721 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
,
722 const struct vattr
*va
)
725 mode_t mode
= va
->va_mode
;
726 const char *path
= PCNPATH(pcn
);
729 fuse
= puffs_getspecific(pu
);
731 set_fuse_context_uid_gid(pcn
->pcn_cred
);
733 if (fuse
->op
.mkdir
== NULL
) {
737 /* wrap up return code */
738 ret
= (*fuse
->op
.mkdir
)(path
, mode
);
741 ret
= fuse_newnode(pu
, path
, va
, NULL
, pni
, NULL
);
748 * create a regular file
750 * since linux/fuse sports using mknod for creating regular files
751 * instead of having a separate call for it in some versions, if
752 * we don't have create, just jump to op->mknod.
756 puffs_fuse_node_create(struct puffs_usermount
*pu
, void *opc
,
757 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
,
758 const struct vattr
*va
)
761 struct fuse_file_info fi
;
762 struct puffs_node
*pn
;
763 mode_t mode
= va
->va_mode
;
764 const char *path
= PCNPATH(pcn
);
767 fuse
= puffs_getspecific(pu
);
769 set_fuse_context_uid_gid(pcn
->pcn_cred
);
772 if (fuse
->op
.create
) {
773 ret
= fuse
->op
.create(path
, mode
| S_IFREG
, &fi
);
777 } else if (fuse
->op
.mknod
) {
778 ret
= fuse
->op
.mknod(path
, mode
| S_IFREG
, 0);
785 ret
= fuse_newnode(pu
, path
, va
, &fi
, pni
, &pn
);
787 /* sweet.. create also open the file */
789 struct refusenode
*rn
;
792 rn
->flags
|= RN_OPEN
;
800 /* remove the directory entry */
803 puffs_fuse_node_remove(struct puffs_usermount
*pu
, void *opc
, void *targ
,
804 const struct puffs_cn
*pcn
)
806 struct puffs_node
*pn_targ
= targ
;
808 const char *path
= PNPATH(pn_targ
);
811 fuse
= puffs_getspecific(pu
);
813 set_fuse_context_uid_gid(pcn
->pcn_cred
);
815 if (fuse
->op
.unlink
== NULL
) {
819 /* wrap up return code */
820 ret
= (*fuse
->op
.unlink
)(path
);
825 /* remove the directory */
828 puffs_fuse_node_rmdir(struct puffs_usermount
*pu
, void *opc
, void *targ
,
829 const struct puffs_cn
*pcn
)
831 struct puffs_node
*pn_targ
= targ
;
833 const char *path
= PNPATH(pn_targ
);
836 fuse
= puffs_getspecific(pu
);
838 set_fuse_context_uid_gid(pcn
->pcn_cred
);
840 if (fuse
->op
.rmdir
== NULL
) {
844 /* wrap up return code */
845 ret
= (*fuse
->op
.rmdir
)(path
);
850 /* create a symbolic link */
853 puffs_fuse_node_symlink(struct puffs_usermount
*pu
, void *opc
,
854 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn_src
,
855 const struct vattr
*va
, const char *link_target
)
858 const char *path
= PCNPATH(pcn_src
);
861 fuse
= puffs_getspecific(pu
);
863 set_fuse_context_uid_gid(pcn_src
->pcn_cred
);
865 if (fuse
->op
.symlink
== NULL
) {
869 /* wrap up return code */
870 ret
= fuse
->op
.symlink(link_target
, path
);
873 ret
= fuse_newnode(pu
, path
, va
, NULL
, pni
, NULL
);
879 /* rename a directory entry */
882 puffs_fuse_node_rename(struct puffs_usermount
*pu
, void *opc
, void *src
,
883 const struct puffs_cn
*pcn_src
, void *targ_dir
, void *targ
,
884 const struct puffs_cn
*pcn_targ
)
887 const char *path_src
= PCNPATH(pcn_src
);
888 const char *path_dest
= PCNPATH(pcn_targ
);
891 fuse
= puffs_getspecific(pu
);
893 set_fuse_context_uid_gid(pcn_targ
->pcn_cred
);
895 if (fuse
->op
.rename
== NULL
) {
899 ret
= fuse
->op
.rename(path_src
, path_dest
);
907 /* create a link in the file system */
910 puffs_fuse_node_link(struct puffs_usermount
*pu
, void *opc
, void *targ
,
911 const struct puffs_cn
*pcn
)
913 struct puffs_node
*pn
= targ
;
917 fuse
= puffs_getspecific(pu
);
919 set_fuse_context_uid_gid(pcn
->pcn_cred
);
921 if (fuse
->op
.link
== NULL
) {
925 /* wrap up return code */
926 ret
= (*fuse
->op
.link
)(PNPATH(pn
), PCNPATH(pcn
));
932 * fuse's regular interface provides chmod(), chown(), utimes()
933 * and truncate() + some variations, so try to fit the square block
934 * in the circle hole and the circle block .... something like that
938 puffs_fuse_node_setattr(struct puffs_usermount
*pu
, void *opc
,
939 const struct vattr
*va
, const struct puffs_cred
*pcr
)
941 struct puffs_node
*pn
= opc
;
943 const char *path
= PNPATH(pn
);
945 fuse
= puffs_getspecific(pu
);
947 set_fuse_context_uid_gid(pcr
);
949 return fuse_setattr(fuse
, pn
, path
, va
);
954 puffs_fuse_node_open(struct puffs_usermount
*pu
, void *opc
, int mode
,
955 const struct puffs_cred
*cred
)
957 struct puffs_node
*pn
= opc
;
958 struct refusenode
*rn
= pn
->pn_data
;
959 struct fuse_file_info
*fi
= &rn
->file_info
;
961 const char *path
= PNPATH(pn
);
963 fuse
= puffs_getspecific(pu
);
965 set_fuse_context_uid_gid(cred
);
967 /* if open, don't open again, lest risk nuking file private info */
968 if (rn
->flags
& RN_OPEN
) {
973 /* OFLAGS(), need to convert FREAD/FWRITE to O_RD/WR */
974 fi
->flags
= (mode
& ~(O_CREAT
| O_EXCL
| O_TRUNC
)) - 1;
976 if (pn
->pn_va
.va_type
== VDIR
) {
977 if (fuse
->op
.opendir
)
978 fuse
->op
.opendir(path
, fi
);
981 fuse
->op
.open(path
, fi
);
984 rn
->flags
|= RN_OPEN
;
992 puffs_fuse_node_close(struct puffs_usermount
*pu
, void *opc
, int fflag
,
993 const struct puffs_cred
*pcr
)
995 struct puffs_node
*pn
= opc
;
996 struct refusenode
*rn
= pn
->pn_data
;
998 struct fuse_file_info
*fi
;
999 const char *path
= PNPATH(pn
);
1002 fuse
= puffs_getspecific(pu
);
1003 fi
= &rn
->file_info
;
1006 set_fuse_context_uid_gid(pcr
);
1008 if (rn
->flags
& RN_OPEN
) {
1009 if (pn
->pn_va
.va_type
== VDIR
) {
1010 if (fuse
->op
.releasedir
)
1011 ret
= fuse
->op
.releasedir(path
, fi
);
1013 if (fuse
->op
.release
)
1014 ret
= fuse
->op
.release(path
, fi
);
1017 rn
->flags
&= ~RN_OPEN
;
1023 /* read some more from the file */
1026 puffs_fuse_node_read(struct puffs_usermount
*pu
, void *opc
, uint8_t *buf
,
1027 off_t offset
, size_t *resid
, const struct puffs_cred
*pcr
,
1030 struct puffs_node
*pn
= opc
;
1031 struct refusenode
*rn
= pn
->pn_data
;
1033 const char *path
= PNPATH(pn
);
1037 fuse
= puffs_getspecific(pu
);
1038 if (fuse
->op
.read
== NULL
) {
1042 set_fuse_context_uid_gid(pcr
);
1045 if (maxread
> pn
->pn_va
.va_size
- offset
) {
1047 maxread
= pn
->pn_va
.va_size
- offset
;
1052 ret
= (*fuse
->op
.read
)(path
, (char *)buf
, maxread
, offset
,
1063 /* write to the file */
1066 puffs_fuse_node_write(struct puffs_usermount
*pu
, void *opc
, uint8_t *buf
,
1067 off_t offset
, size_t *resid
, const struct puffs_cred
*pcr
,
1070 struct puffs_node
*pn
= opc
;
1071 struct refusenode
*rn
= pn
->pn_data
;
1073 const char *path
= PNPATH(pn
);
1076 fuse
= puffs_getspecific(pu
);
1077 if (fuse
->op
.write
== NULL
) {
1081 set_fuse_context_uid_gid(pcr
);
1083 if (ioflag
& PUFFS_IO_APPEND
)
1084 offset
= pn
->pn_va
.va_size
;
1086 ret
= (*fuse
->op
.write
)(path
, (char *)buf
, *resid
, offset
,
1090 if ((uint64_t)(offset
+ ret
) > pn
->pn_va
.va_size
)
1091 pn
->pn_va
.va_size
= offset
+ ret
;
1102 puffs_fuse_node_readdir(struct puffs_usermount
*pu
, void *opc
,
1103 struct dirent
*dent
, off_t
*readoff
, size_t *reslen
,
1104 const struct puffs_cred
*pcr
, int *eofflag
,
1105 off_t
*cookies
, size_t *ncookies
)
1107 struct puffs_node
*pn
= opc
;
1108 struct refusenode
*rn
= pn
->pn_data
;
1109 struct puffs_fuse_dirh
*dirh
;
1111 struct dirent
*fromdent
;
1112 const char *path
= PNPATH(pn
);
1115 fuse
= puffs_getspecific(pu
);
1116 if (fuse
->op
.readdir
== NULL
&& fuse
->op
.getdir
== NULL
) {
1120 set_fuse_context_uid_gid(pcr
);
1122 if (pn
->pn_va
.va_type
!= VDIR
)
1128 * if we are starting from the beginning, slurp entire directory
1131 if (*readoff
== 0) {
1132 /* free old buffers */
1134 memset(dirh
, 0, sizeof(struct puffs_fuse_dirh
));
1136 if (fuse
->op
.readdir
)
1137 ret
= fuse
->op
.readdir(path
, dirh
, puffs_fuse_fill_dir
,
1140 ret
= fuse
->op
.getdir(path
, dirh
, puffs_fuse_dirfil
);
1145 /* Both op.readdir and op.getdir read full directory */
1148 /* now, stuff results into the kernel buffers */
1149 while (*readoff
< (off_t
)(dirh
->bufsize
- dirh
->reslen
)) {
1151 fromdent
= (struct dirent
*)((uint8_t *)dirh
->dbuf
+ *readoff
);
1153 if (*reslen
< _DIRENT_SIZE(fromdent
))
1156 memcpy(dent
, fromdent
, _DIRENT_SIZE(fromdent
));
1157 *readoff
+= _DIRENT_SIZE(fromdent
);
1158 *reslen
-= _DIRENT_SIZE(fromdent
);
1160 dent
= _DIRENT_NEXT(dent
);
1168 puffs_fuse_node_reclaim(struct puffs_usermount
*pu
, void *opc
)
1170 struct puffs_node
*pn
= opc
;
1178 puffs_fuse_fs_unmount(struct puffs_usermount
*pu
, int flags
)
1182 fuse
= puffs_getspecific(pu
);
1183 if (fuse
->op
.destroy
== NULL
) {
1186 (*fuse
->op
.destroy
)(fuse
);
1192 puffs_fuse_fs_sync(struct puffs_usermount
*pu
, int flags
,
1193 const struct puffs_cred
*cr
)
1195 set_fuse_context_uid_gid(cr
);
1201 puffs_fuse_fs_statvfs(struct puffs_usermount
*pu
, struct statvfs
*svfsb
)
1206 fuse
= puffs_getspecific(pu
);
1207 if (fuse
->op
.statfs
== NULL
) {
1208 if ((ret
= statvfs(PNPATH(puffs_getroot(pu
)), svfsb
)) == -1) {
1212 ret
= fuse
->op
.statfs(PNPATH(puffs_getroot(pu
)), svfsb
);
1219 /* End of puffs_fuse operations */
1222 fuse_main_real(int argc
, char **argv
, const struct fuse_operations
*ops
,
1223 size_t size
, void *userdata
)
1230 fuse
= fuse_setup(argc
, argv
, ops
, size
, &mountpoint
, &multithreaded
,
1233 return fuse_loop(fuse
);
1237 * XXX: just defer the operation until fuse_new() when we have more
1238 * info on our hands. The real beef is why's this separate in fuse in
1243 fuse_mount(const char *dir
, struct fuse_args
*args
)
1245 struct fuse_chan
*fc
;
1248 if ((fc
= calloc(1, sizeof(*fc
))) == NULL
) {
1249 err(EXIT_FAILURE
, "fuse_mount");
1253 if ((fc
->dir
= strdup(dir
)) == NULL
) {
1254 err(EXIT_FAILURE
, "fuse_mount");
1258 * we need to deep copy the args struct - some fuse file
1259 * systems "clean up" the argument vector for "security
1262 fc
->args
= fuse_opt_deep_copy_args(args
->argc
, args
->argv
);
1264 if (args
->argc
> 0) {
1265 set_refuse_mount_name(args
->argv
, name
, sizeof(name
));
1266 if ((args
->argv
[0] = strdup(name
)) == NULL
)
1267 err(1, "fuse_mount");
1275 fuse_new(struct fuse_chan
*fc
, struct fuse_args
*args
,
1276 const struct fuse_operations
*ops
, size_t size
, void *userdata
)
1278 struct puffs_usermount
*pu
;
1279 struct fuse_context
*fusectx
;
1280 struct puffs_pathobj
*po_root
;
1281 struct puffs_node
*pn_root
;
1282 struct puffs_ops
*pops
;
1283 struct refusenode
*rn_root
;
1284 struct statvfs svfsb
;
1287 extern int puffs_fakecc
;
1291 if ((fuse
= calloc(1, sizeof(*fuse
))) == NULL
) {
1292 err(EXIT_FAILURE
, "fuse_new");
1295 /* copy fuse ops to their own structure */
1296 (void) memcpy(&fuse
->op
, ops
, sizeof(fuse
->op
));
1298 fusectx
= fuse_get_context();
1299 fusectx
->fuse
= fuse
;
1303 fusectx
->private_data
= userdata
;
1307 /* initialise the puffs operations structure */
1310 PUFFSOP_SET(pops
, puffs_fuse
, fs
, sync
);
1311 PUFFSOP_SET(pops
, puffs_fuse
, fs
, statvfs
);
1312 PUFFSOP_SET(pops
, puffs_fuse
, fs
, unmount
);
1315 * XXX: all of these don't possibly need to be
1316 * unconditionally set
1318 PUFFSOP_SET(pops
, puffs_fuse
, node
, lookup
);
1319 PUFFSOP_SET(pops
, puffs_fuse
, node
, getattr
);
1320 PUFFSOP_SET(pops
, puffs_fuse
, node
, setattr
);
1321 PUFFSOP_SET(pops
, puffs_fuse
, node
, readdir
);
1322 PUFFSOP_SET(pops
, puffs_fuse
, node
, readlink
);
1323 PUFFSOP_SET(pops
, puffs_fuse
, node
, mknod
);
1324 PUFFSOP_SET(pops
, puffs_fuse
, node
, create
);
1325 PUFFSOP_SET(pops
, puffs_fuse
, node
, remove
);
1326 PUFFSOP_SET(pops
, puffs_fuse
, node
, mkdir
);
1327 PUFFSOP_SET(pops
, puffs_fuse
, node
, rmdir
);
1328 PUFFSOP_SET(pops
, puffs_fuse
, node
, symlink
);
1329 PUFFSOP_SET(pops
, puffs_fuse
, node
, rename
);
1330 PUFFSOP_SET(pops
, puffs_fuse
, node
, link
);
1331 PUFFSOP_SET(pops
, puffs_fuse
, node
, open
);
1332 PUFFSOP_SET(pops
, puffs_fuse
, node
, close
);
1333 PUFFSOP_SET(pops
, puffs_fuse
, node
, read
);
1334 PUFFSOP_SET(pops
, puffs_fuse
, node
, write
);
1335 PUFFSOP_SET(pops
, puffs_fuse
, node
, reclaim
);
1337 argv0
= (*args
->argv
[0] == 0x0) ? fc
->args
->argv
[0] : args
->argv
[0];
1338 set_refuse_mount_name(&argv0
, name
, sizeof(name
));
1340 puffs_fakecc
= 1; /* XXX */
1341 pu
= puffs_init(pops
, _PATH_PUFFS
, name
, fuse
,
1342 PUFFS_FLAG_BUILDPATH
1343 | PUFFS_FLAG_HASHPATH
1344 | PUFFS_KFLAG_NOCACHE
);
1346 err(EXIT_FAILURE
, "puffs_init");
1350 pn_root
= newrn(pu
);
1351 puffs_setroot(pu
, pn_root
);
1352 rn_root
= pn_root
->pn_data
;
1353 rn_root
->flags
|= RN_ROOT
;
1355 po_root
= puffs_getrootpathobj(pu
);
1356 if ((po_root
->po_path
= strdup("/")) == NULL
)
1358 po_root
->po_len
= 1;
1359 puffs_path_buildhash(pu
, po_root
);
1362 puffs_vattr_null(&pn_root
->pn_va
);
1363 pn_root
->pn_va
.va_type
= VDIR
;
1364 pn_root
->pn_va
.va_mode
= 0755;
1365 if (fuse
->op
.getattr
)
1366 if (fuse
->op
.getattr(po_root
->po_path
, &st
) == 0)
1367 puffs_stat2vattr(&pn_root
->pn_va
, &st
);
1368 assert(pn_root
->pn_va
.va_type
== VDIR
);
1371 fusectx
->private_data
= fuse
->op
.init(NULL
); /* XXX */
1373 puffs_set_prepost(pu
, set_fuse_context_pid
, NULL
);
1375 puffs_zerostatvfs(&svfsb
);
1376 if (puffs_mount(pu
, fc
->dir
, MNT_NODEV
| MNT_NOSUID
, pn_root
) == -1) {
1377 err(EXIT_FAILURE
, "puffs_mount: directory \"%s\"", fc
->dir
);
1384 fuse_loop(struct fuse
*fuse
)
1387 return puffs_mainloop(fuse
->fc
->pu
);
1391 fuse_destroy(struct fuse
*fuse
)
1395 * TODO: needs to assert the fs is quiescent, i.e. no other
1399 delete_context_key();
1400 /* XXXXXX: missing stuff */
1405 fuse_exit(struct fuse
*fuse
)
1408 /* XXX: puffs_exit() is WRONG */
1409 if (fuse
->fc
->dead
== 0)
1410 puffs_exit(fuse
->fc
->pu
, 1);
1415 * XXX: obviously not the most perfect of functions, but needs some
1416 * puffs tweaking for a better tomorrow
1420 fuse_unmount(const char *mp
, struct fuse_chan
*fc
)
1423 /* XXX: puffs_exit() is WRONG */
1425 puffs_exit(fc
->pu
, 1);
1431 fuse_unmount_compat22(const char *mp
)
1437 /* The next function "exposes" struct fuse to userland. Not much
1438 * that we can do about this, as we're conforming to a defined
1442 fuse_teardown(struct fuse
*fuse
, char *mountpoint
)
1444 fuse_unmount(mountpoint
, fuse
->fc
);