1 /* $NetBSD: refuse.c,v 1.94 2011/07/09 17:16:46 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/types.h>
42 #ifdef MULTITHREADED_REFUSE
48 typedef uint64_t fuse_ino_t
;
55 double negative_timeout
;
57 double ac_attr_timeout
;
58 int ac_attr_timeout_set
;
75 struct fuse_args
*args
;
76 struct puffs_usermount
*pu
;
80 /* this is the private fuse structure */
82 struct fuse_chan
*fc
; /* fuse channel pointer */
83 struct fuse_operations op
; /* switch table of operations */
84 int compat
; /* compat level -
85 * not used in puffs_fuse */
86 struct node
**name_table
;
87 size_t name_table_size
;
88 struct node
**id_table
;
91 unsigned int generation
;
94 pthread_rwlock_t tree_lock
;
96 struct fuse_config conf
;
100 struct puffs_fuse_dirh
{
109 struct fuse_file_info file_info
;
110 struct puffs_fuse_dirh dirh
;
115 #define RN_OPEN 0x02 /* XXX: could just use opencount */
117 static int fuse_setattr(struct fuse
*, struct puffs_node
*,
118 const char *, const struct vattr
*);
120 static struct puffs_node
*
121 newrn(struct puffs_usermount
*pu
)
123 struct puffs_node
*pn
;
124 struct refusenode
*rn
;
126 if ((rn
= calloc(1, sizeof(*rn
))) == NULL
) {
127 err(EXIT_FAILURE
, "newrn");
129 pn
= puffs_pn_new(pu
, rn
);
135 nukern(struct puffs_node
*pn
)
137 struct refusenode
*rn
= pn
->pn_data
;
144 /* XXX - not threadsafe */
145 static ino_t fakeino
= 3;
147 /***************** start of pthread context routines ************************/
150 * Notes on fuse_context:
151 * we follow fuse's lead and use the pthread specific information to hold
152 * a reference to the fuse_context structure for this thread.
154 #ifdef MULTITHREADED_REFUSE
155 static pthread_mutex_t context_mutex
= PTHREAD_MUTEX_INITIALIZER
;
156 static pthread_key_t context_key
;
157 static unsigned long context_refc
;
160 /* return the fuse_context struct related to this thread */
161 struct fuse_context
*
162 fuse_get_context(void)
164 #ifdef MULTITHREADED_REFUSE
165 struct fuse_context
*ctxt
;
167 if ((ctxt
= pthread_getspecific(context_key
)) == NULL
) {
168 if ((ctxt
= calloc(1, sizeof(struct fuse_context
))) == NULL
) {
171 pthread_setspecific(context_key
, ctxt
);
175 static struct fuse_context fcon
;
181 /* used as a callback function */
182 #ifdef MULTITHREADED_REFUSE
184 free_context(void *ctxt
)
191 * Create the pthread key. The reason for the complexity is to
192 * enable use of multiple fuse instances within a single process.
195 create_context_key(void)
197 #ifdef MULTITHREADED_REFUSE
200 rv
= pthread_mutex_lock(&context_mutex
);
203 if (context_refc
== 0) {
204 if (pthread_key_create(&context_key
, free_context
) != 0) {
205 warnx("create_context_key: pthread_key_create failed");
206 pthread_mutex_unlock(&context_mutex
);
211 pthread_mutex_unlock(&context_mutex
);
219 delete_context_key(void)
221 #ifdef MULTITHREADED_REFUSE
222 pthread_mutex_lock(&context_mutex
);
223 /* If we are the last fuse instances using the key, delete it */
224 if (--context_refc
== 0) {
225 free(pthread_getspecific(context_key
));
226 pthread_key_delete(context_key
);
228 pthread_mutex_unlock(&context_mutex
);
232 /* set the uid and gid of the calling process in the current fuse context */
234 set_fuse_context_uid_gid(const struct puffs_cred
*cred
)
236 struct fuse_context
*fusectx
;
240 fusectx
= fuse_get_context();
241 if (puffs_cred_getuid(cred
, &uid
) == 0) {
244 if (puffs_cred_getgid(cred
, &gid
) == 0) {
249 /* set the pid of the calling process in the current fuse context */
251 set_fuse_context_pid(struct puffs_usermount
*pu
)
253 struct puffs_cc
*pcc
= puffs_cc_getcc(pu
);
254 struct fuse_context
*fusectx
;
256 fusectx
= fuse_get_context();
257 puffs_cc_getcaller(pcc
, &fusectx
->pid
, NULL
);
260 /***************** end of pthread context routines ************************/
262 #define DIR_CHUNKSIZE 4096
264 fill_dirbuf(struct puffs_fuse_dirh
*dh
, const char *name
, ino_t dino
,
269 if (dh
->bufsize
== 0) {
270 if ((dh
->dbuf
= calloc(1, DIR_CHUNKSIZE
)) == NULL
) {
274 dh
->reslen
= dh
->bufsize
= DIR_CHUNKSIZE
;
277 if (puffs_nextdent(&dh
->d
, name
, dino
, dtype
, &dh
->reslen
)) {
281 /* try to increase buffer space */
282 dh
->dbuf
= realloc(dh
->dbuf
, dh
->bufsize
+ DIR_CHUNKSIZE
);
283 if (dh
->dbuf
== NULL
) {
286 dh
->d
= (void *)((uint8_t *)dh
->dbuf
+ (dh
->bufsize
- dh
->reslen
));
287 dh
->reslen
+= DIR_CHUNKSIZE
;
288 dh
->bufsize
+= DIR_CHUNKSIZE
;
290 return !puffs_nextdent(&dh
->d
, name
, dino
, dtype
, &dh
->reslen
);
294 /* XXX: I have no idea how "off" is supposed to be used */
296 puffs_fuse_fill_dir(void *buf
, const char *name
,
297 const struct stat
*stbuf
, off_t off
)
299 struct puffs_fuse_dirh
*deh
= buf
;
307 dtype
= puffs_vtype2dt(puffs_mode2vt(stbuf
->st_mode
));
308 dino
= stbuf
->st_ino
;
311 * Some FUSE file systems like to always use 0 as the
312 * inode number. Our readdir() doesn't like to show
313 * directory entries with inode number 0 ==> workaround.
320 return fill_dirbuf(deh
, name
, dino
, dtype
);
324 puffs_fuse_dirfil(fuse_dirh_t h
, const char *name
, int type
, ino_t ino
)
329 if ((dtype
= type
) == 0) {
333 dino
= (ino
) ? ino
: fakeino
++;
335 return fill_dirbuf(h
, name
, dino
, dtype
);
338 /* place the refuse file system name into `name' */
340 set_refuse_mount_name(char **argv
, char *name
, size_t size
)
344 if (argv
== NULL
|| *argv
== NULL
) {
345 (void) strlcpy(name
, "refuse", size
);
347 if ((slash
= strrchr(*argv
, '/')) == NULL
) {
352 if (strncmp(*argv
, "refuse:", 7) == 0) {
353 /* we've already done this */
354 (void) strlcpy(name
, *argv
, size
);
356 (void) snprintf(name
, size
, "refuse:%s", slash
);
362 /* this function exposes struct fuse to userland */
364 fuse_setup_real(int argc
, char **argv
, const struct fuse_operations
*ops
,
365 size_t size
, char **mountpoint
, int *multithreaded
, int *fd
,
368 struct fuse_chan
*fc
;
369 struct fuse_args
*args
;
374 /* set up the proper name */
375 set_refuse_mount_name(argv
, name
, sizeof(name
));
377 /* grab the pthread context key */
378 if (!create_context_key()) {
382 /* stuff name into fuse_args */
383 args
= fuse_opt_deep_copy_args(argc
, argv
);
384 if (args
->argc
> 0) {
387 if ((args
->argv
[0] = strdup(name
)) == NULL
) {
388 fuse_opt_free_args(args
);
392 /* count back from the end over arguments starting with '-' */
393 for (i
= argc
- 1 ; i
> 0 && *argv
[i
] == '-' ; --i
) {
396 fc
= fuse_mount(*mountpoint
= argv
[i
], args
);
397 fuse
= fuse_new(fc
, args
, ops
, size
, user_data
);
399 fuse_opt_free_args(args
);
402 /* XXX - wait for puffs to become multi-threaded */
407 /* XXX - this is unused */
420 fuse_setup(int argc
, char **argv
, const struct fuse_operations
*ops
,
421 size_t size
, char **mountpoint
, int *multithreaded
, int *fd
)
423 return fuse_setup_real(argc
, argv
, ops
, size
, mountpoint
,
424 multithreaded
, fd
, NULL
);
428 fuse_setup26(int argc
, char **argv
, const struct fuse_operations
*ops
,
429 size_t size
, char **mountpoint
, int *multithreaded
, void *user_data
)
431 return fuse_setup_real(argc
, argv
, ops
, size
, mountpoint
,
432 multithreaded
, NULL
, user_data
);
435 #define FUSE_ERR_UNLINK(fuse, file) if (fuse->op.unlink) fuse->op.unlink(file)
436 #define FUSE_ERR_RMDIR(fuse, dir) if (fuse->op.rmdir) fuse->op.rmdir(dir)
440 fuse_getattr(struct fuse
*fuse
, struct puffs_node
*pn
, const char *path
,
446 if (fuse
->op
.getattr
== NULL
) {
450 /* wrap up return code */
451 memset(&st
, 0, sizeof(st
));
452 ret
= (*fuse
->op
.getattr
)(path
, &st
);
455 if (st
.st_blksize
== 0)
456 st
.st_blksize
= DEV_BSIZE
;
457 puffs_stat2vattr(va
, &st
);
463 /* utility function to set various elements of the attribute */
465 fuse_setattr(struct fuse
*fuse
, struct puffs_node
*pn
, const char *path
,
466 const struct vattr
*va
)
468 struct refusenode
*rn
= pn
->pn_data
;
480 if (mode
!= (mode_t
)PUFFS_VNOVAL
) {
483 if (fuse
->op
.chmod
== NULL
) {
486 ret
= fuse
->op
.chmod(path
, mode
);
491 if (uid
!= (uid_t
)PUFFS_VNOVAL
|| gid
!= (gid_t
)PUFFS_VNOVAL
) {
494 if (fuse
->op
.chown
== NULL
) {
497 ret
= fuse
->op
.chown(path
, uid
, gid
);
502 if (va
->va_atime
.tv_sec
!= (time_t)PUFFS_VNOVAL
503 || va
->va_mtime
.tv_sec
!= (long)PUFFS_VNOVAL
) {
506 if (fuse
->op
.utimens
) {
507 struct timespec tv
[2];
509 tv
[0].tv_sec
= va
->va_atime
.tv_sec
;
510 tv
[0].tv_nsec
= va
->va_atime
.tv_nsec
;
511 tv
[1].tv_sec
= va
->va_mtime
.tv_sec
;
512 tv
[1].tv_nsec
= va
->va_mtime
.tv_nsec
;
514 ret
= fuse
->op
.utimens(path
, tv
);
515 } else if (fuse
->op
.utime
) {
516 struct utimbuf timbuf
;
518 timbuf
.actime
= va
->va_atime
.tv_sec
;
519 timbuf
.modtime
= va
->va_mtime
.tv_sec
;
521 ret
= fuse
->op
.utime(path
, &timbuf
);
529 if (va
->va_size
!= (u_quad_t
)PUFFS_VNOVAL
) {
532 if (fuse
->op
.truncate
) {
533 ret
= fuse
->op
.truncate(path
, (off_t
)va
->va_size
);
534 } else if (fuse
->op
.ftruncate
) {
535 ret
= fuse
->op
.ftruncate(path
, (off_t
)va
->va_size
,
544 /* XXX: no reflection with reality */
545 puffs_setvattr(&pn
->pn_va
, va
);
552 fuse_newnode(struct puffs_usermount
*pu
, const char *path
,
553 const struct vattr
*va
, struct fuse_file_info
*fi
,
554 struct puffs_newinfo
*pni
, struct puffs_node
**pn_new
)
556 struct puffs_node
*pn
;
557 struct refusenode
*rn
;
561 fuse
= puffs_getspecific(pu
);
566 if (va
->va_type
== VDIR
) {
567 FUSE_ERR_RMDIR(fuse
, path
);
569 FUSE_ERR_UNLINK(fuse
, path
);
573 fuse_setattr(fuse
, pn
, path
, va
);
574 if (fuse_getattr(fuse
, pn
, path
, &newva
) == 0)
575 puffs_setvattr(&pn
->pn_va
, &newva
);
579 memcpy(&rn
->file_info
, fi
, sizeof(struct fuse_file_info
));
581 puffs_newinfo_setcookie(pni
, pn
);
589 /* operation wrappers start here */
591 /* lookup the path */
594 puffs_fuse_node_lookup(struct puffs_usermount
*pu
, void *opc
,
595 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
)
597 struct puffs_node
*pn_res
;
600 const char *path
= PCNPATH(pcn
);
603 fuse
= puffs_getspecific(pu
);
605 set_fuse_context_uid_gid(pcn
->pcn_cred
);
607 ret
= fuse
->op
.getattr(path
, &st
);
613 /* XXX: fiXXXme unconst */
614 pn_res
= puffs_pn_nodewalk(pu
, puffs_path_walkcmp
,
615 __DECONST(struct puffs_pathobj
*, &pcn
->pcn_po_full
));
616 if (pn_res
== NULL
) {
620 puffs_stat2vattr(&pn_res
->pn_va
, &st
);
623 puffs_newinfo_setcookie(pni
, pn_res
);
624 puffs_newinfo_setvtype(pni
, pn_res
->pn_va
.va_type
);
625 puffs_newinfo_setsize(pni
, (voff_t
)pn_res
->pn_va
.va_size
);
630 /* get attributes for the path name */
633 puffs_fuse_node_getattr(struct puffs_usermount
*pu
, void *opc
, struct vattr
*va
,
634 const struct puffs_cred
*pcr
)
636 struct puffs_node
*pn
= opc
;
638 const char *path
= PNPATH(pn
);
640 fuse
= puffs_getspecific(pu
);
642 set_fuse_context_uid_gid(pcr
);
644 return fuse_getattr(fuse
, pn
, path
, va
);
647 /* read the contents of the symbolic link */
650 puffs_fuse_node_readlink(struct puffs_usermount
*pu
, void *opc
,
651 const struct puffs_cred
*cred
, char *linkname
, size_t *linklen
)
653 struct puffs_node
*pn
= opc
;
655 const char *path
= PNPATH(pn
), *p
;
658 fuse
= puffs_getspecific(pu
);
659 if (fuse
->op
.readlink
== NULL
) {
663 set_fuse_context_uid_gid(cred
);
665 /* wrap up return code */
666 ret
= (*fuse
->op
.readlink
)(path
, linkname
, *linklen
);
669 p
= memchr(linkname
, '\0', *linklen
);
673 *linklen
= p
- linkname
;
679 /* make the special node */
682 puffs_fuse_node_mknod(struct puffs_usermount
*pu
, void *opc
,
683 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
,
684 const struct vattr
*va
)
688 const char *path
= PCNPATH(pcn
);
691 fuse
= puffs_getspecific(pu
);
692 if (fuse
->op
.mknod
== NULL
) {
696 set_fuse_context_uid_gid(pcn
->pcn_cred
);
698 /* wrap up return code */
699 mode
= puffs_addvtype2mode(va
->va_mode
, va
->va_type
);
700 ret
= (*fuse
->op
.mknod
)(path
, mode
, 0);
703 ret
= fuse_newnode(pu
, path
, va
, NULL
, pni
, NULL
);
709 /* make a directory */
712 puffs_fuse_node_mkdir(struct puffs_usermount
*pu
, void *opc
,
713 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
,
714 const struct vattr
*va
)
717 mode_t mode
= va
->va_mode
;
718 const char *path
= PCNPATH(pcn
);
721 fuse
= puffs_getspecific(pu
);
723 set_fuse_context_uid_gid(pcn
->pcn_cred
);
725 if (fuse
->op
.mkdir
== NULL
) {
729 /* wrap up return code */
730 ret
= (*fuse
->op
.mkdir
)(path
, mode
);
733 ret
= fuse_newnode(pu
, path
, va
, NULL
, pni
, NULL
);
740 * create a regular file
742 * since linux/fuse sports using mknod for creating regular files
743 * instead of having a separate call for it in some versions, if
744 * we don't have create, just jump to op->mknod.
748 puffs_fuse_node_create(struct puffs_usermount
*pu
, void *opc
,
749 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
,
750 const struct vattr
*va
)
753 struct fuse_file_info fi
;
754 struct puffs_node
*pn
;
755 mode_t mode
= va
->va_mode
;
756 const char *path
= PCNPATH(pcn
);
759 fuse
= puffs_getspecific(pu
);
761 set_fuse_context_uid_gid(pcn
->pcn_cred
);
764 if (fuse
->op
.create
) {
765 ret
= fuse
->op
.create(path
, mode
| S_IFREG
, &fi
);
769 } else if (fuse
->op
.mknod
) {
770 ret
= fuse
->op
.mknod(path
, mode
| S_IFREG
, 0);
777 ret
= fuse_newnode(pu
, path
, va
, &fi
, pni
, &pn
);
779 /* sweet.. create also open the file */
781 struct refusenode
*rn
;
784 rn
->flags
|= RN_OPEN
;
792 /* remove the directory entry */
795 puffs_fuse_node_remove(struct puffs_usermount
*pu
, void *opc
, void *targ
,
796 const struct puffs_cn
*pcn
)
798 struct puffs_node
*pn_targ
= targ
;
800 const char *path
= PNPATH(pn_targ
);
803 fuse
= puffs_getspecific(pu
);
805 set_fuse_context_uid_gid(pcn
->pcn_cred
);
807 if (fuse
->op
.unlink
== NULL
) {
811 /* wrap up return code */
812 ret
= (*fuse
->op
.unlink
)(path
);
817 /* remove the directory */
820 puffs_fuse_node_rmdir(struct puffs_usermount
*pu
, void *opc
, void *targ
,
821 const struct puffs_cn
*pcn
)
823 struct puffs_node
*pn_targ
= targ
;
825 const char *path
= PNPATH(pn_targ
);
828 fuse
= puffs_getspecific(pu
);
830 set_fuse_context_uid_gid(pcn
->pcn_cred
);
832 if (fuse
->op
.rmdir
== NULL
) {
836 /* wrap up return code */
837 ret
= (*fuse
->op
.rmdir
)(path
);
842 /* create a symbolic link */
845 puffs_fuse_node_symlink(struct puffs_usermount
*pu
, void *opc
,
846 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn_src
,
847 const struct vattr
*va
, const char *link_target
)
850 const char *path
= PCNPATH(pcn_src
);
853 fuse
= puffs_getspecific(pu
);
855 set_fuse_context_uid_gid(pcn_src
->pcn_cred
);
857 if (fuse
->op
.symlink
== NULL
) {
861 /* wrap up return code */
862 ret
= fuse
->op
.symlink(link_target
, path
);
865 ret
= fuse_newnode(pu
, path
, va
, NULL
, pni
, NULL
);
871 /* rename a directory entry */
874 puffs_fuse_node_rename(struct puffs_usermount
*pu
, void *opc
, void *src
,
875 const struct puffs_cn
*pcn_src
, void *targ_dir
, void *targ
,
876 const struct puffs_cn
*pcn_targ
)
879 const char *path_src
= PCNPATH(pcn_src
);
880 const char *path_dest
= PCNPATH(pcn_targ
);
883 fuse
= puffs_getspecific(pu
);
885 set_fuse_context_uid_gid(pcn_targ
->pcn_cred
);
887 if (fuse
->op
.rename
== NULL
) {
891 ret
= fuse
->op
.rename(path_src
, path_dest
);
899 /* create a link in the file system */
902 puffs_fuse_node_link(struct puffs_usermount
*pu
, void *opc
, void *targ
,
903 const struct puffs_cn
*pcn
)
905 struct puffs_node
*pn
= targ
;
909 fuse
= puffs_getspecific(pu
);
911 set_fuse_context_uid_gid(pcn
->pcn_cred
);
913 if (fuse
->op
.link
== NULL
) {
917 /* wrap up return code */
918 ret
= (*fuse
->op
.link
)(PNPATH(pn
), PCNPATH(pcn
));
924 * fuse's regular interface provides chmod(), chown(), utimes()
925 * and truncate() + some variations, so try to fit the square block
926 * in the circle hole and the circle block .... something like that
930 puffs_fuse_node_setattr(struct puffs_usermount
*pu
, void *opc
,
931 const struct vattr
*va
, const struct puffs_cred
*pcr
)
933 struct puffs_node
*pn
= opc
;
935 const char *path
= PNPATH(pn
);
937 fuse
= puffs_getspecific(pu
);
939 set_fuse_context_uid_gid(pcr
);
941 return fuse_setattr(fuse
, pn
, path
, va
);
946 puffs_fuse_node_open(struct puffs_usermount
*pu
, void *opc
, int mode
,
947 const struct puffs_cred
*cred
)
949 struct puffs_node
*pn
= opc
;
950 struct refusenode
*rn
= pn
->pn_data
;
951 struct fuse_file_info
*fi
= &rn
->file_info
;
953 const char *path
= PNPATH(pn
);
955 fuse
= puffs_getspecific(pu
);
957 set_fuse_context_uid_gid(cred
);
959 /* if open, don't open again, lest risk nuking file private info */
960 if (rn
->flags
& RN_OPEN
) {
965 /* OFLAGS(), need to convert FREAD/FWRITE to O_RD/WR */
966 fi
->flags
= (mode
& ~(O_CREAT
| O_EXCL
| O_TRUNC
)) - 1;
968 if (pn
->pn_va
.va_type
== VDIR
) {
969 if (fuse
->op
.opendir
)
970 fuse
->op
.opendir(path
, fi
);
973 fuse
->op
.open(path
, fi
);
976 rn
->flags
|= RN_OPEN
;
984 puffs_fuse_node_close(struct puffs_usermount
*pu
, void *opc
, int fflag
)
986 struct puffs_node
*pn
= opc
;
987 struct refusenode
*rn
= pn
->pn_data
;
989 struct fuse_file_info
*fi
;
990 const char *path
= PNPATH(pn
);
993 fuse
= puffs_getspecific(pu
);
998 set_fuse_context_uid_gid(pcr
);
1001 if (rn
->flags
& RN_OPEN
) {
1002 if (pn
->pn_va
.va_type
== VDIR
) {
1003 if (fuse
->op
.releasedir
)
1004 ret
= fuse
->op
.releasedir(path
, fi
);
1006 if (fuse
->op
.release
)
1007 ret
= fuse
->op
.release(path
, fi
);
1010 rn
->flags
&= ~RN_OPEN
;
1016 /* read some more from the file */
1019 puffs_fuse_node_read(struct puffs_usermount
*pu
, void *opc
, uint8_t *buf
,
1020 off_t offset
, size_t *resid
, const struct puffs_cred
*pcr
,
1023 struct puffs_node
*pn
= opc
;
1024 struct refusenode
*rn
= pn
->pn_data
;
1026 const char *path
= PNPATH(pn
);
1030 fuse
= puffs_getspecific(pu
);
1031 if (fuse
->op
.read
== NULL
) {
1035 set_fuse_context_uid_gid(pcr
);
1038 if (maxread
> pn
->pn_va
.va_size
- offset
) {
1040 maxread
= pn
->pn_va
.va_size
- offset
;
1045 ret
= (*fuse
->op
.read
)(path
, (char *)buf
, maxread
, offset
,
1056 /* write to the file */
1059 puffs_fuse_node_write(struct puffs_usermount
*pu
, void *opc
, uint8_t *buf
,
1060 off_t offset
, size_t *resid
, const struct puffs_cred
*pcr
,
1063 struct puffs_node
*pn
= opc
;
1064 struct refusenode
*rn
= pn
->pn_data
;
1066 const char *path
= PNPATH(pn
);
1069 fuse
= puffs_getspecific(pu
);
1070 if (fuse
->op
.write
== NULL
) {
1074 set_fuse_context_uid_gid(pcr
);
1076 if (ioflag
& PUFFS_IO_APPEND
)
1077 offset
= pn
->pn_va
.va_size
;
1079 ret
= (*fuse
->op
.write
)(path
, (char *)buf
, *resid
, offset
,
1083 if ((uint64_t)(offset
+ ret
) > pn
->pn_va
.va_size
)
1084 pn
->pn_va
.va_size
= offset
+ ret
;
1095 puffs_fuse_node_readdir(struct puffs_usermount
*pu
, void *opc
,
1096 struct dirent
*dent
, off_t
*readoff
, size_t *reslen
,
1097 const struct puffs_cred
*pcr
, int *eofflag
,
1098 off_t
*cookies
, size_t *ncookies
)
1100 struct puffs_node
*pn
= opc
;
1101 struct refusenode
*rn
= pn
->pn_data
;
1102 struct puffs_fuse_dirh
*dirh
;
1104 struct dirent
*fromdent
;
1105 const char *path
= PNPATH(pn
);
1108 fuse
= puffs_getspecific(pu
);
1109 if (fuse
->op
.readdir
== NULL
&& fuse
->op
.getdir
== NULL
) {
1113 set_fuse_context_uid_gid(pcr
);
1115 if (pn
->pn_va
.va_type
!= VDIR
)
1121 * if we are starting from the beginning, slurp entire directory
1124 if (*readoff
== 0) {
1125 /* free old buffers */
1127 memset(dirh
, 0, sizeof(struct puffs_fuse_dirh
));
1129 if (fuse
->op
.readdir
)
1130 ret
= fuse
->op
.readdir(path
, dirh
, puffs_fuse_fill_dir
,
1133 ret
= fuse
->op
.getdir(path
, dirh
, puffs_fuse_dirfil
);
1138 /* now, stuff results into the kernel buffers */
1139 while (*readoff
< (off_t
)(dirh
->bufsize
- dirh
->reslen
)) {
1141 fromdent
= (struct dirent
*)((uint8_t *)dirh
->dbuf
+ *readoff
);
1143 if (*reslen
< _DIRENT_DIRSIZ(fromdent
))
1146 memcpy(dent
, fromdent
, _DIRENT_DIRSIZ(fromdent
));
1147 *readoff
+= _DIRENT_DIRSIZ(fromdent
);
1148 *reslen
-= _DIRENT_DIRSIZ(fromdent
);
1150 dent
= _DIRENT_NEXT(dent
);
1158 puffs_fuse_node_reclaim(struct puffs_usermount
*pu
, void *opc
)
1160 struct puffs_node
*pn
= opc
;
1168 puffs_fuse_fs_unmount(struct puffs_usermount
*pu
, int flags
)
1172 fuse
= puffs_getspecific(pu
);
1173 if (fuse
->op
.destroy
== NULL
) {
1176 (*fuse
->op
.destroy
)(fuse
);
1182 puffs_fuse_fs_sync(struct puffs_usermount
*pu
, int flags
)
1185 set_fuse_context_uid_gid(cr
);
1192 puffs_fuse_fs_statvfs(struct puffs_usermount
*pu
, struct statvfs
*svfsb
)
1197 fuse
= puffs_getspecific(pu
);
1198 if (fuse
->op
.statfs
== NULL
) {
1199 if ((ret
= statvfs(PNPATH(puffs_getroot(pu
)), svfsb
)) == -1) {
1203 ret
= fuse
->op
.statfs(PNPATH(puffs_getroot(pu
)), svfsb
);
1210 /* End of puffs_fuse operations */
1213 fuse_main_real(int argc
, char **argv
, const struct fuse_operations
*ops
,
1214 size_t size
, void *userdata
)
1221 fuse
= fuse_setup_real(argc
, argv
, ops
, size
, &mountpoint
,
1222 &multithreaded
, &fd
, userdata
);
1224 return fuse_loop(fuse
);
1228 * XXX: just defer the operation until fuse_new() when we have more
1229 * info on our hands. The real beef is why's this separate in fuse in
1234 fuse_mount(const char *dir
, struct fuse_args
*args
)
1236 struct fuse_chan
*fc
;
1239 if ((fc
= calloc(1, sizeof(*fc
))) == NULL
) {
1240 err(EXIT_FAILURE
, "fuse_mount");
1244 if ((fc
->dir
= strdup(dir
)) == NULL
) {
1245 err(EXIT_FAILURE
, "fuse_mount");
1249 * we need to deep copy the args struct - some fuse file
1250 * systems "clean up" the argument vector for "security
1253 fc
->args
= fuse_opt_deep_copy_args(args
->argc
, args
->argv
);
1255 if (args
->argc
> 0) {
1256 set_refuse_mount_name(args
->argv
, name
, sizeof(name
));
1257 if ((args
->argv
[0] = strdup(name
)) == NULL
)
1258 err(1, "fuse_mount");
1266 fuse_new(struct fuse_chan
*fc
, struct fuse_args
*args
,
1267 const struct fuse_operations
*ops
, size_t size
, void *userdata
)
1269 struct puffs_usermount
*pu
;
1270 struct fuse_context
*fusectx
;
1271 struct puffs_pathobj
*po_root
;
1272 struct puffs_node
*pn_root
;
1273 struct puffs_ops
*pops
;
1274 struct refusenode
*rn_root
;
1275 struct statvfs svfsb
;
1278 extern int puffs_fakecc
;
1282 if ((fuse
= calloc(1, sizeof(*fuse
))) == NULL
) {
1283 err(EXIT_FAILURE
, "fuse_new");
1286 /* copy fuse ops to their own structure */
1287 (void) memcpy(&fuse
->op
, ops
, sizeof(fuse
->op
));
1289 fusectx
= fuse_get_context();
1290 fusectx
->fuse
= fuse
;
1294 fusectx
->private_data
= userdata
;
1298 if (fuse
->op
.init
!= NULL
)
1299 fusectx
->private_data
= fuse
->op
.init(NULL
); /* XXX */
1301 /* initialise the puffs operations structure */
1304 PUFFSOP_SET(pops
, puffs_fuse
, fs
, sync
);
1305 PUFFSOP_SET(pops
, puffs_fuse
, fs
, statvfs
);
1306 PUFFSOP_SET(pops
, puffs_fuse
, fs
, unmount
);
1309 * XXX: all of these don't possibly need to be
1310 * unconditionally set
1312 PUFFSOP_SET(pops
, puffs_fuse
, node
, lookup
);
1313 PUFFSOP_SET(pops
, puffs_fuse
, node
, getattr
);
1314 PUFFSOP_SET(pops
, puffs_fuse
, node
, setattr
);
1315 PUFFSOP_SET(pops
, puffs_fuse
, node
, readdir
);
1316 PUFFSOP_SET(pops
, puffs_fuse
, node
, readlink
);
1317 PUFFSOP_SET(pops
, puffs_fuse
, node
, mknod
);
1318 PUFFSOP_SET(pops
, puffs_fuse
, node
, create
);
1319 PUFFSOP_SET(pops
, puffs_fuse
, node
, remove
);
1320 PUFFSOP_SET(pops
, puffs_fuse
, node
, mkdir
);
1321 PUFFSOP_SET(pops
, puffs_fuse
, node
, rmdir
);
1322 PUFFSOP_SET(pops
, puffs_fuse
, node
, symlink
);
1323 PUFFSOP_SET(pops
, puffs_fuse
, node
, rename
);
1324 PUFFSOP_SET(pops
, puffs_fuse
, node
, link
);
1325 PUFFSOP_SET(pops
, puffs_fuse
, node
, open
);
1326 PUFFSOP_SET(pops
, puffs_fuse
, node
, close
);
1327 PUFFSOP_SET(pops
, puffs_fuse
, node
, read
);
1328 PUFFSOP_SET(pops
, puffs_fuse
, node
, write
);
1329 PUFFSOP_SET(pops
, puffs_fuse
, node
, reclaim
);
1331 argv0
= (*args
->argv
[0] == 0x0) ? fc
->args
->argv
[0] : args
->argv
[0];
1332 set_refuse_mount_name(&argv0
, name
, sizeof(name
));
1334 puffs_fakecc
= 1; /* XXX */
1335 pu
= puffs_init(pops
, _PATH_PUFFS
, name
, fuse
,
1336 PUFFS_FLAG_BUILDPATH
1337 | PUFFS_FLAG_HASHPATH
1338 | PUFFS_KFLAG_NOCACHE
);
1340 err(EXIT_FAILURE
, "puffs_init");
1344 pn_root
= newrn(pu
);
1345 puffs_setroot(pu
, pn_root
);
1346 rn_root
= pn_root
->pn_data
;
1347 rn_root
->flags
|= RN_ROOT
;
1349 po_root
= puffs_getrootpathobj(pu
);
1350 if ((po_root
->po_path
= strdup("/")) == NULL
)
1352 po_root
->po_len
= 1;
1353 puffs_path_buildhash(pu
, po_root
);
1356 puffs_vattr_null(&pn_root
->pn_va
);
1357 pn_root
->pn_va
.va_type
= VDIR
;
1358 pn_root
->pn_va
.va_mode
= 0755;
1359 if (fuse
->op
.getattr
)
1360 if (fuse
->op
.getattr(po_root
->po_path
, &st
) == 0)
1361 puffs_stat2vattr(&pn_root
->pn_va
, &st
);
1362 assert(pn_root
->pn_va
.va_type
== VDIR
);
1364 puffs_set_prepost(pu
, set_fuse_context_pid
, NULL
);
1366 puffs_zerostatvfs(&svfsb
);
1367 if (puffs_mount(pu
, fc
->dir
, MNT_NODEV
| MNT_NOSUID
, pn_root
) == -1) {
1368 err(EXIT_FAILURE
, "puffs_mount: directory \"%s\"", fc
->dir
);
1375 fuse_loop(struct fuse
*fuse
)
1378 return puffs_mainloop(fuse
->fc
->pu
);
1382 fuse_destroy(struct fuse
*fuse
)
1386 * TODO: needs to assert the fs is quiescent, i.e. no other
1390 delete_context_key();
1391 /* XXXXXX: missing stuff */
1396 fuse_exit(struct fuse
*fuse
)
1399 /* XXX: puffs_exit() is WRONG */
1400 if (fuse
->fc
->dead
== 0)
1401 puffs_exit(fuse
->fc
->pu
, 1);
1406 * XXX: obviously not the most perfect of functions, but needs some
1407 * puffs tweaking for a better tomorrow
1411 fuse_unmount(const char *mp
, struct fuse_chan
*fc
)
1414 /* XXX: puffs_exit() is WRONG */
1416 puffs_exit(fc
->pu
, 1);
1422 fuse_unmount_compat22(const char *mp
)
1428 /* The next function "exposes" struct fuse to userland. Not much
1429 * that we can do about this, as we're conforming to a defined
1433 fuse_teardown(struct fuse
*fuse
, char *mountpoint
)
1435 fuse_unmount(mountpoint
, fuse
->fc
);