Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

27
fs/fuse/Kconfig Normal file
View file

@ -0,0 +1,27 @@
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 a companion library: libfuse2. This library is available
from the FUSE homepage:
<http://fuse.sourceforge.net/>
although chances are your distribution already has that library
installed if you've installed the "fuse" package itself.
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.
config CUSE
tristate "Character device in Userspace support"
depends on FUSE_FS
help
This FUSE extension allows character devices to be
implemented in userspace.
If you want to develop or use a userspace character device
based on CUSE, answer Y or M.

8
fs/fuse/Makefile Normal file
View file

@ -0,0 +1,8 @@
#
# 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

354
fs/fuse/control.c Normal file
View file

@ -0,0 +1,354 @@
/*
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_inode(file)->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;
unsigned limit = (1 << 16) - 1;
int err;
if (*ppos)
return -EINVAL;
err = kstrtoul_from_user(buf, count, 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 uninitialized_var(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 uninitialized_var(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,
};
MODULE_ALIAS_FS("fusectl");
int __init fuse_ctl_init(void)
{
return register_filesystem(&fuse_ctl_fs_type);
}
void __exit fuse_ctl_cleanup(void)
{
unregister_filesystem(&fuse_ctl_fs_type);
}

645
fs/fuse/cuse.c Normal file
View file

@ -0,0 +1,645 @@
/*
* 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/aio.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/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_MUTEX(cuse_lock); /* protects registration */
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;
struct iovec iov = { .iov_base = buf, .iov_len = count };
struct fuse_io_priv io = { .async = 0, .file = file };
struct iov_iter ii;
iov_iter_init(&ii, READ, &iov, 1, count);
return fuse_direct_io(&io, &ii, &pos, FUSE_DIO_CUSE);
}
static ssize_t cuse_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
loff_t pos = 0;
struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = count };
struct fuse_io_priv io = { .async = 0, .file = file };
struct iov_iter ii;
iov_iter_init(&ii, WRITE, &iov, 1, count);
/*
* No locking or generic_write_checks(), the server is
* responsible for locking and sanity checks.
*/
return fuse_direct_io(&io, &ii, &pos,
FUSE_DIO_WRITE | FUSE_DIO_CUSE);
}
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 */
mutex_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;
}
mutex_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 *uninitialized_var(key), *uninitialized_var(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), *pos;
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, i;
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);
mutex_lock(&cuse_lock);
/* make sure the device-name is unique */
for (i = 0; i < CUSE_CONNTBL_LEN; ++i) {
list_for_each_entry(pos, &cuse_conntbl[i], list)
if (!strcmp(dev_name(pos->dev), dev_name(dev)))
goto err_unlock;
}
rc = device_add(dev);
if (rc)
goto err_unlock;
/* register cdev */
rc = -ENOMEM;
cdev = cdev_alloc();
if (!cdev)
goto err_unlock;
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 */
list_add(&cc->list, cuse_conntbl_head(devt));
mutex_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_unlock:
mutex_unlock(&cuse_lock);
put_device(dev);
err_region:
unregister_chrdev_region(devt, 1);
err:
fuse_conn_kill(fc);
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_for_background(fc, 1);
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->page_descs[0].length = req->out.args[1].size;
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_rcu(cc, fc.rcu);
}
/**
* 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.initialized = 1;
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 */
mutex_lock(&cuse_lock);
list_del_init(&cc->list);
mutex_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);
}
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 DEVICE_ATTR(waiting, 0400, cuse_class_waiting_show, NULL);
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 DEVICE_ATTR(abort, 0200, NULL, cuse_class_abort_store);
static struct attribute *cuse_class_dev_attrs[] = {
&dev_attr_waiting.attr,
&dev_attr_abort.attr,
NULL,
};
ATTRIBUTE_GROUPS(cuse_class_dev);
static struct miscdevice cuse_miscdev = {
.minor = CUSE_MINOR,
.name = "cuse",
.fops = &cuse_channel_fops,
};
MODULE_ALIAS_MISCDEV(CUSE_MINOR);
MODULE_ALIAS("devname:cuse");
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_groups = cuse_class_dev_groups;
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");

2195
fs/fuse/dev.c Normal file

File diff suppressed because it is too large Load diff

2077
fs/fuse/dir.c Normal file

File diff suppressed because it is too large Load diff

3126
fs/fuse/file.c Normal file

File diff suppressed because it is too large Load diff

900
fs/fuse/fuse_i.h Normal file
View file

@ -0,0 +1,900 @@
/*
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)
/** Number of page pointers embedded in fuse_req */
#define FUSE_REQ_INLINE_PAGES 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;
/** Miscellaneous bits describing inode state */
unsigned long state;
};
/** FUSE inode state bits */
enum {
/** Advise readdirplus */
FUSE_I_ADVISE_RDPLUS,
/** Initialized with readdirplus */
FUSE_I_INIT_RDPLUS,
/** An operation changing file size is in progress */
FUSE_I_SIZE_UNSTABLE,
};
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];
};
/** FUSE page descriptor */
struct fuse_page_desc {
unsigned int length;
unsigned int offset;
};
/** 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
};
/** The request IO state (for asynchronous processing) */
struct fuse_io_priv {
int async;
spinlock_t lock;
unsigned reqs;
ssize_t bytes;
size_t size;
__u64 offset;
bool write;
int err;
struct kiocb *iocb;
struct file *file;
};
/**
* 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;
struct fuse_req *next;
} write;
struct fuse_notify_retrieve_in retrieve_in;
struct fuse_lk_in lk_in;
} misc;
/** page vector */
struct page **pages;
/** page-descriptor vector */
struct fuse_page_desc *page_descs;
/** size of the 'pages' array */
unsigned max_pages;
/** inline page vector */
struct page *inline_pages[FUSE_REQ_INLINE_PAGES];
/** inline page-descriptor vector */
struct fuse_page_desc inline_page_descs[FUSE_REQ_INLINE_PAGES];
/** number of pages in vector */
unsigned num_pages;
/** File used in the request (or NULL) */
struct fuse_file *ff;
/** Inode used in the request or NULL */
struct inode *inode;
/** AIO control block */
struct fuse_io_priv *io;
/** 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;
/** Refcount */
atomic_t count;
struct rcu_head rcu;
/** The user id for this mount */
kuid_t user_id;
/** The group id for this mount */
kgid_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 that INIT reply has been received. Allocating
* any fuse request will be suspended until the flag is set */
int initialized;
/** 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;
/** write-back cache policy (default is write-through) */
unsigned writeback_cache:1;
/*
* The following bitfields are only for optimization purposes
* and hence races in setting them will not cause malfunction
*/
/** Is open/release not implemented by fs? */
unsigned no_open:1;
/** 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;
/** Is fallocate not implemented by fs? */
unsigned no_fallocate:1;
/** Is rename with flags implemented by fs? */
unsigned no_rename2:1;
/** Use enhanced/automatic page cache invalidation. */
unsigned auto_inval_data:1;
/** Does the filesystem support readdirplus? */
unsigned do_readdirplus:1;
/** Does the filesystem want adaptive readdirplus? */
unsigned readdirplus_auto:1;
/** Does the filesystem support asynchronous direct-IO submission? */
unsigned async_dio: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);
/* Used by READDIRPLUS */
void fuse_force_forget(struct file *file, u64 nodeid);
/**
* 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 __exit fuse_ctl_cleanup(void);
/**
* Allocate a request
*/
struct fuse_req *fuse_request_alloc(unsigned npages);
struct fuse_req *fuse_request_alloc_nofs(unsigned npages);
/**
* Free a request
*/
void fuse_request_free(struct fuse_req *req);
/**
* Get a request, may fail with -ENOMEM,
* caller should specify # elements in req->pages[] explicitly
*/
struct fuse_req *fuse_get_req(struct fuse_conn *fc, unsigned npages);
struct fuse_req *fuse_get_req_for_background(struct fuse_conn *fc,
unsigned npages);
/*
* Increment reference count on request
*/
void __fuse_get_request(struct fuse_req *req);
/**
* Get a request, may fail with -ENOMEM,
* useful for callers who doesn't use req->pages[]
*/
static inline struct fuse_req *fuse_get_req_nopages(struct fuse_conn *fc)
{
return fuse_get_req(fc, 0);
}
/**
* Gets a requests for a file operation, always succeeds
*/
struct fuse_req *fuse_get_req_nofail_nopages(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);
void fuse_invalidate_atime(struct inode *inode);
/**
* 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 current process allowed to perform filesystem operation?
*/
int fuse_allow_current_process(struct fuse_conn *fc);
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);
/**
* fuse_direct_io() flags
*/
/** If set, it is WRITE; otherwise - READ */
#define FUSE_DIO_WRITE (1 << 0)
/** CUSE pass fuse_direct_io() a file which f_mapping->host is not from FUSE */
#define FUSE_DIO_CUSE (1 << 1)
ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
loff_t *ppos, int flags);
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);
bool fuse_write_update_size(struct inode *inode, loff_t pos);
int fuse_flush_times(struct inode *inode, struct fuse_file *ff);
int fuse_write_inode(struct inode *inode, struct writeback_control *wbc);
int fuse_do_setattr(struct inode *inode, struct iattr *attr,
struct file *file);
#endif /* _FS_FUSE_I_H */

1346
fs/fuse/inode.c Normal file

File diff suppressed because it is too large Load diff