├── test ├── .gitignore ├── Makefile └── stracedecode.c ├── doc ├── .gitignore ├── Makefile.am ├── ulockmgr_server.1 ├── fusermount.1 ├── how-fuse-works └── mount.fuse.8 ├── util ├── udev.rules ├── .gitignore ├── init_script ├── Makefile.am ├── mount.fuse.c └── ulockmgr_server.c ├── example ├── .gitignore ├── Makefile.am ├── fioc.h ├── fselclient.c ├── null.c ├── hello.c ├── fioclient.c ├── hello_ll.c ├── fioc.c ├── fsel.c ├── cusexmp.c └── fusexmp.c ├── include ├── old │ └── fuse.h ├── Makefile.am ├── ulockmgr.h ├── fuse_common_compat.h ├── cuse_lowlevel.h ├── fuse_lowlevel_compat.h ├── fuse_opt.h └── fuse_compat.h ├── fuse.pc.in ├── Makefile.am ├── .gitignore ├── makeconf.sh ├── lib ├── mount_util.h ├── fuse_loop.c ├── Makefile.am ├── fuse_signals.c ├── fuse_misc.h ├── Android.mk ├── fuse_kern_chan.c ├── fuse_mt.c ├── fuse_i.h ├── fuse_versionscript ├── fuse_session.c ├── fuse_loop_mt.c ├── buffer.c ├── mount_util.c ├── mount_bsd.c └── cuse_lowlevel.c ├── Android.mk ├── README.NFS ├── android ├── statvfs.c ├── sys │ └── statvfs.h └── config.h ├── AUTHORS ├── configure.ac ├── README.md └── NEWS /test/.gitignore: -------------------------------------------------------------------------------- 1 | !Makefile 2 | test 3 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | doxygen_sqlite3.db 2 | html/ 3 | -------------------------------------------------------------------------------- /util/udev.rules: -------------------------------------------------------------------------------- 1 | KERNEL=="fuse", MODE="0666" 2 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-Wall -W 3 | 4 | all: test 5 | 6 | clean: 7 | rm -f *.o test 8 | -------------------------------------------------------------------------------- /util/.gitignore: -------------------------------------------------------------------------------- 1 | fusermount 2 | ulockmgr_server 3 | fuse_ioslave 4 | mount.fuse 5 | mount_util.c 6 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | /fusexmp 2 | /fusexmp_fh 3 | /null 4 | /hello 5 | /hello_ll 6 | /fioc 7 | /fioclient 8 | /fsel 9 | /fselclient 10 | /cusexmp 11 | -------------------------------------------------------------------------------- /doc/Makefile.am: -------------------------------------------------------------------------------- 1 | ## Process this file with automake to produce Makefile.in 2 | 3 | dist_man_MANS = fusermount.1 mount.fuse.8 ulockmgr_server.1 4 | 5 | EXTRA_DIST = how-fuse-works kernel.txt Doxyfile html 6 | -------------------------------------------------------------------------------- /include/old/fuse.h: -------------------------------------------------------------------------------- 1 | /* 2 | This header is for compatibility with older software using FUSE. 3 | 4 | Please use 'pkg-config --cflags fuse' to set include path. The 5 | correct usage is still '#include ', not '#include 6 | '. 7 | */ 8 | 9 | #include "fuse/fuse.h" 10 | -------------------------------------------------------------------------------- /fuse.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: fuse 7 | Description: Filesystem in Userspace 8 | Version: @VERSION@ 9 | Libs: -L${libdir} -lfuse -pthread 10 | Libs.private: @libfuse_libs@ 11 | Cflags: -I${includedir}/fuse -D_FILE_OFFSET_BITS=64 12 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ## Process this file with automake to produce Makefile.in 2 | 3 | ACLOCAL_AMFLAGS = -I m4 4 | 5 | SUBDIRS = @subdirs2@ doc 6 | AUTOMAKE_OPTIONS = subdir-objects 7 | 8 | EXTRA_DIST = \ 9 | fuse.pc.in \ 10 | README* 11 | 12 | pkgconfigdir = @pkgconfigdir@ 13 | pkgconfig_DATA = fuse.pc 14 | 15 | $(pkgconfig_DATA): config.status 16 | -------------------------------------------------------------------------------- /include/Makefile.am: -------------------------------------------------------------------------------- 1 | ## Process this file with automake to produce Makefile.in 2 | 3 | fuseincludedir=$(includedir)/fuse 4 | 5 | fuseinclude_HEADERS = \ 6 | fuse.h \ 7 | fuse_compat.h \ 8 | fuse_common.h \ 9 | fuse_common_compat.h \ 10 | fuse_lowlevel.h \ 11 | fuse_lowlevel_compat.h \ 12 | fuse_opt.h \ 13 | cuse_lowlevel.h 14 | 15 | include_HEADERS = old/fuse.h ulockmgr.h 16 | 17 | noinst_HEADERS = fuse_kernel.h 18 | -------------------------------------------------------------------------------- /example/Makefile.am: -------------------------------------------------------------------------------- 1 | ## Process this file with automake to produce Makefile.in 2 | 3 | AM_CPPFLAGS = -I$(top_srcdir)/include -D_FILE_OFFSET_BITS=64 -D_REENTRANT 4 | noinst_HEADERS = fioc.h 5 | noinst_PROGRAMS = fusexmp fusexmp_fh null hello hello_ll fioc fioclient \ 6 | fsel fselclient cusexmp 7 | 8 | LDADD = ../lib/libfuse.la 9 | fusexmp_fh_LDADD = ../lib/libfuse.la ../lib/libulockmgr.la 10 | 11 | fioclient_CPPFLAGS = 12 | fioclient_LDFLAGS = 13 | fioclient_LDADD = 14 | fselclient_CPPFLAGS = 15 | fselclient_LDFLAGS = 16 | fselclient_LDADD = 17 | 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # NOTE! Don't add files that are generated in specific 3 | # subdirectories here. Add them in the ".gitignore" file 4 | # in that subdirectory instead. 5 | # 6 | # NOTE! Please use 'git ls-files -i --exclude-standard' 7 | # command after changing this file, to see if there are 8 | # any tracked files which get ignored after the change. 9 | .* 10 | !.gitignore 11 | *.o 12 | *.lo 13 | *.la 14 | *.gz 15 | \#*# 16 | *.orig 17 | *~ 18 | Makefile.in 19 | Makefile 20 | *.m4 21 | stamp-h* 22 | /ltmain.sh 23 | /configure 24 | /install-sh 25 | /mkinstalldirs 26 | /missing 27 | /*.cache 28 | /depcomp 29 | /compile 30 | /libtool 31 | /INSTALL 32 | /fuse.pc 33 | /.pc 34 | /patches 35 | /m4 36 | -------------------------------------------------------------------------------- /makeconf.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | echo "Running libtoolize..." 4 | libtoolize -c 5 | 6 | # We use iconv directly rather than via gettext, so 7 | # we need to manually copy config.rpath. 8 | CONFIG_RPATH=/usr/share/gettext/config.rpath 9 | if ! [ -f $CONFIG_RPATH ]; then 10 | CONFIG_RPATH=/usr/local/share/gettext/config.rpath 11 | fi 12 | if ! [ -f $CONFIG_RPATH ]; then 13 | if [ -f config.rpath ]; then 14 | CONFIG_RPATH= 15 | else 16 | echo "config.rpath not found! - is gettext installed?" >&2 17 | exit 1 18 | fi 19 | fi 20 | if ! [ -z "$CONFIG_RPATH" ]; then 21 | cp "$CONFIG_RPATH" . 22 | fi 23 | 24 | echo "Running autoreconf..." 25 | autoreconf -i 26 | 27 | -------------------------------------------------------------------------------- /lib/mount_util.h: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU LGPLv2. 6 | See the file COPYING.LIB. 7 | */ 8 | 9 | #include 10 | 11 | int fuse_mnt_add_mount(const char *progname, const char *fsname, 12 | const char *mnt, const char *type, const char *opts); 13 | int fuse_mnt_remove_mount(const char *progname, const char *mnt); 14 | int fuse_mnt_umount(const char *progname, const char *abs_mnt, 15 | const char *rel_mnt, int lazy); 16 | char *fuse_mnt_resolve_path(const char *progname, const char *orig); 17 | int fuse_mnt_check_empty(const char *progname, const char *mnt, 18 | mode_t rootmode, loff_t rootsize); 19 | int fuse_mnt_check_fuseblk(void); 20 | -------------------------------------------------------------------------------- /Android.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2008 The Android Open Source Project 3 | # Copyright (C) 2015 The CyanogenMod Project 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | LOCAL_PATH := $(call my-dir) 19 | 20 | include $(call first-makefiles-under,$(LOCAL_PATH)) 21 | -------------------------------------------------------------------------------- /include/ulockmgr.h: -------------------------------------------------------------------------------- 1 | /* 2 | libulockmgr: Userspace Lock Manager Library 3 | Copyright (C) 2006 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU LGPLv2. 6 | See the file COPYING.LIB. 7 | */ 8 | 9 | #include "fuse_lowlevel.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | /** 16 | * Perform POSIX locking operation 17 | * 18 | * @param fd the file descriptor 19 | * @param cmd the locking command (F_GETFL, F_SETLK or F_SETLKW) 20 | * @param lock the lock parameters 21 | * @param owner the lock owner ID cookie 22 | * @param owner_len length of the lock owner ID cookie 23 | * @return 0 on success -errno on error 24 | */ 25 | int ulockmgr_op(int fd, int cmd, struct flock *lock, const void *owner, 26 | size_t owner_len); 27 | -------------------------------------------------------------------------------- /include/fuse_common_compat.h: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU LGPLv2. 6 | See the file COPYING.LIB. 7 | */ 8 | 9 | /* these definitions provide source compatibility to prior versions. 10 | Do not include this file directly! */ 11 | 12 | struct fuse_file_info_compat { 13 | int flags; 14 | unsigned long fh; 15 | int writepage; 16 | unsigned int direct_io : 1; 17 | unsigned int keep_cache : 1; 18 | }; 19 | 20 | int fuse_mount_compat25(const char *mountpoint, struct fuse_args *args); 21 | 22 | int fuse_mount_compat22(const char *mountpoint, const char *opts); 23 | 24 | int fuse_mount_compat1(const char *mountpoint, const char *args[]); 25 | 26 | void fuse_unmount_compat22(const char *mountpoint); 27 | -------------------------------------------------------------------------------- /example/fioc.h: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE-ioctl: ioctl support for FUSE 3 | Copyright (C) 2008 SUSE Linux Products GmbH 4 | Copyright (C) 2008 Tejun Heo 5 | 6 | This program can be distributed under the terms of the GNU GPL. 7 | See the file COPYING. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | enum { 15 | FIOC_GET_SIZE = _IOR('E', 0, size_t), 16 | FIOC_SET_SIZE = _IOW('E', 1, size_t), 17 | 18 | /* 19 | * The following two ioctls don't follow usual encoding rules 20 | * and transfer variable amount of data. 21 | */ 22 | FIOC_READ = _IO('E', 2), 23 | FIOC_WRITE = _IO('E', 3), 24 | }; 25 | 26 | struct fioc_rw_arg { 27 | off_t offset; 28 | void *buf; 29 | size_t size; 30 | size_t prev_size; /* out param for previous total size */ 31 | size_t new_size; /* out param for new total size */ 32 | }; 33 | -------------------------------------------------------------------------------- /doc/ulockmgr_server.1: -------------------------------------------------------------------------------- 1 | .TH ULOCKMGR_SERVER 1 2011\-10\-23 2.8.6 "Filesystem in Userspace (FUSE)" 2 | 3 | .SH NAME 4 | \fBulockmgr_server\fR \- Lock Manager Server for FUSE filesystems 5 | 6 | .SH SYNOPSIS 7 | \fBulockmgr_server\fR 8 | 9 | .SH DESCRIPTION 10 | Filesystem in Userspace (FUSE) is a simple interface for userspace programs to export a virtual filesystem to the Linux kernel. It also aims to provide a secure method for non privileged users to create and mount their own filesystem implementations. 11 | .PP 12 | \fBulockmgr_server\fR is the Userspace Lock Manager Server for FUSE filesystems. 13 | 14 | .SH OPTIONS 15 | \fBulockmgr_server\fR has no options. 16 | 17 | .SH SEE ALSO 18 | \fIfusermount\fR(1), 19 | \fImount\fR(8), 20 | \fImount.fuse\fR(8). 21 | 22 | .SH HOMEPAGE 23 | More information about ulockmgr_server and the FUSE project can be found at <\fIhttp://fuse.sourceforge.net/\fR>. 24 | 25 | .SH AUTHOR 26 | FUSE was written by Miklos Szeredi <\fImiklos@szeredi.hu\fR>. 27 | .PP 28 | This manual page was written by Daniel Baumann <\fIdaniel.baumann@progress\-technologies.net\fR>. 29 | -------------------------------------------------------------------------------- /lib/fuse_loop.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU LGPLv2. 6 | See the file COPYING.LIB 7 | */ 8 | 9 | #include "fuse_lowlevel.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | int fuse_session_loop(struct fuse_session *se) 16 | { 17 | int res = 0; 18 | struct fuse_chan *ch = fuse_session_next_chan(se, NULL); 19 | size_t bufsize = fuse_chan_bufsize(ch); 20 | char *buf = (char *) malloc(bufsize); 21 | if (!buf) { 22 | fprintf(stderr, "fuse: failed to allocate read buffer\n"); 23 | return -1; 24 | } 25 | 26 | while (!fuse_session_exited(se)) { 27 | struct fuse_chan *tmpch = ch; 28 | struct fuse_buf fbuf = { 29 | .mem = buf, 30 | .size = bufsize, 31 | }; 32 | 33 | res = fuse_session_receive_buf(se, &fbuf, &tmpch); 34 | 35 | if (res == -EINTR) 36 | continue; 37 | if (res <= 0) 38 | break; 39 | 40 | fuse_session_process_buf(se, &fbuf, tmpch); 41 | } 42 | 43 | free(buf); 44 | fuse_session_reset(se); 45 | return res < 0 ? -1 : 0; 46 | } 47 | -------------------------------------------------------------------------------- /doc/fusermount.1: -------------------------------------------------------------------------------- 1 | .TH FUSERMOUNT 1 2011\-10\-23 2.8.6 "Filesystem in Userspace (FUSE)" 2 | 3 | .SH NAME 4 | \fBfusermount\fR \- mount and unmount FUSE filesystems 5 | 6 | .SH SYNOPSIS 7 | \fBfusermount\fR [\fIOPTIONS\fR] \fIMOUNTPOINT\fR 8 | 9 | .SH DESCRIPTION 10 | Filesystem in Userspace (FUSE) is a simple interface for userspace programs to export a virtual filesystem to the Linux kernel. It also aims to provide a secure method for non privileged users to create and mount their own filesystem implementations. 11 | .PP 12 | \fBfusermount\fR is a program to mount and unmount FUSE filesystems. 13 | 14 | .SH OPTIONS 15 | .IP "\-h" 4 16 | print help. 17 | .IP "\-V" 4 18 | print version. 19 | .IP "-o \fIOPTION\fR[,\fIOPTION\fR...]" 4 20 | mount options. 21 | .IP "-u" 4 22 | unmount. 23 | .IP "-q" 4 24 | quiet. 25 | .IP "-z" 4 26 | lazy unmount. 27 | 28 | .SH SEE ALSO 29 | \fImount\fR(8), 30 | \fImount.fuse\fR(8), 31 | \fIulockmgr_server\fR(1). 32 | 33 | .SH HOMEPAGE 34 | More information about fusermount and the FUSE project can be found at <\fIhttp://fuse.sourceforge.net/\fR>. 35 | 36 | .SH AUTHOR 37 | FUSE was written by Miklos Szeredi <\fImiklos@szeredi.hu\fR>. 38 | .PP 39 | This manual page was written by Daniel Baumann <\fIdaniel.baumann@progress\-technologies.net\fR>. 40 | -------------------------------------------------------------------------------- /lib/Makefile.am: -------------------------------------------------------------------------------- 1 | ## Process this file with automake to produce Makefile.in 2 | 3 | AUTOMAKE_OPTIONS = subdir-objects 4 | AM_CPPFLAGS = -I$(top_srcdir)/include -DFUSERMOUNT_DIR=\"$(bindir)\" \ 5 | -D_FILE_OFFSET_BITS=64 -D_REENTRANT -DFUSE_USE_VERSION=26 6 | 7 | lib_LTLIBRARIES = libfuse.la libulockmgr.la 8 | 9 | if BSD 10 | mount_source = mount_bsd.c 11 | else 12 | mount_source = mount.c mount_util.c mount_util.h 13 | endif 14 | 15 | if ICONV 16 | iconv_source = modules/iconv.c 17 | else 18 | iconv_source = 19 | endif 20 | 21 | libfuse_la_SOURCES = \ 22 | fuse.c \ 23 | fuse_i.h \ 24 | fuse_kern_chan.c \ 25 | fuse_loop.c \ 26 | fuse_loop_mt.c \ 27 | fuse_lowlevel.c \ 28 | fuse_misc.h \ 29 | fuse_mt.c \ 30 | fuse_opt.c \ 31 | fuse_session.c \ 32 | fuse_signals.c \ 33 | buffer.c \ 34 | cuse_lowlevel.c \ 35 | helper.c \ 36 | modules/subdir.c \ 37 | $(iconv_source) \ 38 | $(mount_source) 39 | 40 | libfuse_la_LDFLAGS = -pthread @libfuse_libs@ -version-number 2:9:7 \ 41 | -Wl,--version-script,$(srcdir)/fuse_versionscript 42 | 43 | if NETBSD 44 | libfuse_la_LIBADD = -lperfuse -lpuffs 45 | endif 46 | 47 | libulockmgr_la_SOURCES = ulockmgr.c 48 | libulockmgr_la_LDFLAGS = -pthread -version-number 1:0:1 49 | 50 | EXTRA_DIST = fuse_versionscript 51 | -------------------------------------------------------------------------------- /README.NFS: -------------------------------------------------------------------------------- 1 | NFS exporting is supported in Linux kernels 2.6.27 or later. 2 | 3 | You need to add an fsid=NNN option to /etc/exports to make exporting a 4 | FUSE directory work. 5 | 6 | Filesystem support 7 | ------------------ 8 | 9 | NFS exporting works to some extent on all fuse filesystems, but not 10 | perfectly. This is due to the stateless nature of the protocol, the 11 | server has no way of knowing whether the client is keeping a reference 12 | to a file or not, and hence that file may be removed from the server's 13 | cache. In that case there has to be a way to look up that object 14 | using the inode number, otherwise an ESTALE error will be returned. 15 | 16 | 1) low-level interface 17 | 18 | Filesystems need to implement special lookups for the names "." and 19 | "..". The former may be requested on any inode, including 20 | non-directories, while the latter is only requested for directories. 21 | Otherwise these special lookups should behave identically to ordinary 22 | lookups. 23 | 24 | 2) high-level interface 25 | 26 | Because the high-level interface is path based, it is not possible to 27 | delegate looking up by inode to the filesystem. 28 | 29 | To work around this, currently a "noforget" option is provided, which 30 | makes the library remember nodes forever. This will make the NFS 31 | server happy, but also results in an ever growing memory footprint for 32 | the filesystem. For this reason if the filesystem is large (or the 33 | memory is small), then this option is not recommended. 34 | -------------------------------------------------------------------------------- /android/statvfs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Freescale Semiconductor, Inc. 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | * 18 | */ 19 | 20 | #include "sys/statvfs.h" 21 | #include 22 | 23 | #define MAP(to,from) vfs->to = sfs.from 24 | 25 | int statvfs(const char *path, struct statvfs *vfs) { 26 | struct statfs sfs; 27 | int ret; 28 | int *fsid; 29 | if ((ret = statfs(path, &sfs)) != 0) 30 | return ret; 31 | 32 | MAP(f_bsize, f_bsize); 33 | MAP(f_frsize, f_frsize); 34 | MAP(f_blocks, f_blocks); 35 | MAP(f_bfree, f_bfree); 36 | MAP(f_bavail, f_bavail); 37 | MAP(f_files, f_files); 38 | MAP(f_ffree, f_ffree); 39 | MAP(f_namemax, f_namelen); 40 | 41 | vfs->f_favail = 0; 42 | vfs->f_flag = 0; 43 | 44 | fsid = (int *)&sfs.f_fsid; 45 | vfs->f_fsid = (fsid[0] << sizeof(fsid[0])) | fsid[1]; 46 | 47 | return ret; 48 | } 49 | -------------------------------------------------------------------------------- /android/sys/statvfs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Freescale Semiconductor, Inc. 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | * 18 | */ 19 | 20 | #ifndef _SYS_STATVFS_H_ 21 | #define _SYS_STATVFS_H_ 22 | #include 23 | 24 | struct statvfs { 25 | unsigned long f_bsize; /* file system block size */ 26 | unsigned long f_frsize; /* fragment size */ 27 | fsblkcnt_t f_blocks; /* size of fs in f_frsize units */ 28 | fsblkcnt_t f_bfree; /* # free blocks */ 29 | fsblkcnt_t f_bavail; /* # free blocks for non-root */ 30 | fsfilcnt_t f_files; /* # inodes */ 31 | fsfilcnt_t f_ffree; /* # free inodes */ 32 | fsfilcnt_t f_favail; /* # free inodes for non-root */ 33 | unsigned long f_fsid; /* file system ID */ 34 | unsigned long f_flag; /* mount flags */ 35 | unsigned long f_namemax; /* maximum filename length */ 36 | }; 37 | 38 | int statvfs(const char *, struct statvfs *); 39 | #endif 40 | -------------------------------------------------------------------------------- /example/fselclient.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE fselclient: FUSE select example client 3 | Copyright (C) 2008 SUSE Linux Products GmbH 4 | Copyright (C) 2008 Tejun Heo 5 | 6 | This program can be distributed under the terms of the GNU GPL. 7 | See the file COPYING. 8 | 9 | gcc -Wall fselclient.c -o fselclient 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #define FSEL_FILES 16 24 | 25 | int main(void) 26 | { 27 | static const char hex_map[FSEL_FILES] = "0123456789ABCDEF"; 28 | int fds[FSEL_FILES]; 29 | int i, nfds; 30 | 31 | for (i = 0; i < FSEL_FILES; i++) { 32 | char name[] = { hex_map[i], '\0' }; 33 | fds[i] = open(name, O_RDONLY); 34 | if (fds[i] < 0) { 35 | perror("open"); 36 | return 1; 37 | } 38 | } 39 | nfds = fds[FSEL_FILES - 1] + 1; 40 | 41 | while (1) { 42 | static char buf[4096]; 43 | fd_set rfds; 44 | int rc; 45 | 46 | FD_ZERO(&rfds); 47 | for (i = 0; i < FSEL_FILES; i++) 48 | FD_SET(fds[i], &rfds); 49 | 50 | rc = select(nfds, &rfds, NULL, NULL, NULL); 51 | 52 | if (rc < 0) { 53 | perror("select"); 54 | return 1; 55 | } 56 | 57 | for (i = 0; i < FSEL_FILES; i++) { 58 | if (!FD_ISSET(fds[i], &rfds)) { 59 | printf("_: "); 60 | continue; 61 | } 62 | printf("%X:", i); 63 | rc = read(fds[i], buf, sizeof(buf)); 64 | if (rc < 0) { 65 | perror("read"); 66 | return 1; 67 | } 68 | printf("%02d ", rc); 69 | } 70 | printf("\n"); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/fuse_signals.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU LGPLv2. 6 | See the file COPYING.LIB 7 | */ 8 | 9 | #include "fuse_lowlevel.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | static struct fuse_session *fuse_instance; 16 | 17 | static void exit_handler(int sig) 18 | { 19 | (void) sig; 20 | if (fuse_instance) 21 | fuse_session_exit(fuse_instance); 22 | } 23 | 24 | static int set_one_signal_handler(int sig, void (*handler)(int), int remove) 25 | { 26 | struct sigaction sa; 27 | struct sigaction old_sa; 28 | 29 | memset(&sa, 0, sizeof(struct sigaction)); 30 | sa.sa_handler = remove ? SIG_DFL : handler; 31 | sigemptyset(&(sa.sa_mask)); 32 | sa.sa_flags = 0; 33 | 34 | if (sigaction(sig, NULL, &old_sa) == -1) { 35 | perror("fuse: cannot get old signal handler"); 36 | return -1; 37 | } 38 | 39 | if (old_sa.sa_handler == (remove ? handler : SIG_DFL) && 40 | sigaction(sig, &sa, NULL) == -1) { 41 | perror("fuse: cannot set signal handler"); 42 | return -1; 43 | } 44 | return 0; 45 | } 46 | 47 | int fuse_set_signal_handlers(struct fuse_session *se) 48 | { 49 | if (set_one_signal_handler(SIGHUP, exit_handler, 0) == -1 || 50 | set_one_signal_handler(SIGINT, exit_handler, 0) == -1 || 51 | set_one_signal_handler(SIGTERM, exit_handler, 0) == -1 || 52 | set_one_signal_handler(SIGPIPE, SIG_IGN, 0) == -1) 53 | return -1; 54 | 55 | fuse_instance = se; 56 | return 0; 57 | } 58 | 59 | void fuse_remove_signal_handlers(struct fuse_session *se) 60 | { 61 | if (fuse_instance != se) 62 | fprintf(stderr, 63 | "fuse: fuse_remove_signal_handlers: unknown session\n"); 64 | else 65 | fuse_instance = NULL; 66 | 67 | set_one_signal_handler(SIGHUP, exit_handler, 1); 68 | set_one_signal_handler(SIGINT, exit_handler, 1); 69 | set_one_signal_handler(SIGTERM, exit_handler, 1); 70 | set_one_signal_handler(SIGPIPE, SIG_IGN, 1); 71 | } 72 | 73 | -------------------------------------------------------------------------------- /lib/fuse_misc.h: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU LGPLv2. 6 | See the file COPYING.LIB 7 | */ 8 | 9 | #include "config.h" 10 | #include 11 | 12 | /* 13 | Versioned symbols cannot be used in some cases because it 14 | - confuse the dynamic linker in uClibc 15 | - not supported on MacOSX (in MachO binary format) 16 | */ 17 | #if (!defined(__UCLIBC__) && !defined(__APPLE__)) 18 | #define FUSE_SYMVER(x) __asm__(x) 19 | #else 20 | #define FUSE_SYMVER(x) 21 | #endif 22 | 23 | #ifndef USE_UCLIBC 24 | #define fuse_mutex_init(mut) pthread_mutex_init(mut, NULL) 25 | #else 26 | /* Is this hack still needed? */ 27 | static inline void fuse_mutex_init(pthread_mutex_t *mut) 28 | { 29 | pthread_mutexattr_t attr; 30 | pthread_mutexattr_init(&attr); 31 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); 32 | pthread_mutex_init(mut, &attr); 33 | pthread_mutexattr_destroy(&attr); 34 | } 35 | #endif 36 | 37 | #ifdef HAVE_STRUCT_STAT_ST_ATIM 38 | /* Linux */ 39 | #define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec) 40 | #define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec) 41 | #define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec) 42 | #define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atim.tv_nsec = (val) 43 | #define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtim.tv_nsec = (val) 44 | #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC) 45 | /* FreeBSD */ 46 | #define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec) 47 | #define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec) 48 | #define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec) 49 | #define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimespec.tv_nsec = (val) 50 | #define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimespec.tv_nsec = (val) 51 | #else 52 | #define ST_ATIM_NSEC(stbuf) 0 53 | #define ST_CTIM_NSEC(stbuf) 0 54 | #define ST_MTIM_NSEC(stbuf) 0 55 | #define ST_ATIM_NSEC_SET(stbuf, val) do { } while (0) 56 | #define ST_MTIM_NSEC_SET(stbuf, val) do { } while (0) 57 | #endif 58 | -------------------------------------------------------------------------------- /lib/Android.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2009 The Android Open Source Project 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | LOCAL_PATH := $(call my-dir) 16 | 17 | common_src_files := \ 18 | buffer.c \ 19 | cuse_lowlevel.c \ 20 | fuse.c \ 21 | fuse_kern_chan.c \ 22 | fuse_loop.c \ 23 | fuse_loop_mt.c \ 24 | fuse_lowlevel.c \ 25 | fuse_mt.c fuse_opt.c \ 26 | fuse_session.c \ 27 | fuse_signals.c \ 28 | helper.c \ 29 | mount.c \ 30 | mount_util.c \ 31 | ulockmgr.c 32 | 33 | common_c_includes := \ 34 | external/fuse/android \ 35 | external/fuse/include 36 | 37 | common_shared_libraries := \ 38 | libutils 39 | 40 | common_cflags := \ 41 | -D_FILE_OFFSET_BITS=64 \ 42 | -DFUSE_USE_VERSION=26 \ 43 | -fno-strict-aliasing 44 | 45 | common_ldflags := \ 46 | -Wl,--version-script,$(LOCAL_PATH)/fuse_versionscript 47 | 48 | include $(CLEAR_VARS) 49 | LOCAL_MODULE := libfuse 50 | LOCAL_MODULE_TAGS := optional 51 | LOCAL_SRC_FILES := $(common_src_files) 52 | LOCAL_C_INCLUDES := $(common_c_includes) 53 | LOCAL_SHARED_LIBRARIES := $(common_shared_libraries) 54 | LOCAL_CFLAGS := $(common_cflags) -fPIC 55 | LOCAL_LDFLAGS := $(common_ldflags) 56 | LOCAL_CLANG := true 57 | include $(BUILD_SHARED_LIBRARY) 58 | 59 | include $(CLEAR_VARS) 60 | LOCAL_MODULE := libfuse_static 61 | LOCAL_MODULE_TAGS := optional 62 | LOCAL_SRC_FILES := $(common_src_files) 63 | LOCAL_C_INCLUDES := $(common_c_includes) 64 | LOCAL_STATIC_LIBRARIES := $(common_shared_libraries) 65 | LOCAL_CFLAGS := $(common_cflags) 66 | LOCAL_CLANG := true 67 | include $(BUILD_STATIC_LIBRARY) 68 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Current Maintainer 2 | ------------------ 3 | 4 | Nikolaus Rath 5 | 6 | 7 | Past Maintainers 8 | ---------------- 9 | 10 | Miklos Szeredi (until 12/2015) 11 | 12 | 13 | Contributors 14 | ------------ 15 | 16 | CUSE has been written by Tejun Heo . Furthermore, the 17 | following people have contributed patches (autogenerated list): 18 | 19 | Anatol Pomozov 20 | Antonio SJ Musumeci 21 | Christopher Harrison 22 | Csaba Henk 23 | cvs2git <> 24 | Dalvik Khertel 25 | Daniel Thau 26 | David McNab 27 | David Sheets 28 | Emmanuel Dreyfus 29 | Enke Chen 30 | Eric Engestrom 31 | Eric Wong 32 | Fabrice Bauzac 33 | Feng Shuo 34 | Hendrik Brueckner 35 | Ikey Doherty 36 | Jan Blumschein 37 | Joachim Schiele 38 | Joachim Schiele 39 | John Muir 40 | Laszlo Papp 41 | Madan Valluri 42 | Mark Glines 43 | Max Krasnyansky 44 | Michael Grigoriev 45 | Miklos Szeredi 46 | Miklos Szeredi 47 | mkmm@gmx-topmail.de 48 | Natanael Copa 49 | Nikolaus Rath 50 | Olivier Blin 51 | Ratna_Bolla@dell.com 52 | Reuben Hawkins 53 | Richard W.M. Jones 54 | Riku Voipio 55 | Roland Bauerschmidt 56 | Sam Stuewe 57 | Sebastian Pipping 58 | therealneworld@gmail.com 59 | Winfried Koehler 60 | -------------------------------------------------------------------------------- /example/null.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU GPL. 6 | See the file COPYING. 7 | 8 | gcc -Wall null.c `pkg-config fuse --cflags --libs` -o null 9 | */ 10 | 11 | #define FUSE_USE_VERSION 26 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | static int null_getattr(const char *path, struct stat *stbuf) 20 | { 21 | if(strcmp(path, "/") != 0) 22 | return -ENOENT; 23 | 24 | stbuf->st_mode = S_IFREG | 0644; 25 | stbuf->st_nlink = 1; 26 | stbuf->st_uid = getuid(); 27 | stbuf->st_gid = getgid(); 28 | stbuf->st_size = (1ULL << 32); /* 4G */ 29 | stbuf->st_blocks = 0; 30 | stbuf->st_atime = stbuf->st_mtime = stbuf->st_ctime = time(NULL); 31 | 32 | return 0; 33 | } 34 | 35 | static int null_truncate(const char *path, off_t size) 36 | { 37 | (void) size; 38 | 39 | if(strcmp(path, "/") != 0) 40 | return -ENOENT; 41 | 42 | return 0; 43 | } 44 | 45 | static int null_open(const char *path, struct fuse_file_info *fi) 46 | { 47 | (void) fi; 48 | 49 | if(strcmp(path, "/") != 0) 50 | return -ENOENT; 51 | 52 | return 0; 53 | } 54 | 55 | static int null_read(const char *path, char *buf, size_t size, 56 | off_t offset, struct fuse_file_info *fi) 57 | { 58 | (void) buf; 59 | (void) offset; 60 | (void) fi; 61 | 62 | if(strcmp(path, "/") != 0) 63 | return -ENOENT; 64 | 65 | if (offset >= (1ULL << 32)) 66 | return 0; 67 | 68 | return size; 69 | } 70 | 71 | static int null_write(const char *path, const char *buf, size_t size, 72 | off_t offset, struct fuse_file_info *fi) 73 | { 74 | (void) buf; 75 | (void) offset; 76 | (void) fi; 77 | 78 | if(strcmp(path, "/") != 0) 79 | return -ENOENT; 80 | 81 | return size; 82 | } 83 | 84 | static struct fuse_operations null_oper = { 85 | .getattr = null_getattr, 86 | .truncate = null_truncate, 87 | .open = null_open, 88 | .read = null_read, 89 | .write = null_write, 90 | }; 91 | 92 | int main(int argc, char *argv[]) 93 | { 94 | return fuse_main(argc, argv, &null_oper, NULL); 95 | } 96 | -------------------------------------------------------------------------------- /util/init_script: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: fuse 4 | # Required-Start: 5 | # Should-Start: udev 6 | # Required-Stop: 7 | # Default-Start: S 8 | # Default-Stop: 9 | # Short-Description: Start and stop fuse. 10 | # Description: Load the fuse module and mount the fuse control 11 | # filesystem. 12 | ### END INIT INFO 13 | 14 | set -e 15 | 16 | PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 17 | MOUNTPOINT=/sys/fs/fuse/connections 18 | 19 | # Gracefully exit if the package has been removed. 20 | which fusermount &>/dev/null || exit 5 21 | 22 | case "$1" in 23 | start|restart|force-reload) 24 | if ! grep -qw fuse /proc/filesystems; then 25 | echo -n "Loading fuse module" 26 | if ! modprobe fuse >/dev/null 2>&1; then 27 | echo " failed!" 28 | exit 1 29 | else 30 | echo "." 31 | fi 32 | else 33 | echo "Fuse filesystem already available." 34 | fi 35 | if grep -qw fusectl /proc/filesystems && \ 36 | ! grep -qw $MOUNTPOINT /proc/mounts; then 37 | echo -n "Mounting fuse control filesystem" 38 | if ! mount -t fusectl fusectl $MOUNTPOINT >/dev/null 2>&1; then 39 | echo " failed!" 40 | exit 1 41 | else 42 | echo "." 43 | fi 44 | else 45 | echo "Fuse control filesystem already available." 46 | fi 47 | ;; 48 | stop) 49 | if ! grep -qw fuse /proc/filesystems; then 50 | echo "Fuse filesystem not loaded." 51 | exit 7 52 | fi 53 | if grep -qw $MOUNTPOINT /proc/mounts; then 54 | echo -n "Unmounting fuse control filesystem" 55 | if ! umount $MOUNTPOINT >/dev/null 2>&1; then 56 | echo " failed!" 57 | else 58 | echo "." 59 | fi 60 | else 61 | echo "Fuse control filesystem not mounted." 62 | fi 63 | if grep -qw "^fuse" /proc/modules; then 64 | echo -n "Unloading fuse module" 65 | if ! rmmod fuse >/dev/null 2>&1; then 66 | echo " failed!" 67 | else 68 | echo "." 69 | fi 70 | else 71 | echo "Fuse module not loaded." 72 | fi 73 | ;; 74 | status) 75 | echo -n "Checking fuse filesystem" 76 | if ! grep -qw fuse /proc/filesystems; then 77 | echo " not available." 78 | exit 3 79 | else 80 | echo " ok." 81 | fi 82 | ;; 83 | *) 84 | echo "Usage: $0 {start|stop|restart|force-reload|status}" 85 | exit 1 86 | ;; 87 | esac 88 | 89 | exit 0 90 | -------------------------------------------------------------------------------- /util/Makefile.am: -------------------------------------------------------------------------------- 1 | ## Process this file with automake to produce Makefile.in 2 | 3 | AM_CPPFLAGS = -D_FILE_OFFSET_BITS=64 4 | bin_PROGRAMS = fusermount ulockmgr_server 5 | noinst_PROGRAMS = mount.fuse 6 | 7 | # we re-use mount_util.c from the library, but do want to keep ourself 8 | # as stand-alone as possible. in order to make an out-of-source build 9 | # possible, we "generate" the file from its original location by 10 | # copying it over. 11 | fusermount_SOURCES = fusermount.c mount_util.c 12 | fusermount_CPPFLAGS = -I$(top_srcdir)/lib 13 | BUILT_SOURCES = mount_util.c 14 | mount_util.c: $(top_srcdir)/lib/mount_util.c 15 | @cp $(top_srcdir)/lib/mount_util.c . 16 | 17 | mount_fuse_SOURCES = mount.fuse.c 18 | 19 | ulockmgr_server_SOURCES = ulockmgr_server.c 20 | ulockmgr_server_CPPFLAGS = -D_FILE_OFFSET_BITS=64 -D_REENTRANT 21 | ulockmgr_server_LDFLAGS = -pthread 22 | 23 | install-exec-hook: 24 | -chmod u+s $(DESTDIR)$(bindir)/fusermount 25 | @if test ! -e $(DESTDIR)/dev/fuse; then \ 26 | $(MKDIR_P) $(DESTDIR)/dev; \ 27 | echo "mknod $(DESTDIR)/dev/fuse -m 0666 c 10 229 || true"; \ 28 | mknod $(DESTDIR)/dev/fuse -m 0666 c 10 229 || true; \ 29 | fi 30 | 31 | EXTRA_DIST = udev.rules init_script 32 | 33 | MOUNT_FUSE_PATH = @MOUNT_FUSE_PATH@ 34 | UDEV_RULES_PATH = @UDEV_RULES_PATH@ 35 | INIT_D_PATH = @INIT_D_PATH@ 36 | 37 | install-exec-local: 38 | $(MKDIR_P) $(DESTDIR)$(MOUNT_FUSE_PATH) 39 | $(INSTALL_PROGRAM) $(builddir)/mount.fuse $(DESTDIR)$(MOUNT_FUSE_PATH)/mount.fuse 40 | $(MKDIR_P) $(DESTDIR)$(INIT_D_PATH) 41 | $(INSTALL_SCRIPT) $(srcdir)/init_script $(DESTDIR)$(INIT_D_PATH)/fuse 42 | @if test -x /usr/sbin/update-rc.d; then \ 43 | echo "/usr/sbin/update-rc.d fuse start 34 S . start 41 0 6 . || true"; \ 44 | /usr/sbin/update-rc.d fuse start 34 S . start 41 0 6 . || true; \ 45 | fi 46 | 47 | install-data-local: 48 | $(MKDIR_P) $(DESTDIR)$(UDEV_RULES_PATH) 49 | $(INSTALL_DATA) $(srcdir)/udev.rules $(DESTDIR)$(UDEV_RULES_PATH)/99-fuse.rules 50 | 51 | uninstall-local: 52 | rm -f $(DESTDIR)$(MOUNT_FUSE_PATH)/mount.fuse 53 | rm -f $(DESTDIR)$(UDEV_RULES_PATH)/99-fuse.rules 54 | rm -f $(DESTDIR)$(INIT_D_PATH)/fuse 55 | @if test -x /usr/sbin/update-rc.d; then \ 56 | echo "/usr/sbin/update-rc.d fuse remove || true"; \ 57 | /usr/sbin/update-rc.d fuse remove || true; \ 58 | fi 59 | -------------------------------------------------------------------------------- /example/hello.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU GPL. 6 | See the file COPYING. 7 | 8 | gcc -Wall hello.c `pkg-config fuse --cflags --libs` -o hello 9 | */ 10 | 11 | #define FUSE_USE_VERSION 26 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | static const char *hello_str = "Hello World!\n"; 20 | static const char *hello_path = "/hello"; 21 | 22 | static int hello_getattr(const char *path, struct stat *stbuf) 23 | { 24 | int res = 0; 25 | 26 | memset(stbuf, 0, sizeof(struct stat)); 27 | if (strcmp(path, "/") == 0) { 28 | stbuf->st_mode = S_IFDIR | 0755; 29 | stbuf->st_nlink = 2; 30 | } else if (strcmp(path, hello_path) == 0) { 31 | stbuf->st_mode = S_IFREG | 0444; 32 | stbuf->st_nlink = 1; 33 | stbuf->st_size = strlen(hello_str); 34 | } else 35 | res = -ENOENT; 36 | 37 | return res; 38 | } 39 | 40 | static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler, 41 | off_t offset, struct fuse_file_info *fi) 42 | { 43 | (void) offset; 44 | (void) fi; 45 | 46 | if (strcmp(path, "/") != 0) 47 | return -ENOENT; 48 | 49 | filler(buf, ".", NULL, 0); 50 | filler(buf, "..", NULL, 0); 51 | filler(buf, hello_path + 1, NULL, 0); 52 | 53 | return 0; 54 | } 55 | 56 | static int hello_open(const char *path, struct fuse_file_info *fi) 57 | { 58 | if (strcmp(path, hello_path) != 0) 59 | return -ENOENT; 60 | 61 | if ((fi->flags & 3) != O_RDONLY) 62 | return -EACCES; 63 | 64 | return 0; 65 | } 66 | 67 | static int hello_read(const char *path, char *buf, size_t size, off_t offset, 68 | struct fuse_file_info *fi) 69 | { 70 | size_t len; 71 | (void) fi; 72 | if(strcmp(path, hello_path) != 0) 73 | return -ENOENT; 74 | 75 | len = strlen(hello_str); 76 | if (offset < len) { 77 | if (offset + size > len) 78 | size = len - offset; 79 | memcpy(buf, hello_str + offset, size); 80 | } else 81 | size = 0; 82 | 83 | return size; 84 | } 85 | 86 | static struct fuse_operations hello_oper = { 87 | .getattr = hello_getattr, 88 | .readdir = hello_readdir, 89 | .open = hello_open, 90 | .read = hello_read, 91 | }; 92 | 93 | int main(int argc, char *argv[]) 94 | { 95 | return fuse_main(argc, argv, &hello_oper, NULL); 96 | } 97 | -------------------------------------------------------------------------------- /doc/how-fuse-works: -------------------------------------------------------------------------------- 1 | How Fuse-1.3 Works 2 | 3 | [Written by Terje Oseberg] 4 | 5 | 1. The fuse library. 6 | 7 | When your user mode program calls fuse_main() (lib/helper.c), 8 | fuse_main() parses the arguments passed to your user mode program, 9 | then calls fuse_mount() (lib/mount.c). 10 | 11 | fuse_mount() creates a UNIX domain socket pair, then forks and execs 12 | fusermount (util/fusermount.c) passing it one end of the socket in the 13 | FUSE_COMMFD_ENV environment variable. 14 | 15 | fusermount (util/fusermount.c) makes sure that the fuse module is 16 | loaded. fusermount then open /dev/fuse and send the file handle over a 17 | UNIX domain socket back to fuse_mount(). 18 | 19 | fuse_mount() returns the filehandle for /dev/fuse to fuse_main(). 20 | 21 | fuse_main() calls fuse_new() (lib/fuse.c) which allocates the struct 22 | fuse datastructure that stores and maintains a cached image of the 23 | filesystem data. 24 | 25 | Lastly, fuse_main() calls either fuse_loop() (lib/fuse.c) or 26 | fuse_loop_mt() (lib/fuse_mt.c) which both start to read the filesystem 27 | system calls from the /dev/fuse, call the usermode functions 28 | stored in struct fuse_operations datastructure before calling 29 | fuse_main(). The results of those calls are then written back to the 30 | /dev/fuse file where they can be forwarded back to the system 31 | calls. 32 | 33 | 2. The kernel module. 34 | 35 | The kernel module consists of two parts. First the proc filesystem 36 | component in kernel/dev.c -and second the filesystem system calls 37 | kernel/file.c, kernel/inode.c, and kernel/dir.c 38 | 39 | All the system calls in kernel/file.c, kernel/inode.c, and 40 | kernel/dir.c make calls to either request_send(), 41 | request_send_noreply(), or request_send_nonblock(). Most of the calls 42 | (all but 2) are to request_send(). request_send() adds the request to, 43 | "list of requests" structure (fc->pending), then waits for a response. 44 | request_send_noreply() and request_send_nonblock() are both similar in 45 | function to request_send() except that one is non-blocking, and the 46 | other does not respond with a reply. 47 | 48 | The proc filesystem component in kernel/dev.c responds to file io 49 | requests to the file /dev/fuse. fuse_dev_read() handles the 50 | file reads and returns commands from the "list of requests" structure 51 | to the calling program. fuse_dev_write() handles file writes and takes 52 | the data written and places them into the req->out datastructure where 53 | they can be returned to the system call through the "list of requests" 54 | structure and request_send(). 55 | -------------------------------------------------------------------------------- /lib/fuse_kern_chan.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU LGPLv2. 6 | See the file COPYING.LIB 7 | */ 8 | 9 | #include "fuse_lowlevel.h" 10 | #include "fuse_kernel.h" 11 | #include "fuse_i.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | static int fuse_kern_chan_receive(struct fuse_chan **chp, char *buf, 19 | size_t size) 20 | { 21 | struct fuse_chan *ch = *chp; 22 | int err; 23 | ssize_t res; 24 | struct fuse_session *se = fuse_chan_session(ch); 25 | assert(se != NULL); 26 | 27 | restart: 28 | res = read(fuse_chan_fd(ch), buf, size); 29 | err = errno; 30 | 31 | if (fuse_session_exited(se)) 32 | return 0; 33 | if (res == -1) { 34 | /* ENOENT means the operation was interrupted, it's safe 35 | to restart */ 36 | if (err == ENOENT) 37 | goto restart; 38 | 39 | if (err == ENODEV) { 40 | fuse_session_exit(se); 41 | return 0; 42 | } 43 | /* Errors occurring during normal operation: EINTR (read 44 | interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem 45 | umounted) */ 46 | if (err != EINTR && err != EAGAIN) 47 | perror("fuse: reading device"); 48 | return -err; 49 | } 50 | if ((size_t) res < sizeof(struct fuse_in_header)) { 51 | fprintf(stderr, "short read on fuse device\n"); 52 | return -EIO; 53 | } 54 | return res; 55 | } 56 | 57 | static int fuse_kern_chan_send(struct fuse_chan *ch, const struct iovec iov[], 58 | size_t count) 59 | { 60 | if (iov) { 61 | ssize_t res = writev(fuse_chan_fd(ch), iov, count); 62 | int err = errno; 63 | 64 | if (res == -1) { 65 | struct fuse_session *se = fuse_chan_session(ch); 66 | 67 | assert(se != NULL); 68 | 69 | /* ENOENT means the operation was interrupted */ 70 | if (!fuse_session_exited(se) && err != ENOENT) 71 | perror("fuse: writing device"); 72 | return -err; 73 | } 74 | } 75 | return 0; 76 | } 77 | 78 | static void fuse_kern_chan_destroy(struct fuse_chan *ch) 79 | { 80 | int fd = fuse_chan_fd(ch); 81 | 82 | if (fd != -1) 83 | close(fd); 84 | } 85 | 86 | #define MIN_BUFSIZE 0x21000 87 | 88 | struct fuse_chan *fuse_kern_chan_new(int fd) 89 | { 90 | struct fuse_chan_ops op = { 91 | .receive = fuse_kern_chan_receive, 92 | .send = fuse_kern_chan_send, 93 | .destroy = fuse_kern_chan_destroy, 94 | }; 95 | size_t bufsize = getpagesize() + 0x1000; 96 | bufsize = bufsize < MIN_BUFSIZE ? MIN_BUFSIZE : bufsize; 97 | return fuse_chan_new(&op, fd, bufsize, NULL); 98 | } 99 | -------------------------------------------------------------------------------- /android/config.h: -------------------------------------------------------------------------------- 1 | /* include/config.h. Generated from config.h.in by configure. */ 2 | /* include/config.h.in. Generated from configure.in by autoheader. */ 3 | 4 | /* Define to 1 if you have the header file. */ 5 | #define HAVE_DLFCN_H 1 6 | 7 | /* Define to 1 if you have the `fdatasync' function. */ 8 | #define HAVE_FDATASYNC 1 9 | 10 | /* Define to 1 if you have the `fork' function. */ 11 | #define HAVE_FORK 1 12 | 13 | /* Define if you have the iconv() function and it works. */ 14 | #define HAVE_ICONV 1 15 | 16 | /* Define to 1 if you have the header file. */ 17 | #define HAVE_INTTYPES_H 1 18 | 19 | /* Define to 1 if you have the header file. */ 20 | #define HAVE_MEMORY_H 1 21 | 22 | /* Define to 1 if you have the `setxattr' function. */ 23 | #define HAVE_SETXATTR 1 24 | 25 | /* Define to 1 if you have the header file. */ 26 | #define HAVE_STDINT_H 1 27 | 28 | /* Define to 1 if you have the header file. */ 29 | #define HAVE_STDLIB_H 1 30 | 31 | /* Define to 1 if you have the header file. */ 32 | #define HAVE_STRINGS_H 1 33 | 34 | /* Define to 1 if you have the header file. */ 35 | #define HAVE_STRING_H 1 36 | 37 | /* Define to 1 if `st_atim' is member of `struct stat'. */ 38 | //#define HAVE_STRUCT_STAT_ST_ATIM 0 39 | 40 | /* Define to 1 if `st_atimespec' is member of `struct stat'. */ 41 | /* #undef HAVE_STRUCT_STAT_ST_ATIMESPEC */ 42 | 43 | /* Define to 1 if you have the header file. */ 44 | #define HAVE_SYS_STAT_H 1 45 | 46 | /* Define to 1 if you have the header file. */ 47 | #define HAVE_SYS_TYPES_H 1 48 | 49 | /* Define to 1 if you have the header file. */ 50 | #define HAVE_UNISTD_H 1 51 | 52 | /* Define as const if the declaration of iconv() needs const. */ 53 | #define ICONV_CONST 54 | 55 | /* Don't update /etc/mtab */ 56 | /* #undef IGNORE_MTAB */ 57 | 58 | /* Define to the sub-directory in which libtool stores uninstalled libraries. 59 | */ 60 | #define LT_OBJDIR ".libs/" 61 | 62 | /* Define to 1 if your C compiler doesn't accept -c and -o together. */ 63 | /* #undef NO_MINUS_C_MINUS_O */ 64 | 65 | /* Name of package */ 66 | #define PACKAGE "fuse" 67 | 68 | /* Define to the address where bug reports for this package should be sent. */ 69 | #define PACKAGE_BUGREPORT "" 70 | 71 | /* Define to the full name of this package. */ 72 | #define PACKAGE_NAME "fuse" 73 | 74 | /* Define to the full name and version of this package. */ 75 | #define PACKAGE_STRING "fuse 2.9.3" 76 | 77 | /* Define to the one symbol short name of this package. */ 78 | #define PACKAGE_TARNAME "fuse" 79 | 80 | /* Define to the version of this package. */ 81 | #define PACKAGE_VERSION "2.9.3" 82 | 83 | /* Define to 1 if you have the ANSI C header files. */ 84 | #define STDC_HEADERS 1 85 | 86 | /* Version number of package */ 87 | #define VERSION "2.9.3" 88 | -------------------------------------------------------------------------------- /include/cuse_lowlevel.h: -------------------------------------------------------------------------------- 1 | /* 2 | CUSE: Character device in Userspace 3 | Copyright (C) 2008-2009 SUSE Linux Products GmbH 4 | Copyright (C) 2008-2009 Tejun Heo 5 | 6 | This program can be distributed under the terms of the GNU LGPLv2. 7 | See the file COPYING.LIB. 8 | 9 | Read example/cusexmp.c for usages. 10 | */ 11 | 12 | #ifndef _CUSE_LOWLEVEL_H_ 13 | #define _CUSE_LOWLEVEL_H_ 14 | 15 | #ifndef FUSE_USE_VERSION 16 | #define FUSE_USE_VERSION 29 17 | #endif 18 | 19 | #include "fuse_lowlevel.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #define CUSE_UNRESTRICTED_IOCTL (1 << 0) /* use unrestricted ioctl */ 30 | 31 | struct fuse_session; 32 | 33 | struct cuse_info { 34 | unsigned dev_major; 35 | unsigned dev_minor; 36 | unsigned dev_info_argc; 37 | const char **dev_info_argv; 38 | unsigned flags; 39 | }; 40 | 41 | /* 42 | * Most ops behave almost identically to the matching fuse_lowlevel 43 | * ops except that they don't take @ino. 44 | * 45 | * init_done : called after initialization is complete 46 | * read/write : always direct IO, simultaneous operations allowed 47 | * ioctl : might be in unrestricted mode depending on ci->flags 48 | */ 49 | struct cuse_lowlevel_ops { 50 | void (*init) (void *userdata, struct fuse_conn_info *conn); 51 | void (*init_done) (void *userdata); 52 | void (*destroy) (void *userdata); 53 | void (*open) (fuse_req_t req, struct fuse_file_info *fi); 54 | void (*read) (fuse_req_t req, size_t size, loff_t off, 55 | struct fuse_file_info *fi); 56 | void (*write) (fuse_req_t req, const char *buf, size_t size, loff_t off, 57 | struct fuse_file_info *fi); 58 | void (*flush) (fuse_req_t req, struct fuse_file_info *fi); 59 | void (*release) (fuse_req_t req, struct fuse_file_info *fi); 60 | void (*fsync) (fuse_req_t req, int datasync, struct fuse_file_info *fi); 61 | void (*ioctl) (fuse_req_t req, int cmd, void *arg, 62 | struct fuse_file_info *fi, unsigned int flags, 63 | const void *in_buf, size_t in_bufsz, size_t out_bufsz); 64 | void (*poll) (fuse_req_t req, struct fuse_file_info *fi, 65 | struct fuse_pollhandle *ph); 66 | }; 67 | 68 | struct fuse_session *cuse_lowlevel_new(struct fuse_args *args, 69 | const struct cuse_info *ci, 70 | const struct cuse_lowlevel_ops *clop, 71 | void *userdata); 72 | 73 | struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[], 74 | const struct cuse_info *ci, 75 | const struct cuse_lowlevel_ops *clop, 76 | int *multithreaded, void *userdata); 77 | 78 | void cuse_lowlevel_teardown(struct fuse_session *se); 79 | 80 | int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci, 81 | const struct cuse_lowlevel_ops *clop, void *userdata); 82 | 83 | #ifdef __cplusplus 84 | } 85 | #endif 86 | 87 | #endif /* _CUSE_LOWLEVEL_H_ */ 88 | -------------------------------------------------------------------------------- /example/fioclient.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE fioclient: FUSE ioctl example client 3 | Copyright (C) 2008 SUSE Linux Products GmbH 4 | Copyright (C) 2008 Tejun Heo 5 | 6 | This program can be distributed under the terms of the GNU GPL. 7 | See the file COPYING. 8 | 9 | gcc -Wall fioclient.c -o fioclient 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "fioc.h" 21 | 22 | const char *usage = 23 | "Usage: fioclient FIOC_FILE COMMAND\n" 24 | "\n" 25 | "COMMANDS\n" 26 | " s [SIZE] : get size if SIZE is omitted, set size otherwise\n" 27 | " r SIZE [OFF] : read SIZE bytes @ OFF (dfl 0) and output to stdout\n" 28 | " w SIZE [OFF] : write SIZE bytes @ OFF (dfl 0) from stdin\n" 29 | "\n"; 30 | 31 | static int do_rw(int fd, int is_read, size_t size, off_t offset, 32 | size_t *prev_size, size_t *new_size) 33 | { 34 | struct fioc_rw_arg arg = { .offset = offset }; 35 | ssize_t ret; 36 | 37 | arg.buf = calloc(1, size); 38 | if (!arg.buf) { 39 | fprintf(stderr, "failed to allocated %zu bytes\n", size); 40 | return -1; 41 | } 42 | 43 | if (is_read) { 44 | arg.size = size; 45 | ret = ioctl(fd, FIOC_READ, &arg); 46 | if (ret >= 0) 47 | fwrite(arg.buf, 1, ret, stdout); 48 | } else { 49 | arg.size = fread(arg.buf, 1, size, stdin); 50 | fprintf(stderr, "Writing %zu bytes\n", arg.size); 51 | ret = ioctl(fd, FIOC_WRITE, &arg); 52 | } 53 | 54 | if (ret >= 0) { 55 | *prev_size = arg.prev_size; 56 | *new_size = arg.new_size; 57 | } else 58 | perror("ioctl"); 59 | 60 | free(arg.buf); 61 | return ret; 62 | } 63 | 64 | int main(int argc, char **argv) 65 | { 66 | size_t param[2] = { }; 67 | size_t size, prev_size = 0, new_size = 0; 68 | char cmd; 69 | int fd, i, rc; 70 | 71 | if (argc < 3) 72 | goto usage; 73 | 74 | fd = open(argv[1], O_RDWR); 75 | if (fd < 0) { 76 | perror("open"); 77 | return 1; 78 | } 79 | 80 | cmd = tolower(argv[2][0]); 81 | argc -= 3; 82 | argv += 3; 83 | 84 | for (i = 0; i < argc; i++) { 85 | char *endp; 86 | param[i] = strtoul(argv[i], &endp, 0); 87 | if (endp == argv[i] || *endp != '\0') 88 | goto usage; 89 | } 90 | 91 | switch (cmd) { 92 | case 's': 93 | if (!argc) { 94 | if (ioctl(fd, FIOC_GET_SIZE, &size)) { 95 | perror("ioctl"); 96 | return 1; 97 | } 98 | printf("%zu\n", size); 99 | } else { 100 | size = param[0]; 101 | if (ioctl(fd, FIOC_SET_SIZE, &size)) { 102 | perror("ioctl"); 103 | return 1; 104 | } 105 | } 106 | return 0; 107 | 108 | case 'r': 109 | case 'w': 110 | rc = do_rw(fd, cmd == 'r', param[0], param[1], 111 | &prev_size, &new_size); 112 | if (rc < 0) 113 | return 1; 114 | fprintf(stderr, "transferred %d bytes (%zu -> %zu)\n", 115 | rc, prev_size, new_size); 116 | return 0; 117 | } 118 | 119 | usage: 120 | fprintf(stderr, "%s", usage); 121 | return 1; 122 | } 123 | -------------------------------------------------------------------------------- /lib/fuse_mt.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU LGPLv2. 6 | See the file COPYING.LIB. 7 | */ 8 | 9 | #include "fuse_i.h" 10 | #include "fuse_misc.h" 11 | #include "fuse_lowlevel.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | struct procdata { 20 | struct fuse *f; 21 | struct fuse_chan *prevch; 22 | struct fuse_session *prevse; 23 | fuse_processor_t proc; 24 | void *data; 25 | }; 26 | 27 | static void mt_session_proc(void *data, const char *buf, size_t len, 28 | struct fuse_chan *ch) 29 | { 30 | struct procdata *pd = (struct procdata *) data; 31 | struct fuse_cmd *cmd = *(struct fuse_cmd **) buf; 32 | 33 | (void) len; 34 | (void) ch; 35 | pd->proc(pd->f, cmd, pd->data); 36 | } 37 | 38 | static void mt_session_exit(void *data, int val) 39 | { 40 | struct procdata *pd = (struct procdata *) data; 41 | if (val) 42 | fuse_session_exit(pd->prevse); 43 | else 44 | fuse_session_reset(pd->prevse); 45 | } 46 | 47 | static int mt_session_exited(void *data) 48 | { 49 | struct procdata *pd = (struct procdata *) data; 50 | return fuse_session_exited(pd->prevse); 51 | } 52 | 53 | static int mt_chan_receive(struct fuse_chan **chp, char *buf, size_t size __unused) 54 | { 55 | struct fuse_cmd *cmd; 56 | struct procdata *pd = (struct procdata *) fuse_chan_data(*chp); 57 | 58 | assert(size >= sizeof(cmd)); 59 | 60 | cmd = fuse_read_cmd(pd->f); 61 | if (cmd == NULL) 62 | return 0; 63 | 64 | *(struct fuse_cmd **) buf = cmd; 65 | 66 | return sizeof(cmd); 67 | } 68 | 69 | int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data) 70 | { 71 | int res; 72 | struct procdata pd; 73 | struct fuse_session *prevse = fuse_get_session(f); 74 | struct fuse_session *se; 75 | struct fuse_chan *prevch = fuse_session_next_chan(prevse, NULL); 76 | struct fuse_chan *ch; 77 | struct fuse_session_ops sop = { 78 | .exit = mt_session_exit, 79 | .exited = mt_session_exited, 80 | .process = mt_session_proc, 81 | }; 82 | struct fuse_chan_ops cop = { 83 | .receive = mt_chan_receive, 84 | }; 85 | 86 | pd.f = f; 87 | pd.prevch = prevch; 88 | pd.prevse = prevse; 89 | pd.proc = proc; 90 | pd.data = data; 91 | 92 | se = fuse_session_new(&sop, &pd); 93 | if (se == NULL) 94 | return -1; 95 | 96 | ch = fuse_chan_new(&cop, fuse_chan_fd(prevch), 97 | sizeof(struct fuse_cmd *), &pd); 98 | if (ch == NULL) { 99 | fuse_session_destroy(se); 100 | return -1; 101 | } 102 | fuse_session_add_chan(se, ch); 103 | res = fuse_session_loop_mt(se); 104 | fuse_session_destroy(se); 105 | return res; 106 | } 107 | 108 | int fuse_loop_mt(struct fuse *f) 109 | { 110 | if (f == NULL) 111 | return -1; 112 | 113 | int res = fuse_start_cleanup_thread(f); 114 | if (res) 115 | return -1; 116 | 117 | res = fuse_session_loop_mt(fuse_get_session(f)); 118 | fuse_stop_cleanup_thread(f); 119 | return res; 120 | } 121 | 122 | FUSE_SYMVER(".symver fuse_loop_mt_proc,__fuse_loop_mt@FUSE_UNVERSIONED"); 123 | -------------------------------------------------------------------------------- /lib/fuse_i.h: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU LGPLv2. 6 | See the file COPYING.LIB 7 | */ 8 | 9 | #include "fuse.h" 10 | #include "fuse_lowlevel.h" 11 | 12 | struct fuse_chan; 13 | struct fuse_ll; 14 | 15 | struct fuse_session { 16 | struct fuse_session_ops op; 17 | 18 | int (*receive_buf)(struct fuse_session *se, struct fuse_buf *buf, 19 | struct fuse_chan **chp); 20 | 21 | void (*process_buf)(void *data, const struct fuse_buf *buf, 22 | struct fuse_chan *ch); 23 | 24 | void *data; 25 | 26 | volatile int exited; 27 | 28 | struct fuse_chan *ch; 29 | }; 30 | 31 | struct fuse_req { 32 | struct fuse_ll *f; 33 | uint64_t unique; 34 | int ctr; 35 | pthread_mutex_t lock; 36 | struct fuse_ctx ctx; 37 | struct fuse_chan *ch; 38 | int interrupted; 39 | unsigned int ioctl_64bit : 1; 40 | union { 41 | struct { 42 | uint64_t unique; 43 | } i; 44 | struct { 45 | fuse_interrupt_func_t func; 46 | void *data; 47 | } ni; 48 | } u; 49 | struct fuse_req *next; 50 | struct fuse_req *prev; 51 | }; 52 | 53 | struct fuse_notify_req { 54 | uint64_t unique; 55 | void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t, 56 | const void *, const struct fuse_buf *); 57 | struct fuse_notify_req *next; 58 | struct fuse_notify_req *prev; 59 | }; 60 | 61 | struct fuse_ll { 62 | int debug; 63 | int allow_root; 64 | int atomic_o_trunc; 65 | int no_remote_posix_lock; 66 | int no_remote_flock; 67 | int big_writes; 68 | int splice_write; 69 | int splice_move; 70 | int splice_read; 71 | int no_splice_write; 72 | int no_splice_move; 73 | int no_splice_read; 74 | struct fuse_lowlevel_ops op; 75 | int got_init; 76 | struct cuse_data *cuse_data; 77 | void *userdata; 78 | uid_t owner; 79 | struct fuse_conn_info conn; 80 | struct fuse_req list; 81 | struct fuse_req interrupts; 82 | pthread_mutex_t lock; 83 | int got_destroy; 84 | pthread_key_t pipe_key; 85 | int broken_splice_nonblock; 86 | uint64_t notify_ctr; 87 | struct fuse_notify_req notify_list; 88 | }; 89 | 90 | struct fuse_cmd { 91 | char *buf; 92 | size_t buflen; 93 | struct fuse_chan *ch; 94 | }; 95 | 96 | struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args, 97 | const struct fuse_operations *op, 98 | size_t op_size, void *user_data, int compat); 99 | 100 | int fuse_sync_compat_args(struct fuse_args *args); 101 | 102 | struct fuse_chan *fuse_kern_chan_new(int fd); 103 | 104 | struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args, 105 | const struct fuse_lowlevel_ops *op, 106 | size_t op_size, void *userdata); 107 | 108 | void fuse_kern_unmount_compat22(const char *mountpoint); 109 | int fuse_chan_clearfd(struct fuse_chan *ch); 110 | 111 | void fuse_kern_unmount(const char *mountpoint, int fd); 112 | int fuse_kern_mount(const char *mountpoint, struct fuse_args *args); 113 | 114 | int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, 115 | int count); 116 | void fuse_free_req(fuse_req_t req); 117 | 118 | 119 | struct fuse *fuse_setup_common(int argc, char *argv[], 120 | const struct fuse_operations *op, 121 | size_t op_size, 122 | char **mountpoint, 123 | int *multithreaded, 124 | int *fd, 125 | void *user_data, 126 | int compat); 127 | 128 | void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg); 129 | 130 | int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg); 131 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT(fuse, 2.9.7) 2 | 3 | AC_PREREQ(2.59d) 4 | AC_CONFIG_MACRO_DIR([m4]) 5 | AC_CANONICAL_TARGET 6 | AM_INIT_AUTOMAKE([foreign]) 7 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)]) 8 | AC_CONFIG_HEADERS(include/config.h) 9 | 10 | AC_PROG_LIBTOOL 11 | AC_PROG_CC 12 | AC_PROG_MKDIR_P 13 | AM_PROG_CC_C_O 14 | 15 | case $target_os in 16 | *linux*) arch=linux;; 17 | *netbsd*) arch=netbsd;; 18 | *bsd*) arch=bsd;; 19 | *) arch=unknown;; 20 | esac 21 | 22 | if test "$ac_env_CFLAGS_set" != set; then 23 | CFLAGS="-Wall -W -Wno-sign-compare -Wstrict-prototypes -Wmissing-declarations -Wwrite-strings -g -O2 -fno-strict-aliasing" 24 | fi 25 | 26 | AC_ARG_ENABLE(lib, 27 | [ --enable-lib Compile with library ]) 28 | AC_ARG_ENABLE(util, 29 | [ --enable-util Compile with util ]) 30 | AC_ARG_ENABLE(example, 31 | [ --enable-example Compile with examples ]) 32 | AC_ARG_ENABLE(mtab, 33 | [ --disable-mtab Disable and ignore usage of /etc/mtab ]) 34 | 35 | AC_ARG_WITH(pkgconfigdir, 36 | [ --with-pkgconfigdir=DIR pkgconfig file in DIR @<:@LIBDIR/pkgconfig@:>@], 37 | [pkgconfigdir=$withval], 38 | [pkgconfigdir='${libdir}/pkgconfig']) 39 | AC_SUBST(pkgconfigdir) 40 | 41 | subdirs2="include" 42 | 43 | if test "$enable_lib" != "no"; then 44 | subdirs2="$subdirs2 lib"; 45 | fi 46 | if test "$arch" = linux -a "$enable_util" != "no"; then 47 | subdirs2="$subdirs2 util"; 48 | fi 49 | if test "$enable_example" != "no"; then 50 | subdirs2="$subdirs2 example"; 51 | fi 52 | if test "$enable_mtab" = "no"; then 53 | AC_DEFINE(IGNORE_MTAB, 1, [Don't update /etc/mtab]) 54 | fi 55 | 56 | AC_CHECK_FUNCS([fork setxattr fdatasync splice vmsplice utimensat]) 57 | AC_CHECK_FUNCS([posix_fallocate]) 58 | AC_CHECK_MEMBERS([struct stat.st_atim]) 59 | AC_CHECK_MEMBERS([struct stat.st_atimespec]) 60 | 61 | LIBS= 62 | AC_SEARCH_LIBS(dlopen, [dl]) 63 | AC_SEARCH_LIBS(clock_gettime, [rt]) 64 | libfuse_libs=$LIBS 65 | LIBS= 66 | AC_ARG_WITH([libiconv-prefix], 67 | [ --with-libiconv-prefix=DIR search for libiconv in DIR/include and DIR/lib], [ 68 | for dir in `echo "$withval" | tr : ' '`; do 69 | if test -d $dir/include; then CPPFLAGS="$CPPFLAGS -I$dir/include"; fi 70 | if test -d $dir/lib; then LDFLAGS="$LDFLAGS -L$dir/lib"; fi 71 | done 72 | ]) 73 | AM_ICONV 74 | libfuse_libs="$libfuse_libs $LTLIBICONV" 75 | AM_CONDITIONAL(ICONV, test "$am_cv_func_iconv" = yes) 76 | AC_SUBST(libfuse_libs) 77 | 78 | if test -z "$MOUNT_FUSE_PATH"; then 79 | MOUNT_FUSE_PATH=/sbin 80 | AC_MSG_NOTICE([MOUNT_FUSE_PATH env var not set, using default $MOUNT_FUSE_PATH]) 81 | fi 82 | AC_SUBST(MOUNT_FUSE_PATH) 83 | if test -z "$UDEV_RULES_PATH"; then 84 | UDEV_RULES_PATH=/etc/udev/rules.d 85 | AC_MSG_NOTICE([UDEV_RULES_PATH env var not set, using default $UDEV_RULES_PATH]) 86 | fi 87 | AC_SUBST(UDEV_RULES_PATH) 88 | if test -z "$INIT_D_PATH"; then 89 | INIT_D_PATH=/etc/init.d 90 | AC_MSG_NOTICE([INIT_D_PATH env var not set, using default $INIT_D_PATH]) 91 | fi 92 | AC_SUBST(INIT_D_PATH) 93 | 94 | AC_SUBST(subdirs2) 95 | 96 | AM_CONDITIONAL(LINUX, test "$arch" = linux) 97 | AM_CONDITIONAL(NETBSD, test "$arch" = netbsd) 98 | AM_CONDITIONAL(BSD, test "$arch" = bsd) 99 | 100 | util_linux_ok=yes 101 | if test "$arch" = linux -a "$cross_compiling" != "yes"; then 102 | AC_MSG_CHECKING([if umount supports --fake --no-canonicalize]) 103 | # exit code of umount is 1 if option is unrecognised, 2 otherwise 104 | umount --fake --no-canonicalize > /dev/null 2>&1 105 | if test $? != 1; then 106 | AC_MSG_RESULT([yes]) 107 | else 108 | firstline=`umount --fake --no-canonicalize 2>&1 | head -1` 109 | if test "$firstline" = 'umount: only root can use "--fake" option'; then 110 | AC_MSG_RESULT([yes]) 111 | else 112 | AC_MSG_RESULT([$firstline]) 113 | util_linux_ok=no 114 | fi 115 | fi 116 | fi 117 | 118 | AC_CONFIG_FILES([fuse.pc Makefile lib/Makefile util/Makefile example/Makefile include/Makefile doc/Makefile]) 119 | AC_OUTPUT 120 | 121 | if test "$util_linux_ok" = no; then 122 | AC_MSG_WARN([ 123 | ****************************************************************** 124 | * Please install util-linux version 2.18 or later which supports * 125 | * --fake and --no-canonicalize options in mount and umount * 126 | ******************************************************************]) 127 | fi 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | libfuse 2 | ======= 3 | 4 | Warning: unresolved security issue 5 | ---------------------------------- 6 | 7 | Be aware that FUSE has an unresolved security bug 8 | ([bug #15](https://github.com/libfuse/libfuse/issues/15)): the 9 | permission check for accessing a cached directory is only done once 10 | when the directory entry is first loaded into the cache. Subsequent 11 | accesses will re-use the results of the first check, even if the 12 | directory permissions have since changed, and even if the subsequent 13 | access is made by a different user. 14 | 15 | This bug needs to be fixed in the Linux kernel and has been known 16 | since 2006 but unfortunately no fix has been applied yet. If you 17 | depend on correct permission handling for FUSE file systems, the only 18 | workaround is to completely disable caching of directory 19 | entries. Alternatively, the severity of the bug can be somewhat 20 | reduced by not using the `allow_other` mount option. 21 | 22 | 23 | About 24 | ----- 25 | 26 | FUSE (Filesystem in Userspace) is an interface for userspace programs 27 | to export a filesystem to the Linux kernel. The FUSE project consists 28 | of two components: the *fuse* kernel module (maintained in the regular 29 | kernel repositories) and the *libfuse* userspace library (maintained 30 | in this repository). libfuse provides the reference implementation 31 | for communicating with the FUSE kernel module. 32 | 33 | A FUSE file system is typically implemented as a standalone 34 | application that links with libfuse. libfuse provides functions to 35 | mount the file system, unmount it, read requests from the kernel, and 36 | send responses back. libfuse offers two APIs: a "high-level", 37 | synchronous API, and a "low-level" asynchronous API. In both cases, 38 | incoming requests from the kernel are passed to the main program using 39 | callbacks. When using the high-level API, the callbacks may work with 40 | file names and paths instead of inodes, and processing of a request 41 | finishes when the callback function returns. When using the low-level 42 | API, the callbacks must work with inodes and responses must be sent 43 | explicitly using a separate set of API functions. 44 | 45 | 46 | Installation 47 | ------------ 48 | 49 | ./configure 50 | make -j8 51 | make install 52 | 53 | You may also need to add `/usr/local/lib` to `/etc/ld.so.conf` and/or 54 | run *ldconfig*. If you're building from the git repository (instead of 55 | using a release tarball), you also need to run `./makeconf.sh` to 56 | create the `configure` script. 57 | 58 | You'll also need a fuse kernel module (Linux kernels 2.6.14 or later 59 | contain FUSE support). 60 | 61 | For more details see the file `INSTALL` 62 | 63 | Security implications 64 | --------------------- 65 | 66 | If you run `make install`, the *fusermount* program is installed 67 | set-user-id to root. This is done to allow normal users to mount 68 | their own filesystem implementations. 69 | 70 | There must however be some limitations, in order to prevent Bad User from 71 | doing nasty things. Currently those limitations are: 72 | 73 | - The user can only mount on a mountpoint, for which it has write 74 | permission 75 | 76 | - The mountpoint is not a sticky directory which isn't owned by the 77 | user (like /tmp usually is) 78 | 79 | - No other user (including root) can access the contents of the 80 | mounted filesystem (though this can be relaxed by allowing the use 81 | of the `allow_other` and `allow_root` mount options in `fuse.conf`) 82 | 83 | 84 | Building your own filesystem 85 | ------------------------------ 86 | 87 | FUSE comes with several example file systems in the `examples` 88 | directory. For example, the *fusexmp* example mirrors the contents of 89 | the root directory under the mountpoint. Start from there and adapt 90 | the code! 91 | 92 | The documentation of the API functions and necessary callbacks is 93 | mostly contained in the files `include/fuse.h` (for the high-level 94 | API) and `include/fuse_lowlevel.h` (for the low-level API). An 95 | autogenerated html version of the API is available in the `doc/html` 96 | directory and at http://libfuse.github.io/doxygen. 97 | 98 | 99 | Getting Help 100 | ------------ 101 | 102 | If you need help, please ask on the 103 | mailing list (subscribe at 104 | https://lists.sourceforge.net/lists/listinfo/fuse-devel). 105 | 106 | Please report any bugs on the GitHub issue tracker at 107 | https://github.com/libfuse/main/issues. 108 | 109 | -------------------------------------------------------------------------------- /example/hello_ll.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU GPL. 6 | See the file COPYING. 7 | 8 | gcc -Wall hello_ll.c `pkg-config fuse --cflags --libs` -o hello_ll 9 | */ 10 | 11 | #define FUSE_USE_VERSION 26 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | static const char *hello_str = "Hello World!\n"; 23 | static const char *hello_name = "hello"; 24 | 25 | static int hello_stat(fuse_ino_t ino, struct stat *stbuf) 26 | { 27 | stbuf->st_ino = ino; 28 | switch (ino) { 29 | case 1: 30 | stbuf->st_mode = S_IFDIR | 0755; 31 | stbuf->st_nlink = 2; 32 | break; 33 | 34 | case 2: 35 | stbuf->st_mode = S_IFREG | 0444; 36 | stbuf->st_nlink = 1; 37 | stbuf->st_size = strlen(hello_str); 38 | break; 39 | 40 | default: 41 | return -1; 42 | } 43 | return 0; 44 | } 45 | 46 | static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino, 47 | struct fuse_file_info *fi) 48 | { 49 | struct stat stbuf; 50 | 51 | (void) fi; 52 | 53 | memset(&stbuf, 0, sizeof(stbuf)); 54 | if (hello_stat(ino, &stbuf) == -1) 55 | fuse_reply_err(req, ENOENT); 56 | else 57 | fuse_reply_attr(req, &stbuf, 1.0); 58 | } 59 | 60 | static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) 61 | { 62 | struct fuse_entry_param e; 63 | 64 | if (parent != 1 || strcmp(name, hello_name) != 0) 65 | fuse_reply_err(req, ENOENT); 66 | else { 67 | memset(&e, 0, sizeof(e)); 68 | e.ino = 2; 69 | e.attr_timeout = 1.0; 70 | e.entry_timeout = 1.0; 71 | hello_stat(e.ino, &e.attr); 72 | 73 | fuse_reply_entry(req, &e); 74 | } 75 | } 76 | 77 | struct dirbuf { 78 | char *p; 79 | size_t size; 80 | }; 81 | 82 | static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name, 83 | fuse_ino_t ino) 84 | { 85 | struct stat stbuf; 86 | size_t oldsize = b->size; 87 | b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0); 88 | b->p = (char *) realloc(b->p, b->size); 89 | memset(&stbuf, 0, sizeof(stbuf)); 90 | stbuf.st_ino = ino; 91 | fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf, 92 | b->size); 93 | } 94 | 95 | #define min(x, y) ((x) < (y) ? (x) : (y)) 96 | 97 | static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize, 98 | off_t off, size_t maxsize) 99 | { 100 | if (off < bufsize) 101 | return fuse_reply_buf(req, buf + off, 102 | min(bufsize - off, maxsize)); 103 | else 104 | return fuse_reply_buf(req, NULL, 0); 105 | } 106 | 107 | static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, 108 | off_t off, struct fuse_file_info *fi) 109 | { 110 | (void) fi; 111 | 112 | if (ino != 1) 113 | fuse_reply_err(req, ENOTDIR); 114 | else { 115 | struct dirbuf b; 116 | 117 | memset(&b, 0, sizeof(b)); 118 | dirbuf_add(req, &b, ".", 1); 119 | dirbuf_add(req, &b, "..", 1); 120 | dirbuf_add(req, &b, hello_name, 2); 121 | reply_buf_limited(req, b.p, b.size, off, size); 122 | free(b.p); 123 | } 124 | } 125 | 126 | static void hello_ll_open(fuse_req_t req, fuse_ino_t ino, 127 | struct fuse_file_info *fi) 128 | { 129 | if (ino != 2) 130 | fuse_reply_err(req, EISDIR); 131 | else if ((fi->flags & 3) != O_RDONLY) 132 | fuse_reply_err(req, EACCES); 133 | else 134 | fuse_reply_open(req, fi); 135 | } 136 | 137 | static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size, 138 | off_t off, struct fuse_file_info *fi) 139 | { 140 | (void) fi; 141 | 142 | assert(ino == 2); 143 | reply_buf_limited(req, hello_str, strlen(hello_str), off, size); 144 | } 145 | 146 | static struct fuse_lowlevel_ops hello_ll_oper = { 147 | .lookup = hello_ll_lookup, 148 | .getattr = hello_ll_getattr, 149 | .readdir = hello_ll_readdir, 150 | .open = hello_ll_open, 151 | .read = hello_ll_read, 152 | }; 153 | 154 | int main(int argc, char *argv[]) 155 | { 156 | struct fuse_args args = FUSE_ARGS_INIT(argc, argv); 157 | struct fuse_chan *ch; 158 | char *mountpoint; 159 | int err = -1; 160 | 161 | if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) != -1 && 162 | (ch = fuse_mount(mountpoint, &args)) != NULL) { 163 | struct fuse_session *se; 164 | 165 | se = fuse_lowlevel_new(&args, &hello_ll_oper, 166 | sizeof(hello_ll_oper), NULL); 167 | if (se != NULL) { 168 | if (fuse_set_signal_handlers(se) != -1) { 169 | fuse_session_add_chan(se, ch); 170 | err = fuse_session_loop(se); 171 | fuse_remove_signal_handlers(se); 172 | fuse_session_remove_chan(ch); 173 | } 174 | fuse_session_destroy(se); 175 | } 176 | fuse_unmount(mountpoint, ch); 177 | } 178 | fuse_opt_free_args(&args); 179 | 180 | return err ? 1 : 0; 181 | } 182 | -------------------------------------------------------------------------------- /lib/fuse_versionscript: -------------------------------------------------------------------------------- 1 | FUSE_UNVERSIONED { 2 | }; 3 | 4 | FUSE_2.2 { 5 | global: 6 | fuse_destroy; 7 | fuse_exit; 8 | fuse_exited; 9 | fuse_invalidate; 10 | fuse_is_lib_option; 11 | fuse_loop; 12 | fuse_loop_mt; 13 | fuse_loop_mt_proc; 14 | fuse_main; 15 | fuse_main_compat1; 16 | fuse_main_compat2; 17 | fuse_mount_compat1; 18 | fuse_new_compat1; 19 | fuse_new_compat2; 20 | fuse_process_cmd; 21 | fuse_read_cmd; 22 | fuse_set_getcontext_func; 23 | fuse_setup_compat2; 24 | }; 25 | 26 | FUSE_2.4 { 27 | global: 28 | fuse_add_dirent; 29 | fuse_chan_bufsize; 30 | fuse_chan_data; 31 | fuse_chan_destroy; 32 | fuse_chan_fd; 33 | fuse_chan_receive; 34 | fuse_chan_send; 35 | fuse_chan_session; 36 | fuse_dirent_size; 37 | fuse_kern_chan_new; 38 | fuse_lowlevel_is_lib_option; 39 | fuse_reply_attr; 40 | fuse_reply_buf; 41 | fuse_reply_entry; 42 | fuse_reply_err; 43 | fuse_reply_none; 44 | fuse_reply_readlink; 45 | fuse_reply_write; 46 | fuse_reply_xattr; 47 | fuse_req_userdata; 48 | fuse_session_add_chan; 49 | fuse_session_destroy; 50 | fuse_session_exit; 51 | fuse_session_exited; 52 | fuse_session_loop; 53 | fuse_session_loop_mt; 54 | fuse_session_new; 55 | fuse_session_next_chan; 56 | fuse_session_process; 57 | fuse_session_reset; 58 | } FUSE_2.2; 59 | 60 | FUSE_2.5 { 61 | global: 62 | fuse_lowlevel_new_compat; 63 | fuse_main_real_compat22; 64 | fuse_mount_compat22; 65 | fuse_new_compat22; 66 | fuse_opt_parse; 67 | fuse_opt_add_opt; 68 | fuse_opt_add_arg; 69 | fuse_opt_free_args; 70 | fuse_opt_match; 71 | fuse_parse_cmdline; 72 | fuse_remove_signal_handlers; 73 | fuse_reply_create; 74 | fuse_reply_open; 75 | fuse_reply_open_compat; 76 | fuse_reply_statfs; 77 | fuse_reply_statfs_compat; 78 | fuse_setup_compat22; 79 | fuse_set_signal_handlers; 80 | } FUSE_2.4; 81 | 82 | FUSE_2.6 { 83 | global: 84 | fuse_add_direntry; 85 | fuse_chan_new; 86 | fuse_chan_new_compat24; 87 | fuse_chan_recv; 88 | fuse_daemonize; 89 | fuse_get_session; 90 | fuse_interrupted; 91 | fuse_lowlevel_new; 92 | fuse_lowlevel_new_compat25; 93 | fuse_main_real; 94 | fuse_main_real_compat25; 95 | fuse_mount; 96 | fuse_mount_compat25; 97 | fuse_new; 98 | fuse_new_compat25; 99 | fuse_opt_insert_arg; 100 | fuse_reply_lock; 101 | fuse_req_interrupt_func; 102 | fuse_req_interrupted; 103 | fuse_session_remove_chan; 104 | fuse_setup; 105 | fuse_setup_compat25; 106 | fuse_teardown; 107 | fuse_teardown_compat22; 108 | fuse_unmount; 109 | fuse_unmount_compat22; 110 | } FUSE_2.5; 111 | 112 | FUSE_2.7 { 113 | global: 114 | fuse_fs_access; 115 | fuse_fs_bmap; 116 | fuse_fs_chmod; 117 | fuse_fs_chown; 118 | fuse_fs_create; 119 | fuse_fs_destroy; 120 | fuse_fs_fgetattr; 121 | fuse_fs_flush; 122 | fuse_fs_fsync; 123 | fuse_fs_fsyncdir; 124 | fuse_fs_ftruncate; 125 | fuse_fs_getattr; 126 | fuse_fs_getxattr; 127 | fuse_fs_init; 128 | fuse_fs_link; 129 | fuse_fs_listxattr; 130 | fuse_fs_lock; 131 | fuse_fs_mkdir; 132 | fuse_fs_mknod; 133 | fuse_fs_new; 134 | fuse_fs_open; 135 | fuse_fs_opendir; 136 | fuse_fs_read; 137 | fuse_fs_readdir; 138 | fuse_fs_readlink; 139 | fuse_fs_release; 140 | fuse_fs_releasedir; 141 | fuse_fs_removexattr; 142 | fuse_fs_rename; 143 | fuse_fs_rmdir; 144 | fuse_fs_setxattr; 145 | fuse_fs_statfs; 146 | fuse_fs_symlink; 147 | fuse_fs_truncate; 148 | fuse_fs_unlink; 149 | fuse_fs_utimens; 150 | fuse_fs_write; 151 | fuse_reply_iov; 152 | fuse_version; 153 | local: 154 | fuse_register_module; 155 | } FUSE_2.6; 156 | 157 | FUSE_2.7.5 { 158 | global: 159 | fuse_reply_bmap; 160 | } FUSE_2.7; 161 | 162 | FUSE_2.8 { 163 | global: 164 | cuse_lowlevel_new; 165 | cuse_lowlevel_main; 166 | cuse_lowlevel_setup; 167 | cuse_lowlevel_teardown; 168 | fuse_fs_ioctl; 169 | fuse_fs_poll; 170 | fuse_get_context; 171 | fuse_getgroups; 172 | fuse_lowlevel_notify_inval_entry; 173 | fuse_lowlevel_notify_inval_inode; 174 | fuse_lowlevel_notify_poll; 175 | fuse_notify_poll; 176 | fuse_opt_add_opt_escaped; 177 | fuse_pollhandle_destroy; 178 | fuse_reply_ioctl; 179 | fuse_reply_ioctl_iov; 180 | fuse_reply_ioctl_retry; 181 | fuse_reply_poll; 182 | fuse_req_ctx; 183 | fuse_req_getgroups; 184 | fuse_session_data; 185 | } FUSE_2.7.5; 186 | 187 | FUSE_2.9 { 188 | global: 189 | fuse_buf_copy; 190 | fuse_buf_size; 191 | fuse_fs_read_buf; 192 | fuse_fs_write_buf; 193 | fuse_lowlevel_notify_retrieve; 194 | fuse_lowlevel_notify_store; 195 | fuse_reply_data; 196 | fuse_session_process_buf; 197 | fuse_session_receive_buf; 198 | fuse_start_cleanup_thread; 199 | fuse_stop_cleanup_thread; 200 | fuse_clean_cache; 201 | fuse_lowlevel_notify_delete; 202 | fuse_fs_flock; 203 | } FUSE_2.8; 204 | 205 | FUSE_2.9.1 { 206 | global: 207 | fuse_fs_fallocate; 208 | 209 | local: 210 | *; 211 | } FUSE_2.9; 212 | -------------------------------------------------------------------------------- /example/fioc.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE fioc: FUSE ioctl example 3 | Copyright (C) 2008 SUSE Linux Products GmbH 4 | Copyright (C) 2008 Tejun Heo 5 | 6 | This program can be distributed under the terms of the GNU GPL. 7 | See the file COPYING. 8 | 9 | gcc -Wall fioc.c `pkg-config fuse --cflags --libs` -o fioc 10 | */ 11 | 12 | #define FUSE_USE_VERSION 26 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "fioc.h" 23 | 24 | #define FIOC_NAME "fioc" 25 | 26 | enum { 27 | FIOC_NONE, 28 | FIOC_ROOT, 29 | FIOC_FILE, 30 | }; 31 | 32 | static void *fioc_buf; 33 | static size_t fioc_size; 34 | 35 | static int fioc_resize(size_t new_size) 36 | { 37 | void *new_buf; 38 | 39 | if (new_size == fioc_size) 40 | return 0; 41 | 42 | new_buf = realloc(fioc_buf, new_size); 43 | if (!new_buf && new_size) 44 | return -ENOMEM; 45 | 46 | if (new_size > fioc_size) 47 | memset(new_buf + fioc_size, 0, new_size - fioc_size); 48 | 49 | fioc_buf = new_buf; 50 | fioc_size = new_size; 51 | 52 | return 0; 53 | } 54 | 55 | static int fioc_expand(size_t new_size) 56 | { 57 | if (new_size > fioc_size) 58 | return fioc_resize(new_size); 59 | return 0; 60 | } 61 | 62 | static int fioc_file_type(const char *path) 63 | { 64 | if (strcmp(path, "/") == 0) 65 | return FIOC_ROOT; 66 | if (strcmp(path, "/" FIOC_NAME) == 0) 67 | return FIOC_FILE; 68 | return FIOC_NONE; 69 | } 70 | 71 | static int fioc_getattr(const char *path, struct stat *stbuf) 72 | { 73 | stbuf->st_uid = getuid(); 74 | stbuf->st_gid = getgid(); 75 | stbuf->st_atime = stbuf->st_mtime = time(NULL); 76 | 77 | switch (fioc_file_type(path)) { 78 | case FIOC_ROOT: 79 | stbuf->st_mode = S_IFDIR | 0755; 80 | stbuf->st_nlink = 2; 81 | break; 82 | case FIOC_FILE: 83 | stbuf->st_mode = S_IFREG | 0644; 84 | stbuf->st_nlink = 1; 85 | stbuf->st_size = fioc_size; 86 | break; 87 | case FIOC_NONE: 88 | return -ENOENT; 89 | } 90 | 91 | return 0; 92 | } 93 | 94 | static int fioc_open(const char *path, struct fuse_file_info *fi) 95 | { 96 | (void) fi; 97 | 98 | if (fioc_file_type(path) != FIOC_NONE) 99 | return 0; 100 | return -ENOENT; 101 | } 102 | 103 | static int fioc_do_read(char *buf, size_t size, off_t offset) 104 | { 105 | if (offset >= fioc_size) 106 | return 0; 107 | 108 | if (size > fioc_size - offset) 109 | size = fioc_size - offset; 110 | 111 | memcpy(buf, fioc_buf + offset, size); 112 | 113 | return size; 114 | } 115 | 116 | static int fioc_read(const char *path, char *buf, size_t size, 117 | off_t offset, struct fuse_file_info *fi) 118 | { 119 | (void) fi; 120 | 121 | if (fioc_file_type(path) != FIOC_FILE) 122 | return -EINVAL; 123 | 124 | return fioc_do_read(buf, size, offset); 125 | } 126 | 127 | static int fioc_do_write(const char *buf, size_t size, off_t offset) 128 | { 129 | if (fioc_expand(offset + size)) 130 | return -ENOMEM; 131 | 132 | memcpy(fioc_buf + offset, buf, size); 133 | 134 | return size; 135 | } 136 | 137 | static int fioc_write(const char *path, const char *buf, size_t size, 138 | off_t offset, struct fuse_file_info *fi) 139 | { 140 | (void) fi; 141 | 142 | if (fioc_file_type(path) != FIOC_FILE) 143 | return -EINVAL; 144 | 145 | return fioc_do_write(buf, size, offset); 146 | } 147 | 148 | static int fioc_truncate(const char *path, off_t size) 149 | { 150 | if (fioc_file_type(path) != FIOC_FILE) 151 | return -EINVAL; 152 | 153 | return fioc_resize(size); 154 | } 155 | 156 | static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler, 157 | off_t offset, struct fuse_file_info *fi) 158 | { 159 | (void) fi; 160 | (void) offset; 161 | 162 | if (fioc_file_type(path) != FIOC_ROOT) 163 | return -ENOENT; 164 | 165 | filler(buf, ".", NULL, 0); 166 | filler(buf, "..", NULL, 0); 167 | filler(buf, FIOC_NAME, NULL, 0); 168 | 169 | return 0; 170 | } 171 | 172 | static int fioc_ioctl(const char *path, int cmd, void *arg, 173 | struct fuse_file_info *fi, unsigned int flags, void *data) 174 | { 175 | (void) arg; 176 | (void) fi; 177 | (void) flags; 178 | 179 | if (fioc_file_type(path) != FIOC_FILE) 180 | return -EINVAL; 181 | 182 | if (flags & FUSE_IOCTL_COMPAT) 183 | return -ENOSYS; 184 | 185 | switch (cmd) { 186 | case FIOC_GET_SIZE: 187 | *(size_t *)data = fioc_size; 188 | return 0; 189 | 190 | case FIOC_SET_SIZE: 191 | fioc_resize(*(size_t *)data); 192 | return 0; 193 | } 194 | 195 | return -EINVAL; 196 | } 197 | 198 | static struct fuse_operations fioc_oper = { 199 | .getattr = fioc_getattr, 200 | .readdir = fioc_readdir, 201 | .truncate = fioc_truncate, 202 | .open = fioc_open, 203 | .read = fioc_read, 204 | .write = fioc_write, 205 | .ioctl = fioc_ioctl, 206 | }; 207 | 208 | int main(int argc, char *argv[]) 209 | { 210 | return fuse_main(argc, argv, &fioc_oper, NULL); 211 | } 212 | -------------------------------------------------------------------------------- /util/mount.fuse.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU GPL. 6 | See the file COPYING. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | static char *progname; 16 | 17 | static char *xstrdup(const char *s) 18 | { 19 | char *t = strdup(s); 20 | if (!t) { 21 | fprintf(stderr, "%s: failed to allocate memory\n", progname); 22 | exit(1); 23 | } 24 | return t; 25 | } 26 | 27 | static void *xrealloc(void *oldptr, size_t size) 28 | { 29 | void *ptr = realloc(oldptr, size); 30 | if (!ptr) { 31 | fprintf(stderr, "%s: failed to allocate memory\n", progname); 32 | exit(1); 33 | } 34 | return ptr; 35 | } 36 | 37 | static void add_arg(char **cmdp, const char *opt) 38 | { 39 | size_t optlen = strlen(opt); 40 | size_t cmdlen = *cmdp ? strlen(*cmdp) : 0; 41 | char *cmd = xrealloc(*cmdp, cmdlen + optlen * 4 + 4); 42 | char *s; 43 | s = cmd + cmdlen; 44 | if (*cmdp) 45 | *s++ = ' '; 46 | 47 | *s++ = '\''; 48 | for (; *opt; opt++) { 49 | if (*opt == '\'') { 50 | *s++ = '\''; 51 | *s++ = '\\'; 52 | *s++ = '\''; 53 | *s++ = '\''; 54 | } else 55 | *s++ = *opt; 56 | } 57 | *s++ = '\''; 58 | *s = '\0'; 59 | *cmdp = cmd; 60 | } 61 | 62 | static char *add_option(const char *opt, char *options) 63 | { 64 | int oldlen = options ? strlen(options) : 0; 65 | 66 | options = xrealloc(options, oldlen + 1 + strlen(opt) + 1); 67 | if (!oldlen) 68 | strcpy(options, opt); 69 | else { 70 | strcat(options, ","); 71 | strcat(options, opt); 72 | } 73 | return options; 74 | } 75 | 76 | int main(int argc, char *argv[]) 77 | { 78 | char *type = NULL; 79 | char *source; 80 | const char *mountpoint; 81 | char *basename; 82 | char *options = NULL; 83 | char *command = NULL; 84 | char *setuid = NULL; 85 | int i; 86 | int dev = 1; 87 | int suid = 1; 88 | 89 | progname = argv[0]; 90 | basename = strrchr(argv[0], '/'); 91 | if (basename) 92 | basename++; 93 | else 94 | basename = argv[0]; 95 | 96 | if (strncmp(basename, "mount.fuse.", 11) == 0) 97 | type = basename + 11; 98 | if (strncmp(basename, "mount.fuseblk.", 14) == 0) 99 | type = basename + 14; 100 | 101 | if (type && !type[0]) 102 | type = NULL; 103 | 104 | if (argc < 3) { 105 | fprintf(stderr, 106 | "usage: %s %s destination [-t type] [-o opt[,opts...]]\n", 107 | progname, type ? "source" : "type#[source]"); 108 | exit(1); 109 | } 110 | 111 | source = argv[1]; 112 | if (!source[0]) 113 | source = NULL; 114 | 115 | mountpoint = argv[2]; 116 | 117 | for (i = 3; i < argc; i++) { 118 | if (strcmp(argv[i], "-v") == 0) { 119 | continue; 120 | } else if (strcmp(argv[i], "-t") == 0) { 121 | i++; 122 | 123 | if (i == argc) { 124 | fprintf(stderr, 125 | "%s: missing argument to option '-t'\n", 126 | progname); 127 | exit(1); 128 | } 129 | type = argv[i]; 130 | if (strncmp(type, "fuse.", 5) == 0) 131 | type += 5; 132 | else if (strncmp(type, "fuseblk.", 8) == 0) 133 | type += 8; 134 | 135 | if (!type[0]) { 136 | fprintf(stderr, 137 | "%s: empty type given as argument to option '-t'\n", 138 | progname); 139 | exit(1); 140 | } 141 | } else if (strcmp(argv[i], "-o") == 0) { 142 | char *opts; 143 | char *opt; 144 | i++; 145 | if (i == argc) 146 | break; 147 | 148 | opts = xstrdup(argv[i]); 149 | opt = strtok(opts, ","); 150 | while (opt) { 151 | int j; 152 | int ignore = 0; 153 | const char *ignore_opts[] = { "", 154 | "user", 155 | "nouser", 156 | "users", 157 | "auto", 158 | "noauto", 159 | "_netdev", 160 | NULL}; 161 | if (strncmp(opt, "setuid=", 7) == 0) { 162 | setuid = xstrdup(opt + 7); 163 | ignore = 1; 164 | } 165 | for (j = 0; ignore_opts[j]; j++) 166 | if (strcmp(opt, ignore_opts[j]) == 0) 167 | ignore = 1; 168 | 169 | if (!ignore) { 170 | if (strcmp(opt, "nodev") == 0) 171 | dev = 0; 172 | else if (strcmp(opt, "nosuid") == 0) 173 | suid = 0; 174 | 175 | options = add_option(opt, options); 176 | } 177 | opt = strtok(NULL, ","); 178 | } 179 | } 180 | } 181 | 182 | if (dev) 183 | options = add_option("dev", options); 184 | if (suid) 185 | options = add_option("suid", options); 186 | 187 | if (!type) { 188 | if (source) { 189 | type = xstrdup(source); 190 | source = strchr(type, '#'); 191 | if (source) 192 | *source++ = '\0'; 193 | if (!type[0]) { 194 | fprintf(stderr, "%s: empty filesystem type\n", 195 | progname); 196 | exit(1); 197 | } 198 | } else { 199 | fprintf(stderr, "%s: empty source\n", progname); 200 | exit(1); 201 | } 202 | } 203 | 204 | add_arg(&command, type); 205 | if (source) 206 | add_arg(&command, source); 207 | add_arg(&command, mountpoint); 208 | if (options) { 209 | add_arg(&command, "-o"); 210 | add_arg(&command, options); 211 | } 212 | 213 | if (setuid && setuid[0]) { 214 | char *sucommand = command; 215 | command = NULL; 216 | add_arg(&command, "su"); 217 | add_arg(&command, "-"); 218 | add_arg(&command, setuid); 219 | add_arg(&command, "-c"); 220 | add_arg(&command, sucommand); 221 | } else if (!getenv("HOME")) { 222 | /* Hack to make filesystems work in the boot environment */ 223 | setenv("HOME", "/root", 0); 224 | } 225 | 226 | execl("/bin/sh", "/bin/sh", "-c", command, NULL); 227 | fprintf(stderr, "%s: failed to execute /bin/sh: %s\n", progname, 228 | strerror(errno)); 229 | return 1; 230 | } 231 | -------------------------------------------------------------------------------- /lib/fuse_session.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU LGPLv2. 6 | See the file COPYING.LIB 7 | */ 8 | 9 | #include "fuse_i.h" 10 | #include "fuse_misc.h" 11 | #include "fuse_common_compat.h" 12 | #include "fuse_lowlevel_compat.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | struct fuse_chan { 21 | struct fuse_chan_ops op; 22 | 23 | struct fuse_session *se; 24 | 25 | int fd; 26 | 27 | size_t bufsize; 28 | 29 | void *data; 30 | 31 | int compat; 32 | }; 33 | 34 | struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data) 35 | { 36 | struct fuse_session *se = (struct fuse_session *) malloc(sizeof(*se)); 37 | if (se == NULL) { 38 | fprintf(stderr, "fuse: failed to allocate session\n"); 39 | return NULL; 40 | } 41 | 42 | memset(se, 0, sizeof(*se)); 43 | se->op = *op; 44 | se->data = data; 45 | 46 | return se; 47 | } 48 | 49 | void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch) 50 | { 51 | assert(se->ch == NULL); 52 | assert(ch->se == NULL); 53 | se->ch = ch; 54 | ch->se = se; 55 | } 56 | 57 | void fuse_session_remove_chan(struct fuse_chan *ch) 58 | { 59 | struct fuse_session *se = ch->se; 60 | if (se) { 61 | assert(se->ch == ch); 62 | se->ch = NULL; 63 | ch->se = NULL; 64 | } 65 | } 66 | 67 | struct fuse_chan *fuse_session_next_chan(struct fuse_session *se, 68 | struct fuse_chan *ch) 69 | { 70 | assert(ch == NULL || ch == se->ch); 71 | if (ch == NULL) 72 | return se->ch; 73 | else 74 | return NULL; 75 | } 76 | 77 | void fuse_session_process(struct fuse_session *se, const char *buf, size_t len, 78 | struct fuse_chan *ch) 79 | { 80 | se->op.process(se->data, buf, len, ch); 81 | } 82 | 83 | void fuse_session_process_buf(struct fuse_session *se, 84 | const struct fuse_buf *buf, struct fuse_chan *ch) 85 | { 86 | if (se->process_buf) { 87 | se->process_buf(se->data, buf, ch); 88 | } else { 89 | assert(!(buf->flags & FUSE_BUF_IS_FD)); 90 | fuse_session_process(se->data, buf->mem, buf->size, ch); 91 | } 92 | } 93 | 94 | int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf, 95 | struct fuse_chan **chp) 96 | { 97 | int res; 98 | 99 | if (se->receive_buf) { 100 | res = se->receive_buf(se, buf, chp); 101 | } else { 102 | res = fuse_chan_recv(chp, buf->mem, buf->size); 103 | if (res > 0) 104 | buf->size = res; 105 | } 106 | 107 | return res; 108 | } 109 | 110 | 111 | void fuse_session_destroy(struct fuse_session *se) 112 | { 113 | if (se->op.destroy) 114 | se->op.destroy(se->data); 115 | if (se->ch != NULL) 116 | fuse_chan_destroy(se->ch); 117 | free(se); 118 | } 119 | 120 | void fuse_session_exit(struct fuse_session *se) 121 | { 122 | if (se->op.exit) 123 | se->op.exit(se->data, 1); 124 | se->exited = 1; 125 | } 126 | 127 | void fuse_session_reset(struct fuse_session *se) 128 | { 129 | if (se->op.exit) 130 | se->op.exit(se->data, 0); 131 | se->exited = 0; 132 | } 133 | 134 | int fuse_session_exited(struct fuse_session *se) 135 | { 136 | if (se->op.exited) 137 | return se->op.exited(se->data); 138 | else 139 | return se->exited; 140 | } 141 | 142 | void *fuse_session_data(struct fuse_session *se) 143 | { 144 | return se->data; 145 | } 146 | 147 | static struct fuse_chan *fuse_chan_new_common(struct fuse_chan_ops *op, int fd, 148 | size_t bufsize, void *data, 149 | int compat) 150 | { 151 | struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch)); 152 | if (ch == NULL) { 153 | fprintf(stderr, "fuse: failed to allocate channel\n"); 154 | return NULL; 155 | } 156 | 157 | memset(ch, 0, sizeof(*ch)); 158 | ch->op = *op; 159 | ch->fd = fd; 160 | ch->bufsize = bufsize; 161 | ch->data = data; 162 | ch->compat = compat; 163 | 164 | return ch; 165 | } 166 | 167 | struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd, 168 | size_t bufsize, void *data) 169 | { 170 | return fuse_chan_new_common(op, fd, bufsize, data, 0); 171 | } 172 | 173 | struct fuse_chan *fuse_chan_new_compat24(struct fuse_chan_ops_compat24 *op, 174 | int fd, size_t bufsize, void *data) 175 | { 176 | return fuse_chan_new_common((struct fuse_chan_ops *) op, fd, bufsize, 177 | data, 24); 178 | } 179 | 180 | int fuse_chan_fd(struct fuse_chan *ch) 181 | { 182 | return ch->fd; 183 | } 184 | 185 | int fuse_chan_clearfd(struct fuse_chan *ch) 186 | { 187 | if (ch == NULL) 188 | return -1; 189 | 190 | int fd = ch->fd; 191 | ch->fd = -1; 192 | return fd; 193 | } 194 | 195 | size_t fuse_chan_bufsize(struct fuse_chan *ch) 196 | { 197 | return ch->bufsize; 198 | } 199 | 200 | void *fuse_chan_data(struct fuse_chan *ch) 201 | { 202 | return ch->data; 203 | } 204 | 205 | struct fuse_session *fuse_chan_session(struct fuse_chan *ch) 206 | { 207 | return ch->se; 208 | } 209 | 210 | int fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t size) 211 | { 212 | struct fuse_chan *ch = *chp; 213 | if (ch->compat) 214 | return ((struct fuse_chan_ops_compat24 *) &ch->op) 215 | ->receive(ch, buf, size); 216 | else 217 | return ch->op.receive(chp, buf, size); 218 | } 219 | 220 | int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size) 221 | { 222 | int res; 223 | 224 | res = fuse_chan_recv(&ch, buf, size); 225 | return res >= 0 ? res : (res != -EINTR && res != -EAGAIN) ? -1 : 0; 226 | } 227 | 228 | int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count) 229 | { 230 | return ch->op.send(ch, iov, count); 231 | } 232 | 233 | void fuse_chan_destroy(struct fuse_chan *ch) 234 | { 235 | fuse_session_remove_chan(ch); 236 | if (ch->op.destroy) 237 | ch->op.destroy(ch); 238 | free(ch); 239 | } 240 | 241 | #ifndef __FreeBSD__ 242 | FUSE_SYMVER(".symver fuse_chan_new_compat24,fuse_chan_new@FUSE_2.4"); 243 | #endif 244 | -------------------------------------------------------------------------------- /test/stracedecode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "fuse_kernel.h" 4 | 5 | static struct { 6 | const char *name; 7 | } fuse_ll_ops[] = { 8 | [FUSE_LOOKUP] = { "LOOKUP" }, 9 | [FUSE_FORGET] = { "FORGET" }, 10 | [FUSE_GETATTR] = { "GETATTR" }, 11 | [FUSE_SETATTR] = { "SETATTR" }, 12 | [FUSE_READLINK] = { "READLINK" }, 13 | [FUSE_SYMLINK] = { "SYMLINK" }, 14 | [FUSE_MKNOD] = { "MKNOD" }, 15 | [FUSE_MKDIR] = { "MKDIR" }, 16 | [FUSE_UNLINK] = { "UNLINK" }, 17 | [FUSE_RMDIR] = { "RMDIR" }, 18 | [FUSE_RENAME] = { "RENAME" }, 19 | [FUSE_LINK] = { "LINK" }, 20 | [FUSE_OPEN] = { "OPEN" }, 21 | [FUSE_READ] = { "READ" }, 22 | [FUSE_WRITE] = { "WRITE" }, 23 | [FUSE_STATFS] = { "STATFS" }, 24 | [FUSE_RELEASE] = { "RELEASE" }, 25 | [FUSE_FSYNC] = { "FSYNC" }, 26 | [FUSE_SETXATTR] = { "SETXATTR" }, 27 | [FUSE_GETXATTR] = { "GETXATTR" }, 28 | [FUSE_LISTXATTR] = { "LISTXATTR" }, 29 | [FUSE_REMOVEXATTR] = { "REMOVEXATTR" }, 30 | [FUSE_FLUSH] = { "FLUSH" }, 31 | [FUSE_INIT] = { "INIT" }, 32 | [FUSE_OPENDIR] = { "OPENDIR" }, 33 | [FUSE_READDIR] = { "READDIR" }, 34 | [FUSE_RELEASEDIR] = { "RELEASEDIR" }, 35 | [FUSE_FSYNCDIR] = { "FSYNCDIR" }, 36 | [FUSE_GETLK] = { "GETLK" }, 37 | [FUSE_SETLK] = { "SETLK" }, 38 | [FUSE_SETLKW] = { "SETLKW" }, 39 | [FUSE_ACCESS] = { "ACCESS" }, 40 | [FUSE_CREATE] = { "CREATE" }, 41 | [FUSE_INTERRUPT] = { "INTERRUPT" }, 42 | [FUSE_BMAP] = { "BMAP" }, 43 | [FUSE_DESTROY] = { "DESTROY" }, 44 | }; 45 | 46 | #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0])) 47 | 48 | static const char *opname(enum fuse_opcode opcode) 49 | { 50 | if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name) 51 | return "???"; 52 | else 53 | return fuse_ll_ops[opcode].name; 54 | } 55 | 56 | 57 | static void process_buf(int dir, char *buf, int len) 58 | { 59 | static unsigned long long prevuniq = -1; 60 | static int prevopcode; 61 | 62 | if (!dir) { 63 | struct fuse_in_header *in = (struct fuse_in_header *) buf; 64 | buf += sizeof(struct fuse_in_header); 65 | 66 | printf("unique: %llu, opcode: %s (%i), nodeid: %lu, len: %i, insize: %i\n", 67 | (unsigned long long) in->unique, 68 | opname((enum fuse_opcode) in->opcode), in->opcode, 69 | (unsigned long) in->nodeid, in->len, len); 70 | 71 | switch (in->opcode) { 72 | case FUSE_READ: { 73 | struct fuse_read_in *arg = (struct fuse_read_in *) buf; 74 | printf("-READ fh:%llu off:%llu siz:%u rfl:%u own:%llu fl:%u\n", 75 | arg->fh, arg->offset, arg->size, arg->read_flags, 76 | arg->lock_owner, arg->flags); 77 | break; 78 | } 79 | case FUSE_WRITE: { 80 | struct fuse_write_in *arg = (struct fuse_write_in *) buf; 81 | printf("-WRITE fh:%llu off:%llu siz:%u wfl:%u own:%llu fl:%u\n", 82 | arg->fh, arg->offset, arg->size, arg->write_flags, 83 | arg->lock_owner, arg->flags); 84 | break; 85 | } 86 | } 87 | prevuniq = in->unique; 88 | prevopcode = in->opcode; 89 | } else { 90 | struct fuse_out_header *out = (struct fuse_out_header *) buf; 91 | buf += sizeof(struct fuse_out_header); 92 | 93 | printf(" unique: %llu, error: %i (%s), len: %i, outsize: %i\n", 94 | (unsigned long long) out->unique, out->error, 95 | strerror(-out->error), out->len, len); 96 | 97 | if (out->unique == prevuniq) { 98 | switch (prevopcode) { 99 | case FUSE_GETATTR: { 100 | struct fuse_attr_out *arg = (struct fuse_attr_out *) buf; 101 | printf("+ATTR v:%llu.%09u i:%llu s:%llu b:%llu\n", 102 | arg->attr_valid, arg->attr_valid_nsec, 103 | arg->attr.ino, arg->attr.size, arg->attr.blocks); 104 | break; 105 | } 106 | case FUSE_LOOKUP: { 107 | struct fuse_entry_out *arg = (struct fuse_entry_out *) buf; 108 | printf("+ENTRY nodeid:%llu v:%llu.%09u i:%llu s:%llu b:%llu\n", 109 | arg->nodeid, arg->attr_valid, arg->attr_valid_nsec, 110 | arg->attr.ino, arg->attr.size, arg->attr.blocks); 111 | break; 112 | } 113 | } 114 | } 115 | } 116 | 117 | } 118 | 119 | int main(void) 120 | { 121 | FILE *in = stdin; 122 | while (1) { 123 | int dir; 124 | int res; 125 | char buf[1048576]; 126 | unsigned len = 0; 127 | 128 | memset(buf, 0, sizeof(buf)); 129 | while (1) { 130 | char str[32]; 131 | 132 | res = fscanf(in, "%30s", str); 133 | if (res != 1 && feof(in)) 134 | return 0; 135 | 136 | if (res == 0) 137 | continue; 138 | 139 | if (strncmp(str, "read(", 5) == 0) { 140 | dir = 0; 141 | break; 142 | } else if (strncmp(str, "writev(", 7) == 0) { 143 | dir = 1; 144 | break; 145 | } 146 | } 147 | 148 | while (1) { 149 | int c = getc(in); 150 | if (c == '"') { 151 | while (1) { 152 | int val; 153 | 154 | c = getc(in); 155 | if (c == EOF) { 156 | fprintf(stderr, "eof in string\n"); 157 | break; 158 | } 159 | if (c == '\n') { 160 | fprintf(stderr, "eol in string\n"); 161 | break; 162 | } 163 | if (c == '"') 164 | break; 165 | if (c != '\\') { 166 | val = c; 167 | } else { 168 | c = getc(in); 169 | switch (c) { 170 | case 'n': val = '\n'; break; 171 | case 'r': val = '\r'; break; 172 | case 't': val = '\t'; break; 173 | case '"': val = '"'; break; 174 | case '\\': val = '\\'; break; 175 | case 'x': 176 | res = scanf("%x", &val); 177 | if (res != 1) { 178 | fprintf(stderr, "parse error\n"); 179 | continue; 180 | } 181 | break; 182 | default: 183 | fprintf(stderr, "unknown sequence: '\\%c'\n", c); 184 | continue; 185 | } 186 | } 187 | buf[len++] = val; 188 | } 189 | } 190 | if (c == '\n') 191 | break; 192 | } 193 | process_buf(dir, buf, len); 194 | memset(buf, 0, len); 195 | len = 0; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /example/fsel.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE fsel: FUSE select example 3 | Copyright (C) 2008 SUSE Linux Products GmbH 4 | Copyright (C) 2008 Tejun Heo 5 | 6 | This program can be distributed under the terms of the GNU GPL. 7 | See the file COPYING. 8 | 9 | gcc -Wall fsel.c `pkg-config fuse --cflags --libs` -o fsel 10 | */ 11 | 12 | #define FUSE_USE_VERSION 29 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | /* 26 | * fsel_open_mask is used to limit the number of opens to 1 per file. 27 | * This is to use file index (0-F) as fh as poll support requires 28 | * unique fh per open file. Lifting this would require proper open 29 | * file management. 30 | */ 31 | static unsigned fsel_open_mask; 32 | static const char fsel_hex_map[] = "0123456789ABCDEF"; 33 | static struct fuse *fsel_fuse; /* needed for poll notification */ 34 | 35 | #define FSEL_CNT_MAX 10 /* each file can store upto 10 chars */ 36 | #define FSEL_FILES 16 37 | 38 | static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */ 39 | static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */ 40 | static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */ 41 | static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */ 42 | 43 | static int fsel_path_index(const char *path) 44 | { 45 | char ch = path[1]; 46 | 47 | if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch)) 48 | return -1; 49 | return ch <= '9' ? ch - '0' : ch - 'A' + 10; 50 | } 51 | 52 | static int fsel_getattr(const char *path, struct stat *stbuf) 53 | { 54 | int idx; 55 | 56 | memset(stbuf, 0, sizeof(struct stat)); 57 | 58 | if (strcmp(path, "/") == 0) { 59 | stbuf->st_mode = S_IFDIR | 0555; 60 | stbuf->st_nlink = 2; 61 | return 0; 62 | } 63 | 64 | idx = fsel_path_index(path); 65 | if (idx < 0) 66 | return -ENOENT; 67 | 68 | stbuf->st_mode = S_IFREG | 0444; 69 | stbuf->st_nlink = 1; 70 | stbuf->st_size = fsel_cnt[idx]; 71 | return 0; 72 | } 73 | 74 | static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler, 75 | off_t offset, struct fuse_file_info *fi) 76 | { 77 | char name[2] = { }; 78 | int i; 79 | 80 | (void) offset; 81 | (void) fi; 82 | 83 | if (strcmp(path, "/") != 0) 84 | return -ENOENT; 85 | 86 | for (i = 0; i < FSEL_FILES; i++) { 87 | name[0] = fsel_hex_map[i]; 88 | filler(buf, name, NULL, 0); 89 | } 90 | 91 | return 0; 92 | } 93 | 94 | static int fsel_open(const char *path, struct fuse_file_info *fi) 95 | { 96 | int idx = fsel_path_index(path); 97 | 98 | if (idx < 0) 99 | return -ENOENT; 100 | if ((fi->flags & 3) != O_RDONLY) 101 | return -EACCES; 102 | if (fsel_open_mask & (1 << idx)) 103 | return -EBUSY; 104 | fsel_open_mask |= (1 << idx); 105 | 106 | /* 107 | * fsel files are nonseekable somewhat pipe-like files which 108 | * gets filled up periodically by producer thread and consumed 109 | * on read. Tell FUSE as such. 110 | */ 111 | fi->fh = idx; 112 | fi->direct_io = 1; 113 | fi->nonseekable = 1; 114 | 115 | return 0; 116 | } 117 | 118 | static int fsel_release(const char *path, struct fuse_file_info *fi) 119 | { 120 | int idx = fi->fh; 121 | 122 | (void) path; 123 | 124 | fsel_open_mask &= ~(1 << idx); 125 | return 0; 126 | } 127 | 128 | static int fsel_read(const char *path, char *buf, size_t size, off_t offset, 129 | struct fuse_file_info *fi) 130 | { 131 | int idx = fi->fh; 132 | 133 | (void) path; 134 | (void) offset; 135 | 136 | pthread_mutex_lock(&fsel_mutex); 137 | if (fsel_cnt[idx] < size) 138 | size = fsel_cnt[idx]; 139 | printf("READ %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]); 140 | fsel_cnt[idx] -= size; 141 | pthread_mutex_unlock(&fsel_mutex); 142 | 143 | memset(buf, fsel_hex_map[idx], size); 144 | return size; 145 | } 146 | 147 | static int fsel_poll(const char *path, struct fuse_file_info *fi, 148 | struct fuse_pollhandle *ph, unsigned *reventsp) 149 | { 150 | static unsigned polled_zero; 151 | int idx = fi->fh; 152 | 153 | (void) path; 154 | 155 | /* 156 | * Poll notification requires pointer to struct fuse which 157 | * can't be obtained when using fuse_main(). As notification 158 | * happens only after poll is called, fill it here from 159 | * fuse_context. 160 | */ 161 | if (!fsel_fuse) { 162 | struct fuse_context *cxt = fuse_get_context(); 163 | if (cxt) 164 | fsel_fuse = cxt->fuse; 165 | } 166 | 167 | pthread_mutex_lock(&fsel_mutex); 168 | 169 | if (ph != NULL) { 170 | struct fuse_pollhandle *oldph = fsel_poll_handle[idx]; 171 | 172 | if (oldph) 173 | fuse_pollhandle_destroy(oldph); 174 | 175 | fsel_poll_notify_mask |= (1 << idx); 176 | fsel_poll_handle[idx] = ph; 177 | } 178 | 179 | if (fsel_cnt[idx]) { 180 | *reventsp |= POLLIN; 181 | printf("POLL %X cnt=%u polled_zero=%u\n", 182 | idx, fsel_cnt[idx], polled_zero); 183 | polled_zero = 0; 184 | } else 185 | polled_zero++; 186 | 187 | pthread_mutex_unlock(&fsel_mutex); 188 | return 0; 189 | } 190 | 191 | static struct fuse_operations fsel_oper = { 192 | .getattr = fsel_getattr, 193 | .readdir = fsel_readdir, 194 | .open = fsel_open, 195 | .release = fsel_release, 196 | .read = fsel_read, 197 | .poll = fsel_poll, 198 | }; 199 | 200 | static void *fsel_producer(void *data) 201 | { 202 | const struct timespec interval = { 0, 250000000 }; 203 | unsigned idx = 0, nr = 1; 204 | 205 | (void) data; 206 | 207 | while (1) { 208 | int i, t; 209 | 210 | pthread_mutex_lock(&fsel_mutex); 211 | 212 | /* 213 | * This is the main producer loop which is executed 214 | * ever 500ms. On each iteration, it fills one byte 215 | * to 1, 2 or 4 files and sends poll notification if 216 | * requested. 217 | */ 218 | for (i = 0, t = idx; i < nr; 219 | i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) { 220 | if (fsel_cnt[t] == FSEL_CNT_MAX) 221 | continue; 222 | 223 | fsel_cnt[t]++; 224 | if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) { 225 | struct fuse_pollhandle *ph; 226 | 227 | printf("NOTIFY %X\n", t); 228 | ph = fsel_poll_handle[t]; 229 | fuse_notify_poll(ph); 230 | fuse_pollhandle_destroy(ph); 231 | fsel_poll_notify_mask &= ~(1 << t); 232 | fsel_poll_handle[t] = NULL; 233 | } 234 | } 235 | 236 | idx = (idx + 1) % FSEL_FILES; 237 | if (idx == 0) 238 | nr = (nr * 2) % 7; /* cycle through 1, 2 and 4 */ 239 | 240 | pthread_mutex_unlock(&fsel_mutex); 241 | 242 | nanosleep(&interval, NULL); 243 | } 244 | 245 | return NULL; 246 | } 247 | 248 | int main(int argc, char *argv[]) 249 | { 250 | pthread_t producer; 251 | pthread_attr_t attr; 252 | int ret; 253 | 254 | errno = pthread_mutex_init(&fsel_mutex, NULL); 255 | if (errno) { 256 | perror("pthread_mutex_init"); 257 | return 1; 258 | } 259 | 260 | errno = pthread_attr_init(&attr); 261 | if (errno) { 262 | perror("pthread_attr_init"); 263 | return 1; 264 | } 265 | 266 | errno = pthread_create(&producer, &attr, fsel_producer, NULL); 267 | if (errno) { 268 | perror("pthread_create"); 269 | return 1; 270 | } 271 | 272 | ret = fuse_main(argc, argv, &fsel_oper, NULL); 273 | 274 | pthread_cancel(producer); 275 | pthread_join(producer, NULL); 276 | 277 | return ret; 278 | } 279 | -------------------------------------------------------------------------------- /include/fuse_lowlevel_compat.h: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU LGPLv2. 6 | See the file COPYING.LIB. 7 | */ 8 | 9 | /* these definitions provide source compatibility to prior versions. 10 | Do not include this file directly! */ 11 | 12 | #include "fuse_common.h" 13 | 14 | struct fuse_lowlevel_ops_compat25 { 15 | void (*init) (void *userdata); 16 | void (*destroy) (void *userdata); 17 | void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name); 18 | void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup); 19 | void (*getattr) (fuse_req_t req, fuse_ino_t ino, 20 | struct fuse_file_info *fi); 21 | void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr, 22 | int to_set, struct fuse_file_info *fi); 23 | void (*readlink) (fuse_req_t req, fuse_ino_t ino); 24 | void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name, 25 | mode_t mode, dev_t rdev); 26 | void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name, 27 | mode_t mode); 28 | void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name); 29 | void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name); 30 | void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent, 31 | const char *name); 32 | void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name, 33 | fuse_ino_t newparent, const char *newname); 34 | void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, 35 | const char *newname); 36 | void (*open) (fuse_req_t req, fuse_ino_t ino, 37 | struct fuse_file_info *fi); 38 | void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, loff_t off, 39 | struct fuse_file_info *fi); 40 | void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf, 41 | size_t size, loff_t off, struct fuse_file_info *fi); 42 | void (*flush) (fuse_req_t req, fuse_ino_t ino, 43 | struct fuse_file_info *fi); 44 | void (*release) (fuse_req_t req, fuse_ino_t ino, 45 | struct fuse_file_info *fi); 46 | void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync, 47 | struct fuse_file_info *fi); 48 | void (*opendir) (fuse_req_t req, fuse_ino_t ino, 49 | struct fuse_file_info *fi); 50 | void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, loff_t off, 51 | struct fuse_file_info *fi); 52 | void (*releasedir) (fuse_req_t req, fuse_ino_t ino, 53 | struct fuse_file_info *fi); 54 | void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync, 55 | struct fuse_file_info *fi); 56 | void (*statfs) (fuse_req_t req); 57 | void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, 58 | const char *value, size_t size, int flags); 59 | void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, 60 | size_t size); 61 | void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size); 62 | void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name); 63 | void (*access) (fuse_req_t req, fuse_ino_t ino, int mask); 64 | void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name, 65 | mode_t mode, struct fuse_file_info *fi); 66 | }; 67 | 68 | struct fuse_session *fuse_lowlevel_new_compat25(struct fuse_args *args, 69 | const struct fuse_lowlevel_ops_compat25 *op, 70 | size_t op_size, void *userdata); 71 | 72 | size_t fuse_dirent_size(size_t namelen); 73 | 74 | char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf, 75 | loff_t off); 76 | 77 | #if !defined(__FreeBSD__) && !defined(__NetBSD__) 78 | 79 | #include 80 | 81 | struct fuse_lowlevel_ops_compat { 82 | void (*init) (void *userdata); 83 | void (*destroy) (void *userdata); 84 | void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name); 85 | void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup); 86 | void (*getattr) (fuse_req_t req, fuse_ino_t ino, 87 | struct fuse_file_info_compat *fi); 88 | void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr, 89 | int to_set, struct fuse_file_info_compat *fi); 90 | void (*readlink) (fuse_req_t req, fuse_ino_t ino); 91 | void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name, 92 | mode_t mode, dev_t rdev); 93 | void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name, 94 | mode_t mode); 95 | void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name); 96 | void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name); 97 | void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent, 98 | const char *name); 99 | void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name, 100 | fuse_ino_t newparent, const char *newname); 101 | void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, 102 | const char *newname); 103 | void (*open) (fuse_req_t req, fuse_ino_t ino, 104 | struct fuse_file_info_compat *fi); 105 | void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, loff_t off, 106 | struct fuse_file_info_compat *fi); 107 | void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf, 108 | size_t size, loff_t off, struct fuse_file_info_compat *fi); 109 | void (*flush) (fuse_req_t req, fuse_ino_t ino, 110 | struct fuse_file_info_compat *fi); 111 | void (*release) (fuse_req_t req, fuse_ino_t ino, 112 | struct fuse_file_info_compat *fi); 113 | void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync, 114 | struct fuse_file_info_compat *fi); 115 | void (*opendir) (fuse_req_t req, fuse_ino_t ino, 116 | struct fuse_file_info_compat *fi); 117 | void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, loff_t off, 118 | struct fuse_file_info_compat *fi); 119 | void (*releasedir) (fuse_req_t req, fuse_ino_t ino, 120 | struct fuse_file_info_compat *fi); 121 | void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync, 122 | struct fuse_file_info_compat *fi); 123 | void (*statfs) (fuse_req_t req); 124 | void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, 125 | const char *value, size_t size, int flags); 126 | void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, 127 | size_t size); 128 | void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size); 129 | void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name); 130 | void (*access) (fuse_req_t req, fuse_ino_t ino, int mask); 131 | void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name, 132 | mode_t mode, struct fuse_file_info_compat *fi); 133 | }; 134 | 135 | int fuse_reply_statfs_compat(fuse_req_t req, const struct statfs *stbuf); 136 | 137 | int fuse_reply_open_compat(fuse_req_t req, 138 | const struct fuse_file_info_compat *fi); 139 | 140 | struct fuse_session *fuse_lowlevel_new_compat(const char *opts, 141 | const struct fuse_lowlevel_ops_compat *op, 142 | size_t op_size, void *userdata); 143 | 144 | #endif /* __FreeBSD__ || __NetBSD__ */ 145 | 146 | struct fuse_chan_ops_compat24 { 147 | int (*receive)(struct fuse_chan *ch, char *buf, size_t size); 148 | int (*send)(struct fuse_chan *ch, const struct iovec iov[], 149 | size_t count); 150 | void (*destroy)(struct fuse_chan *ch); 151 | }; 152 | 153 | struct fuse_chan *fuse_chan_new_compat24(struct fuse_chan_ops_compat24 *op, 154 | int fd, size_t bufsize, void *data); 155 | 156 | int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size); 157 | struct fuse_chan *fuse_kern_chan_new(int fd); 158 | -------------------------------------------------------------------------------- /lib/fuse_loop_mt.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU LGPLv2. 6 | See the file COPYING.LIB. 7 | */ 8 | 9 | #include "fuse_lowlevel.h" 10 | #include "fuse_misc.h" 11 | #include "fuse_kernel.h" 12 | #include "fuse_i.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | /* Environment var controlling the thread stack size */ 24 | #define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK" 25 | 26 | struct fuse_worker { 27 | struct fuse_worker *prev; 28 | struct fuse_worker *next; 29 | pthread_t thread_id; 30 | size_t bufsize; 31 | char *buf; 32 | struct fuse_mt *mt; 33 | }; 34 | 35 | struct fuse_mt { 36 | pthread_mutex_t lock; 37 | int numworker; 38 | int numavail; 39 | struct fuse_session *se; 40 | struct fuse_chan *prevch; 41 | struct fuse_worker main; 42 | sem_t finish; 43 | int exit; 44 | int error; 45 | }; 46 | 47 | static void list_add_worker(struct fuse_worker *w, struct fuse_worker *next) 48 | { 49 | struct fuse_worker *prev = next->prev; 50 | w->next = next; 51 | w->prev = prev; 52 | prev->next = w; 53 | next->prev = w; 54 | } 55 | 56 | static void list_del_worker(struct fuse_worker *w) 57 | { 58 | struct fuse_worker *prev = w->prev; 59 | struct fuse_worker *next = w->next; 60 | prev->next = next; 61 | next->prev = prev; 62 | } 63 | 64 | static int fuse_loop_start_thread(struct fuse_mt *mt); 65 | 66 | static void thread_exit_handler(int sig __unused) 67 | { 68 | pthread_exit(0); 69 | } 70 | 71 | static void *fuse_do_work(void *data) 72 | { 73 | struct fuse_worker *w = (struct fuse_worker *) data; 74 | struct fuse_mt *mt = w->mt; 75 | 76 | #if defined(__ANDROID__) 77 | struct sigaction actions; 78 | memset(&actions, 0, sizeof(actions)); 79 | sigemptyset(&actions.sa_mask); 80 | actions.sa_flags = 0; 81 | actions.sa_handler = thread_exit_handler; 82 | sigaction(SIGUSR1, &actions, NULL); 83 | 84 | sigset_t setusr1; 85 | sigemptyset(&setusr1); 86 | sigaddset(&setusr1, SIGUSR1); 87 | pthread_sigmask(SIG_BLOCK, &setusr1, NULL); 88 | #endif 89 | 90 | while (!fuse_session_exited(mt->se)) { 91 | int isforget = 0; 92 | struct fuse_chan *ch = mt->prevch; 93 | struct fuse_buf fbuf = { 94 | .mem = w->buf, 95 | .size = w->bufsize, 96 | }; 97 | int res; 98 | 99 | #if defined(__ANDROID__) 100 | pthread_sigmask(SIG_UNBLOCK, &setusr1, NULL); 101 | #else 102 | pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 103 | #endif 104 | res = fuse_session_receive_buf(mt->se, &fbuf, &ch); 105 | #if defined(__ANDROID__) 106 | pthread_sigmask(SIG_BLOCK, &setusr1, NULL); 107 | #else 108 | pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); 109 | #endif 110 | if (res == -EINTR) 111 | continue; 112 | if (res <= 0) { 113 | if (res < 0) { 114 | fuse_session_exit(mt->se); 115 | mt->error = -1; 116 | } 117 | break; 118 | } 119 | 120 | pthread_mutex_lock(&mt->lock); 121 | if (mt->exit) { 122 | pthread_mutex_unlock(&mt->lock); 123 | return NULL; 124 | } 125 | 126 | /* 127 | * This disgusting hack is needed so that zillions of threads 128 | * are not created on a burst of FORGET messages 129 | */ 130 | if (!(fbuf.flags & FUSE_BUF_IS_FD)) { 131 | struct fuse_in_header *in = fbuf.mem; 132 | 133 | if (in->opcode == FUSE_FORGET || 134 | in->opcode == FUSE_BATCH_FORGET) 135 | isforget = 1; 136 | } 137 | 138 | if (!isforget) 139 | mt->numavail--; 140 | if (mt->numavail == 0) 141 | fuse_loop_start_thread(mt); 142 | pthread_mutex_unlock(&mt->lock); 143 | 144 | fuse_session_process_buf(mt->se, &fbuf, ch); 145 | 146 | pthread_mutex_lock(&mt->lock); 147 | if (!isforget) 148 | mt->numavail++; 149 | if (mt->numavail > 10) { 150 | if (mt->exit) { 151 | pthread_mutex_unlock(&mt->lock); 152 | return NULL; 153 | } 154 | list_del_worker(w); 155 | mt->numavail--; 156 | mt->numworker--; 157 | pthread_mutex_unlock(&mt->lock); 158 | 159 | pthread_detach(w->thread_id); 160 | free(w->buf); 161 | free(w); 162 | return NULL; 163 | } 164 | pthread_mutex_unlock(&mt->lock); 165 | } 166 | 167 | sem_post(&mt->finish); 168 | 169 | return NULL; 170 | } 171 | 172 | int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg) 173 | { 174 | sigset_t oldset; 175 | sigset_t newset; 176 | int res; 177 | pthread_attr_t attr; 178 | char *stack_size; 179 | 180 | /* Override default stack size */ 181 | pthread_attr_init(&attr); 182 | stack_size = getenv(ENVNAME_THREAD_STACK); 183 | if (stack_size && pthread_attr_setstacksize(&attr, atoi(stack_size))) 184 | fprintf(stderr, "fuse: invalid stack size: %s\n", stack_size); 185 | 186 | /* Disallow signal reception in worker threads */ 187 | sigemptyset(&newset); 188 | sigaddset(&newset, SIGTERM); 189 | sigaddset(&newset, SIGINT); 190 | sigaddset(&newset, SIGHUP); 191 | sigaddset(&newset, SIGQUIT); 192 | pthread_sigmask(SIG_BLOCK, &newset, &oldset); 193 | res = pthread_create(thread_id, &attr, func, arg); 194 | pthread_sigmask(SIG_SETMASK, &oldset, NULL); 195 | pthread_attr_destroy(&attr); 196 | if (res != 0) { 197 | fprintf(stderr, "fuse: error creating thread: %s\n", 198 | strerror(res)); 199 | return -1; 200 | } 201 | 202 | return 0; 203 | } 204 | 205 | static int fuse_loop_start_thread(struct fuse_mt *mt) 206 | { 207 | int res; 208 | struct fuse_worker *w = malloc(sizeof(struct fuse_worker)); 209 | if (!w) { 210 | fprintf(stderr, "fuse: failed to allocate worker structure\n"); 211 | return -1; 212 | } 213 | memset(w, 0, sizeof(struct fuse_worker)); 214 | w->bufsize = fuse_chan_bufsize(mt->prevch); 215 | w->buf = malloc(w->bufsize); 216 | w->mt = mt; 217 | if (!w->buf) { 218 | fprintf(stderr, "fuse: failed to allocate read buffer\n"); 219 | free(w); 220 | return -1; 221 | } 222 | 223 | res = fuse_start_thread(&w->thread_id, fuse_do_work, w); 224 | if (res == -1) { 225 | free(w->buf); 226 | free(w); 227 | return -1; 228 | } 229 | list_add_worker(w, &mt->main); 230 | mt->numavail ++; 231 | mt->numworker ++; 232 | 233 | return 0; 234 | } 235 | 236 | static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w) 237 | { 238 | pthread_join(w->thread_id, NULL); 239 | pthread_mutex_lock(&mt->lock); 240 | list_del_worker(w); 241 | pthread_mutex_unlock(&mt->lock); 242 | free(w->buf); 243 | free(w); 244 | } 245 | 246 | int fuse_session_loop_mt(struct fuse_session *se) 247 | { 248 | int err; 249 | struct fuse_mt mt; 250 | struct fuse_worker *w; 251 | 252 | memset(&mt, 0, sizeof(struct fuse_mt)); 253 | mt.se = se; 254 | mt.prevch = fuse_session_next_chan(se, NULL); 255 | mt.error = 0; 256 | mt.numworker = 0; 257 | mt.numavail = 0; 258 | mt.main.thread_id = pthread_self(); 259 | mt.main.prev = mt.main.next = &mt.main; 260 | sem_init(&mt.finish, 0, 0); 261 | fuse_mutex_init(&mt.lock); 262 | 263 | pthread_mutex_lock(&mt.lock); 264 | err = fuse_loop_start_thread(&mt); 265 | pthread_mutex_unlock(&mt.lock); 266 | if (!err) { 267 | /* sem_wait() is interruptible */ 268 | while (!fuse_session_exited(se)) 269 | sem_wait(&mt.finish); 270 | 271 | pthread_mutex_lock(&mt.lock); 272 | for (w = mt.main.next; w != &mt.main; w = w->next) 273 | #if defined(__ANDROID__) 274 | pthread_kill(w->thread_id, SIGUSR1); 275 | #else 276 | pthread_cancel(w->thread_id); 277 | #endif 278 | mt.exit = 1; 279 | pthread_mutex_unlock(&mt.lock); 280 | 281 | while (mt.main.next != &mt.main) 282 | fuse_join_worker(&mt, mt.main.next); 283 | 284 | err = mt.error; 285 | } 286 | 287 | pthread_mutex_destroy(&mt.lock); 288 | sem_destroy(&mt.finish); 289 | fuse_session_reset(se); 290 | return err; 291 | } 292 | -------------------------------------------------------------------------------- /example/cusexmp.c: -------------------------------------------------------------------------------- 1 | /* 2 | CUSE example: Character device in Userspace 3 | Copyright (C) 2008-2009 SUSE Linux Products GmbH 4 | Copyright (C) 2008-2009 Tejun Heo 5 | 6 | This program can be distributed under the terms of the GNU GPL. 7 | See the file COPYING. 8 | 9 | gcc -Wall cusexmp.c `pkg-config fuse --cflags --libs` -o cusexmp 10 | */ 11 | 12 | #define FUSE_USE_VERSION 29 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "fioc.h" 24 | 25 | static void *cusexmp_buf; 26 | static size_t cusexmp_size; 27 | 28 | static const char *usage = 29 | "usage: cusexmp [options]\n" 30 | "\n" 31 | "options:\n" 32 | " --help|-h print this help message\n" 33 | " --maj=MAJ|-M MAJ device major number\n" 34 | " --min=MIN|-m MIN device minor number\n" 35 | " --name=NAME|-n NAME device name (mandatory)\n" 36 | "\n"; 37 | 38 | static int cusexmp_resize(size_t new_size) 39 | { 40 | void *new_buf; 41 | 42 | if (new_size == cusexmp_size) 43 | return 0; 44 | 45 | new_buf = realloc(cusexmp_buf, new_size); 46 | if (!new_buf && new_size) 47 | return -ENOMEM; 48 | 49 | if (new_size > cusexmp_size) 50 | memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size); 51 | 52 | cusexmp_buf = new_buf; 53 | cusexmp_size = new_size; 54 | 55 | return 0; 56 | } 57 | 58 | static int cusexmp_expand(size_t new_size) 59 | { 60 | if (new_size > cusexmp_size) 61 | return cusexmp_resize(new_size); 62 | return 0; 63 | } 64 | 65 | static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi) 66 | { 67 | fuse_reply_open(req, fi); 68 | } 69 | 70 | static void cusexmp_read(fuse_req_t req, size_t size, off_t off, 71 | struct fuse_file_info *fi) 72 | { 73 | (void)fi; 74 | 75 | if (off >= cusexmp_size) 76 | off = cusexmp_size; 77 | if (size > cusexmp_size - off) 78 | size = cusexmp_size - off; 79 | 80 | fuse_reply_buf(req, cusexmp_buf + off, size); 81 | } 82 | 83 | static void cusexmp_write(fuse_req_t req, const char *buf, size_t size, 84 | off_t off, struct fuse_file_info *fi) 85 | { 86 | (void)fi; 87 | 88 | if (cusexmp_expand(off + size)) { 89 | fuse_reply_err(req, ENOMEM); 90 | return; 91 | } 92 | 93 | memcpy(cusexmp_buf + off, buf, size); 94 | fuse_reply_write(req, size); 95 | } 96 | 97 | static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf, 98 | size_t in_bufsz, size_t out_bufsz, int is_read) 99 | { 100 | const struct fioc_rw_arg *arg; 101 | struct iovec in_iov[2], out_iov[3], iov[3]; 102 | size_t cur_size; 103 | 104 | /* read in arg */ 105 | in_iov[0].iov_base = addr; 106 | in_iov[0].iov_len = sizeof(*arg); 107 | if (!in_bufsz) { 108 | fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0); 109 | return; 110 | } 111 | arg = in_buf; 112 | in_buf += sizeof(*arg); 113 | in_bufsz -= sizeof(*arg); 114 | 115 | /* prepare size outputs */ 116 | out_iov[0].iov_base = 117 | addr + (unsigned long)&(((struct fioc_rw_arg *)0)->prev_size); 118 | out_iov[0].iov_len = sizeof(arg->prev_size); 119 | 120 | out_iov[1].iov_base = 121 | addr + (unsigned long)&(((struct fioc_rw_arg *)0)->new_size); 122 | out_iov[1].iov_len = sizeof(arg->new_size); 123 | 124 | /* prepare client buf */ 125 | if (is_read) { 126 | out_iov[2].iov_base = arg->buf; 127 | out_iov[2].iov_len = arg->size; 128 | if (!out_bufsz) { 129 | fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3); 130 | return; 131 | } 132 | } else { 133 | in_iov[1].iov_base = arg->buf; 134 | in_iov[1].iov_len = arg->size; 135 | if (arg->size && !in_bufsz) { 136 | fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2); 137 | return; 138 | } 139 | } 140 | 141 | /* we're all set */ 142 | cur_size = cusexmp_size; 143 | iov[0].iov_base = &cur_size; 144 | iov[0].iov_len = sizeof(cur_size); 145 | 146 | iov[1].iov_base = &cusexmp_size; 147 | iov[1].iov_len = sizeof(cusexmp_size); 148 | 149 | if (is_read) { 150 | size_t off = arg->offset; 151 | size_t size = arg->size; 152 | 153 | if (off >= cusexmp_size) 154 | off = cusexmp_size; 155 | if (size > cusexmp_size - off) 156 | size = cusexmp_size - off; 157 | 158 | iov[2].iov_base = cusexmp_buf + off; 159 | iov[2].iov_len = size; 160 | fuse_reply_ioctl_iov(req, size, iov, 3); 161 | } else { 162 | if (cusexmp_expand(arg->offset + in_bufsz)) { 163 | fuse_reply_err(req, ENOMEM); 164 | return; 165 | } 166 | 167 | memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz); 168 | fuse_reply_ioctl_iov(req, in_bufsz, iov, 2); 169 | } 170 | } 171 | 172 | static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg, 173 | struct fuse_file_info *fi, unsigned flags, 174 | const void *in_buf, size_t in_bufsz, size_t out_bufsz) 175 | { 176 | int is_read = 0; 177 | 178 | (void)fi; 179 | 180 | if (flags & FUSE_IOCTL_COMPAT) { 181 | fuse_reply_err(req, ENOSYS); 182 | return; 183 | } 184 | 185 | switch (cmd) { 186 | case FIOC_GET_SIZE: 187 | if (!out_bufsz) { 188 | struct iovec iov = { arg, sizeof(size_t) }; 189 | 190 | fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1); 191 | } else 192 | fuse_reply_ioctl(req, 0, &cusexmp_size, 193 | sizeof(cusexmp_size)); 194 | break; 195 | 196 | case FIOC_SET_SIZE: 197 | if (!in_bufsz) { 198 | struct iovec iov = { arg, sizeof(size_t) }; 199 | 200 | fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0); 201 | } else { 202 | cusexmp_resize(*(size_t *)in_buf); 203 | fuse_reply_ioctl(req, 0, NULL, 0); 204 | } 205 | break; 206 | 207 | case FIOC_READ: 208 | is_read = 1; 209 | case FIOC_WRITE: 210 | fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read); 211 | break; 212 | 213 | default: 214 | fuse_reply_err(req, EINVAL); 215 | } 216 | } 217 | 218 | struct cusexmp_param { 219 | unsigned major; 220 | unsigned minor; 221 | char *dev_name; 222 | int is_help; 223 | }; 224 | 225 | #define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 } 226 | 227 | static const struct fuse_opt cusexmp_opts[] = { 228 | CUSEXMP_OPT("-M %u", major), 229 | CUSEXMP_OPT("--maj=%u", major), 230 | CUSEXMP_OPT("-m %u", minor), 231 | CUSEXMP_OPT("--min=%u", minor), 232 | CUSEXMP_OPT("-n %s", dev_name), 233 | CUSEXMP_OPT("--name=%s", dev_name), 234 | FUSE_OPT_KEY("-h", 0), 235 | FUSE_OPT_KEY("--help", 0), 236 | FUSE_OPT_END 237 | }; 238 | 239 | static int cusexmp_process_arg(void *data, const char *arg, int key, 240 | struct fuse_args *outargs) 241 | { 242 | struct cusexmp_param *param = data; 243 | 244 | (void)outargs; 245 | (void)arg; 246 | 247 | switch (key) { 248 | case 0: 249 | param->is_help = 1; 250 | fprintf(stderr, "%s", usage); 251 | return fuse_opt_add_arg(outargs, "-ho"); 252 | default: 253 | return 1; 254 | } 255 | } 256 | 257 | static const struct cuse_lowlevel_ops cusexmp_clop = { 258 | .open = cusexmp_open, 259 | .read = cusexmp_read, 260 | .write = cusexmp_write, 261 | .ioctl = cusexmp_ioctl, 262 | }; 263 | 264 | int main(int argc, char **argv) 265 | { 266 | struct fuse_args args = FUSE_ARGS_INIT(argc, argv); 267 | struct cusexmp_param param = { 0, 0, NULL, 0 }; 268 | char dev_name[128] = "DEVNAME="; 269 | const char *dev_info_argv[] = { dev_name }; 270 | struct cuse_info ci; 271 | 272 | if (fuse_opt_parse(&args, ¶m, cusexmp_opts, cusexmp_process_arg)) { 273 | printf("failed to parse option\n"); 274 | return 1; 275 | } 276 | 277 | if (!param.is_help) { 278 | if (!param.dev_name) { 279 | fprintf(stderr, "Error: device name missing\n"); 280 | return 1; 281 | } 282 | strncat(dev_name, param.dev_name, sizeof(dev_name) - 9); 283 | } 284 | 285 | memset(&ci, 0, sizeof(ci)); 286 | ci.dev_major = param.major; 287 | ci.dev_minor = param.minor; 288 | ci.dev_info_argc = 1; 289 | ci.dev_info_argv = dev_info_argv; 290 | ci.flags = CUSE_UNRESTRICTED_IOCTL; 291 | 292 | return cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop, 293 | NULL); 294 | } 295 | -------------------------------------------------------------------------------- /lib/buffer.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2010 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU LGPLv2. 6 | See the file COPYING.LIB 7 | */ 8 | 9 | #define _GNU_SOURCE 10 | 11 | #include "config.h" 12 | #include "fuse_i.h" 13 | #include "fuse_lowlevel.h" 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | size_t fuse_buf_size(const struct fuse_bufvec *bufv) 20 | { 21 | size_t i; 22 | size_t size = 0; 23 | 24 | for (i = 0; i < bufv->count; i++) { 25 | if (bufv->buf[i].size == SIZE_MAX) 26 | size = SIZE_MAX; 27 | else 28 | size += bufv->buf[i].size; 29 | } 30 | 31 | return size; 32 | } 33 | 34 | static size_t min_size(size_t s1, size_t s2) 35 | { 36 | return s1 < s2 ? s1 : s2; 37 | } 38 | 39 | static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off, 40 | const struct fuse_buf *src, size_t src_off, 41 | size_t len) 42 | { 43 | ssize_t res = 0; 44 | size_t copied = 0; 45 | 46 | while (len) { 47 | if (dst->flags & FUSE_BUF_FD_SEEK) { 48 | res = pwrite64(dst->fd, (const intptr_t *)src->mem + src_off, len, 49 | dst->pos + dst_off); 50 | } else { 51 | res = write(dst->fd, (const intptr_t *)src->mem + src_off, len); 52 | } 53 | if (res == -1) { 54 | if (!copied) 55 | return -errno; 56 | break; 57 | } 58 | if (res == 0) 59 | break; 60 | 61 | copied += res; 62 | if (!(dst->flags & FUSE_BUF_FD_RETRY)) 63 | break; 64 | 65 | src_off += res; 66 | dst_off += res; 67 | len -= res; 68 | } 69 | 70 | return copied; 71 | } 72 | 73 | static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off, 74 | const struct fuse_buf *src, size_t src_off, 75 | size_t len) 76 | { 77 | ssize_t res = 0; 78 | size_t copied = 0; 79 | 80 | while (len) { 81 | if (src->flags & FUSE_BUF_FD_SEEK) { 82 | res = pread(src->fd, (intptr_t *)dst->mem + dst_off, len, 83 | src->pos + src_off); 84 | } else { 85 | res = read(src->fd, (intptr_t *)dst->mem + dst_off, len); 86 | } 87 | if (res == -1) { 88 | if (!copied) 89 | return -errno; 90 | break; 91 | } 92 | if (res == 0) 93 | break; 94 | 95 | copied += res; 96 | if (!(src->flags & FUSE_BUF_FD_RETRY)) 97 | break; 98 | 99 | dst_off += res; 100 | src_off += res; 101 | len -= res; 102 | } 103 | 104 | return copied; 105 | } 106 | 107 | static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off, 108 | const struct fuse_buf *src, size_t src_off, 109 | size_t len) 110 | { 111 | char buf[4096]; 112 | struct fuse_buf tmp = { 113 | .size = sizeof(buf), 114 | .flags = 0, 115 | }; 116 | ssize_t res; 117 | size_t copied = 0; 118 | 119 | tmp.mem = buf; 120 | 121 | while (len) { 122 | ssize_t this_len = min_size(tmp.size, len); 123 | size_t read_len; 124 | 125 | res = fuse_buf_read(&tmp, 0, src, src_off, this_len); 126 | if (res < 0) { 127 | if (!copied) 128 | return res; 129 | break; 130 | } 131 | if (res == 0) 132 | break; 133 | 134 | read_len = res; 135 | res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len); 136 | if (res < 0) { 137 | if (!copied) 138 | return res; 139 | break; 140 | } 141 | if (res == 0) 142 | break; 143 | 144 | copied += res; 145 | 146 | if (res < this_len) 147 | break; 148 | 149 | dst_off += res; 150 | src_off += res; 151 | len -= res; 152 | } 153 | 154 | return copied; 155 | } 156 | 157 | #ifdef HAVE_SPLICE 158 | static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off, 159 | const struct fuse_buf *src, size_t src_off, 160 | size_t len, enum fuse_buf_copy_flags flags) 161 | { 162 | int splice_flags = 0; 163 | loff_t *srcpos = NULL; 164 | loff_t *dstpos = NULL; 165 | loff_t srcpos_val; 166 | loff_t dstpos_val; 167 | ssize_t res; 168 | size_t copied = 0; 169 | 170 | if (flags & FUSE_BUF_SPLICE_MOVE) 171 | splice_flags |= SPLICE_F_MOVE; 172 | if (flags & FUSE_BUF_SPLICE_NONBLOCK) 173 | splice_flags |= SPLICE_F_NONBLOCK; 174 | 175 | if (src->flags & FUSE_BUF_FD_SEEK) { 176 | srcpos_val = src->pos + src_off; 177 | srcpos = &srcpos_val; 178 | } 179 | if (dst->flags & FUSE_BUF_FD_SEEK) { 180 | dstpos_val = dst->pos + dst_off; 181 | dstpos = &dstpos_val; 182 | } 183 | 184 | while (len) { 185 | res = splice(src->fd, srcpos, dst->fd, dstpos, len, 186 | splice_flags); 187 | if (res == -1) { 188 | if (copied) 189 | break; 190 | 191 | if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE)) 192 | return -errno; 193 | 194 | /* Maybe splice is not supported for this combination */ 195 | return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, 196 | len); 197 | } 198 | if (res == 0) 199 | break; 200 | 201 | copied += res; 202 | if (!(src->flags & FUSE_BUF_FD_RETRY) && 203 | !(dst->flags & FUSE_BUF_FD_RETRY)) { 204 | break; 205 | } 206 | 207 | len -= res; 208 | } 209 | 210 | return copied; 211 | } 212 | #else 213 | static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off, 214 | const struct fuse_buf *src, size_t src_off, 215 | size_t len, enum fuse_buf_copy_flags flags) 216 | { 217 | (void) flags; 218 | 219 | return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); 220 | } 221 | #endif 222 | 223 | 224 | static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off, 225 | const struct fuse_buf *src, size_t src_off, 226 | size_t len, enum fuse_buf_copy_flags flags) 227 | { 228 | int src_is_fd = src->flags & FUSE_BUF_IS_FD; 229 | int dst_is_fd = dst->flags & FUSE_BUF_IS_FD; 230 | 231 | if (!src_is_fd && !dst_is_fd) { 232 | void *dstmem = (intptr_t *)dst->mem + dst_off; 233 | void *srcmem = (intptr_t *)src->mem + src_off; 234 | 235 | if (dstmem != srcmem) { 236 | if ((intptr_t *)dstmem + len <= (intptr_t *)srcmem 237 | || (intptr_t *)srcmem + len <= (intptr_t *)dstmem) 238 | memcpy(dstmem, srcmem, len); 239 | else 240 | memmove(dstmem, srcmem, len); 241 | } 242 | 243 | return len; 244 | } else if (!src_is_fd) { 245 | return fuse_buf_write(dst, dst_off, src, src_off, len); 246 | } else if (!dst_is_fd) { 247 | return fuse_buf_read(dst, dst_off, src, src_off, len); 248 | } else if (flags & FUSE_BUF_NO_SPLICE) { 249 | return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); 250 | } else { 251 | return fuse_buf_splice(dst, dst_off, src, src_off, len, flags); 252 | } 253 | } 254 | 255 | static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv) 256 | { 257 | if (bufv->idx < bufv->count) 258 | return &bufv->buf[bufv->idx]; 259 | else 260 | return NULL; 261 | } 262 | 263 | static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len) 264 | { 265 | const struct fuse_buf *buf = fuse_bufvec_current(bufv); 266 | 267 | bufv->off += len; 268 | assert(bufv->off <= buf->size); 269 | if (bufv->off == buf->size) { 270 | assert(bufv->idx < bufv->count); 271 | bufv->idx++; 272 | if (bufv->idx == bufv->count) 273 | return 0; 274 | bufv->off = 0; 275 | } 276 | return 1; 277 | } 278 | 279 | ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv, 280 | enum fuse_buf_copy_flags flags) 281 | { 282 | size_t copied = 0; 283 | 284 | if (dstv == srcv) 285 | return fuse_buf_size(dstv); 286 | 287 | for (;;) { 288 | const struct fuse_buf *src = fuse_bufvec_current(srcv); 289 | const struct fuse_buf *dst = fuse_bufvec_current(dstv); 290 | size_t src_len; 291 | size_t dst_len; 292 | ssize_t len; 293 | ssize_t res; 294 | 295 | if (src == NULL || dst == NULL) 296 | break; 297 | 298 | src_len = src->size - srcv->off; 299 | dst_len = dst->size - dstv->off; 300 | len = min_size(src_len, dst_len); 301 | 302 | res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags); 303 | if (res < 0) { 304 | if (!copied) 305 | return res; 306 | break; 307 | } 308 | copied += res; 309 | 310 | if (!fuse_bufvec_advance(srcv, res) || 311 | !fuse_bufvec_advance(dstv, res)) 312 | break; 313 | 314 | if (res < len) 315 | break; 316 | } 317 | 318 | return copied; 319 | } 320 | -------------------------------------------------------------------------------- /include/fuse_opt.h: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU LGPLv2. 6 | See the file COPYING.LIB. 7 | */ 8 | 9 | #ifndef _FUSE_OPT_H_ 10 | #define _FUSE_OPT_H_ 11 | 12 | /** @file 13 | * 14 | * This file defines the option parsing interface of FUSE 15 | */ 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | /** 22 | * Option description 23 | * 24 | * This structure describes a single option, and action associated 25 | * with it, in case it matches. 26 | * 27 | * More than one such match may occur, in which case the action for 28 | * each match is executed. 29 | * 30 | * There are three possible actions in case of a match: 31 | * 32 | * i) An integer (int or unsigned) variable determined by 'offset' is 33 | * set to 'value' 34 | * 35 | * ii) The processing function is called, with 'value' as the key 36 | * 37 | * iii) An integer (any) or string (char *) variable determined by 38 | * 'offset' is set to the value of an option parameter 39 | * 40 | * 'offset' should normally be either set to 41 | * 42 | * - 'offsetof(struct foo, member)' actions i) and iii) 43 | * 44 | * - -1 action ii) 45 | * 46 | * The 'offsetof()' macro is defined in the header. 47 | * 48 | * The template determines which options match, and also have an 49 | * effect on the action. Normally the action is either i) or ii), but 50 | * if a format is present in the template, then action iii) is 51 | * performed. 52 | * 53 | * The types of templates are: 54 | * 55 | * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only 56 | * themselves. Invalid values are "--" and anything beginning 57 | * with "-o" 58 | * 59 | * 2) "foo", "foo-bar", etc. These match "-ofoo", "-ofoo-bar" or 60 | * the relevant option in a comma separated option list 61 | * 62 | * 3) "bar=", "--foo=", etc. These are variations of 1) and 2) 63 | * which have a parameter 64 | * 65 | * 4) "bar=%s", "--foo=%lu", etc. Same matching as above but perform 66 | * action iii). 67 | * 68 | * 5) "-x ", etc. Matches either "-xparam" or "-x param" as 69 | * two separate arguments 70 | * 71 | * 6) "-x %s", etc. Combination of 4) and 5) 72 | * 73 | * If the format is "%s", memory is allocated for the string unlike 74 | * with scanf(). 75 | */ 76 | struct fuse_opt { 77 | /** Matching template and optional parameter formatting */ 78 | const char *templ; 79 | 80 | /** 81 | * Offset of variable within 'data' parameter of fuse_opt_parse() 82 | * or -1 83 | */ 84 | unsigned long offset; 85 | 86 | /** 87 | * Value to set the variable to, or to be passed as 'key' to the 88 | * processing function. Ignored if template has a format 89 | */ 90 | int value; 91 | }; 92 | 93 | /** 94 | * Key option. In case of a match, the processing function will be 95 | * called with the specified key. 96 | */ 97 | #define FUSE_OPT_KEY(templ, key) { templ, -1U, key } 98 | 99 | /** 100 | * Last option. An array of 'struct fuse_opt' must end with a NULL 101 | * template value 102 | */ 103 | #define FUSE_OPT_END { NULL, 0, 0 } 104 | 105 | /** 106 | * Argument list 107 | */ 108 | struct fuse_args { 109 | /** Argument count */ 110 | int argc; 111 | 112 | /** Argument vector. NULL terminated */ 113 | char **argv; 114 | 115 | /** Is 'argv' allocated? */ 116 | int allocated; 117 | }; 118 | 119 | /** 120 | * Initializer for 'struct fuse_args' 121 | */ 122 | #define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 } 123 | 124 | /** 125 | * Key value passed to the processing function if an option did not 126 | * match any template 127 | */ 128 | #define FUSE_OPT_KEY_OPT -1 129 | 130 | /** 131 | * Key value passed to the processing function for all non-options 132 | * 133 | * Non-options are the arguments beginning with a character other than 134 | * '-' or all arguments after the special '--' option 135 | */ 136 | #define FUSE_OPT_KEY_NONOPT -2 137 | 138 | /** 139 | * Special key value for options to keep 140 | * 141 | * Argument is not passed to processing function, but behave as if the 142 | * processing function returned 1 143 | */ 144 | #define FUSE_OPT_KEY_KEEP -3 145 | 146 | /** 147 | * Special key value for options to discard 148 | * 149 | * Argument is not passed to processing function, but behave as if the 150 | * processing function returned zero 151 | */ 152 | #define FUSE_OPT_KEY_DISCARD -4 153 | 154 | /** 155 | * Processing function 156 | * 157 | * This function is called if 158 | * - option did not match any 'struct fuse_opt' 159 | * - argument is a non-option 160 | * - option did match and offset was set to -1 161 | * 162 | * The 'arg' parameter will always contain the whole argument or 163 | * option including the parameter if exists. A two-argument option 164 | * ("-x foo") is always converted to single argument option of the 165 | * form "-xfoo" before this function is called. 166 | * 167 | * Options of the form '-ofoo' are passed to this function without the 168 | * '-o' prefix. 169 | * 170 | * The return value of this function determines whether this argument 171 | * is to be inserted into the output argument vector, or discarded. 172 | * 173 | * @param data is the user data passed to the fuse_opt_parse() function 174 | * @param arg is the whole argument or option 175 | * @param key determines why the processing function was called 176 | * @param outargs the current output argument list 177 | * @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept 178 | */ 179 | typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key, 180 | struct fuse_args *outargs); 181 | 182 | /** 183 | * Option parsing function 184 | * 185 | * If 'args' was returned from a previous call to fuse_opt_parse() or 186 | * it was constructed from 187 | * 188 | * A NULL 'args' is equivalent to an empty argument vector 189 | * 190 | * A NULL 'opts' is equivalent to an 'opts' array containing a single 191 | * end marker 192 | * 193 | * A NULL 'proc' is equivalent to a processing function always 194 | * returning '1' 195 | * 196 | * @param args is the input and output argument list 197 | * @param data is the user data 198 | * @param opts is the option description array 199 | * @param proc is the processing function 200 | * @return -1 on error, 0 on success 201 | */ 202 | int fuse_opt_parse(struct fuse_args *args, void *data, 203 | const struct fuse_opt opts[], fuse_opt_proc_t proc); 204 | 205 | /** 206 | * Add an option to a comma separated option list 207 | * 208 | * @param opts is a pointer to an option list, may point to a NULL value 209 | * @param opt is the option to add 210 | * @return -1 on allocation error, 0 on success 211 | */ 212 | int fuse_opt_add_opt(char **opts, const char *opt); 213 | 214 | /** 215 | * Add an option, escaping commas, to a comma separated option list 216 | * 217 | * @param opts is a pointer to an option list, may point to a NULL value 218 | * @param opt is the option to add 219 | * @return -1 on allocation error, 0 on success 220 | */ 221 | int fuse_opt_add_opt_escaped(char **opts, const char *opt); 222 | 223 | /** 224 | * Add an argument to a NULL terminated argument vector 225 | * 226 | * @param args is the structure containing the current argument list 227 | * @param arg is the new argument to add 228 | * @return -1 on allocation error, 0 on success 229 | */ 230 | int fuse_opt_add_arg(struct fuse_args *args, const char *arg); 231 | 232 | /** 233 | * Add an argument at the specified position in a NULL terminated 234 | * argument vector 235 | * 236 | * Adds the argument to the N-th position. This is useful for adding 237 | * options at the beginning of the array which must not come after the 238 | * special '--' option. 239 | * 240 | * @param args is the structure containing the current argument list 241 | * @param pos is the position at which to add the argument 242 | * @param arg is the new argument to add 243 | * @return -1 on allocation error, 0 on success 244 | */ 245 | int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg); 246 | 247 | /** 248 | * Free the contents of argument list 249 | * 250 | * The structure itself is not freed 251 | * 252 | * @param args is the structure containing the argument list 253 | */ 254 | void fuse_opt_free_args(struct fuse_args *args); 255 | 256 | 257 | /** 258 | * Check if an option matches 259 | * 260 | * @param opts is the option description array 261 | * @param opt is the option to match 262 | * @return 1 if a match is found, 0 if not 263 | */ 264 | int fuse_opt_match(const struct fuse_opt opts[], const char *opt); 265 | 266 | #ifdef __cplusplus 267 | } 268 | #endif 269 | 270 | #endif /* _FUSE_OPT_H_ */ 271 | -------------------------------------------------------------------------------- /include/fuse_compat.h: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU LGPLv2. 6 | See the file COPYING.LIB. 7 | */ 8 | 9 | /* these definitions provide source compatibility to prior versions. 10 | Do not include this file directly! */ 11 | 12 | #include "fuse_lowlevel.h" 13 | 14 | struct fuse_operations_compat25 { 15 | int (*getattr) (const char *, struct stat *); 16 | int (*readlink) (const char *, char *, size_t); 17 | int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t); 18 | int (*mknod) (const char *, mode_t, dev_t); 19 | int (*mkdir) (const char *, mode_t); 20 | int (*unlink) (const char *); 21 | int (*rmdir) (const char *); 22 | int (*symlink) (const char *, const char *); 23 | int (*rename) (const char *, const char *); 24 | int (*link) (const char *, const char *); 25 | int (*chmod) (const char *, mode_t); 26 | int (*chown) (const char *, uid_t, gid_t); 27 | int (*truncate) (const char *, loff_t); 28 | int (*utime) (const char *, struct utimbuf *); 29 | int (*open) (const char *, struct fuse_file_info *); 30 | int (*read) (const char *, char *, size_t, loff_t, 31 | struct fuse_file_info *); 32 | int (*write) (const char *, const char *, size_t, loff_t, 33 | struct fuse_file_info *); 34 | int (*statfs) (const char *, struct statvfs *); 35 | int (*flush) (const char *, struct fuse_file_info *); 36 | int (*release) (const char *, struct fuse_file_info *); 37 | int (*fsync) (const char *, int, struct fuse_file_info *); 38 | int (*setxattr) (const char *, const char *, const char *, size_t, int); 39 | int (*getxattr) (const char *, const char *, char *, size_t); 40 | int (*listxattr) (const char *, char *, size_t); 41 | int (*removexattr) (const char *, const char *); 42 | int (*opendir) (const char *, struct fuse_file_info *); 43 | int (*readdir) (const char *, void *, fuse_fill_dir_t, loff_t, 44 | struct fuse_file_info *); 45 | int (*releasedir) (const char *, struct fuse_file_info *); 46 | int (*fsyncdir) (const char *, int, struct fuse_file_info *); 47 | void *(*init) (void); 48 | void (*destroy) (void *); 49 | int (*access) (const char *, int); 50 | int (*create) (const char *, mode_t, struct fuse_file_info *); 51 | int (*ftruncate) (const char *, loff_t, struct fuse_file_info *); 52 | int (*fgetattr) (const char *, struct stat *, struct fuse_file_info *); 53 | }; 54 | 55 | struct fuse *fuse_new_compat25(int fd, struct fuse_args *args, 56 | const struct fuse_operations_compat25 *op, 57 | size_t op_size); 58 | 59 | int fuse_main_real_compat25(int argc, char *argv[], 60 | const struct fuse_operations_compat25 *op, 61 | size_t op_size); 62 | 63 | struct fuse *fuse_setup_compat25(int argc, char *argv[], 64 | const struct fuse_operations_compat25 *op, 65 | size_t op_size, char **mountpoint, 66 | int *multithreaded, int *fd); 67 | 68 | void fuse_teardown_compat22(struct fuse *fuse, int fd, char *mountpoint); 69 | 70 | #if !defined(__FreeBSD__) && !defined(__NetBSD__) 71 | #include 72 | 73 | struct fuse_operations_compat22 { 74 | int (*getattr) (const char *, struct stat *); 75 | int (*readlink) (const char *, char *, size_t); 76 | int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t); 77 | int (*mknod) (const char *, mode_t, dev_t); 78 | int (*mkdir) (const char *, mode_t); 79 | int (*unlink) (const char *); 80 | int (*rmdir) (const char *); 81 | int (*symlink) (const char *, const char *); 82 | int (*rename) (const char *, const char *); 83 | int (*link) (const char *, const char *); 84 | int (*chmod) (const char *, mode_t); 85 | int (*chown) (const char *, uid_t, gid_t); 86 | int (*truncate) (const char *, loff_t); 87 | int (*utime) (const char *, struct utimbuf *); 88 | int (*open) (const char *, struct fuse_file_info_compat *); 89 | int (*read) (const char *, char *, size_t, loff_t, 90 | struct fuse_file_info_compat *); 91 | int (*write) (const char *, const char *, size_t, loff_t, 92 | struct fuse_file_info_compat *); 93 | int (*statfs) (const char *, struct statfs *); 94 | int (*flush) (const char *, struct fuse_file_info_compat *); 95 | int (*release) (const char *, struct fuse_file_info_compat *); 96 | int (*fsync) (const char *, int, struct fuse_file_info_compat *); 97 | int (*setxattr) (const char *, const char *, const char *, size_t, int); 98 | int (*getxattr) (const char *, const char *, char *, size_t); 99 | int (*listxattr) (const char *, char *, size_t); 100 | int (*removexattr) (const char *, const char *); 101 | int (*opendir) (const char *, struct fuse_file_info_compat *); 102 | int (*readdir) (const char *, void *, fuse_fill_dir_t, loff_t, 103 | struct fuse_file_info_compat *); 104 | int (*releasedir) (const char *, struct fuse_file_info_compat *); 105 | int (*fsyncdir) (const char *, int, struct fuse_file_info_compat *); 106 | void *(*init) (void); 107 | void (*destroy) (void *); 108 | }; 109 | 110 | struct fuse *fuse_new_compat22(int fd, const char *opts, 111 | const struct fuse_operations_compat22 *op, 112 | size_t op_size); 113 | 114 | struct fuse *fuse_setup_compat22(int argc, char *argv[], 115 | const struct fuse_operations_compat22 *op, 116 | size_t op_size, char **mountpoint, 117 | int *multithreaded, int *fd); 118 | 119 | int fuse_main_real_compat22(int argc, char *argv[], 120 | const struct fuse_operations_compat22 *op, 121 | size_t op_size); 122 | 123 | typedef int (*fuse_dirfil_t_compat) (fuse_dirh_t h, const char *name, int type); 124 | struct fuse_operations_compat2 { 125 | int (*getattr) (const char *, struct stat *); 126 | int (*readlink) (const char *, char *, size_t); 127 | int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t_compat); 128 | int (*mknod) (const char *, mode_t, dev_t); 129 | int (*mkdir) (const char *, mode_t); 130 | int (*unlink) (const char *); 131 | int (*rmdir) (const char *); 132 | int (*symlink) (const char *, const char *); 133 | int (*rename) (const char *, const char *); 134 | int (*link) (const char *, const char *); 135 | int (*chmod) (const char *, mode_t); 136 | int (*chown) (const char *, uid_t, gid_t); 137 | int (*truncate) (const char *, loff_t); 138 | int (*utime) (const char *, struct utimbuf *); 139 | int (*open) (const char *, int); 140 | int (*read) (const char *, char *, size_t, loff_t); 141 | int (*write) (const char *, const char *, size_t, loff_t); 142 | int (*statfs) (const char *, struct statfs *); 143 | int (*flush) (const char *); 144 | int (*release) (const char *, int); 145 | int (*fsync) (const char *, int); 146 | int (*setxattr) (const char *, const char *, const char *, 147 | size_t, int); 148 | int (*getxattr) (const char *, const char *, char *, size_t); 149 | int (*listxattr) (const char *, char *, size_t); 150 | int (*removexattr) (const char *, const char *); 151 | }; 152 | 153 | int fuse_main_compat2(int argc, char *argv[], 154 | const struct fuse_operations_compat2 *op); 155 | 156 | struct fuse *fuse_new_compat2(int fd, const char *opts, 157 | const struct fuse_operations_compat2 *op); 158 | 159 | struct fuse *fuse_setup_compat2(int argc, char *argv[], 160 | const struct fuse_operations_compat2 *op, 161 | char **mountpoint, int *multithreaded, int *fd); 162 | 163 | struct fuse_statfs_compat1 { 164 | long block_size; 165 | long blocks; 166 | long blocks_free; 167 | long files; 168 | long files_free; 169 | long namelen; 170 | }; 171 | 172 | struct fuse_operations_compat1 { 173 | int (*getattr) (const char *, struct stat *); 174 | int (*readlink) (const char *, char *, size_t); 175 | int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t_compat); 176 | int (*mknod) (const char *, mode_t, dev_t); 177 | int (*mkdir) (const char *, mode_t); 178 | int (*unlink) (const char *); 179 | int (*rmdir) (const char *); 180 | int (*symlink) (const char *, const char *); 181 | int (*rename) (const char *, const char *); 182 | int (*link) (const char *, const char *); 183 | int (*chmod) (const char *, mode_t); 184 | int (*chown) (const char *, uid_t, gid_t); 185 | int (*truncate) (const char *, loff_t); 186 | int (*utime) (const char *, struct utimbuf *); 187 | int (*open) (const char *, int); 188 | int (*read) (const char *, char *, size_t, loff_t); 189 | int (*write) (const char *, const char *, size_t, loff_t); 190 | int (*statfs) (struct fuse_statfs_compat1 *); 191 | int (*release) (const char *, int); 192 | int (*fsync) (const char *, int); 193 | }; 194 | 195 | #define FUSE_DEBUG_COMPAT1 (1 << 1) 196 | 197 | struct fuse *fuse_new_compat1(int fd, int flags, 198 | const struct fuse_operations_compat1 *op); 199 | 200 | void fuse_main_compat1(int argc, char *argv[], 201 | const struct fuse_operations_compat1 *op); 202 | 203 | #endif /* __FreeBSD__ || __NetBSD__ */ 204 | -------------------------------------------------------------------------------- /example/fusexmp.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | Copyright (C) 2011 Sebastian Pipping 5 | 6 | This program can be distributed under the terms of the GNU GPL. 7 | See the file COPYING. 8 | 9 | gcc -Wall fusexmp.c `pkg-config fuse --cflags --libs` -o fusexmp 10 | */ 11 | 12 | #define FUSE_USE_VERSION 26 13 | 14 | #ifdef HAVE_CONFIG_H 15 | #include 16 | #endif 17 | 18 | #ifdef linux 19 | /* For pread()/pwrite()/utimensat() */ 20 | #define _XOPEN_SOURCE 700 21 | #endif 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #ifdef HAVE_SETXATTR 33 | #include 34 | #endif 35 | 36 | static int xmp_getattr(const char *path, struct stat *stbuf) 37 | { 38 | int res; 39 | 40 | res = lstat(path, stbuf); 41 | if (res == -1) 42 | return -errno; 43 | 44 | return 0; 45 | } 46 | 47 | static int xmp_access(const char *path, int mask) 48 | { 49 | int res; 50 | 51 | res = access(path, mask); 52 | if (res == -1) 53 | return -errno; 54 | 55 | return 0; 56 | } 57 | 58 | static int xmp_readlink(const char *path, char *buf, size_t size) 59 | { 60 | int res; 61 | 62 | res = readlink(path, buf, size - 1); 63 | if (res == -1) 64 | return -errno; 65 | 66 | buf[res] = '\0'; 67 | return 0; 68 | } 69 | 70 | 71 | static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, 72 | off_t offset, struct fuse_file_info *fi) 73 | { 74 | DIR *dp; 75 | struct dirent *de; 76 | 77 | (void) offset; 78 | (void) fi; 79 | 80 | dp = opendir(path); 81 | if (dp == NULL) 82 | return -errno; 83 | 84 | while ((de = readdir(dp)) != NULL) { 85 | struct stat st; 86 | memset(&st, 0, sizeof(st)); 87 | st.st_ino = de->d_ino; 88 | st.st_mode = de->d_type << 12; 89 | if (filler(buf, de->d_name, &st, 0)) 90 | break; 91 | } 92 | 93 | closedir(dp); 94 | return 0; 95 | } 96 | 97 | static int xmp_mknod(const char *path, mode_t mode, dev_t rdev) 98 | { 99 | int res; 100 | 101 | /* On Linux this could just be 'mknod(path, mode, rdev)' but this 102 | is more portable */ 103 | if (S_ISREG(mode)) { 104 | res = open(path, O_CREAT | O_EXCL | O_WRONLY, mode); 105 | if (res >= 0) 106 | res = close(res); 107 | } else if (S_ISFIFO(mode)) 108 | res = mkfifo(path, mode); 109 | else 110 | res = mknod(path, mode, rdev); 111 | if (res == -1) 112 | return -errno; 113 | 114 | return 0; 115 | } 116 | 117 | static int xmp_mkdir(const char *path, mode_t mode) 118 | { 119 | int res; 120 | 121 | res = mkdir(path, mode); 122 | if (res == -1) 123 | return -errno; 124 | 125 | return 0; 126 | } 127 | 128 | static int xmp_unlink(const char *path) 129 | { 130 | int res; 131 | 132 | res = unlink(path); 133 | if (res == -1) 134 | return -errno; 135 | 136 | return 0; 137 | } 138 | 139 | static int xmp_rmdir(const char *path) 140 | { 141 | int res; 142 | 143 | res = rmdir(path); 144 | if (res == -1) 145 | return -errno; 146 | 147 | return 0; 148 | } 149 | 150 | static int xmp_symlink(const char *from, const char *to) 151 | { 152 | int res; 153 | 154 | res = symlink(from, to); 155 | if (res == -1) 156 | return -errno; 157 | 158 | return 0; 159 | } 160 | 161 | static int xmp_rename(const char *from, const char *to) 162 | { 163 | int res; 164 | 165 | res = rename(from, to); 166 | if (res == -1) 167 | return -errno; 168 | 169 | return 0; 170 | } 171 | 172 | static int xmp_link(const char *from, const char *to) 173 | { 174 | int res; 175 | 176 | res = link(from, to); 177 | if (res == -1) 178 | return -errno; 179 | 180 | return 0; 181 | } 182 | 183 | static int xmp_chmod(const char *path, mode_t mode) 184 | { 185 | int res; 186 | 187 | res = chmod(path, mode); 188 | if (res == -1) 189 | return -errno; 190 | 191 | return 0; 192 | } 193 | 194 | static int xmp_chown(const char *path, uid_t uid, gid_t gid) 195 | { 196 | int res; 197 | 198 | res = lchown(path, uid, gid); 199 | if (res == -1) 200 | return -errno; 201 | 202 | return 0; 203 | } 204 | 205 | static int xmp_truncate(const char *path, off_t size) 206 | { 207 | int res; 208 | 209 | res = truncate(path, size); 210 | if (res == -1) 211 | return -errno; 212 | 213 | return 0; 214 | } 215 | 216 | #ifdef HAVE_UTIMENSAT 217 | static int xmp_utimens(const char *path, const struct timespec ts[2]) 218 | { 219 | int res; 220 | 221 | /* don't use utime/utimes since they follow symlinks */ 222 | res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW); 223 | if (res == -1) 224 | return -errno; 225 | 226 | return 0; 227 | } 228 | #endif 229 | 230 | static int xmp_open(const char *path, struct fuse_file_info *fi) 231 | { 232 | int res; 233 | 234 | res = open(path, fi->flags); 235 | if (res == -1) 236 | return -errno; 237 | 238 | close(res); 239 | return 0; 240 | } 241 | 242 | static int xmp_read(const char *path, char *buf, size_t size, off_t offset, 243 | struct fuse_file_info *fi) 244 | { 245 | int fd; 246 | int res; 247 | 248 | (void) fi; 249 | fd = open(path, O_RDONLY); 250 | if (fd == -1) 251 | return -errno; 252 | 253 | res = pread(fd, buf, size, offset); 254 | if (res == -1) 255 | res = -errno; 256 | 257 | close(fd); 258 | return res; 259 | } 260 | 261 | static int xmp_write(const char *path, const char *buf, size_t size, 262 | off_t offset, struct fuse_file_info *fi) 263 | { 264 | int fd; 265 | int res; 266 | 267 | (void) fi; 268 | fd = open(path, O_WRONLY); 269 | if (fd == -1) 270 | return -errno; 271 | 272 | res = pwrite(fd, buf, size, offset); 273 | if (res == -1) 274 | res = -errno; 275 | 276 | close(fd); 277 | return res; 278 | } 279 | 280 | static int xmp_statfs(const char *path, struct statvfs *stbuf) 281 | { 282 | int res; 283 | 284 | res = statvfs(path, stbuf); 285 | if (res == -1) 286 | return -errno; 287 | 288 | return 0; 289 | } 290 | 291 | static int xmp_release(const char *path, struct fuse_file_info *fi) 292 | { 293 | /* Just a stub. This method is optional and can safely be left 294 | unimplemented */ 295 | 296 | (void) path; 297 | (void) fi; 298 | return 0; 299 | } 300 | 301 | static int xmp_fsync(const char *path, int isdatasync, 302 | struct fuse_file_info *fi) 303 | { 304 | /* Just a stub. This method is optional and can safely be left 305 | unimplemented */ 306 | 307 | (void) path; 308 | (void) isdatasync; 309 | (void) fi; 310 | return 0; 311 | } 312 | 313 | #ifdef HAVE_POSIX_FALLOCATE 314 | static int xmp_fallocate(const char *path, int mode, 315 | off_t offset, off_t length, struct fuse_file_info *fi) 316 | { 317 | int fd; 318 | int res; 319 | 320 | (void) fi; 321 | 322 | if (mode) 323 | return -EOPNOTSUPP; 324 | 325 | fd = open(path, O_WRONLY); 326 | if (fd == -1) 327 | return -errno; 328 | 329 | res = -posix_fallocate(fd, offset, length); 330 | 331 | close(fd); 332 | return res; 333 | } 334 | #endif 335 | 336 | #ifdef HAVE_SETXATTR 337 | /* xattr operations are optional and can safely be left unimplemented */ 338 | static int xmp_setxattr(const char *path, const char *name, const char *value, 339 | size_t size, int flags) 340 | { 341 | int res = lsetxattr(path, name, value, size, flags); 342 | if (res == -1) 343 | return -errno; 344 | return 0; 345 | } 346 | 347 | static int xmp_getxattr(const char *path, const char *name, char *value, 348 | size_t size) 349 | { 350 | int res = lgetxattr(path, name, value, size); 351 | if (res == -1) 352 | return -errno; 353 | return res; 354 | } 355 | 356 | static int xmp_listxattr(const char *path, char *list, size_t size) 357 | { 358 | int res = llistxattr(path, list, size); 359 | if (res == -1) 360 | return -errno; 361 | return res; 362 | } 363 | 364 | static int xmp_removexattr(const char *path, const char *name) 365 | { 366 | int res = lremovexattr(path, name); 367 | if (res == -1) 368 | return -errno; 369 | return 0; 370 | } 371 | #endif /* HAVE_SETXATTR */ 372 | 373 | static struct fuse_operations xmp_oper = { 374 | .getattr = xmp_getattr, 375 | .access = xmp_access, 376 | .readlink = xmp_readlink, 377 | .readdir = xmp_readdir, 378 | .mknod = xmp_mknod, 379 | .mkdir = xmp_mkdir, 380 | .symlink = xmp_symlink, 381 | .unlink = xmp_unlink, 382 | .rmdir = xmp_rmdir, 383 | .rename = xmp_rename, 384 | .link = xmp_link, 385 | .chmod = xmp_chmod, 386 | .chown = xmp_chown, 387 | .truncate = xmp_truncate, 388 | #ifdef HAVE_UTIMENSAT 389 | .utimens = xmp_utimens, 390 | #endif 391 | .open = xmp_open, 392 | .read = xmp_read, 393 | .write = xmp_write, 394 | .statfs = xmp_statfs, 395 | .release = xmp_release, 396 | .fsync = xmp_fsync, 397 | #ifdef HAVE_POSIX_FALLOCATE 398 | .fallocate = xmp_fallocate, 399 | #endif 400 | #ifdef HAVE_SETXATTR 401 | .setxattr = xmp_setxattr, 402 | .getxattr = xmp_getxattr, 403 | .listxattr = xmp_listxattr, 404 | .removexattr = xmp_removexattr, 405 | #endif 406 | }; 407 | 408 | int main(int argc, char *argv[]) 409 | { 410 | umask(0); 411 | return fuse_main(argc, argv, &xmp_oper, NULL); 412 | } 413 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | What is new in 2.9 2 | 3 | - Add "zero copy" support for kernel 2.6.35 or newer 4 | 5 | - Make maximum background requests tunable on kernel 2.6.32 or newer 6 | 7 | - Require --no-canonicalize in (u)mount (util-linux version 2.18 or 8 | newer) to fix security problems with fusermount 9 | 10 | - Use dynamically sized hash tables in high level library 11 | 12 | - Memory use of filesystem daemon can shrink more easily 13 | 14 | - Add "auto_unmount" option 15 | 16 | - Add "remember" option 17 | 18 | - Add man pages for fusermount, mount.fuse and ulockmgr_server 19 | 20 | - API changes: 21 | 22 | o Introduce "store" and "retrieve" for accessing kernel buffers on 23 | kernel 2.6.36 or newer 24 | 25 | o Introduce abstract buffer for zero copy operations 26 | 27 | o Allow path calculation to be omitted on certain operations 28 | 29 | o Allow batching forget requests 30 | 31 | o Add "flock" method 32 | 33 | o Add support for ioctl on directories 34 | 35 | o Add delete notification 36 | 37 | o Add fallocate operation (linux kernel 3.5 or newer) 38 | 39 | - Bug fixes and small improvements 40 | 41 | ============================================================================ 42 | 43 | What is new in 2.8 44 | 45 | - More scalable directory tree locking 46 | 47 | - Atomic open(O_TRUNC) support 48 | 49 | - Support big write requests on kernels 2.6.26 and newer 50 | 51 | - Out-of-tree fuse module removed 52 | 53 | - Better NFS exporting support 54 | 55 | - New ioctl and poll requests 56 | 57 | - New CUSE (Character Device in Userspace) interface 58 | 59 | - Allow umask processing in userspace 60 | 61 | - Added cache invalidation notifications 62 | 63 | - Bugfixes and small improvements 64 | 65 | ============================================================================ 66 | 67 | What is new in 2.7 68 | 69 | - Stacking support for the high level API 70 | 71 | - Add filename charset conversion module 72 | 73 | - Improved mounting 74 | 75 | ============================================================================ 76 | 77 | What is new in 2.6 78 | 79 | - Improved read characteristics (asynchronous reads) 80 | 81 | - Support for aborting filesystem connection 82 | 83 | - POSIX file locking support 84 | 85 | - Request interruption support 86 | 87 | - Building module for Linux kernels earlier than 2.6.9 not supported 88 | 89 | - Allow block device based filesystems to support swap files 90 | 91 | - Several bugs fixed, including a rare system hang on SMP 92 | 93 | ============================================================================ 94 | 95 | What is new in 2.5 96 | 97 | - Merge library part of FreeBSD port 98 | 99 | - New atomic create+open, access and ftruncate operations 100 | 101 | - On filesystems implementing the new create+open operation, and 102 | running on Linux kernels 2.6.15 or later, the 'cp' operation will 103 | work correctly when copying read-only files. 104 | 105 | - New option parsing interface added to the library 106 | 107 | - Lots of minor improvements and fixes 108 | 109 | ============================================================================ 110 | 111 | What is new in 2.4 112 | 113 | - Simplify device opening. Now '/dev/fuse' is a requirement 114 | 115 | - Allow module auto-loading if user has access to '/dev/fuse' 116 | 117 | - Allow mounting over a regular file for unprivileged users 118 | 119 | - Allow mounting of arbitrary FUSE filesystems from /etc/fstab 120 | 121 | - New mount options: 'umask=M', 'uid=N', 'gid=N' 122 | 123 | - Check for non-empty mountpoint, and refuse mount by default. New 124 | mount option: 'nonempty' 125 | 126 | - Low level (inode based) API added 127 | 128 | - Allow 'direct_io' and 'keep_cache' options to be set on a 129 | case-by-case basis on open. 130 | 131 | - Add 'attr_timeout' and 'entry_timeout' mount options to the 132 | high-level library. Until now these timeouts were fixed at 1 sec. 133 | 134 | - Some bugfixes 135 | 136 | ============================================================================ 137 | 138 | What is new in 2.3 139 | 140 | - Add new directory related operations: opendir(), readdir(), 141 | releasedir() and fsyncdir() 142 | 143 | - Add init() and destroy() operations which are called before the 144 | event loop is started and after it has exited 145 | 146 | - Update kernel ABI so that on dual architectures (e.g. AMD64) 32bit 147 | binaries work under a 64bit kernel 148 | 149 | - Bugfixes 150 | 151 | ============================================================================ 152 | 153 | What is new in 2.2 154 | 155 | Userspace changes: 156 | 157 | - Add fuse_file_info structure to file operations, this allows the 158 | filesystem to return a file handle in open() which is passed to 159 | read(), write(), flush(), fsync() and release(). 160 | 161 | - Add source compatibility with 2.1 and 1.4 releases 162 | 163 | - Binary compatibility with 2.1 release is retained 164 | 165 | Kernel changes: 166 | 167 | - Make requests interruptible. This prevents the filesystem to go 168 | into an unbreakable deadlock with itself. 169 | 170 | - Make readpages() synchronous. Asynchronous requests are deadlock 171 | prone, since they cannot be interrupted (see above) 172 | 173 | - Remove shared-writeable mapping support, which could deadlock the 174 | machine 175 | 176 | - Remove INVALIDATE userspace initiated request 177 | 178 | - Update ABI to be independent of sizeof(long), so dual-size archs 179 | don't cause problems 180 | 181 | - Remove /sys/fs/fuse/version. Version checking is now done through 182 | the fuse device 183 | 184 | - Replace directory reading method on the kernel interface. Instead 185 | of passing an open file descriptor to the kernel, send data through 186 | the FUSE device, like all other operations. 187 | 188 | ============================================================================ 189 | 190 | What is new in 2.1 191 | 192 | * Bug fixes 193 | 194 | * Improved support for filesystems implementing a custom event-loop 195 | 196 | * Add 'pkg-config' support 197 | 198 | * Kernel module can be compiled separately 199 | 200 | ============================================================================ 201 | 202 | What is new in 1.9 203 | 204 | * Lots of bugs fixed 205 | 206 | * Minor modifications to the library API 207 | 208 | * Improvements to the kernel/userspace interface 209 | 210 | * Mounting by non-root made more secure 211 | 212 | * Build shared library in addition to the static one 213 | 214 | * Consolidated mount options 215 | 216 | * Optimized reading under 2.6 kernels 217 | 218 | * Direct I/O support 219 | 220 | * Support file I/O on deleted files 221 | 222 | * Extended attributes support 223 | 224 | ============================================================================ 225 | 226 | What is new in 1.3 227 | 228 | * Thanks to user bugreports and stress testing with LTP and sfx-linux 229 | a number of bugs were fixed, some quite serious. 230 | 231 | * Fix compile problems with recent SuSE kernles 232 | 233 | ============================================================================ 234 | 235 | What is new in 1.2 236 | 237 | * Fix mount problems on recent 2.6 kernels with SELinux enabled 238 | 239 | * Fixed writing files lager than 2GBytes 240 | 241 | * Other bugfixes 242 | 243 | ============================================================================ 244 | 245 | What is new in 1.1 246 | 247 | * Support for the 2.6 kernels 248 | 249 | * Support for exporting filesystem over NFS in 2.6 kernels 250 | 251 | * Read efficiency improvements: read in 64k blocks instead of 4k 252 | (Michael Grigoriev). Can be turned on with '-l' option of fusermount 253 | 254 | * Lazy automatic unmount 255 | 256 | * Added 'fsync()' VFS call to the FUSE interface 257 | 258 | * Bugfixes 259 | 260 | ============================================================================ 261 | 262 | What is new in 1.0 263 | 264 | * Cleanups and bugfixes 265 | 266 | * Added 'release()' VFS call to the FUSE interface 267 | 268 | * 64 bit file offsets (handling of > 4 GByte files) 269 | 270 | * libfuse is now under LGPL 271 | 272 | * New 'statfs' call (Mark Glines) 273 | 274 | * Cleaned up mount procedure (mostly by Mark Glines) 275 | 276 | NOTE: Binaries linked with with a previous version of libavfs may 277 | not work with the new version of the fusermount program. In such 278 | case recompile the program after installing the new libavfs library. 279 | 280 | * Fix for problems under linux kernel 2.4.19 281 | 282 | ============================================================================ 283 | 284 | What is new in 0.95 285 | 286 | * Optimized read/write operations. Raw throughput has increased to 287 | about 60Mbyte/s on a Celeron/360 288 | 289 | * Python bindings by Jeff Epler 290 | 291 | * Perl bindings by Mark Glines 292 | 293 | * Improved multithreaded operation 294 | 295 | * Simplified library interface 296 | 297 | * Bugfixes 298 | 299 | ============================================================================ 300 | 301 | What is new in 0.9: 302 | 303 | * Everything 304 | -------------------------------------------------------------------------------- /lib/mount_util.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2007 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU LGPLv2. 6 | See the file COPYING.LIB. 7 | */ 8 | 9 | #include "mount_util.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #ifndef __NetBSD__ 21 | #include 22 | #endif 23 | #include 24 | #include 25 | #include 26 | #include 27 | #if defined(__ANDROID__) 28 | #include 29 | #endif 30 | 31 | #ifdef __NetBSD__ 32 | #define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0) 33 | #define mtab_needs_update(mnt) 0 34 | #elif defined(__ANDROID__) 35 | #define mtab_needs_update(mnt) 0 36 | #else 37 | static int mtab_needs_update(const char *mnt) 38 | { 39 | int res; 40 | struct stat stbuf; 41 | 42 | /* If mtab is within new mount, don't touch it */ 43 | if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 && 44 | _PATH_MOUNTED[strlen(mnt)] == '/') 45 | return 0; 46 | 47 | /* 48 | * Skip mtab update if /etc/mtab: 49 | * 50 | * - doesn't exist, 51 | * - is a symlink, 52 | * - is on a read-only filesystem. 53 | */ 54 | res = lstat(_PATH_MOUNTED, &stbuf); 55 | if (res == -1) { 56 | if (errno == ENOENT) 57 | return 0; 58 | } else { 59 | uid_t ruid; 60 | int err; 61 | 62 | if (S_ISLNK(stbuf.st_mode)) 63 | return 0; 64 | 65 | ruid = getuid(); 66 | if (ruid != 0) 67 | setreuid(0, -1); 68 | 69 | res = access(_PATH_MOUNTED, W_OK); 70 | err = (res == -1) ? errno : 0; 71 | if (ruid != 0) 72 | setreuid(ruid, -1); 73 | 74 | if (err == EROFS) 75 | return 0; 76 | } 77 | 78 | return 1; 79 | } 80 | #endif /* __NetBSD__ */ 81 | 82 | static int add_mount(const char *progname, const char *fsname, 83 | const char *mnt, const char *type, const char *opts) 84 | { 85 | int res; 86 | int status; 87 | sigset_t blockmask; 88 | sigset_t oldmask; 89 | 90 | sigemptyset(&blockmask); 91 | sigaddset(&blockmask, SIGCHLD); 92 | res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask); 93 | if (res == -1) { 94 | fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno)); 95 | return -1; 96 | } 97 | 98 | res = fork(); 99 | if (res == -1) { 100 | fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); 101 | goto out_restore; 102 | } 103 | if (res == 0) { 104 | char *env = NULL; 105 | 106 | sigprocmask(SIG_SETMASK, &oldmask, NULL); 107 | setuid(geteuid()); 108 | execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i", 109 | "-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env); 110 | fprintf(stderr, "%s: failed to execute /bin/mount: %s\n", 111 | progname, strerror(errno)); 112 | exit(1); 113 | } 114 | res = waitpid(res, &status, 0); 115 | if (res == -1) 116 | fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno)); 117 | 118 | if (status != 0) 119 | res = -1; 120 | 121 | out_restore: 122 | sigprocmask(SIG_SETMASK, &oldmask, NULL); 123 | 124 | return res; 125 | } 126 | 127 | int fuse_mnt_add_mount(const char *progname, const char *fsname, 128 | const char *mnt, const char *type, const char *opts) 129 | { 130 | if (!mtab_needs_update(mnt)) 131 | return 0; 132 | 133 | return add_mount(progname, fsname, mnt, type, opts); 134 | } 135 | 136 | static int exec_umount(const char *progname, const char *rel_mnt, int lazy) 137 | { 138 | int res; 139 | int status; 140 | sigset_t blockmask; 141 | sigset_t oldmask; 142 | 143 | sigemptyset(&blockmask); 144 | sigaddset(&blockmask, SIGCHLD); 145 | res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask); 146 | if (res == -1) { 147 | fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno)); 148 | return -1; 149 | } 150 | 151 | res = fork(); 152 | if (res == -1) { 153 | fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); 154 | goto out_restore; 155 | } 156 | if (res == 0) { 157 | char *env = NULL; 158 | 159 | sigprocmask(SIG_SETMASK, &oldmask, NULL); 160 | setuid(geteuid()); 161 | if (lazy) { 162 | execle("/bin/umount", "/bin/umount", "-i", rel_mnt, 163 | "-l", NULL, &env); 164 | } else { 165 | execle("/bin/umount", "/bin/umount", "-i", rel_mnt, 166 | NULL, &env); 167 | } 168 | fprintf(stderr, "%s: failed to execute /bin/umount: %s\n", 169 | progname, strerror(errno)); 170 | exit(1); 171 | } 172 | res = waitpid(res, &status, 0); 173 | if (res == -1) 174 | fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno)); 175 | 176 | if (status != 0) { 177 | res = -1; 178 | } 179 | 180 | out_restore: 181 | sigprocmask(SIG_SETMASK, &oldmask, NULL); 182 | return res; 183 | 184 | } 185 | 186 | int fuse_mnt_umount(const char *progname, const char *abs_mnt, 187 | const char *rel_mnt, int lazy) 188 | { 189 | int res; 190 | 191 | if (!mtab_needs_update(abs_mnt)) { 192 | res = umount2(rel_mnt, lazy ? 2 : 0); 193 | if (res == -1) 194 | fprintf(stderr, "%s: failed to unmount %s: %s\n", 195 | progname, abs_mnt, strerror(errno)); 196 | return res; 197 | } 198 | 199 | return exec_umount(progname, rel_mnt, lazy); 200 | } 201 | 202 | static int remove_mount(const char *progname, const char *mnt) 203 | { 204 | int res; 205 | int status; 206 | sigset_t blockmask; 207 | sigset_t oldmask; 208 | 209 | sigemptyset(&blockmask); 210 | sigaddset(&blockmask, SIGCHLD); 211 | res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask); 212 | if (res == -1) { 213 | fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno)); 214 | return -1; 215 | } 216 | 217 | res = fork(); 218 | if (res == -1) { 219 | fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); 220 | goto out_restore; 221 | } 222 | if (res == 0) { 223 | char *env = NULL; 224 | 225 | sigprocmask(SIG_SETMASK, &oldmask, NULL); 226 | setuid(geteuid()); 227 | execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i", 228 | "--fake", mnt, NULL, &env); 229 | fprintf(stderr, "%s: failed to execute /bin/umount: %s\n", 230 | progname, strerror(errno)); 231 | exit(1); 232 | } 233 | res = waitpid(res, &status, 0); 234 | if (res == -1) 235 | fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno)); 236 | 237 | if (status != 0) 238 | res = -1; 239 | 240 | out_restore: 241 | sigprocmask(SIG_SETMASK, &oldmask, NULL); 242 | return res; 243 | } 244 | 245 | int fuse_mnt_remove_mount(const char *progname, const char *mnt) 246 | { 247 | if (!mtab_needs_update(mnt)) 248 | return 0; 249 | 250 | return remove_mount(progname, mnt); 251 | } 252 | 253 | char *fuse_mnt_resolve_path(const char *progname, const char *orig) 254 | { 255 | char buf[PATH_MAX]; 256 | char *copy; 257 | char *dst; 258 | char *end; 259 | char *lastcomp; 260 | const char *toresolv; 261 | 262 | if (!orig[0]) { 263 | fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname, 264 | orig); 265 | return NULL; 266 | } 267 | 268 | copy = strdup(orig); 269 | if (copy == NULL) { 270 | fprintf(stderr, "%s: failed to allocate memory\n", progname); 271 | return NULL; 272 | } 273 | 274 | toresolv = copy; 275 | lastcomp = NULL; 276 | for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --); 277 | if (end[0] != '/') { 278 | char *tmp; 279 | end[1] = '\0'; 280 | tmp = strrchr(copy, '/'); 281 | if (tmp == NULL) { 282 | lastcomp = copy; 283 | toresolv = "."; 284 | } else { 285 | lastcomp = tmp + 1; 286 | if (tmp == copy) 287 | toresolv = "/"; 288 | } 289 | if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) { 290 | lastcomp = NULL; 291 | toresolv = copy; 292 | } 293 | else if (tmp) 294 | tmp[0] = '\0'; 295 | } 296 | if (realpath(toresolv, buf) == NULL) { 297 | fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig, 298 | strerror(errno)); 299 | free(copy); 300 | return NULL; 301 | } 302 | if (lastcomp == NULL) 303 | dst = strdup(buf); 304 | else { 305 | dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1); 306 | if (dst) { 307 | unsigned buflen = strlen(buf); 308 | if (buflen && buf[buflen-1] == '/') 309 | sprintf(dst, "%s%s", buf, lastcomp); 310 | else 311 | sprintf(dst, "%s/%s", buf, lastcomp); 312 | } 313 | } 314 | free(copy); 315 | if (dst == NULL) 316 | fprintf(stderr, "%s: failed to allocate memory\n", progname); 317 | return dst; 318 | } 319 | 320 | int fuse_mnt_check_empty(const char *progname, const char *mnt, 321 | mode_t rootmode, loff_t rootsize) 322 | { 323 | int isempty = 1; 324 | 325 | if (S_ISDIR(rootmode)) { 326 | struct dirent *ent; 327 | DIR *dp = opendir(mnt); 328 | if (dp == NULL) { 329 | fprintf(stderr, 330 | "%s: failed to open mountpoint for reading: %s\n", 331 | progname, strerror(errno)); 332 | return -1; 333 | } 334 | while ((ent = readdir(dp)) != NULL) { 335 | if (strcmp(ent->d_name, ".") != 0 && 336 | strcmp(ent->d_name, "..") != 0) { 337 | isempty = 0; 338 | break; 339 | } 340 | } 341 | closedir(dp); 342 | } else if (rootsize) 343 | isempty = 0; 344 | 345 | if (!isempty) { 346 | fprintf(stderr, "%s: mountpoint is not empty\n", progname); 347 | fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname); 348 | return -1; 349 | } 350 | return 0; 351 | } 352 | 353 | int fuse_mnt_check_fuseblk(void) 354 | { 355 | char buf[256]; 356 | FILE *f = fopen("/proc/filesystems", "r"); 357 | if (!f) 358 | return 1; 359 | 360 | while (fgets(buf, sizeof(buf), f)) 361 | if (strstr(buf, "fuseblk\n")) { 362 | fclose(f); 363 | return 1; 364 | } 365 | 366 | fclose(f); 367 | return 0; 368 | } 369 | -------------------------------------------------------------------------------- /util/ulockmgr_server.c: -------------------------------------------------------------------------------- 1 | /* 2 | ulockmgr_server: Userspace Lock Manager Server 3 | Copyright (C) 2006 Miklos Szeredi 4 | 5 | This program can be distributed under the terms of the GNU GPL. 6 | See the file COPYING. 7 | */ 8 | 9 | /* #define DEBUG 1 */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | struct message { 26 | unsigned intr : 1; 27 | unsigned nofd : 1; 28 | pthread_t thr; 29 | int cmd; 30 | int fd; 31 | struct flock lock; 32 | int error; 33 | }; 34 | 35 | struct fd_store { 36 | struct fd_store *next; 37 | int fd; 38 | int origfd; 39 | int inuse; 40 | }; 41 | 42 | struct owner { 43 | struct fd_store *fds; 44 | pthread_mutex_t lock; 45 | }; 46 | 47 | struct req_data { 48 | struct owner *o; 49 | int cfd; 50 | struct fd_store *f; 51 | struct message msg; 52 | }; 53 | 54 | #define MAX_SEND_FDS 2 55 | 56 | static int receive_message(int sock, void *buf, size_t buflen, int *fdp, 57 | int *numfds) 58 | { 59 | struct msghdr msg; 60 | struct iovec iov; 61 | size_t ccmsg[CMSG_SPACE(sizeof(int) * MAX_SEND_FDS) / sizeof(size_t)]; 62 | struct cmsghdr *cmsg; 63 | int res; 64 | int i; 65 | 66 | assert(*numfds <= MAX_SEND_FDS); 67 | iov.iov_base = buf; 68 | iov.iov_len = buflen; 69 | 70 | memset(&msg, 0, sizeof(msg)); 71 | memset(ccmsg, -1, sizeof(ccmsg)); 72 | msg.msg_iov = &iov; 73 | msg.msg_iovlen = 1; 74 | msg.msg_control = ccmsg; 75 | msg.msg_controllen = sizeof(ccmsg); 76 | 77 | res = recvmsg(sock, &msg, MSG_WAITALL); 78 | if (!res) { 79 | /* retry on zero return, see do_recv() in ulockmgr.c */ 80 | res = recvmsg(sock, &msg, MSG_WAITALL); 81 | if (!res) 82 | return 0; 83 | } 84 | if (res == -1) { 85 | perror("ulockmgr_server: recvmsg"); 86 | return -1; 87 | } 88 | if ((size_t) res != buflen) { 89 | fprintf(stderr, "ulockmgr_server: short message received\n"); 90 | return -1; 91 | } 92 | 93 | cmsg = CMSG_FIRSTHDR(&msg); 94 | if (cmsg) { 95 | if (cmsg->cmsg_type != SCM_RIGHTS) { 96 | fprintf(stderr, 97 | "ulockmgr_server: unknown control message %d\n", 98 | cmsg->cmsg_type); 99 | return -1; 100 | } 101 | memcpy(fdp, CMSG_DATA(cmsg), sizeof(int) * *numfds); 102 | if (msg.msg_flags & MSG_CTRUNC) { 103 | fprintf(stderr, 104 | "ulockmgr_server: control message truncated\n"); 105 | for (i = 0; i < *numfds; i++) 106 | close(fdp[i]); 107 | *numfds = 0; 108 | } 109 | } else { 110 | if (msg.msg_flags & MSG_CTRUNC) { 111 | fprintf(stderr, 112 | "ulockmgr_server: control message truncated(*)\n"); 113 | 114 | /* There's a bug in the Linux kernel, that if 115 | not all file descriptors were allocated, 116 | then the cmsg header is not filled in */ 117 | cmsg = (struct cmsghdr *) ccmsg; 118 | memcpy(fdp, CMSG_DATA(cmsg), sizeof(int) * *numfds); 119 | for (i = 0; i < *numfds; i++) 120 | close(fdp[i]); 121 | } 122 | *numfds = 0; 123 | } 124 | return res; 125 | } 126 | 127 | static int closefrom(int minfd) 128 | { 129 | DIR *dir = opendir("/proc/self/fd"); 130 | if (dir) { 131 | int dfd = dirfd(dir); 132 | struct dirent *ent; 133 | while ((ent = readdir(dir))) { 134 | char *end; 135 | int fd = strtol(ent->d_name, &end, 10); 136 | if (ent->d_name[0] && !end[0] && fd >= minfd && 137 | fd != dfd) 138 | close(fd); 139 | } 140 | closedir(dir); 141 | } 142 | return 0; 143 | } 144 | 145 | static void send_reply(int cfd, struct message *msg) 146 | { 147 | int res = send(cfd, msg, sizeof(struct message), MSG_NOSIGNAL); 148 | if (res == -1) 149 | perror("ulockmgr_server: sending reply"); 150 | #ifdef DEBUG 151 | fprintf(stderr, "ulockmgr_server: error: %i\n", msg->error); 152 | #endif 153 | } 154 | 155 | static void *process_request(void *d_) 156 | { 157 | struct req_data *d = d_; 158 | int res; 159 | 160 | assert(d->msg.cmd == F_SETLKW); 161 | res = fcntl(d->f->fd, F_SETLK, &d->msg.lock); 162 | if (res == -1 && errno == EAGAIN) { 163 | d->msg.error = EAGAIN; 164 | d->msg.thr = pthread_self(); 165 | send_reply(d->cfd, &d->msg); 166 | res = fcntl(d->f->fd, F_SETLKW, &d->msg.lock); 167 | } 168 | d->msg.error = (res == -1) ? errno : 0; 169 | pthread_mutex_lock(&d->o->lock); 170 | d->f->inuse--; 171 | pthread_mutex_unlock(&d->o->lock); 172 | send_reply(d->cfd, &d->msg); 173 | close(d->cfd); 174 | free(d); 175 | 176 | return NULL; 177 | } 178 | 179 | static void process_message(struct owner *o, struct message *msg, int cfd, 180 | int fd) 181 | { 182 | struct fd_store *f = NULL; 183 | struct fd_store *newf = NULL; 184 | struct fd_store **fp; 185 | struct req_data *d; 186 | pthread_t tid; 187 | int res; 188 | 189 | #ifdef DEBUG 190 | fprintf(stderr, "ulockmgr_server: %i %i %i %lli %lli\n", 191 | msg->cmd, msg->lock.l_type, msg->lock.l_whence, 192 | msg->lock.l_start, msg->lock.l_len); 193 | #endif 194 | 195 | if (msg->cmd == F_SETLK && msg->lock.l_type == F_UNLCK && 196 | msg->lock.l_start == 0 && msg->lock.l_len == 0) { 197 | for (fp = &o->fds; *fp;) { 198 | f = *fp; 199 | if (f->origfd == msg->fd && !f->inuse) { 200 | close(f->fd); 201 | *fp = f->next; 202 | free(f); 203 | } else 204 | fp = &f->next; 205 | } 206 | if (!msg->nofd) 207 | close(fd); 208 | 209 | msg->error = 0; 210 | send_reply(cfd, msg); 211 | close(cfd); 212 | return; 213 | } 214 | 215 | if (msg->nofd) { 216 | for (fp = &o->fds; *fp; fp = &(*fp)->next) { 217 | f = *fp; 218 | if (f->origfd == msg->fd) 219 | break; 220 | } 221 | if (!*fp) { 222 | fprintf(stderr, "ulockmgr_server: fd %i not found\n", 223 | msg->fd); 224 | msg->error = EIO; 225 | send_reply(cfd, msg); 226 | close(cfd); 227 | return; 228 | } 229 | } else { 230 | newf = f = malloc(sizeof(struct fd_store)); 231 | if (!f) { 232 | msg->error = ENOLCK; 233 | send_reply(cfd, msg); 234 | close(cfd); 235 | return; 236 | } 237 | 238 | f->fd = fd; 239 | f->origfd = msg->fd; 240 | f->inuse = 0; 241 | } 242 | 243 | if (msg->cmd == F_GETLK || msg->cmd == F_SETLK || 244 | msg->lock.l_type == F_UNLCK) { 245 | res = fcntl(f->fd, msg->cmd, &msg->lock); 246 | msg->error = (res == -1) ? errno : 0; 247 | send_reply(cfd, msg); 248 | close(cfd); 249 | if (newf) { 250 | newf->next = o->fds; 251 | o->fds = newf; 252 | } 253 | return; 254 | } 255 | 256 | d = malloc(sizeof(struct req_data)); 257 | if (!d) { 258 | msg->error = ENOLCK; 259 | send_reply(cfd, msg); 260 | close(cfd); 261 | free(newf); 262 | return; 263 | } 264 | 265 | f->inuse++; 266 | d->o = o; 267 | d->cfd = cfd; 268 | d->f = f; 269 | d->msg = *msg; 270 | res = pthread_create(&tid, NULL, process_request, d); 271 | if (res) { 272 | msg->error = ENOLCK; 273 | send_reply(cfd, msg); 274 | close(cfd); 275 | free(d); 276 | f->inuse--; 277 | free(newf); 278 | return; 279 | } 280 | 281 | if (newf) { 282 | newf->next = o->fds; 283 | o->fds = newf; 284 | } 285 | pthread_detach(tid); 286 | } 287 | 288 | static void sigusr1_handler(int sig) 289 | { 290 | (void) sig; 291 | /* Nothing to do */ 292 | } 293 | 294 | static void process_owner(int cfd) 295 | { 296 | struct owner o; 297 | struct sigaction sa; 298 | 299 | memset(&sa, 0, sizeof(struct sigaction)); 300 | sa.sa_handler = sigusr1_handler; 301 | sigemptyset(&sa.sa_mask); 302 | 303 | if (sigaction(SIGUSR1, &sa, NULL) == -1) { 304 | perror("ulockmgr_server: cannot set sigusr1 signal handler"); 305 | exit(1); 306 | } 307 | 308 | memset(&o, 0, sizeof(struct owner)); 309 | pthread_mutex_init(&o.lock, NULL); 310 | while (1) { 311 | struct message msg; 312 | int rfds[2]; 313 | int res; 314 | int numfds = 2; 315 | 316 | res = receive_message(cfd, &msg, sizeof(msg), rfds, &numfds); 317 | if (!res) 318 | break; 319 | if (res == -1) 320 | exit(1); 321 | 322 | if (msg.intr) { 323 | if (numfds != 0) 324 | fprintf(stderr, 325 | "ulockmgr_server: too many fds for intr\n"); 326 | pthread_kill(msg.thr, SIGUSR1); 327 | } else { 328 | if (numfds != 2) 329 | continue; 330 | 331 | pthread_mutex_lock(&o.lock); 332 | process_message(&o, &msg, rfds[0], rfds[1]); 333 | pthread_mutex_unlock(&o.lock); 334 | } 335 | } 336 | if (o.fds) 337 | fprintf(stderr, 338 | "ulockmgr_server: open file descriptors on exit\n"); 339 | } 340 | 341 | int main(int argc, char *argv[]) 342 | { 343 | int nullfd; 344 | char *end; 345 | int cfd; 346 | sigset_t empty; 347 | 348 | if (argc != 2 || !argv[1][0]) 349 | goto out_inval; 350 | 351 | cfd = strtol(argv[1], &end, 10); 352 | if (*end) 353 | goto out_inval; 354 | 355 | /* demonize current process */ 356 | switch(fork()) { 357 | case -1: 358 | perror("ulockmgr_server: fork"); 359 | exit(1); 360 | case 0: 361 | break; 362 | default: 363 | _exit(0); 364 | } 365 | 366 | if (setsid() == -1) { 367 | perror("ulockmgr_server: setsid"); 368 | exit(1); 369 | } 370 | 371 | (void) chdir("/"); 372 | 373 | sigemptyset(&empty); 374 | sigprocmask(SIG_SETMASK, &empty, NULL); 375 | 376 | if (dup2(cfd, 4) == -1) { 377 | perror("ulockmgr_server: dup2"); 378 | exit(1); 379 | } 380 | cfd = 4; 381 | nullfd = open("/dev/null", O_RDWR); 382 | if (nullfd >= 0) { 383 | dup2(nullfd, 0); 384 | dup2(nullfd, 1); 385 | } 386 | close(3); 387 | closefrom(5); 388 | while (1) { 389 | char c; 390 | int sock; 391 | int pid; 392 | int numfds = 1; 393 | int res = receive_message(cfd, &c, sizeof(c), &sock, &numfds); 394 | if (!res) 395 | break; 396 | if (res == -1) 397 | exit(1); 398 | assert(numfds == 1); 399 | 400 | pid = fork(); 401 | if (pid == -1) { 402 | perror("ulockmgr_server: fork"); 403 | close(sock); 404 | continue; 405 | } 406 | if (pid == 0) { 407 | close(cfd); 408 | pid = fork(); 409 | if (pid == -1) { 410 | perror("ulockmgr_server: fork"); 411 | _exit(1); 412 | } 413 | if (pid == 0) 414 | process_owner(sock); 415 | _exit(0); 416 | } 417 | waitpid(pid, NULL, 0); 418 | close(sock); 419 | } 420 | return 0; 421 | 422 | out_inval: 423 | fprintf(stderr, "%s should be started by libulockmgr\n", argv[0]); 424 | return 1; 425 | } 426 | -------------------------------------------------------------------------------- /lib/mount_bsd.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2005-2008 Csaba Henk 4 | 5 | This program can be distributed under the terms of the GNU LGPLv2. 6 | See the file COPYING.LIB. 7 | */ 8 | 9 | #include "fuse_i.h" 10 | #include "fuse_misc.h" 11 | #include "fuse_opt.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #define FUSERMOUNT_PROG "mount_fusefs" 28 | #define FUSE_DEV_TRUNK "/dev/fuse" 29 | 30 | enum { 31 | KEY_ALLOW_ROOT, 32 | KEY_RO, 33 | KEY_HELP, 34 | KEY_VERSION, 35 | KEY_KERN 36 | }; 37 | 38 | struct mount_opts { 39 | int allow_other; 40 | int allow_root; 41 | int ishelp; 42 | char *kernel_opts; 43 | }; 44 | 45 | #define FUSE_DUAL_OPT_KEY(templ, key) \ 46 | FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key) 47 | 48 | static const struct fuse_opt fuse_mount_opts[] = { 49 | { "allow_other", offsetof(struct mount_opts, allow_other), 1 }, 50 | { "allow_root", offsetof(struct mount_opts, allow_root), 1 }, 51 | FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT), 52 | FUSE_OPT_KEY("-r", KEY_RO), 53 | FUSE_OPT_KEY("-h", KEY_HELP), 54 | FUSE_OPT_KEY("--help", KEY_HELP), 55 | FUSE_OPT_KEY("-V", KEY_VERSION), 56 | FUSE_OPT_KEY("--version", KEY_VERSION), 57 | /* standard FreeBSD mount options */ 58 | FUSE_DUAL_OPT_KEY("dev", KEY_KERN), 59 | FUSE_DUAL_OPT_KEY("async", KEY_KERN), 60 | FUSE_DUAL_OPT_KEY("atime", KEY_KERN), 61 | FUSE_DUAL_OPT_KEY("dev", KEY_KERN), 62 | FUSE_DUAL_OPT_KEY("exec", KEY_KERN), 63 | FUSE_DUAL_OPT_KEY("suid", KEY_KERN), 64 | FUSE_DUAL_OPT_KEY("symfollow", KEY_KERN), 65 | FUSE_DUAL_OPT_KEY("rdonly", KEY_KERN), 66 | FUSE_DUAL_OPT_KEY("sync", KEY_KERN), 67 | FUSE_DUAL_OPT_KEY("union", KEY_KERN), 68 | FUSE_DUAL_OPT_KEY("userquota", KEY_KERN), 69 | FUSE_DUAL_OPT_KEY("groupquota", KEY_KERN), 70 | FUSE_DUAL_OPT_KEY("clusterr", KEY_KERN), 71 | FUSE_DUAL_OPT_KEY("clusterw", KEY_KERN), 72 | FUSE_DUAL_OPT_KEY("suiddir", KEY_KERN), 73 | FUSE_DUAL_OPT_KEY("snapshot", KEY_KERN), 74 | FUSE_DUAL_OPT_KEY("multilabel", KEY_KERN), 75 | FUSE_DUAL_OPT_KEY("acls", KEY_KERN), 76 | FUSE_DUAL_OPT_KEY("force", KEY_KERN), 77 | FUSE_DUAL_OPT_KEY("update", KEY_KERN), 78 | FUSE_DUAL_OPT_KEY("ro", KEY_KERN), 79 | FUSE_DUAL_OPT_KEY("rw", KEY_KERN), 80 | FUSE_DUAL_OPT_KEY("auto", KEY_KERN), 81 | /* options supported under both Linux and FBSD */ 82 | FUSE_DUAL_OPT_KEY("allow_other", KEY_KERN), 83 | FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN), 84 | FUSE_OPT_KEY("max_read=", KEY_KERN), 85 | FUSE_OPT_KEY("subtype=", KEY_KERN), 86 | /* FBSD FUSE specific mount options */ 87 | FUSE_DUAL_OPT_KEY("private", KEY_KERN), 88 | FUSE_DUAL_OPT_KEY("neglect_shares", KEY_KERN), 89 | FUSE_DUAL_OPT_KEY("push_symlinks_in", KEY_KERN), 90 | FUSE_OPT_KEY("nosync_unmount", KEY_KERN), 91 | /* stock FBSD mountopt parsing routine lets anything be negated... */ 92 | /* 93 | * Linux specific mount options, but let just the mount util 94 | * handle them 95 | */ 96 | FUSE_OPT_KEY("fsname=", KEY_KERN), 97 | FUSE_OPT_KEY("nonempty", KEY_KERN), 98 | FUSE_OPT_KEY("large_read", KEY_KERN), 99 | FUSE_OPT_END 100 | }; 101 | 102 | static void mount_help(void) 103 | { 104 | fprintf(stderr, 105 | " -o allow_root allow access to root\n" 106 | ); 107 | system(FUSERMOUNT_PROG " --help"); 108 | fputc('\n', stderr); 109 | } 110 | 111 | static void mount_version(void) 112 | { 113 | system(FUSERMOUNT_PROG " --version"); 114 | } 115 | 116 | static int fuse_mount_opt_proc(void *data, const char *arg, int key, 117 | struct fuse_args *outargs) 118 | { 119 | struct mount_opts *mo = data; 120 | 121 | switch (key) { 122 | case KEY_ALLOW_ROOT: 123 | if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 || 124 | fuse_opt_add_arg(outargs, "-oallow_root") == -1) 125 | return -1; 126 | return 0; 127 | 128 | case KEY_RO: 129 | arg = "ro"; 130 | /* fall through */ 131 | 132 | case KEY_KERN: 133 | return fuse_opt_add_opt(&mo->kernel_opts, arg); 134 | 135 | case KEY_HELP: 136 | mount_help(); 137 | mo->ishelp = 1; 138 | break; 139 | 140 | case KEY_VERSION: 141 | mount_version(); 142 | mo->ishelp = 1; 143 | break; 144 | } 145 | return 1; 146 | } 147 | 148 | void fuse_unmount_compat22(const char *mountpoint) 149 | { 150 | char dev[128]; 151 | char *ssc, *umount_cmd; 152 | FILE *sf; 153 | int rv; 154 | char seekscript[] = 155 | /* error message is annoying in help output */ 156 | "exec 2>/dev/null; " 157 | "/usr/bin/fstat " FUSE_DEV_TRUNK "* | " 158 | "/usr/bin/awk 'BEGIN{ getline; if (! ($3 == \"PID\" && $10 == \"NAME\")) exit 1; }; " 159 | " { if ($3 == %d) print $10; }' | " 160 | "/usr/bin/sort | " 161 | "/usr/bin/uniq | " 162 | "/usr/bin/awk '{ i += 1; if (i > 1){ exit 1; }; printf; }; END{ if (i == 0) exit 1; }'"; 163 | 164 | (void) mountpoint; 165 | 166 | /* 167 | * If we don't know the fd, we have to resort to the scripted 168 | * solution -- iterating over the fd-s is unpractical, as we 169 | * don't know how many of open files we have. (This could be 170 | * looked up in procfs -- however, that's optional on FBSD; or 171 | * read out from the kmem -- however, that's bound to 172 | * privileges (in fact, that's what happens when we call the 173 | * setgid kmem fstat(1) utility). 174 | */ 175 | if (asprintf(&ssc, seekscript, getpid()) == -1) 176 | return; 177 | 178 | errno = 0; 179 | sf = popen(ssc, "r"); 180 | free(ssc); 181 | if (! sf) 182 | return; 183 | 184 | fgets(dev, sizeof(dev), sf); 185 | rv = pclose(sf); 186 | if (rv) 187 | return; 188 | 189 | if (asprintf(&umount_cmd, "/sbin/umount %s", dev) == -1) 190 | return; 191 | system(umount_cmd); 192 | free(umount_cmd); 193 | } 194 | 195 | static void do_unmount(char *dev, int fd) 196 | { 197 | char device_path[SPECNAMELEN + 12]; 198 | const char *argv[4]; 199 | const char umount_cmd[] = "/sbin/umount"; 200 | pid_t pid; 201 | 202 | snprintf(device_path, SPECNAMELEN + 12, _PATH_DEV "%s", dev); 203 | 204 | argv[0] = umount_cmd; 205 | argv[1] = "-f"; 206 | argv[2] = device_path; 207 | argv[3] = NULL; 208 | 209 | pid = fork(); 210 | 211 | if (pid == -1) 212 | return; 213 | 214 | if (pid == 0) { 215 | close(fd); 216 | execvp(umount_cmd, (char **)argv); 217 | exit(1); 218 | } 219 | 220 | waitpid(pid, NULL, 0); 221 | } 222 | 223 | void fuse_kern_unmount(const char *mountpoint, int fd) 224 | { 225 | char *ep, dev[128]; 226 | struct stat sbuf; 227 | 228 | (void)mountpoint; 229 | 230 | if (fstat(fd, &sbuf) == -1) 231 | goto out; 232 | 233 | devname_r(sbuf.st_rdev, S_IFCHR, dev, 128); 234 | 235 | if (strncmp(dev, "fuse", 4)) 236 | goto out; 237 | 238 | strtol(dev + 4, &ep, 10); 239 | if (*ep != '\0') 240 | goto out; 241 | 242 | do_unmount(dev, fd); 243 | 244 | out: 245 | close(fd); 246 | } 247 | 248 | /* Check if kernel is doing init in background */ 249 | static int init_backgrounded(void) 250 | { 251 | unsigned ibg, len; 252 | 253 | len = sizeof(ibg); 254 | 255 | if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0)) 256 | return 0; 257 | 258 | return ibg; 259 | } 260 | 261 | 262 | static int fuse_mount_core(const char *mountpoint, const char *opts) 263 | { 264 | const char *mountprog = FUSERMOUNT_PROG; 265 | int fd; 266 | char *fdnam, *dev; 267 | pid_t pid, cpid; 268 | int status; 269 | 270 | fdnam = getenv("FUSE_DEV_FD"); 271 | 272 | if (fdnam) { 273 | char *ep; 274 | 275 | fd = strtol(fdnam, &ep, 10); 276 | 277 | if (*ep != '\0') { 278 | fprintf(stderr, "invalid value given in FUSE_DEV_FD\n"); 279 | return -1; 280 | } 281 | 282 | if (fd < 0) 283 | return -1; 284 | 285 | goto mount; 286 | } 287 | 288 | dev = getenv("FUSE_DEV_NAME"); 289 | 290 | if (! dev) 291 | dev = (char *)FUSE_DEV_TRUNK; 292 | 293 | if ((fd = open(dev, O_RDWR)) < 0) { 294 | perror("fuse: failed to open fuse device"); 295 | return -1; 296 | } 297 | 298 | mount: 299 | if (getenv("FUSE_NO_MOUNT") || ! mountpoint) 300 | goto out; 301 | 302 | pid = fork(); 303 | cpid = pid; 304 | 305 | if (pid == -1) { 306 | perror("fuse: fork() failed"); 307 | close(fd); 308 | return -1; 309 | } 310 | 311 | if (pid == 0) { 312 | if (! init_backgrounded()) { 313 | /* 314 | * If init is not backgrounded, we have to 315 | * call the mount util backgrounded, to avoid 316 | * deadlock. 317 | */ 318 | 319 | pid = fork(); 320 | 321 | if (pid == -1) { 322 | perror("fuse: fork() failed"); 323 | close(fd); 324 | exit(1); 325 | } 326 | } 327 | 328 | if (pid == 0) { 329 | const char *argv[32]; 330 | int a = 0; 331 | 332 | if (! fdnam && asprintf(&fdnam, "%d", fd) == -1) { 333 | perror("fuse: failed to assemble mount arguments"); 334 | exit(1); 335 | } 336 | 337 | argv[a++] = mountprog; 338 | if (opts) { 339 | argv[a++] = "-o"; 340 | argv[a++] = opts; 341 | } 342 | argv[a++] = fdnam; 343 | argv[a++] = mountpoint; 344 | argv[a++] = NULL; 345 | execvp(mountprog, (char **) argv); 346 | perror("fuse: failed to exec mount program"); 347 | exit(1); 348 | } 349 | 350 | exit(0); 351 | } 352 | 353 | if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) { 354 | perror("fuse: failed to mount file system"); 355 | close(fd); 356 | return -1; 357 | } 358 | 359 | out: 360 | return fd; 361 | } 362 | 363 | int fuse_kern_mount(const char *mountpoint, struct fuse_args *args) 364 | { 365 | struct mount_opts mo; 366 | int res = -1; 367 | 368 | memset(&mo, 0, sizeof(mo)); 369 | /* mount util should not try to spawn the daemon */ 370 | setenv("MOUNT_FUSEFS_SAFE", "1", 1); 371 | /* to notify the mount util it's called from lib */ 372 | setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1); 373 | 374 | if (args && 375 | fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1) 376 | return -1; 377 | 378 | if (mo.allow_other && mo.allow_root) { 379 | fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n"); 380 | goto out; 381 | } 382 | if (mo.ishelp) 383 | return 0; 384 | 385 | res = fuse_mount_core(mountpoint, mo.kernel_opts); 386 | out: 387 | free(mo.kernel_opts); 388 | return res; 389 | } 390 | 391 | FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2"); 392 | -------------------------------------------------------------------------------- /lib/cuse_lowlevel.c: -------------------------------------------------------------------------------- 1 | /* 2 | CUSE: Character device in Userspace 3 | Copyright (C) 2008 SUSE Linux Products GmbH 4 | Copyright (C) 2008 Tejun Heo 5 | 6 | This program can be distributed under the terms of the GNU LGPLv2. 7 | See the file COPYING.LIB. 8 | */ 9 | 10 | #include "cuse_lowlevel.h" 11 | #include "fuse_kernel.h" 12 | #include "fuse_i.h" 13 | #include "fuse_opt.h" 14 | #include "fuse_misc.h" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | struct cuse_data { 24 | struct cuse_lowlevel_ops clop; 25 | unsigned max_read; 26 | unsigned dev_major; 27 | unsigned dev_minor; 28 | unsigned flags; 29 | unsigned dev_info_len; 30 | char dev_info[]; 31 | }; 32 | 33 | static struct cuse_lowlevel_ops *req_clop(fuse_req_t req) 34 | { 35 | return &req->f->cuse_data->clop; 36 | } 37 | 38 | static void cuse_fll_open(fuse_req_t req, fuse_ino_t ino, 39 | struct fuse_file_info *fi) 40 | { 41 | (void)ino; 42 | req_clop(req)->open(req, fi); 43 | } 44 | 45 | static void cuse_fll_read(fuse_req_t req, fuse_ino_t ino, size_t size, 46 | loff_t off, struct fuse_file_info *fi) 47 | { 48 | (void)ino; 49 | req_clop(req)->read(req, size, off, fi); 50 | } 51 | 52 | static void cuse_fll_write(fuse_req_t req, fuse_ino_t ino, const char *buf, 53 | size_t size, loff_t off, struct fuse_file_info *fi) 54 | { 55 | (void)ino; 56 | req_clop(req)->write(req, buf, size, off, fi); 57 | } 58 | 59 | static void cuse_fll_flush(fuse_req_t req, fuse_ino_t ino, 60 | struct fuse_file_info *fi) 61 | { 62 | (void)ino; 63 | req_clop(req)->flush(req, fi); 64 | } 65 | 66 | static void cuse_fll_release(fuse_req_t req, fuse_ino_t ino, 67 | struct fuse_file_info *fi) 68 | { 69 | (void)ino; 70 | req_clop(req)->release(req, fi); 71 | } 72 | 73 | static void cuse_fll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, 74 | struct fuse_file_info *fi) 75 | { 76 | (void)ino; 77 | req_clop(req)->fsync(req, datasync, fi); 78 | } 79 | 80 | static void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg, 81 | struct fuse_file_info *fi, unsigned int flags, 82 | const void *in_buf, size_t in_bufsz, size_t out_bufsz) 83 | { 84 | (void)ino; 85 | req_clop(req)->ioctl(req, cmd, arg, fi, flags, in_buf, in_bufsz, 86 | out_bufsz); 87 | } 88 | 89 | static void cuse_fll_poll(fuse_req_t req, fuse_ino_t ino, 90 | struct fuse_file_info *fi, struct fuse_pollhandle *ph) 91 | { 92 | (void)ino; 93 | req_clop(req)->poll(req, fi, ph); 94 | } 95 | 96 | static size_t cuse_pack_info(int argc, const char **argv, char *buf) 97 | { 98 | size_t size = 0; 99 | int i; 100 | 101 | for (i = 0; i < argc; i++) { 102 | size_t len; 103 | 104 | len = strlen(argv[i]) + 1; 105 | size += len; 106 | if (buf) { 107 | memcpy(buf, argv[i], len); 108 | buf += len; 109 | } 110 | } 111 | 112 | return size; 113 | } 114 | 115 | static struct cuse_data *cuse_prep_data(const struct cuse_info *ci, 116 | const struct cuse_lowlevel_ops *clop) 117 | { 118 | struct cuse_data *cd; 119 | size_t dev_info_len; 120 | 121 | dev_info_len = cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, 122 | NULL); 123 | 124 | if (dev_info_len > CUSE_INIT_INFO_MAX) { 125 | fprintf(stderr, "cuse: dev_info (%zu) too large, limit=%u\n", 126 | dev_info_len, CUSE_INIT_INFO_MAX); 127 | return NULL; 128 | } 129 | 130 | cd = calloc(1, sizeof(*cd) + dev_info_len); 131 | if (!cd) { 132 | fprintf(stderr, "cuse: failed to allocate cuse_data\n"); 133 | return NULL; 134 | } 135 | 136 | memcpy(&cd->clop, clop, sizeof(cd->clop)); 137 | cd->max_read = 131072; 138 | cd->dev_major = ci->dev_major; 139 | cd->dev_minor = ci->dev_minor; 140 | cd->dev_info_len = dev_info_len; 141 | cd->flags = ci->flags; 142 | cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, cd->dev_info); 143 | 144 | return cd; 145 | } 146 | 147 | struct fuse_session *cuse_lowlevel_new(struct fuse_args *args, 148 | const struct cuse_info *ci, 149 | const struct cuse_lowlevel_ops *clop, 150 | void *userdata) 151 | { 152 | struct fuse_lowlevel_ops lop; 153 | struct cuse_data *cd; 154 | struct fuse_session *se; 155 | struct fuse_ll *ll; 156 | 157 | cd = cuse_prep_data(ci, clop); 158 | if (!cd) 159 | return NULL; 160 | 161 | memset(&lop, 0, sizeof(lop)); 162 | lop.init = clop->init; 163 | lop.destroy = clop->destroy; 164 | lop.open = clop->open ? cuse_fll_open : NULL; 165 | lop.read = clop->read ? cuse_fll_read : NULL; 166 | lop.write = clop->write ? cuse_fll_write : NULL; 167 | lop.flush = clop->flush ? cuse_fll_flush : NULL; 168 | lop.release = clop->release ? cuse_fll_release : NULL; 169 | lop.fsync = clop->fsync ? cuse_fll_fsync : NULL; 170 | lop.ioctl = clop->ioctl ? cuse_fll_ioctl : NULL; 171 | lop.poll = clop->poll ? cuse_fll_poll : NULL; 172 | 173 | se = fuse_lowlevel_new_common(args, &lop, sizeof(lop), userdata); 174 | if (!se) { 175 | free(cd); 176 | return NULL; 177 | } 178 | ll = se->data; 179 | ll->cuse_data = cd; 180 | 181 | return se; 182 | } 183 | 184 | static int cuse_reply_init(fuse_req_t req, struct cuse_init_out *arg, 185 | char *dev_info, unsigned dev_info_len) 186 | { 187 | struct iovec iov[3]; 188 | 189 | iov[1].iov_base = arg; 190 | iov[1].iov_len = sizeof(struct cuse_init_out); 191 | iov[2].iov_base = dev_info; 192 | iov[2].iov_len = dev_info_len; 193 | 194 | return fuse_send_reply_iov_nofree(req, 0, iov, 3); 195 | } 196 | 197 | void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) 198 | { 199 | struct fuse_init_in *arg = (struct fuse_init_in *) inarg; 200 | struct cuse_init_out outarg; 201 | struct fuse_ll *f = req->f; 202 | struct cuse_data *cd = f->cuse_data; 203 | size_t bufsize = fuse_chan_bufsize(req->ch); 204 | struct cuse_lowlevel_ops *clop = req_clop(req); 205 | 206 | (void) nodeid; 207 | if (f->debug) { 208 | fprintf(stderr, "CUSE_INIT: %u.%u\n", arg->major, arg->minor); 209 | fprintf(stderr, "flags=0x%08x\n", arg->flags); 210 | } 211 | f->conn.proto_major = arg->major; 212 | f->conn.proto_minor = arg->minor; 213 | f->conn.capable = 0; 214 | f->conn.want = 0; 215 | 216 | if (arg->major < 7) { 217 | fprintf(stderr, "cuse: unsupported protocol version: %u.%u\n", 218 | arg->major, arg->minor); 219 | fuse_reply_err(req, EPROTO); 220 | return; 221 | } 222 | 223 | if (bufsize < FUSE_MIN_READ_BUFFER) { 224 | fprintf(stderr, "cuse: warning: buffer size too small: %zu\n", 225 | bufsize); 226 | bufsize = FUSE_MIN_READ_BUFFER; 227 | } 228 | 229 | bufsize -= 4096; 230 | if (bufsize < f->conn.max_write) 231 | f->conn.max_write = bufsize; 232 | 233 | f->got_init = 1; 234 | if (f->op.init) 235 | f->op.init(f->userdata, &f->conn); 236 | 237 | memset(&outarg, 0, sizeof(outarg)); 238 | outarg.major = FUSE_KERNEL_VERSION; 239 | outarg.minor = FUSE_KERNEL_MINOR_VERSION; 240 | outarg.flags = cd->flags; 241 | outarg.max_read = cd->max_read; 242 | outarg.max_write = f->conn.max_write; 243 | outarg.dev_major = cd->dev_major; 244 | outarg.dev_minor = cd->dev_minor; 245 | 246 | if (f->debug) { 247 | fprintf(stderr, " CUSE_INIT: %u.%u\n", 248 | outarg.major, outarg.minor); 249 | fprintf(stderr, " flags=0x%08x\n", outarg.flags); 250 | fprintf(stderr, " max_read=0x%08x\n", outarg.max_read); 251 | fprintf(stderr, " max_write=0x%08x\n", outarg.max_write); 252 | fprintf(stderr, " dev_major=%u\n", outarg.dev_major); 253 | fprintf(stderr, " dev_minor=%u\n", outarg.dev_minor); 254 | fprintf(stderr, " dev_info: %.*s\n", cd->dev_info_len, 255 | cd->dev_info); 256 | } 257 | 258 | cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len); 259 | 260 | if (clop->init_done) 261 | clop->init_done(f->userdata); 262 | 263 | fuse_free_req(req); 264 | } 265 | 266 | struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[], 267 | const struct cuse_info *ci, 268 | const struct cuse_lowlevel_ops *clop, 269 | int *multithreaded, void *userdata) 270 | { 271 | const char *devname = "/dev/cuse"; 272 | static const struct fuse_opt kill_subtype_opts[] = { 273 | FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_DISCARD), 274 | FUSE_OPT_END 275 | }; 276 | struct fuse_args args = FUSE_ARGS_INIT(argc, argv); 277 | struct fuse_session *se; 278 | struct fuse_chan *ch; 279 | int fd; 280 | int foreground; 281 | int res; 282 | 283 | res = fuse_parse_cmdline(&args, NULL, multithreaded, &foreground); 284 | if (res == -1) 285 | goto err_args; 286 | 287 | res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL); 288 | if (res == -1) 289 | goto err_args; 290 | 291 | /* 292 | * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos 293 | * would ensue. 294 | */ 295 | do { 296 | fd = open("/dev/null", O_RDWR); 297 | if (fd > 2) 298 | close(fd); 299 | } while (fd >= 0 && fd <= 2); 300 | 301 | se = cuse_lowlevel_new(&args, ci, clop, userdata); 302 | fuse_opt_free_args(&args); 303 | if (se == NULL) 304 | goto err_args; 305 | 306 | fd = open(devname, O_RDWR); 307 | if (fd == -1) { 308 | if (errno == ENODEV || errno == ENOENT) 309 | fprintf(stderr, "cuse: device not found, try 'modprobe cuse' first\n"); 310 | else 311 | fprintf(stderr, "cuse: failed to open %s: %s\n", 312 | devname, strerror(errno)); 313 | goto err_se; 314 | } 315 | 316 | ch = fuse_kern_chan_new(fd); 317 | if (!ch) { 318 | close(fd); 319 | goto err_se; 320 | } 321 | 322 | fuse_session_add_chan(se, ch); 323 | 324 | res = fuse_set_signal_handlers(se); 325 | if (res == -1) 326 | goto err_se; 327 | 328 | res = fuse_daemonize(foreground); 329 | if (res == -1) 330 | goto err_sig; 331 | 332 | return se; 333 | 334 | err_sig: 335 | fuse_remove_signal_handlers(se); 336 | err_se: 337 | fuse_session_destroy(se); 338 | err_args: 339 | fuse_opt_free_args(&args); 340 | return NULL; 341 | } 342 | 343 | void cuse_lowlevel_teardown(struct fuse_session *se) 344 | { 345 | fuse_remove_signal_handlers(se); 346 | fuse_session_destroy(se); 347 | } 348 | 349 | int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci, 350 | const struct cuse_lowlevel_ops *clop, void *userdata) 351 | { 352 | struct fuse_session *se; 353 | int multithreaded; 354 | int res; 355 | 356 | se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded, 357 | userdata); 358 | if (se == NULL) 359 | return 1; 360 | 361 | if (multithreaded) 362 | res = fuse_session_loop_mt(se); 363 | else 364 | res = fuse_session_loop(se); 365 | 366 | cuse_lowlevel_teardown(se); 367 | if (res == -1) 368 | return 1; 369 | 370 | return 0; 371 | } 372 | -------------------------------------------------------------------------------- /doc/mount.fuse.8: -------------------------------------------------------------------------------- 1 | .TH fuse "8" 2 | .SH NAME 3 | fuse \- format and options for the fuse file systems 4 | .SH DESCRIPTION 5 | FUSE (Filesystem in Userspace) is a simple interface for userspace programs to export a virtual filesystem to the Linux kernel. FUSE also aims to provide a secure method for non privileged users to create and mount their own filesystem implementations. 6 | .SH CONFIGURATION 7 | Some options regarding mount policy can be set in the file \fI/etc/fuse.conf\fP. Currently these options are: 8 | .TP 9 | \fBmount_max = NNN\fP 10 | Set the maximum number of FUSE mounts allowed to non-root users. The default is 1000. 11 | .TP 12 | \fBuser_allow_other\fP 13 | Allow non-root users to specify the \fBallow_other\fP or \fBallow_root\fP mount options (see below). 14 | .SH OPTIONS 15 | Most of the generic mount options described in \fBmount\fP are supported (\fBro\fP, \fBrw\fP, \fBsuid\fP, \fBnosuid\fP, \fBdev\fP, \fBnodev\fP, \fBexec\fP, \fBnoexec\fP, \fBatime\fP, \fBnoatime\fP, \fBsync\fP, \fBasync\fP, \fBdirsync\fP). Filesystems are mounted with \fBnodev,nosuid\fP by default, which can only be overridden by a privileged user. 16 | .SS "General mount options:" 17 | These are FUSE specific mount options that can be specified for all filesystems: 18 | .TP 19 | \fBdefault_permissions\fP 20 | By default FUSE doesn't check file access permissions, the filesystem is free to implement it's access policy or leave it to the underlying file access mechanism (e.g. in case of network filesystems). This option enables permission checking, restricting access based on file mode. This is option is usually useful together with the \fBallow_other\fP mount option. 21 | .TP 22 | \fBallow_other\fP 23 | This option overrides the security measure restricting file access to the user mounting the filesystem. So all users (including root) can access the files. This option is by default only allowed to root, but this restriction can be removed with a configuration option described in the previous section. 24 | .TP 25 | \fBallow_root\fP 26 | This option is similar to \fBallow_other\fP but file access is limited to the user mounting the filesystem and root. This option and \fBallow_other\fP are mutually exclusive. 27 | .TP 28 | \fBkernel_cache\fP 29 | This option disables flushing the cache of the file contents on every \fBopen\fP(2). This should only be enabled on filesystems, where the file data is never changed externally (not through the mounted FUSE filesystem). Thus it is not suitable for network filesystems and other \fIintermediate\fP filesystems. 30 | 31 | \fBNOTE\fP: if this option is not specified (and neither \fBdirect_io\fP) data is still cached after the \fBopen\fP(2), so a \fBread\fP(2) system call will not always initiate a read operation. 32 | .TP 33 | \fBauto_cache\fP 34 | This option enables automatic flushing of the data cache on \fBopen\fP(2). The cache will only be flushed if the modification time or the size of the file has changed. 35 | .TP 36 | \fBlarge_read\fP 37 | Issue large read requests. This can improve performance for some filesystems, but can also degrade performance. This option is only useful on 2.4.X kernels, as on 2.6 kernels requests size is automatically determined for optimum performance. 38 | .TP 39 | \fBdirect_io\fP 40 | This option disables the use of page cache (file content cache) in the kernel for this filesystem. This has several affects: 41 | .IP 1. 42 | Each \fBread\fP(2) or \fBwrite\fP(2) system call will initiate one or more read or write operations, data will not be cached in the kernel. 43 | .IP 2. 44 | The return value of the read() and write() system calls will correspond to the return values of the read and write operations. This is useful for example if the file size is not known in advance (before reading it). 45 | .TP 46 | \fBmax_read=N\fP 47 | With this option the maximum size of read operations can be set. The default is infinite. Note that the size of read requests is limited anyway to 32 pages (which is 128kbyte on i386). 48 | .TP 49 | \fBmax_readahead=N\fN 50 | Set the maximum number of bytes to read-ahead. The default is determined by the kernel. On linux-2.6.22 or earlier it's 131072 (128kbytes) 51 | .TP 52 | \fBmax_write=N\fP 53 | Set the maximum number of bytes in a single write operation. The default is 128kbytes. Note, that due to various limitations, the size of write requests can be much smaller (4kbytes). This limitation will be removed in the future. 54 | .TP 55 | \fBasync_read\fP 56 | Perform reads asynchronously. This is the default 57 | .TP 58 | \fBsync_read\fP 59 | Perform all reads (even read-ahead) synchronously. 60 | .TP 61 | \fBhard_remove\fP 62 | The default behavior is that if an open file is deleted, the file is renamed to a hidden file (\fB.fuse_hiddenXXX\fP), and only removed when the file is finally released. This relieves the filesystem implementation of having to deal with this problem. This option disables the hiding behavior, and files are removed immediately in an unlink operation (or in a rename operation which overwrites an existing file). 63 | 64 | It is recommended that you not use the hard_remove option. When hard_remove is set, the following libc functions fail on unlinked files (returning errno of \fBENOENT\fP): \fBread\fP(2), \fBwrite\fP(2), \fBfsync\fP(2), \fBclose\fP(2), \fBf*xattr\fP(2), \fBftruncate\fP(2), \fBfstat\fP(2), \fBfchmod\fP(2), \fBfchown\fP(2) 65 | .TP 66 | \fBdebug\fP 67 | Turns on debug information printing by the library. 68 | .TP 69 | \fBfsname=NAME\fP 70 | Sets the filesystem source (first field in \fI/etc/mtab\fP). The default is the mount program name. 71 | .TP 72 | \fBsubtype=TYPE\fP 73 | Sets the filesystem type (third field in \fI/etc/mtab\fP). The default is the mount program name. If the kernel suppports it, \fI/etc/mtab\fP and \fI/proc/mounts\fP will show the filesystem type as \fBfuse.TYPE\fP 74 | 75 | If the kernel doesn't support subtypes, the source filed will be \fBTYPE#NAME\fP, or if \fBfsname\fP option is not specified, just \fBTYPE\fP. 76 | .TP 77 | \fBuse_ino\fP 78 | Honor the \fIst_ino\fP field in kernel functions \fBgetattr()\fP and \fBfill_dir()\fP. This value is used to fill in the 79 | \fIst_ino\fP field in the \fBstat\fP(2), \fBlstat\fP(2), \fBfstat\fP(2) functions and the \fId_ino\fP field in the \fBreaddir\fP(2) function. The filesystem does not have to guarantee uniqueness, however some applications rely on this value being unique for the whole filesystem. 80 | .TP 81 | \fBreaddir_ino\fP 82 | If \fBuse_ino\fP option is not given, still try to fill in the \fId_ino\fP field in \fBreaddir\fP(2). If the name was previously looked up, and is still in the cache, the inode number found there will be used. Otherwise it will be set to \fB-1\fP. If \fBuse_ino\fP option is given, this option is ignored. 83 | .TP 84 | \fBnonempty\fP 85 | Allows mounts over a non-empty file or directory. By default these mounts are rejected to prevent accidental covering up of data, which could for example prevent automatic backup. 86 | .TP 87 | \fBumask=M\fP 88 | Override the permission bits in \fIst_mode\fP set by the filesystem. The resulting permission bits are the ones missing from the given umask value. The value is given in octal representation. 89 | .TP 90 | \fBuid=N\fP 91 | Override the \fIst_uid\fP field set by the filesystem (N is numeric). 92 | .TP 93 | \fBgid=N\fP 94 | Override the \fIst_gid\fP field set by the filesystem (N is numeric). 95 | .TP 96 | \fBblkdev\fP 97 | Mount a filesystem backed by a block device. This is a privileged option. The device must be specified with the \fBfsname=NAME\fP option. 98 | .TP 99 | \fBentry_timeout=T\fP 100 | The timeout in seconds for which name lookups will be cached. The default is 1.0 second. For all the timeout options, it is possible to give fractions of a second as well (e.g. \fBentry_timeout=2.8\fP) 101 | .TP 102 | \fBnegative_timeout=T\fP 103 | The timeout in seconds for which a negative lookup will be cached. This means, that if file did not exist (lookup retuned \fBENOENT\fP), the lookup will only be redone after the timeout, and the file/directory will be assumed to not exist until then. The default is 0.0 second, meaning that caching negative lookups are disabled. 104 | .TP 105 | \fBattr_timeout=T\fP 106 | The timeout in seconds for which file/directory attributes are cached. The default is 1.0 second. 107 | .TP 108 | \fBac_attr_timeout=T\fP 109 | The timeout in seconds for which file attributes are cached for the purpose of checking if \fBauto_cache\fP should flush the file data on open. The default is the value of \fBattr_timeout\fP 110 | .TP 111 | \fBintr\fP 112 | Allow requests to be interrupted. Turning on this option may result in unexpected behavior, if the filesystem does not support request interruption. 113 | .TP 114 | \fBintr_signal=NUM\fP 115 | Specify which signal number to send to the filesystem when a request is interrupted. The default is hardcoded to USR1. 116 | .TP 117 | \fBmodules=M1[:M2...]\fP 118 | Add modules to the filesystem stack. Modules are pushed in the order they are specified, with the original filesystem being on the bottom of the stack. 119 | .SH FUSE MODULES (STACKING) 120 | Modules are filesystem stacking support to high level API. Filesystem modules can be built into libfuse or loaded from shared object 121 | .SS "iconv" 122 | Perform file name character set conversion. Options are: 123 | .TP 124 | \fBfrom_code=CHARSET\fP 125 | Character set to convert from (see \fBiconv -l\fP for a list of possible values). Default is \fBUTF-8\fP. 126 | .TP 127 | \fBto_code=CHARSET\fP 128 | Character set to convert to. Default is determined by the current locale. 129 | .SS "subdir" 130 | Prepend a given directory to each path. Options are: 131 | .TP 132 | \fBsubdir=DIR\fP 133 | Directory to prepend to all paths. This option is \fImandatory\fP. 134 | .TP 135 | \fBrellinks\fP 136 | Transform absolute symlinks into relative 137 | .TP 138 | \fBnorellinks\fP 139 | Do not transform absolute symlinks into relative. This is the default. 140 | .SH SECURITY 141 | The fusermount program is installed set-user-gid to fuse. This is done to allow users from fuse group to mount 142 | their own filesystem implementations. 143 | There must however be some limitations, in order to prevent Bad User from 144 | doing nasty things. Currently those limitations are: 145 | .IP 1. 146 | The user can only mount on a mountpoint, for which it has write permission 147 | .IP 2. 148 | The mountpoint is not a sticky directory which isn't owned by the user (like \fI/tmp\fP usually is) 149 | .IP 3. 150 | No other user (including root) can access the contents of the mounted filesystem. 151 | .SH NOTE 152 | FUSE filesystems are unmounted using the \fBfusermount\fP(1) command (\fBfusermount -u mountpoint\fP). 153 | .SH "AUTHORS" 154 | .LP 155 | The main author of FUSE is Miklos Szeredi . 156 | .LP 157 | This man page was written by Bastien Roucaries for the 158 | Debian GNU/Linux distribution (but it may be used by others) from README file. 159 | .SH SEE ALSO 160 | fusermount(1) 161 | mount(8) 162 | --------------------------------------------------------------------------------