mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 09:08:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
27
fs/fuse/Kconfig
Normal file
27
fs/fuse/Kconfig
Normal 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
8
fs/fuse/Makefile
Normal 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
354
fs/fuse/control.c
Normal 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
645
fs/fuse/cuse.c
Normal 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
2195
fs/fuse/dev.c
Normal file
File diff suppressed because it is too large
Load diff
2077
fs/fuse/dir.c
Normal file
2077
fs/fuse/dir.c
Normal file
File diff suppressed because it is too large
Load diff
3126
fs/fuse/file.c
Normal file
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
900
fs/fuse/fuse_i.h
Normal 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
1346
fs/fuse/inode.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue