From: Miklos Szeredi Date: Mon, 16 Jun 2008 14:16:02 +0000 (+0000) Subject: Remove fuse kernel module sources X-Git-Tag: fuse_2_8_0_pre2~15 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=c75d6298b4d083b5e8b99117602e75fbe9ddd45b;p=qemu-gpiodev%2Flibfuse.git Remove fuse kernel module sources --- diff --git a/ChangeLog b/ChangeLog index 18a479a..dee8a3b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2008-06-16 Miklos Szeredi + + * Remove fuse kernel module sources. Linux 2.6.27 will support + NFS exporting. + 2008-06-10 Miklos Szeredi * Fix theoretical infinite loops in libfuse. Reported by Szabolcs diff --git a/Makefile.am b/Makefile.am index 06014ee..772dfcc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in -SUBDIRS = @subdirs@ @subdirs2@ +SUBDIRS = @subdirs2@ EXTRA_DIST = \ fuse.pc.in \ diff --git a/README b/README index 4d9dd92..398dd65 100644 --- a/README +++ b/README @@ -35,25 +35,8 @@ modprobe fuse You may also need to add '/usr/local/lib' to '/etc/ld.so.conf' and/or run ldconfig. -Linux kernels 2.6.14 or later contain FUSE support out of the box. If -FUSE support is detected, the kernel module in this package will not -be compiled. It is possible to override this with the -'--enable-kernel-module' configure option. - -If './configure' cannot find the kernel source or it says the kernel -source should be prepared, you may either try - - ./configure --disable-kernel-module - -or if your kernel does not already contain FUSE support, do the -following: - - - Extract the kernel source to some directory - - - Copy the running kernel's config (usually found in - /boot/config-X.Y.Z) to .config at the top of the source tree - - - Run 'make prepare' +You'll also need a fuse kernel module, Linux kernels 2.6.14 or later +contain FUSE support. For more details see the file 'INSTALL' diff --git a/README.NFS b/README.NFS index a6f48c5..b805f39 100644 --- a/README.NFS +++ b/README.NFS @@ -1,17 +1,4 @@ -FUSE module in official kernels (>= 2.6.14) don't support NFS -exporting. In this case if you need NFS exporting capability, use the -'--enable-kernel-module' configure option to compile the module from -this package. And make sure, that the FUSE is not compiled into the -kernel (CONFIG_FUSE_FS must be 'm' or 'n'). +NFS exporting is supported in Linux kernels 2.6.27 or later. You need to add an fsid=NNN option to /etc/exports to make exporting a FUSE directory work. - -You may get ESTALE (Stale NFS file handle) errors with this. This is -because the current FUSE kernel API and the userspace library cannot -handle a situation where the kernel forgets about an inode which is -still referenced by the remote NFS client. This problem will be -addressed in a later version. - -In the future it planned that NFS exporting will be done solely in -userspace. diff --git a/configure.in b/configure.in index 6c4f484..61d3ca8 100644 --- a/configure.in +++ b/configure.in @@ -23,8 +23,6 @@ if test "$ac_env_CFLAGS_set" != set; then CFLAGS="-Wall -W -Wno-sign-compare -Wstrict-prototypes -Wmissing-declarations -Wwrite-strings -g -O2 -fno-strict-aliasing" fi -AC_ARG_ENABLE(kernel-module, - [ --enable-kernel-module Compile kernel module ]) AC_ARG_ENABLE(lib, [ --enable-lib Compile with library ]) AC_ARG_ENABLE(util, @@ -42,10 +40,6 @@ AC_SUBST(pkgconfigdir) subdirs2="include" -if test "$arch" = linux -a "$enable_kernel_module" != "no"; then - AC_CONFIG_SUBDIRS(kernel) -fi - if test "$enable_lib" != "no"; then subdirs2="$subdirs2 lib"; fi diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h new file mode 100644 index 0000000..c9c4c7e --- /dev/null +++ b/include/fuse_kernel.h @@ -0,0 +1,441 @@ +/* + This file defines the kernel interface of FUSE + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. + + This -- and only this -- header file may also be distributed under + the terms of the BSD Licence as follows: + + Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +/* + * This file defines the kernel interface of FUSE + * + * Protocol changelog: + * + * 7.9: + * - new fuse_getattr_in input argument of GETATTR + * - add lk_flags in fuse_lk_in + * - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in + * - add blksize field to fuse_attr + * - add file flags field to fuse_read_in and fuse_write_in + */ + +#ifndef linux +#include +#define __u64 uint64_t +#define __u32 uint32_t +#define __s32 int32_t +#else +#include +#include +#endif + +/** Version number of this interface */ +#define FUSE_KERNEL_VERSION 7 + +/** Minor version number of this interface */ +#define FUSE_KERNEL_MINOR_VERSION 9 + +/** The node ID of the root inode */ +#define FUSE_ROOT_ID 1 + +/** The major number of the fuse character device */ +#define FUSE_MAJOR MISC_MAJOR + +/** The minor number of the fuse character device */ +#define FUSE_MINOR 229 + +/* Make sure all structures are padded to 64bit boundary, so 32bit + userspace works under 64bit kernels */ + +struct fuse_attr { + __u64 ino; + __u64 size; + __u64 blocks; + __u64 atime; + __u64 mtime; + __u64 ctime; + __u32 atimensec; + __u32 mtimensec; + __u32 ctimensec; + __u32 mode; + __u32 nlink; + __u32 uid; + __u32 gid; + __u32 rdev; + __u32 blksize; + __u32 padding; +}; + +struct fuse_kstatfs { + __u64 blocks; + __u64 bfree; + __u64 bavail; + __u64 files; + __u64 ffree; + __u32 bsize; + __u32 namelen; + __u32 frsize; + __u32 padding; + __u32 spare[6]; +}; + +struct fuse_file_lock { + __u64 start; + __u64 end; + __u32 type; + __u32 pid; /* tgid */ +}; + +/** + * Bitmasks for fuse_setattr_in.valid + */ +#define FATTR_MODE (1 << 0) +#define FATTR_UID (1 << 1) +#define FATTR_GID (1 << 2) +#define FATTR_SIZE (1 << 3) +#define FATTR_ATIME (1 << 4) +#define FATTR_MTIME (1 << 5) +#define FATTR_FH (1 << 6) +#define FATTR_ATIME_NOW (1 << 7) +#define FATTR_MTIME_NOW (1 << 8) +#define FATTR_LOCKOWNER (1 << 9) + +/** + * Flags returned by the OPEN request + * + * FOPEN_DIRECT_IO: bypass page cache for this open file + * FOPEN_KEEP_CACHE: don't invalidate the data cache on open + */ +#define FOPEN_DIRECT_IO (1 << 0) +#define FOPEN_KEEP_CACHE (1 << 1) + +/** + * INIT request/reply flags + */ +#define FUSE_ASYNC_READ (1 << 0) +#define FUSE_POSIX_LOCKS (1 << 1) +#define FUSE_FILE_OPS (1 << 2) +#define FUSE_ATOMIC_O_TRUNC (1 << 3) +#define FUSE_BIG_WRITES (1 << 5) + +/** + * Release flags + */ +#define FUSE_RELEASE_FLUSH (1 << 0) + +/** + * Getattr flags + */ +#define FUSE_GETATTR_FH (1 << 0) + +/** + * Lock flags + */ +#define FUSE_LK_FLOCK (1 << 0) + +/** + * WRITE flags + * + * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed + * FUSE_WRITE_LOCKOWNER: lock_owner field is valid + */ +#define FUSE_WRITE_CACHE (1 << 0) +#define FUSE_WRITE_LOCKOWNER (1 << 1) + +/** + * Read flags + */ +#define FUSE_READ_LOCKOWNER (1 << 1) + +enum fuse_opcode { + FUSE_LOOKUP = 1, + FUSE_FORGET = 2, /* no reply */ + FUSE_GETATTR = 3, + FUSE_SETATTR = 4, + FUSE_READLINK = 5, + FUSE_SYMLINK = 6, + FUSE_MKNOD = 8, + FUSE_MKDIR = 9, + FUSE_UNLINK = 10, + FUSE_RMDIR = 11, + FUSE_RENAME = 12, + FUSE_LINK = 13, + FUSE_OPEN = 14, + FUSE_READ = 15, + FUSE_WRITE = 16, + FUSE_STATFS = 17, + FUSE_RELEASE = 18, + FUSE_FSYNC = 20, + FUSE_SETXATTR = 21, + FUSE_GETXATTR = 22, + FUSE_LISTXATTR = 23, + FUSE_REMOVEXATTR = 24, + FUSE_FLUSH = 25, + FUSE_INIT = 26, + FUSE_OPENDIR = 27, + FUSE_READDIR = 28, + FUSE_RELEASEDIR = 29, + FUSE_FSYNCDIR = 30, + FUSE_GETLK = 31, + FUSE_SETLK = 32, + FUSE_SETLKW = 33, + FUSE_ACCESS = 34, + FUSE_CREATE = 35, + FUSE_INTERRUPT = 36, + FUSE_BMAP = 37, + FUSE_DESTROY = 38, +}; + +/* The read buffer is required to be at least 8k, but may be much larger */ +#define FUSE_MIN_READ_BUFFER 8192 + +#define FUSE_COMPAT_ENTRY_OUT_SIZE 120 + +struct fuse_entry_out { + __u64 nodeid; /* Inode ID */ + __u64 generation; /* Inode generation: nodeid:gen must + be unique for the fs's lifetime */ + __u64 entry_valid; /* Cache timeout for the name */ + __u64 attr_valid; /* Cache timeout for the attributes */ + __u32 entry_valid_nsec; + __u32 attr_valid_nsec; + struct fuse_attr attr; +}; + +struct fuse_forget_in { + __u64 nlookup; +}; + +struct fuse_getattr_in { + __u32 getattr_flags; + __u32 dummy; + __u64 fh; +}; + +#define FUSE_COMPAT_ATTR_OUT_SIZE 96 + +struct fuse_attr_out { + __u64 attr_valid; /* Cache timeout for the attributes */ + __u32 attr_valid_nsec; + __u32 dummy; + struct fuse_attr attr; +}; + +struct fuse_mknod_in { + __u32 mode; + __u32 rdev; +}; + +struct fuse_mkdir_in { + __u32 mode; + __u32 padding; +}; + +struct fuse_rename_in { + __u64 newdir; +}; + +struct fuse_link_in { + __u64 oldnodeid; +}; + +struct fuse_setattr_in { + __u32 valid; + __u32 padding; + __u64 fh; + __u64 size; + __u64 lock_owner; + __u64 atime; + __u64 mtime; + __u64 unused2; + __u32 atimensec; + __u32 mtimensec; + __u32 unused3; + __u32 mode; + __u32 unused4; + __u32 uid; + __u32 gid; + __u32 unused5; +}; + +struct fuse_open_in { + __u32 flags; + __u32 mode; +}; + +struct fuse_open_out { + __u64 fh; + __u32 open_flags; + __u32 padding; +}; + +struct fuse_release_in { + __u64 fh; + __u32 flags; + __u32 release_flags; + __u64 lock_owner; +}; + +struct fuse_flush_in { + __u64 fh; + __u32 unused; + __u32 padding; + __u64 lock_owner; +}; + +struct fuse_read_in { + __u64 fh; + __u64 offset; + __u32 size; + __u32 read_flags; + __u64 lock_owner; + __u32 flags; + __u32 padding; +}; + +#define FUSE_COMPAT_WRITE_IN_SIZE 24 + +struct fuse_write_in { + __u64 fh; + __u64 offset; + __u32 size; + __u32 write_flags; + __u64 lock_owner; + __u32 flags; + __u32 padding; +}; + +struct fuse_write_out { + __u32 size; + __u32 padding; +}; + +#define FUSE_COMPAT_STATFS_SIZE 48 + +struct fuse_statfs_out { + struct fuse_kstatfs st; +}; + +struct fuse_fsync_in { + __u64 fh; + __u32 fsync_flags; + __u32 padding; +}; + +struct fuse_setxattr_in { + __u32 size; + __u32 flags; +}; + +struct fuse_getxattr_in { + __u32 size; + __u32 padding; +}; + +struct fuse_getxattr_out { + __u32 size; + __u32 padding; +}; + +struct fuse_lk_in { + __u64 fh; + __u64 owner; + struct fuse_file_lock lk; + __u32 lk_flags; + __u32 padding; +}; + +struct fuse_lk_out { + struct fuse_file_lock lk; +}; + +struct fuse_access_in { + __u32 mask; + __u32 padding; +}; + +struct fuse_init_in { + __u32 major; + __u32 minor; + __u32 max_readahead; + __u32 flags; +}; + +struct fuse_init_out { + __u32 major; + __u32 minor; + __u32 max_readahead; + __u32 flags; + __u32 unused; + __u32 max_write; +}; + +struct fuse_interrupt_in { + __u64 unique; +}; + +struct fuse_bmap_in { + __u64 block; + __u32 blocksize; + __u32 padding; +}; + +struct fuse_bmap_out { + __u64 block; +}; + +struct fuse_in_header { + __u32 len; + __u32 opcode; + __u64 unique; + __u64 nodeid; + __u32 uid; + __u32 gid; + __u32 pid; + __u32 padding; +}; + +struct fuse_out_header { + __u32 len; + __s32 error; + __u64 unique; +}; + +struct fuse_dirent { + __u64 ino; + __u64 off; + __u32 namelen; + __u32 type; + char name[0]; +}; + +#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) +#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1)) +#define FUSE_DIRENT_SIZE(d) \ + FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) diff --git a/kernel/.cvsignore b/kernel/.cvsignore deleted file mode 100644 index ed24c33..0000000 --- a/kernel/.cvsignore +++ /dev/null @@ -1,12 +0,0 @@ -Makefile -configure -config.* -*.cache -.deps -.*.cmd -*.mod.c -*.ko -*.s -.tmp_versions -.*.d -*.symvers diff --git a/kernel/Makefile.in b/kernel/Makefile.in deleted file mode 100644 index e26c700..0000000 --- a/kernel/Makefile.in +++ /dev/null @@ -1,53 +0,0 @@ -# Makefile.in for kernel module - -SHELL = /bin/sh -INSTALL = @INSTALL@ -mkdir_p = mkdir -p -VERSION = @PACKAGE_VERSION@ - -DISTFILES = Makefile.in configure.ac configure config.h.in ../install-sh \ - dev.c dir.c file.c inode.c fuse_i.h fuse_kernel.h control.c - -fusemoduledir = @kmoduledir@/kernel/fs/fuse - -fusemodule := fuse.ko - -all: all-@ENABLE_FUSE_MODULE@ -install: install-@ENABLE_FUSE_MODULE@ -uninstall: uninstall-@ENABLE_FUSE_MODULE@ - -all-n: -install-n: -uninstall-n: - -all-y: all-spec - -install-y: all - $(mkdir_p) $(DESTDIR)$(fusemoduledir) - $(INSTALL) -m 644 $(fusemodule) $(DESTDIR)$(fusemoduledir)/$(fusemodule) - -/sbin/depmod -a - -uninstall-y: - rm -f $(DESTDIR)$(fusemoduledir)/$(fusemodule) - -/sbin/depmod -a - -clean: - -rm -f $(fusemodule) *.o .*.cmd *.mod.c *.ko *.s */*.o - -distclean: clean - rm -f Makefile - rm -f config.h config.log config.status config.cache - rm -rf .tmp_versions - -maintainer-clean: distclean - -distdir: $(DISTFILES) - cp -p $(DISTFILES) $(distdir) - -EXTRA_CFLAGS += -DFUSE_VERSION=\"$(VERSION)\" - -obj-m := fuse.o -fuse-objs := dev.o dir.o file.o inode.o control.o - -all-spec: - $(MAKE) -C @kernelsrc@ SUBDIRS=`pwd` @KERNELMAKE_PARAMS@ modules diff --git a/kernel/configure.ac b/kernel/configure.ac deleted file mode 100644 index fb42264..0000000 --- a/kernel/configure.ac +++ /dev/null @@ -1,230 +0,0 @@ -AC_INIT(fuse-kernel, 2.8.0-pre0) -AC_CONFIG_HEADERS([config.h]) - -AC_PROG_INSTALL - -runver=`uname -r` -bad_kernel_version=no -ENABLE_FUSE_MODULE=y -KERNELCFLAGS= - -kernelsrc= -kernelbuild= -AC_ARG_WITH(kernel, - [ --with-kernel=PATH Specify location of kernel source ], - [kernelsrc="$withval"; kernelbuild="$withval"]) -AC_ARG_WITH(kernel-build, - [ --with-kernel-build=PATH Specify location of kernel build ], - [kernelbuild="$withval"]) -AC_ARG_ENABLE(kernel-module, - [ --enable-kernel-module Compile kernel module ]) - -if test -z "$enable_kernel_module" -a -z "$kernelbuild" && echo "$runver" | grep -q "^2.6"; then - checkmodule=no - AC_MSG_CHECKING([if FUSE is loaded as a module]) - if cat /proc/modules | grep -q "^fuse "; then - AC_MSG_RESULT([yes]) - checkmodule=yes - else - AC_MSG_RESULT([no]) - AC_MSG_CHECKING([if FUSE module is built into the kernel]) - if test -e /sys/class/misc/fuse; then - AC_MSG_RESULT([yes]) - ENABLE_FUSE_MODULE=n - else - AC_MSG_RESULT([no]) - checkmodule=yes - fi - fi - if test "$checkmodule" = yes; then - AC_MSG_CHECKING([if FUSE module is from official kernel]) - if test ! -f /lib/modules/${runver}/kernel/fs/fuse/fuse.ko; then - AC_MSG_RESULT([no]) - elif fgrep -q "fuse distribution version: " /lib/modules/${runver}/kernel/fs/fuse/fuse.ko 2> /dev/null; then - AC_MSG_RESULT([no]) - else - AC_MSG_RESULT([yes]) - ENABLE_FUSE_MODULE=n - fi - fi -fi - -if test "$ENABLE_FUSE_MODULE" = y; then - AC_MSG_CHECKING([kernel source directory]) - if test -z "$kernelsrc"; then - kernelbuild= - sourcelink=/lib/modules/${runver}/source - buildlink=/lib/modules/${runver}/build - - if test -e $sourcelink; then - kernelsrc=`(cd $sourcelink; /bin/pwd)` - fi - if test -e $buildlink; then - kernelbuild=`(cd $buildlink; /bin/pwd)` - fi - if test -z "$kernelsrc"; then - kernelsrc=$kernelbuild - fi - if test -z "$kernelsrc" -o -z "$kernelbuild"; then - AC_MSG_RESULT([Not found]) - AC_MSG_ERROR([ - *** Please specify the location of the kernel source with - *** the '--with-kernel=SRCDIR' option]) - fi - fi - AC_MSG_RESULT([$kernelsrc]) - AC_MSG_CHECKING([kernel build directory]) - AC_MSG_RESULT([$kernelbuild]) - - AC_MSG_CHECKING([kernel source version]) - if test -r $kernelbuild/include/linux/version.h && fgrep -q UTS_RELEASE $kernelbuild/include/linux/version.h; then - kernsrcver=`(echo "#include "; echo "kernsrcver=UTS_RELEASE") | cpp -I $kernelbuild/include | grep "^kernsrcver=" | cut -d \" -f 2` - elif test -r $kernelbuild/include/linux/utsrelease.h && fgrep -q UTS_RELEASE $kernelbuild/include/linux/utsrelease.h; then - kernsrcver=`(echo "#include "; echo "kernsrcver=UTS_RELEASE") | cpp -I $kernelbuild/include | grep "^kernsrcver=" | cut -d \" -f 2` - fi - if test -z "$kernsrcver"; then - AC_MSG_RESULT([Not found]) - AC_MSG_ERROR([ - *** Cannot determine the version of the linux kernel source. Please - *** prepare the kernel before running this script]) - fi - AC_MSG_RESULT([$kernsrcver]) - kmoduledir=${INSTALL_MOD_PATH}/lib/modules/$kernsrcver - AC_SUBST(kernelsrc) - AC_SUBST(kmoduledir) - - if echo "$kernsrcver" | egrep -q ["^(2.4|2.6.[0-8]([^0-9]|\$))"]; then - bad_kernel_version=yes - AC_MSG_NOTICE([ -NOTE: Disabled building the kernel module, because this release only -NOTE: supports Linux versions 2.6.9 or later. You can use the kernel -NOTE: module from an earlier FUSE release with the library from this -NOTE: release.]) - else - fuse_configured=no - kernel_autoconf=$kernelbuild/include/linux/autoconf.h - AC_MSG_CHECKING([if FUSE is configured in the kernel]) - if test -f $kernel_autoconf; then - if grep -q "^#define CONFIG_FUSE_FS 1" $kernel_autoconf || grep -q "^#define CONFIG_FUSE_FS_MODULE 1" $kernel_autoconf; then - fuse_configured=yes - fi - fi - AC_MSG_RESULT([$fuse_configured]) - if test -z "$enable_kernel_module" -a "$fuse_configured" = yes; then - ENABLE_FUSE_MODULE=n - fi - fi -fi - -if test "$ENABLE_FUSE_MODULE" = n; then - AC_MSG_NOTICE([ -NOTE: Detected that FUSE is already present in the kernel, so -NOTE: building of kernel module is disabled. To force building -NOTE: of kernel module use the '--enable-kernel-module' option.]) -fi - -if test "$enable_kernel_module" = no; then - ENABLE_FUSE_MODULE=n -fi -if test "$bad_kernel_version" = yes; then - ENABLE_FUSE_MODULE=n -fi - -AC_SUBST(ENABLE_FUSE_MODULE) - -if test "$ENABLE_FUSE_MODULE" = y; then - AC_MSG_CHECKING([if kernel defines kzalloc function]) - if egrep -qw "kzalloc" $kernelsrc/include/linux/slab.h; then - AC_DEFINE(HAVE_KZALLOC, 1, [kzalloc() is defined]) - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - - AC_MSG_CHECKING([if kernel defines fs_subsys]) - if egrep -qw "fs_subsys" $kernelsrc/include/linux/fs.h; then - AC_DEFINE(HAVE_FS_SUBSYS, 1, [fs_subsys is defined]) - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - - AC_MSG_CHECKING([whether lookup_instantiate_filp is defined]) - if test -f $kernelsrc/include/linux/namei.h && egrep -q "lookup_instantiate_filp" $kernelsrc/include/linux/namei.h; then - AC_DEFINE(HAVE_LOOKUP_INSTANTIATE_FILP, 1, [lookup_instantiate_filp() is defined]) - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - - AC_MSG_CHECKING([if umount_begin is passed a vfsmount]) - if egrep -q "\(\*umount_begin\) *\(struct vfsmount \*" $kernelsrc/include/linux/fs.h; then - AC_DEFINE(UMOUNT_BEGIN_VFSMOUNT, 1, [umount_begin is passed a vfsmount]) - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - - AC_MSG_CHECKING([if inode has i_blksize field]) - if egrep -qw "i_blksize" $kernelsrc/include/linux/fs.h; then - AC_DEFINE(HAVE_I_BLKSIZE, 1, [inode has i_blksize field]) - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - AC_MSG_CHECKING([if inode has i_private field]) - if egrep -qw "i_private" $kernelsrc/include/linux/fs.h; then - AC_DEFINE(HAVE_I_PRIVATE, 1, [inode has i_private field]) - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - AC_MSG_CHECKING([if inode has i_mutex field ]) - if egrep -qw "i_mutex" $kernelsrc/include/linux/fs.h; then - AC_DEFINE(HAVE_I_MUTEX, 1, [inode has i_mutex field]) - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - AC_MSG_CHECKING([if kernel has mutex.h ]) - if test -f $kernelsrc/include/linux/mutex.h; then - AC_DEFINE(HAVE_MUTEX_H, 1, [kernel has mutex.h]) - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - AC_MSG_CHECKING([if kernel has exportfs.h ]) - if test -f $kernelsrc/include/linux/exportfs.h; then - AC_DEFINE(HAVE_EXPORTFS_H, 1, [kernel has exportfs.h]) - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - AC_MSG_CHECKING([if kernel has BLOCK option ]) - if test -f $kernelsrc/block/Kconfig && egrep -q "config *BLOCK" $kernelsrc/block/Kconfig; then - AC_DEFINE(HAVE_CONFIG_BLOCK, 1, [kernel has BLOCK option]) - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - - isuml=no - KERNELMAKE_PARAMS= - KERNELCPPFLAGS= - AC_MSG_CHECKING([if this is user mode linux]) - if test -f $kernelbuild/include/linux/autoconf.h && egrep -q "^#define CONFIG_(USERMODE|UML) 1" $kernelbuild/include/linux/autoconf.h; then - isuml=yes - KERNELMAKE_PARAMS="ARCH=um" - KERNELCPPFLAGS="-D__arch_um__ -DSUBARCH=\\\"i386\\\" -D_LARGEFILE64_SOURCE -I${kernelsrc}/arch/um/include -Derrno=kernel_errno -I${kernelsrc}/arch/um/kernel/tt/include -I${kernelsrc}/arch/um/kernel/skas/include" - fi - AC_MSG_RESULT([$isuml]) - if test "$kernelbuild" != "$kernelsrc"; then - KERNELMAKE_PARAMS="$KERNELMAKE_PARAMS O=$kernelbuild" - fi - AC_SUBST(KERNELMAKE_PARAMS) - AC_SUBST(KERNELCPPFLAGS) - AC_SUBST(KERNELCFLAGS) -fi - -AC_CONFIG_FILES([Makefile]) -AC_OUTPUT diff --git a/kernel/control.c b/kernel/control.c deleted file mode 100644 index 5ffe6ba..0000000 --- a/kernel/control.c +++ /dev/null @@ -1,254 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2007 Miklos Szeredi - - This program can be distributed under the terms of the GNU GPL. - See the file COPYING. -*/ - -#include "fuse_i.h" - -#include -#include - -#define FUSE_CTL_SUPER_MAGIC 0x65735543 -#ifndef HAVE_I_PRIVATE -#define i_private u.generic_ip -#endif - -/* - * This is non-NULL when the single instance of the control filesystem - * exists. Protected by fuse_mutex - */ -static struct super_block *fuse_control_sb; - -static struct fuse_conn *fuse_ctl_file_conn_get(struct file *file) -{ - struct fuse_conn *fc; - mutex_lock(&fuse_mutex); - fc = file->f_dentry->d_inode->i_private; - if (fc) - fc = fuse_conn_get(fc); - mutex_unlock(&fuse_mutex); - return fc; -} - -static ssize_t fuse_conn_abort_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct fuse_conn *fc = fuse_ctl_file_conn_get(file); - if (fc) { - fuse_abort_conn(fc); - fuse_conn_put(fc); - } - return count; -} - -static ssize_t fuse_conn_waiting_read(struct file *file, char __user *buf, - size_t len, loff_t *ppos) -{ - char tmp[32]; - size_t size; - - if (!*ppos) { - struct fuse_conn *fc = fuse_ctl_file_conn_get(file); - if (!fc) - return 0; - - file->private_data=(void *)(long)atomic_read(&fc->num_waiting); - fuse_conn_put(fc); - } - size = sprintf(tmp, "%ld\n", (long)file->private_data); - return simple_read_from_buffer(buf, len, ppos, tmp, size); -} - -static struct file_operations fuse_ctl_abort_ops = { - .open = nonseekable_open, - .write = fuse_conn_abort_write, -}; - -static struct file_operations fuse_ctl_waiting_ops = { - .open = nonseekable_open, - .read = fuse_conn_waiting_read, -}; - -#ifndef KERNEL_2_6_10_PLUS -struct dentry *d_alloc_name(struct dentry *parent, const char *name) -{ - struct qstr q; - - q.name = (const unsigned char *) name; - q.len = strlen(name); - q.hash = full_name_hash(q.name, q.len); - return d_alloc(parent, &q); -} -#endif -static struct dentry *fuse_ctl_add_dentry(struct dentry *parent, - struct fuse_conn *fc, - const char *name, - int mode, int nlink, -#ifdef KERNEL_2_6_21_PLUS - const struct inode_operations *iop, -#else - struct inode_operations *iop, -#endif -#ifdef KERNEL_2_6_17_PLUS - const struct file_operations *fop -#else - struct file_operations *fop -#endif -) -{ - struct dentry *dentry; - struct inode *inode; - - BUG_ON(fc->ctl_ndents >= FUSE_CTL_NUM_DENTRIES); - dentry = d_alloc_name(parent, name); - if (!dentry) - return NULL; - - fc->ctl_dentry[fc->ctl_ndents++] = dentry; - inode = new_inode(fuse_control_sb); - if (!inode) - return NULL; - - inode->i_mode = mode; - inode->i_uid = fc->user_id; - inode->i_gid = fc->group_id; - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - /* setting ->i_op to NULL is not allowed */ - if (iop) - inode->i_op = iop; - inode->i_fop = fop; - inode->i_nlink = nlink; - inode->i_private = fc; - d_add(dentry, inode); - return dentry; -} - -/* - * Add a connection to the control filesystem (if it exists). Caller - * must hold fuse_mutex - */ -int fuse_ctl_add_conn(struct fuse_conn *fc) -{ - struct dentry *parent; - char name[32]; - - if (!fuse_control_sb) - return 0; - - parent = fuse_control_sb->s_root; - inc_nlink(parent->d_inode); - sprintf(name, "%llu", (unsigned long long) fc->id); - parent = fuse_ctl_add_dentry(parent, fc, name, S_IFDIR | 0500, 2, - &simple_dir_inode_operations, - &simple_dir_operations); - if (!parent) - goto err; - - if (!fuse_ctl_add_dentry(parent, fc, "waiting", S_IFREG | 0400, 1, - NULL, &fuse_ctl_waiting_ops) || - !fuse_ctl_add_dentry(parent, fc, "abort", S_IFREG | 0200, 1, - NULL, &fuse_ctl_abort_ops)) - goto err; - - return 0; - - err: - fuse_ctl_remove_conn(fc); - return -ENOMEM; -} - -/* - * Remove a connection from the control filesystem (if it exists). - * Caller must hold fuse_mutex - */ -void fuse_ctl_remove_conn(struct fuse_conn *fc) -{ - int i; - - if (!fuse_control_sb) - return; - - for (i = fc->ctl_ndents - 1; i >= 0; i--) { - struct dentry *dentry = fc->ctl_dentry[i]; - dentry->d_inode->i_private = NULL; - d_drop(dentry); - dput(dentry); - } - fuse_control_sb->s_root->d_inode->i_nlink--; -} - -static int fuse_ctl_fill_super(struct super_block *sb, void *data, int silent) -{ - struct tree_descr empty_descr = {""}; - struct fuse_conn *fc; - int err; - - err = simple_fill_super(sb, FUSE_CTL_SUPER_MAGIC, &empty_descr); - if (err) - return err; - - mutex_lock(&fuse_mutex); - BUG_ON(fuse_control_sb); - fuse_control_sb = sb; - list_for_each_entry(fc, &fuse_conn_list, entry) { - err = fuse_ctl_add_conn(fc); - if (err) { - fuse_control_sb = NULL; - mutex_unlock(&fuse_mutex); - return err; - } - } - mutex_unlock(&fuse_mutex); - - return 0; -} - -#ifdef KERNEL_2_6_18_PLUS -static int fuse_ctl_get_sb(struct file_system_type *fs_type, int flags, - const char *dev_name, void *raw_data, - struct vfsmount *mnt) -{ - return get_sb_single(fs_type, flags, raw_data, - fuse_ctl_fill_super, mnt); -} -#else -static struct super_block *fuse_ctl_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *raw_data) -{ - return get_sb_single(fs_type, flags, raw_data, fuse_ctl_fill_super); -} -#endif - -static void fuse_ctl_kill_sb(struct super_block *sb) -{ - struct fuse_conn *fc; - - mutex_lock(&fuse_mutex); - fuse_control_sb = NULL; - list_for_each_entry(fc, &fuse_conn_list, entry) - fc->ctl_ndents = 0; - mutex_unlock(&fuse_mutex); - - kill_litter_super(sb); -} - -static struct file_system_type fuse_ctl_fs_type = { - .owner = THIS_MODULE, - .name = "fusectl", - .get_sb = fuse_ctl_get_sb, - .kill_sb = fuse_ctl_kill_sb, -}; - -int __init fuse_ctl_init(void) -{ - return register_filesystem(&fuse_ctl_fs_type); -} - -void fuse_ctl_cleanup(void) -{ - unregister_filesystem(&fuse_ctl_fs_type); -} diff --git a/kernel/dev.c b/kernel/dev.c deleted file mode 100644 index f8b7f3a..0000000 --- a/kernel/dev.c +++ /dev/null @@ -1,1124 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2007 Miklos Szeredi - - This program can be distributed under the terms of the GNU GPL. - See the file COPYING. -*/ - -#include "fuse_i.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef MODULE_ALIAS_MISCDEV -MODULE_ALIAS_MISCDEV(FUSE_MINOR); -#endif - -static struct kmem_cache *fuse_req_cachep; - -static struct fuse_conn *fuse_get_conn(struct file *file) -{ - /* - * Lockless access is OK, because file->private data is set - * once during mount and is valid until the file is released. - */ - return file->private_data; -} - -static void fuse_request_init(struct fuse_req *req) -{ - memset(req, 0, sizeof(*req)); - INIT_LIST_HEAD(&req->list); - INIT_LIST_HEAD(&req->intr_entry); - init_waitqueue_head(&req->waitq); - atomic_set(&req->count, 1); -} - -struct fuse_req *fuse_request_alloc(void) -{ - struct fuse_req *req = kmem_cache_alloc(fuse_req_cachep, GFP_KERNEL); - if (req) - fuse_request_init(req); - return req; -} - -void fuse_request_free(struct fuse_req *req) -{ - kmem_cache_free(fuse_req_cachep, req); -} - -static void block_sigs(sigset_t *oldset) -{ - sigset_t mask; - - siginitsetinv(&mask, sigmask(SIGKILL)); - sigprocmask(SIG_BLOCK, &mask, oldset); -} - -static void restore_sigs(sigset_t *oldset) -{ - sigprocmask(SIG_SETMASK, oldset, NULL); -} - -static void __fuse_get_request(struct fuse_req *req) -{ - atomic_inc(&req->count); -} - -/* Must be called with > 1 refcount */ -static void __fuse_put_request(struct fuse_req *req) -{ - BUG_ON(atomic_read(&req->count) < 2); - atomic_dec(&req->count); -} - -static void fuse_req_init_context(struct fuse_req *req) -{ - req->in.h.uid = current->fsuid; - req->in.h.gid = current->fsgid; - req->in.h.pid = current->pid; -} - -struct fuse_req *fuse_get_req(struct fuse_conn *fc) -{ - struct fuse_req *req; - sigset_t oldset; - int intr; - int err; - - atomic_inc(&fc->num_waiting); - block_sigs(&oldset); - intr = wait_event_interruptible(fc->blocked_waitq, !fc->blocked); - restore_sigs(&oldset); - err = -EINTR; - if (intr) - goto out; - - err = -ENOTCONN; - if (!fc->connected) - goto out; - - req = fuse_request_alloc(); - err = -ENOMEM; - if (!req) - goto out; - - fuse_req_init_context(req); - req->waiting = 1; - return req; - - out: - atomic_dec(&fc->num_waiting); - return ERR_PTR(err); -} - -/* - * Return request in fuse_file->reserved_req. However that may - * currently be in use. If that is the case, wait for it to become - * available. - */ -static struct fuse_req *get_reserved_req(struct fuse_conn *fc, - struct file *file) -{ - struct fuse_req *req = NULL; - struct fuse_file *ff = file->private_data; - - do { - wait_event(fc->reserved_req_waitq, ff->reserved_req); - spin_lock(&fc->lock); - if (ff->reserved_req) { - req = ff->reserved_req; - ff->reserved_req = NULL; - get_file(file); - req->stolen_file = file; - } - spin_unlock(&fc->lock); - } while (!req); - - return req; -} - -/* - * Put stolen request back into fuse_file->reserved_req - */ -static void put_reserved_req(struct fuse_conn *fc, struct fuse_req *req) -{ - struct file *file = req->stolen_file; - struct fuse_file *ff = file->private_data; - - spin_lock(&fc->lock); - fuse_request_init(req); - BUG_ON(ff->reserved_req); - ff->reserved_req = req; - wake_up_all(&fc->reserved_req_waitq); - spin_unlock(&fc->lock); - fput(file); -} - -/* - * Gets a requests for a file operation, always succeeds - * - * This is used for sending the FLUSH request, which must get to - * userspace, due to POSIX locks which may need to be unlocked. - * - * If allocation fails due to OOM, use the reserved request in - * fuse_file. - * - * This is very unlikely to deadlock accidentally, since the - * filesystem should not have it's own file open. If deadlock is - * intentional, it can still be broken by "aborting" the filesystem. - */ -struct fuse_req *fuse_get_req_nofail(struct fuse_conn *fc, struct file *file) -{ - struct fuse_req *req; - - atomic_inc(&fc->num_waiting); - wait_event(fc->blocked_waitq, !fc->blocked); - req = fuse_request_alloc(); - if (!req) - req = get_reserved_req(fc, file); - - fuse_req_init_context(req); - req->waiting = 1; - return req; -} - -void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) -{ - if (atomic_dec_and_test(&req->count)) { - if (req->waiting) - atomic_dec(&fc->num_waiting); - - if (req->stolen_file) - put_reserved_req(fc, req); - else - fuse_request_free(req); - } -} - -/* - * This function is called when a request is finished. Either a reply - * has arrived or it was aborted (and not yet sent) or some error - * occurred during communication with userspace, or the device file - * was closed. The requester thread is woken up (if still waiting), - * the 'end' callback is called if given, else the reference to the - * request is released - * - * Called with fc->lock, unlocks it - */ -static void request_end(struct fuse_conn *fc, struct fuse_req *req) -{ - void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; - req->end = NULL; - list_del(&req->list); - list_del(&req->intr_entry); - req->state = FUSE_REQ_FINISHED; - if (req->background) { - if (fc->num_background == FUSE_MAX_BACKGROUND) { - fc->blocked = 0; - wake_up_all(&fc->blocked_waitq); - } - fc->num_background--; - } - spin_unlock(&fc->lock); - dput(req->dentry); - mntput(req->vfsmount); - if (req->file) - fput(req->file); - wake_up(&req->waitq); - if (end) - end(fc, req); - else - fuse_put_request(fc, req); -} - -static void wait_answer_interruptible(struct fuse_conn *fc, - struct fuse_req *req) -{ - if (signal_pending(current)) - return; - - spin_unlock(&fc->lock); - wait_event_interruptible(req->waitq, req->state == FUSE_REQ_FINISHED); - spin_lock(&fc->lock); -} - -static void queue_interrupt(struct fuse_conn *fc, struct fuse_req *req) -{ - list_add_tail(&req->intr_entry, &fc->interrupts); - wake_up(&fc->waitq); - kill_fasync(&fc->fasync, SIGIO, POLL_IN); -} - -/* Called with fc->lock held. Releases, and then reacquires it. */ -static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) -{ - if (!fc->no_interrupt) { - /* Any signal may interrupt this */ - wait_answer_interruptible(fc, req); - - if (req->aborted) - goto aborted; - if (req->state == FUSE_REQ_FINISHED) - return; - - req->interrupted = 1; - if (req->state == FUSE_REQ_SENT) - queue_interrupt(fc, req); - } - - if (req->force) { - spin_unlock(&fc->lock); - wait_event(req->waitq, req->state == FUSE_REQ_FINISHED); - spin_lock(&fc->lock); - } else { - sigset_t oldset; - - /* Only fatal signals may interrupt this */ - block_sigs(&oldset); - wait_answer_interruptible(fc, req); - restore_sigs(&oldset); - } - - if (req->aborted) - goto aborted; - if (req->state == FUSE_REQ_FINISHED) - return; - - req->out.h.error = -EINTR; - req->aborted = 1; - - aborted: - if (req->locked) { - /* This is uninterruptible sleep, because data is - being copied to/from the buffers of req. During - locked state, there mustn't be any filesystem - operation (e.g. page fault), since that could lead - to deadlock */ - spin_unlock(&fc->lock); - wait_event(req->waitq, !req->locked); - spin_lock(&fc->lock); - } - if (req->state == FUSE_REQ_PENDING) { - list_del(&req->list); - __fuse_put_request(req); - } else if (req->state == FUSE_REQ_SENT) { - spin_unlock(&fc->lock); - wait_event(req->waitq, req->state == FUSE_REQ_FINISHED); - spin_lock(&fc->lock); - } -} - -static unsigned len_args(unsigned numargs, struct fuse_arg *args) -{ - unsigned nbytes = 0; - unsigned i; - - for (i = 0; i < numargs; i++) - nbytes += args[i].size; - - return nbytes; -} - -static u64 fuse_get_unique(struct fuse_conn *fc) - { - fc->reqctr++; - /* zero is special */ - if (fc->reqctr == 0) - fc->reqctr = 1; - - return fc->reqctr; -} - -static void queue_request(struct fuse_conn *fc, struct fuse_req *req) -{ - req->in.h.unique = fuse_get_unique(fc); - req->in.h.len = sizeof(struct fuse_in_header) + - len_args(req->in.numargs, (struct fuse_arg *) req->in.args); - list_add_tail(&req->list, &fc->pending); - req->state = FUSE_REQ_PENDING; - if (!req->waiting) { - req->waiting = 1; - atomic_inc(&fc->num_waiting); - } - wake_up(&fc->waitq); - kill_fasync(&fc->fasync, SIGIO, POLL_IN); -} - -void request_send(struct fuse_conn *fc, struct fuse_req *req) -{ - req->isreply = 1; - spin_lock(&fc->lock); - if (!fc->connected) - req->out.h.error = -ENOTCONN; - else if (fc->conn_error) - req->out.h.error = -ECONNREFUSED; - else { - queue_request(fc, req); - /* acquire extra reference, since request is still needed - after request_end() */ - __fuse_get_request(req); - - request_wait_answer(fc, req); - } - spin_unlock(&fc->lock); -} - -static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) -{ - spin_lock(&fc->lock); - if (fc->connected) { - req->background = 1; - fc->num_background++; - if (fc->num_background == FUSE_MAX_BACKGROUND) - fc->blocked = 1; - - queue_request(fc, req); - spin_unlock(&fc->lock); - } else { - req->out.h.error = -ENOTCONN; - request_end(fc, req); - } -} - -void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req) -{ - req->isreply = 0; - request_send_nowait(fc, req); -} - -void request_send_background(struct fuse_conn *fc, struct fuse_req *req) -{ - req->isreply = 1; - request_send_nowait(fc, req); -} - -/* - * Lock the request. Up to the next unlock_request() there mustn't be - * anything that could cause a page-fault. If the request was already - * aborted bail out. - */ -static int lock_request(struct fuse_conn *fc, struct fuse_req *req) -{ - int err = 0; - if (req) { - spin_lock(&fc->lock); - if (req->aborted) - err = -ENOENT; - else - req->locked = 1; - spin_unlock(&fc->lock); - } - return err; -} - -/* - * Unlock request. If it was aborted during being locked, the - * requester thread is currently waiting for it to be unlocked, so - * wake it up. - */ -static void unlock_request(struct fuse_conn *fc, struct fuse_req *req) -{ - if (req) { - spin_lock(&fc->lock); - req->locked = 0; - if (req->aborted) - wake_up(&req->waitq); - spin_unlock(&fc->lock); - } -} - -struct fuse_copy_state { - struct fuse_conn *fc; - int write; - struct fuse_req *req; - const struct iovec *iov; - unsigned long nr_segs; - unsigned long seglen; - unsigned long addr; - struct page *pg; - void *mapaddr; - void *buf; - unsigned len; -}; - -static void fuse_copy_init(struct fuse_copy_state *cs, struct fuse_conn *fc, - int write, struct fuse_req *req, - const struct iovec *iov, unsigned long nr_segs) -{ - memset(cs, 0, sizeof(*cs)); - cs->fc = fc; - cs->write = write; - cs->req = req; - cs->iov = iov; - cs->nr_segs = nr_segs; -} - -/* Unmap and put previous page of userspace buffer */ -static void fuse_copy_finish(struct fuse_copy_state *cs) -{ - if (cs->mapaddr) { - kunmap_atomic(cs->mapaddr, KM_USER0); - if (cs->write) { - flush_dcache_page(cs->pg); - set_page_dirty_lock(cs->pg); - } - put_page(cs->pg); - cs->mapaddr = NULL; - } -} - -/* - * Get another pagefull of userspace buffer, and map it to kernel - * address space, and lock request - */ -static int fuse_copy_fill(struct fuse_copy_state *cs) -{ - unsigned long offset; - int err; -#ifdef DCACHE_BUG - struct vm_area_struct *vma; -#endif - - unlock_request(cs->fc, cs->req); - fuse_copy_finish(cs); - if (!cs->seglen) { - BUG_ON(!cs->nr_segs); - cs->seglen = cs->iov[0].iov_len; - cs->addr = (unsigned long) cs->iov[0].iov_base; - cs->iov ++; - cs->nr_segs --; - } - down_read(¤t->mm->mmap_sem); -#ifndef DCACHE_BUG - err = get_user_pages(current, current->mm, cs->addr, 1, cs->write, 0, - &cs->pg, NULL); -#else - err = get_user_pages(current, current->mm, cs->addr, 1, cs->write, 0, - &cs->pg, &vma); -#endif - up_read(¤t->mm->mmap_sem); - if (err < 0) - return err; - BUG_ON(err != 1); - offset = cs->addr % PAGE_SIZE; - cs->mapaddr = kmap_atomic(cs->pg, KM_USER0); -#ifdef DCACHE_BUG - flush_cache_page(vma, cs->addr, page_to_pfn(cs->pg)); -#endif - cs->buf = cs->mapaddr + offset; - cs->len = min(PAGE_SIZE - offset, cs->seglen); - cs->seglen -= cs->len; - cs->addr += cs->len; - - return lock_request(cs->fc, cs->req); -} - -/* Do as much copy to/from userspace buffer as we can */ -static int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size) -{ - unsigned ncpy = min(*size, cs->len); - if (val) { - if (cs->write) - memcpy(cs->buf, *val, ncpy); - else - memcpy(*val, cs->buf, ncpy); - *val += ncpy; - } - *size -= ncpy; - cs->len -= ncpy; - cs->buf += ncpy; - return ncpy; -} - -/* - * Copy a page in the request to/from the userspace buffer. Must be - * done atomically - */ -static int fuse_copy_page(struct fuse_copy_state *cs, struct page *page, - unsigned offset, unsigned count, int zeroing) -{ - if (page && zeroing && count < PAGE_SIZE) { - void *mapaddr = kmap_atomic(page, KM_USER1); - memset(mapaddr, 0, PAGE_SIZE); - kunmap_atomic(mapaddr, KM_USER1); - } - while (count) { - int err; - if (!cs->len && (err = fuse_copy_fill(cs))) - return err; - if (page) { - void *mapaddr = kmap_atomic(page, KM_USER1); - void *buf = mapaddr + offset; - offset += fuse_copy_do(cs, &buf, &count); - kunmap_atomic(mapaddr, KM_USER1); - } else - offset += fuse_copy_do(cs, NULL, &count); - } - if (page && !cs->write) - flush_dcache_page(page); - return 0; -} - -/* Copy pages in the request to/from userspace buffer */ -static int fuse_copy_pages(struct fuse_copy_state *cs, unsigned nbytes, - int zeroing) -{ - unsigned i; - struct fuse_req *req = cs->req; - unsigned offset = req->page_offset; - unsigned count = min(nbytes, (unsigned) PAGE_SIZE - offset); - - for (i = 0; i < req->num_pages && (nbytes || zeroing); i++) { - struct page *page = req->pages[i]; - int err = fuse_copy_page(cs, page, offset, count, zeroing); - if (err) - return err; - - nbytes -= count; - count = min(nbytes, (unsigned) PAGE_SIZE); - offset = 0; - } - return 0; -} - -/* Copy a single argument in the request to/from userspace buffer */ -static int fuse_copy_one(struct fuse_copy_state *cs, void *val, unsigned size) -{ - while (size) { - int err; - if (!cs->len && (err = fuse_copy_fill(cs))) - return err; - fuse_copy_do(cs, &val, &size); - } - return 0; -} - -/* Copy request arguments to/from userspace buffer */ -static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs, - unsigned argpages, struct fuse_arg *args, - int zeroing) -{ - int err = 0; - unsigned i; - - for (i = 0; !err && i < numargs; i++) { - struct fuse_arg *arg = &args[i]; - if (i == numargs - 1 && argpages) - err = fuse_copy_pages(cs, arg->size, zeroing); - else - err = fuse_copy_one(cs, arg->value, arg->size); - } - return err; -} - -static int request_pending(struct fuse_conn *fc) -{ - return !list_empty(&fc->pending) || !list_empty(&fc->interrupts); -} - -/* Wait until a request is available on the pending list */ -static void request_wait(struct fuse_conn *fc) -{ - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue_exclusive(&fc->waitq, &wait); - while (fc->connected && !request_pending(fc)) { - set_current_state(TASK_INTERRUPTIBLE); - if (signal_pending(current)) - break; - - spin_unlock(&fc->lock); - schedule(); - spin_lock(&fc->lock); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&fc->waitq, &wait); -} - -/* - * Transfer an interrupt request to userspace - * - * Unlike other requests this is assembled on demand, without a need - * to allocate a separate fuse_req structure. - * - * Called with fc->lock held, releases it - */ -static int fuse_read_interrupt(struct fuse_conn *fc, struct fuse_req *req, - const struct iovec *iov, unsigned long nr_segs) -{ - struct fuse_copy_state cs; - struct fuse_in_header ih; - struct fuse_interrupt_in arg; - unsigned reqsize = sizeof(ih) + sizeof(arg); - int err; - - list_del_init(&req->intr_entry); - req->intr_unique = fuse_get_unique(fc); - memset(&ih, 0, sizeof(ih)); - memset(&arg, 0, sizeof(arg)); - ih.len = reqsize; - ih.opcode = FUSE_INTERRUPT; - ih.unique = req->intr_unique; - arg.unique = req->in.h.unique; - - spin_unlock(&fc->lock); - if (iov_length(iov, nr_segs) < reqsize) - return -EINVAL; - - fuse_copy_init(&cs, fc, 1, NULL, iov, nr_segs); - err = fuse_copy_one(&cs, &ih, sizeof(ih)); - if (!err) - err = fuse_copy_one(&cs, &arg, sizeof(arg)); - fuse_copy_finish(&cs); - - return err ? err : reqsize; -} - -/* - * Read a single request into the userspace filesystem's buffer. This - * function waits until a request is available, then removes it from - * the pending list and copies request data to userspace buffer. If - * no reply is needed (FORGET) or request has been aborted or there - * was an error during the copying then it's finished by calling - * request_end(). Otherwise add it to the processing list, and set - * the 'sent' flag. - */ -static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov, - unsigned long nr_segs, loff_t *off) -{ - int err; - struct fuse_req *req; - struct fuse_in *in; - struct fuse_copy_state cs; - unsigned reqsize; - struct fuse_conn *fc = fuse_get_conn(file); - if (!fc) - return -EPERM; - - restart: - spin_lock(&fc->lock); - err = -EAGAIN; - if ((file->f_flags & O_NONBLOCK) && fc->connected && - !request_pending(fc)) - goto err_unlock; - - request_wait(fc); - err = -ENODEV; - if (!fc->connected) - goto err_unlock; - err = -ERESTARTSYS; - if (!request_pending(fc)) - goto err_unlock; - - if (!list_empty(&fc->interrupts)) { - req = list_entry(fc->interrupts.next, struct fuse_req, - intr_entry); - return fuse_read_interrupt(fc, req, iov, nr_segs); - } - - req = list_entry(fc->pending.next, struct fuse_req, list); - req->state = FUSE_REQ_READING; - list_move(&req->list, &fc->io); - - in = &req->in; - reqsize = in->h.len; - /* If request is too large, reply with an error and restart the read */ - if (iov_length(iov, nr_segs) < reqsize) { - req->out.h.error = -EIO; - /* SETXATTR is special, since it may contain too large data */ - if (in->h.opcode == FUSE_SETXATTR) - req->out.h.error = -E2BIG; - request_end(fc, req); - goto restart; - } - spin_unlock(&fc->lock); - fuse_copy_init(&cs, fc, 1, req, iov, nr_segs); - err = fuse_copy_one(&cs, &in->h, sizeof(in->h)); - if (!err) - err = fuse_copy_args(&cs, in->numargs, in->argpages, - (struct fuse_arg *) in->args, 0); - fuse_copy_finish(&cs); - spin_lock(&fc->lock); - req->locked = 0; - if (!err && req->aborted) - err = -ENOENT; - if (err) { - if (!req->aborted) - req->out.h.error = -EIO; - request_end(fc, req); - return err; - } - if (!req->isreply) - request_end(fc, req); - else { - req->state = FUSE_REQ_SENT; - list_move_tail(&req->list, &fc->processing); - if (req->interrupted) - queue_interrupt(fc, req); - spin_unlock(&fc->lock); - } - return reqsize; - - err_unlock: - spin_unlock(&fc->lock); - return err; -} - -#ifndef KERNEL_2_6_19_PLUS -static ssize_t fuse_dev_read(struct file *file, char __user *buf, - size_t nbytes, loff_t *off) -{ - struct iovec iov; - iov.iov_len = nbytes; - iov.iov_base = buf; - return fuse_dev_readv(file, &iov, 1, off); -} -#else -static ssize_t fuse_dev_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - return fuse_dev_readv(iocb->ki_filp, iov, nr_segs, &pos); -} -#endif - -/* Look up request on processing list by unique ID */ -static struct fuse_req *request_find(struct fuse_conn *fc, u64 unique) -{ - struct list_head *entry; - - list_for_each(entry, &fc->processing) { - struct fuse_req *req; - req = list_entry(entry, struct fuse_req, list); - if (req->in.h.unique == unique || req->intr_unique == unique) - return req; - } - return NULL; -} - -static int copy_out_args(struct fuse_copy_state *cs, struct fuse_out *out, - unsigned nbytes) -{ - unsigned reqsize = sizeof(struct fuse_out_header); - - if (out->h.error) - return nbytes != reqsize ? -EINVAL : 0; - - reqsize += len_args(out->numargs, out->args); - - if (reqsize < nbytes || (reqsize > nbytes && !out->argvar)) - return -EINVAL; - else if (reqsize > nbytes) { - struct fuse_arg *lastarg = &out->args[out->numargs-1]; - unsigned diffsize = reqsize - nbytes; - if (diffsize > lastarg->size) - return -EINVAL; - lastarg->size -= diffsize; - } - return fuse_copy_args(cs, out->numargs, out->argpages, out->args, - out->page_zeroing); -} - -/* - * Write a single reply to a request. First the header is copied from - * the write buffer. The request is then searched on the processing - * list by the unique ID found in the header. If found, then remove - * it from the list and copy the rest of the buffer to the request. - * The request is finished by calling request_end() - */ -static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov, - unsigned long nr_segs, loff_t *off) -{ - int err; - unsigned nbytes = iov_length(iov, nr_segs); - struct fuse_req *req; - struct fuse_out_header oh; - struct fuse_copy_state cs; - struct fuse_conn *fc = fuse_get_conn(file); - if (!fc) - return -EPERM; - - fuse_copy_init(&cs, fc, 0, NULL, iov, nr_segs); - if (nbytes < sizeof(struct fuse_out_header)) - return -EINVAL; - - err = fuse_copy_one(&cs, &oh, sizeof(oh)); - if (err) - goto err_finish; - err = -EINVAL; - if (!oh.unique || oh.error <= -1000 || oh.error > 0 || - oh.len != nbytes) - goto err_finish; - - spin_lock(&fc->lock); - err = -ENOENT; - if (!fc->connected) - goto err_unlock; - - req = request_find(fc, oh.unique); - if (!req) - goto err_unlock; - - if (req->aborted) { - spin_unlock(&fc->lock); - fuse_copy_finish(&cs); - spin_lock(&fc->lock); - request_end(fc, req); - return -ENOENT; - } - /* Is it an interrupt reply? */ - if (req->intr_unique == oh.unique) { - err = -EINVAL; - if (nbytes != sizeof(struct fuse_out_header)) - goto err_unlock; - - if (oh.error == -ENOSYS) - fc->no_interrupt = 1; - else if (oh.error == -EAGAIN) - queue_interrupt(fc, req); - - spin_unlock(&fc->lock); - fuse_copy_finish(&cs); - return nbytes; - } - - req->state = FUSE_REQ_WRITING; - list_move(&req->list, &fc->io); - req->out.h = oh; - req->locked = 1; - cs.req = req; - spin_unlock(&fc->lock); - - err = copy_out_args(&cs, &req->out, nbytes); - fuse_copy_finish(&cs); - - spin_lock(&fc->lock); - req->locked = 0; - if (!err) { - if (req->aborted) - err = -ENOENT; - } else if (!req->aborted) - req->out.h.error = -EIO; - request_end(fc, req); - - return err ? err : nbytes; - - err_unlock: - spin_unlock(&fc->lock); - err_finish: - fuse_copy_finish(&cs); - return err; -} - -#ifndef KERNEL_2_6_19_PLUS -static ssize_t fuse_dev_write(struct file *file, const char __user *buf, - size_t nbytes, loff_t *off) -{ - struct iovec iov; - iov.iov_len = nbytes; - iov.iov_base = (char __user *) buf; - return fuse_dev_writev(file, &iov, 1, off); -} -#else -static ssize_t fuse_dev_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - return fuse_dev_writev(iocb->ki_filp, iov, nr_segs, &pos); -} -#endif - -static unsigned fuse_dev_poll(struct file *file, poll_table *wait) -{ - unsigned mask = POLLOUT | POLLWRNORM; - struct fuse_conn *fc = fuse_get_conn(file); - if (!fc) - return POLLERR; - - poll_wait(file, &fc->waitq, wait); - - spin_lock(&fc->lock); - if (!fc->connected) - mask = POLLERR; - else if (request_pending(fc)) - mask |= POLLIN | POLLRDNORM; - spin_unlock(&fc->lock); - - return mask; -} - -/* - * Abort all requests on the given list (pending or processing) - * - * This function releases and reacquires fc->lock - */ -static void end_requests(struct fuse_conn *fc, struct list_head *head) -{ - while (!list_empty(head)) { - struct fuse_req *req; - req = list_entry(head->next, struct fuse_req, list); - req->out.h.error = -ECONNABORTED; - request_end(fc, req); - spin_lock(&fc->lock); - } -} - -/* - * Abort requests under I/O - * - * The requests are set to aborted and finished, and the request - * waiter is woken up. This will make request_wait_answer() wait - * until the request is unlocked and then return. - * - * If the request is asynchronous, then the end function needs to be - * called after waiting for the request to be unlocked (if it was - * locked). - */ -static void end_io_requests(struct fuse_conn *fc) -{ - while (!list_empty(&fc->io)) { - struct fuse_req *req = - list_entry(fc->io.next, struct fuse_req, list); - void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; - - req->aborted = 1; - req->out.h.error = -ECONNABORTED; - req->state = FUSE_REQ_FINISHED; - list_del_init(&req->list); - wake_up(&req->waitq); - if (end) { - req->end = NULL; - /* The end function will consume this reference */ - __fuse_get_request(req); - spin_unlock(&fc->lock); - wait_event(req->waitq, !req->locked); - end(fc, req); - spin_lock(&fc->lock); - } - } -} - -/* - * Abort all requests. - * - * Emergency exit in case of a malicious or accidental deadlock, or - * just a hung filesystem. - * - * The same effect is usually achievable through killing the - * filesystem daemon and all users of the filesystem. The exception - * is the combination of an asynchronous request and the tricky - * deadlock (see Documentation/filesystems/fuse.txt). - * - * During the aborting, progression of requests from the pending and - * processing lists onto the io list, and progression of new requests - * onto the pending list is prevented by req->connected being false. - * - * Progression of requests under I/O to the processing list is - * prevented by the req->aborted flag being true for these requests. - * For this reason requests on the io list must be aborted first. - */ -void fuse_abort_conn(struct fuse_conn *fc) -{ - spin_lock(&fc->lock); - if (fc->connected) { - fc->connected = 0; - fc->blocked = 0; - end_io_requests(fc); - end_requests(fc, &fc->pending); - end_requests(fc, &fc->processing); - wake_up_all(&fc->waitq); - wake_up_all(&fc->blocked_waitq); - kill_fasync(&fc->fasync, SIGIO, POLL_IN); - } - spin_unlock(&fc->lock); -} - -static int fuse_dev_release(struct inode *inode, struct file *file) -{ - struct fuse_conn *fc = fuse_get_conn(file); - if (fc) { - spin_lock(&fc->lock); - fc->connected = 0; - end_requests(fc, &fc->pending); - end_requests(fc, &fc->processing); - spin_unlock(&fc->lock); - fasync_helper(-1, file, 0, &fc->fasync); - fuse_conn_put(fc); - } - - return 0; -} - -static int fuse_dev_fasync(int fd, struct file *file, int on) -{ - struct fuse_conn *fc = fuse_get_conn(file); - if (!fc) - return -EPERM; - - /* No locking - fasync_helper does its own locking */ - return fasync_helper(fd, file, on, &fc->fasync); -} - -struct file_operations fuse_dev_operations = { - .owner = THIS_MODULE, - .llseek = no_llseek, -#ifndef KERNEL_2_6_19_PLUS - .read = fuse_dev_read, - .readv = fuse_dev_readv, - .write = fuse_dev_write, - .writev = fuse_dev_writev, -#else - .read = do_sync_read, - .aio_read = fuse_dev_read, - .write = do_sync_write, - .aio_write = fuse_dev_write, -#endif - .poll = fuse_dev_poll, - .release = fuse_dev_release, - .fasync = fuse_dev_fasync, -}; - -static struct miscdevice fuse_miscdevice = { - .minor = FUSE_MINOR, - .name = "fuse", - .fops = &fuse_dev_operations, -}; - -int __init fuse_dev_init(void) -{ - int err = -ENOMEM; -#ifdef KERNEL_2_6_23_PLUS - fuse_req_cachep = kmem_cache_create("fuse_request", - sizeof(struct fuse_req), - 0, 0, NULL); -#else - fuse_req_cachep = kmem_cache_create("fuse_request", - sizeof(struct fuse_req), - 0, 0, NULL, NULL); -#endif - if (!fuse_req_cachep) - goto out; - - err = misc_register(&fuse_miscdevice); - if (err) - goto out_cache_clean; - - return 0; - - out_cache_clean: - kmem_cache_destroy(fuse_req_cachep); - out: - return err; -} - -void fuse_dev_cleanup(void) -{ - misc_deregister(&fuse_miscdevice); - kmem_cache_destroy(fuse_req_cachep); -} diff --git a/kernel/dir.c b/kernel/dir.c deleted file mode 100644 index b1fe4c6..0000000 --- a/kernel/dir.c +++ /dev/null @@ -1,1377 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2007 Miklos Szeredi - - This program can be distributed under the terms of the GNU GPL. - See the file COPYING. -*/ - -#include "fuse_i.h" - -#include -#include -#include -#include -#include - -#if BITS_PER_LONG >= 64 -static inline void fuse_dentry_settime(struct dentry *entry, u64 time) -{ - entry->d_time = time; -} - -static inline u64 fuse_dentry_time(struct dentry *entry) -{ - return entry->d_time; -} -#else -/* - * On 32 bit archs store the high 32 bits of time in d_fsdata - */ -static void fuse_dentry_settime(struct dentry *entry, u64 time) -{ - entry->d_time = time; - entry->d_fsdata = (void *) (unsigned long) (time >> 32); -} - -static u64 fuse_dentry_time(struct dentry *entry) -{ - return (u64) entry->d_time + - ((u64) (unsigned long) entry->d_fsdata << 32); -} -#endif - -/* - * FUSE caches dentries and attributes with separate timeout. The - * time in jiffies until the dentry/attributes are valid is stored in - * dentry->d_time and fuse_inode->i_time respectively. - */ - -/* - * Calculate the time in jiffies until a dentry/attributes are valid - */ -static u64 time_to_jiffies(unsigned long sec, unsigned long nsec) -{ - if (sec || nsec) { - struct timespec ts = {sec, nsec}; - return get_jiffies_64() + timespec_to_jiffies(&ts); - } else - return 0; -} - -/* - * Set dentry and possibly attribute timeouts from the lookup/mk* - * replies - */ -static void fuse_change_timeout(struct dentry *entry, struct fuse_entry_out *o) -{ - fuse_dentry_settime(entry, - time_to_jiffies(o->entry_valid, o->entry_valid_nsec)); - if (entry->d_inode) - get_fuse_inode(entry->d_inode)->i_time = - time_to_jiffies(o->attr_valid, o->attr_valid_nsec); -} - -/* - * Mark the attributes as stale, so that at the next call to - * ->getattr() they will be fetched from userspace - */ -void fuse_invalidate_attr(struct inode *inode) -{ - get_fuse_inode(inode)->i_time = 0; -} - -/* - * Just mark the entry as stale, so that a next attempt to look it up - * will result in a new lookup call to userspace - * - * This is called when a dentry is about to become negative and the - * timeout is unknown (unlink, rmdir, rename and in some cases - * lookup) - */ -static void fuse_invalidate_entry_cache(struct dentry *entry) -{ - fuse_dentry_settime(entry, 0); -} - -/* - * Same as fuse_invalidate_entry_cache(), but also try to remove the - * dentry from the hash - */ -static void fuse_invalidate_entry(struct dentry *entry) -{ - d_invalidate(entry); - fuse_invalidate_entry_cache(entry); -} - -static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, - struct dentry *entry, - struct fuse_entry_out *outarg) -{ - req->in.h.opcode = FUSE_LOOKUP; - req->in.h.nodeid = get_node_id(dir); - req->in.numargs = 1; - req->in.args[0].size = entry->d_name.len + 1; - req->in.args[0].value = entry->d_name.name; - req->out.numargs = 1; - req->out.args[0].size = sizeof(struct fuse_entry_out); - req->out.args[0].value = outarg; -} - -/* - * Check whether the dentry is still valid - * - * If the entry validity timeout has expired and the dentry is - * positive, try to redo the lookup. If the lookup results in a - * different inode, then let the VFS invalidate the dentry and redo - * the lookup once more. If the lookup results in the same inode, - * then refresh the attributes, timeouts and mark the dentry valid. - */ -static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) -{ - struct inode *inode = entry->d_inode; - - if (inode && is_bad_inode(inode)) - return 0; - else if (fuse_dentry_time(entry) < get_jiffies_64()) { - int err; - struct fuse_entry_out outarg; - struct fuse_conn *fc; - struct fuse_req *req; - struct fuse_req *forget_req; - struct dentry *parent; - - /* For negative dentries, always do a fresh lookup */ - if (!inode) - return 0; - - fc = get_fuse_conn(inode); - req = fuse_get_req(fc); - if (IS_ERR(req)) - return 0; - - forget_req = fuse_get_req(fc); - if (IS_ERR(forget_req)) { - fuse_put_request(fc, req); - return 0; - } - - parent = dget_parent(entry); - fuse_lookup_init(req, parent->d_inode, entry, &outarg); - request_send(fc, req); - dput(parent); - err = req->out.h.error; - fuse_put_request(fc, req); - /* Zero nodeid is same as -ENOENT */ - if (!err && !outarg.nodeid) - err = -ENOENT; - if (!err) { - struct fuse_inode *fi = get_fuse_inode(inode); - if (outarg.nodeid != get_node_id(inode)) { - fuse_send_forget(fc, forget_req, - outarg.nodeid, 1); - return 0; - } - spin_lock(&fc->lock); - fi->nlookup ++; - spin_unlock(&fc->lock); - } - fuse_put_request(fc, forget_req); - if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT) - return 0; - - fuse_change_attributes(inode, &outarg.attr); - fuse_change_timeout(entry, &outarg); - } - return 1; -} - -static int invalid_nodeid(u64 nodeid) -{ - return !nodeid || nodeid == FUSE_ROOT_ID; -} - -struct dentry_operations fuse_dentry_operations = { - .d_revalidate = fuse_dentry_revalidate, -}; - -int fuse_valid_type(int m) -{ - return S_ISREG(m) || S_ISDIR(m) || S_ISLNK(m) || S_ISCHR(m) || - S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(m); -} - -/* - * Add a directory inode to a dentry, ensuring that no other dentry - * refers to this inode. Called with fc->inst_mutex. - */ -static struct dentry *fuse_d_add_directory(struct dentry *entry, - struct inode *inode) -{ - struct dentry *alias = d_find_alias(inode); - if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) { - /* This tries to shrink the subtree below alias */ - fuse_invalidate_entry(alias); - dput(alias); - if (!list_empty(&inode->i_dentry)) - return ERR_PTR(-EBUSY); - } else - dput(alias); - return d_splice_alias(inode, entry); -} - -static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, - struct nameidata *nd) -{ - int err; - struct fuse_entry_out outarg; - struct inode *inode = NULL; - struct dentry *newent; - struct fuse_conn *fc = get_fuse_conn(dir); - struct fuse_req *req; - struct fuse_req *forget_req; - - if (entry->d_name.len > FUSE_NAME_MAX) - return ERR_PTR(-ENAMETOOLONG); - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return ERR_PTR(PTR_ERR(req)); - - forget_req = fuse_get_req(fc); - if (IS_ERR(forget_req)) { - fuse_put_request(fc, req); - return ERR_PTR(PTR_ERR(forget_req)); - } - - fuse_lookup_init(req, dir, entry, &outarg); - request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - /* Zero nodeid is same as -ENOENT, but with valid timeout */ - if (!err && outarg.nodeid && - (invalid_nodeid(outarg.nodeid) || - !fuse_valid_type(outarg.attr.mode))) - err = -EIO; - if (!err && outarg.nodeid) { - inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, - &outarg.attr); - if (!inode) { - fuse_send_forget(fc, forget_req, outarg.nodeid, 1); - return ERR_PTR(-ENOMEM); - } - } - fuse_put_request(fc, forget_req); - if (err && err != -ENOENT) - return ERR_PTR(err); - - if (inode && S_ISDIR(inode->i_mode)) { - mutex_lock(&fc->inst_mutex); - newent = fuse_d_add_directory(entry, inode); - mutex_unlock(&fc->inst_mutex); - if (IS_ERR(newent)) { - iput(inode); - return newent; - } - } else - newent = d_splice_alias(inode, entry); - - entry = newent ? newent : entry; - entry->d_op = &fuse_dentry_operations; - if (!err) - fuse_change_timeout(entry, &outarg); - else - fuse_invalidate_entry_cache(entry); - return newent; -} - -#ifdef HAVE_LOOKUP_INSTANTIATE_FILP -/* - * Synchronous release for the case when something goes wrong in CREATE_OPEN - */ -static void fuse_sync_release(struct fuse_conn *fc, struct fuse_file *ff, - u64 nodeid, int flags) -{ - struct fuse_req *req; - - req = fuse_release_fill(ff, nodeid, flags, FUSE_RELEASE); - req->force = 1; - request_send(fc, req); - fuse_put_request(fc, req); -} - -/* - * Atomic create+open operation - * - * If the filesystem doesn't support this, then fall back to separate - * 'mknod' + 'open' requests. - */ -static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, - struct nameidata *nd) -{ - int err; - struct inode *inode; - struct fuse_conn *fc = get_fuse_conn(dir); - struct fuse_req *req; - struct fuse_req *forget_req; - struct fuse_open_in inarg; - struct fuse_open_out outopen; - struct fuse_entry_out outentry; - struct fuse_file *ff; - struct file *file; - int flags = nd->intent.open.flags - 1; - - if (fc->no_create) - return -ENOSYS; - - forget_req = fuse_get_req(fc); - if (IS_ERR(forget_req)) - return PTR_ERR(forget_req); - - req = fuse_get_req(fc); - err = PTR_ERR(req); - if (IS_ERR(req)) - goto out_put_forget_req; - - err = -ENOMEM; - ff = fuse_file_alloc(); - if (!ff) - goto out_put_request; - - flags &= ~O_NOCTTY; - memset(&inarg, 0, sizeof(inarg)); - inarg.flags = flags; - inarg.mode = mode; - req->in.h.opcode = FUSE_CREATE; - req->in.h.nodeid = get_node_id(dir); - req->in.numargs = 2; - req->in.args[0].size = sizeof(inarg); - req->in.args[0].value = &inarg; - req->in.args[1].size = entry->d_name.len + 1; - req->in.args[1].value = entry->d_name.name; - req->out.numargs = 2; - req->out.args[0].size = sizeof(outentry); - req->out.args[0].value = &outentry; - req->out.args[1].size = sizeof(outopen); - req->out.args[1].value = &outopen; - request_send(fc, req); - err = req->out.h.error; - if (err) { - if (err == -ENOSYS) - fc->no_create = 1; - goto out_free_ff; - } - - err = -EIO; - if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid)) - goto out_free_ff; - - fuse_put_request(fc, req); - inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, - &outentry.attr); - if (!inode) { - flags &= ~(O_CREAT | O_EXCL | O_TRUNC); - ff->fh = outopen.fh; - fuse_sync_release(fc, ff, outentry.nodeid, flags); - fuse_send_forget(fc, forget_req, outentry.nodeid, 1); - return -ENOMEM; - } - fuse_put_request(fc, forget_req); - d_instantiate(entry, inode); - fuse_invalidate_attr(dir); - fuse_change_timeout(entry, &outentry); - file = lookup_instantiate_filp(nd, entry, generic_file_open); - if (IS_ERR(file)) { - ff->fh = outopen.fh; - fuse_sync_release(fc, ff, outentry.nodeid, flags); - return PTR_ERR(file); - } - fuse_finish_open(inode, file, ff, &outopen); - return 0; - - out_free_ff: - fuse_file_free(ff); - out_put_request: - fuse_put_request(fc, req); - out_put_forget_req: - fuse_put_request(fc, forget_req); - return err; -} -#endif - -/* - * Code shared between mknod, mkdir, symlink and link - */ -static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, - struct inode *dir, struct dentry *entry, - int mode) -{ - struct fuse_entry_out outarg; - struct inode *inode; - int err; - struct fuse_req *forget_req; - - forget_req = fuse_get_req(fc); - if (IS_ERR(forget_req)) { - fuse_put_request(fc, req); - return PTR_ERR(forget_req); - } - - req->in.h.nodeid = get_node_id(dir); - req->out.numargs = 1; - req->out.args[0].size = sizeof(outarg); - req->out.args[0].value = &outarg; - request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - if (err) - goto out_put_forget_req; - - err = -EIO; - if (invalid_nodeid(outarg.nodeid)) - goto out_put_forget_req; - - if ((outarg.attr.mode ^ mode) & S_IFMT) - goto out_put_forget_req; - - inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, - &outarg.attr); - if (!inode) { - fuse_send_forget(fc, forget_req, outarg.nodeid, 1); - return -ENOMEM; - } - fuse_put_request(fc, forget_req); - - if (S_ISDIR(inode->i_mode)) { - struct dentry *alias; - mutex_lock(&fc->inst_mutex); - alias = d_find_alias(inode); - if (alias) { - /* New directory must have moved since mkdir */ - mutex_unlock(&fc->inst_mutex); - dput(alias); - iput(inode); - return -EBUSY; - } - d_instantiate(entry, inode); - mutex_unlock(&fc->inst_mutex); - } else - d_instantiate(entry, inode); - - fuse_change_timeout(entry, &outarg); - fuse_invalidate_attr(dir); - return 0; - - out_put_forget_req: - fuse_put_request(fc, forget_req); - return err; -} - -static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode, - dev_t rdev) -{ - struct fuse_mknod_in inarg; - struct fuse_conn *fc = get_fuse_conn(dir); - struct fuse_req *req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - memset(&inarg, 0, sizeof(inarg)); - inarg.mode = mode; - inarg.rdev = new_encode_dev(rdev); - req->in.h.opcode = FUSE_MKNOD; - req->in.numargs = 2; - req->in.args[0].size = sizeof(inarg); - req->in.args[0].value = &inarg; - req->in.args[1].size = entry->d_name.len + 1; - req->in.args[1].value = entry->d_name.name; - return create_new_entry(fc, req, dir, entry, mode); -} - -static int fuse_create(struct inode *dir, struct dentry *entry, int mode, - struct nameidata *nd) -{ -#ifdef HAVE_LOOKUP_INSTANTIATE_FILP - if (nd && (nd->flags & LOOKUP_OPEN)) { - int err = fuse_create_open(dir, entry, mode, nd); - if (err != -ENOSYS) - return err; - /* Fall back on mknod */ - } -#endif - return fuse_mknod(dir, entry, mode, 0); -} - -static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode) -{ - struct fuse_mkdir_in inarg; - struct fuse_conn *fc = get_fuse_conn(dir); - struct fuse_req *req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - memset(&inarg, 0, sizeof(inarg)); - inarg.mode = mode; - req->in.h.opcode = FUSE_MKDIR; - req->in.numargs = 2; - req->in.args[0].size = sizeof(inarg); - req->in.args[0].value = &inarg; - req->in.args[1].size = entry->d_name.len + 1; - req->in.args[1].value = entry->d_name.name; - return create_new_entry(fc, req, dir, entry, S_IFDIR); -} - -static int fuse_symlink(struct inode *dir, struct dentry *entry, - const char *link) -{ - struct fuse_conn *fc = get_fuse_conn(dir); - unsigned len = strlen(link) + 1; - struct fuse_req *req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - req->in.h.opcode = FUSE_SYMLINK; - req->in.numargs = 2; - req->in.args[0].size = entry->d_name.len + 1; - req->in.args[0].value = entry->d_name.name; - req->in.args[1].size = len; - req->in.args[1].value = link; - return create_new_entry(fc, req, dir, entry, S_IFLNK); -} - -static int fuse_unlink(struct inode *dir, struct dentry *entry) -{ - int err; - struct fuse_conn *fc = get_fuse_conn(dir); - struct fuse_req *req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - req->in.h.opcode = FUSE_UNLINK; - req->in.h.nodeid = get_node_id(dir); - req->in.numargs = 1; - req->in.args[0].size = entry->d_name.len + 1; - req->in.args[0].value = entry->d_name.name; - request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - if (!err) { - struct inode *inode = entry->d_inode; - - /* Set nlink to zero so the inode can be cleared, if - the inode does have more links this will be - discovered at the next lookup/getattr */ - clear_nlink(inode); - fuse_invalidate_attr(inode); - fuse_invalidate_attr(dir); - fuse_invalidate_entry_cache(entry); - } else if (err == -EINTR) - fuse_invalidate_entry(entry); - return err; -} - -static int fuse_rmdir(struct inode *dir, struct dentry *entry) -{ - int err; - struct fuse_conn *fc = get_fuse_conn(dir); - struct fuse_req *req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - req->in.h.opcode = FUSE_RMDIR; - req->in.h.nodeid = get_node_id(dir); - req->in.numargs = 1; - req->in.args[0].size = entry->d_name.len + 1; - req->in.args[0].value = entry->d_name.name; - request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - if (!err) { - clear_nlink(entry->d_inode); - fuse_invalidate_attr(dir); - fuse_invalidate_entry_cache(entry); - } else if (err == -EINTR) - fuse_invalidate_entry(entry); - return err; -} - -static int fuse_rename(struct inode *olddir, struct dentry *oldent, - struct inode *newdir, struct dentry *newent) -{ - int err; - struct fuse_rename_in inarg; - struct fuse_conn *fc = get_fuse_conn(olddir); - struct fuse_req *req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - memset(&inarg, 0, sizeof(inarg)); - inarg.newdir = get_node_id(newdir); - req->in.h.opcode = FUSE_RENAME; - req->in.h.nodeid = get_node_id(olddir); - req->in.numargs = 3; - req->in.args[0].size = sizeof(inarg); - req->in.args[0].value = &inarg; - req->in.args[1].size = oldent->d_name.len + 1; - req->in.args[1].value = oldent->d_name.name; - req->in.args[2].size = newent->d_name.len + 1; - req->in.args[2].value = newent->d_name.name; - request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - if (!err) { - /* ctime changes */ - fuse_invalidate_attr(oldent->d_inode); - - fuse_invalidate_attr(olddir); - if (olddir != newdir) - fuse_invalidate_attr(newdir); - - /* newent will end up negative */ - if (newent->d_inode) - fuse_invalidate_entry_cache(newent); - } else if (err == -EINTR) { - /* If request was interrupted, DEITY only knows if the - rename actually took place. If the invalidation - fails (e.g. some process has CWD under the renamed - directory), then there can be inconsistency between - the dcache and the real filesystem. Tough luck. */ - fuse_invalidate_entry(oldent); - if (newent->d_inode) - fuse_invalidate_entry(newent); - } - - return err; -} - -static int fuse_link(struct dentry *entry, struct inode *newdir, - struct dentry *newent) -{ - int err; - struct fuse_link_in inarg; - struct inode *inode = entry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - memset(&inarg, 0, sizeof(inarg)); - inarg.oldnodeid = get_node_id(inode); - req->in.h.opcode = FUSE_LINK; - req->in.numargs = 2; - req->in.args[0].size = sizeof(inarg); - req->in.args[0].value = &inarg; - req->in.args[1].size = newent->d_name.len + 1; - req->in.args[1].value = newent->d_name.name; - err = create_new_entry(fc, req, newdir, newent, inode->i_mode); - /* Contrary to "normal" filesystems it can happen that link - makes two "logical" inodes point to the same "physical" - inode. We invalidate the attributes of the old one, so it - will reflect changes in the backing inode (link count, - etc.) - */ - if (!err || err == -EINTR) - fuse_invalidate_attr(inode); - return err; -} - -int fuse_do_getattr(struct inode *inode) -{ - int err; - struct fuse_attr_out arg; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - req->in.h.opcode = FUSE_GETATTR; - req->in.h.nodeid = get_node_id(inode); - req->out.numargs = 1; - req->out.args[0].size = sizeof(arg); - req->out.args[0].value = &arg; - request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - if (!err) { - if ((inode->i_mode ^ arg.attr.mode) & S_IFMT) { -#ifndef KERNEL_2_6_12_PLUS - if (get_node_id(inode) != FUSE_ROOT_ID) - make_bad_inode(inode); -#else - make_bad_inode(inode); -#endif - err = -EIO; - } else { - struct fuse_inode *fi = get_fuse_inode(inode); - fuse_change_attributes(inode, &arg.attr); - fi->i_time = time_to_jiffies(arg.attr_valid, - arg.attr_valid_nsec); - } - } - return err; -} - -/* - * Calling into a user-controlled filesystem gives the filesystem - * daemon ptrace-like capabilities over the requester process. This - * means, that the filesystem daemon is able to record the exact - * filesystem operations performed, and can also control the behavior - * of the requester process in otherwise impossible ways. For example - * it can delay the operation for arbitrary length of time allowing - * DoS against the requester. - * - * For this reason only those processes can call into the filesystem, - * for which the owner of the mount has ptrace privilege. This - * excludes processes started by other users, suid or sgid processes. - */ -int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task) -{ - if (fc->flags & FUSE_ALLOW_OTHER) - return 1; - - if (task->euid == fc->user_id && - task->suid == fc->user_id && - task->uid == fc->user_id && - task->egid == fc->group_id && - task->sgid == fc->group_id && - task->gid == fc->group_id) - return 1; - - return 0; -} - -/* - * Check whether the inode attributes are still valid - * - * If the attribute validity timeout has expired, then fetch the fresh - * attributes with a 'getattr' request - * - * I'm not sure why cached attributes are never returned for the root - * inode, this is probably being too cautious. - */ -static int fuse_revalidate(struct dentry *entry) -{ - struct inode *inode = entry->d_inode; - struct fuse_inode *fi = get_fuse_inode(inode); - struct fuse_conn *fc = get_fuse_conn(inode); - - if (!fuse_allow_task(fc, current)) - return -EACCES; - if (get_node_id(inode) != FUSE_ROOT_ID && - fi->i_time >= get_jiffies_64()) - return 0; - - return fuse_do_getattr(inode); -} - -static int fuse_access(struct inode *inode, int mask) -{ - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req; - struct fuse_access_in inarg; - int err; - - if (fc->no_access) - return 0; - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - memset(&inarg, 0, sizeof(inarg)); - inarg.mask = mask; - req->in.h.opcode = FUSE_ACCESS; - req->in.h.nodeid = get_node_id(inode); - req->in.numargs = 1; - req->in.args[0].size = sizeof(inarg); - req->in.args[0].value = &inarg; - request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - if (err == -ENOSYS) { - fc->no_access = 1; - err = 0; - } - return err; -} - -/* - * Check permission. The two basic access models of FUSE are: - * - * 1) Local access checking ('default_permissions' mount option) based - * on file mode. This is the plain old disk filesystem permission - * modell. - * - * 2) "Remote" access checking, where server is responsible for - * checking permission in each inode operation. An exception to this - * is if ->permission() was invoked from sys_access() in which case an - * access request is sent. Execute permission is still checked - * locally based on file mode. - */ -static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) -{ - struct fuse_conn *fc = get_fuse_conn(inode); - - if (!fuse_allow_task(fc, current)) - return -EACCES; - else if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { -#ifdef KERNEL_2_6_10_PLUS - int err = generic_permission(inode, mask, NULL); -#else - int err = vfs_permission(inode, mask); -#endif - - /* If permission is denied, try to refresh file - attributes. This is also needed, because the root - node will at first have no permissions */ - if (err == -EACCES) { - err = fuse_do_getattr(inode); - if (!err) -#ifdef KERNEL_2_6_10_PLUS - err = generic_permission(inode, mask, NULL); -#else - err = vfs_permission(inode, mask); -#endif - } - - /* Note: the opposite of the above test does not - exist. So if permissions are revoked this won't be - noticed immediately, only after the attribute - timeout has expired */ - - return err; - } else { - int mode = inode->i_mode; -#ifndef KERNEL_2_6_11_PLUS - if ((mask & MAY_WRITE) && IS_RDONLY(inode) && - (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) - return -EROFS; -#endif - if ((mask & MAY_EXEC) && !S_ISDIR(mode) && !(mode & S_IXUGO)) - return -EACCES; - -#ifndef LOOKUP_CHDIR -#define LOOKUP_CHDIR 0 -#endif - if (nd && (nd->flags & (LOOKUP_ACCESS | LOOKUP_CHDIR))) - return fuse_access(inode, mask); - return 0; - } -} - -static int parse_dirfile(char *buf, size_t nbytes, struct file *file, - void *dstbuf, filldir_t filldir) -{ - while (nbytes >= FUSE_NAME_OFFSET) { - struct fuse_dirent *dirent = (struct fuse_dirent *) buf; - size_t reclen = FUSE_DIRENT_SIZE(dirent); - int over; - if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX) - return -EIO; - if (reclen > nbytes) - break; - - over = filldir(dstbuf, dirent->name, dirent->namelen, - file->f_pos, dirent->ino, dirent->type); - if (over) - break; - - buf += reclen; - nbytes -= reclen; - file->f_pos = dirent->off; - } - - return 0; -} - -static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) -{ - int err; - size_t nbytes; - struct page *page; - struct inode *inode = file->f_dentry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req; - - if (is_bad_inode(inode)) - return -EIO; - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - page = alloc_page(GFP_KERNEL); - if (!page) { - fuse_put_request(fc, req); - return -ENOMEM; - } - req->num_pages = 1; - req->pages[0] = page; - fuse_read_fill(req, file, inode, file->f_pos, PAGE_SIZE, FUSE_READDIR); - request_send(fc, req); - nbytes = req->out.args[0].size; - err = req->out.h.error; - fuse_put_request(fc, req); - if (!err) - err = parse_dirfile(page_address(page), nbytes, file, dstbuf, - filldir); - - __free_page(page); - fuse_invalidate_attr(inode); /* atime changed */ - return err; -} - -static char *read_link(struct dentry *dentry) -{ - struct inode *inode = dentry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req = fuse_get_req(fc); - char *link; - - if (IS_ERR(req)) - return ERR_PTR(PTR_ERR(req)); - - link = (char *) __get_free_page(GFP_KERNEL); - if (!link) { - link = ERR_PTR(-ENOMEM); - goto out; - } - req->in.h.opcode = FUSE_READLINK; - req->in.h.nodeid = get_node_id(inode); - req->out.argvar = 1; - req->out.numargs = 1; - req->out.args[0].size = PAGE_SIZE - 1; - req->out.args[0].value = link; - request_send(fc, req); - if (req->out.h.error) { - free_page((unsigned long) link); - link = ERR_PTR(req->out.h.error); - } else - link[req->out.args[0].size] = '\0'; - out: - fuse_put_request(fc, req); - fuse_invalidate_attr(inode); /* atime changed */ - return link; -} - -static void free_link(char *link) -{ - if (!IS_ERR(link)) - free_page((unsigned long) link); -} - -#ifdef KERNEL_2_6_13_PLUS -static void *fuse_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - nd_set_link(nd, read_link(dentry)); - return NULL; -} - -static void fuse_put_link(struct dentry *dentry, struct nameidata *nd, void *c) -{ - free_link(nd_get_link(nd)); -} -#else -static int fuse_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - nd_set_link(nd, read_link(dentry)); - return 0; -} - -static void fuse_put_link(struct dentry *dentry, struct nameidata *nd) -{ - free_link(nd_get_link(nd)); -} -#endif - -static int fuse_dir_open(struct inode *inode, struct file *file) -{ - return fuse_open_common(inode, file, 1); -} - -static int fuse_dir_release(struct inode *inode, struct file *file) -{ - return fuse_release_common(inode, file, 1); -} - -static int fuse_dir_fsync(struct file *file, struct dentry *de, int datasync) -{ - /* nfsd can call this with no file */ - return file ? fuse_fsync_common(file, de, datasync, 1) : 0; -} - -static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) -{ - unsigned ivalid = iattr->ia_valid; - - if (ivalid & ATTR_MODE) - arg->valid |= FATTR_MODE, arg->mode = iattr->ia_mode; - if (ivalid & ATTR_UID) - arg->valid |= FATTR_UID, arg->uid = iattr->ia_uid; - if (ivalid & ATTR_GID) - arg->valid |= FATTR_GID, arg->gid = iattr->ia_gid; - if (ivalid & ATTR_SIZE) - arg->valid |= FATTR_SIZE, arg->size = iattr->ia_size; - /* You can only _set_ these together (they may change by themselves) */ - if ((ivalid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME)) { - arg->valid |= FATTR_ATIME | FATTR_MTIME; - arg->atime = iattr->ia_atime.tv_sec; - arg->mtime = iattr->ia_mtime.tv_sec; - } -#ifdef ATTR_FILE - if (ivalid & ATTR_FILE) { - struct fuse_file *ff = iattr->ia_file->private_data; - arg->valid |= FATTR_FH; - arg->fh = ff->fh; - } -#endif -} - -static void fuse_vmtruncate(struct inode *inode, loff_t offset) -{ - struct fuse_conn *fc = get_fuse_conn(inode); - int need_trunc; - - spin_lock(&fc->lock); - need_trunc = inode->i_size > offset; - i_size_write(inode, offset); - spin_unlock(&fc->lock); - - if (need_trunc) { - struct address_space *mapping = inode->i_mapping; - unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1); - truncate_inode_pages(mapping, offset); - } -} - -/* - * Set attributes, and at the same time refresh them. - * - * Truncation is slightly complicated, because the 'truncate' request - * may fail, in which case we don't want to touch the mapping. - * vmtruncate() doesn't allow for this case, so do the rlimit checking - * and the actual truncation by hand. - */ -static int fuse_setattr(struct dentry *entry, struct iattr *attr) -{ - struct inode *inode = entry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_inode *fi = get_fuse_inode(inode); - struct fuse_req *req; - struct fuse_setattr_in inarg; - struct fuse_attr_out outarg; - int err; - int is_truncate = 0; - - if (!fuse_allow_task(fc, current)) - return -EACCES; - - if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { - err = inode_change_ok(inode, attr); - if (err) - return err; - } - - if (attr->ia_valid & ATTR_SIZE) { - unsigned long limit; - is_truncate = 1; - if (IS_SWAPFILE(inode)) - return -ETXTBSY; -#ifdef KERNEL_2_6_10_PLUS - limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur; -#else - limit = current->rlim[RLIMIT_FSIZE].rlim_cur; -#endif - if (limit != RLIM_INFINITY && attr->ia_size > (loff_t) limit) { - send_sig(SIGXFSZ, current, 0); - return -EFBIG; - } - } - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - memset(&inarg, 0, sizeof(inarg)); - iattr_to_fattr(attr, &inarg); - /* Defend against future expansion of ATTR_FILE use */ - if (S_ISDIR(inode->i_mode)) - inarg.valid &= ~FATTR_FH; - req->in.h.opcode = FUSE_SETATTR; - req->in.h.nodeid = get_node_id(inode); - req->in.numargs = 1; - req->in.args[0].size = sizeof(inarg); - req->in.args[0].value = &inarg; - req->out.numargs = 1; - req->out.args[0].size = sizeof(outarg); - req->out.args[0].value = &outarg; - request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - if (!err) { - if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) { -#ifndef KERNEL_2_6_12_PLUS - if (get_node_id(inode) != FUSE_ROOT_ID) - make_bad_inode(inode); -#else - make_bad_inode(inode); -#endif - err = -EIO; - } else { - if (is_truncate) - fuse_vmtruncate(inode, outarg.attr.size); - fuse_change_attributes(inode, &outarg.attr); - fi->i_time = time_to_jiffies(outarg.attr_valid, - outarg.attr_valid_nsec); - } - } else if (err == -EINTR) - fuse_invalidate_attr(inode); - - return err; -} - -static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, - struct kstat *stat) -{ - struct inode *inode = entry->d_inode; - int err = fuse_revalidate(entry); - if (!err) - /* FIXME: may want specialized function because of - st_blksize on block devices on 2.6.19+ */ - generic_fillattr(inode, stat); - - return err; -} - -static int fuse_setxattr(struct dentry *entry, const char *name, - const void *value, size_t size, int flags) -{ - struct inode *inode = entry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req; - struct fuse_setxattr_in inarg; - int err; - - if (fc->no_setxattr) - return -EOPNOTSUPP; - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - memset(&inarg, 0, sizeof(inarg)); - inarg.size = size; - inarg.flags = flags; - req->in.h.opcode = FUSE_SETXATTR; - req->in.h.nodeid = get_node_id(inode); - req->in.numargs = 3; - req->in.args[0].size = sizeof(inarg); - req->in.args[0].value = &inarg; - req->in.args[1].size = strlen(name) + 1; - req->in.args[1].value = name; - req->in.args[2].size = size; - req->in.args[2].value = value; - request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - if (err == -ENOSYS) { - fc->no_setxattr = 1; - err = -EOPNOTSUPP; - } - return err; -} - -static ssize_t fuse_getxattr(struct dentry *entry, const char *name, - void *value, size_t size) -{ - struct inode *inode = entry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req; - struct fuse_getxattr_in inarg; - struct fuse_getxattr_out outarg; - ssize_t ret; - - if (fc->no_getxattr) - return -EOPNOTSUPP; - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - memset(&inarg, 0, sizeof(inarg)); - inarg.size = size; - req->in.h.opcode = FUSE_GETXATTR; - req->in.h.nodeid = get_node_id(inode); - req->in.numargs = 2; - req->in.args[0].size = sizeof(inarg); - req->in.args[0].value = &inarg; - req->in.args[1].size = strlen(name) + 1; - req->in.args[1].value = name; - /* This is really two different operations rolled into one */ - req->out.numargs = 1; - if (size) { - req->out.argvar = 1; - req->out.args[0].size = size; - req->out.args[0].value = value; - } else { - req->out.args[0].size = sizeof(outarg); - req->out.args[0].value = &outarg; - } - request_send(fc, req); - ret = req->out.h.error; - if (!ret) - ret = size ? req->out.args[0].size : outarg.size; - else { - if (ret == -ENOSYS) { - fc->no_getxattr = 1; - ret = -EOPNOTSUPP; - } - } - fuse_put_request(fc, req); - return ret; -} - -static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size) -{ - struct inode *inode = entry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req; - struct fuse_getxattr_in inarg; - struct fuse_getxattr_out outarg; - ssize_t ret; - - if (!fuse_allow_task(fc, current)) - return -EACCES; - - if (fc->no_listxattr) - return -EOPNOTSUPP; - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - memset(&inarg, 0, sizeof(inarg)); - inarg.size = size; - req->in.h.opcode = FUSE_LISTXATTR; - req->in.h.nodeid = get_node_id(inode); - req->in.numargs = 1; - req->in.args[0].size = sizeof(inarg); - req->in.args[0].value = &inarg; - /* This is really two different operations rolled into one */ - req->out.numargs = 1; - if (size) { - req->out.argvar = 1; - req->out.args[0].size = size; - req->out.args[0].value = list; - } else { - req->out.args[0].size = sizeof(outarg); - req->out.args[0].value = &outarg; - } - request_send(fc, req); - ret = req->out.h.error; - if (!ret) - ret = size ? req->out.args[0].size : outarg.size; - else { - if (ret == -ENOSYS) { - fc->no_listxattr = 1; - ret = -EOPNOTSUPP; - } - } - fuse_put_request(fc, req); - return ret; -} - -static int fuse_removexattr(struct dentry *entry, const char *name) -{ - struct inode *inode = entry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req; - int err; - - if (fc->no_removexattr) - return -EOPNOTSUPP; - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - req->in.h.opcode = FUSE_REMOVEXATTR; - req->in.h.nodeid = get_node_id(inode); - req->in.numargs = 1; - req->in.args[0].size = strlen(name) + 1; - req->in.args[0].value = name; - request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - if (err == -ENOSYS) { - fc->no_removexattr = 1; - err = -EOPNOTSUPP; - } - return err; -} - -static struct inode_operations fuse_dir_inode_operations = { - .lookup = fuse_lookup, - .mkdir = fuse_mkdir, - .symlink = fuse_symlink, - .unlink = fuse_unlink, - .rmdir = fuse_rmdir, - .rename = fuse_rename, - .link = fuse_link, - .setattr = fuse_setattr, - .create = fuse_create, - .mknod = fuse_mknod, - .permission = fuse_permission, - .getattr = fuse_getattr, - .setxattr = fuse_setxattr, - .getxattr = fuse_getxattr, - .listxattr = fuse_listxattr, - .removexattr = fuse_removexattr, -}; - -static struct file_operations fuse_dir_operations = { - .llseek = generic_file_llseek, - .read = generic_read_dir, - .readdir = fuse_readdir, - .open = fuse_dir_open, - .release = fuse_dir_release, - .fsync = fuse_dir_fsync, -}; - -static struct inode_operations fuse_common_inode_operations = { - .setattr = fuse_setattr, - .permission = fuse_permission, - .getattr = fuse_getattr, - .setxattr = fuse_setxattr, - .getxattr = fuse_getxattr, - .listxattr = fuse_listxattr, - .removexattr = fuse_removexattr, -}; - -static struct inode_operations fuse_symlink_inode_operations = { - .setattr = fuse_setattr, - .follow_link = fuse_follow_link, - .put_link = fuse_put_link, - .readlink = generic_readlink, - .getattr = fuse_getattr, - .setxattr = fuse_setxattr, - .getxattr = fuse_getxattr, - .listxattr = fuse_listxattr, - .removexattr = fuse_removexattr, -}; - -void fuse_init_common(struct inode *inode) -{ - inode->i_op = &fuse_common_inode_operations; -} - -void fuse_init_dir(struct inode *inode) -{ - inode->i_op = &fuse_dir_inode_operations; - inode->i_fop = &fuse_dir_operations; -} - -void fuse_init_symlink(struct inode *inode) -{ - inode->i_op = &fuse_symlink_inode_operations; -} diff --git a/kernel/file.c b/kernel/file.c deleted file mode 100644 index 16a2d02..0000000 --- a/kernel/file.c +++ /dev/null @@ -1,900 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2007 Miklos Szeredi - - This program can be distributed under the terms of the GNU GPL. - See the file COPYING. -*/ - -#include "fuse_i.h" - -#include -#include -#include -#include - -#ifndef KERNEL_2_6_11_PLUS -static inline loff_t page_offset(struct page *page) -{ - return ((loff_t)page->index) << PAGE_CACHE_SHIFT; -} -#endif -static struct file_operations fuse_direct_io_file_operations; - -static int fuse_send_open(struct inode *inode, struct file *file, int isdir, - struct fuse_open_out *outargp) -{ - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_open_in inarg; - struct fuse_req *req; - int err; - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - memset(&inarg, 0, sizeof(inarg)); - inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); - req->in.h.opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN; - req->in.h.nodeid = get_node_id(inode); - req->in.numargs = 1; - req->in.args[0].size = sizeof(inarg); - req->in.args[0].value = &inarg; - req->out.numargs = 1; - req->out.args[0].size = sizeof(*outargp); - req->out.args[0].value = outargp; - request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - - return err; -} - -struct fuse_file *fuse_file_alloc(void) -{ - struct fuse_file *ff; - ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL); - if (ff) { - ff->reserved_req = fuse_request_alloc(); - if (!ff->reserved_req) { - kfree(ff); - ff = NULL; - } - } - return ff; -} - -void fuse_file_free(struct fuse_file *ff) -{ - fuse_request_free(ff->reserved_req); - kfree(ff); -} - -void fuse_finish_open(struct inode *inode, struct file *file, - struct fuse_file *ff, struct fuse_open_out *outarg) -{ - if (outarg->open_flags & FOPEN_DIRECT_IO) - file->f_op = &fuse_direct_io_file_operations; - if (!(outarg->open_flags & FOPEN_KEEP_CACHE)) -#ifdef KERNEL_2_6_21_PLUS - invalidate_mapping_pages(inode->i_mapping, 0, -1); -#else - invalidate_inode_pages(inode->i_mapping); -#endif - ff->fh = outarg->fh; - file->private_data = ff; -} - -int fuse_open_common(struct inode *inode, struct file *file, int isdir) -{ - struct fuse_open_out outarg; - struct fuse_file *ff; - int err; - - /* VFS checks this, but only _after_ ->open() */ - if (file->f_flags & O_DIRECT) - return -EINVAL; - - err = generic_file_open(inode, file); - if (err) - return err; - - /* If opening the root node, no lookup has been performed on - it, so the attributes must be refreshed */ - if (get_node_id(inode) == FUSE_ROOT_ID) { - err = fuse_do_getattr(inode); - if (err) - return err; - } - - ff = fuse_file_alloc(); - if (!ff) - return -ENOMEM; - - err = fuse_send_open(inode, file, isdir, &outarg); - if (err) - fuse_file_free(ff); - else { - if (isdir) - outarg.open_flags &= ~FOPEN_DIRECT_IO; - fuse_finish_open(inode, file, ff, &outarg); - } - - return err; -} - -struct fuse_req *fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags, - int opcode) -{ - struct fuse_req *req = ff->reserved_req; - struct fuse_release_in *inarg = &req->misc.release_in; - - inarg->fh = ff->fh; - inarg->flags = flags; - req->in.h.opcode = opcode; - req->in.h.nodeid = nodeid; - req->in.numargs = 1; - req->in.args[0].size = sizeof(struct fuse_release_in); - req->in.args[0].value = inarg; - kfree(ff); - - return req; -} - -int fuse_release_common(struct inode *inode, struct file *file, int isdir) -{ - struct fuse_file *ff = file->private_data; - if (ff) { - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req; - - req = fuse_release_fill(ff, get_node_id(inode), file->f_flags, - isdir ? FUSE_RELEASEDIR : FUSE_RELEASE); - - /* Hold vfsmount and dentry until release is finished */ - req->vfsmount = mntget(file->f_vfsmnt); - req->dentry = dget(file->f_dentry); - request_send_background(fc, req); - } - - /* Return value is ignored by VFS */ - return 0; -} - -static int fuse_open(struct inode *inode, struct file *file) -{ - return fuse_open_common(inode, file, 0); -} - -static int fuse_release(struct inode *inode, struct file *file) -{ - return fuse_release_common(inode, file, 0); -} - -/* - * Scramble the ID space with XTEA, so that the value of the files_struct - * pointer is not exposed to userspace. - */ -static u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id) -{ - u32 *k = fc->scramble_key; - u64 v = (unsigned long) id; - u32 v0 = v; - u32 v1 = v >> 32; - u32 sum = 0; - int i; - - for (i = 0; i < 32; i++) { - v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + k[sum & 3]); - sum += 0x9E3779B9; - v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[sum>>11 & 3]); - } - - return (u64) v0 + ((u64) v1 << 32); -} - -#ifdef KERNEL_2_6_18_PLUS -static int fuse_flush(struct file *file, fl_owner_t id) -#else -static int fuse_flush(struct file *file) -#endif -{ - struct inode *inode = file->f_dentry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_file *ff = file->private_data; - struct fuse_req *req; - struct fuse_flush_in inarg; - int err; - - if (is_bad_inode(inode)) - return -EIO; - - if (fc->no_flush) - return 0; - - req = fuse_get_req_nofail(fc, file); - memset(&inarg, 0, sizeof(inarg)); - inarg.fh = ff->fh; -#ifdef KERNEL_2_6_18_PLUS - inarg.lock_owner = fuse_lock_owner_id(fc, id); -#else - inarg.lock_owner = fuse_lock_owner_id(fc, NULL); -#endif - req->in.h.opcode = FUSE_FLUSH; - req->in.h.nodeid = get_node_id(inode); - req->in.numargs = 1; - req->in.args[0].size = sizeof(inarg); - req->in.args[0].value = &inarg; - req->force = 1; - request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - if (err == -ENOSYS) { - fc->no_flush = 1; - err = 0; - } - return err; -} - -int fuse_fsync_common(struct file *file, struct dentry *de, int datasync, - int isdir) -{ - struct inode *inode = de->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_file *ff = file->private_data; - struct fuse_req *req; - struct fuse_fsync_in inarg; - int err; - - if (is_bad_inode(inode)) - return -EIO; - - if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir)) - return 0; - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - memset(&inarg, 0, sizeof(inarg)); - inarg.fh = ff->fh; - inarg.fsync_flags = datasync ? 1 : 0; - req->in.h.opcode = isdir ? FUSE_FSYNCDIR : FUSE_FSYNC; - req->in.h.nodeid = get_node_id(inode); - req->in.numargs = 1; - req->in.args[0].size = sizeof(inarg); - req->in.args[0].value = &inarg; - request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - if (err == -ENOSYS) { - if (isdir) - fc->no_fsyncdir = 1; - else - fc->no_fsync = 1; - err = 0; - } - return err; -} - -static int fuse_fsync(struct file *file, struct dentry *de, int datasync) -{ - return fuse_fsync_common(file, de, datasync, 0); -} - -void fuse_read_fill(struct fuse_req *req, struct file *file, - struct inode *inode, loff_t pos, size_t count, int opcode) -{ - struct fuse_file *ff = file->private_data; - struct fuse_read_in *inarg = &req->misc.read_in; - - inarg->fh = ff->fh; - inarg->offset = pos; - inarg->size = count; - req->in.h.opcode = opcode; - req->in.h.nodeid = get_node_id(inode); - req->in.numargs = 1; - req->in.args[0].size = sizeof(struct fuse_read_in); - req->in.args[0].value = inarg; - req->out.argpages = 1; - req->out.argvar = 1; - req->out.numargs = 1; - req->out.args[0].size = count; -} - -static size_t fuse_send_read(struct fuse_req *req, struct file *file, - struct inode *inode, loff_t pos, size_t count) -{ - struct fuse_conn *fc = get_fuse_conn(inode); - fuse_read_fill(req, file, inode, pos, count, FUSE_READ); - request_send(fc, req); - return req->out.args[0].size; -} - -static int fuse_readpage(struct file *file, struct page *page) -{ - struct inode *inode = page->mapping->host; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req; - int err; - - err = -EIO; - if (is_bad_inode(inode)) - goto out; - - req = fuse_get_req(fc); - err = PTR_ERR(req); - if (IS_ERR(req)) - goto out; - - req->out.page_zeroing = 1; - req->num_pages = 1; - req->pages[0] = page; - fuse_send_read(req, file, inode, page_offset(page), PAGE_CACHE_SIZE); - err = req->out.h.error; - fuse_put_request(fc, req); - if (!err) - SetPageUptodate(page); - fuse_invalidate_attr(inode); /* atime changed */ - out: - unlock_page(page); - return err; -} - -static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req) -{ - int i; - - fuse_invalidate_attr(req->pages[0]->mapping->host); /* atime changed */ - - for (i = 0; i < req->num_pages; i++) { - struct page *page = req->pages[i]; - if (!req->out.h.error) - SetPageUptodate(page); - else - SetPageError(page); - unlock_page(page); - } - fuse_put_request(fc, req); -} - -static void fuse_send_readpages(struct fuse_req *req, struct file *file, - struct inode *inode) -{ - struct fuse_conn *fc = get_fuse_conn(inode); - loff_t pos = page_offset(req->pages[0]); - size_t count = req->num_pages << PAGE_CACHE_SHIFT; - req->out.page_zeroing = 1; - fuse_read_fill(req, file, inode, pos, count, FUSE_READ); - if (fc->async_read) { - get_file(file); - req->file = file; - req->end = fuse_readpages_end; - request_send_background(fc, req); - } else { - request_send(fc, req); - fuse_readpages_end(fc, req); - } -} - -struct fuse_readpages_data { - struct fuse_req *req; - struct file *file; - struct inode *inode; -}; - -static int fuse_readpages_fill(void *_data, struct page *page) -{ - struct fuse_readpages_data *data = _data; - struct fuse_req *req = data->req; - struct inode *inode = data->inode; - struct fuse_conn *fc = get_fuse_conn(inode); - int err; - - if (req && req->num_pages && - (req->num_pages == FUSE_MAX_PAGES_PER_REQ || - (req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_read || - req->pages[req->num_pages - 1]->index + 1 != page->index)) { - fuse_send_readpages(req, data->file, inode); - req = NULL; - } - if (!req) { - err = -EIO; - if (is_bad_inode(inode)) - goto out_unlock_page; - - data->req = req = fuse_get_req(fc); - err = PTR_ERR(req); - if (IS_ERR(req)) - goto out_unlock_page; - } - req->pages[req->num_pages] = page; - req->num_pages ++; - return 0; - - out_unlock_page: - unlock_page(page); - return err; -} - -static int fuse_readpages(struct file *file, struct address_space *mapping, - struct list_head *pages, unsigned nr_pages) -{ - struct inode *inode = mapping->host; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_readpages_data data; - int err; - - data.file = file; - data.inode = inode; - data.req = NULL; - - err = read_cache_pages(mapping, pages, fuse_readpages_fill, &data); - if (!err && data.req) { - if (data.req->num_pages) - fuse_send_readpages(data.req, file, inode); - else - fuse_put_request(fc, data.req); - } - return err; -} - -static size_t fuse_send_write(struct fuse_req *req, struct file *file, - struct inode *inode, loff_t pos, size_t count) -{ - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_file *ff = file->private_data; - struct fuse_write_in inarg; - struct fuse_write_out outarg; - - memset(&inarg, 0, sizeof(struct fuse_write_in)); - inarg.fh = ff->fh; - inarg.offset = pos; - inarg.size = count; - req->in.h.opcode = FUSE_WRITE; - req->in.h.nodeid = get_node_id(inode); - req->in.argpages = 1; - req->in.numargs = 2; - req->in.args[0].size = sizeof(struct fuse_write_in); - req->in.args[0].value = &inarg; - req->in.args[1].size = count; - req->out.numargs = 1; - req->out.args[0].size = sizeof(struct fuse_write_out); - req->out.args[0].value = &outarg; - request_send(fc, req); - return outarg.size; -} - -static int fuse_prepare_write(struct file *file, struct page *page, - unsigned offset, unsigned to) -{ - /* No op */ - return 0; -} - -static int fuse_commit_write(struct file *file, struct page *page, - unsigned offset, unsigned to) -{ - int err; - size_t nres; - unsigned count = to - offset; - struct inode *inode = page->mapping->host; - struct fuse_conn *fc = get_fuse_conn(inode); - loff_t pos = page_offset(page) + offset; - struct fuse_req *req; - - if (is_bad_inode(inode)) - return -EIO; - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - req->num_pages = 1; - req->pages[0] = page; - req->page_offset = offset; - nres = fuse_send_write(req, file, inode, pos, count); - err = req->out.h.error; - fuse_put_request(fc, req); - if (!err && nres != count) - err = -EIO; - if (!err) { - pos += count; - spin_lock(&fc->lock); - if (pos > inode->i_size) - i_size_write(inode, pos); - spin_unlock(&fc->lock); - - if (offset == 0 && to == PAGE_CACHE_SIZE) - SetPageUptodate(page); - } - fuse_invalidate_attr(inode); - return err; -} - -static void fuse_release_user_pages(struct fuse_req *req, int write) -{ - unsigned i; - - for (i = 0; i < req->num_pages; i++) { - struct page *page = req->pages[i]; - if (write) - set_page_dirty_lock(page); - put_page(page); - } -} - -static int fuse_get_user_pages(struct fuse_req *req, const char __user *buf, - unsigned nbytes, int write) -{ - unsigned long user_addr = (unsigned long) buf; - unsigned offset = user_addr & ~PAGE_MASK; - int npages; - - /* This doesn't work with nfsd */ - if (!current->mm) - return -EPERM; - - nbytes = min(nbytes, (unsigned) FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT); - npages = (nbytes + offset + PAGE_SIZE - 1) >> PAGE_SHIFT; - npages = max(npages, 1); - npages = min(npages, FUSE_MAX_PAGES_PER_REQ); - down_read(¤t->mm->mmap_sem); - npages = get_user_pages(current, current->mm, user_addr, npages, write, - 0, req->pages, NULL); - up_read(¤t->mm->mmap_sem); - if (npages < 0) - return npages; - - req->num_pages = npages; - req->page_offset = offset; - return 0; -} - -static ssize_t fuse_direct_io(struct file *file, const char __user *buf, - size_t count, loff_t *ppos, int write) -{ - struct inode *inode = file->f_dentry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - size_t nmax = write ? fc->max_write : fc->max_read; - loff_t pos = *ppos; - ssize_t res = 0; - struct fuse_req *req; - - if (is_bad_inode(inode)) - return -EIO; - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - while (count) { - size_t nres; - size_t nbytes = min(count, nmax); - int err = fuse_get_user_pages(req, buf, nbytes, !write); - if (err) { - res = err; - break; - } - nbytes = (req->num_pages << PAGE_SHIFT) - req->page_offset; - nbytes = min(count, nbytes); - if (write) - nres = fuse_send_write(req, file, inode, pos, nbytes); - else - nres = fuse_send_read(req, file, inode, pos, nbytes); - fuse_release_user_pages(req, !write); - if (req->out.h.error) { - if (!res) - res = req->out.h.error; - break; - } else if (nres > nbytes) { - res = -EIO; - break; - } - count -= nres; - res += nres; - pos += nres; - buf += nres; - if (nres != nbytes) - break; - if (count) { - fuse_put_request(fc, req); - req = fuse_get_req(fc); - if (IS_ERR(req)) - break; - } - } - fuse_put_request(fc, req); - if (res > 0) { - if (write) { - spin_lock(&fc->lock); - if (pos > inode->i_size) - i_size_write(inode, pos); - spin_unlock(&fc->lock); - } - *ppos = pos; - } - fuse_invalidate_attr(inode); - - return res; -} - -static ssize_t fuse_direct_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - return fuse_direct_io(file, buf, count, ppos, 0); -} - -static ssize_t fuse_direct_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct inode *inode = file->f_dentry->d_inode; - ssize_t res; - /* Don't allow parallel writes to the same file */ - mutex_lock(&inode->i_mutex); - res = generic_write_checks(file, ppos, &count, 0); - if (!res) - res = fuse_direct_io(file, buf, count, ppos, 1); - mutex_unlock(&inode->i_mutex); - return res; -} - -static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma) -{ - if ((vma->vm_flags & VM_SHARED)) { - if ((vma->vm_flags & VM_WRITE)) - return -ENODEV; - else - vma->vm_flags &= ~VM_MAYWRITE; - } - return generic_file_mmap(file, vma); -} - -static int fuse_set_page_dirty(struct page *page) -{ - printk("fuse_set_page_dirty: should not happen\n"); - dump_stack(); - return 0; -} - -static int convert_fuse_file_lock(const struct fuse_file_lock *ffl, - struct file_lock *fl) -{ - switch (ffl->type) { - case F_UNLCK: - break; - - case F_RDLCK: - case F_WRLCK: - if (ffl->start > OFFSET_MAX || ffl->end > OFFSET_MAX || - ffl->end < ffl->start) - return -EIO; - - fl->fl_start = ffl->start; - fl->fl_end = ffl->end; - fl->fl_pid = ffl->pid; - break; - - default: - return -EIO; - } - fl->fl_type = ffl->type; - return 0; -} - -static void fuse_lk_fill(struct fuse_req *req, struct file *file, - const struct file_lock *fl, int opcode, pid_t pid) -{ - struct inode *inode = file->f_dentry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_file *ff = file->private_data; - struct fuse_lk_in *arg = &req->misc.lk_in; - - arg->fh = ff->fh; - arg->owner = fuse_lock_owner_id(fc, fl->fl_owner); - arg->lk.start = fl->fl_start; - arg->lk.end = fl->fl_end; - arg->lk.type = fl->fl_type; - arg->lk.pid = pid; - req->in.h.opcode = opcode; - req->in.h.nodeid = get_node_id(inode); - req->in.numargs = 1; - req->in.args[0].size = sizeof(*arg); - req->in.args[0].value = arg; -} - -static int fuse_getlk(struct file *file, struct file_lock *fl) -{ - struct inode *inode = file->f_dentry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req; - struct fuse_lk_out outarg; - int err; - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - fuse_lk_fill(req, file, fl, FUSE_GETLK, 0); - req->out.numargs = 1; - req->out.args[0].size = sizeof(outarg); - req->out.args[0].value = &outarg; - request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - if (!err) - err = convert_fuse_file_lock(&outarg.lk, fl); - - return err; -} - -static int fuse_setlk(struct file *file, struct file_lock *fl) -{ - struct inode *inode = file->f_dentry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req; - int opcode = (fl->fl_flags & FL_SLEEP) ? FUSE_SETLKW : FUSE_SETLK; - pid_t pid = fl->fl_type != F_UNLCK ? current->tgid : 0; - int err; - -#ifdef KERNEL_2_6_18_PLUS - /* Unlock on close is handled by the flush method */ - if (fl->fl_flags & FL_CLOSE) - return 0; - - req = fuse_get_req(fc); -#else - /* If it's (possibly) unlock on close, don't fail the allocation */ - if (fl->fl_type == F_UNLCK && fl->fl_start == 0 && - fl->fl_end == OFFSET_MAX) - req = fuse_get_req_nofail(fc, file); - else { - /* Hack: add dummy lock, otherwise unlock on close is - optimized away */ - struct file_lock **flp; - for (flp = &inode->i_flock; - *flp && !((*flp)->fl_flags & FL_POSIX); - flp = &(*flp)->fl_next); - if (!*flp) { - struct file_lock *dummy = - kmalloc(sizeof(struct file_lock), GFP_KERNEL); - if (!dummy) - return -ENOLCK; - locks_init_lock(dummy); - dummy->fl_flags |= FL_POSIX; - *flp = dummy; - } - req = fuse_get_req(fc); - } -#endif - if (IS_ERR(req)) - return PTR_ERR(req); - - fuse_lk_fill(req, file, fl, opcode, pid); - request_send(fc, req); - err = req->out.h.error; - /* locking is restartable */ - if (err == -EINTR) - err = -ERESTARTSYS; - fuse_put_request(fc, req); - return err; -} - -static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl) -{ - struct inode *inode = file->f_dentry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - int err; - - if (cmd == F_GETLK) { - if (fc->no_lock) { -#ifdef KERNEL_2_6_22_PLUS - posix_test_lock(file, fl); -#elif defined(KERNEL_2_6_17_PLUS) - if (!posix_test_lock(file, fl, fl)) - fl->fl_type = F_UNLCK; -#else - struct file_lock *cfl = posix_test_lock(file, fl); - if (!cfl) - fl->fl_type = F_UNLCK; - else - *fl = *cfl; -#endif - err = 0; - } else - err = fuse_getlk(file, fl); - } else { - if (fc->no_lock) - err = posix_lock_file_wait(file, fl); - else - err = fuse_setlk(file, fl); - } - return err; -} - -static sector_t fuse_bmap(struct address_space *mapping, sector_t block) -{ - struct inode *inode = mapping->host; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req; - struct fuse_bmap_in inarg; - struct fuse_bmap_out outarg; - int err; - - if (!inode->i_sb->s_bdev || fc->no_bmap) - return 0; - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return 0; - - memset(&inarg, 0, sizeof(inarg)); - inarg.block = block; - inarg.blocksize = inode->i_sb->s_blocksize; - req->in.h.opcode = FUSE_BMAP; - req->in.h.nodeid = get_node_id(inode); - req->in.numargs = 1; - req->in.args[0].size = sizeof(inarg); - req->in.args[0].value = &inarg; - req->out.numargs = 1; - req->out.args[0].size = sizeof(outarg); - req->out.args[0].value = &outarg; - request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - if (err == -ENOSYS) - fc->no_bmap = 1; - - return err ? 0 : outarg.block; -} - -static struct file_operations fuse_file_operations = { - .llseek = generic_file_llseek, -#ifndef KERNEL_2_6_19_PLUS - .read = generic_file_read, - .write = generic_file_write, -#else - .read = do_sync_read, - .aio_read = generic_file_aio_read, - .write = do_sync_write, - .aio_write = generic_file_aio_write, -#endif - .mmap = fuse_file_mmap, - .open = fuse_open, - .flush = fuse_flush, - .release = fuse_release, - .fsync = fuse_fsync, - .lock = fuse_file_lock, -#ifdef KERNEL_2_6_23_PLUS - .splice_read = generic_file_splice_read, -#else - .sendfile = generic_file_sendfile, -#endif -}; - -static struct file_operations fuse_direct_io_file_operations = { - .llseek = generic_file_llseek, - .read = fuse_direct_read, - .write = fuse_direct_write, - .open = fuse_open, - .flush = fuse_flush, - .release = fuse_release, - .fsync = fuse_fsync, - .lock = fuse_file_lock, - /* no mmap and sendfile */ -}; - -static struct address_space_operations fuse_file_aops = { - .readpage = fuse_readpage, - .prepare_write = fuse_prepare_write, - .commit_write = fuse_commit_write, - .readpages = fuse_readpages, - .set_page_dirty = fuse_set_page_dirty, - .bmap = fuse_bmap, -}; - -void fuse_init_file_inode(struct inode *inode) -{ - inode->i_fop = &fuse_file_operations; - inode->i_data.a_ops = &fuse_file_aops; -} diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h deleted file mode 100644 index a43fc97..0000000 --- a/kernel/fuse_i.h +++ /dev/null @@ -1,654 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2007 Miklos Szeredi - - This program can be distributed under the terms of the GNU GPL. - See the file COPYING. -*/ - -#ifdef FUSE_MAINLINE -#include -#else -#include - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9) -#error Kernel versions earlier than 2.6.9 are not supported -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) -# define KERNEL_2_6_10_PLUS -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11) -# define KERNEL_2_6_11_PLUS -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12) -# define KERNEL_2_6_12_PLUS -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13) -# define KERNEL_2_6_13_PLUS -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -# define KERNEL_2_6_16_PLUS -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) -# define KERNEL_2_6_17_PLUS -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -# define KERNEL_2_6_18_PLUS -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) -# define KERNEL_2_6_19_PLUS -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) -# define KERNEL_2_6_21_PLUS -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -# define KERNEL_2_6_22_PLUS -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -# define KERNEL_2_6_23_PLUS -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) -# define KERNEL_2_6_24_PLUS -#endif - -#if defined(__arm__) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) -#define DCACHE_BUG -#endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15) -#define kmem_cache kmem_cache_s -#endif - -#include "fuse_kernel.h" -#include "config.h" -#endif /* FUSE_MAINLINE */ -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_MUTEX_H -#include -#else -#include -#define DEFINE_MUTEX(m) DECLARE_MUTEX(m) -#define mutex_init(m) init_MUTEX(m) -#define mutex_destroy(m) do { } while (0) -#define mutex_lock(m) down(m) -#define mutex_unlock(m) up(m) -#define mutex semaphore -#endif -#ifndef HAVE_I_MUTEX -#ifndef mutex_destroy -/* Some RHEL kernels include a backported mutex.h, which lacks mutex_destroy */ -#define mutex_destroy(m) do { } while (0) -#endif -#define i_mutex i_sem /* Hack for struct inode */ -#endif -#ifndef KERNEL_2_6_19_PLUS -#define clear_nlink(inode) (inode)->i_nlink = 0 -#define inc_nlink(inode) (inode)->i_nlink++ -#endif -#ifndef HAVE_CONFIG_BLOCK -#define CONFIG_BLOCK -#endif -#ifndef FS_HAS_SUBTYPE -#define FS_HAS_SUBTYPE 0 -#endif -#ifndef FS_SAFE -#define FS_SAFE 0 -#endif - -/** Max number of pages that can be used in a single read request */ -#define FUSE_MAX_PAGES_PER_REQ 32 - -/** Maximum number of outstanding background requests */ -#define FUSE_MAX_BACKGROUND 10 - -/** It could be as large as PATH_MAX, but would that have any uses? */ -#define FUSE_NAME_MAX 1024 - -/** Number of dentries for each connection in the control filesystem */ -#define FUSE_CTL_NUM_DENTRIES 3 - -/** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem - module will check permissions based on the file mode. Otherwise no - permission checking is done in the kernel */ -#define FUSE_DEFAULT_PERMISSIONS (1 << 0) - -/** If the FUSE_ALLOW_OTHER flag is given, then not only the user - doing the mount will be allowed to access the filesystem */ -#define FUSE_ALLOW_OTHER (1 << 1) - -/** List of active connections */ -extern struct list_head fuse_conn_list; - -/** Global mutex protecting fuse_conn_list and the control filesystem */ -extern struct mutex fuse_mutex; - -/** FUSE inode */ -struct fuse_inode { - /** Inode data */ - struct inode inode; - - /** Unique ID, which identifies the inode between userspace - * and kernel */ - u64 nodeid; - - /** Number of lookups on this inode */ - u64 nlookup; - - /** The request used for sending the FORGET message */ - struct fuse_req *forget_req; - - /** Time in jiffies until the file attributes are valid */ - u64 i_time; -}; - -/** FUSE specific file data */ -struct fuse_file { - /** Request reserved for flush and release */ - struct fuse_req *reserved_req; - - /** File handle used by userspace */ - u64 fh; -}; - -/** One input argument of a request */ -struct fuse_in_arg { - unsigned size; - const void *value; -}; - -/** The request input */ -struct fuse_in { - /** The request header */ - struct fuse_in_header h; - - /** True if the data for the last argument is in req->pages */ - unsigned argpages:1; - - /** Number of arguments */ - unsigned numargs; - - /** Array of arguments */ - struct fuse_in_arg args[3]; -}; - -/** One output argument of a request */ -struct fuse_arg { - unsigned size; - void *value; -}; - -/** The request output */ -struct fuse_out { - /** Header returned from userspace */ - struct fuse_out_header h; - - /* - * The following bitfields are not changed during the request - * processing - */ - - /** Last argument is variable length (can be shorter than - arg->size) */ - unsigned argvar:1; - - /** Last argument is a list of pages to copy data to */ - unsigned argpages:1; - - /** Zero partially or not copied pages */ - unsigned page_zeroing:1; - - /** Number or arguments */ - unsigned numargs; - - /** Array of arguments */ - struct fuse_arg args[3]; -}; - -/** The request state */ -enum fuse_req_state { - FUSE_REQ_INIT = 0, - FUSE_REQ_PENDING, - FUSE_REQ_READING, - FUSE_REQ_SENT, - FUSE_REQ_WRITING, - FUSE_REQ_FINISHED -}; - -struct fuse_conn; - -/** - * A request to the client - */ -struct fuse_req { - /** This can be on either pending processing or io lists in - fuse_conn */ - struct list_head list; - - /** Entry on the interrupts list */ - struct list_head intr_entry; - - /** refcount */ - atomic_t count; - - /** Unique ID for the interrupt request */ - u64 intr_unique; - - /* - * The following bitfields are either set once before the - * request is queued or setting/clearing them is protected by - * fuse_conn->lock - */ - - /** True if the request has reply */ - unsigned isreply:1; - - /** Force sending of the request even if interrupted */ - unsigned force:1; - - /** The request was aborted */ - unsigned aborted:1; - - /** Request is sent in the background */ - unsigned background:1; - - /** The request has been interrupted */ - unsigned interrupted:1; - - /** Data is being copied to/from the request */ - unsigned locked:1; - - /** Request is counted as "waiting" */ - unsigned waiting:1; - - /** State of the request */ - enum fuse_req_state state; - - /** The request input */ - struct fuse_in in; - - /** The request output */ - struct fuse_out out; - - /** Used to wake up the task waiting for completion of request*/ - wait_queue_head_t waitq; - - /** Data for asynchronous requests */ - union { - struct fuse_forget_in forget_in; - struct fuse_release_in release_in; - struct fuse_init_in init_in; - struct fuse_init_out init_out; - struct fuse_read_in read_in; - struct fuse_lk_in lk_in; - } misc; - - /** page vector */ - struct page *pages[FUSE_MAX_PAGES_PER_REQ]; - - /** number of pages in vector */ - unsigned num_pages; - - /** offset of data on first page */ - unsigned page_offset; - - /** File used in the request (or NULL) */ - struct file *file; - - /** vfsmount used in release */ - struct vfsmount *vfsmount; - - /** dentry used in release */ - struct dentry *dentry; - - /** Request completion callback */ - void (*end)(struct fuse_conn *, struct fuse_req *); - - /** Request is stolen from fuse_file->reserved_req */ - struct file *stolen_file; -}; - -/** - * A Fuse connection. - * - * This structure is created, when the filesystem is mounted, and is - * destroyed, when the client device is closed and the filesystem is - * unmounted. - */ -struct fuse_conn { - /** Lock protecting accessess to members of this structure */ - spinlock_t lock; - - /** Mutex protecting against directory alias creation */ - struct mutex inst_mutex; - - /** Refcount */ - atomic_t count; - - /** The user id for this mount */ - uid_t user_id; - - /** The group id for this mount */ - gid_t group_id; - - /** The fuse mount flags for this mount */ - unsigned flags; - - /** Maximum read size */ - unsigned max_read; - - /** Maximum write size */ - unsigned max_write; - - /** Readers of the connection are waiting on this */ - wait_queue_head_t waitq; - - /** The list of pending requests */ - struct list_head pending; - - /** The list of requests being processed */ - struct list_head processing; - - /** The list of requests under I/O */ - struct list_head io; - - /** Number of requests currently in the background */ - unsigned num_background; - - /** Pending interrupts */ - struct list_head interrupts; - - /** Flag indicating if connection is blocked. This will be - the case before the INIT reply is received, and if there - are too many outstading backgrounds requests */ - int blocked; - - /** waitq for blocked connection */ - wait_queue_head_t blocked_waitq; - - /** waitq for reserved requests */ - wait_queue_head_t reserved_req_waitq; - - /** The next unique request id */ - u64 reqctr; - - /** Connection established, cleared on umount, connection - abort and device release */ - unsigned connected; - - /** Connection failed (version mismatch). Cannot race with - setting other bitfields since it is only set once in INIT - reply, before any other request, and never cleared */ - unsigned conn_error : 1; - - /** Connection successful. Only set in INIT */ - unsigned conn_init : 1; - - /** Do readpages asynchronously? Only set in INIT */ - unsigned async_read : 1; - - /* - * The following bitfields are only for optimization purposes - * and hence races in setting them will not cause malfunction - */ - - /** Is fsync not implemented by fs? */ - unsigned no_fsync : 1; - - /** Is fsyncdir not implemented by fs? */ - unsigned no_fsyncdir : 1; - - /** Is flush not implemented by fs? */ - unsigned no_flush : 1; - - /** Is setxattr not implemented by fs? */ - unsigned no_setxattr : 1; - - /** Is getxattr not implemented by fs? */ - unsigned no_getxattr : 1; - - /** Is listxattr not implemented by fs? */ - unsigned no_listxattr : 1; - - /** Is removexattr not implemented by fs? */ - unsigned no_removexattr : 1; - - /** Are file locking primitives not implemented by fs? */ - unsigned no_lock : 1; - - /** Is access not implemented by fs? */ - unsigned no_access : 1; - - /** Is create not implemented by fs? */ - unsigned no_create : 1; - - /** Is interrupt not implemented by fs? */ - unsigned no_interrupt : 1; - - /** Is bmap not implemented by fs? */ - unsigned no_bmap : 1; - - /** The number of requests waiting for completion */ - atomic_t num_waiting; - - /** Negotiated minor version */ - unsigned minor; - - /** Backing dev info */ - struct backing_dev_info bdi; - - /** Entry on the fuse_conn_list */ - struct list_head entry; - - /** Unique ID */ - u64 id; - - /** Dentries in the control filesystem */ - struct dentry *ctl_dentry[FUSE_CTL_NUM_DENTRIES]; - - /** number of dentries used in the above array */ - int ctl_ndents; - - /** O_ASYNC requests */ - struct fasync_struct *fasync; - - /** Key for lock owner ID scrambling */ - u32 scramble_key[4]; - - /** Reserved request for the DESTROY message */ - struct fuse_req *destroy_req; -}; - -static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb) -{ - return sb->s_fs_info; -} - -static inline struct fuse_conn *get_fuse_conn(struct inode *inode) -{ - return get_fuse_conn_super(inode->i_sb); -} - -static inline struct fuse_inode *get_fuse_inode(struct inode *inode) -{ - return container_of(inode, struct fuse_inode, inode); -} - -static inline u64 get_node_id(struct inode *inode) -{ - return get_fuse_inode(inode)->nodeid; -} - -/** Device operations */ -extern struct file_operations fuse_dev_operations; - -/** - * Get a filled in inode - */ -struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, - int generation, struct fuse_attr *attr); - -/** - * Send FORGET command - */ -void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, - unsigned long nodeid, u64 nlookup); - -/** - * Initialize READ or READDIR request - */ -void fuse_read_fill(struct fuse_req *req, struct file *file, - struct inode *inode, loff_t pos, size_t count, int opcode); - -/** - * Send OPEN or OPENDIR request - */ -int fuse_open_common(struct inode *inode, struct file *file, int isdir); - -struct fuse_file *fuse_file_alloc(void); -void fuse_file_free(struct fuse_file *ff); -void fuse_finish_open(struct inode *inode, struct file *file, - struct fuse_file *ff, struct fuse_open_out *outarg); - -/** */ -struct fuse_req *fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags, - int opcode); -/** - * Send RELEASE or RELEASEDIR request - */ -int fuse_release_common(struct inode *inode, struct file *file, int isdir); - -/** - * Send FSYNC or FSYNCDIR request - */ -int fuse_fsync_common(struct file *file, struct dentry *de, int datasync, - int isdir); - -/** - * Initialize file operations on a regular file - */ -void fuse_init_file_inode(struct inode *inode); - -/** - * Initialize inode operations on regular files and special files - */ -void fuse_init_common(struct inode *inode); - -/** - * Initialize inode and file operations on a directory - */ -void fuse_init_dir(struct inode *inode); - -/** - * Initialize inode operations on a symlink - */ -void fuse_init_symlink(struct inode *inode); - -/** - * Change attributes of an inode - */ -void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr); - -/** - * Initialize the client device - */ -int fuse_dev_init(void); - -/** - * Cleanup the client device - */ -void fuse_dev_cleanup(void); - -int fuse_ctl_init(void); -void fuse_ctl_cleanup(void); - -/** - * Allocate a request - */ -struct fuse_req *fuse_request_alloc(void); - -/** - * Free a request - */ -void fuse_request_free(struct fuse_req *req); - -/** - * Get a request, may fail with -ENOMEM - */ -struct fuse_req *fuse_get_req(struct fuse_conn *fc); - -/** - * Gets a requests for a file operation, always succeeds - */ -struct fuse_req *fuse_get_req_nofail(struct fuse_conn *fc, struct file *file); - -/** - * Decrement reference count of a request. If count goes to zero free - * the request. - */ -void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req); - -/** - * Send a request (synchronous) - */ -void request_send(struct fuse_conn *fc, struct fuse_req *req); - -/** - * Send a request with no reply - */ -void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req); - -/** - * Send a request in the background - */ -void request_send_background(struct fuse_conn *fc, struct fuse_req *req); - -/* Abort all requests */ -void fuse_abort_conn(struct fuse_conn *fc); - -/** - * Get the attributes of a file - */ -int fuse_do_getattr(struct inode *inode); - -/** - * Invalidate inode attributes - */ -void fuse_invalidate_attr(struct inode *inode); - -/** - * Acquire reference to fuse_conn - */ -struct fuse_conn *fuse_conn_get(struct fuse_conn *fc); - -/** - * Release reference to fuse_conn - */ -void fuse_conn_put(struct fuse_conn *fc); - -/** - * Add connection to control filesystem - */ -int fuse_ctl_add_conn(struct fuse_conn *fc); - -/** - * Remove connection from control filesystem - */ -void fuse_ctl_remove_conn(struct fuse_conn *fc); - -/** - * Is file type valid? - */ -int fuse_valid_type(int m); - -/** - * Is task allowed to perform filesystem operation? - */ -int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task); - -extern struct dentry_operations fuse_dentry_operations; diff --git a/kernel/fuse_kernel.h b/kernel/fuse_kernel.h deleted file mode 100644 index c9c4c7e..0000000 --- a/kernel/fuse_kernel.h +++ /dev/null @@ -1,441 +0,0 @@ -/* - This file defines the kernel interface of FUSE - Copyright (C) 2001-2007 Miklos Szeredi - - This program can be distributed under the terms of the GNU GPL. - See the file COPYING. - - This -- and only this -- header file may also be distributed under - the terms of the BSD Licence as follows: - - Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - SUCH DAMAGE. -*/ - -/* - * This file defines the kernel interface of FUSE - * - * Protocol changelog: - * - * 7.9: - * - new fuse_getattr_in input argument of GETATTR - * - add lk_flags in fuse_lk_in - * - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in - * - add blksize field to fuse_attr - * - add file flags field to fuse_read_in and fuse_write_in - */ - -#ifndef linux -#include -#define __u64 uint64_t -#define __u32 uint32_t -#define __s32 int32_t -#else -#include -#include -#endif - -/** Version number of this interface */ -#define FUSE_KERNEL_VERSION 7 - -/** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 9 - -/** The node ID of the root inode */ -#define FUSE_ROOT_ID 1 - -/** The major number of the fuse character device */ -#define FUSE_MAJOR MISC_MAJOR - -/** The minor number of the fuse character device */ -#define FUSE_MINOR 229 - -/* Make sure all structures are padded to 64bit boundary, so 32bit - userspace works under 64bit kernels */ - -struct fuse_attr { - __u64 ino; - __u64 size; - __u64 blocks; - __u64 atime; - __u64 mtime; - __u64 ctime; - __u32 atimensec; - __u32 mtimensec; - __u32 ctimensec; - __u32 mode; - __u32 nlink; - __u32 uid; - __u32 gid; - __u32 rdev; - __u32 blksize; - __u32 padding; -}; - -struct fuse_kstatfs { - __u64 blocks; - __u64 bfree; - __u64 bavail; - __u64 files; - __u64 ffree; - __u32 bsize; - __u32 namelen; - __u32 frsize; - __u32 padding; - __u32 spare[6]; -}; - -struct fuse_file_lock { - __u64 start; - __u64 end; - __u32 type; - __u32 pid; /* tgid */ -}; - -/** - * Bitmasks for fuse_setattr_in.valid - */ -#define FATTR_MODE (1 << 0) -#define FATTR_UID (1 << 1) -#define FATTR_GID (1 << 2) -#define FATTR_SIZE (1 << 3) -#define FATTR_ATIME (1 << 4) -#define FATTR_MTIME (1 << 5) -#define FATTR_FH (1 << 6) -#define FATTR_ATIME_NOW (1 << 7) -#define FATTR_MTIME_NOW (1 << 8) -#define FATTR_LOCKOWNER (1 << 9) - -/** - * Flags returned by the OPEN request - * - * FOPEN_DIRECT_IO: bypass page cache for this open file - * FOPEN_KEEP_CACHE: don't invalidate the data cache on open - */ -#define FOPEN_DIRECT_IO (1 << 0) -#define FOPEN_KEEP_CACHE (1 << 1) - -/** - * INIT request/reply flags - */ -#define FUSE_ASYNC_READ (1 << 0) -#define FUSE_POSIX_LOCKS (1 << 1) -#define FUSE_FILE_OPS (1 << 2) -#define FUSE_ATOMIC_O_TRUNC (1 << 3) -#define FUSE_BIG_WRITES (1 << 5) - -/** - * Release flags - */ -#define FUSE_RELEASE_FLUSH (1 << 0) - -/** - * Getattr flags - */ -#define FUSE_GETATTR_FH (1 << 0) - -/** - * Lock flags - */ -#define FUSE_LK_FLOCK (1 << 0) - -/** - * WRITE flags - * - * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed - * FUSE_WRITE_LOCKOWNER: lock_owner field is valid - */ -#define FUSE_WRITE_CACHE (1 << 0) -#define FUSE_WRITE_LOCKOWNER (1 << 1) - -/** - * Read flags - */ -#define FUSE_READ_LOCKOWNER (1 << 1) - -enum fuse_opcode { - FUSE_LOOKUP = 1, - FUSE_FORGET = 2, /* no reply */ - FUSE_GETATTR = 3, - FUSE_SETATTR = 4, - FUSE_READLINK = 5, - FUSE_SYMLINK = 6, - FUSE_MKNOD = 8, - FUSE_MKDIR = 9, - FUSE_UNLINK = 10, - FUSE_RMDIR = 11, - FUSE_RENAME = 12, - FUSE_LINK = 13, - FUSE_OPEN = 14, - FUSE_READ = 15, - FUSE_WRITE = 16, - FUSE_STATFS = 17, - FUSE_RELEASE = 18, - FUSE_FSYNC = 20, - FUSE_SETXATTR = 21, - FUSE_GETXATTR = 22, - FUSE_LISTXATTR = 23, - FUSE_REMOVEXATTR = 24, - FUSE_FLUSH = 25, - FUSE_INIT = 26, - FUSE_OPENDIR = 27, - FUSE_READDIR = 28, - FUSE_RELEASEDIR = 29, - FUSE_FSYNCDIR = 30, - FUSE_GETLK = 31, - FUSE_SETLK = 32, - FUSE_SETLKW = 33, - FUSE_ACCESS = 34, - FUSE_CREATE = 35, - FUSE_INTERRUPT = 36, - FUSE_BMAP = 37, - FUSE_DESTROY = 38, -}; - -/* The read buffer is required to be at least 8k, but may be much larger */ -#define FUSE_MIN_READ_BUFFER 8192 - -#define FUSE_COMPAT_ENTRY_OUT_SIZE 120 - -struct fuse_entry_out { - __u64 nodeid; /* Inode ID */ - __u64 generation; /* Inode generation: nodeid:gen must - be unique for the fs's lifetime */ - __u64 entry_valid; /* Cache timeout for the name */ - __u64 attr_valid; /* Cache timeout for the attributes */ - __u32 entry_valid_nsec; - __u32 attr_valid_nsec; - struct fuse_attr attr; -}; - -struct fuse_forget_in { - __u64 nlookup; -}; - -struct fuse_getattr_in { - __u32 getattr_flags; - __u32 dummy; - __u64 fh; -}; - -#define FUSE_COMPAT_ATTR_OUT_SIZE 96 - -struct fuse_attr_out { - __u64 attr_valid; /* Cache timeout for the attributes */ - __u32 attr_valid_nsec; - __u32 dummy; - struct fuse_attr attr; -}; - -struct fuse_mknod_in { - __u32 mode; - __u32 rdev; -}; - -struct fuse_mkdir_in { - __u32 mode; - __u32 padding; -}; - -struct fuse_rename_in { - __u64 newdir; -}; - -struct fuse_link_in { - __u64 oldnodeid; -}; - -struct fuse_setattr_in { - __u32 valid; - __u32 padding; - __u64 fh; - __u64 size; - __u64 lock_owner; - __u64 atime; - __u64 mtime; - __u64 unused2; - __u32 atimensec; - __u32 mtimensec; - __u32 unused3; - __u32 mode; - __u32 unused4; - __u32 uid; - __u32 gid; - __u32 unused5; -}; - -struct fuse_open_in { - __u32 flags; - __u32 mode; -}; - -struct fuse_open_out { - __u64 fh; - __u32 open_flags; - __u32 padding; -}; - -struct fuse_release_in { - __u64 fh; - __u32 flags; - __u32 release_flags; - __u64 lock_owner; -}; - -struct fuse_flush_in { - __u64 fh; - __u32 unused; - __u32 padding; - __u64 lock_owner; -}; - -struct fuse_read_in { - __u64 fh; - __u64 offset; - __u32 size; - __u32 read_flags; - __u64 lock_owner; - __u32 flags; - __u32 padding; -}; - -#define FUSE_COMPAT_WRITE_IN_SIZE 24 - -struct fuse_write_in { - __u64 fh; - __u64 offset; - __u32 size; - __u32 write_flags; - __u64 lock_owner; - __u32 flags; - __u32 padding; -}; - -struct fuse_write_out { - __u32 size; - __u32 padding; -}; - -#define FUSE_COMPAT_STATFS_SIZE 48 - -struct fuse_statfs_out { - struct fuse_kstatfs st; -}; - -struct fuse_fsync_in { - __u64 fh; - __u32 fsync_flags; - __u32 padding; -}; - -struct fuse_setxattr_in { - __u32 size; - __u32 flags; -}; - -struct fuse_getxattr_in { - __u32 size; - __u32 padding; -}; - -struct fuse_getxattr_out { - __u32 size; - __u32 padding; -}; - -struct fuse_lk_in { - __u64 fh; - __u64 owner; - struct fuse_file_lock lk; - __u32 lk_flags; - __u32 padding; -}; - -struct fuse_lk_out { - struct fuse_file_lock lk; -}; - -struct fuse_access_in { - __u32 mask; - __u32 padding; -}; - -struct fuse_init_in { - __u32 major; - __u32 minor; - __u32 max_readahead; - __u32 flags; -}; - -struct fuse_init_out { - __u32 major; - __u32 minor; - __u32 max_readahead; - __u32 flags; - __u32 unused; - __u32 max_write; -}; - -struct fuse_interrupt_in { - __u64 unique; -}; - -struct fuse_bmap_in { - __u64 block; - __u32 blocksize; - __u32 padding; -}; - -struct fuse_bmap_out { - __u64 block; -}; - -struct fuse_in_header { - __u32 len; - __u32 opcode; - __u64 unique; - __u64 nodeid; - __u32 uid; - __u32 gid; - __u32 pid; - __u32 padding; -}; - -struct fuse_out_header { - __u32 len; - __s32 error; - __u64 unique; -}; - -struct fuse_dirent { - __u64 ino; - __u64 off; - __u32 namelen; - __u32 type; - char name[0]; -}; - -#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) -#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1)) -#define FUSE_DIRENT_SIZE(d) \ - FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) diff --git a/kernel/inode.c b/kernel/inode.c deleted file mode 100644 index 0a5900e..0000000 --- a/kernel/inode.c +++ /dev/null @@ -1,1101 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2007 Miklos Szeredi - - This program can be distributed under the terms of the GNU GPL. - See the file COPYING. -*/ - -#include "fuse_i.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Miklos Szeredi "); -MODULE_DESCRIPTION("Filesystem in Userspace"); -#ifdef MODULE_LICENSE -MODULE_LICENSE("GPL"); -#endif - -static struct kmem_cache *fuse_inode_cachep; -struct list_head fuse_conn_list; -DEFINE_MUTEX(fuse_mutex); - -#define FUSE_SUPER_MAGIC 0x65735546 - -#ifndef MAX_LFS_FILESIZE -#define MAX_LFS_FILESIZE (((u64)PAGE_CACHE_SIZE << (BITS_PER_LONG-1))-1) -#endif -struct fuse_mount_data { - int fd; - unsigned rootmode; - unsigned user_id; - unsigned group_id; - unsigned fd_present : 1; - unsigned rootmode_present : 1; - unsigned user_id_present : 1; - unsigned group_id_present : 1; - unsigned flags; - unsigned max_read; - unsigned blksize; -}; - -static struct inode *fuse_alloc_inode(struct super_block *sb) -{ - struct inode *inode; - struct fuse_inode *fi; - - inode = kmem_cache_alloc(fuse_inode_cachep, GFP_KERNEL); - if (!inode) - return NULL; - - fi = get_fuse_inode(inode); - fi->i_time = 0; - fi->nodeid = 0; - fi->nlookup = 0; - fi->forget_req = fuse_request_alloc(); - if (!fi->forget_req) { - kmem_cache_free(fuse_inode_cachep, inode); - return NULL; - } - - return inode; -} - -static void fuse_destroy_inode(struct inode *inode) -{ - struct fuse_inode *fi = get_fuse_inode(inode); - if (fi->forget_req) - fuse_request_free(fi->forget_req); -#ifndef KERNEL_2_6_18_PLUS - if (inode->i_flock) { - WARN_ON(inode->i_flock->fl_next); - kfree(inode->i_flock); - inode->i_flock = NULL; - } -#endif - kmem_cache_free(fuse_inode_cachep, inode); -} - -static void fuse_read_inode(struct inode *inode) -{ - /* No op */ -} - -void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, - unsigned long nodeid, u64 nlookup) -{ - struct fuse_forget_in *inarg = &req->misc.forget_in; - inarg->nlookup = nlookup; - req->in.h.opcode = FUSE_FORGET; - req->in.h.nodeid = nodeid; - req->in.numargs = 1; - req->in.args[0].size = sizeof(struct fuse_forget_in); - req->in.args[0].value = inarg; - request_send_noreply(fc, req); -} - -static void fuse_clear_inode(struct inode *inode) -{ - if (inode->i_sb->s_flags & MS_ACTIVE) { - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_inode *fi = get_fuse_inode(inode); - fuse_send_forget(fc, fi->forget_req, fi->nodeid, fi->nlookup); - fi->forget_req = NULL; - } -} - -static int fuse_remount_fs(struct super_block *sb, int *flags, char *data) -{ - if (*flags & MS_MANDLOCK) - return -EINVAL; - - return 0; -} - -void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr) -{ - struct fuse_conn *fc = get_fuse_conn(inode); - if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size) -#ifdef KERNEL_2_6_21_PLUS - invalidate_mapping_pages(inode->i_mapping, 0, -1); -#else - invalidate_inode_pages(inode->i_mapping); -#endif - - inode->i_ino = attr->ino; - inode->i_mode = (inode->i_mode & S_IFMT) + (attr->mode & 07777); - inode->i_nlink = attr->nlink; - inode->i_uid = attr->uid; - inode->i_gid = attr->gid; - spin_lock(&fc->lock); - i_size_write(inode, attr->size); - spin_unlock(&fc->lock); -#ifdef HAVE_I_BLKSIZE - inode->i_blksize = PAGE_CACHE_SIZE; -#endif - inode->i_blocks = attr->blocks; - inode->i_atime.tv_sec = attr->atime; - inode->i_atime.tv_nsec = attr->atimensec; - inode->i_mtime.tv_sec = attr->mtime; - inode->i_mtime.tv_nsec = attr->mtimensec; - inode->i_ctime.tv_sec = attr->ctime; - inode->i_ctime.tv_nsec = attr->ctimensec; -} - -static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) -{ - inode->i_mode = attr->mode & S_IFMT; - inode->i_size = attr->size; - if (S_ISREG(inode->i_mode)) { - fuse_init_common(inode); - fuse_init_file_inode(inode); - } else if (S_ISDIR(inode->i_mode)) - fuse_init_dir(inode); - else if (S_ISLNK(inode->i_mode)) - fuse_init_symlink(inode); - else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || - S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { - fuse_init_common(inode); - init_special_inode(inode, inode->i_mode, - new_decode_dev(attr->rdev)); - } else - BUG(); -} - -static int fuse_inode_eq(struct inode *inode, void *_nodeidp) -{ - unsigned long nodeid = *(unsigned long *) _nodeidp; - if (get_node_id(inode) == nodeid) - return 1; - else - return 0; -} - -static int fuse_inode_set(struct inode *inode, void *_nodeidp) -{ - unsigned long nodeid = *(unsigned long *) _nodeidp; - get_fuse_inode(inode)->nodeid = nodeid; - return 0; -} - -struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, - int generation, struct fuse_attr *attr) -{ - struct inode *inode; - struct fuse_inode *fi; - struct fuse_conn *fc = get_fuse_conn_super(sb); - - retry: - inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid); - if (!inode) - return NULL; - - if ((inode->i_state & I_NEW)) { - inode->i_flags |= S_NOATIME|S_NOCMTIME; - inode->i_generation = generation; - inode->i_data.backing_dev_info = &fc->bdi; - fuse_init_inode(inode, attr); - unlock_new_inode(inode); - } else if ((inode->i_mode ^ attr->mode) & S_IFMT) { - /* Inode has changed type, any I/O on the old should fail */ - make_bad_inode(inode); - iput(inode); - goto retry; - } - - fi = get_fuse_inode(inode); - spin_lock(&fc->lock); - fi->nlookup ++; - spin_unlock(&fc->lock); - fuse_change_attributes(inode, attr); - return inode; -} - -#ifdef UMOUNT_BEGIN_VFSMOUNT -static void fuse_umount_begin(struct vfsmount *vfsmnt, int flags) -{ - if (flags & MNT_FORCE) - fuse_abort_conn(get_fuse_conn_super(vfsmnt->mnt_sb)); -} -#else -static void fuse_umount_begin(struct super_block *sb) -{ - fuse_abort_conn(get_fuse_conn_super(sb)); -} -#endif - -static void fuse_send_destroy(struct fuse_conn *fc) -{ - struct fuse_req *req = fc->destroy_req; - if (req && fc->conn_init) { - fc->destroy_req = NULL; - req->in.h.opcode = FUSE_DESTROY; - req->force = 1; - request_send(fc, req); - fuse_put_request(fc, req); - } -} - -static void fuse_put_super(struct super_block *sb) -{ - struct fuse_conn *fc = get_fuse_conn_super(sb); - - fuse_send_destroy(fc); - spin_lock(&fc->lock); - fc->connected = 0; - fc->blocked = 0; - spin_unlock(&fc->lock); - /* Flush all readers on this fs */ - kill_fasync(&fc->fasync, SIGIO, POLL_IN); - wake_up_all(&fc->waitq); - wake_up_all(&fc->blocked_waitq); - wake_up_all(&fc->reserved_req_waitq); - mutex_lock(&fuse_mutex); - list_del(&fc->entry); - fuse_ctl_remove_conn(fc); - mutex_unlock(&fuse_mutex); - fuse_conn_put(fc); -} - -static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr) -{ - stbuf->f_type = FUSE_SUPER_MAGIC; - stbuf->f_bsize = attr->bsize; - stbuf->f_frsize = attr->frsize; - stbuf->f_blocks = attr->blocks; - stbuf->f_bfree = attr->bfree; - stbuf->f_bavail = attr->bavail; - stbuf->f_files = attr->files; - stbuf->f_ffree = attr->ffree; - stbuf->f_namelen = attr->namelen; - /* fsid is left zero */ -} - -#ifdef KERNEL_2_6_18_PLUS -static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf) -#else -static int fuse_statfs(struct super_block *sb, struct kstatfs *buf) -#endif -{ -#ifdef KERNEL_2_6_18_PLUS - struct super_block *sb = dentry->d_sb; -#endif - struct fuse_conn *fc = get_fuse_conn_super(sb); - struct fuse_req *req; - struct fuse_statfs_out outarg; - int err; - - if (!fuse_allow_task(fc, current)) { - buf->f_type = FUSE_SUPER_MAGIC; - return 0; - } - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - memset(&outarg, 0, sizeof(outarg)); - req->in.numargs = 0; - req->in.h.opcode = FUSE_STATFS; -#ifdef KERNEL_2_6_18_PLUS - req->in.h.nodeid = get_node_id(dentry->d_inode); -#endif - req->out.numargs = 1; - req->out.args[0].size = - fc->minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(outarg); - req->out.args[0].value = &outarg; - request_send(fc, req); - err = req->out.h.error; - if (!err) - convert_fuse_statfs(buf, &outarg.st); - fuse_put_request(fc, req); - return err; -} - -enum { - OPT_FD, - OPT_ROOTMODE, - OPT_USER_ID, - OPT_GROUP_ID, - OPT_DEFAULT_PERMISSIONS, - OPT_ALLOW_OTHER, - OPT_MAX_READ, - OPT_BLKSIZE, - OPT_ERR -}; - -static match_table_t tokens = { - {OPT_FD, "fd=%u"}, - {OPT_ROOTMODE, "rootmode=%o"}, - {OPT_USER_ID, "user_id=%u"}, - {OPT_GROUP_ID, "group_id=%u"}, - {OPT_DEFAULT_PERMISSIONS, "default_permissions"}, - {OPT_ALLOW_OTHER, "allow_other"}, - {OPT_MAX_READ, "max_read=%u"}, - {OPT_BLKSIZE, "blksize=%u"}, - {OPT_ERR, NULL} -}; - -static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev) -{ - char *p; - memset(d, 0, sizeof(struct fuse_mount_data)); - d->max_read = ~0; - d->blksize = 512; - - /* - * For unprivileged mounts use current uid/gid. Still allow - * "user_id" and "group_id" options for compatibility, but - * only if they match these values. - */ - if (!capable(CAP_SYS_ADMIN)) { - d->user_id = current->uid; - d->user_id_present = 1; - d->group_id = current->gid; - d->group_id_present = 1; - - } - - while ((p = strsep(&opt, ",")) != NULL) { - int token; - int value; - substring_t args[MAX_OPT_ARGS]; - if (!*p) - continue; - - token = match_token(p, tokens, args); - switch (token) { - case OPT_FD: - if (match_int(&args[0], &value)) - return 0; - d->fd = value; - d->fd_present = 1; - break; - - case OPT_ROOTMODE: - if (match_octal(&args[0], &value)) - return 0; - if (!fuse_valid_type(value)) - return 0; - d->rootmode = value; - d->rootmode_present = 1; - break; - - case OPT_USER_ID: - if (match_int(&args[0], &value)) - return 0; - if (d->user_id_present && d->user_id != value) - return 0; - d->user_id = value; - d->user_id_present = 1; - break; - - case OPT_GROUP_ID: - if (match_int(&args[0], &value)) - return 0; - if (d->group_id_present && d->group_id != value) - return 0; - d->group_id = value; - d->group_id_present = 1; - break; - - case OPT_DEFAULT_PERMISSIONS: - d->flags |= FUSE_DEFAULT_PERMISSIONS; - break; - - case OPT_ALLOW_OTHER: - d->flags |= FUSE_ALLOW_OTHER; - break; - - case OPT_MAX_READ: - if (match_int(&args[0], &value)) - return 0; - d->max_read = value; - break; - - case OPT_BLKSIZE: - if (!is_bdev || match_int(&args[0], &value)) - return 0; - d->blksize = value; - break; - - default: - return 0; - } - } - - if (!d->fd_present || !d->rootmode_present || - !d->user_id_present || !d->group_id_present) - return 0; - - return 1; -} - -static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt) -{ - struct fuse_conn *fc = get_fuse_conn_super(mnt->mnt_sb); - - seq_printf(m, ",user_id=%u", fc->user_id); - seq_printf(m, ",group_id=%u", fc->group_id); - if (fc->flags & FUSE_DEFAULT_PERMISSIONS) - seq_puts(m, ",default_permissions"); - if (fc->flags & FUSE_ALLOW_OTHER) - seq_puts(m, ",allow_other"); - if (fc->max_read != ~0) - seq_printf(m, ",max_read=%u", fc->max_read); - return 0; -} - -#ifndef HAVE_KZALLOC -static void *kzalloc(size_t size, int flags) -{ - void *ret = kmalloc(size, flags); - if (ret) - memset(ret, 0, size); - return ret; -} -#endif -static struct fuse_conn *new_conn(void) -{ - struct fuse_conn *fc; - - fc = kzalloc(sizeof(*fc), GFP_KERNEL); - if (fc) { - spin_lock_init(&fc->lock); - mutex_init(&fc->inst_mutex); - atomic_set(&fc->count, 1); - init_waitqueue_head(&fc->waitq); - init_waitqueue_head(&fc->blocked_waitq); - init_waitqueue_head(&fc->reserved_req_waitq); - INIT_LIST_HEAD(&fc->pending); - INIT_LIST_HEAD(&fc->processing); - INIT_LIST_HEAD(&fc->io); - INIT_LIST_HEAD(&fc->interrupts); - atomic_set(&fc->num_waiting, 0); - fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; - fc->bdi.unplug_io_fn = default_unplug_io_fn; - fc->reqctr = 0; - fc->blocked = 1; - get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key)); - } - return fc; -} - -void fuse_conn_put(struct fuse_conn *fc) -{ - if (atomic_dec_and_test(&fc->count)) { - if (fc->destroy_req) - fuse_request_free(fc->destroy_req); - mutex_destroy(&fc->inst_mutex); - kfree(fc); - } -} - -struct fuse_conn *fuse_conn_get(struct fuse_conn *fc) -{ - atomic_inc(&fc->count); - return fc; -} - -static struct inode *get_root_inode(struct super_block *sb, unsigned mode) -{ - struct fuse_attr attr; - memset(&attr, 0, sizeof(attr)); - - attr.mode = mode; - attr.ino = FUSE_ROOT_ID; - attr.nlink = 1; - return fuse_iget(sb, 1, 0, &attr); -} -#ifndef FUSE_MAINLINE -#ifdef HAVE_EXPORTFS_H -#include -#endif - -struct fuse_inode_handle -{ - u64 nodeid; - u32 generation; -}; - -static struct dentry *fuse_get_dentry(struct super_block *sb, - struct fuse_inode_handle *handle) -{ - struct inode *inode; - struct dentry *entry; - - if (handle->nodeid == 0) - return ERR_PTR(-ESTALE); - - inode = ilookup5(sb, handle->nodeid, fuse_inode_eq, &handle->nodeid); - if (!inode) - return ERR_PTR(-ESTALE); - if (inode->i_generation != handle->generation) { - iput(inode); - return ERR_PTR(-ESTALE); - } - - entry = d_alloc_anon(inode); - if (!entry) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - entry->d_op = &fuse_dentry_operations; - - return entry; -} - -static int fuse_encode_fh(struct dentry *dentry, u32 *fh, int *max_len, - int connectable) -{ - struct inode *inode = dentry->d_inode; - int len = *max_len; - int type = 1; - u64 nodeid; - u32 generation; - - if (len < 3 || (connectable && len < 6)) - return 255; - - nodeid = get_fuse_inode(inode)->nodeid; - generation = inode->i_generation; - - len = 3; - fh[0] = (u32)(nodeid >> 32); - fh[1] = (u32)(nodeid & 0xffffffff); - fh[2] = generation; - - if (connectable && !S_ISDIR(inode->i_mode)) { - struct inode *parent; - - spin_lock(&dentry->d_lock); - parent = dentry->d_parent->d_inode; - nodeid = get_fuse_inode(parent)->nodeid; - generation = parent->i_generation; - - fh[3] = (u32)(nodeid >> 32); - fh[4] = (u32)(nodeid & 0xffffffff); - fh[5] = generation; - spin_unlock(&dentry->d_lock); - - len = 6; - type = 2; - } - - *max_len = len; - return type; -} - -#ifdef KERNEL_2_6_24_PLUS -static struct dentry *fuse_fh_to_dentry(struct super_block *sb, - struct fid *fid, int fh_len, int fh_type) -{ - struct fuse_inode_handle handle; - - if (fh_len < 3 || fh_type > 2) - return NULL; - - handle.nodeid = (u64) fid->raw[0] << 32; - handle.nodeid |= (u64) fid->raw[1]; - handle.generation = fid->raw[2]; - return fuse_get_dentry(sb, &handle); -} - -static struct dentry *fuse_fh_to_parent(struct super_block *sb, - struct fid *fid, int fh_len, int fh_type) -{ - struct fuse_inode_handle parent; - - if (fh_type != 2 || fh_len < 6) - return NULL; - - parent.nodeid = (u64) fid->raw[3] << 32; - parent.nodeid |= (u64) fid->raw[4]; - parent.generation = fid->raw[5]; - return fuse_get_dentry(sb, &parent); -} - - -static const struct export_operations fuse_export_operations = { - .fh_to_dentry = fuse_fh_to_dentry, - .fh_to_parent = fuse_fh_to_parent, - .encode_fh = fuse_encode_fh, -}; -#else -static struct dentry *fuse_get_dentry_old(struct super_block *sb, void *objp) -{ - return fuse_get_dentry(sb, objp); -} - -static struct export_operations fuse_export_operations; - -static struct dentry *fuse_decode_fh(struct super_block *sb, u32 *fh, - int fh_len, int fileid_type, - int (*acceptable)(void *context, struct dentry *de), - void *context) -{ - struct fuse_inode_handle handle; - struct fuse_inode_handle parent; - - if (fh_len < 3 || fileid_type > 2) - return NULL; - - if (fileid_type == 2) { - if (fh_len < 6) - return NULL; - - parent.nodeid = (u64) fh[3] << 32; - parent.nodeid |= (u64) fh[4]; - parent.generation = fh[5]; - } else { - parent.nodeid = 0; - parent.generation = 0; - } - - handle.nodeid = (u64) fh[0] << 32; - handle.nodeid |= (u64) fh[1]; - handle.generation = fh[2]; - - return fuse_export_operations. - find_exported_dentry(sb, &handle, &parent, acceptable, context); -} - -static struct export_operations fuse_export_operations = { - .get_dentry = fuse_get_dentry_old, - .encode_fh = fuse_encode_fh, - .decode_fh = fuse_decode_fh, -}; -#endif -#endif - -static struct super_operations fuse_super_operations = { - .alloc_inode = fuse_alloc_inode, - .destroy_inode = fuse_destroy_inode, - .read_inode = fuse_read_inode, - .clear_inode = fuse_clear_inode, - .drop_inode = generic_delete_inode, - .remount_fs = fuse_remount_fs, - .put_super = fuse_put_super, - .umount_begin = fuse_umount_begin, - .statfs = fuse_statfs, - .show_options = fuse_show_options, -}; - -static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) -{ - struct fuse_init_out *arg = &req->misc.init_out; - - if (req->out.h.error || arg->major != FUSE_KERNEL_VERSION) - fc->conn_error = 1; - else { - unsigned long ra_pages; - - if (arg->minor >= 6) { - ra_pages = arg->max_readahead / PAGE_CACHE_SIZE; - if (arg->flags & FUSE_ASYNC_READ) - fc->async_read = 1; - if (!(arg->flags & FUSE_POSIX_LOCKS)) - fc->no_lock = 1; - } else { - ra_pages = fc->max_read / PAGE_CACHE_SIZE; - fc->no_lock = 1; - } - - fc->bdi.ra_pages = min(fc->bdi.ra_pages, ra_pages); - fc->minor = arg->minor; - fc->max_write = arg->minor < 5 ? 4096 : arg->max_write; - fc->conn_init = 1; - } - fuse_put_request(fc, req); - fc->blocked = 0; - wake_up_all(&fc->blocked_waitq); -} - -static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) -{ - struct fuse_init_in *arg = &req->misc.init_in; - - arg->major = FUSE_KERNEL_VERSION; - arg->minor = FUSE_KERNEL_MINOR_VERSION; - arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE; - arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS; - req->in.h.opcode = FUSE_INIT; - req->in.numargs = 1; - req->in.args[0].size = sizeof(*arg); - req->in.args[0].value = arg; - req->out.numargs = 1; - /* Variable length arguement used for backward compatibility - with interface version < 7.5. Rest of init_out is zeroed - by do_get_request(), so a short reply is not a problem */ - req->out.argvar = 1; - req->out.args[0].size = sizeof(struct fuse_init_out); - req->out.args[0].value = &req->misc.init_out; - req->end = process_init_reply; - request_send_background(fc, req); -} - -static u64 conn_id(void) -{ - static u64 ctr = 1; - return ctr++; -} - -static int fuse_fill_super(struct super_block *sb, void *data, int silent) -{ - struct fuse_conn *fc; - struct inode *root; - struct fuse_mount_data d; - struct file *file; - struct dentry *root_dentry; - struct fuse_req *init_req; - int err; - int is_bdev = sb->s_bdev != NULL; - - if (sb->s_flags & MS_MANDLOCK) - return -EINVAL; - - if (!parse_fuse_opt((char *) data, &d, is_bdev)) - return -EINVAL; - - /* This is a privileged option */ - if ((d.flags & FUSE_ALLOW_OTHER) && !capable(CAP_SYS_ADMIN)) - return -EPERM; - - if (is_bdev) { -#ifdef CONFIG_BLOCK - if (!sb_set_blocksize(sb, d.blksize)) - return -EINVAL; -#endif - } else { - sb->s_blocksize = PAGE_CACHE_SIZE; - sb->s_blocksize_bits = PAGE_CACHE_SHIFT; - } - sb->s_magic = FUSE_SUPER_MAGIC; - sb->s_op = &fuse_super_operations; - sb->s_maxbytes = MAX_LFS_FILESIZE; -#ifndef FUSE_MAINLINE - sb->s_export_op = &fuse_export_operations; -#endif - - file = fget(d.fd); - if (!file) - return -EINVAL; - - if (file->f_op != &fuse_dev_operations) - return -EINVAL; - - fc = new_conn(); - if (!fc) - return -ENOMEM; - - fc->flags = d.flags; - fc->user_id = d.user_id; - fc->group_id = d.group_id; - fc->max_read = d.max_read; - - /* Used by get_root_inode() */ - sb->s_fs_info = fc; - - err = -ENOMEM; - root = get_root_inode(sb, d.rootmode); - if (!root) - goto err; - - root_dentry = d_alloc_root(root); - if (!root_dentry) { - iput(root); - goto err; - } - - init_req = fuse_request_alloc(); - if (!init_req) - goto err_put_root; - - if (is_bdev) { - fc->destroy_req = fuse_request_alloc(); - if (!fc->destroy_req) - goto err_put_root; - } - - mutex_lock(&fuse_mutex); - err = -EINVAL; - if (file->private_data) - goto err_unlock; - - fc->id = conn_id(); - err = fuse_ctl_add_conn(fc); - if (err) - goto err_unlock; - - list_add_tail(&fc->entry, &fuse_conn_list); - sb->s_root = root_dentry; - fc->connected = 1; - file->private_data = fuse_conn_get(fc); - mutex_unlock(&fuse_mutex); - /* - * atomic_dec_and_test() in fput() provides the necessary - * memory barrier for file->private_data to be visible on all - * CPUs after this - */ - fput(file); - - fuse_send_init(fc, init_req); - - return 0; - - err_unlock: - mutex_unlock(&fuse_mutex); - fuse_request_free(init_req); - err_put_root: - dput(root_dentry); - err: - fput(file); - fuse_conn_put(fc); - return err; -} - -#ifdef KERNEL_2_6_18_PLUS -static int fuse_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *raw_data, struct vfsmount *mnt) -{ - return get_sb_nodev(fs_type, flags, raw_data, fuse_fill_super, mnt); -} -#else -static struct super_block *fuse_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *raw_data) -{ - return get_sb_nodev(fs_type, flags, raw_data, fuse_fill_super); -} -#endif - -static struct file_system_type fuse_fs_type = { - .owner = THIS_MODULE, - .name = "fuse", - .get_sb = fuse_get_sb, - .kill_sb = kill_anon_super, - .fs_flags = FS_HAS_SUBTYPE | FS_SAFE, -}; - -#ifdef CONFIG_BLOCK -#ifdef KERNEL_2_6_18_PLUS -static int fuse_get_sb_blk(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *raw_data, struct vfsmount *mnt) -{ - return get_sb_bdev(fs_type, flags, dev_name, raw_data, fuse_fill_super, - mnt); -} -#else -static struct super_block *fuse_get_sb_blk(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *raw_data) -{ - return get_sb_bdev(fs_type, flags, dev_name, raw_data, - fuse_fill_super); -} -#endif - -static struct file_system_type fuseblk_fs_type = { - .owner = THIS_MODULE, - .name = "fuseblk", - .get_sb = fuse_get_sb_blk, - .kill_sb = kill_block_super, - .fs_flags = FS_REQUIRES_DEV | FS_HAS_SUBTYPE, -}; - -static inline int register_fuseblk(void) -{ - return register_filesystem(&fuseblk_fs_type); -} - -static inline void unregister_fuseblk(void) -{ - unregister_filesystem(&fuseblk_fs_type); -} -#else -static inline int register_fuseblk(void) -{ - return 0; -} - -static inline void unregister_fuseblk(void) -{ -} -#endif - -#ifndef HAVE_FS_SUBSYS -static decl_subsys(fs, NULL, NULL); -#endif -static decl_subsys(fuse, NULL, NULL); -static decl_subsys(connections, NULL, NULL); - -#ifdef KERNEL_2_6_24_PLUS -static void fuse_inode_init_once(struct kmem_cache *cachep, void *foo) -#else -static void fuse_inode_init_once(void *foo, struct kmem_cache *cachep, - unsigned long flags) -#endif -{ - struct inode * inode = foo; - -#ifndef KERNEL_2_6_22_PLUS - if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == - SLAB_CTOR_CONSTRUCTOR) -#endif - inode_init_once(inode); -} - -static int __init fuse_fs_init(void) -{ - int err; - - err = register_filesystem(&fuse_fs_type); - if (err) - goto out; - - err = register_fuseblk(); - if (err) - goto out_unreg; - -#ifdef KERNEL_2_6_23_PLUS - fuse_inode_cachep = kmem_cache_create("fuse_inode", - sizeof(struct fuse_inode), - 0, SLAB_HWCACHE_ALIGN, - fuse_inode_init_once); -#else - fuse_inode_cachep = kmem_cache_create("fuse_inode", - sizeof(struct fuse_inode), - 0, SLAB_HWCACHE_ALIGN, - fuse_inode_init_once, NULL); -#endif - - err = -ENOMEM; - if (!fuse_inode_cachep) - goto out_unreg2; - - return 0; - - out_unreg2: - unregister_fuseblk(); - out_unreg: - unregister_filesystem(&fuse_fs_type); - out: - return err; -} - -static void fuse_fs_cleanup(void) -{ - unregister_filesystem(&fuse_fs_type); - unregister_fuseblk(); - kmem_cache_destroy(fuse_inode_cachep); -} - -static int fuse_sysfs_init(void) -{ - int err; - -#ifndef HAVE_FS_SUBSYS - err = subsystem_register(&fs_subsys); - if (err) - return err; -#endif -#ifdef KERNEL_2_6_22_PLUS - kobj_set_kset_s(&fuse_subsys, fs_subsys); -#else - kset_set_kset_s(&fuse_subsys, fs_subsys); -#endif - err = subsystem_register(&fuse_subsys); - if (err) - goto out_err; - -#ifdef KERNEL_2_6_22_PLUS - kobj_set_kset_s(&connections_subsys, fuse_subsys); -#else - kset_set_kset_s(&connections_subsys, fuse_subsys); -#endif - err = subsystem_register(&connections_subsys); - if (err) - goto out_fuse_unregister; - - return 0; - - out_fuse_unregister: - subsystem_unregister(&fuse_subsys); - out_err: -#ifndef HAVE_FS_SUBSYS - subsystem_unregister(&fs_subsys); -#endif - return err; -} - -static void fuse_sysfs_cleanup(void) -{ - subsystem_unregister(&connections_subsys); - subsystem_unregister(&fuse_subsys); -#ifndef HAVE_FS_SUBSYS - subsystem_unregister(&fs_subsys); -#endif -} - -static int __init fuse_init(void) -{ - int res; - - printk("fuse init (API version %i.%i)\n", - FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); -#ifndef FUSE_MAINLINE - printk("fuse distribution version: %s\n", FUSE_VERSION); -#endif - - INIT_LIST_HEAD(&fuse_conn_list); - res = fuse_fs_init(); - if (res) - goto err; - - res = fuse_dev_init(); - if (res) - goto err_fs_cleanup; - - res = fuse_sysfs_init(); - if (res) - goto err_dev_cleanup; - - res = fuse_ctl_init(); - if (res) - goto err_sysfs_cleanup; - - return 0; - - err_sysfs_cleanup: - fuse_sysfs_cleanup(); - err_dev_cleanup: - fuse_dev_cleanup(); - err_fs_cleanup: - fuse_fs_cleanup(); - err: - return res; -} - -static void __exit fuse_exit(void) -{ - printk(KERN_DEBUG "fuse exit\n"); - - fuse_ctl_cleanup(); - fuse_sysfs_cleanup(); - fuse_fs_cleanup(); - fuse_dev_cleanup(); -} - -module_init(fuse_init); -module_exit(fuse_exit); diff --git a/makeconf.sh b/makeconf.sh index 179cd68..6678bdd 100755 --- a/makeconf.sh +++ b/makeconf.sh @@ -40,8 +40,6 @@ else autoconf ) fi -echo Linking kernel header file... -ln -sf ../kernel/fuse_kernel.h `dirname $0`/include rm -f config.cache config.status echo "To compile run './configure', and then 'make'."