diff options
author | Srikant Patnaik | 2015-01-11 12:28:04 +0530 |
---|---|---|
committer | Srikant Patnaik | 2015-01-11 12:28:04 +0530 |
commit | 871480933a1c28f8a9fed4c4d34d06c439a7a422 (patch) | |
tree | 8718f573808810c2a1e8cb8fb6ac469093ca2784 /ANDROID_3.4.5/fs/fuse | |
parent | 9d40ac5867b9aefe0722bc1f110b965ff294d30d (diff) | |
download | FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.gz FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.bz2 FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.zip |
Moved, renamed, and deleted files
The original directory structure was scattered and unorganized.
Changes are basically to make it look like kernel structure.
Diffstat (limited to 'ANDROID_3.4.5/fs/fuse')
-rw-r--r-- | ANDROID_3.4.5/fs/fuse/Kconfig | 15 | ||||
-rw-r--r-- | ANDROID_3.4.5/fs/fuse/Makefile | 8 | ||||
-rw-r--r-- | ANDROID_3.4.5/fs/fuse/control.c | 359 | ||||
-rw-r--r-- | ANDROID_3.4.5/fs/fuse/cuse.c | 621 | ||||
-rw-r--r-- | ANDROID_3.4.5/fs/fuse/dev.c | 2104 | ||||
-rw-r--r-- | ANDROID_3.4.5/fs/fuse/dir.c | 1697 | ||||
-rw-r--r-- | ANDROID_3.4.5/fs/fuse/file.c | 2224 | ||||
-rw-r--r-- | ANDROID_3.4.5/fs/fuse/fuse_i.h | 784 | ||||
-rw-r--r-- | ANDROID_3.4.5/fs/fuse/inode.c | 1267 |
9 files changed, 0 insertions, 9079 deletions
diff --git a/ANDROID_3.4.5/fs/fuse/Kconfig b/ANDROID_3.4.5/fs/fuse/Kconfig deleted file mode 100644 index 0cf160a9..00000000 --- a/ANDROID_3.4.5/fs/fuse/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -config FUSE_FS - tristate "FUSE (Filesystem in Userspace) support" - help - With FUSE it is possible to implement a fully functional filesystem - in a userspace program. - - There's also companion library: libfuse. This library along with - utilities is available from the FUSE homepage: - <http://fuse.sourceforge.net/> - - See <file:Documentation/filesystems/fuse.txt> for more information. - See <file:Documentation/Changes> for needed library/utility version. - - If you want to develop a userspace FS, or if you want to use - a filesystem based on FUSE, answer Y or M. diff --git a/ANDROID_3.4.5/fs/fuse/Makefile b/ANDROID_3.4.5/fs/fuse/Makefile deleted file mode 100644 index e95eeb44..00000000 --- a/ANDROID_3.4.5/fs/fuse/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# Makefile for the FUSE filesystem. -# - -obj-$(CONFIG_FUSE_FS) += fuse.o -obj-$(CONFIG_CUSE) += cuse.o - -fuse-objs := dev.o dir.o file.o inode.o control.o diff --git a/ANDROID_3.4.5/fs/fuse/control.c b/ANDROID_3.4.5/fs/fuse/control.c deleted file mode 100644 index 42593c58..00000000 --- a/ANDROID_3.4.5/fs/fuse/control.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu> - - This program can be distributed under the terms of the GNU GPL. - See the file COPYING. -*/ - -#include "fuse_i.h" - -#include <linux/init.h> -#include <linux/module.h> - -#define FUSE_CTL_SUPER_MAGIC 0x65735543 - -/* - * 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_path.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) { - long value; - struct fuse_conn *fc = fuse_ctl_file_conn_get(file); - if (!fc) - return 0; - - value = atomic_read(&fc->num_waiting); - file->private_data = (void *)value; - fuse_conn_put(fc); - } - size = sprintf(tmp, "%ld\n", (long)file->private_data); - return simple_read_from_buffer(buf, len, ppos, tmp, size); -} - -static ssize_t fuse_conn_limit_read(struct file *file, char __user *buf, - size_t len, loff_t *ppos, unsigned val) -{ - char tmp[32]; - size_t size = sprintf(tmp, "%u\n", val); - - return simple_read_from_buffer(buf, len, ppos, tmp, size); -} - -static ssize_t fuse_conn_limit_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos, unsigned *val, - unsigned global_limit) -{ - unsigned long t; - char tmp[32]; - unsigned limit = (1 << 16) - 1; - int err; - - if (*ppos || count >= sizeof(tmp) - 1) - return -EINVAL; - - if (copy_from_user(tmp, buf, count)) - return -EINVAL; - - tmp[count] = '\0'; - - err = strict_strtoul(tmp, 0, &t); - if (err) - return err; - - if (!capable(CAP_SYS_ADMIN)) - limit = min(limit, global_limit); - - if (t > limit) - return -EINVAL; - - *val = t; - - return count; -} - -static ssize_t fuse_conn_max_background_read(struct file *file, - char __user *buf, size_t len, - loff_t *ppos) -{ - struct fuse_conn *fc; - unsigned val; - - fc = fuse_ctl_file_conn_get(file); - if (!fc) - return 0; - - val = fc->max_background; - fuse_conn_put(fc); - - return fuse_conn_limit_read(file, buf, len, ppos, val); -} - -static ssize_t fuse_conn_max_background_write(struct file *file, - const char __user *buf, - size_t count, loff_t *ppos) -{ - unsigned val; - ssize_t ret; - - ret = fuse_conn_limit_write(file, buf, count, ppos, &val, - max_user_bgreq); - if (ret > 0) { - struct fuse_conn *fc = fuse_ctl_file_conn_get(file); - if (fc) { - fc->max_background = val; - fuse_conn_put(fc); - } - } - - return ret; -} - -static ssize_t fuse_conn_congestion_threshold_read(struct file *file, - char __user *buf, size_t len, - loff_t *ppos) -{ - struct fuse_conn *fc; - unsigned val; - - fc = fuse_ctl_file_conn_get(file); - if (!fc) - return 0; - - val = fc->congestion_threshold; - fuse_conn_put(fc); - - return fuse_conn_limit_read(file, buf, len, ppos, val); -} - -static ssize_t fuse_conn_congestion_threshold_write(struct file *file, - const char __user *buf, - size_t count, loff_t *ppos) -{ - unsigned val; - ssize_t ret; - - ret = fuse_conn_limit_write(file, buf, count, ppos, &val, - max_user_congthresh); - if (ret > 0) { - struct fuse_conn *fc = fuse_ctl_file_conn_get(file); - if (fc) { - fc->congestion_threshold = val; - fuse_conn_put(fc); - } - } - - return ret; -} - -static const struct file_operations fuse_ctl_abort_ops = { - .open = nonseekable_open, - .write = fuse_conn_abort_write, - .llseek = no_llseek, -}; - -static const struct file_operations fuse_ctl_waiting_ops = { - .open = nonseekable_open, - .read = fuse_conn_waiting_read, - .llseek = no_llseek, -}; - -static const struct file_operations fuse_conn_max_background_ops = { - .open = nonseekable_open, - .read = fuse_conn_max_background_read, - .write = fuse_conn_max_background_write, - .llseek = no_llseek, -}; - -static const struct file_operations fuse_conn_congestion_threshold_ops = { - .open = nonseekable_open, - .read = fuse_conn_congestion_threshold_read, - .write = fuse_conn_congestion_threshold_write, - .llseek = no_llseek, -}; - -static struct dentry *fuse_ctl_add_dentry(struct dentry *parent, - struct fuse_conn *fc, - const char *name, - int mode, int nlink, - const struct inode_operations *iop, - const struct file_operations *fop) -{ - 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_ino = get_next_ino(); - 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; - set_nlink(inode, 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, "%u", fc->dev); - 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) || - !fuse_ctl_add_dentry(parent, fc, "max_background", S_IFREG | 0600, - 1, NULL, &fuse_conn_max_background_ops) || - !fuse_ctl_add_dentry(parent, fc, "congestion_threshold", - S_IFREG | 0600, 1, NULL, - &fuse_conn_congestion_threshold_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); - } - drop_nlink(fuse_control_sb->s_root->d_inode); -} - -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; -} - -static struct dentry *fuse_ctl_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *raw_data) -{ - return mount_single(fs_type, flags, raw_data, fuse_ctl_fill_super); -} - -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", - .mount = fuse_ctl_mount, - .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/ANDROID_3.4.5/fs/fuse/cuse.c b/ANDROID_3.4.5/fs/fuse/cuse.c deleted file mode 100644 index 3426521f..00000000 --- a/ANDROID_3.4.5/fs/fuse/cuse.c +++ /dev/null @@ -1,621 +0,0 @@ -/* - * CUSE: Character device in Userspace - * - * Copyright (C) 2008-2009 SUSE Linux Products GmbH - * Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org> - * - * This file is released under the GPLv2. - * - * CUSE enables character devices to be implemented from userland much - * like FUSE allows filesystems. On initialization /dev/cuse is - * created. By opening the file and replying to the CUSE_INIT request - * userland CUSE server can create a character device. After that the - * operation is very similar to FUSE. - * - * A CUSE instance involves the following objects. - * - * cuse_conn : contains fuse_conn and serves as bonding structure - * channel : file handle connected to the userland CUSE server - * cdev : the implemented character device - * dev : generic device for cdev - * - * Note that 'channel' is what 'dev' is in FUSE. As CUSE deals with - * devices, it's called 'channel' to reduce confusion. - * - * channel determines when the character device dies. When channel is - * closed, everything begins to destruct. The cuse_conn is taken off - * the lookup table preventing further access from cdev, cdev and - * generic device are removed and the base reference of cuse_conn is - * put. - * - * On each open, the matching cuse_conn is looked up and if found an - * additional reference is taken which is released when the file is - * closed. - */ - -#include <linux/fuse.h> -#include <linux/cdev.h> -#include <linux/device.h> -#include <linux/file.h> -#include <linux/fs.h> -#include <linux/kdev_t.h> -#include <linux/kthread.h> -#include <linux/list.h> -#include <linux/magic.h> -#include <linux/miscdevice.h> -#include <linux/mutex.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/stat.h> -#include <linux/module.h> - -#include "fuse_i.h" - -#define CUSE_CONNTBL_LEN 64 - -struct cuse_conn { - struct list_head list; /* linked on cuse_conntbl */ - struct fuse_conn fc; /* fuse connection */ - struct cdev *cdev; /* associated character device */ - struct device *dev; /* device representing @cdev */ - - /* init parameters, set once during initialization */ - bool unrestricted_ioctl; -}; - -static DEFINE_SPINLOCK(cuse_lock); /* protects cuse_conntbl */ -static struct list_head cuse_conntbl[CUSE_CONNTBL_LEN]; -static struct class *cuse_class; - -static struct cuse_conn *fc_to_cc(struct fuse_conn *fc) -{ - return container_of(fc, struct cuse_conn, fc); -} - -static struct list_head *cuse_conntbl_head(dev_t devt) -{ - return &cuse_conntbl[(MAJOR(devt) + MINOR(devt)) % CUSE_CONNTBL_LEN]; -} - - -/************************************************************************** - * CUSE frontend operations - * - * These are file operations for the character device. - * - * On open, CUSE opens a file from the FUSE mnt and stores it to - * private_data of the open file. All other ops call FUSE ops on the - * FUSE file. - */ - -static ssize_t cuse_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - loff_t pos = 0; - - return fuse_direct_io(file, buf, count, &pos, 0); -} - -static ssize_t cuse_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - loff_t pos = 0; - /* - * No locking or generic_write_checks(), the server is - * responsible for locking and sanity checks. - */ - return fuse_direct_io(file, buf, count, &pos, 1); -} - -static int cuse_open(struct inode *inode, struct file *file) -{ - dev_t devt = inode->i_cdev->dev; - struct cuse_conn *cc = NULL, *pos; - int rc; - - /* look up and get the connection */ - spin_lock(&cuse_lock); - list_for_each_entry(pos, cuse_conntbl_head(devt), list) - if (pos->dev->devt == devt) { - fuse_conn_get(&pos->fc); - cc = pos; - break; - } - spin_unlock(&cuse_lock); - - /* dead? */ - if (!cc) - return -ENODEV; - - /* - * Generic permission check is already done against the chrdev - * file, proceed to open. - */ - rc = fuse_do_open(&cc->fc, 0, file, 0); - if (rc) - fuse_conn_put(&cc->fc); - return rc; -} - -static int cuse_release(struct inode *inode, struct file *file) -{ - struct fuse_file *ff = file->private_data; - struct fuse_conn *fc = ff->fc; - - fuse_sync_release(ff, file->f_flags); - fuse_conn_put(fc); - - return 0; -} - -static long cuse_file_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct fuse_file *ff = file->private_data; - struct cuse_conn *cc = fc_to_cc(ff->fc); - unsigned int flags = 0; - - if (cc->unrestricted_ioctl) - flags |= FUSE_IOCTL_UNRESTRICTED; - - return fuse_do_ioctl(file, cmd, arg, flags); -} - -static long cuse_file_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct fuse_file *ff = file->private_data; - struct cuse_conn *cc = fc_to_cc(ff->fc); - unsigned int flags = FUSE_IOCTL_COMPAT; - - if (cc->unrestricted_ioctl) - flags |= FUSE_IOCTL_UNRESTRICTED; - - return fuse_do_ioctl(file, cmd, arg, flags); -} - -static const struct file_operations cuse_frontend_fops = { - .owner = THIS_MODULE, - .read = cuse_read, - .write = cuse_write, - .open = cuse_open, - .release = cuse_release, - .unlocked_ioctl = cuse_file_ioctl, - .compat_ioctl = cuse_file_compat_ioctl, - .poll = fuse_file_poll, - .llseek = noop_llseek, -}; - - -/************************************************************************** - * CUSE channel initialization and destruction - */ - -struct cuse_devinfo { - const char *name; -}; - -/** - * cuse_parse_one - parse one key=value pair - * @pp: i/o parameter for the current position - * @end: points to one past the end of the packed string - * @keyp: out parameter for key - * @valp: out parameter for value - * - * *@pp points to packed strings - "key0=val0\0key1=val1\0" which ends - * at @end - 1. This function parses one pair and set *@keyp to the - * start of the key and *@valp to the start of the value. Note that - * the original string is modified such that the key string is - * terminated with '\0'. *@pp is updated to point to the next string. - * - * RETURNS: - * 1 on successful parse, 0 on EOF, -errno on failure. - */ -static int cuse_parse_one(char **pp, char *end, char **keyp, char **valp) -{ - char *p = *pp; - char *key, *val; - - while (p < end && *p == '\0') - p++; - if (p == end) - return 0; - - if (end[-1] != '\0') { - printk(KERN_ERR "CUSE: info not properly terminated\n"); - return -EINVAL; - } - - key = val = p; - p += strlen(p); - - if (valp) { - strsep(&val, "="); - if (!val) - val = key + strlen(key); - key = strstrip(key); - val = strstrip(val); - } else - key = strstrip(key); - - if (!strlen(key)) { - printk(KERN_ERR "CUSE: zero length info key specified\n"); - return -EINVAL; - } - - *pp = p; - *keyp = key; - if (valp) - *valp = val; - - return 1; -} - -/** - * cuse_parse_dev_info - parse device info - * @p: device info string - * @len: length of device info string - * @devinfo: out parameter for parsed device info - * - * Parse @p to extract device info and store it into @devinfo. String - * pointed to by @p is modified by parsing and @devinfo points into - * them, so @p shouldn't be freed while @devinfo is in use. - * - * RETURNS: - * 0 on success, -errno on failure. - */ -static int cuse_parse_devinfo(char *p, size_t len, struct cuse_devinfo *devinfo) -{ - char *end = p + len; - char *key, *val; - int rc; - - while (true) { - rc = cuse_parse_one(&p, end, &key, &val); - if (rc < 0) - return rc; - if (!rc) - break; - if (strcmp(key, "DEVNAME") == 0) - devinfo->name = val; - else - printk(KERN_WARNING "CUSE: unknown device info \"%s\"\n", - key); - } - - if (!devinfo->name || !strlen(devinfo->name)) { - printk(KERN_ERR "CUSE: DEVNAME unspecified\n"); - return -EINVAL; - } - - return 0; -} - -static void cuse_gendev_release(struct device *dev) -{ - kfree(dev); -} - -/** - * cuse_process_init_reply - finish initializing CUSE channel - * - * This function creates the character device and sets up all the - * required data structures for it. Please read the comment at the - * top of this file for high level overview. - */ -static void cuse_process_init_reply(struct fuse_conn *fc, struct fuse_req *req) -{ - struct cuse_conn *cc = fc_to_cc(fc); - struct cuse_init_out *arg = req->out.args[0].value; - struct page *page = req->pages[0]; - struct cuse_devinfo devinfo = { }; - struct device *dev; - struct cdev *cdev; - dev_t devt; - int rc; - - if (req->out.h.error || - arg->major != FUSE_KERNEL_VERSION || arg->minor < 11) { - goto err; - } - - fc->minor = arg->minor; - fc->max_read = max_t(unsigned, arg->max_read, 4096); - fc->max_write = max_t(unsigned, arg->max_write, 4096); - - /* parse init reply */ - cc->unrestricted_ioctl = arg->flags & CUSE_UNRESTRICTED_IOCTL; - - rc = cuse_parse_devinfo(page_address(page), req->out.args[1].size, - &devinfo); - if (rc) - goto err; - - /* determine and reserve devt */ - devt = MKDEV(arg->dev_major, arg->dev_minor); - if (!MAJOR(devt)) - rc = alloc_chrdev_region(&devt, MINOR(devt), 1, devinfo.name); - else - rc = register_chrdev_region(devt, 1, devinfo.name); - if (rc) { - printk(KERN_ERR "CUSE: failed to register chrdev region\n"); - goto err; - } - - /* devt determined, create device */ - rc = -ENOMEM; - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - goto err_region; - - device_initialize(dev); - dev_set_uevent_suppress(dev, 1); - dev->class = cuse_class; - dev->devt = devt; - dev->release = cuse_gendev_release; - dev_set_drvdata(dev, cc); - dev_set_name(dev, "%s", devinfo.name); - - rc = device_add(dev); - if (rc) - goto err_device; - - /* register cdev */ - rc = -ENOMEM; - cdev = cdev_alloc(); - if (!cdev) - goto err_device; - - cdev->owner = THIS_MODULE; - cdev->ops = &cuse_frontend_fops; - - rc = cdev_add(cdev, devt, 1); - if (rc) - goto err_cdev; - - cc->dev = dev; - cc->cdev = cdev; - - /* make the device available */ - spin_lock(&cuse_lock); - list_add(&cc->list, cuse_conntbl_head(devt)); - spin_unlock(&cuse_lock); - - /* announce device availability */ - dev_set_uevent_suppress(dev, 0); - kobject_uevent(&dev->kobj, KOBJ_ADD); -out: - kfree(arg); - __free_page(page); - return; - -err_cdev: - cdev_del(cdev); -err_device: - put_device(dev); -err_region: - unregister_chrdev_region(devt, 1); -err: - fc->conn_error = 1; - goto out; -} - -static int cuse_send_init(struct cuse_conn *cc) -{ - int rc; - struct fuse_req *req; - struct page *page; - struct fuse_conn *fc = &cc->fc; - struct cuse_init_in *arg; - void *outarg; - - BUILD_BUG_ON(CUSE_INIT_INFO_MAX > PAGE_SIZE); - - req = fuse_get_req(fc); - if (IS_ERR(req)) { - rc = PTR_ERR(req); - goto err; - } - - rc = -ENOMEM; - page = alloc_page(GFP_KERNEL | __GFP_ZERO); - if (!page) - goto err_put_req; - - outarg = kzalloc(sizeof(struct cuse_init_out), GFP_KERNEL); - if (!outarg) - goto err_free_page; - - arg = &req->misc.cuse_init_in; - arg->major = FUSE_KERNEL_VERSION; - arg->minor = FUSE_KERNEL_MINOR_VERSION; - arg->flags |= CUSE_UNRESTRICTED_IOCTL; - req->in.h.opcode = CUSE_INIT; - req->in.numargs = 1; - req->in.args[0].size = sizeof(struct cuse_init_in); - req->in.args[0].value = arg; - req->out.numargs = 2; - req->out.args[0].size = sizeof(struct cuse_init_out); - req->out.args[0].value = outarg; - req->out.args[1].size = CUSE_INIT_INFO_MAX; - req->out.argvar = 1; - req->out.argpages = 1; - req->pages[0] = page; - req->num_pages = 1; - req->end = cuse_process_init_reply; - fuse_request_send_background(fc, req); - - return 0; - -err_free_page: - __free_page(page); -err_put_req: - fuse_put_request(fc, req); -err: - return rc; -} - -static void cuse_fc_release(struct fuse_conn *fc) -{ - struct cuse_conn *cc = fc_to_cc(fc); - kfree(cc); -} - -/** - * cuse_channel_open - open method for /dev/cuse - * @inode: inode for /dev/cuse - * @file: file struct being opened - * - * Userland CUSE server can create a CUSE device by opening /dev/cuse - * and replying to the initialization request kernel sends. This - * function is responsible for handling CUSE device initialization. - * Because the fd opened by this function is used during - * initialization, this function only creates cuse_conn and sends - * init. The rest is delegated to a kthread. - * - * RETURNS: - * 0 on success, -errno on failure. - */ -static int cuse_channel_open(struct inode *inode, struct file *file) -{ - struct cuse_conn *cc; - int rc; - - /* set up cuse_conn */ - cc = kzalloc(sizeof(*cc), GFP_KERNEL); - if (!cc) - return -ENOMEM; - - fuse_conn_init(&cc->fc); - - INIT_LIST_HEAD(&cc->list); - cc->fc.release = cuse_fc_release; - - cc->fc.connected = 1; - cc->fc.blocked = 0; - rc = cuse_send_init(cc); - if (rc) { - fuse_conn_put(&cc->fc); - return rc; - } - file->private_data = &cc->fc; /* channel owns base reference to cc */ - - return 0; -} - -/** - * cuse_channel_release - release method for /dev/cuse - * @inode: inode for /dev/cuse - * @file: file struct being closed - * - * Disconnect the channel, deregister CUSE device and initiate - * destruction by putting the default reference. - * - * RETURNS: - * 0 on success, -errno on failure. - */ -static int cuse_channel_release(struct inode *inode, struct file *file) -{ - struct cuse_conn *cc = fc_to_cc(file->private_data); - int rc; - - /* remove from the conntbl, no more access from this point on */ - spin_lock(&cuse_lock); - list_del_init(&cc->list); - spin_unlock(&cuse_lock); - - /* remove device */ - if (cc->dev) - device_unregister(cc->dev); - if (cc->cdev) { - unregister_chrdev_region(cc->cdev->dev, 1); - cdev_del(cc->cdev); - } - - /* kill connection and shutdown channel */ - fuse_conn_kill(&cc->fc); - rc = fuse_dev_release(inode, file); /* puts the base reference */ - - return rc; -} - -static struct file_operations cuse_channel_fops; /* initialized during init */ - - -/************************************************************************** - * Misc stuff and module initializatiion - * - * CUSE exports the same set of attributes to sysfs as fusectl. - */ - -static ssize_t cuse_class_waiting_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct cuse_conn *cc = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", atomic_read(&cc->fc.num_waiting)); -} - -static ssize_t cuse_class_abort_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cuse_conn *cc = dev_get_drvdata(dev); - - fuse_abort_conn(&cc->fc); - return count; -} - -static struct device_attribute cuse_class_dev_attrs[] = { - __ATTR(waiting, S_IFREG | 0400, cuse_class_waiting_show, NULL), - __ATTR(abort, S_IFREG | 0200, NULL, cuse_class_abort_store), - { } -}; - -static struct miscdevice cuse_miscdev = { - .minor = MISC_DYNAMIC_MINOR, - .name = "cuse", - .fops = &cuse_channel_fops, -}; - -static int __init cuse_init(void) -{ - int i, rc; - - /* init conntbl */ - for (i = 0; i < CUSE_CONNTBL_LEN; i++) - INIT_LIST_HEAD(&cuse_conntbl[i]); - - /* inherit and extend fuse_dev_operations */ - cuse_channel_fops = fuse_dev_operations; - cuse_channel_fops.owner = THIS_MODULE; - cuse_channel_fops.open = cuse_channel_open; - cuse_channel_fops.release = cuse_channel_release; - - cuse_class = class_create(THIS_MODULE, "cuse"); - if (IS_ERR(cuse_class)) - return PTR_ERR(cuse_class); - - cuse_class->dev_attrs = cuse_class_dev_attrs; - - rc = misc_register(&cuse_miscdev); - if (rc) { - class_destroy(cuse_class); - return rc; - } - - return 0; -} - -static void __exit cuse_exit(void) -{ - misc_deregister(&cuse_miscdev); - class_destroy(cuse_class); -} - -module_init(cuse_init); -module_exit(cuse_exit); - -MODULE_AUTHOR("Tejun Heo <tj@kernel.org>"); -MODULE_DESCRIPTION("Character device in Userspace"); -MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/fs/fuse/dev.c b/ANDROID_3.4.5/fs/fuse/dev.c deleted file mode 100644 index ceb3b29c..00000000 --- a/ANDROID_3.4.5/fs/fuse/dev.c +++ /dev/null @@ -1,2104 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu> - - This program can be distributed under the terms of the GNU GPL. - See the file COPYING. -*/ - -#include "fuse_i.h" - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/poll.h> -#include <linux/uio.h> -#include <linux/miscdevice.h> -#include <linux/pagemap.h> -#include <linux/file.h> -#include <linux/slab.h> -#include <linux/pipe_fs_i.h> -#include <linux/swap.h> -#include <linux/splice.h> -#include <linux/freezer.h> - -MODULE_ALIAS_MISCDEV(FUSE_MINOR); -MODULE_ALIAS("devname:fuse"); - -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; -} -EXPORT_SYMBOL_GPL(fuse_request_alloc); - -struct fuse_req *fuse_request_alloc_nofs(void) -{ - struct fuse_req *req = kmem_cache_alloc(fuse_req_cachep, GFP_NOFS); - 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); -} -EXPORT_SYMBOL_GPL(fuse_get_req); - -/* - * 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); - } -} -EXPORT_SYMBOL_GPL(fuse_put_request); - -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.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 fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget, - u64 nodeid, u64 nlookup) -{ - forget->forget_one.nodeid = nodeid; - forget->forget_one.nlookup = nlookup; - - spin_lock(&fc->lock); - if (fc->connected) { - fc->forget_list_tail->next = forget; - fc->forget_list_tail = forget; - wake_up(&fc->waitq); - kill_fasync(&fc->fasync, SIGIO, POLL_IN); - } else { - kfree(forget); - } - spin_unlock(&fc->lock); -} - -static void flush_bg_queue(struct fuse_conn *fc) -{ - while (fc->active_background < fc->max_background && - !list_empty(&fc->bg_queue)) { - struct fuse_req *req; - - req = list_entry(fc->bg_queue.next, struct fuse_req, list); - list_del(&req->list); - fc->active_background++; - req->in.h.unique = fuse_get_unique(fc); - queue_request(fc, 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) -__releases(fc->lock) -{ - 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 == fc->max_background) { - fc->blocked = 0; - wake_up_all(&fc->blocked_waitq); - } - if (fc->num_background == fc->congestion_threshold && - fc->connected && fc->bdi_initialized) { - clear_bdi_congested(&fc->bdi, BLK_RW_SYNC); - clear_bdi_congested(&fc->bdi, BLK_RW_ASYNC); - } - fc->num_background--; - fc->active_background--; - flush_bg_queue(fc); - } - spin_unlock(&fc->lock); - wake_up(&req->waitq); - if (end) - end(fc, req); - fuse_put_request(fc, req); -} - -static void wait_answer_interruptible(struct fuse_conn *fc, - struct fuse_req *req) -__releases(fc->lock) -__acquires(fc->lock) -{ - 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); -} - -static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) -__releases(fc->lock) -__acquires(fc->lock) -{ - 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) { - 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; - - /* Request is not yet in userspace, bail out */ - if (req->state == FUSE_REQ_PENDING) { - list_del(&req->list); - __fuse_put_request(req); - req->out.h.error = -EINTR; - return; - } - } - - /* - * Either request is already in userspace, or it was forced. - * Wait it out. - */ - spin_unlock(&fc->lock); - - while (req->state != FUSE_REQ_FINISHED) - wait_event_freezable(req->waitq, - req->state == FUSE_REQ_FINISHED); - spin_lock(&fc->lock); - - if (!req->aborted) - return; - - aborted: - BUG_ON(req->state != FUSE_REQ_FINISHED); - 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); - } -} - -void fuse_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 { - req->in.h.unique = fuse_get_unique(fc); - 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); -} -EXPORT_SYMBOL_GPL(fuse_request_send); - -static void fuse_request_send_nowait_locked(struct fuse_conn *fc, - struct fuse_req *req) -{ - req->background = 1; - fc->num_background++; - if (fc->num_background == fc->max_background) - fc->blocked = 1; - if (fc->num_background == fc->congestion_threshold && - fc->bdi_initialized) { - set_bdi_congested(&fc->bdi, BLK_RW_SYNC); - set_bdi_congested(&fc->bdi, BLK_RW_ASYNC); - } - list_add_tail(&req->list, &fc->bg_queue); - flush_bg_queue(fc); -} - -static void fuse_request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) -{ - spin_lock(&fc->lock); - if (fc->connected) { - fuse_request_send_nowait_locked(fc, req); - spin_unlock(&fc->lock); - } else { - req->out.h.error = -ENOTCONN; - request_end(fc, req); - } -} - -void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req) -{ - req->isreply = 1; - fuse_request_send_nowait(fc, req); -} -EXPORT_SYMBOL_GPL(fuse_request_send_background); - -static int fuse_request_send_notify_reply(struct fuse_conn *fc, - struct fuse_req *req, u64 unique) -{ - int err = -ENODEV; - - req->isreply = 0; - req->in.h.unique = unique; - spin_lock(&fc->lock); - if (fc->connected) { - queue_request(fc, req); - err = 0; - } - spin_unlock(&fc->lock); - - return err; -} - -/* - * Called under fc->lock - * - * fc->connected must have been checked previously - */ -void fuse_request_send_background_locked(struct fuse_conn *fc, - struct fuse_req *req) -{ - req->isreply = 1; - fuse_request_send_nowait_locked(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; - struct pipe_buffer *pipebufs; - struct pipe_buffer *currbuf; - struct pipe_inode_info *pipe; - unsigned long nr_segs; - unsigned long seglen; - unsigned long addr; - struct page *pg; - void *mapaddr; - void *buf; - unsigned len; - unsigned move_pages:1; -}; - -static void fuse_copy_init(struct fuse_copy_state *cs, struct fuse_conn *fc, - int write, - const struct iovec *iov, unsigned long nr_segs) -{ - memset(cs, 0, sizeof(*cs)); - cs->fc = fc; - cs->write = write; - 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->currbuf) { - struct pipe_buffer *buf = cs->currbuf; - - if (!cs->write) { - buf->ops->unmap(cs->pipe, buf, cs->mapaddr); - } else { - kunmap(buf->page); - buf->len = PAGE_SIZE - cs->len; - } - cs->currbuf = NULL; - cs->mapaddr = NULL; - } else if (cs->mapaddr) { - kunmap(cs->pg); - 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; - - unlock_request(cs->fc, cs->req); - fuse_copy_finish(cs); - if (cs->pipebufs) { - struct pipe_buffer *buf = cs->pipebufs; - - if (!cs->write) { - err = buf->ops->confirm(cs->pipe, buf); - if (err) - return err; - - BUG_ON(!cs->nr_segs); - cs->currbuf = buf; - cs->mapaddr = buf->ops->map(cs->pipe, buf, 0); - cs->len = buf->len; - cs->buf = cs->mapaddr + buf->offset; - cs->pipebufs++; - cs->nr_segs--; - } else { - struct page *page; - - if (cs->nr_segs == cs->pipe->buffers) - return -EIO; - - page = alloc_page(GFP_HIGHUSER); - if (!page) - return -ENOMEM; - - buf->page = page; - buf->offset = 0; - buf->len = 0; - - cs->currbuf = buf; - cs->mapaddr = kmap(page); - cs->buf = cs->mapaddr; - cs->len = PAGE_SIZE; - cs->pipebufs++; - cs->nr_segs++; - } - } else { - 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--; - } - err = get_user_pages_fast(cs->addr, 1, cs->write, &cs->pg); - if (err < 0) - return err; - BUG_ON(err != 1); - offset = cs->addr % PAGE_SIZE; - cs->mapaddr = kmap(cs->pg); - 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; -} - -static int fuse_check_page(struct page *page) -{ - if (page_mapcount(page) || - page->mapping != NULL || - page_count(page) != 1 || - (page->flags & PAGE_FLAGS_CHECK_AT_PREP & - ~(1 << PG_locked | - 1 << PG_referenced | - 1 << PG_uptodate | - 1 << PG_lru | - 1 << PG_active | - 1 << PG_reclaim))) { - printk(KERN_WARNING "fuse: trying to steal weird page\n"); - printk(KERN_WARNING " page=%p index=%li flags=%08lx, count=%i, mapcount=%i, mapping=%p\n", page, page->index, page->flags, page_count(page), page_mapcount(page), page->mapping); - return 1; - } - return 0; -} - -static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep) -{ - int err; - struct page *oldpage = *pagep; - struct page *newpage; - struct pipe_buffer *buf = cs->pipebufs; - struct address_space *mapping; - pgoff_t index; - - unlock_request(cs->fc, cs->req); - fuse_copy_finish(cs); - - err = buf->ops->confirm(cs->pipe, buf); - if (err) - return err; - - BUG_ON(!cs->nr_segs); - cs->currbuf = buf; - cs->len = buf->len; - cs->pipebufs++; - cs->nr_segs--; - - if (cs->len != PAGE_SIZE) - goto out_fallback; - - if (buf->ops->steal(cs->pipe, buf) != 0) - goto out_fallback; - - newpage = buf->page; - - if (WARN_ON(!PageUptodate(newpage))) - return -EIO; - - ClearPageMappedToDisk(newpage); - - if (fuse_check_page(newpage) != 0) - goto out_fallback_unlock; - - mapping = oldpage->mapping; - index = oldpage->index; - - /* - * This is a new and locked page, it shouldn't be mapped or - * have any special flags on it - */ - if (WARN_ON(page_mapped(oldpage))) - goto out_fallback_unlock; - if (WARN_ON(page_has_private(oldpage))) - goto out_fallback_unlock; - if (WARN_ON(PageDirty(oldpage) || PageWriteback(oldpage))) - goto out_fallback_unlock; - if (WARN_ON(PageMlocked(oldpage))) - goto out_fallback_unlock; - - err = replace_page_cache_page(oldpage, newpage, GFP_KERNEL); - if (err) { - unlock_page(newpage); - return err; - } - - page_cache_get(newpage); - - if (!(buf->flags & PIPE_BUF_FLAG_LRU)) - lru_cache_add_file(newpage); - - err = 0; - spin_lock(&cs->fc->lock); - if (cs->req->aborted) - err = -ENOENT; - else - *pagep = newpage; - spin_unlock(&cs->fc->lock); - - if (err) { - unlock_page(newpage); - page_cache_release(newpage); - return err; - } - - unlock_page(oldpage); - page_cache_release(oldpage); - cs->len = 0; - - return 0; - -out_fallback_unlock: - unlock_page(newpage); -out_fallback: - cs->mapaddr = buf->ops->map(cs->pipe, buf, 1); - cs->buf = cs->mapaddr + buf->offset; - - err = lock_request(cs->fc, cs->req); - if (err) - return err; - - return 1; -} - -static int fuse_ref_page(struct fuse_copy_state *cs, struct page *page, - unsigned offset, unsigned count) -{ - struct pipe_buffer *buf; - - if (cs->nr_segs == cs->pipe->buffers) - return -EIO; - - unlock_request(cs->fc, cs->req); - fuse_copy_finish(cs); - - buf = cs->pipebufs; - page_cache_get(page); - buf->page = page; - buf->offset = offset; - buf->len = count; - - cs->pipebufs++; - cs->nr_segs++; - cs->len = 0; - - return 0; -} - -/* - * 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 **pagep, - unsigned offset, unsigned count, int zeroing) -{ - int err; - struct page *page = *pagep; - - if (page && zeroing && count < PAGE_SIZE) - clear_highpage(page); - - while (count) { - if (cs->write && cs->pipebufs && page) { - return fuse_ref_page(cs, page, offset, count); - } else if (!cs->len) { - if (cs->move_pages && page && - offset == 0 && count == PAGE_SIZE) { - err = fuse_try_move_page(cs, pagep); - if (err <= 0) - return err; - } else { - err = fuse_copy_fill(cs); - if (err) - return err; - } - } - if (page) { - void *mapaddr = kmap_atomic(page); - void *buf = mapaddr + offset; - offset += fuse_copy_do(cs, &buf, &count); - kunmap_atomic(mapaddr); - } 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++) { - int err; - - err = fuse_copy_page(cs, &req->pages[i], 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) { - if (!cs->len) { - int err = fuse_copy_fill(cs); - if (err) - 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 forget_pending(struct fuse_conn *fc) -{ - return fc->forget_list_head.next != NULL; -} - -static int request_pending(struct fuse_conn *fc) -{ - return !list_empty(&fc->pending) || !list_empty(&fc->interrupts) || - forget_pending(fc); -} - -/* Wait until a request is available on the pending list */ -static void request_wait(struct fuse_conn *fc) -__releases(fc->lock) -__acquires(fc->lock) -{ - 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_copy_state *cs, - size_t nbytes, struct fuse_req *req) -__releases(fc->lock) -{ - 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 (nbytes < reqsize) - return -EINVAL; - - 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; -} - -static struct fuse_forget_link *dequeue_forget(struct fuse_conn *fc, - unsigned max, - unsigned *countp) -{ - struct fuse_forget_link *head = fc->forget_list_head.next; - struct fuse_forget_link **newhead = &head; - unsigned count; - - for (count = 0; *newhead != NULL && count < max; count++) - newhead = &(*newhead)->next; - - fc->forget_list_head.next = *newhead; - *newhead = NULL; - if (fc->forget_list_head.next == NULL) - fc->forget_list_tail = &fc->forget_list_head; - - if (countp != NULL) - *countp = count; - - return head; -} - -static int fuse_read_single_forget(struct fuse_conn *fc, - struct fuse_copy_state *cs, - size_t nbytes) -__releases(fc->lock) -{ - int err; - struct fuse_forget_link *forget = dequeue_forget(fc, 1, NULL); - struct fuse_forget_in arg = { - .nlookup = forget->forget_one.nlookup, - }; - struct fuse_in_header ih = { - .opcode = FUSE_FORGET, - .nodeid = forget->forget_one.nodeid, - .unique = fuse_get_unique(fc), - .len = sizeof(ih) + sizeof(arg), - }; - - spin_unlock(&fc->lock); - kfree(forget); - if (nbytes < ih.len) - return -EINVAL; - - err = fuse_copy_one(cs, &ih, sizeof(ih)); - if (!err) - err = fuse_copy_one(cs, &arg, sizeof(arg)); - fuse_copy_finish(cs); - - if (err) - return err; - - return ih.len; -} - -static int fuse_read_batch_forget(struct fuse_conn *fc, - struct fuse_copy_state *cs, size_t nbytes) -__releases(fc->lock) -{ - int err; - unsigned max_forgets; - unsigned count; - struct fuse_forget_link *head; - struct fuse_batch_forget_in arg = { .count = 0 }; - struct fuse_in_header ih = { - .opcode = FUSE_BATCH_FORGET, - .unique = fuse_get_unique(fc), - .len = sizeof(ih) + sizeof(arg), - }; - - if (nbytes < ih.len) { - spin_unlock(&fc->lock); - return -EINVAL; - } - - max_forgets = (nbytes - ih.len) / sizeof(struct fuse_forget_one); - head = dequeue_forget(fc, max_forgets, &count); - spin_unlock(&fc->lock); - - arg.count = count; - ih.len += count * sizeof(struct fuse_forget_one); - err = fuse_copy_one(cs, &ih, sizeof(ih)); - if (!err) - err = fuse_copy_one(cs, &arg, sizeof(arg)); - - while (head) { - struct fuse_forget_link *forget = head; - - if (!err) { - err = fuse_copy_one(cs, &forget->forget_one, - sizeof(forget->forget_one)); - } - head = forget->next; - kfree(forget); - } - - fuse_copy_finish(cs); - - if (err) - return err; - - return ih.len; -} - -static int fuse_read_forget(struct fuse_conn *fc, struct fuse_copy_state *cs, - size_t nbytes) -__releases(fc->lock) -{ - if (fc->minor < 16 || fc->forget_list_head.next->next == NULL) - return fuse_read_single_forget(fc, cs, nbytes); - else - return fuse_read_batch_forget(fc, cs, nbytes); -} - -/* - * 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_do_read(struct fuse_conn *fc, struct file *file, - struct fuse_copy_state *cs, size_t nbytes) -{ - int err; - struct fuse_req *req; - struct fuse_in *in; - unsigned reqsize; - - 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, cs, nbytes, req); - } - - if (forget_pending(fc)) { - if (list_empty(&fc->pending) || fc->forget_batch-- > 0) - return fuse_read_forget(fc, cs, nbytes); - - if (fc->forget_batch <= -8) - fc->forget_batch = 16; - } - - 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 (nbytes < 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); - cs->req = req; - 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 (req->aborted) { - request_end(fc, req); - return -ENODEV; - } - if (err) { - 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; -} - -static ssize_t fuse_dev_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - struct fuse_copy_state cs; - struct file *file = iocb->ki_filp; - struct fuse_conn *fc = fuse_get_conn(file); - if (!fc) - return -EPERM; - - fuse_copy_init(&cs, fc, 1, iov, nr_segs); - - return fuse_dev_do_read(fc, file, &cs, iov_length(iov, nr_segs)); -} - -static int fuse_dev_pipe_buf_steal(struct pipe_inode_info *pipe, - struct pipe_buffer *buf) -{ - return 1; -} - -static const struct pipe_buf_operations fuse_dev_pipe_buf_ops = { - .can_merge = 0, - .map = generic_pipe_buf_map, - .unmap = generic_pipe_buf_unmap, - .confirm = generic_pipe_buf_confirm, - .release = generic_pipe_buf_release, - .steal = fuse_dev_pipe_buf_steal, - .get = generic_pipe_buf_get, -}; - -static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, - struct pipe_inode_info *pipe, - size_t len, unsigned int flags) -{ - int ret; - int page_nr = 0; - int do_wakeup = 0; - struct pipe_buffer *bufs; - struct fuse_copy_state cs; - struct fuse_conn *fc = fuse_get_conn(in); - if (!fc) - return -EPERM; - - bufs = kmalloc(pipe->buffers * sizeof(struct pipe_buffer), GFP_KERNEL); - if (!bufs) - return -ENOMEM; - - fuse_copy_init(&cs, fc, 1, NULL, 0); - cs.pipebufs = bufs; - cs.pipe = pipe; - ret = fuse_dev_do_read(fc, in, &cs, len); - if (ret < 0) - goto out; - - ret = 0; - pipe_lock(pipe); - - if (!pipe->readers) { - send_sig(SIGPIPE, current, 0); - if (!ret) - ret = -EPIPE; - goto out_unlock; - } - - if (pipe->nrbufs + cs.nr_segs > pipe->buffers) { - ret = -EIO; - goto out_unlock; - } - - while (page_nr < cs.nr_segs) { - int newbuf = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1); - struct pipe_buffer *buf = pipe->bufs + newbuf; - - buf->page = bufs[page_nr].page; - buf->offset = bufs[page_nr].offset; - buf->len = bufs[page_nr].len; - buf->ops = &fuse_dev_pipe_buf_ops; - - pipe->nrbufs++; - page_nr++; - ret += buf->len; - - if (pipe->inode) - do_wakeup = 1; - } - -out_unlock: - pipe_unlock(pipe); - - if (do_wakeup) { - smp_mb(); - if (waitqueue_active(&pipe->wait)) - wake_up_interruptible(&pipe->wait); - kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN); - } - -out: - for (; page_nr < cs.nr_segs; page_nr++) - page_cache_release(bufs[page_nr].page); - - kfree(bufs); - return ret; -} - -static int fuse_notify_poll(struct fuse_conn *fc, unsigned int size, - struct fuse_copy_state *cs) -{ - struct fuse_notify_poll_wakeup_out outarg; - int err = -EINVAL; - - if (size != sizeof(outarg)) - goto err; - - err = fuse_copy_one(cs, &outarg, sizeof(outarg)); - if (err) - goto err; - - fuse_copy_finish(cs); - return fuse_notify_poll_wakeup(fc, &outarg); - -err: - fuse_copy_finish(cs); - return err; -} - -static int fuse_notify_inval_inode(struct fuse_conn *fc, unsigned int size, - struct fuse_copy_state *cs) -{ - struct fuse_notify_inval_inode_out outarg; - int err = -EINVAL; - - if (size != sizeof(outarg)) - goto err; - - err = fuse_copy_one(cs, &outarg, sizeof(outarg)); - if (err) - goto err; - fuse_copy_finish(cs); - - down_read(&fc->killsb); - err = -ENOENT; - if (fc->sb) { - err = fuse_reverse_inval_inode(fc->sb, outarg.ino, - outarg.off, outarg.len); - } - up_read(&fc->killsb); - return err; - -err: - fuse_copy_finish(cs); - return err; -} - -static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size, - struct fuse_copy_state *cs) -{ - struct fuse_notify_inval_entry_out outarg; - int err = -ENOMEM; - char *buf; - struct qstr name; - - buf = kzalloc(FUSE_NAME_MAX + 1, GFP_KERNEL); - if (!buf) - goto err; - - err = -EINVAL; - if (size < sizeof(outarg)) - goto err; - - err = fuse_copy_one(cs, &outarg, sizeof(outarg)); - if (err) - goto err; - - err = -ENAMETOOLONG; - if (outarg.namelen > FUSE_NAME_MAX) - goto err; - - err = -EINVAL; - if (size != sizeof(outarg) + outarg.namelen + 1) - goto err; - - name.name = buf; - name.len = outarg.namelen; - err = fuse_copy_one(cs, buf, outarg.namelen + 1); - if (err) - goto err; - fuse_copy_finish(cs); - buf[outarg.namelen] = 0; - name.hash = full_name_hash(name.name, name.len); - - down_read(&fc->killsb); - err = -ENOENT; - if (fc->sb) - err = fuse_reverse_inval_entry(fc->sb, outarg.parent, 0, &name); - up_read(&fc->killsb); - kfree(buf); - return err; - -err: - kfree(buf); - fuse_copy_finish(cs); - return err; -} - -static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size, - struct fuse_copy_state *cs) -{ - struct fuse_notify_delete_out outarg; - int err = -ENOMEM; - char *buf; - struct qstr name; - - buf = kzalloc(FUSE_NAME_MAX + 1, GFP_KERNEL); - if (!buf) - goto err; - - err = -EINVAL; - if (size < sizeof(outarg)) - goto err; - - err = fuse_copy_one(cs, &outarg, sizeof(outarg)); - if (err) - goto err; - - err = -ENAMETOOLONG; - if (outarg.namelen > FUSE_NAME_MAX) - goto err; - - err = -EINVAL; - if (size != sizeof(outarg) + outarg.namelen + 1) - goto err; - - name.name = buf; - name.len = outarg.namelen; - err = fuse_copy_one(cs, buf, outarg.namelen + 1); - if (err) - goto err; - fuse_copy_finish(cs); - buf[outarg.namelen] = 0; - name.hash = full_name_hash(name.name, name.len); - - down_read(&fc->killsb); - err = -ENOENT; - if (fc->sb) - err = fuse_reverse_inval_entry(fc->sb, outarg.parent, - outarg.child, &name); - up_read(&fc->killsb); - kfree(buf); - return err; - -err: - kfree(buf); - fuse_copy_finish(cs); - return err; -} - -static int fuse_notify_store(struct fuse_conn *fc, unsigned int size, - struct fuse_copy_state *cs) -{ - struct fuse_notify_store_out outarg; - struct inode *inode; - struct address_space *mapping; - u64 nodeid; - int err; - pgoff_t index; - unsigned int offset; - unsigned int num; - loff_t file_size; - loff_t end; - - err = -EINVAL; - if (size < sizeof(outarg)) - goto out_finish; - - err = fuse_copy_one(cs, &outarg, sizeof(outarg)); - if (err) - goto out_finish; - - err = -EINVAL; - if (size - sizeof(outarg) != outarg.size) - goto out_finish; - - nodeid = outarg.nodeid; - - down_read(&fc->killsb); - - err = -ENOENT; - if (!fc->sb) - goto out_up_killsb; - - inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid); - if (!inode) - goto out_up_killsb; - - mapping = inode->i_mapping; - index = outarg.offset >> PAGE_CACHE_SHIFT; - offset = outarg.offset & ~PAGE_CACHE_MASK; - file_size = i_size_read(inode); - end = outarg.offset + outarg.size; - if (end > file_size) { - file_size = end; - fuse_write_update_size(inode, file_size); - } - - num = outarg.size; - while (num) { - struct page *page; - unsigned int this_num; - - err = -ENOMEM; - page = find_or_create_page(mapping, index, - mapping_gfp_mask(mapping)); - if (!page) - goto out_iput; - - this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset); - err = fuse_copy_page(cs, &page, offset, this_num, 0); - if (!err && offset == 0 && (num != 0 || file_size == end)) - SetPageUptodate(page); - unlock_page(page); - page_cache_release(page); - - if (err) - goto out_iput; - - num -= this_num; - offset = 0; - index++; - } - - err = 0; - -out_iput: - iput(inode); -out_up_killsb: - up_read(&fc->killsb); -out_finish: - fuse_copy_finish(cs); - return err; -} - -static void fuse_retrieve_end(struct fuse_conn *fc, struct fuse_req *req) -{ - release_pages(req->pages, req->num_pages, 0); -} - -static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode, - struct fuse_notify_retrieve_out *outarg) -{ - int err; - struct address_space *mapping = inode->i_mapping; - struct fuse_req *req; - pgoff_t index; - loff_t file_size; - unsigned int num; - unsigned int offset; - size_t total_len = 0; - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - offset = outarg->offset & ~PAGE_CACHE_MASK; - - req->in.h.opcode = FUSE_NOTIFY_REPLY; - req->in.h.nodeid = outarg->nodeid; - req->in.numargs = 2; - req->in.argpages = 1; - req->page_offset = offset; - req->end = fuse_retrieve_end; - - index = outarg->offset >> PAGE_CACHE_SHIFT; - file_size = i_size_read(inode); - num = outarg->size; - if (outarg->offset > file_size) - num = 0; - else if (outarg->offset + num > file_size) - num = file_size - outarg->offset; - - while (num && req->num_pages < FUSE_MAX_PAGES_PER_REQ) { - struct page *page; - unsigned int this_num; - - page = find_get_page(mapping, index); - if (!page) - break; - - this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset); - req->pages[req->num_pages] = page; - req->num_pages++; - - num -= this_num; - total_len += this_num; - index++; - } - req->misc.retrieve_in.offset = outarg->offset; - req->misc.retrieve_in.size = total_len; - req->in.args[0].size = sizeof(req->misc.retrieve_in); - req->in.args[0].value = &req->misc.retrieve_in; - req->in.args[1].size = total_len; - - err = fuse_request_send_notify_reply(fc, req, outarg->notify_unique); - if (err) - fuse_retrieve_end(fc, req); - - return err; -} - -static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size, - struct fuse_copy_state *cs) -{ - struct fuse_notify_retrieve_out outarg; - struct inode *inode; - int err; - - err = -EINVAL; - if (size != sizeof(outarg)) - goto copy_finish; - - err = fuse_copy_one(cs, &outarg, sizeof(outarg)); - if (err) - goto copy_finish; - - fuse_copy_finish(cs); - - down_read(&fc->killsb); - err = -ENOENT; - if (fc->sb) { - u64 nodeid = outarg.nodeid; - - inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid); - if (inode) { - err = fuse_retrieve(fc, inode, &outarg); - iput(inode); - } - } - up_read(&fc->killsb); - - return err; - -copy_finish: - fuse_copy_finish(cs); - return err; -} - -static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, - unsigned int size, struct fuse_copy_state *cs) -{ - switch (code) { - case FUSE_NOTIFY_POLL: - return fuse_notify_poll(fc, size, cs); - - case FUSE_NOTIFY_INVAL_INODE: - return fuse_notify_inval_inode(fc, size, cs); - - case FUSE_NOTIFY_INVAL_ENTRY: - return fuse_notify_inval_entry(fc, size, cs); - - case FUSE_NOTIFY_STORE: - return fuse_notify_store(fc, size, cs); - - case FUSE_NOTIFY_RETRIEVE: - return fuse_notify_retrieve(fc, size, cs); - - case FUSE_NOTIFY_DELETE: - return fuse_notify_delete(fc, size, cs); - - default: - fuse_copy_finish(cs); - return -EINVAL; - } -} - -/* 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_do_write(struct fuse_conn *fc, - struct fuse_copy_state *cs, size_t nbytes) -{ - int err; - struct fuse_req *req; - struct fuse_out_header oh; - - 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.len != nbytes) - goto err_finish; - - /* - * Zero oh.unique indicates unsolicited notification message - * and error contains notification code. - */ - if (!oh.unique) { - err = fuse_notify(fc, oh.error, nbytes - sizeof(oh), cs); - return err ? err : nbytes; - } - - err = -EINVAL; - if (oh.error <= -1000 || oh.error > 0) - 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; - if (!req->out.page_replace) - cs->move_pages = 0; - 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; -} - -static ssize_t fuse_dev_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - struct fuse_copy_state cs; - struct fuse_conn *fc = fuse_get_conn(iocb->ki_filp); - if (!fc) - return -EPERM; - - fuse_copy_init(&cs, fc, 0, iov, nr_segs); - - return fuse_dev_do_write(fc, &cs, iov_length(iov, nr_segs)); -} - -static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, - struct file *out, loff_t *ppos, - size_t len, unsigned int flags) -{ - unsigned nbuf; - unsigned idx; - struct pipe_buffer *bufs; - struct fuse_copy_state cs; - struct fuse_conn *fc; - size_t rem; - ssize_t ret; - - fc = fuse_get_conn(out); - if (!fc) - return -EPERM; - - bufs = kmalloc(pipe->buffers * sizeof(struct pipe_buffer), GFP_KERNEL); - if (!bufs) - return -ENOMEM; - - pipe_lock(pipe); - nbuf = 0; - rem = 0; - for (idx = 0; idx < pipe->nrbufs && rem < len; idx++) - rem += pipe->bufs[(pipe->curbuf + idx) & (pipe->buffers - 1)].len; - - ret = -EINVAL; - if (rem < len) { - pipe_unlock(pipe); - goto out; - } - - rem = len; - while (rem) { - struct pipe_buffer *ibuf; - struct pipe_buffer *obuf; - - BUG_ON(nbuf >= pipe->buffers); - BUG_ON(!pipe->nrbufs); - ibuf = &pipe->bufs[pipe->curbuf]; - obuf = &bufs[nbuf]; - - if (rem >= ibuf->len) { - *obuf = *ibuf; - ibuf->ops = NULL; - pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1); - pipe->nrbufs--; - } else { - ibuf->ops->get(pipe, ibuf); - *obuf = *ibuf; - obuf->flags &= ~PIPE_BUF_FLAG_GIFT; - obuf->len = rem; - ibuf->offset += obuf->len; - ibuf->len -= obuf->len; - } - nbuf++; - rem -= obuf->len; - } - pipe_unlock(pipe); - - fuse_copy_init(&cs, fc, 0, NULL, nbuf); - cs.pipebufs = bufs; - cs.pipe = pipe; - - if (flags & SPLICE_F_MOVE) - cs.move_pages = 1; - - ret = fuse_dev_do_write(fc, &cs, len); - - for (idx = 0; idx < nbuf; idx++) { - struct pipe_buffer *buf = &bufs[idx]; - buf->ops->release(pipe, buf); - } -out: - kfree(bufs); - return ret; -} - -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) -__releases(fc->lock) -__acquires(fc->lock) -{ - 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) -__releases(fc->lock) -__acquires(fc->lock) -{ - 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; - __fuse_get_request(req); - spin_unlock(&fc->lock); - wait_event(req->waitq, !req->locked); - end(fc, req); - fuse_put_request(fc, req); - spin_lock(&fc->lock); - } - } -} - -static void end_queued_requests(struct fuse_conn *fc) -__releases(fc->lock) -__acquires(fc->lock) -{ - fc->max_background = UINT_MAX; - flush_bg_queue(fc); - end_requests(fc, &fc->pending); - end_requests(fc, &fc->processing); - while (forget_pending(fc)) - kfree(dequeue_forget(fc, 1, NULL)); -} - -static void end_polls(struct fuse_conn *fc) -{ - struct rb_node *p; - - p = rb_first(&fc->polled_files); - - while (p) { - struct fuse_file *ff; - ff = rb_entry(p, struct fuse_file, polled_node); - wake_up_interruptible_all(&ff->poll_wait); - - p = rb_next(p); - } -} - -/* - * 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_queued_requests(fc); - end_polls(fc); - wake_up_all(&fc->waitq); - wake_up_all(&fc->blocked_waitq); - kill_fasync(&fc->fasync, SIGIO, POLL_IN); - } - spin_unlock(&fc->lock); -} -EXPORT_SYMBOL_GPL(fuse_abort_conn); - -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; - fc->blocked = 0; - end_queued_requests(fc); - end_polls(fc); - wake_up_all(&fc->blocked_waitq); - spin_unlock(&fc->lock); - fuse_conn_put(fc); - } - - return 0; -} -EXPORT_SYMBOL_GPL(fuse_dev_release); - -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); -} - -const struct file_operations fuse_dev_operations = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = do_sync_read, - .aio_read = fuse_dev_read, - .splice_read = fuse_dev_splice_read, - .write = do_sync_write, - .aio_write = fuse_dev_write, - .splice_write = fuse_dev_splice_write, - .poll = fuse_dev_poll, - .release = fuse_dev_release, - .fasync = fuse_dev_fasync, -}; -EXPORT_SYMBOL_GPL(fuse_dev_operations); - -static struct miscdevice fuse_miscdevice = { - .minor = FUSE_MINOR, - .name = "fuse", - .fops = &fuse_dev_operations, -}; - -int __init fuse_dev_init(void) -{ - int err = -ENOMEM; - fuse_req_cachep = kmem_cache_create("fuse_request", - sizeof(struct fuse_req), - 0, 0, NULL); - 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/ANDROID_3.4.5/fs/fuse/dir.c b/ANDROID_3.4.5/fs/fuse/dir.c deleted file mode 100644 index bc438320..00000000 --- a/ANDROID_3.4.5/fs/fuse/dir.c +++ /dev/null @@ -1,1697 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu> - - This program can be distributed under the terms of the GNU GPL. - See the file COPYING. -*/ - -#include "fuse_i.h" - -#include <linux/pagemap.h> -#include <linux/file.h> -#include <linux/sched.h> -#include <linux/namei.h> -#include <linux/slab.h> - -#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_entry_timeout(struct dentry *entry, - struct fuse_entry_out *o) -{ - fuse_dentry_settime(entry, - time_to_jiffies(o->entry_valid, o->entry_valid_nsec)); -} - -static u64 attr_timeout(struct fuse_attr_out *o) -{ - return time_to_jiffies(o->attr_valid, o->attr_valid_nsec); -} - -static u64 entry_attr_timeout(struct fuse_entry_out *o) -{ - return 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) - */ -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_conn *fc, struct fuse_req *req, - u64 nodeid, struct qstr *name, - struct fuse_entry_out *outarg) -{ - memset(outarg, 0, sizeof(struct fuse_entry_out)); - req->in.h.opcode = FUSE_LOOKUP; - req->in.h.nodeid = nodeid; - req->in.numargs = 1; - req->in.args[0].size = name->len + 1; - req->in.args[0].value = name->name; - req->out.numargs = 1; - if (fc->minor < 9) - req->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE; - else - req->out.args[0].size = sizeof(struct fuse_entry_out); - req->out.args[0].value = outarg; -} - -u64 fuse_get_attr_version(struct fuse_conn *fc) -{ - u64 curr_version; - - /* - * The spin lock isn't actually needed on 64bit archs, but we - * don't yet care too much about such optimizations. - */ - spin_lock(&fc->lock); - curr_version = fc->attr_version; - spin_unlock(&fc->lock); - - return curr_version; -} - -/* - * 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; - - inode = ACCESS_ONCE(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_forget_link *forget; - struct dentry *parent; - u64 attr_version; - - /* For negative dentries, always do a fresh lookup */ - if (!inode) - return 0; - - if (nd && (nd->flags & LOOKUP_RCU)) - return -ECHILD; - - fc = get_fuse_conn(inode); - req = fuse_get_req(fc); - if (IS_ERR(req)) - return 0; - - forget = fuse_alloc_forget(); - if (!forget) { - fuse_put_request(fc, req); - return 0; - } - - attr_version = fuse_get_attr_version(fc); - - parent = dget_parent(entry); - fuse_lookup_init(fc, req, get_node_id(parent->d_inode), - &entry->d_name, &outarg); - fuse_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_queue_forget(fc, forget, outarg.nodeid, 1); - return 0; - } - spin_lock(&fc->lock); - fi->nlookup++; - spin_unlock(&fc->lock); - } - kfree(forget); - if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT) - return 0; - - fuse_change_attributes(inode, &outarg.attr, - entry_attr_timeout(&outarg), - attr_version); - fuse_change_entry_timeout(entry, &outarg); - } - return 1; -} - -static int invalid_nodeid(u64 nodeid) -{ - return !nodeid || nodeid == FUSE_ROOT_ID; -} - -const 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); -} - -int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name, - struct fuse_entry_out *outarg, struct inode **inode) -{ - struct fuse_conn *fc = get_fuse_conn_super(sb); - struct fuse_req *req; - struct fuse_forget_link *forget; - u64 attr_version; - int err; - - *inode = NULL; - err = -ENAMETOOLONG; - if (name->len > FUSE_NAME_MAX) - goto out; - - req = fuse_get_req(fc); - err = PTR_ERR(req); - if (IS_ERR(req)) - goto out; - - forget = fuse_alloc_forget(); - err = -ENOMEM; - if (!forget) { - fuse_put_request(fc, req); - goto out; - } - - attr_version = fuse_get_attr_version(fc); - - fuse_lookup_init(fc, req, nodeid, name, outarg); - fuse_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) - goto out_put_forget; - - err = -EIO; - if (!outarg->nodeid) - goto out_put_forget; - if (!fuse_valid_type(outarg->attr.mode)) - goto out_put_forget; - - *inode = fuse_iget(sb, outarg->nodeid, outarg->generation, - &outarg->attr, entry_attr_timeout(outarg), - attr_version); - err = -ENOMEM; - if (!*inode) { - fuse_queue_forget(fc, forget, outarg->nodeid, 1); - goto out; - } - err = 0; - - out_put_forget: - kfree(forget); - out: - return err; -} - -static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, - struct nameidata *nd) -{ - int err; - struct fuse_entry_out outarg; - struct inode *inode; - struct dentry *newent; - struct fuse_conn *fc = get_fuse_conn(dir); - bool outarg_valid = true; - - err = fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name, - &outarg, &inode); - if (err == -ENOENT) { - outarg_valid = false; - err = 0; - } - if (err) - goto out_err; - - err = -EIO; - if (inode && get_node_id(inode) == FUSE_ROOT_ID) - goto out_iput; - - if (inode && S_ISDIR(inode->i_mode)) { - mutex_lock(&fc->inst_mutex); - newent = fuse_d_add_directory(entry, inode); - mutex_unlock(&fc->inst_mutex); - err = PTR_ERR(newent); - if (IS_ERR(newent)) - goto out_iput; - } else { - newent = d_splice_alias(inode, entry); - } - - entry = newent ? newent : entry; - if (outarg_valid) - fuse_change_entry_timeout(entry, &outarg); - else - fuse_invalidate_entry_cache(entry); - - return newent; - - out_iput: - iput(inode); - out_err: - return ERR_PTR(err); -} - -/* - * 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, - umode_t mode, struct nameidata *nd) -{ - int err; - struct inode *inode; - struct fuse_conn *fc = get_fuse_conn(dir); - struct fuse_req *req; - struct fuse_forget_link *forget; - struct fuse_create_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; - - if (fc->no_create) - return -ENOSYS; - - forget = fuse_alloc_forget(); - if (!forget) - return -ENOMEM; - - req = fuse_get_req(fc); - err = PTR_ERR(req); - if (IS_ERR(req)) - goto out_put_forget_req; - - err = -ENOMEM; - ff = fuse_file_alloc(fc); - if (!ff) - goto out_put_request; - - if (!fc->dont_mask) - mode &= ~current_umask(); - - flags &= ~O_NOCTTY; - memset(&inarg, 0, sizeof(inarg)); - memset(&outentry, 0, sizeof(outentry)); - inarg.flags = flags; - inarg.mode = mode; - inarg.umask = current_umask(); - req->in.h.opcode = FUSE_CREATE; - req->in.h.nodeid = get_node_id(dir); - req->in.numargs = 2; - req->in.args[0].size = fc->minor < 12 ? sizeof(struct fuse_open_in) : - 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; - if (fc->minor < 9) - req->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE; - else - 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; - fuse_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); - ff->fh = outopen.fh; - ff->nodeid = outentry.nodeid; - ff->open_flags = outopen.open_flags; - inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, - &outentry.attr, entry_attr_timeout(&outentry), 0); - if (!inode) { - flags &= ~(O_CREAT | O_EXCL | O_TRUNC); - fuse_sync_release(ff, flags); - fuse_queue_forget(fc, forget, outentry.nodeid, 1); - return -ENOMEM; - } - kfree(forget); - d_instantiate(entry, inode); - fuse_change_entry_timeout(entry, &outentry); - fuse_invalidate_attr(dir); - file = lookup_instantiate_filp(nd, entry, generic_file_open); - if (IS_ERR(file)) { - fuse_sync_release(ff, flags); - return PTR_ERR(file); - } - file->private_data = fuse_file_get(ff); - fuse_finish_open(inode, file); - return 0; - - out_free_ff: - fuse_file_free(ff); - out_put_request: - fuse_put_request(fc, req); - out_put_forget_req: - kfree(forget); - return err; -} - -/* - * 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, - umode_t mode) -{ - struct fuse_entry_out outarg; - struct inode *inode; - int err; - struct fuse_forget_link *forget; - - forget = fuse_alloc_forget(); - if (!forget) { - fuse_put_request(fc, req); - return -ENOMEM; - } - - memset(&outarg, 0, sizeof(outarg)); - req->in.h.nodeid = get_node_id(dir); - req->out.numargs = 1; - if (fc->minor < 9) - req->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE; - else - req->out.args[0].size = sizeof(outarg); - req->out.args[0].value = &outarg; - fuse_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, entry_attr_timeout(&outarg), 0); - if (!inode) { - fuse_queue_forget(fc, forget, outarg.nodeid, 1); - return -ENOMEM; - } - kfree(forget); - - 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_entry_timeout(entry, &outarg); - fuse_invalidate_attr(dir); - return 0; - - out_put_forget_req: - kfree(forget); - return err; -} - -static int fuse_mknod(struct inode *dir, struct dentry *entry, umode_t 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); - - if (!fc->dont_mask) - mode &= ~current_umask(); - - memset(&inarg, 0, sizeof(inarg)); - inarg.mode = mode; - inarg.rdev = new_encode_dev(rdev); - inarg.umask = current_umask(); - req->in.h.opcode = FUSE_MKNOD; - req->in.numargs = 2; - req->in.args[0].size = fc->minor < 12 ? FUSE_COMPAT_MKNOD_IN_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, umode_t mode, - struct nameidata *nd) -{ - if (nd) { - int err = fuse_create_open(dir, entry, mode, nd); - if (err != -ENOSYS) - return err; - /* Fall back on mknod */ - } - return fuse_mknod(dir, entry, mode, 0); -} - -static int fuse_mkdir(struct inode *dir, struct dentry *entry, umode_t 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); - - if (!fc->dont_mask) - mode &= ~current_umask(); - - memset(&inarg, 0, sizeof(inarg)); - inarg.mode = mode; - inarg.umask = current_umask(); - 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; - fuse_request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - if (!err) { - struct inode *inode = entry->d_inode; - struct fuse_inode *fi = get_fuse_inode(inode); - - spin_lock(&fc->lock); - fi->attr_version = ++fc->attr_version; - drop_nlink(inode); - spin_unlock(&fc->lock); - 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; - fuse_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; - fuse_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_attr(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) { - struct fuse_inode *fi = get_fuse_inode(inode); - - spin_lock(&fc->lock); - fi->attr_version = ++fc->attr_version; - inc_nlink(inode); - spin_unlock(&fc->lock); - fuse_invalidate_attr(inode); - } else if (err == -EINTR) { - fuse_invalidate_attr(inode); - } - return err; -} - -static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr, - struct kstat *stat) -{ - stat->dev = inode->i_sb->s_dev; - stat->ino = attr->ino; - stat->mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777); - stat->nlink = attr->nlink; - stat->uid = attr->uid; - stat->gid = attr->gid; - stat->rdev = inode->i_rdev; - stat->atime.tv_sec = attr->atime; - stat->atime.tv_nsec = attr->atimensec; - stat->mtime.tv_sec = attr->mtime; - stat->mtime.tv_nsec = attr->mtimensec; - stat->ctime.tv_sec = attr->ctime; - stat->ctime.tv_nsec = attr->ctimensec; - stat->size = attr->size; - stat->blocks = attr->blocks; - stat->blksize = (1 << inode->i_blkbits); -} - -static int fuse_do_getattr(struct inode *inode, struct kstat *stat, - struct file *file) -{ - int err; - struct fuse_getattr_in inarg; - struct fuse_attr_out outarg; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req; - u64 attr_version; - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - attr_version = fuse_get_attr_version(fc); - - memset(&inarg, 0, sizeof(inarg)); - memset(&outarg, 0, sizeof(outarg)); - /* Directories have separate file-handle space */ - if (file && S_ISREG(inode->i_mode)) { - struct fuse_file *ff = file->private_data; - - inarg.getattr_flags |= FUSE_GETATTR_FH; - inarg.fh = ff->fh; - } - req->in.h.opcode = FUSE_GETATTR; - 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; - if (fc->minor < 9) - req->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE; - else - req->out.args[0].size = sizeof(outarg); - req->out.args[0].value = &outarg; - fuse_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) { - make_bad_inode(inode); - err = -EIO; - } else { - fuse_change_attributes(inode, &outarg.attr, - attr_timeout(&outarg), - attr_version); - if (stat) - fuse_fillattr(inode, &outarg.attr, stat); - } - } - return err; -} - -int fuse_update_attributes(struct inode *inode, struct kstat *stat, - struct file *file, bool *refreshed) -{ - struct fuse_inode *fi = get_fuse_inode(inode); - int err; - bool r; - - if (fi->i_time < get_jiffies_64()) { - r = true; - err = fuse_do_getattr(inode, stat, file); - } else { - r = false; - err = 0; - if (stat) { - generic_fillattr(inode, stat); - stat->mode = fi->orig_i_mode; - stat->ino = fi->orig_ino; - } - } - - if (refreshed != NULL) - *refreshed = r; - - return err; -} - -int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, - u64 child_nodeid, struct qstr *name) -{ - int err = -ENOTDIR; - struct inode *parent; - struct dentry *dir; - struct dentry *entry; - - parent = ilookup5(sb, parent_nodeid, fuse_inode_eq, &parent_nodeid); - if (!parent) - return -ENOENT; - - mutex_lock(&parent->i_mutex); - if (!S_ISDIR(parent->i_mode)) - goto unlock; - - err = -ENOENT; - dir = d_find_alias(parent); - if (!dir) - goto unlock; - - entry = d_lookup(dir, name); - dput(dir); - if (!entry) - goto unlock; - - fuse_invalidate_attr(parent); - fuse_invalidate_entry(entry); - - if (child_nodeid != 0 && entry->d_inode) { - mutex_lock(&entry->d_inode->i_mutex); - if (get_node_id(entry->d_inode) != child_nodeid) { - err = -ENOENT; - goto badentry; - } - if (d_mountpoint(entry)) { - err = -EBUSY; - goto badentry; - } - if (S_ISDIR(entry->d_inode->i_mode)) { - shrink_dcache_parent(entry); - if (!simple_empty(entry)) { - err = -ENOTEMPTY; - goto badentry; - } - entry->d_inode->i_flags |= S_DEAD; - } - dont_mount(entry); - clear_nlink(entry->d_inode); - err = 0; - badentry: - mutex_unlock(&entry->d_inode->i_mutex); - if (!err) - d_delete(entry); - } else { - err = 0; - } - dput(entry); - - unlock: - mutex_unlock(&parent->i_mutex); - iput(parent); - 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) -{ - const struct cred *cred; - int ret; - - if (fc->flags & FUSE_ALLOW_OTHER) - return 1; - - rcu_read_lock(); - ret = 0; - cred = __task_cred(task); - if (cred->euid == fc->user_id && - cred->suid == fc->user_id && - cred->uid == fc->user_id && - cred->egid == fc->group_id && - cred->sgid == fc->group_id && - cred->gid == fc->group_id) - ret = 1; - rcu_read_unlock(); - - return ret; -} - -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 & (MAY_READ | MAY_WRITE | MAY_EXEC); - 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; - fuse_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; -} - -static int fuse_perm_getattr(struct inode *inode, int mask) -{ - if (mask & MAY_NOT_BLOCK) - return -ECHILD; - - return fuse_do_getattr(inode, NULL, NULL); -} - -/* - * 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 fuse_conn *fc = get_fuse_conn(inode); - bool refreshed = false; - int err = 0; - - if (!fuse_allow_task(fc, current)) - return -EACCES; - - /* - * If attributes are needed, refresh them before proceeding - */ - if ((fc->flags & FUSE_DEFAULT_PERMISSIONS) || - ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))) { - struct fuse_inode *fi = get_fuse_inode(inode); - - if (fi->i_time < get_jiffies_64()) { - refreshed = true; - - err = fuse_perm_getattr(inode, mask); - if (err) - return err; - } - } - - if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { - err = generic_permission(inode, mask); - - /* 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 && !refreshed) { - err = fuse_perm_getattr(inode, mask); - if (!err) - err = generic_permission(inode, mask); - } - - /* 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 */ - } else if (mask & (MAY_ACCESS | MAY_CHDIR)) { - if (mask & MAY_NOT_BLOCK) - return -ECHILD; - - err = fuse_access(inode, mask); - } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) { - if (!(inode->i_mode & S_IXUGO)) { - if (refreshed) - return -EACCES; - - err = fuse_perm_getattr(inode, mask); - if (!err && !(inode->i_mode & S_IXUGO)) - return -EACCES; - } - } - return err; -} - -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_path.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->out.argpages = 1; - req->num_pages = 1; - req->pages[0] = page; - fuse_read_fill(req, file, file->f_pos, PAGE_SIZE, FUSE_READDIR); - fuse_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_CAST(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; - fuse_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); -} - -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)); -} - -static int fuse_dir_open(struct inode *inode, struct file *file) -{ - return fuse_open_common(inode, file, true); -} - -static int fuse_dir_release(struct inode *inode, struct file *file) -{ - fuse_release_common(file, FUSE_RELEASEDIR); - - return 0; -} - -static int fuse_dir_fsync(struct file *file, loff_t start, loff_t end, - int datasync) -{ - return fuse_fsync_common(file, start, end, datasync, 1); -} - -static long fuse_dir_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct fuse_conn *fc = get_fuse_conn(file->f_mapping->host); - - /* FUSE_IOCTL_DIR only supported for API version >= 7.18 */ - if (fc->minor < 18) - return -ENOTTY; - - return fuse_ioctl_common(file, cmd, arg, FUSE_IOCTL_DIR); -} - -static long fuse_dir_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct fuse_conn *fc = get_fuse_conn(file->f_mapping->host); - - if (fc->minor < 18) - return -ENOTTY; - - return fuse_ioctl_common(file, cmd, arg, - FUSE_IOCTL_COMPAT | FUSE_IOCTL_DIR); -} - -static bool update_mtime(unsigned ivalid) -{ - /* Always update if mtime is explicitly set */ - if (ivalid & ATTR_MTIME_SET) - return true; - - /* If it's an open(O_TRUNC) or an ftruncate(), don't update */ - if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE))) - return false; - - /* In all other cases update */ - return true; -} - -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; - if (ivalid & ATTR_ATIME) { - arg->valid |= FATTR_ATIME; - arg->atime = iattr->ia_atime.tv_sec; - arg->atimensec = iattr->ia_atime.tv_nsec; - if (!(ivalid & ATTR_ATIME_SET)) - arg->valid |= FATTR_ATIME_NOW; - } - if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) { - arg->valid |= FATTR_MTIME; - arg->mtime = iattr->ia_mtime.tv_sec; - arg->mtimensec = iattr->ia_mtime.tv_nsec; - if (!(ivalid & ATTR_MTIME_SET)) - arg->valid |= FATTR_MTIME_NOW; - } -} - -/* - * Prevent concurrent writepages on inode - * - * This is done by adding a negative bias to the inode write counter - * and waiting for all pending writes to finish. - */ -void fuse_set_nowrite(struct inode *inode) -{ - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_inode *fi = get_fuse_inode(inode); - - BUG_ON(!mutex_is_locked(&inode->i_mutex)); - - spin_lock(&fc->lock); - BUG_ON(fi->writectr < 0); - fi->writectr += FUSE_NOWRITE; - spin_unlock(&fc->lock); - wait_event(fi->page_waitq, fi->writectr == FUSE_NOWRITE); -} - -/* - * Allow writepages on inode - * - * Remove the bias from the writecounter and send any queued - * writepages. - */ -static void __fuse_release_nowrite(struct inode *inode) -{ - struct fuse_inode *fi = get_fuse_inode(inode); - - BUG_ON(fi->writectr != FUSE_NOWRITE); - fi->writectr = 0; - fuse_flush_writepages(inode); -} - -void fuse_release_nowrite(struct inode *inode) -{ - struct fuse_conn *fc = get_fuse_conn(inode); - - spin_lock(&fc->lock); - __fuse_release_nowrite(inode); - spin_unlock(&fc->lock); -} - -/* - * 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_do_setattr(struct dentry *entry, struct iattr *attr, - struct file *file) -{ - struct inode *inode = entry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req; - struct fuse_setattr_in inarg; - struct fuse_attr_out outarg; - bool is_truncate = false; - loff_t oldsize; - int err; - - if (!fuse_allow_task(fc, current)) - return -EACCES; - - if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) - attr->ia_valid |= ATTR_FORCE; - - err = inode_change_ok(inode, attr); - if (err) - return err; - - if (attr->ia_valid & ATTR_OPEN) { - if (fc->atomic_o_trunc) - return 0; - file = NULL; - } - - if (attr->ia_valid & ATTR_SIZE) - is_truncate = true; - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - if (is_truncate) - fuse_set_nowrite(inode); - - memset(&inarg, 0, sizeof(inarg)); - memset(&outarg, 0, sizeof(outarg)); - iattr_to_fattr(attr, &inarg); - if (file) { - struct fuse_file *ff = file->private_data; - inarg.valid |= FATTR_FH; - inarg.fh = ff->fh; - } - if (attr->ia_valid & ATTR_SIZE) { - /* For mandatory locking in truncate */ - inarg.valid |= FATTR_LOCKOWNER; - inarg.lock_owner = fuse_lock_owner_id(fc, current->files); - } - 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; - if (fc->minor < 9) - req->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE; - else - req->out.args[0].size = sizeof(outarg); - req->out.args[0].value = &outarg; - fuse_request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - if (err) { - if (err == -EINTR) - fuse_invalidate_attr(inode); - goto error; - } - - if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) { - make_bad_inode(inode); - err = -EIO; - goto error; - } - - spin_lock(&fc->lock); - fuse_change_attributes_common(inode, &outarg.attr, - attr_timeout(&outarg)); - oldsize = inode->i_size; - i_size_write(inode, outarg.attr.size); - - if (is_truncate) { - /* NOTE: this may release/reacquire fc->lock */ - __fuse_release_nowrite(inode); - } - spin_unlock(&fc->lock); - - /* - * Only call invalidate_inode_pages2() after removing - * FUSE_NOWRITE, otherwise fuse_launder_page() would deadlock. - */ - if (S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) { - truncate_pagecache(inode, oldsize, outarg.attr.size); - invalidate_inode_pages2(inode->i_mapping); - } - - return 0; - -error: - if (is_truncate) - fuse_release_nowrite(inode); - - return err; -} - -static int fuse_setattr(struct dentry *entry, struct iattr *attr) -{ - if (attr->ia_valid & ATTR_FILE) - return fuse_do_setattr(entry, attr, attr->ia_file); - else - return fuse_do_setattr(entry, attr, NULL); -} - -static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, - struct kstat *stat) -{ - struct inode *inode = entry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - - if (!fuse_allow_task(fc, current)) - return -EACCES; - - return fuse_update_attributes(inode, stat, NULL, NULL); -} - -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; - fuse_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; - } - fuse_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; - } - fuse_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; - fuse_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 const 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 const 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, - .unlocked_ioctl = fuse_dir_ioctl, - .compat_ioctl = fuse_dir_compat_ioctl, -}; - -static const 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 const 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/ANDROID_3.4.5/fs/fuse/file.c b/ANDROID_3.4.5/fs/fuse/file.c deleted file mode 100644 index 504e61b7..00000000 --- a/ANDROID_3.4.5/fs/fuse/file.c +++ /dev/null @@ -1,2224 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu> - - This program can be distributed under the terms of the GNU GPL. - See the file COPYING. -*/ - -#include "fuse_i.h" - -#include <linux/pagemap.h> -#include <linux/slab.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/module.h> -#include <linux/compat.h> -#include <linux/swap.h> - -static const struct file_operations fuse_direct_io_file_operations; - -static int fuse_send_open(struct fuse_conn *fc, u64 nodeid, struct file *file, - int opcode, struct fuse_open_out *outargp) -{ - 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); - if (!fc->atomic_o_trunc) - inarg.flags &= ~O_TRUNC; - req->in.h.opcode = opcode; - req->in.h.nodeid = nodeid; - 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; - fuse_request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - - return err; -} - -struct fuse_file *fuse_file_alloc(struct fuse_conn *fc) -{ - struct fuse_file *ff; - - ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL); - if (unlikely(!ff)) - return NULL; - - ff->fc = fc; - ff->reserved_req = fuse_request_alloc(); - if (unlikely(!ff->reserved_req)) { - kfree(ff); - return NULL; - } - - INIT_LIST_HEAD(&ff->write_entry); - atomic_set(&ff->count, 0); - RB_CLEAR_NODE(&ff->polled_node); - init_waitqueue_head(&ff->poll_wait); - - spin_lock(&fc->lock); - ff->kh = ++fc->khctr; - spin_unlock(&fc->lock); - - return ff; -} - -void fuse_file_free(struct fuse_file *ff) -{ - fuse_request_free(ff->reserved_req); - kfree(ff); -} - -struct fuse_file *fuse_file_get(struct fuse_file *ff) -{ - atomic_inc(&ff->count); - return ff; -} - -static void fuse_release_async(struct work_struct *work) -{ - struct fuse_req *req; - struct fuse_conn *fc; - struct path path; - - req = container_of(work, struct fuse_req, misc.release.work); - path = req->misc.release.path; - fc = get_fuse_conn(path.dentry->d_inode); - - fuse_put_request(fc, req); - path_put(&path); -} - -static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req) -{ - if (fc->destroy_req) { - /* - * If this is a fuseblk mount, then it's possible that - * releasing the path will result in releasing the - * super block and sending the DESTROY request. If - * the server is single threaded, this would hang. - * For this reason do the path_put() in a separate - * thread. - */ - atomic_inc(&req->count); - INIT_WORK(&req->misc.release.work, fuse_release_async); - schedule_work(&req->misc.release.work); - } else { - path_put(&req->misc.release.path); - } -} - -static void fuse_file_put(struct fuse_file *ff, bool sync) -{ - if (atomic_dec_and_test(&ff->count)) { - struct fuse_req *req = ff->reserved_req; - - if (sync) { - fuse_request_send(ff->fc, req); - path_put(&req->misc.release.path); - fuse_put_request(ff->fc, req); - } else { - req->end = fuse_release_end; - fuse_request_send_background(ff->fc, req); - } - kfree(ff); - } -} - -int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file, - bool isdir) -{ - struct fuse_open_out outarg; - struct fuse_file *ff; - int err; - int opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN; - - ff = fuse_file_alloc(fc); - if (!ff) - return -ENOMEM; - - err = fuse_send_open(fc, nodeid, file, opcode, &outarg); - if (err) { - fuse_file_free(ff); - return err; - } - - if (isdir) - outarg.open_flags &= ~FOPEN_DIRECT_IO; - - ff->fh = outarg.fh; - ff->nodeid = nodeid; - ff->open_flags = outarg.open_flags; - file->private_data = fuse_file_get(ff); - - return 0; -} -EXPORT_SYMBOL_GPL(fuse_do_open); - -void fuse_finish_open(struct inode *inode, struct file *file) -{ - struct fuse_file *ff = file->private_data; - struct fuse_conn *fc = get_fuse_conn(inode); - - if (ff->open_flags & FOPEN_DIRECT_IO) - file->f_op = &fuse_direct_io_file_operations; - if (!(ff->open_flags & FOPEN_KEEP_CACHE)) - invalidate_inode_pages2(inode->i_mapping); - if (ff->open_flags & FOPEN_NONSEEKABLE) - nonseekable_open(inode, file); - if (fc->atomic_o_trunc && (file->f_flags & O_TRUNC)) { - struct fuse_inode *fi = get_fuse_inode(inode); - - spin_lock(&fc->lock); - fi->attr_version = ++fc->attr_version; - i_size_write(inode, 0); - spin_unlock(&fc->lock); - fuse_invalidate_attr(inode); - } -} - -int fuse_open_common(struct inode *inode, struct file *file, bool isdir) -{ - struct fuse_conn *fc = get_fuse_conn(inode); - int err; - - err = generic_file_open(inode, file); - if (err) - return err; - - err = fuse_do_open(fc, get_node_id(inode), file, isdir); - if (err) - return err; - - fuse_finish_open(inode, file); - - return 0; -} - -static void fuse_prepare_release(struct fuse_file *ff, int flags, int opcode) -{ - struct fuse_conn *fc = ff->fc; - struct fuse_req *req = ff->reserved_req; - struct fuse_release_in *inarg = &req->misc.release.in; - - spin_lock(&fc->lock); - list_del(&ff->write_entry); - if (!RB_EMPTY_NODE(&ff->polled_node)) - rb_erase(&ff->polled_node, &fc->polled_files); - spin_unlock(&fc->lock); - - wake_up_interruptible_all(&ff->poll_wait); - - inarg->fh = ff->fh; - inarg->flags = flags; - req->in.h.opcode = opcode; - req->in.h.nodeid = ff->nodeid; - req->in.numargs = 1; - req->in.args[0].size = sizeof(struct fuse_release_in); - req->in.args[0].value = inarg; -} - -void fuse_release_common(struct file *file, int opcode) -{ - struct fuse_file *ff; - struct fuse_req *req; - - ff = file->private_data; - if (unlikely(!ff)) - return; - - req = ff->reserved_req; - fuse_prepare_release(ff, file->f_flags, opcode); - - if (ff->flock) { - struct fuse_release_in *inarg = &req->misc.release.in; - inarg->release_flags |= FUSE_RELEASE_FLOCK_UNLOCK; - inarg->lock_owner = fuse_lock_owner_id(ff->fc, - (fl_owner_t) file); - } - /* Hold vfsmount and dentry until release is finished */ - path_get(&file->f_path); - req->misc.release.path = file->f_path; - - /* - * Normally this will send the RELEASE request, however if - * some asynchronous READ or WRITE requests are outstanding, - * the sending will be delayed. - * - * Make the release synchronous if this is a fuseblk mount, - * synchronous RELEASE is allowed (and desirable) in this case - * because the server can be trusted not to screw up. - */ - fuse_file_put(ff, ff->fc->destroy_req != NULL); -} - -static int fuse_open(struct inode *inode, struct file *file) -{ - return fuse_open_common(inode, file, false); -} - -static int fuse_release(struct inode *inode, struct file *file) -{ - fuse_release_common(file, FUSE_RELEASE); - - /* return value is ignored by VFS */ - return 0; -} - -void fuse_sync_release(struct fuse_file *ff, int flags) -{ - WARN_ON(atomic_read(&ff->count) > 1); - fuse_prepare_release(ff, flags, FUSE_RELEASE); - ff->reserved_req->force = 1; - fuse_request_send(ff->fc, ff->reserved_req); - fuse_put_request(ff->fc, ff->reserved_req); - kfree(ff); -} -EXPORT_SYMBOL_GPL(fuse_sync_release); - -/* - * Scramble the ID space with XTEA, so that the value of the files_struct - * pointer is not exposed to userspace. - */ -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); -} - -/* - * Check if page is under writeback - * - * This is currently done by walking the list of writepage requests - * for the inode, which can be pretty inefficient. - */ -static bool fuse_page_is_writeback(struct inode *inode, pgoff_t index) -{ - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_inode *fi = get_fuse_inode(inode); - struct fuse_req *req; - bool found = false; - - spin_lock(&fc->lock); - list_for_each_entry(req, &fi->writepages, writepages_entry) { - pgoff_t curr_index; - - BUG_ON(req->inode != inode); - curr_index = req->misc.write.in.offset >> PAGE_CACHE_SHIFT; - if (curr_index == index) { - found = true; - break; - } - } - spin_unlock(&fc->lock); - - return found; -} - -/* - * Wait for page writeback to be completed. - * - * Since fuse doesn't rely on the VM writeback tracking, this has to - * use some other means. - */ -static int fuse_wait_on_page_writeback(struct inode *inode, pgoff_t index) -{ - struct fuse_inode *fi = get_fuse_inode(inode); - - wait_event(fi->page_waitq, !fuse_page_is_writeback(inode, index)); - return 0; -} - -static int fuse_flush(struct file *file, fl_owner_t id) -{ - struct inode *inode = file->f_path.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; - inarg.lock_owner = fuse_lock_owner_id(fc, id); - 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; - fuse_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; -} - -/* - * Wait for all pending writepages on the inode to finish. - * - * This is currently done by blocking further writes with FUSE_NOWRITE - * and waiting for all sent writes to complete. - * - * This must be called under i_mutex, otherwise the FUSE_NOWRITE usage - * could conflict with truncation. - */ -static void fuse_sync_writes(struct inode *inode) -{ - fuse_set_nowrite(inode); - fuse_release_nowrite(inode); -} - -int fuse_fsync_common(struct file *file, loff_t start, loff_t end, - int datasync, int isdir) -{ - struct inode *inode = file->f_mapping->host; - 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; - - err = filemap_write_and_wait_range(inode->i_mapping, start, end); - if (err) - return err; - - if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir)) - return 0; - - mutex_lock(&inode->i_mutex); - - /* - * Start writeback against all dirty pages of the inode, then - * wait for all outstanding writes, before sending the FSYNC - * request. - */ - err = write_inode_now(inode, 0); - if (err) - goto out; - - fuse_sync_writes(inode); - - req = fuse_get_req(fc); - if (IS_ERR(req)) { - err = PTR_ERR(req); - goto out; - } - - 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; - fuse_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; - } -out: - mutex_unlock(&inode->i_mutex); - return err; -} - -static int fuse_fsync(struct file *file, loff_t start, loff_t end, - int datasync) -{ - return fuse_fsync_common(file, start, end, datasync, 0); -} - -void fuse_read_fill(struct fuse_req *req, struct file *file, loff_t pos, - size_t count, int opcode) -{ - struct fuse_read_in *inarg = &req->misc.read.in; - struct fuse_file *ff = file->private_data; - - inarg->fh = ff->fh; - inarg->offset = pos; - inarg->size = count; - inarg->flags = file->f_flags; - req->in.h.opcode = opcode; - req->in.h.nodeid = ff->nodeid; - req->in.numargs = 1; - req->in.args[0].size = sizeof(struct fuse_read_in); - req->in.args[0].value = inarg; - 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, - loff_t pos, size_t count, fl_owner_t owner) -{ - struct fuse_file *ff = file->private_data; - struct fuse_conn *fc = ff->fc; - - fuse_read_fill(req, file, pos, count, FUSE_READ); - if (owner != NULL) { - struct fuse_read_in *inarg = &req->misc.read.in; - - inarg->read_flags |= FUSE_READ_LOCKOWNER; - inarg->lock_owner = fuse_lock_owner_id(fc, owner); - } - fuse_request_send(fc, req); - return req->out.args[0].size; -} - -static void fuse_read_update_size(struct inode *inode, loff_t size, - u64 attr_ver) -{ - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_inode *fi = get_fuse_inode(inode); - - spin_lock(&fc->lock); - if (attr_ver == fi->attr_version && size < inode->i_size) { - fi->attr_version = ++fc->attr_version; - i_size_write(inode, size); - } - spin_unlock(&fc->lock); -} - -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; - size_t num_read; - loff_t pos = page_offset(page); - size_t count = PAGE_CACHE_SIZE; - u64 attr_ver; - int err; - - err = -EIO; - if (is_bad_inode(inode)) - goto out; - - /* - * Page writeback can extend beyond the lifetime of the - * page-cache page, so make sure we read a properly synced - * page. - */ - fuse_wait_on_page_writeback(inode, page->index); - - req = fuse_get_req(fc); - err = PTR_ERR(req); - if (IS_ERR(req)) - goto out; - - attr_ver = fuse_get_attr_version(fc); - - req->out.page_zeroing = 1; - req->out.argpages = 1; - req->num_pages = 1; - req->pages[0] = page; - num_read = fuse_send_read(req, file, pos, count, NULL); - err = req->out.h.error; - fuse_put_request(fc, req); - - if (!err) { - /* - * Short read means EOF. If file size is larger, truncate it - */ - if (num_read < count) - fuse_read_update_size(inode, pos + num_read, attr_ver); - - 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; - size_t count = req->misc.read.in.size; - size_t num_read = req->out.args[0].size; - struct address_space *mapping = NULL; - - for (i = 0; mapping == NULL && i < req->num_pages; i++) - mapping = req->pages[i]->mapping; - - if (mapping) { - struct inode *inode = mapping->host; - - /* - * Short read means EOF. If file size is larger, truncate it - */ - if (!req->out.h.error && num_read < count) { - loff_t pos; - - pos = page_offset(req->pages[0]) + num_read; - fuse_read_update_size(inode, pos, - req->misc.read.attr_ver); - } - fuse_invalidate_attr(inode); /* 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); - page_cache_release(page); - } - if (req->ff) - fuse_file_put(req->ff, false); -} - -static void fuse_send_readpages(struct fuse_req *req, struct file *file) -{ - struct fuse_file *ff = file->private_data; - struct fuse_conn *fc = ff->fc; - loff_t pos = page_offset(req->pages[0]); - size_t count = req->num_pages << PAGE_CACHE_SHIFT; - - req->out.argpages = 1; - req->out.page_zeroing = 1; - req->out.page_replace = 1; - fuse_read_fill(req, file, pos, count, FUSE_READ); - req->misc.read.attr_ver = fuse_get_attr_version(fc); - if (fc->async_read) { - req->ff = fuse_file_get(ff); - req->end = fuse_readpages_end; - fuse_request_send_background(fc, req); - } else { - fuse_request_send(fc, req); - fuse_readpages_end(fc, req); - fuse_put_request(fc, req); - } -} - -struct fuse_fill_data { - struct fuse_req *req; - struct file *file; - struct inode *inode; -}; - -static int fuse_readpages_fill(void *_data, struct page *page) -{ - struct fuse_fill_data *data = _data; - struct fuse_req *req = data->req; - struct inode *inode = data->inode; - struct fuse_conn *fc = get_fuse_conn(inode); - - fuse_wait_on_page_writeback(inode, page->index); - - if (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); - data->req = req = fuse_get_req(fc); - if (IS_ERR(req)) { - unlock_page(page); - return PTR_ERR(req); - } - } - page_cache_get(page); - req->pages[req->num_pages] = page; - req->num_pages++; - return 0; -} - -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_fill_data data; - int err; - - err = -EIO; - if (is_bad_inode(inode)) - goto out; - - data.file = file; - data.inode = inode; - data.req = fuse_get_req(fc); - err = PTR_ERR(data.req); - if (IS_ERR(data.req)) - goto out; - - err = read_cache_pages(mapping, pages, fuse_readpages_fill, &data); - if (!err) { - if (data.req->num_pages) - fuse_send_readpages(data.req, file); - else - fuse_put_request(fc, data.req); - } -out: - return err; -} - -static ssize_t fuse_file_aio_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - struct inode *inode = iocb->ki_filp->f_mapping->host; - - if (pos + iov_length(iov, nr_segs) > i_size_read(inode)) { - int err; - /* - * If trying to read past EOF, make sure the i_size - * attribute is up-to-date. - */ - err = fuse_update_attributes(inode, NULL, iocb->ki_filp, NULL); - if (err) - return err; - } - - return generic_file_aio_read(iocb, iov, nr_segs, pos); -} - -static void fuse_write_fill(struct fuse_req *req, struct fuse_file *ff, - loff_t pos, size_t count) -{ - struct fuse_write_in *inarg = &req->misc.write.in; - struct fuse_write_out *outarg = &req->misc.write.out; - - inarg->fh = ff->fh; - inarg->offset = pos; - inarg->size = count; - req->in.h.opcode = FUSE_WRITE; - req->in.h.nodeid = ff->nodeid; - req->in.numargs = 2; - if (ff->fc->minor < 9) - req->in.args[0].size = FUSE_COMPAT_WRITE_IN_SIZE; - else - 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; -} - -static size_t fuse_send_write(struct fuse_req *req, struct file *file, - loff_t pos, size_t count, fl_owner_t owner) -{ - struct fuse_file *ff = file->private_data; - struct fuse_conn *fc = ff->fc; - struct fuse_write_in *inarg = &req->misc.write.in; - - fuse_write_fill(req, ff, pos, count); - inarg->flags = file->f_flags; - if (owner != NULL) { - inarg->write_flags |= FUSE_WRITE_LOCKOWNER; - inarg->lock_owner = fuse_lock_owner_id(fc, owner); - } - fuse_request_send(fc, req); - return req->misc.write.out.size; -} - -void fuse_write_update_size(struct inode *inode, loff_t pos) -{ - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_inode *fi = get_fuse_inode(inode); - - spin_lock(&fc->lock); - fi->attr_version = ++fc->attr_version; - if (pos > inode->i_size) - i_size_write(inode, pos); - spin_unlock(&fc->lock); -} - -static size_t fuse_send_write_pages(struct fuse_req *req, struct file *file, - struct inode *inode, loff_t pos, - size_t count) -{ - size_t res; - unsigned offset; - unsigned i; - - for (i = 0; i < req->num_pages; i++) - fuse_wait_on_page_writeback(inode, req->pages[i]->index); - - res = fuse_send_write(req, file, pos, count, NULL); - - offset = req->page_offset; - count = res; - for (i = 0; i < req->num_pages; i++) { - struct page *page = req->pages[i]; - - if (!req->out.h.error && !offset && count >= PAGE_CACHE_SIZE) - SetPageUptodate(page); - - if (count > PAGE_CACHE_SIZE - offset) - count -= PAGE_CACHE_SIZE - offset; - else - count = 0; - offset = 0; - - unlock_page(page); - page_cache_release(page); - } - - return res; -} - -static ssize_t fuse_fill_write_pages(struct fuse_req *req, - struct address_space *mapping, - struct iov_iter *ii, loff_t pos) -{ - struct fuse_conn *fc = get_fuse_conn(mapping->host); - unsigned offset = pos & (PAGE_CACHE_SIZE - 1); - size_t count = 0; - int err; - - req->in.argpages = 1; - req->page_offset = offset; - - do { - size_t tmp; - struct page *page; - pgoff_t index = pos >> PAGE_CACHE_SHIFT; - size_t bytes = min_t(size_t, PAGE_CACHE_SIZE - offset, - iov_iter_count(ii)); - - bytes = min_t(size_t, bytes, fc->max_write - count); - - again: - err = -EFAULT; - if (iov_iter_fault_in_readable(ii, bytes)) - break; - - err = -ENOMEM; - page = grab_cache_page_write_begin(mapping, index, 0); - if (!page) - break; - - if (mapping_writably_mapped(mapping)) - flush_dcache_page(page); - - pagefault_disable(); - tmp = iov_iter_copy_from_user_atomic(page, ii, offset, bytes); - pagefault_enable(); - flush_dcache_page(page); - - mark_page_accessed(page); - - if (!tmp) { - unlock_page(page); - page_cache_release(page); - bytes = min(bytes, iov_iter_single_seg_count(ii)); - goto again; - } - - err = 0; - req->pages[req->num_pages] = page; - req->num_pages++; - - iov_iter_advance(ii, tmp); - count += tmp; - pos += tmp; - offset += tmp; - if (offset == PAGE_CACHE_SIZE) - offset = 0; - - if (!fc->big_writes) - break; - } while (iov_iter_count(ii) && count < fc->max_write && - req->num_pages < FUSE_MAX_PAGES_PER_REQ && offset == 0); - - return count > 0 ? count : err; -} - -static ssize_t fuse_perform_write(struct file *file, - struct address_space *mapping, - struct iov_iter *ii, loff_t pos) -{ - struct inode *inode = mapping->host; - struct fuse_conn *fc = get_fuse_conn(inode); - int err = 0; - ssize_t res = 0; - - if (is_bad_inode(inode)) - return -EIO; - - do { - struct fuse_req *req; - ssize_t count; - - req = fuse_get_req(fc); - if (IS_ERR(req)) { - err = PTR_ERR(req); - break; - } - - count = fuse_fill_write_pages(req, mapping, ii, pos); - if (count <= 0) { - err = count; - } else { - size_t num_written; - - num_written = fuse_send_write_pages(req, file, inode, - pos, count); - err = req->out.h.error; - if (!err) { - res += num_written; - pos += num_written; - - /* break out of the loop on short write */ - if (num_written != count) - err = -EIO; - } - } - fuse_put_request(fc, req); - } while (!err && iov_iter_count(ii)); - - if (res > 0) - fuse_write_update_size(inode, pos); - - fuse_invalidate_attr(inode); - - return res > 0 ? res : err; -} - -static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - struct file *file = iocb->ki_filp; - struct address_space *mapping = file->f_mapping; - size_t count = 0; - size_t ocount = 0; - ssize_t written = 0; - ssize_t written_buffered = 0; - struct inode *inode = mapping->host; - ssize_t err; - struct iov_iter i; - loff_t endbyte = 0; - - WARN_ON(iocb->ki_pos != pos); - - ocount = 0; - err = generic_segment_checks(iov, &nr_segs, &ocount, VERIFY_READ); - if (err) - return err; - - count = ocount; - - mutex_lock(&inode->i_mutex); - vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE); - - /* We can write back this queue in page reclaim */ - current->backing_dev_info = mapping->backing_dev_info; - - err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); - if (err) - goto out; - - if (count == 0) - goto out; - - err = file_remove_suid(file); - if (err) - goto out; - - file_update_time(file); - - if (file->f_flags & O_DIRECT) { - written = generic_file_direct_write(iocb, iov, &nr_segs, - pos, &iocb->ki_pos, - count, ocount); - if (written < 0 || written == count) - goto out; - - pos += written; - count -= written; - - iov_iter_init(&i, iov, nr_segs, count, written); - written_buffered = fuse_perform_write(file, mapping, &i, pos); - if (written_buffered < 0) { - err = written_buffered; - goto out; - } - endbyte = pos + written_buffered - 1; - - err = filemap_write_and_wait_range(file->f_mapping, pos, - endbyte); - if (err) - goto out; - - invalidate_mapping_pages(file->f_mapping, - pos >> PAGE_CACHE_SHIFT, - endbyte >> PAGE_CACHE_SHIFT); - - written += written_buffered; - iocb->ki_pos = pos + written_buffered; - } else { - iov_iter_init(&i, iov, nr_segs, count, 0); - written = fuse_perform_write(file, mapping, &i, pos); - if (written >= 0) - iocb->ki_pos = pos + written; - } -out: - current->backing_dev_info = NULL; - mutex_unlock(&inode->i_mutex); - - return written ? written : 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, - size_t *nbytesp, int write) -{ - size_t nbytes = *nbytesp; - unsigned long user_addr = (unsigned long) buf; - unsigned offset = user_addr & ~PAGE_MASK; - int npages; - - /* Special case for kernel I/O: can copy directly into the buffer */ - if (segment_eq(get_fs(), KERNEL_DS)) { - if (write) - req->in.args[1].value = (void *) user_addr; - else - req->out.args[0].value = (void *) user_addr; - - return 0; - } - - nbytes = min_t(size_t, nbytes, FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT); - npages = (nbytes + offset + PAGE_SIZE - 1) >> PAGE_SHIFT; - npages = clamp(npages, 1, FUSE_MAX_PAGES_PER_REQ); - npages = get_user_pages_fast(user_addr, npages, !write, req->pages); - if (npages < 0) - return npages; - - req->num_pages = npages; - req->page_offset = offset; - - if (write) - req->in.argpages = 1; - else - req->out.argpages = 1; - - nbytes = (req->num_pages << PAGE_SHIFT) - req->page_offset; - *nbytesp = min(*nbytesp, nbytes); - - return 0; -} - -ssize_t fuse_direct_io(struct file *file, const char __user *buf, - size_t count, loff_t *ppos, int write) -{ - struct fuse_file *ff = file->private_data; - struct fuse_conn *fc = ff->fc; - size_t nmax = write ? fc->max_write : fc->max_read; - loff_t pos = *ppos; - ssize_t res = 0; - struct fuse_req *req; - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - while (count) { - size_t nres; - fl_owner_t owner = current->files; - size_t nbytes = min(count, nmax); - int err = fuse_get_user_pages(req, buf, &nbytes, write); - if (err) { - res = err; - break; - } - - if (write) - nres = fuse_send_write(req, file, pos, nbytes, owner); - else - nres = fuse_send_read(req, file, pos, nbytes, owner); - - 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; - } - } - if (!IS_ERR(req)) - fuse_put_request(fc, req); - if (res > 0) - *ppos = pos; - - return res; -} -EXPORT_SYMBOL_GPL(fuse_direct_io); - -static ssize_t fuse_direct_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - ssize_t res; - struct inode *inode = file->f_path.dentry->d_inode; - - if (is_bad_inode(inode)) - return -EIO; - - res = fuse_direct_io(file, buf, count, ppos, 0); - - fuse_invalidate_attr(inode); - - return res; -} - -static ssize_t __fuse_direct_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct inode *inode = file->f_path.dentry->d_inode; - ssize_t res; - - res = generic_write_checks(file, ppos, &count, 0); - if (!res) { - res = fuse_direct_io(file, buf, count, ppos, 1); - if (res > 0) - fuse_write_update_size(inode, *ppos); - } - - fuse_invalidate_attr(inode); - - return res; -} - -static ssize_t fuse_direct_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct inode *inode = file->f_path.dentry->d_inode; - ssize_t res; - - if (is_bad_inode(inode)) - return -EIO; - - /* Don't allow parallel writes to the same file */ - mutex_lock(&inode->i_mutex); - res = __fuse_direct_write(file, buf, count, ppos); - mutex_unlock(&inode->i_mutex); - - return res; -} - -static void fuse_writepage_free(struct fuse_conn *fc, struct fuse_req *req) -{ - __free_page(req->pages[0]); - fuse_file_put(req->ff, false); -} - -static void fuse_writepage_finish(struct fuse_conn *fc, struct fuse_req *req) -{ - struct inode *inode = req->inode; - struct fuse_inode *fi = get_fuse_inode(inode); - struct backing_dev_info *bdi = inode->i_mapping->backing_dev_info; - - list_del(&req->writepages_entry); - dec_bdi_stat(bdi, BDI_WRITEBACK); - dec_zone_page_state(req->pages[0], NR_WRITEBACK_TEMP); - bdi_writeout_inc(bdi); - wake_up(&fi->page_waitq); -} - -/* Called under fc->lock, may release and reacquire it */ -static void fuse_send_writepage(struct fuse_conn *fc, struct fuse_req *req) -__releases(fc->lock) -__acquires(fc->lock) -{ - struct fuse_inode *fi = get_fuse_inode(req->inode); - loff_t size = i_size_read(req->inode); - struct fuse_write_in *inarg = &req->misc.write.in; - - if (!fc->connected) - goto out_free; - - if (inarg->offset + PAGE_CACHE_SIZE <= size) { - inarg->size = PAGE_CACHE_SIZE; - } else if (inarg->offset < size) { - inarg->size = size & (PAGE_CACHE_SIZE - 1); - } else { - /* Got truncated off completely */ - goto out_free; - } - - req->in.args[1].size = inarg->size; - fi->writectr++; - fuse_request_send_background_locked(fc, req); - return; - - out_free: - fuse_writepage_finish(fc, req); - spin_unlock(&fc->lock); - fuse_writepage_free(fc, req); - fuse_put_request(fc, req); - spin_lock(&fc->lock); -} - -/* - * If fi->writectr is positive (no truncate or fsync going on) send - * all queued writepage requests. - * - * Called with fc->lock - */ -void fuse_flush_writepages(struct inode *inode) -__releases(fc->lock) -__acquires(fc->lock) -{ - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_inode *fi = get_fuse_inode(inode); - struct fuse_req *req; - - while (fi->writectr >= 0 && !list_empty(&fi->queued_writes)) { - req = list_entry(fi->queued_writes.next, struct fuse_req, list); - list_del_init(&req->list); - fuse_send_writepage(fc, req); - } -} - -static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_req *req) -{ - struct inode *inode = req->inode; - struct fuse_inode *fi = get_fuse_inode(inode); - - mapping_set_error(inode->i_mapping, req->out.h.error); - spin_lock(&fc->lock); - fi->writectr--; - fuse_writepage_finish(fc, req); - spin_unlock(&fc->lock); - fuse_writepage_free(fc, req); -} - -static int fuse_writepage_locked(struct page *page) -{ - struct address_space *mapping = page->mapping; - struct inode *inode = mapping->host; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_inode *fi = get_fuse_inode(inode); - struct fuse_req *req; - struct fuse_file *ff; - struct page *tmp_page; - - set_page_writeback(page); - - req = fuse_request_alloc_nofs(); - if (!req) - goto err; - - tmp_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); - if (!tmp_page) - goto err_free; - - spin_lock(&fc->lock); - BUG_ON(list_empty(&fi->write_files)); - ff = list_entry(fi->write_files.next, struct fuse_file, write_entry); - req->ff = fuse_file_get(ff); - spin_unlock(&fc->lock); - - fuse_write_fill(req, ff, page_offset(page), 0); - - copy_highpage(tmp_page, page); - req->misc.write.in.write_flags |= FUSE_WRITE_CACHE; - req->in.argpages = 1; - req->num_pages = 1; - req->pages[0] = tmp_page; - req->page_offset = 0; - req->end = fuse_writepage_end; - req->inode = inode; - - inc_bdi_stat(mapping->backing_dev_info, BDI_WRITEBACK); - inc_zone_page_state(tmp_page, NR_WRITEBACK_TEMP); - end_page_writeback(page); - - spin_lock(&fc->lock); - list_add(&req->writepages_entry, &fi->writepages); - list_add_tail(&req->list, &fi->queued_writes); - fuse_flush_writepages(inode); - spin_unlock(&fc->lock); - - return 0; - -err_free: - fuse_request_free(req); -err: - end_page_writeback(page); - return -ENOMEM; -} - -static int fuse_writepage(struct page *page, struct writeback_control *wbc) -{ - int err; - - err = fuse_writepage_locked(page); - unlock_page(page); - - return err; -} - -static int fuse_launder_page(struct page *page) -{ - int err = 0; - if (clear_page_dirty_for_io(page)) { - struct inode *inode = page->mapping->host; - err = fuse_writepage_locked(page); - if (!err) - fuse_wait_on_page_writeback(inode, page->index); - } - return err; -} - -/* - * Write back dirty pages now, because there may not be any suitable - * open files later - */ -static void fuse_vma_close(struct vm_area_struct *vma) -{ - filemap_write_and_wait(vma->vm_file->f_mapping); -} - -/* - * Wait for writeback against this page to complete before allowing it - * to be marked dirty again, and hence written back again, possibly - * before the previous writepage completed. - * - * Block here, instead of in ->writepage(), so that the userspace fs - * can only block processes actually operating on the filesystem. - * - * Otherwise unprivileged userspace fs would be able to block - * unrelated: - * - * - page migration - * - sync(2) - * - try_to_free_pages() with order > PAGE_ALLOC_COSTLY_ORDER - */ -static int fuse_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - struct page *page = vmf->page; - /* - * Don't use page->mapping as it may become NULL from a - * concurrent truncate. - */ - struct inode *inode = vma->vm_file->f_mapping->host; - - fuse_wait_on_page_writeback(inode, page->index); - return 0; -} - -static const struct vm_operations_struct fuse_file_vm_ops = { - .close = fuse_vma_close, - .fault = filemap_fault, - .page_mkwrite = fuse_page_mkwrite, -}; - -static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma) -{ - if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) { - struct inode *inode = file->f_dentry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_inode *fi = get_fuse_inode(inode); - struct fuse_file *ff = file->private_data; - /* - * file may be written through mmap, so chain it onto the - * inodes's write_file list - */ - spin_lock(&fc->lock); - if (list_empty(&ff->write_entry)) - list_add(&ff->write_entry, &fi->write_files); - spin_unlock(&fc->lock); - } - file_accessed(file); - vma->vm_ops = &fuse_file_vm_ops; - return 0; -} - -static int fuse_direct_mmap(struct file *file, struct vm_area_struct *vma) -{ - /* Can't provide the coherency needed for MAP_SHARED */ - if (vma->vm_flags & VM_MAYSHARE) - return -ENODEV; - - invalidate_inode_pages2(file->f_mapping); - - return generic_file_mmap(file, vma); -} - -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, - int flock) -{ - struct inode *inode = file->f_path.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; - if (flock) - arg->lk_flags |= FUSE_LK_FLOCK; - 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_path.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, 0); - req->out.numargs = 1; - req->out.args[0].size = sizeof(outarg); - req->out.args[0].value = &outarg; - fuse_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, int flock) -{ - struct inode *inode = file->f_path.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; - - if (fl->fl_lmops && fl->fl_lmops->lm_grant) { - /* NLM needs asynchronous locks, which we don't support yet */ - return -ENOLCK; - } - - /* Unlock on close is handled by the flush method */ - if (fl->fl_flags & FL_CLOSE) - return 0; - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - - fuse_lk_fill(req, file, fl, opcode, pid, flock); - fuse_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_path.dentry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - int err; - - if (cmd == F_CANCELLK) { - err = 0; - } else if (cmd == F_GETLK) { - if (fc->no_lock) { - posix_test_lock(file, fl); - err = 0; - } else - err = fuse_getlk(file, fl); - } else { - if (fc->no_lock) - err = posix_lock_file(file, fl, NULL); - else - err = fuse_setlk(file, fl, 0); - } - return err; -} - -static int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl) -{ - struct inode *inode = file->f_path.dentry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - int err; - - if (fc->no_flock) { - err = flock_lock_file_wait(file, fl); - } else { - struct fuse_file *ff = file->private_data; - - /* emulate flock with POSIX locks */ - fl->fl_owner = (fl_owner_t) file; - ff->flock = true; - err = fuse_setlk(file, fl, 1); - } - - 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; - fuse_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 loff_t fuse_file_llseek(struct file *file, loff_t offset, int origin) -{ - loff_t retval; - struct inode *inode = file->f_path.dentry->d_inode; - - /* No i_mutex protection necessary for SEEK_CUR and SEEK_SET */ - if (origin == SEEK_CUR || origin == SEEK_SET) - return generic_file_llseek(file, offset, origin); - - mutex_lock(&inode->i_mutex); - retval = fuse_update_attributes(inode, NULL, file, NULL); - if (!retval) - retval = generic_file_llseek(file, offset, origin); - mutex_unlock(&inode->i_mutex); - - return retval; -} - -static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov, - unsigned int nr_segs, size_t bytes, bool to_user) -{ - struct iov_iter ii; - int page_idx = 0; - - if (!bytes) - return 0; - - iov_iter_init(&ii, iov, nr_segs, bytes, 0); - - while (iov_iter_count(&ii)) { - struct page *page = pages[page_idx++]; - size_t todo = min_t(size_t, PAGE_SIZE, iov_iter_count(&ii)); - void *kaddr; - - kaddr = kmap(page); - - while (todo) { - char __user *uaddr = ii.iov->iov_base + ii.iov_offset; - size_t iov_len = ii.iov->iov_len - ii.iov_offset; - size_t copy = min(todo, iov_len); - size_t left; - - if (!to_user) - left = copy_from_user(kaddr, uaddr, copy); - else - left = copy_to_user(uaddr, kaddr, copy); - - if (unlikely(left)) - return -EFAULT; - - iov_iter_advance(&ii, copy); - todo -= copy; - kaddr += copy; - } - - kunmap(page); - } - - return 0; -} - -/* - * CUSE servers compiled on 32bit broke on 64bit kernels because the - * ABI was defined to be 'struct iovec' which is different on 32bit - * and 64bit. Fortunately we can determine which structure the server - * used from the size of the reply. - */ -static int fuse_copy_ioctl_iovec_old(struct iovec *dst, void *src, - size_t transferred, unsigned count, - bool is_compat) -{ -#ifdef CONFIG_COMPAT - if (count * sizeof(struct compat_iovec) == transferred) { - struct compat_iovec *ciov = src; - unsigned i; - - /* - * With this interface a 32bit server cannot support - * non-compat (i.e. ones coming from 64bit apps) ioctl - * requests - */ - if (!is_compat) - return -EINVAL; - - for (i = 0; i < count; i++) { - dst[i].iov_base = compat_ptr(ciov[i].iov_base); - dst[i].iov_len = ciov[i].iov_len; - } - return 0; - } -#endif - - if (count * sizeof(struct iovec) != transferred) - return -EIO; - - memcpy(dst, src, transferred); - return 0; -} - -/* Make sure iov_length() won't overflow */ -static int fuse_verify_ioctl_iov(struct iovec *iov, size_t count) -{ - size_t n; - u32 max = FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT; - - for (n = 0; n < count; n++) { - if (iov->iov_len > (size_t) max) - return -ENOMEM; - max -= iov->iov_len; - } - return 0; -} - -static int fuse_copy_ioctl_iovec(struct fuse_conn *fc, struct iovec *dst, - void *src, size_t transferred, unsigned count, - bool is_compat) -{ - unsigned i; - struct fuse_ioctl_iovec *fiov = src; - - if (fc->minor < 16) { - return fuse_copy_ioctl_iovec_old(dst, src, transferred, - count, is_compat); - } - - if (count * sizeof(struct fuse_ioctl_iovec) != transferred) - return -EIO; - - for (i = 0; i < count; i++) { - /* Did the server supply an inappropriate value? */ - if (fiov[i].base != (unsigned long) fiov[i].base || - fiov[i].len != (unsigned long) fiov[i].len) - return -EIO; - - dst[i].iov_base = (void __user *) (unsigned long) fiov[i].base; - dst[i].iov_len = (size_t) fiov[i].len; - -#ifdef CONFIG_COMPAT - if (is_compat && - (ptr_to_compat(dst[i].iov_base) != fiov[i].base || - (compat_size_t) dst[i].iov_len != fiov[i].len)) - return -EIO; -#endif - } - - return 0; -} - - -/* - * For ioctls, there is no generic way to determine how much memory - * needs to be read and/or written. Furthermore, ioctls are allowed - * to dereference the passed pointer, so the parameter requires deep - * copying but FUSE has no idea whatsoever about what to copy in or - * out. - * - * This is solved by allowing FUSE server to retry ioctl with - * necessary in/out iovecs. Let's assume the ioctl implementation - * needs to read in the following structure. - * - * struct a { - * char *buf; - * size_t buflen; - * } - * - * On the first callout to FUSE server, inarg->in_size and - * inarg->out_size will be NULL; then, the server completes the ioctl - * with FUSE_IOCTL_RETRY set in out->flags, out->in_iovs set to 1 and - * the actual iov array to - * - * { { .iov_base = inarg.arg, .iov_len = sizeof(struct a) } } - * - * which tells FUSE to copy in the requested area and retry the ioctl. - * On the second round, the server has access to the structure and - * from that it can tell what to look for next, so on the invocation, - * it sets FUSE_IOCTL_RETRY, out->in_iovs to 2 and iov array to - * - * { { .iov_base = inarg.arg, .iov_len = sizeof(struct a) }, - * { .iov_base = a.buf, .iov_len = a.buflen } } - * - * FUSE will copy both struct a and the pointed buffer from the - * process doing the ioctl and retry ioctl with both struct a and the - * buffer. - * - * This time, FUSE server has everything it needs and completes ioctl - * without FUSE_IOCTL_RETRY which finishes the ioctl call. - * - * Copying data out works the same way. - * - * Note that if FUSE_IOCTL_UNRESTRICTED is clear, the kernel - * automatically initializes in and out iovs by decoding @cmd with - * _IOC_* macros and the server is not allowed to request RETRY. This - * limits ioctl data transfers to well-formed ioctls and is the forced - * behavior for all FUSE servers. - */ -long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, - unsigned int flags) -{ - struct fuse_file *ff = file->private_data; - struct fuse_conn *fc = ff->fc; - struct fuse_ioctl_in inarg = { - .fh = ff->fh, - .cmd = cmd, - .arg = arg, - .flags = flags - }; - struct fuse_ioctl_out outarg; - struct fuse_req *req = NULL; - struct page **pages = NULL; - struct iovec *iov_page = NULL; - struct iovec *in_iov = NULL, *out_iov = NULL; - unsigned int in_iovs = 0, out_iovs = 0, num_pages = 0, max_pages; - size_t in_size, out_size, transferred; - int err; - -#if BITS_PER_LONG == 32 - inarg.flags |= FUSE_IOCTL_32BIT; -#else - if (flags & FUSE_IOCTL_COMPAT) - inarg.flags |= FUSE_IOCTL_32BIT; -#endif - - /* assume all the iovs returned by client always fits in a page */ - BUILD_BUG_ON(sizeof(struct fuse_ioctl_iovec) * FUSE_IOCTL_MAX_IOV > PAGE_SIZE); - - err = -ENOMEM; - pages = kcalloc(FUSE_MAX_PAGES_PER_REQ, sizeof(pages[0]), GFP_KERNEL); - iov_page = (struct iovec *) __get_free_page(GFP_KERNEL); - if (!pages || !iov_page) - goto out; - - /* - * If restricted, initialize IO parameters as encoded in @cmd. - * RETRY from server is not allowed. - */ - if (!(flags & FUSE_IOCTL_UNRESTRICTED)) { - struct iovec *iov = iov_page; - - iov->iov_base = (void __user *)arg; - iov->iov_len = _IOC_SIZE(cmd); - - if (_IOC_DIR(cmd) & _IOC_WRITE) { - in_iov = iov; - in_iovs = 1; - } - - if (_IOC_DIR(cmd) & _IOC_READ) { - out_iov = iov; - out_iovs = 1; - } - } - - retry: - inarg.in_size = in_size = iov_length(in_iov, in_iovs); - inarg.out_size = out_size = iov_length(out_iov, out_iovs); - - /* - * Out data can be used either for actual out data or iovs, - * make sure there always is at least one page. - */ - out_size = max_t(size_t, out_size, PAGE_SIZE); - max_pages = DIV_ROUND_UP(max(in_size, out_size), PAGE_SIZE); - - /* make sure there are enough buffer pages and init request with them */ - err = -ENOMEM; - if (max_pages > FUSE_MAX_PAGES_PER_REQ) - goto out; - while (num_pages < max_pages) { - pages[num_pages] = alloc_page(GFP_KERNEL | __GFP_HIGHMEM); - if (!pages[num_pages]) - goto out; - num_pages++; - } - - req = fuse_get_req(fc); - if (IS_ERR(req)) { - err = PTR_ERR(req); - req = NULL; - goto out; - } - memcpy(req->pages, pages, sizeof(req->pages[0]) * num_pages); - req->num_pages = num_pages; - - /* okay, let's send it to the client */ - req->in.h.opcode = FUSE_IOCTL; - req->in.h.nodeid = ff->nodeid; - req->in.numargs = 1; - req->in.args[0].size = sizeof(inarg); - req->in.args[0].value = &inarg; - if (in_size) { - req->in.numargs++; - req->in.args[1].size = in_size; - req->in.argpages = 1; - - err = fuse_ioctl_copy_user(pages, in_iov, in_iovs, in_size, - false); - if (err) - goto out; - } - - req->out.numargs = 2; - req->out.args[0].size = sizeof(outarg); - req->out.args[0].value = &outarg; - req->out.args[1].size = out_size; - req->out.argpages = 1; - req->out.argvar = 1; - - fuse_request_send(fc, req); - err = req->out.h.error; - transferred = req->out.args[1].size; - fuse_put_request(fc, req); - req = NULL; - if (err) - goto out; - - /* did it ask for retry? */ - if (outarg.flags & FUSE_IOCTL_RETRY) { - void *vaddr; - - /* no retry if in restricted mode */ - err = -EIO; - if (!(flags & FUSE_IOCTL_UNRESTRICTED)) - goto out; - - in_iovs = outarg.in_iovs; - out_iovs = outarg.out_iovs; - - /* - * Make sure things are in boundary, separate checks - * are to protect against overflow. - */ - err = -ENOMEM; - if (in_iovs > FUSE_IOCTL_MAX_IOV || - out_iovs > FUSE_IOCTL_MAX_IOV || - in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV) - goto out; - - vaddr = kmap_atomic(pages[0]); - err = fuse_copy_ioctl_iovec(fc, iov_page, vaddr, - transferred, in_iovs + out_iovs, - (flags & FUSE_IOCTL_COMPAT) != 0); - kunmap_atomic(vaddr); - if (err) - goto out; - - in_iov = iov_page; - out_iov = in_iov + in_iovs; - - err = fuse_verify_ioctl_iov(in_iov, in_iovs); - if (err) - goto out; - - err = fuse_verify_ioctl_iov(out_iov, out_iovs); - if (err) - goto out; - - goto retry; - } - - err = -EIO; - if (transferred > inarg.out_size) - goto out; - - err = fuse_ioctl_copy_user(pages, out_iov, out_iovs, transferred, true); - out: - if (req) - fuse_put_request(fc, req); - free_page((unsigned long) iov_page); - while (num_pages) - __free_page(pages[--num_pages]); - kfree(pages); - - return err ? err : outarg.result; -} -EXPORT_SYMBOL_GPL(fuse_do_ioctl); - -long fuse_ioctl_common(struct file *file, unsigned int cmd, - unsigned long arg, unsigned int flags) -{ - struct inode *inode = file->f_dentry->d_inode; - struct fuse_conn *fc = get_fuse_conn(inode); - - if (!fuse_allow_task(fc, current)) - return -EACCES; - - if (is_bad_inode(inode)) - return -EIO; - - return fuse_do_ioctl(file, cmd, arg, flags); -} - -static long fuse_file_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - return fuse_ioctl_common(file, cmd, arg, 0); -} - -static long fuse_file_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - return fuse_ioctl_common(file, cmd, arg, FUSE_IOCTL_COMPAT); -} - -/* - * All files which have been polled are linked to RB tree - * fuse_conn->polled_files which is indexed by kh. Walk the tree and - * find the matching one. - */ -static struct rb_node **fuse_find_polled_node(struct fuse_conn *fc, u64 kh, - struct rb_node **parent_out) -{ - struct rb_node **link = &fc->polled_files.rb_node; - struct rb_node *last = NULL; - - while (*link) { - struct fuse_file *ff; - - last = *link; - ff = rb_entry(last, struct fuse_file, polled_node); - - if (kh < ff->kh) - link = &last->rb_left; - else if (kh > ff->kh) - link = &last->rb_right; - else - return link; - } - - if (parent_out) - *parent_out = last; - return link; -} - -/* - * The file is about to be polled. Make sure it's on the polled_files - * RB tree. Note that files once added to the polled_files tree are - * not removed before the file is released. This is because a file - * polled once is likely to be polled again. - */ -static void fuse_register_polled_file(struct fuse_conn *fc, - struct fuse_file *ff) -{ - spin_lock(&fc->lock); - if (RB_EMPTY_NODE(&ff->polled_node)) { - struct rb_node **link, *parent; - - link = fuse_find_polled_node(fc, ff->kh, &parent); - BUG_ON(*link); - rb_link_node(&ff->polled_node, parent, link); - rb_insert_color(&ff->polled_node, &fc->polled_files); - } - spin_unlock(&fc->lock); -} - -unsigned fuse_file_poll(struct file *file, poll_table *wait) -{ - struct fuse_file *ff = file->private_data; - struct fuse_conn *fc = ff->fc; - struct fuse_poll_in inarg = { .fh = ff->fh, .kh = ff->kh }; - struct fuse_poll_out outarg; - struct fuse_req *req; - int err; - - if (fc->no_poll) - return DEFAULT_POLLMASK; - - poll_wait(file, &ff->poll_wait, wait); - - /* - * Ask for notification iff there's someone waiting for it. - * The client may ignore the flag and always notify. - */ - if (waitqueue_active(&ff->poll_wait)) { - inarg.flags |= FUSE_POLL_SCHEDULE_NOTIFY; - fuse_register_polled_file(fc, ff); - } - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return POLLERR; - - req->in.h.opcode = FUSE_POLL; - req->in.h.nodeid = ff->nodeid; - 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; - fuse_request_send(fc, req); - err = req->out.h.error; - fuse_put_request(fc, req); - - if (!err) - return outarg.revents; - if (err == -ENOSYS) { - fc->no_poll = 1; - return DEFAULT_POLLMASK; - } - return POLLERR; -} -EXPORT_SYMBOL_GPL(fuse_file_poll); - -/* - * This is called from fuse_handle_notify() on FUSE_NOTIFY_POLL and - * wakes up the poll waiters. - */ -int fuse_notify_poll_wakeup(struct fuse_conn *fc, - struct fuse_notify_poll_wakeup_out *outarg) -{ - u64 kh = outarg->kh; - struct rb_node **link; - - spin_lock(&fc->lock); - - link = fuse_find_polled_node(fc, kh, NULL); - if (*link) { - struct fuse_file *ff; - - ff = rb_entry(*link, struct fuse_file, polled_node); - wake_up_interruptible_sync(&ff->poll_wait); - } - - spin_unlock(&fc->lock); - return 0; -} - -static ssize_t fuse_loop_dio(struct file *filp, const struct iovec *iov, - unsigned long nr_segs, loff_t *ppos, int rw) -{ - const struct iovec *vector = iov; - ssize_t ret = 0; - - while (nr_segs > 0) { - void __user *base; - size_t len; - ssize_t nr; - - base = vector->iov_base; - len = vector->iov_len; - vector++; - nr_segs--; - - if (rw == WRITE) - nr = __fuse_direct_write(filp, base, len, ppos); - else - nr = fuse_direct_read(filp, base, len, ppos); - - if (nr < 0) { - if (!ret) - ret = nr; - break; - } - ret += nr; - if (nr != len) - break; - } - - return ret; -} - - -static ssize_t -fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, - loff_t offset, unsigned long nr_segs) -{ - ssize_t ret = 0; - struct file *file = NULL; - loff_t pos = 0; - - file = iocb->ki_filp; - pos = offset; - - ret = fuse_loop_dio(file, iov, nr_segs, &pos, rw); - - return ret; -} - -static const struct file_operations fuse_file_operations = { - .llseek = fuse_file_llseek, - .read = do_sync_read, - .aio_read = fuse_file_aio_read, - .write = do_sync_write, - .aio_write = fuse_file_aio_write, - .mmap = fuse_file_mmap, - .open = fuse_open, - .flush = fuse_flush, - .release = fuse_release, - .fsync = fuse_fsync, - .lock = fuse_file_lock, - .flock = fuse_file_flock, - .splice_read = generic_file_splice_read, - .unlocked_ioctl = fuse_file_ioctl, - .compat_ioctl = fuse_file_compat_ioctl, - .poll = fuse_file_poll, -}; - -static const struct file_operations fuse_direct_io_file_operations = { - .llseek = fuse_file_llseek, - .read = fuse_direct_read, - .write = fuse_direct_write, - .mmap = fuse_direct_mmap, - .open = fuse_open, - .flush = fuse_flush, - .release = fuse_release, - .fsync = fuse_fsync, - .lock = fuse_file_lock, - .flock = fuse_file_flock, - .unlocked_ioctl = fuse_file_ioctl, - .compat_ioctl = fuse_file_compat_ioctl, - .poll = fuse_file_poll, - /* no splice_read */ -}; - -static const struct address_space_operations fuse_file_aops = { - .readpage = fuse_readpage, - .writepage = fuse_writepage, - .launder_page = fuse_launder_page, - .readpages = fuse_readpages, - .set_page_dirty = __set_page_dirty_nobuffers, - .bmap = fuse_bmap, - .direct_IO = fuse_direct_IO, -}; - -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/ANDROID_3.4.5/fs/fuse/fuse_i.h b/ANDROID_3.4.5/fs/fuse/fuse_i.h deleted file mode 100644 index d1819269..00000000 --- a/ANDROID_3.4.5/fs/fuse/fuse_i.h +++ /dev/null @@ -1,784 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu> - - This program can be distributed under the terms of the GNU GPL. - See the file COPYING. -*/ - -#ifndef _FS_FUSE_I_H -#define _FS_FUSE_I_H - -#include <linux/fuse.h> -#include <linux/fs.h> -#include <linux/mount.h> -#include <linux/wait.h> -#include <linux/list.h> -#include <linux/spinlock.h> -#include <linux/mm.h> -#include <linux/backing-dev.h> -#include <linux/mutex.h> -#include <linux/rwsem.h> -#include <linux/rbtree.h> -#include <linux/poll.h> -#include <linux/workqueue.h> - -/** Max number of pages that can be used in a single read request */ -#define FUSE_MAX_PAGES_PER_REQ 32 - -/** Bias for fi->writectr, meaning new writepages must not be sent */ -#define FUSE_NOWRITE INT_MIN - -/** 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 5 - -/** 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; - -/** Module parameters */ -extern unsigned max_user_bgreq; -extern unsigned max_user_congthresh; - -/* One forget request */ -struct fuse_forget_link { - struct fuse_forget_one forget_one; - struct fuse_forget_link *next; -}; - -/** 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_forget_link *forget; - - /** Time in jiffies until the file attributes are valid */ - u64 i_time; - - /** The sticky bit in inode->i_mode may have been removed, so - preserve the original mode */ - umode_t orig_i_mode; - - /** 64 bit inode number */ - u64 orig_ino; - - /** Version of last attribute change */ - u64 attr_version; - - /** Files usable in writepage. Protected by fc->lock */ - struct list_head write_files; - - /** Writepages pending on truncate or fsync */ - struct list_head queued_writes; - - /** Number of sent writes, a negative bias (FUSE_NOWRITE) - * means more writes are blocked */ - int writectr; - - /** Waitq for writepage completion */ - wait_queue_head_t page_waitq; - - /** List of writepage requestst (pending or sent) */ - struct list_head writepages; -}; - -struct fuse_conn; - -/** FUSE specific file data */ -struct fuse_file { - /** Fuse connection for this file */ - struct fuse_conn *fc; - - /** Request reserved for flush and release */ - struct fuse_req *reserved_req; - - /** Kernel file handle guaranteed to be unique */ - u64 kh; - - /** File handle used by userspace */ - u64 fh; - - /** Node id of this file */ - u64 nodeid; - - /** Refcount */ - atomic_t count; - - /** FOPEN_* flags returned by open */ - u32 open_flags; - - /** Entry on inode's write_files list */ - struct list_head write_entry; - - /** RB node to be linked on fuse_conn->polled_files */ - struct rb_node polled_node; - - /** Wait queue head for poll */ - wait_queue_head_t poll_wait; - - /** Has flock been performed on this file? */ - bool flock:1; -}; - -/** 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; - - /** Pages may be replaced with new ones */ - unsigned page_replace: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 -}; - -/** - * 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 { - union { - struct fuse_release_in in; - struct work_struct work; - }; - struct path path; - } release; - struct fuse_init_in init_in; - struct fuse_init_out init_out; - struct cuse_init_in cuse_init_in; - struct { - struct fuse_read_in in; - u64 attr_ver; - } read; - struct { - struct fuse_write_in in; - struct fuse_write_out out; - } write; - struct fuse_notify_retrieve_in retrieve_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 fuse_file *ff; - - /** Inode used in the request or NULL */ - struct inode *inode; - - /** Link on fi->writepages */ - struct list_head writepages_entry; - - /** 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; - - /** The next unique kernel file handle */ - u64 khctr; - - /** rbtree of fuse_files waiting for poll events indexed by ph */ - struct rb_root polled_files; - - /** Maximum number of outstanding background requests */ - unsigned max_background; - - /** Number of background requests at which congestion starts */ - unsigned congestion_threshold; - - /** Number of requests currently in the background */ - unsigned num_background; - - /** Number of background requests currently queued for userspace */ - unsigned active_background; - - /** The list of background requests set aside for later queuing */ - struct list_head bg_queue; - - /** Pending interrupts */ - struct list_head interrupts; - - /** Queue of pending forgets */ - struct fuse_forget_link forget_list_head; - struct fuse_forget_link *forget_list_tail; - - /** Batching of FORGET requests (positive indicates FORGET batch) */ - int forget_batch; - - /** 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; - - /** Do not send separate SETATTR request before open(O_TRUNC) */ - unsigned atomic_o_trunc:1; - - /** Filesystem supports NFS exporting. Only set in INIT */ - unsigned export_support:1; - - /** Set if bdi is valid */ - unsigned bdi_initialized: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 posix 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; - - /** Is poll not implemented by fs? */ - unsigned no_poll:1; - - /** Do multi-page cached writes */ - unsigned big_writes:1; - - /** Don't apply umask to creation modes */ - unsigned dont_mask:1; - - /** Are BSD file locking primitives not implemented by fs? */ - unsigned no_flock: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; - - /** Device ID from super block */ - dev_t dev; - - /** 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; - - /** Version counter for attribute changes */ - u64 attr_version; - - /** Called on final put */ - void (*release)(struct fuse_conn *); - - /** Super block for this connection. */ - struct super_block *sb; - - /** Read/write semaphore to hold when accessing sb. */ - struct rw_semaphore killsb; -}; - -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 const struct file_operations fuse_dev_operations; - -extern const struct dentry_operations fuse_dentry_operations; - -/** - * Inode to nodeid comparison. - */ -int fuse_inode_eq(struct inode *inode, void *_nodeidp); - -/** - * Get a filled in inode - */ -struct inode *fuse_iget(struct super_block *sb, u64 nodeid, - int generation, struct fuse_attr *attr, - u64 attr_valid, u64 attr_version); - -int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name, - struct fuse_entry_out *outarg, struct inode **inode); - -/** - * Send FORGET command - */ -void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget, - u64 nodeid, u64 nlookup); - -struct fuse_forget_link *fuse_alloc_forget(void); - -/** - * Initialize READ or READDIR request - */ -void fuse_read_fill(struct fuse_req *req, struct file *file, - loff_t pos, size_t count, int opcode); - -/** - * Send OPEN or OPENDIR request - */ -int fuse_open_common(struct inode *inode, struct file *file, bool isdir); - -struct fuse_file *fuse_file_alloc(struct fuse_conn *fc); -struct fuse_file *fuse_file_get(struct fuse_file *ff); -void fuse_file_free(struct fuse_file *ff); -void fuse_finish_open(struct inode *inode, struct file *file); - -void fuse_sync_release(struct fuse_file *ff, int flags); - -/** - * Send RELEASE or RELEASEDIR request - */ -void fuse_release_common(struct file *file, int opcode); - -/** - * Send FSYNC or FSYNCDIR request - */ -int fuse_fsync_common(struct file *file, loff_t start, loff_t end, - int datasync, int isdir); - -/** - * Notify poll wakeup - */ -int fuse_notify_poll_wakeup(struct fuse_conn *fc, - struct fuse_notify_poll_wakeup_out *outarg); - -/** - * 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, - u64 attr_valid, u64 attr_version); - -void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, - u64 attr_valid); - -/** - * 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); - -struct fuse_req *fuse_request_alloc_nofs(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 fuse_request_send(struct fuse_conn *fc, struct fuse_req *req); - -/** - * Send a request in the background - */ -void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req); - -void fuse_request_send_background_locked(struct fuse_conn *fc, - struct fuse_req *req); - -/* Abort all requests */ -void fuse_abort_conn(struct fuse_conn *fc); - -/** - * Invalidate inode attributes - */ -void fuse_invalidate_attr(struct inode *inode); - -void fuse_invalidate_entry_cache(struct dentry *entry); - -/** - * Acquire reference to fuse_conn - */ -struct fuse_conn *fuse_conn_get(struct fuse_conn *fc); - -void fuse_conn_kill(struct fuse_conn *fc); - -/** - * Initialize fuse_conn - */ -void fuse_conn_init(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); - -u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id); - -int fuse_update_attributes(struct inode *inode, struct kstat *stat, - struct file *file, bool *refreshed); - -void fuse_flush_writepages(struct inode *inode); - -void fuse_set_nowrite(struct inode *inode); -void fuse_release_nowrite(struct inode *inode); - -u64 fuse_get_attr_version(struct fuse_conn *fc); - -/** - * File-system tells the kernel to invalidate cache for the given node id. - */ -int fuse_reverse_inval_inode(struct super_block *sb, u64 nodeid, - loff_t offset, loff_t len); - -/** - * File-system tells the kernel to invalidate parent attributes and - * the dentry matching parent/name. - * - * If the child_nodeid is non-zero and: - * - matches the inode number for the dentry matching parent/name, - * - is not a mount point - * - is a file or oan empty directory - * then the dentry is unhashed (d_delete()). - */ -int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, - u64 child_nodeid, struct qstr *name); - -int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file, - bool isdir); -ssize_t fuse_direct_io(struct file *file, const char __user *buf, - size_t count, loff_t *ppos, int write); -long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, - unsigned int flags); -long fuse_ioctl_common(struct file *file, unsigned int cmd, - unsigned long arg, unsigned int flags); -unsigned fuse_file_poll(struct file *file, poll_table *wait); -int fuse_dev_release(struct inode *inode, struct file *file); - -void fuse_write_update_size(struct inode *inode, loff_t pos); - -#endif /* _FS_FUSE_I_H */ diff --git a/ANDROID_3.4.5/fs/fuse/inode.c b/ANDROID_3.4.5/fs/fuse/inode.c deleted file mode 100644 index a59cf5e6..00000000 --- a/ANDROID_3.4.5/fs/fuse/inode.c +++ /dev/null @@ -1,1267 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu> - - This program can be distributed under the terms of the GNU GPL. - See the file COPYING. -*/ - -#include "fuse_i.h" - -#include <linux/pagemap.h> -#include <linux/slab.h> -#include <linux/file.h> -#include <linux/seq_file.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/parser.h> -#include <linux/statfs.h> -#include <linux/random.h> -#include <linux/sched.h> -#include <linux/exportfs.h> - -MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); -MODULE_DESCRIPTION("Filesystem in Userspace"); -MODULE_LICENSE("GPL"); - -static struct kmem_cache *fuse_inode_cachep; -struct list_head fuse_conn_list; -DEFINE_MUTEX(fuse_mutex); - -static int set_global_limit(const char *val, struct kernel_param *kp); - -unsigned max_user_bgreq; -module_param_call(max_user_bgreq, set_global_limit, param_get_uint, - &max_user_bgreq, 0644); -__MODULE_PARM_TYPE(max_user_bgreq, "uint"); -MODULE_PARM_DESC(max_user_bgreq, - "Global limit for the maximum number of backgrounded requests an " - "unprivileged user can set"); - -unsigned max_user_congthresh; -module_param_call(max_user_congthresh, set_global_limit, param_get_uint, - &max_user_congthresh, 0644); -__MODULE_PARM_TYPE(max_user_congthresh, "uint"); -MODULE_PARM_DESC(max_user_congthresh, - "Global limit for the maximum congestion threshold an " - "unprivileged user can set"); - -#define FUSE_SUPER_MAGIC 0x65735546 - -#define FUSE_DEFAULT_BLKSIZE 512 - -/** Maximum number of outstanding background requests */ -#define FUSE_DEFAULT_MAX_BACKGROUND 12 - -/** Congestion starts at 75% of maximum */ -#define FUSE_DEFAULT_CONGESTION_THRESHOLD (FUSE_DEFAULT_MAX_BACKGROUND * 3 / 4) - -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; -}; - -struct fuse_forget_link *fuse_alloc_forget(void) -{ - return kzalloc(sizeof(struct fuse_forget_link), GFP_KERNEL); -} - -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->attr_version = 0; - fi->writectr = 0; - fi->orig_ino = 0; - INIT_LIST_HEAD(&fi->write_files); - INIT_LIST_HEAD(&fi->queued_writes); - INIT_LIST_HEAD(&fi->writepages); - init_waitqueue_head(&fi->page_waitq); - fi->forget = fuse_alloc_forget(); - if (!fi->forget) { - kmem_cache_free(fuse_inode_cachep, inode); - return NULL; - } - - return inode; -} - -static void fuse_i_callback(struct rcu_head *head) -{ - struct inode *inode = container_of(head, struct inode, i_rcu); - kmem_cache_free(fuse_inode_cachep, inode); -} - -static void fuse_destroy_inode(struct inode *inode) -{ - struct fuse_inode *fi = get_fuse_inode(inode); - BUG_ON(!list_empty(&fi->write_files)); - BUG_ON(!list_empty(&fi->queued_writes)); - kfree(fi->forget); - call_rcu(&inode->i_rcu, fuse_i_callback); -} - -static void fuse_evict_inode(struct inode *inode) -{ - truncate_inode_pages(&inode->i_data, 0); - end_writeback(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_queue_forget(fc, fi->forget, fi->nodeid, fi->nlookup); - fi->forget = NULL; - } -} - -static int fuse_remount_fs(struct super_block *sb, int *flags, char *data) -{ - if (*flags & MS_MANDLOCK) - return -EINVAL; - - return 0; -} - -/* - * ino_t is 32-bits on 32-bit arch. We have to squash the 64-bit value down - * so that it will fit. - */ -static ino_t fuse_squash_ino(u64 ino64) -{ - ino_t ino = (ino_t) ino64; - if (sizeof(ino_t) < sizeof(u64)) - ino ^= ino64 >> (sizeof(u64) - sizeof(ino_t)) * 8; - return ino; -} - -void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, - u64 attr_valid) -{ - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_inode *fi = get_fuse_inode(inode); - - fi->attr_version = ++fc->attr_version; - fi->i_time = attr_valid; - - inode->i_ino = fuse_squash_ino(attr->ino); - inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777); - set_nlink(inode, attr->nlink); - inode->i_uid = attr->uid; - inode->i_gid = attr->gid; - 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; - - if (attr->blksize != 0) - inode->i_blkbits = ilog2(attr->blksize); - else - inode->i_blkbits = inode->i_sb->s_blocksize_bits; - - /* - * Don't set the sticky bit in i_mode, unless we want the VFS - * to check permissions. This prevents failures due to the - * check in may_delete(). - */ - fi->orig_i_mode = inode->i_mode; - if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) - inode->i_mode &= ~S_ISVTX; - - fi->orig_ino = attr->ino; -} - -void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, - u64 attr_valid, u64 attr_version) -{ - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_inode *fi = get_fuse_inode(inode); - loff_t oldsize; - - spin_lock(&fc->lock); - if (attr_version != 0 && fi->attr_version > attr_version) { - spin_unlock(&fc->lock); - return; - } - - fuse_change_attributes_common(inode, attr, attr_valid); - - oldsize = inode->i_size; - i_size_write(inode, attr->size); - spin_unlock(&fc->lock); - - if (S_ISREG(inode->i_mode) && oldsize != attr->size) { - truncate_pagecache(inode, oldsize, attr->size); - invalidate_inode_pages2(inode->i_mapping); - } -} - -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(); -} - -int fuse_inode_eq(struct inode *inode, void *_nodeidp) -{ - u64 nodeid = *(u64 *) _nodeidp; - if (get_node_id(inode) == nodeid) - return 1; - else - return 0; -} - -static int fuse_inode_set(struct inode *inode, void *_nodeidp) -{ - u64 nodeid = *(u64 *) _nodeidp; - get_fuse_inode(inode)->nodeid = nodeid; - return 0; -} - -struct inode *fuse_iget(struct super_block *sb, u64 nodeid, - int generation, struct fuse_attr *attr, - u64 attr_valid, u64 attr_version) -{ - 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, attr_valid, attr_version); - - return inode; -} - -int fuse_reverse_inval_inode(struct super_block *sb, u64 nodeid, - loff_t offset, loff_t len) -{ - struct inode *inode; - pgoff_t pg_start; - pgoff_t pg_end; - - inode = ilookup5(sb, nodeid, fuse_inode_eq, &nodeid); - if (!inode) - return -ENOENT; - - fuse_invalidate_attr(inode); - if (offset >= 0) { - pg_start = offset >> PAGE_CACHE_SHIFT; - if (len <= 0) - pg_end = -1; - else - pg_end = (offset + len - 1) >> PAGE_CACHE_SHIFT; - invalidate_inode_pages2_range(inode->i_mapping, - pg_start, pg_end); - } - iput(inode); - return 0; -} - -static void fuse_umount_begin(struct super_block *sb) -{ - fuse_abort_conn(get_fuse_conn_super(sb)); -} - -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; - fuse_request_send(fc, req); - fuse_put_request(fc, req); - } -} - -static void fuse_bdi_destroy(struct fuse_conn *fc) -{ - if (fc->bdi_initialized) - bdi_destroy(&fc->bdi); -} - -void fuse_conn_kill(struct fuse_conn *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_bdi_destroy(fc); -} -EXPORT_SYMBOL_GPL(fuse_conn_kill); - -static void fuse_put_super(struct super_block *sb) -{ - struct fuse_conn *fc = get_fuse_conn_super(sb); - - fuse_send_destroy(fc); - fuse_conn_kill(fc); - 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 */ -} - -static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf) -{ - struct super_block *sb = dentry->d_sb; - 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; - req->in.h.nodeid = get_node_id(dentry->d_inode); - req->out.numargs = 1; - req->out.args[0].size = - fc->minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(outarg); - req->out.args[0].value = &outarg; - fuse_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 const 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 = FUSE_DEFAULT_BLKSIZE; - - 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; - d->user_id = value; - d->user_id_present = 1; - break; - - case OPT_GROUP_ID: - if (match_int(&args[0], &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 dentry *root) -{ - struct super_block *sb = root->d_sb; - struct fuse_conn *fc = get_fuse_conn_super(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); - if (sb->s_bdev && sb->s_blocksize != FUSE_DEFAULT_BLKSIZE) - seq_printf(m, ",blksize=%lu", sb->s_blocksize); - return 0; -} - -void fuse_conn_init(struct fuse_conn *fc) -{ - memset(fc, 0, sizeof(*fc)); - spin_lock_init(&fc->lock); - mutex_init(&fc->inst_mutex); - init_rwsem(&fc->killsb); - 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); - INIT_LIST_HEAD(&fc->bg_queue); - INIT_LIST_HEAD(&fc->entry); - fc->forget_list_tail = &fc->forget_list_head; - atomic_set(&fc->num_waiting, 0); - fc->max_background = FUSE_DEFAULT_MAX_BACKGROUND; - fc->congestion_threshold = FUSE_DEFAULT_CONGESTION_THRESHOLD; - fc->khctr = 0; - fc->polled_files = RB_ROOT; - fc->reqctr = 0; - fc->blocked = 1; - fc->attr_version = 1; - get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key)); -} -EXPORT_SYMBOL_GPL(fuse_conn_init); - -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); - fc->release(fc); - } -} -EXPORT_SYMBOL_GPL(fuse_conn_put); - -struct fuse_conn *fuse_conn_get(struct fuse_conn *fc) -{ - atomic_inc(&fc->count); - return fc; -} -EXPORT_SYMBOL_GPL(fuse_conn_get); - -static struct inode *fuse_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, 0, 0); -} - -struct fuse_inode_handle { - u64 nodeid; - u32 generation; -}; - -static struct dentry *fuse_get_dentry(struct super_block *sb, - struct fuse_inode_handle *handle) -{ - struct fuse_conn *fc = get_fuse_conn_super(sb); - struct inode *inode; - struct dentry *entry; - int err = -ESTALE; - - if (handle->nodeid == 0) - goto out_err; - - inode = ilookup5(sb, handle->nodeid, fuse_inode_eq, &handle->nodeid); - if (!inode) { - struct fuse_entry_out outarg; - struct qstr name; - - if (!fc->export_support) - goto out_err; - - name.len = 1; - name.name = "."; - err = fuse_lookup_name(sb, handle->nodeid, &name, &outarg, - &inode); - if (err && err != -ENOENT) - goto out_err; - if (err || !inode) { - err = -ESTALE; - goto out_err; - } - err = -EIO; - if (get_node_id(inode) != handle->nodeid) - goto out_iput; - } - err = -ESTALE; - if (inode->i_generation != handle->generation) - goto out_iput; - - entry = d_obtain_alias(inode); - if (!IS_ERR(entry) && get_node_id(inode) != FUSE_ROOT_ID) - fuse_invalidate_entry_cache(entry); - - return entry; - - out_iput: - iput(inode); - out_err: - return ERR_PTR(err); -} - -static int fuse_encode_fh(struct dentry *dentry, u32 *fh, int *max_len, - int connectable) -{ - struct inode *inode = dentry->d_inode; - bool encode_parent = connectable && !S_ISDIR(inode->i_mode); - int len = encode_parent ? 6 : 3; - u64 nodeid; - u32 generation; - - if (*max_len < len) { - *max_len = len; - return 255; - } - - nodeid = get_fuse_inode(inode)->nodeid; - generation = inode->i_generation; - - fh[0] = (u32)(nodeid >> 32); - fh[1] = (u32)(nodeid & 0xffffffff); - fh[2] = generation; - - if (encode_parent) { - struct inode *parent; - - spin_lock(&dentry->d_lock); - parent = dentry->d_parent->d_inode; - nodeid = get_fuse_inode(parent)->nodeid; - generation = parent->i_generation; - spin_unlock(&dentry->d_lock); - - fh[3] = (u32)(nodeid >> 32); - fh[4] = (u32)(nodeid & 0xffffffff); - fh[5] = generation; - } - - *max_len = len; - return encode_parent ? 0x82 : 0x81; -} - -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_type != 0x81 && fh_type != 0x82) || fh_len < 3) - 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 != 0x82 || 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 struct dentry *fuse_get_parent(struct dentry *child) -{ - struct inode *child_inode = child->d_inode; - struct fuse_conn *fc = get_fuse_conn(child_inode); - struct inode *inode; - struct dentry *parent; - struct fuse_entry_out outarg; - struct qstr name; - int err; - - if (!fc->export_support) - return ERR_PTR(-ESTALE); - - name.len = 2; - name.name = ".."; - err = fuse_lookup_name(child_inode->i_sb, get_node_id(child_inode), - &name, &outarg, &inode); - if (err) { - if (err == -ENOENT) - return ERR_PTR(-ESTALE); - return ERR_PTR(err); - } - - parent = d_obtain_alias(inode); - if (!IS_ERR(parent) && get_node_id(inode) != FUSE_ROOT_ID) - fuse_invalidate_entry_cache(parent); - - return 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, - .get_parent = fuse_get_parent, -}; - -static const struct super_operations fuse_super_operations = { - .alloc_inode = fuse_alloc_inode, - .destroy_inode = fuse_destroy_inode, - .evict_inode = fuse_evict_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 sanitize_global_limit(unsigned *limit) -{ - if (*limit == 0) - *limit = ((num_physpages << PAGE_SHIFT) >> 13) / - sizeof(struct fuse_req); - - if (*limit >= 1 << 16) - *limit = (1 << 16) - 1; -} - -static int set_global_limit(const char *val, struct kernel_param *kp) -{ - int rv; - - rv = param_set_uint(val, kp); - if (rv) - return rv; - - sanitize_global_limit((unsigned *)kp->arg); - - return 0; -} - -static void process_init_limits(struct fuse_conn *fc, struct fuse_init_out *arg) -{ - int cap_sys_admin = capable(CAP_SYS_ADMIN); - - if (arg->minor < 13) - return; - - sanitize_global_limit(&max_user_bgreq); - sanitize_global_limit(&max_user_congthresh); - - if (arg->max_background) { - fc->max_background = arg->max_background; - - if (!cap_sys_admin && fc->max_background > max_user_bgreq) - fc->max_background = max_user_bgreq; - } - if (arg->congestion_threshold) { - fc->congestion_threshold = arg->congestion_threshold; - - if (!cap_sys_admin && - fc->congestion_threshold > max_user_congthresh) - fc->congestion_threshold = max_user_congthresh; - } -} - -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; - - process_init_limits(fc, arg); - - 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; - if (arg->minor >= 17) { - if (!(arg->flags & FUSE_FLOCK_LOCKS)) - fc->no_flock = 1; - } else { - if (!(arg->flags & FUSE_POSIX_LOCKS)) - fc->no_flock = 1; - } - if (arg->flags & FUSE_ATOMIC_O_TRUNC) - fc->atomic_o_trunc = 1; - if (arg->minor >= 9) { - /* LOOKUP has dependency on proto version */ - if (arg->flags & FUSE_EXPORT_SUPPORT) - fc->export_support = 1; - } - if (arg->flags & FUSE_BIG_WRITES) - fc->big_writes = 1; - if (arg->flags & FUSE_DONT_MASK) - fc->dont_mask = 1; - } else { - ra_pages = fc->max_read / PAGE_CACHE_SIZE; - fc->no_lock = 1; - fc->no_flock = 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->max_write = max_t(unsigned, 4096, fc->max_write); - fc->conn_init = 1; - } - 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 | FUSE_ATOMIC_O_TRUNC | - FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK | - FUSE_FLOCK_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 argument 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; - fuse_request_send_background(fc, req); -} - -static void fuse_free_conn(struct fuse_conn *fc) -{ - kfree(fc); -} - -static int fuse_bdi_init(struct fuse_conn *fc, struct super_block *sb) -{ - int err; - - fc->bdi.name = "fuse"; - fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; - /* fuse does it's own writeback accounting */ - fc->bdi.capabilities = BDI_CAP_NO_ACCT_WB; - - err = bdi_init(&fc->bdi); - if (err) - return err; - - fc->bdi_initialized = 1; - - if (sb->s_bdev) { - err = bdi_register(&fc->bdi, NULL, "%u:%u-fuseblk", - MAJOR(fc->dev), MINOR(fc->dev)); - } else { - err = bdi_register_dev(&fc->bdi, fc->dev); - } - - if (err) - return err; - - /* - * For a single fuse filesystem use max 1% of dirty + - * writeback threshold. - * - * This gives about 1M of write buffer for memory maps on a - * machine with 1G and 10% dirty_ratio, which should be more - * than enough. - * - * Privileged users can raise it by writing to - * - * /sys/class/bdi/<bdi>/max_ratio - */ - bdi_set_max_ratio(&fc->bdi, 1); - - return 0; -} - -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; - - err = -EINVAL; - if (sb->s_flags & MS_MANDLOCK) - goto err; - - sb->s_flags &= ~MS_NOSEC; - - if (!parse_fuse_opt((char *) data, &d, is_bdev)) - goto err; - - if (is_bdev) { -#ifdef CONFIG_BLOCK - err = -EINVAL; - if (!sb_set_blocksize(sb, d.blksize)) - goto err; -#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; - sb->s_time_gran = 1; - sb->s_export_op = &fuse_export_operations; - - file = fget(d.fd); - err = -EINVAL; - if (!file) - goto err; - - if (file->f_op != &fuse_dev_operations) - goto err_fput; - - fc = kmalloc(sizeof(*fc), GFP_KERNEL); - err = -ENOMEM; - if (!fc) - goto err_fput; - - fuse_conn_init(fc); - - fc->dev = sb->s_dev; - fc->sb = sb; - err = fuse_bdi_init(fc, sb); - if (err) - goto err_put_conn; - - sb->s_bdi = &fc->bdi; - - /* Handle umasking inside the fuse code */ - if (sb->s_flags & MS_POSIXACL) - fc->dont_mask = 1; - sb->s_flags |= MS_POSIXACL; - - fc->release = fuse_free_conn; - fc->flags = d.flags; - fc->user_id = d.user_id; - fc->group_id = d.group_id; - fc->max_read = max_t(unsigned, 4096, d.max_read); - - /* Used by get_root_inode() */ - sb->s_fs_info = fc; - - err = -ENOMEM; - root = fuse_get_root_inode(sb, d.rootmode); - root_dentry = d_make_root(root); - if (!root_dentry) - goto err_put_conn; - /* only now - we want root dentry with NULL ->d_op */ - sb->s_d_op = &fuse_dentry_operations; - - 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_free_init_req; - } - - mutex_lock(&fuse_mutex); - err = -EINVAL; - if (file->private_data) - goto err_unlock; - - 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); - err_free_init_req: - fuse_request_free(init_req); - err_put_root: - dput(root_dentry); - err_put_conn: - fuse_bdi_destroy(fc); - fuse_conn_put(fc); - err_fput: - fput(file); - err: - return err; -} - -static struct dentry *fuse_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *raw_data) -{ - return mount_nodev(fs_type, flags, raw_data, fuse_fill_super); -} - -static void fuse_kill_sb_anon(struct super_block *sb) -{ - struct fuse_conn *fc = get_fuse_conn_super(sb); - - if (fc) { - down_write(&fc->killsb); - fc->sb = NULL; - up_write(&fc->killsb); - } - - kill_anon_super(sb); -} - -static struct file_system_type fuse_fs_type = { - .owner = THIS_MODULE, - .name = "fuse", - .fs_flags = FS_HAS_SUBTYPE, - .mount = fuse_mount, - .kill_sb = fuse_kill_sb_anon, -}; - -#ifdef CONFIG_BLOCK -static struct dentry *fuse_mount_blk(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *raw_data) -{ - return mount_bdev(fs_type, flags, dev_name, raw_data, fuse_fill_super); -} - -static void fuse_kill_sb_blk(struct super_block *sb) -{ - struct fuse_conn *fc = get_fuse_conn_super(sb); - - if (fc) { - down_write(&fc->killsb); - fc->sb = NULL; - up_write(&fc->killsb); - } - - kill_block_super(sb); -} - -static struct file_system_type fuseblk_fs_type = { - .owner = THIS_MODULE, - .name = "fuseblk", - .mount = fuse_mount_blk, - .kill_sb = fuse_kill_sb_blk, - .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 - -static void fuse_inode_init_once(void *foo) -{ - struct inode *inode = foo; - - inode_init_once(inode); -} - -static int __init fuse_fs_init(void) -{ - int err; - - fuse_inode_cachep = kmem_cache_create("fuse_inode", - sizeof(struct fuse_inode), - 0, SLAB_HWCACHE_ALIGN, - fuse_inode_init_once); - err = -ENOMEM; - if (!fuse_inode_cachep) - goto out; - - err = register_fuseblk(); - if (err) - goto out2; - - err = register_filesystem(&fuse_fs_type); - if (err) - goto out3; - - return 0; - - out3: - unregister_fuseblk(); - out2: - kmem_cache_destroy(fuse_inode_cachep); - out: - return err; -} - -static void fuse_fs_cleanup(void) -{ - unregister_filesystem(&fuse_fs_type); - unregister_fuseblk(); - kmem_cache_destroy(fuse_inode_cachep); -} - -static struct kobject *fuse_kobj; -static struct kobject *connections_kobj; - -static int fuse_sysfs_init(void) -{ - int err; - - fuse_kobj = kobject_create_and_add("fuse", fs_kobj); - if (!fuse_kobj) { - err = -ENOMEM; - goto out_err; - } - - connections_kobj = kobject_create_and_add("connections", fuse_kobj); - if (!connections_kobj) { - err = -ENOMEM; - goto out_fuse_unregister; - } - - return 0; - - out_fuse_unregister: - kobject_put(fuse_kobj); - out_err: - return err; -} - -static void fuse_sysfs_cleanup(void) -{ - kobject_put(connections_kobj); - kobject_put(fuse_kobj); -} - -static int __init fuse_init(void) -{ - int res; - - printk(KERN_INFO "fuse init (API version %i.%i)\n", - FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); - - 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; - - sanitize_global_limit(&max_user_bgreq); - sanitize_global_limit(&max_user_congthresh); - - 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); |