1 /* $NetBSD: refuse.c,v 1.91 2009/01/26 04:00:40 lukem 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.91 2009/01/26 04:00:40 lukem 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
;
98 pthread_rwlock_t tree_lock
;
100 struct fuse_config conf
;
104 struct puffs_fuse_dirh
{
113 struct fuse_file_info file_info
;
114 struct puffs_fuse_dirh dirh
;
119 #define RN_OPEN 0x02 /* XXX: could just use opencount */
121 static int fuse_setattr(struct fuse
*, struct puffs_node
*,
122 const char *, const struct vattr
*);
124 static struct puffs_node
*
125 newrn(struct puffs_usermount
*pu
)
127 struct puffs_node
*pn
;
128 struct refusenode
*rn
;
130 if ((rn
= calloc(1, sizeof(*rn
))) == NULL
) {
131 err(EXIT_FAILURE
, "newrn");
133 pn
= puffs_pn_new(pu
, rn
);
139 nukern(struct puffs_node
*pn
)
141 struct refusenode
*rn
= pn
->pn_data
;
148 /* XXX - not threadsafe */
149 static ino_t fakeino
= 3;
151 /***************** start of pthread context routines ************************/
154 * Notes on fuse_context:
155 * we follow fuse's lead and use the pthread specific information to hold
156 * a reference to the fuse_context structure for this thread.
158 #ifdef MULTITHREADED_REFUSE
159 static pthread_mutex_t context_mutex
= PTHREAD_MUTEX_INITIALIZER
;
160 static pthread_key_t context_key
;
161 static unsigned long context_refc
;
164 /* return the fuse_context struct related to this thread */
165 struct fuse_context
*
166 fuse_get_context(void)
168 #ifdef MULTITHREADED_REFUSE
169 struct fuse_context
*ctxt
;
171 if ((ctxt
= pthread_getspecific(context_key
)) == NULL
) {
172 if ((ctxt
= calloc(1, sizeof(struct fuse_context
))) == NULL
) {
175 pthread_setspecific(context_key
, ctxt
);
179 static struct fuse_context fcon
;
185 /* used as a callback function */
186 #ifdef MULTITHREADED_REFUSE
188 free_context(void *ctxt
)
195 * Create the pthread key. The reason for the complexity is to
196 * enable use of multiple fuse instances within a single process.
199 create_context_key(void)
201 #ifdef MULTITHREADED_REFUSE
204 rv
= pthread_mutex_lock(&context_mutex
);
207 if (context_refc
== 0) {
208 if (pthread_key_create(&context_key
, free_context
) != 0) {
209 warnx("create_context_key: pthread_key_create failed");
210 pthread_mutex_unlock(&context_mutex
);
215 pthread_mutex_unlock(&context_mutex
);
223 delete_context_key(void)
225 #ifdef MULTITHREADED_REFUSE
226 pthread_mutex_lock(&context_mutex
);
227 /* If we are the last fuse instances using the key, delete it */
228 if (--context_refc
== 0) {
229 free(pthread_getspecific(context_key
));
230 pthread_key_delete(context_key
);
232 pthread_mutex_unlock(&context_mutex
);
236 /* set the uid and gid of the calling process in the current fuse context */
238 set_fuse_context_uid_gid(const struct puffs_cred
*cred
)
240 struct fuse_context
*fusectx
;
244 fusectx
= fuse_get_context();
245 if (puffs_cred_getuid(cred
, &uid
) == 0) {
248 if (puffs_cred_getgid(cred
, &gid
) == 0) {
253 /* set the pid of the calling process in the current fuse context */
255 set_fuse_context_pid(struct puffs_usermount
*pu
)
257 struct puffs_cc
*pcc
= puffs_cc_getcc(pu
);
258 struct fuse_context
*fusectx
;
260 fusectx
= fuse_get_context();
261 puffs_cc_getcaller(pcc
, &fusectx
->pid
, NULL
);
264 /***************** end of pthread context routines ************************/
266 #define DIR_CHUNKSIZE 4096
268 fill_dirbuf(struct puffs_fuse_dirh
*dh
, const char *name
, ino_t dino
,
273 if (dh
->bufsize
== 0) {
274 if ((dh
->dbuf
= calloc(1, DIR_CHUNKSIZE
)) == NULL
) {
278 dh
->reslen
= dh
->bufsize
= DIR_CHUNKSIZE
;
281 if (puffs_nextdent(&dh
->d
, name
, dino
, dtype
, &dh
->reslen
)) {
285 /* try to increase buffer space */
286 dh
->dbuf
= realloc(dh
->dbuf
, dh
->bufsize
+ DIR_CHUNKSIZE
);
287 if (dh
->dbuf
== NULL
) {
290 dh
->d
= (void *)((uint8_t *)dh
->dbuf
+ (dh
->bufsize
- dh
->reslen
));
291 dh
->reslen
+= DIR_CHUNKSIZE
;
292 dh
->bufsize
+= DIR_CHUNKSIZE
;
294 return !puffs_nextdent(&dh
->d
, name
, dino
, dtype
, &dh
->reslen
);
298 /* XXX: I have no idea how "off" is supposed to be used */
300 puffs_fuse_fill_dir(void *buf
, const char *name
,
301 const struct stat
*stbuf
, off_t off
)
303 struct puffs_fuse_dirh
*deh
= buf
;
311 dtype
= puffs_vtype2dt(puffs_mode2vt(stbuf
->st_mode
));
312 dino
= stbuf
->st_ino
;
315 * Some FUSE file systems like to always use 0 as the
316 * inode number. Our readdir() doesn't like to show
317 * directory entries with inode number 0 ==> workaround.
324 return fill_dirbuf(deh
, name
, dino
, dtype
);
328 puffs_fuse_dirfil(fuse_dirh_t h
, const char *name
, int type
, ino_t ino
)
333 if ((dtype
= type
) == 0) {
337 dino
= (ino
) ? ino
: fakeino
++;
339 return fill_dirbuf(h
, name
, dino
, dtype
);
342 /* place the refuse file system name into `name' */
344 set_refuse_mount_name(char **argv
, char *name
, size_t size
)
348 if (argv
== NULL
|| *argv
== NULL
) {
349 (void) strlcpy(name
, "refuse", size
);
351 if ((slash
= strrchr(*argv
, '/')) == NULL
) {
356 if (strncmp(*argv
, "refuse:", 7) == 0) {
357 /* we've already done this */
358 (void) strlcpy(name
, *argv
, size
);
360 (void) snprintf(name
, size
, "refuse:%s", slash
);
366 /* this function exposes struct fuse to userland */
368 fuse_setup_real(int argc
, char **argv
, const struct fuse_operations
*ops
,
369 size_t size
, char **mountpoint
, int *multithreaded
, int *fd
,
372 struct fuse_chan
*fc
;
373 struct fuse_args
*args
;
378 /* set up the proper name */
379 set_refuse_mount_name(argv
, name
, sizeof(name
));
381 /* grab the pthread context key */
382 if (!create_context_key()) {
386 /* stuff name into fuse_args */
387 args
= fuse_opt_deep_copy_args(argc
, argv
);
388 if (args
->argc
> 0) {
391 if ((args
->argv
[0] = strdup(name
)) == NULL
) {
392 fuse_opt_free_args(args
);
396 /* count back from the end over arguments starting with '-' */
397 for (i
= argc
- 1 ; i
> 0 && *argv
[i
] == '-' ; --i
) {
400 fc
= fuse_mount(*mountpoint
= argv
[i
], args
);
401 fuse
= fuse_new(fc
, args
, ops
, size
, user_data
);
403 fuse_opt_free_args(args
);
406 /* XXX - wait for puffs to become multi-threaded */
411 /* XXX - this is unused */
424 fuse_setup(int argc
, char **argv
, const struct fuse_operations
*ops
,
425 size_t size
, char **mountpoint
, int *multithreaded
, int *fd
)
427 return fuse_setup_real(argc
, argv
, ops
, size
, mountpoint
,
428 multithreaded
, fd
, NULL
);
432 fuse_setup26(int argc
, char **argv
, const struct fuse_operations
*ops
,
433 size_t size
, char **mountpoint
, int *multithreaded
, void *user_data
)
435 return fuse_setup_real(argc
, argv
, ops
, size
, mountpoint
,
436 multithreaded
, NULL
, user_data
);
439 #define FUSE_ERR_UNLINK(fuse, file) if (fuse->op.unlink) fuse->op.unlink(file)
440 #define FUSE_ERR_RMDIR(fuse, dir) if (fuse->op.rmdir) fuse->op.rmdir(dir)
444 fuse_getattr(struct fuse
*fuse
, struct puffs_node
*pn
, const char *path
,
450 if (fuse
->op
.getattr
== NULL
) {
454 /* wrap up return code */
455 memset(&st
, 0, sizeof(st
));
456 ret
= (*fuse
->op
.getattr
)(path
, &st
);
459 if (st
.st_blksize
== 0)
460 st
.st_blksize
= DEV_BSIZE
;
461 puffs_stat2vattr(va
, &st
);
467 /* utility function to set various elements of the attribute */
469 fuse_setattr(struct fuse
*fuse
, struct puffs_node
*pn
, const char *path
,
470 const struct vattr
*va
)
472 struct refusenode
*rn
= pn
->pn_data
;
484 if (mode
!= (mode_t
)PUFFS_VNOVAL
) {
487 if (fuse
->op
.chmod
== NULL
) {
490 ret
= fuse
->op
.chmod(path
, mode
);
495 if (uid
!= (uid_t
)PUFFS_VNOVAL
|| gid
!= (gid_t
)PUFFS_VNOVAL
) {
498 if (fuse
->op
.chown
== NULL
) {
501 ret
= fuse
->op
.chown(path
, uid
, gid
);
506 if (va
->va_atime
.tv_sec
!= (time_t)PUFFS_VNOVAL
507 || va
->va_mtime
.tv_sec
!= (long)PUFFS_VNOVAL
) {
510 if (fuse
->op
.utimens
) {
511 struct timespec tv
[2];
513 tv
[0].tv_sec
= va
->va_atime
.tv_sec
;
514 tv
[0].tv_nsec
= va
->va_atime
.tv_nsec
;
515 tv
[1].tv_sec
= va
->va_mtime
.tv_sec
;
516 tv
[1].tv_nsec
= va
->va_mtime
.tv_nsec
;
518 ret
= fuse
->op
.utimens(path
, tv
);
519 } else if (fuse
->op
.utime
) {
520 struct utimbuf timbuf
;
522 timbuf
.actime
= va
->va_atime
.tv_sec
;
523 timbuf
.modtime
= va
->va_mtime
.tv_sec
;
525 ret
= fuse
->op
.utime(path
, &timbuf
);
533 if (va
->va_size
!= (u_quad_t
)PUFFS_VNOVAL
) {
536 if (fuse
->op
.truncate
) {
537 ret
= fuse
->op
.truncate(path
, (off_t
)va
->va_size
);
538 } else if (fuse
->op
.ftruncate
) {
539 ret
= fuse
->op
.ftruncate(path
, (off_t
)va
->va_size
,
548 /* XXX: no reflection with reality */
549 puffs_setvattr(&pn
->pn_va
, va
);
556 fuse_newnode(struct puffs_usermount
*pu
, const char *path
,
557 const struct vattr
*va
, struct fuse_file_info
*fi
,
558 struct puffs_newinfo
*pni
, struct puffs_node
**pn_new
)
560 struct puffs_node
*pn
;
561 struct refusenode
*rn
;
565 fuse
= puffs_getspecific(pu
);
570 if (va
->va_type
== VDIR
) {
571 FUSE_ERR_RMDIR(fuse
, path
);
573 FUSE_ERR_UNLINK(fuse
, path
);
577 fuse_setattr(fuse
, pn
, path
, va
);
578 if (fuse_getattr(fuse
, pn
, path
, &newva
) == 0)
579 puffs_setvattr(&pn
->pn_va
, &newva
);
583 memcpy(&rn
->file_info
, fi
, sizeof(struct fuse_file_info
));
585 puffs_newinfo_setcookie(pni
, pn
);
593 /* operation wrappers start here */
595 /* lookup the path */
598 puffs_fuse_node_lookup(struct puffs_usermount
*pu
, void *opc
,
599 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
)
601 struct puffs_node
*pn_res
;
604 const char *path
= PCNPATH(pcn
);
607 fuse
= puffs_getspecific(pu
);
609 set_fuse_context_uid_gid(pcn
->pcn_cred
);
611 ret
= fuse
->op
.getattr(path
, &st
);
617 /* XXX: fiXXXme unconst */
618 pn_res
= puffs_pn_nodewalk(pu
, puffs_path_walkcmp
,
619 __UNCONST(&pcn
->pcn_po_full
));
620 if (pn_res
== NULL
) {
624 puffs_stat2vattr(&pn_res
->pn_va
, &st
);
627 puffs_newinfo_setcookie(pni
, pn_res
);
628 puffs_newinfo_setvtype(pni
, pn_res
->pn_va
.va_type
);
629 puffs_newinfo_setsize(pni
, (voff_t
)pn_res
->pn_va
.va_size
);
630 puffs_newinfo_setrdev(pni
, pn_res
->pn_va
.va_rdev
);
635 /* get attributes for the path name */
638 puffs_fuse_node_getattr(struct puffs_usermount
*pu
, void *opc
, struct vattr
*va
,
639 const struct puffs_cred
*pcr
)
641 struct puffs_node
*pn
= opc
;
643 const char *path
= PNPATH(pn
);
645 fuse
= puffs_getspecific(pu
);
647 set_fuse_context_uid_gid(pcr
);
649 return fuse_getattr(fuse
, pn
, path
, va
);
652 /* read the contents of the symbolic link */
655 puffs_fuse_node_readlink(struct puffs_usermount
*pu
, void *opc
,
656 const struct puffs_cred
*cred
, char *linkname
, size_t *linklen
)
658 struct puffs_node
*pn
= opc
;
660 const char *path
= PNPATH(pn
), *p
;
663 fuse
= puffs_getspecific(pu
);
664 if (fuse
->op
.readlink
== NULL
) {
668 set_fuse_context_uid_gid(cred
);
670 /* wrap up return code */
671 ret
= (*fuse
->op
.readlink
)(path
, linkname
, *linklen
);
674 p
= memchr(linkname
, '\0', *linklen
);
678 *linklen
= p
- linkname
;
684 /* make the special node */
687 puffs_fuse_node_mknod(struct puffs_usermount
*pu
, void *opc
,
688 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
,
689 const struct vattr
*va
)
693 const char *path
= PCNPATH(pcn
);
696 fuse
= puffs_getspecific(pu
);
697 if (fuse
->op
.mknod
== NULL
) {
701 set_fuse_context_uid_gid(pcn
->pcn_cred
);
703 /* wrap up return code */
704 mode
= puffs_addvtype2mode(va
->va_mode
, va
->va_type
);
705 ret
= (*fuse
->op
.mknod
)(path
, mode
, va
->va_rdev
);
708 ret
= fuse_newnode(pu
, path
, va
, NULL
, pni
, NULL
);
714 /* make a directory */
717 puffs_fuse_node_mkdir(struct puffs_usermount
*pu
, void *opc
,
718 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
,
719 const struct vattr
*va
)
722 mode_t mode
= va
->va_mode
;
723 const char *path
= PCNPATH(pcn
);
726 fuse
= puffs_getspecific(pu
);
728 set_fuse_context_uid_gid(pcn
->pcn_cred
);
730 if (fuse
->op
.mkdir
== NULL
) {
734 /* wrap up return code */
735 ret
= (*fuse
->op
.mkdir
)(path
, mode
);
738 ret
= fuse_newnode(pu
, path
, va
, NULL
, pni
, NULL
);
745 * create a regular file
747 * since linux/fuse sports using mknod for creating regular files
748 * instead of having a separate call for it in some versions, if
749 * we don't have create, just jump to op->mknod.
753 puffs_fuse_node_create(struct puffs_usermount
*pu
, void *opc
,
754 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
,
755 const struct vattr
*va
)
758 struct fuse_file_info fi
;
759 struct puffs_node
*pn
;
760 mode_t mode
= va
->va_mode
;
761 const char *path
= PCNPATH(pcn
);
764 fuse
= puffs_getspecific(pu
);
766 set_fuse_context_uid_gid(pcn
->pcn_cred
);
769 if (fuse
->op
.create
) {
770 ret
= fuse
->op
.create(path
, mode
| S_IFREG
, &fi
);
774 } else if (fuse
->op
.mknod
) {
775 ret
= fuse
->op
.mknod(path
, mode
| S_IFREG
, 0);
782 ret
= fuse_newnode(pu
, path
, va
, &fi
, pni
, &pn
);
784 /* sweet.. create also open the file */
786 struct refusenode
*rn
;
789 rn
->flags
|= RN_OPEN
;
797 /* remove the directory entry */
800 puffs_fuse_node_remove(struct puffs_usermount
*pu
, void *opc
, void *targ
,
801 const struct puffs_cn
*pcn
)
803 struct puffs_node
*pn_targ
= targ
;
805 const char *path
= PNPATH(pn_targ
);
808 fuse
= puffs_getspecific(pu
);
810 set_fuse_context_uid_gid(pcn
->pcn_cred
);
812 if (fuse
->op
.unlink
== NULL
) {
816 /* wrap up return code */
817 ret
= (*fuse
->op
.unlink
)(path
);
822 /* remove the directory */
825 puffs_fuse_node_rmdir(struct puffs_usermount
*pu
, void *opc
, void *targ
,
826 const struct puffs_cn
*pcn
)
828 struct puffs_node
*pn_targ
= targ
;
830 const char *path
= PNPATH(pn_targ
);
833 fuse
= puffs_getspecific(pu
);
835 set_fuse_context_uid_gid(pcn
->pcn_cred
);
837 if (fuse
->op
.rmdir
== NULL
) {
841 /* wrap up return code */
842 ret
= (*fuse
->op
.rmdir
)(path
);
847 /* create a symbolic link */
850 puffs_fuse_node_symlink(struct puffs_usermount
*pu
, void *opc
,
851 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn_src
,
852 const struct vattr
*va
, const char *link_target
)
855 const char *path
= PCNPATH(pcn_src
);
858 fuse
= puffs_getspecific(pu
);
860 set_fuse_context_uid_gid(pcn_src
->pcn_cred
);
862 if (fuse
->op
.symlink
== NULL
) {
866 /* wrap up return code */
867 ret
= fuse
->op
.symlink(link_target
, path
);
870 ret
= fuse_newnode(pu
, path
, va
, NULL
, pni
, NULL
);
876 /* rename a directory entry */
879 puffs_fuse_node_rename(struct puffs_usermount
*pu
, void *opc
, void *src
,
880 const struct puffs_cn
*pcn_src
, void *targ_dir
, void *targ
,
881 const struct puffs_cn
*pcn_targ
)
884 const char *path_src
= PCNPATH(pcn_src
);
885 const char *path_dest
= PCNPATH(pcn_targ
);
888 fuse
= puffs_getspecific(pu
);
890 set_fuse_context_uid_gid(pcn_targ
->pcn_cred
);
892 if (fuse
->op
.rename
== NULL
) {
896 ret
= fuse
->op
.rename(path_src
, path_dest
);
904 /* create a link in the file system */
907 puffs_fuse_node_link(struct puffs_usermount
*pu
, void *opc
, void *targ
,
908 const struct puffs_cn
*pcn
)
910 struct puffs_node
*pn
= targ
;
914 fuse
= puffs_getspecific(pu
);
916 set_fuse_context_uid_gid(pcn
->pcn_cred
);
918 if (fuse
->op
.link
== NULL
) {
922 /* wrap up return code */
923 ret
= (*fuse
->op
.link
)(PNPATH(pn
), PCNPATH(pcn
));
929 * fuse's regular interface provides chmod(), chown(), utimes()
930 * and truncate() + some variations, so try to fit the square block
931 * in the circle hole and the circle block .... something like that
935 puffs_fuse_node_setattr(struct puffs_usermount
*pu
, void *opc
,
936 const struct vattr
*va
, const struct puffs_cred
*pcr
)
938 struct puffs_node
*pn
= opc
;
940 const char *path
= PNPATH(pn
);
942 fuse
= puffs_getspecific(pu
);
944 set_fuse_context_uid_gid(pcr
);
946 return fuse_setattr(fuse
, pn
, path
, va
);
951 puffs_fuse_node_open(struct puffs_usermount
*pu
, void *opc
, int mode
,
952 const struct puffs_cred
*cred
)
954 struct puffs_node
*pn
= opc
;
955 struct refusenode
*rn
= pn
->pn_data
;
956 struct fuse_file_info
*fi
= &rn
->file_info
;
958 const char *path
= PNPATH(pn
);
960 fuse
= puffs_getspecific(pu
);
962 set_fuse_context_uid_gid(cred
);
964 /* if open, don't open again, lest risk nuking file private info */
965 if (rn
->flags
& RN_OPEN
) {
970 /* OFLAGS(), need to convert FREAD/FWRITE to O_RD/WR */
971 fi
->flags
= (mode
& ~(O_CREAT
| O_EXCL
| O_TRUNC
)) - 1;
973 if (pn
->pn_va
.va_type
== VDIR
) {
974 if (fuse
->op
.opendir
)
975 fuse
->op
.opendir(path
, fi
);
978 fuse
->op
.open(path
, fi
);
981 rn
->flags
|= RN_OPEN
;
989 puffs_fuse_node_close(struct puffs_usermount
*pu
, void *opc
, int fflag
,
990 const struct puffs_cred
*pcr
)
992 struct puffs_node
*pn
= opc
;
993 struct refusenode
*rn
= pn
->pn_data
;
995 struct fuse_file_info
*fi
;
996 const char *path
= PNPATH(pn
);
999 fuse
= puffs_getspecific(pu
);
1000 fi
= &rn
->file_info
;
1003 set_fuse_context_uid_gid(pcr
);
1005 if (rn
->flags
& RN_OPEN
) {
1006 if (pn
->pn_va
.va_type
== VDIR
) {
1007 if (fuse
->op
.releasedir
)
1008 ret
= fuse
->op
.releasedir(path
, fi
);
1010 if (fuse
->op
.release
)
1011 ret
= fuse
->op
.release(path
, fi
);
1014 rn
->flags
&= ~RN_OPEN
;
1020 /* read some more from the file */
1023 puffs_fuse_node_read(struct puffs_usermount
*pu
, void *opc
, uint8_t *buf
,
1024 off_t offset
, size_t *resid
, const struct puffs_cred
*pcr
,
1027 struct puffs_node
*pn
= opc
;
1028 struct refusenode
*rn
= pn
->pn_data
;
1030 const char *path
= PNPATH(pn
);
1034 fuse
= puffs_getspecific(pu
);
1035 if (fuse
->op
.read
== NULL
) {
1039 set_fuse_context_uid_gid(pcr
);
1042 if (maxread
> pn
->pn_va
.va_size
- offset
) {
1044 maxread
= pn
->pn_va
.va_size
- offset
;
1049 ret
= (*fuse
->op
.read
)(path
, (char *)buf
, maxread
, offset
,
1060 /* write to the file */
1063 puffs_fuse_node_write(struct puffs_usermount
*pu
, void *opc
, uint8_t *buf
,
1064 off_t offset
, size_t *resid
, const struct puffs_cred
*pcr
,
1067 struct puffs_node
*pn
= opc
;
1068 struct refusenode
*rn
= pn
->pn_data
;
1070 const char *path
= PNPATH(pn
);
1073 fuse
= puffs_getspecific(pu
);
1074 if (fuse
->op
.write
== NULL
) {
1078 set_fuse_context_uid_gid(pcr
);
1080 if (ioflag
& PUFFS_IO_APPEND
)
1081 offset
= pn
->pn_va
.va_size
;
1083 ret
= (*fuse
->op
.write
)(path
, (char *)buf
, *resid
, offset
,
1087 if ((uint64_t)(offset
+ ret
) > pn
->pn_va
.va_size
)
1088 pn
->pn_va
.va_size
= offset
+ ret
;
1099 puffs_fuse_node_readdir(struct puffs_usermount
*pu
, void *opc
,
1100 struct dirent
*dent
, off_t
*readoff
, size_t *reslen
,
1101 const struct puffs_cred
*pcr
, int *eofflag
,
1102 off_t
*cookies
, size_t *ncookies
)
1104 struct puffs_node
*pn
= opc
;
1105 struct refusenode
*rn
= pn
->pn_data
;
1106 struct puffs_fuse_dirh
*dirh
;
1108 struct dirent
*fromdent
;
1109 const char *path
= PNPATH(pn
);
1112 fuse
= puffs_getspecific(pu
);
1113 if (fuse
->op
.readdir
== NULL
&& fuse
->op
.getdir
== NULL
) {
1117 set_fuse_context_uid_gid(pcr
);
1119 if (pn
->pn_va
.va_type
!= VDIR
)
1125 * if we are starting from the beginning, slurp entire directory
1128 if (*readoff
== 0) {
1129 /* free old buffers */
1131 memset(dirh
, 0, sizeof(struct puffs_fuse_dirh
));
1133 if (fuse
->op
.readdir
)
1134 ret
= fuse
->op
.readdir(path
, dirh
, puffs_fuse_fill_dir
,
1137 ret
= fuse
->op
.getdir(path
, dirh
, puffs_fuse_dirfil
);
1142 /* now, stuff results into the kernel buffers */
1143 while (*readoff
< (off_t
)(dirh
->bufsize
- dirh
->reslen
)) {
1145 fromdent
= (struct dirent
*)((uint8_t *)dirh
->dbuf
+ *readoff
);
1147 if (*reslen
< _DIRENT_SIZE(fromdent
))
1150 memcpy(dent
, fromdent
, _DIRENT_SIZE(fromdent
));
1151 *readoff
+= _DIRENT_SIZE(fromdent
);
1152 *reslen
-= _DIRENT_SIZE(fromdent
);
1154 dent
= _DIRENT_NEXT(dent
);
1162 puffs_fuse_node_reclaim(struct puffs_usermount
*pu
, void *opc
)
1164 struct puffs_node
*pn
= opc
;
1172 puffs_fuse_fs_unmount(struct puffs_usermount
*pu
, int flags
)
1176 fuse
= puffs_getspecific(pu
);
1177 if (fuse
->op
.destroy
== NULL
) {
1180 (*fuse
->op
.destroy
)(fuse
);
1186 puffs_fuse_fs_sync(struct puffs_usermount
*pu
, int flags
,
1187 const struct puffs_cred
*cr
)
1189 set_fuse_context_uid_gid(cr
);
1195 puffs_fuse_fs_statvfs(struct puffs_usermount
*pu
, struct statvfs
*svfsb
)
1200 fuse
= puffs_getspecific(pu
);
1201 if (fuse
->op
.statfs
== NULL
) {
1202 if ((ret
= statvfs(PNPATH(puffs_getroot(pu
)), svfsb
)) == -1) {
1206 ret
= fuse
->op
.statfs(PNPATH(puffs_getroot(pu
)), svfsb
);
1213 /* End of puffs_fuse operations */
1216 fuse_main_real(int argc
, char **argv
, const struct fuse_operations
*ops
,
1217 size_t size
, void *userdata
)
1224 fuse
= fuse_setup(argc
, argv
, ops
, size
, &mountpoint
, &multithreaded
,
1227 return fuse_loop(fuse
);
1231 * XXX: just defer the operation until fuse_new() when we have more
1232 * info on our hands. The real beef is why's this separate in fuse in
1237 fuse_mount(const char *dir
, struct fuse_args
*args
)
1239 struct fuse_chan
*fc
;
1242 if ((fc
= calloc(1, sizeof(*fc
))) == NULL
) {
1243 err(EXIT_FAILURE
, "fuse_mount");
1247 if ((fc
->dir
= strdup(dir
)) == NULL
) {
1248 err(EXIT_FAILURE
, "fuse_mount");
1252 * we need to deep copy the args struct - some fuse file
1253 * systems "clean up" the argument vector for "security
1256 fc
->args
= fuse_opt_deep_copy_args(args
->argc
, args
->argv
);
1258 if (args
->argc
> 0) {
1259 set_refuse_mount_name(args
->argv
, name
, sizeof(name
));
1260 if ((args
->argv
[0] = strdup(name
)) == NULL
)
1261 err(1, "fuse_mount");
1269 fuse_new(struct fuse_chan
*fc
, struct fuse_args
*args
,
1270 const struct fuse_operations
*ops
, size_t size
, void *userdata
)
1272 struct puffs_usermount
*pu
;
1273 struct fuse_context
*fusectx
;
1274 struct puffs_pathobj
*po_root
;
1275 struct puffs_node
*pn_root
;
1276 struct puffs_ops
*pops
;
1277 struct refusenode
*rn_root
;
1278 struct statvfs svfsb
;
1281 extern int puffs_fakecc
;
1285 if ((fuse
= calloc(1, sizeof(*fuse
))) == NULL
) {
1286 err(EXIT_FAILURE
, "fuse_new");
1289 /* copy fuse ops to their own structure */
1290 (void) memcpy(&fuse
->op
, ops
, sizeof(fuse
->op
));
1292 fusectx
= fuse_get_context();
1293 fusectx
->fuse
= fuse
;
1297 fusectx
->private_data
= userdata
;
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
);
1365 fusectx
->private_data
= fuse
->op
.init(NULL
); /* XXX */
1367 puffs_set_prepost(pu
, set_fuse_context_pid
, NULL
);
1369 puffs_zerostatvfs(&svfsb
);
1370 if (puffs_mount(pu
, fc
->dir
, MNT_NODEV
| MNT_NOSUID
, pn_root
) == -1) {
1371 err(EXIT_FAILURE
, "puffs_mount: directory \"%s\"", fc
->dir
);
1378 fuse_loop(struct fuse
*fuse
)
1381 return puffs_mainloop(fuse
->fc
->pu
);
1385 fuse_destroy(struct fuse
*fuse
)
1389 * TODO: needs to assert the fs is quiescent, i.e. no other
1393 delete_context_key();
1394 /* XXXXXX: missing stuff */
1399 fuse_exit(struct fuse
*fuse
)
1402 /* XXX: puffs_exit() is WRONG */
1403 if (fuse
->fc
->dead
== 0)
1404 puffs_exit(fuse
->fc
->pu
, 1);
1409 * XXX: obviously not the most perfect of functions, but needs some
1410 * puffs tweaking for a better tomorrow
1414 fuse_unmount(const char *mp
, struct fuse_chan
*fc
)
1417 /* XXX: puffs_exit() is WRONG */
1419 puffs_exit(fc
->pu
, 1);
1425 fuse_unmount_compat22(const char *mp
)
1431 /* The next function "exposes" struct fuse to userland. Not much
1432 * that we can do about this, as we're conforming to a defined
1436 fuse_teardown(struct fuse
*fuse
, char *mountpoint
)
1438 fuse_unmount(mountpoint
, fuse
->fc
);