fuse update for 5.4
-----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCXYod6wAKCRDh3BK/laaZ PG3fAP9WXuvUeYh3X7ThQPa2D33VCIMJRd6t+1TVSSc/H8P3dAD/ehN5HIWjnmzz iZFc3zDtO9UCJUe23IZomblxOQbu6Qk= =I0S2 -----END PGP SIGNATURE----- Merge tag 'fuse-update-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse Pull fuse updates from Miklos Szeredi: - Continue separating the transport (user/kernel communication) and the filesystem layers of fuse. Getting rid of most layering violations will allow for easier cleanup and optimization later on. - Prepare for the addition of the virtio-fs filesystem. The actual filesystem will be introduced by a separate pull request. - Convert to new mount API. - Various fixes, optimizations and cleanups. * tag 'fuse-update-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: (55 commits) fuse: Make fuse_args_to_req static fuse: fix memleak in cuse_channel_open fuse: fix beyond-end-of-page access in fuse_parse_cache() fuse: unexport fuse_put_request fuse: kmemcg account fs data fuse: on 64-bit store time in d_fsdata directly fuse: fix missing unlock_page in fuse_writepage() fuse: reserve byteswapped init opcodes fuse: allow skipping control interface and forced unmount fuse: dissociate DESTROY from fuseblk fuse: delete dentry if timeout is zero fuse: separate fuse device allocation and installation in fuse_conn fuse: add fuse_iqueue_ops callbacks fuse: extract fuse_fill_super_common() fuse: export fuse_dequeue_forget() function fuse: export fuse_get_unique() fuse: export fuse_send_init_request() fuse: export fuse_len_args() fuse: export fuse_end_request() fuse: fix request limit ...
This commit is contained in:
commit
7b1373dd6e
|
@ -504,7 +504,6 @@ void put_fs_context(struct fs_context *fc)
|
|||
put_net(fc->net_ns);
|
||||
put_user_ns(fc->user_ns);
|
||||
put_cred(fc->cred);
|
||||
kfree(fc->subtype);
|
||||
put_fc_log(fc);
|
||||
put_filesystem(fc->fs_type);
|
||||
kfree(fc->source);
|
||||
|
@ -571,17 +570,6 @@ static int legacy_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if ((fc->fs_type->fs_flags & FS_HAS_SUBTYPE) &&
|
||||
strcmp(param->key, "subtype") == 0) {
|
||||
if (param->type != fs_value_is_string)
|
||||
return invalf(fc, "VFS: Legacy: Non-string subtype");
|
||||
if (fc->subtype)
|
||||
return invalf(fc, "VFS: Legacy: Multiple subtype");
|
||||
fc->subtype = param->string;
|
||||
param->string = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ctx->param_type == LEGACY_FS_MONOLITHIC_PARAMS)
|
||||
return invalf(fc, "VFS: Legacy: Can't mix monolithic and individual options");
|
||||
|
||||
|
@ -738,8 +726,6 @@ void vfs_clean_context(struct fs_context *fc)
|
|||
fc->s_fs_info = NULL;
|
||||
fc->sb_flags = 0;
|
||||
security_free_mnt_opts(&fc->security);
|
||||
kfree(fc->subtype);
|
||||
fc->subtype = NULL;
|
||||
kfree(fc->source);
|
||||
fc->source = NULL;
|
||||
|
||||
|
|
|
@ -142,11 +142,10 @@ static int cuse_open(struct inode *inode, struct file *file)
|
|||
|
||||
static int cuse_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
struct fuse_file *ff = file->private_data;
|
||||
struct fuse_conn *fc = ff->fc;
|
||||
|
||||
fuse_sync_release(fi, ff, file->f_flags);
|
||||
fuse_sync_release(NULL, ff, file->f_flags);
|
||||
fuse_conn_put(fc);
|
||||
|
||||
return 0;
|
||||
|
@ -299,6 +298,14 @@ static void cuse_gendev_release(struct device *dev)
|
|||
kfree(dev);
|
||||
}
|
||||
|
||||
struct cuse_init_args {
|
||||
struct fuse_args_pages ap;
|
||||
struct cuse_init_in in;
|
||||
struct cuse_init_out out;
|
||||
struct page *page;
|
||||
struct fuse_page_desc desc;
|
||||
};
|
||||
|
||||
/**
|
||||
* cuse_process_init_reply - finish initializing CUSE channel
|
||||
*
|
||||
|
@ -306,21 +313,22 @@ static void cuse_gendev_release(struct device *dev)
|
|||
* 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)
|
||||
static void cuse_process_init_reply(struct fuse_conn *fc,
|
||||
struct fuse_args *args, int error)
|
||||
{
|
||||
struct cuse_init_args *ia = container_of(args, typeof(*ia), ap.args);
|
||||
struct fuse_args_pages *ap = &ia->ap;
|
||||
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_init_out *arg = &ia->out;
|
||||
struct page *page = ap->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) {
|
||||
if (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);
|
||||
|
@ -329,7 +337,7 @@ static void cuse_process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
|
|||
/* parse init reply */
|
||||
cc->unrestricted_ioctl = arg->flags & CUSE_UNRESTRICTED_IOCTL;
|
||||
|
||||
rc = cuse_parse_devinfo(page_address(page), req->out.args[1].size,
|
||||
rc = cuse_parse_devinfo(page_address(page), ap->args.out_args[1].size,
|
||||
&devinfo);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
@ -396,7 +404,7 @@ static void cuse_process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
|
|||
dev_set_uevent_suppress(dev, 0);
|
||||
kobject_uevent(&dev->kobj, KOBJ_ADD);
|
||||
out:
|
||||
kfree(arg);
|
||||
kfree(ia);
|
||||
__free_page(page);
|
||||
return;
|
||||
|
||||
|
@ -415,55 +423,49 @@ err:
|
|||
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;
|
||||
struct cuse_init_args *ia;
|
||||
struct fuse_args_pages *ap;
|
||||
|
||||
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;
|
||||
goto err;
|
||||
|
||||
outarg = kzalloc(sizeof(struct cuse_init_out), GFP_KERNEL);
|
||||
if (!outarg)
|
||||
ia = kzalloc(sizeof(*ia), GFP_KERNEL);
|
||||
if (!ia)
|
||||
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;
|
||||
ap = &ia->ap;
|
||||
ia->in.major = FUSE_KERNEL_VERSION;
|
||||
ia->in.minor = FUSE_KERNEL_MINOR_VERSION;
|
||||
ia->in.flags |= CUSE_UNRESTRICTED_IOCTL;
|
||||
ap->args.opcode = CUSE_INIT;
|
||||
ap->args.in_numargs = 1;
|
||||
ap->args.in_args[0].size = sizeof(ia->in);
|
||||
ap->args.in_args[0].value = &ia->in;
|
||||
ap->args.out_numargs = 2;
|
||||
ap->args.out_args[0].size = sizeof(ia->out);
|
||||
ap->args.out_args[0].value = &ia->out;
|
||||
ap->args.out_args[1].size = CUSE_INIT_INFO_MAX;
|
||||
ap->args.out_argvar = 1;
|
||||
ap->args.out_pages = 1;
|
||||
ap->num_pages = 1;
|
||||
ap->pages = &ia->page;
|
||||
ap->descs = &ia->desc;
|
||||
ia->page = page;
|
||||
ia->desc.length = ap->args.out_args[1].size;
|
||||
ap->args.end = cuse_process_init_reply;
|
||||
|
||||
rc = fuse_simple_background(fc, &ap->args, GFP_KERNEL);
|
||||
if (rc) {
|
||||
kfree(ia);
|
||||
err_free_page:
|
||||
__free_page(page);
|
||||
err_put_req:
|
||||
fuse_put_request(fc, req);
|
||||
__free_page(page);
|
||||
}
|
||||
err:
|
||||
return rc;
|
||||
}
|
||||
|
@ -504,9 +506,9 @@ static int cuse_channel_open(struct inode *inode, struct file *file)
|
|||
* Limit the cuse channel to requests that can
|
||||
* be represented in file->f_cred->user_ns.
|
||||
*/
|
||||
fuse_conn_init(&cc->fc, file->f_cred->user_ns);
|
||||
fuse_conn_init(&cc->fc, file->f_cred->user_ns, &fuse_dev_fiq_ops, NULL);
|
||||
|
||||
fud = fuse_dev_alloc(&cc->fc);
|
||||
fud = fuse_dev_alloc_install(&cc->fc);
|
||||
if (!fud) {
|
||||
kfree(cc);
|
||||
return -ENOMEM;
|
||||
|
@ -519,6 +521,7 @@ static int cuse_channel_open(struct inode *inode, struct file *file)
|
|||
rc = cuse_send_init(cc);
|
||||
if (rc) {
|
||||
fuse_dev_free(fud);
|
||||
fuse_conn_put(&cc->fc);
|
||||
return rc;
|
||||
}
|
||||
file->private_data = fud;
|
||||
|
|
664
fs/fuse/dev.c
664
fs/fuse/dev.c
File diff suppressed because it is too large
Load Diff
285
fs/fuse/dir.c
285
fs/fuse/dir.c
|
@ -24,20 +24,54 @@ static void fuse_advise_use_readdirplus(struct inode *dir)
|
|||
set_bit(FUSE_I_ADVISE_RDPLUS, &fi->state);
|
||||
}
|
||||
|
||||
#if BITS_PER_LONG >= 64
|
||||
static inline void __fuse_dentry_settime(struct dentry *entry, u64 time)
|
||||
{
|
||||
entry->d_fsdata = (void *) time;
|
||||
}
|
||||
|
||||
static inline u64 fuse_dentry_time(const struct dentry *entry)
|
||||
{
|
||||
return (u64)entry->d_fsdata;
|
||||
}
|
||||
|
||||
#else
|
||||
union fuse_dentry {
|
||||
u64 time;
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
static inline void fuse_dentry_settime(struct dentry *entry, u64 time)
|
||||
static inline void __fuse_dentry_settime(struct dentry *dentry, u64 time)
|
||||
{
|
||||
((union fuse_dentry *) entry->d_fsdata)->time = time;
|
||||
((union fuse_dentry *) dentry->d_fsdata)->time = time;
|
||||
}
|
||||
|
||||
static inline u64 fuse_dentry_time(struct dentry *entry)
|
||||
static inline u64 fuse_dentry_time(const struct dentry *entry)
|
||||
{
|
||||
return ((union fuse_dentry *) entry->d_fsdata)->time;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void fuse_dentry_settime(struct dentry *dentry, u64 time)
|
||||
{
|
||||
struct fuse_conn *fc = get_fuse_conn_super(dentry->d_sb);
|
||||
bool delete = !time && fc->delete_stale;
|
||||
/*
|
||||
* Mess with DCACHE_OP_DELETE because dput() will be faster without it.
|
||||
* Don't care about races, either way it's just an optimization
|
||||
*/
|
||||
if ((!delete && (dentry->d_flags & DCACHE_OP_DELETE)) ||
|
||||
(delete && !(dentry->d_flags & DCACHE_OP_DELETE))) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (!delete)
|
||||
dentry->d_flags &= ~DCACHE_OP_DELETE;
|
||||
else
|
||||
dentry->d_flags |= DCACHE_OP_DELETE;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
|
||||
__fuse_dentry_settime(dentry, time);
|
||||
}
|
||||
|
||||
/*
|
||||
* FUSE caches dentries and attributes with separate timeout. The
|
||||
|
@ -139,14 +173,14 @@ static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args,
|
|||
struct fuse_entry_out *outarg)
|
||||
{
|
||||
memset(outarg, 0, sizeof(struct fuse_entry_out));
|
||||
args->in.h.opcode = FUSE_LOOKUP;
|
||||
args->in.h.nodeid = nodeid;
|
||||
args->in.numargs = 1;
|
||||
args->in.args[0].size = name->len + 1;
|
||||
args->in.args[0].value = name->name;
|
||||
args->out.numargs = 1;
|
||||
args->out.args[0].size = sizeof(struct fuse_entry_out);
|
||||
args->out.args[0].value = outarg;
|
||||
args->opcode = FUSE_LOOKUP;
|
||||
args->nodeid = nodeid;
|
||||
args->in_numargs = 1;
|
||||
args->in_args[0].size = name->len + 1;
|
||||
args->in_args[0].value = name->name;
|
||||
args->out_numargs = 1;
|
||||
args->out_args[0].size = sizeof(struct fuse_entry_out);
|
||||
args->out_args[0].value = outarg;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -242,9 +276,11 @@ invalid:
|
|||
goto out;
|
||||
}
|
||||
|
||||
#if BITS_PER_LONG < 64
|
||||
static int fuse_dentry_init(struct dentry *dentry)
|
||||
{
|
||||
dentry->d_fsdata = kzalloc(sizeof(union fuse_dentry), GFP_KERNEL);
|
||||
dentry->d_fsdata = kzalloc(sizeof(union fuse_dentry),
|
||||
GFP_KERNEL_ACCOUNT | __GFP_RECLAIMABLE);
|
||||
|
||||
return dentry->d_fsdata ? 0 : -ENOMEM;
|
||||
}
|
||||
|
@ -254,16 +290,27 @@ static void fuse_dentry_release(struct dentry *dentry)
|
|||
|
||||
kfree_rcu(fd, rcu);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int fuse_dentry_delete(const struct dentry *dentry)
|
||||
{
|
||||
return time_before64(fuse_dentry_time(dentry), get_jiffies_64());
|
||||
}
|
||||
|
||||
const struct dentry_operations fuse_dentry_operations = {
|
||||
.d_revalidate = fuse_dentry_revalidate,
|
||||
.d_delete = fuse_dentry_delete,
|
||||
#if BITS_PER_LONG < 64
|
||||
.d_init = fuse_dentry_init,
|
||||
.d_release = fuse_dentry_release,
|
||||
#endif
|
||||
};
|
||||
|
||||
const struct dentry_operations fuse_root_dentry_operations = {
|
||||
#if BITS_PER_LONG < 64
|
||||
.d_init = fuse_dentry_init,
|
||||
.d_release = fuse_dentry_release,
|
||||
#endif
|
||||
};
|
||||
|
||||
int fuse_valid_type(int m)
|
||||
|
@ -410,18 +457,18 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
|
|||
inarg.flags = flags;
|
||||
inarg.mode = mode;
|
||||
inarg.umask = current_umask();
|
||||
args.in.h.opcode = FUSE_CREATE;
|
||||
args.in.h.nodeid = get_node_id(dir);
|
||||
args.in.numargs = 2;
|
||||
args.in.args[0].size = sizeof(inarg);
|
||||
args.in.args[0].value = &inarg;
|
||||
args.in.args[1].size = entry->d_name.len + 1;
|
||||
args.in.args[1].value = entry->d_name.name;
|
||||
args.out.numargs = 2;
|
||||
args.out.args[0].size = sizeof(outentry);
|
||||
args.out.args[0].value = &outentry;
|
||||
args.out.args[1].size = sizeof(outopen);
|
||||
args.out.args[1].value = &outopen;
|
||||
args.opcode = FUSE_CREATE;
|
||||
args.nodeid = get_node_id(dir);
|
||||
args.in_numargs = 2;
|
||||
args.in_args[0].size = sizeof(inarg);
|
||||
args.in_args[0].value = &inarg;
|
||||
args.in_args[1].size = entry->d_name.len + 1;
|
||||
args.in_args[1].value = entry->d_name.name;
|
||||
args.out_numargs = 2;
|
||||
args.out_args[0].size = sizeof(outentry);
|
||||
args.out_args[0].value = &outentry;
|
||||
args.out_args[1].size = sizeof(outopen);
|
||||
args.out_args[1].value = &outopen;
|
||||
err = fuse_simple_request(fc, &args);
|
||||
if (err)
|
||||
goto out_free_ff;
|
||||
|
@ -526,10 +573,10 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_args *args,
|
|||
return -ENOMEM;
|
||||
|
||||
memset(&outarg, 0, sizeof(outarg));
|
||||
args->in.h.nodeid = get_node_id(dir);
|
||||
args->out.numargs = 1;
|
||||
args->out.args[0].size = sizeof(outarg);
|
||||
args->out.args[0].value = &outarg;
|
||||
args->nodeid = get_node_id(dir);
|
||||
args->out_numargs = 1;
|
||||
args->out_args[0].size = sizeof(outarg);
|
||||
args->out_args[0].value = &outarg;
|
||||
err = fuse_simple_request(fc, args);
|
||||
if (err)
|
||||
goto out_put_forget_req;
|
||||
|
@ -582,12 +629,12 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, umode_t mode,
|
|||
inarg.mode = mode;
|
||||
inarg.rdev = new_encode_dev(rdev);
|
||||
inarg.umask = current_umask();
|
||||
args.in.h.opcode = FUSE_MKNOD;
|
||||
args.in.numargs = 2;
|
||||
args.in.args[0].size = sizeof(inarg);
|
||||
args.in.args[0].value = &inarg;
|
||||
args.in.args[1].size = entry->d_name.len + 1;
|
||||
args.in.args[1].value = entry->d_name.name;
|
||||
args.opcode = FUSE_MKNOD;
|
||||
args.in_numargs = 2;
|
||||
args.in_args[0].size = sizeof(inarg);
|
||||
args.in_args[0].value = &inarg;
|
||||
args.in_args[1].size = entry->d_name.len + 1;
|
||||
args.in_args[1].value = entry->d_name.name;
|
||||
return create_new_entry(fc, &args, dir, entry, mode);
|
||||
}
|
||||
|
||||
|
@ -609,12 +656,12 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, umode_t mode)
|
|||
memset(&inarg, 0, sizeof(inarg));
|
||||
inarg.mode = mode;
|
||||
inarg.umask = current_umask();
|
||||
args.in.h.opcode = FUSE_MKDIR;
|
||||
args.in.numargs = 2;
|
||||
args.in.args[0].size = sizeof(inarg);
|
||||
args.in.args[0].value = &inarg;
|
||||
args.in.args[1].size = entry->d_name.len + 1;
|
||||
args.in.args[1].value = entry->d_name.name;
|
||||
args.opcode = FUSE_MKDIR;
|
||||
args.in_numargs = 2;
|
||||
args.in_args[0].size = sizeof(inarg);
|
||||
args.in_args[0].value = &inarg;
|
||||
args.in_args[1].size = entry->d_name.len + 1;
|
||||
args.in_args[1].value = entry->d_name.name;
|
||||
return create_new_entry(fc, &args, dir, entry, S_IFDIR);
|
||||
}
|
||||
|
||||
|
@ -625,12 +672,12 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry,
|
|||
unsigned len = strlen(link) + 1;
|
||||
FUSE_ARGS(args);
|
||||
|
||||
args.in.h.opcode = FUSE_SYMLINK;
|
||||
args.in.numargs = 2;
|
||||
args.in.args[0].size = entry->d_name.len + 1;
|
||||
args.in.args[0].value = entry->d_name.name;
|
||||
args.in.args[1].size = len;
|
||||
args.in.args[1].value = link;
|
||||
args.opcode = FUSE_SYMLINK;
|
||||
args.in_numargs = 2;
|
||||
args.in_args[0].size = entry->d_name.len + 1;
|
||||
args.in_args[0].value = entry->d_name.name;
|
||||
args.in_args[1].size = len;
|
||||
args.in_args[1].value = link;
|
||||
return create_new_entry(fc, &args, dir, entry, S_IFLNK);
|
||||
}
|
||||
|
||||
|
@ -648,11 +695,11 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
|
|||
struct fuse_conn *fc = get_fuse_conn(dir);
|
||||
FUSE_ARGS(args);
|
||||
|
||||
args.in.h.opcode = FUSE_UNLINK;
|
||||
args.in.h.nodeid = get_node_id(dir);
|
||||
args.in.numargs = 1;
|
||||
args.in.args[0].size = entry->d_name.len + 1;
|
||||
args.in.args[0].value = entry->d_name.name;
|
||||
args.opcode = FUSE_UNLINK;
|
||||
args.nodeid = get_node_id(dir);
|
||||
args.in_numargs = 1;
|
||||
args.in_args[0].size = entry->d_name.len + 1;
|
||||
args.in_args[0].value = entry->d_name.name;
|
||||
err = fuse_simple_request(fc, &args);
|
||||
if (!err) {
|
||||
struct inode *inode = d_inode(entry);
|
||||
|
@ -684,11 +731,11 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
|
|||
struct fuse_conn *fc = get_fuse_conn(dir);
|
||||
FUSE_ARGS(args);
|
||||
|
||||
args.in.h.opcode = FUSE_RMDIR;
|
||||
args.in.h.nodeid = get_node_id(dir);
|
||||
args.in.numargs = 1;
|
||||
args.in.args[0].size = entry->d_name.len + 1;
|
||||
args.in.args[0].value = entry->d_name.name;
|
||||
args.opcode = FUSE_RMDIR;
|
||||
args.nodeid = get_node_id(dir);
|
||||
args.in_numargs = 1;
|
||||
args.in_args[0].size = entry->d_name.len + 1;
|
||||
args.in_args[0].value = entry->d_name.name;
|
||||
err = fuse_simple_request(fc, &args);
|
||||
if (!err) {
|
||||
clear_nlink(d_inode(entry));
|
||||
|
@ -711,15 +758,15 @@ static int fuse_rename_common(struct inode *olddir, struct dentry *oldent,
|
|||
memset(&inarg, 0, argsize);
|
||||
inarg.newdir = get_node_id(newdir);
|
||||
inarg.flags = flags;
|
||||
args.in.h.opcode = opcode;
|
||||
args.in.h.nodeid = get_node_id(olddir);
|
||||
args.in.numargs = 3;
|
||||
args.in.args[0].size = argsize;
|
||||
args.in.args[0].value = &inarg;
|
||||
args.in.args[1].size = oldent->d_name.len + 1;
|
||||
args.in.args[1].value = oldent->d_name.name;
|
||||
args.in.args[2].size = newent->d_name.len + 1;
|
||||
args.in.args[2].value = newent->d_name.name;
|
||||
args.opcode = opcode;
|
||||
args.nodeid = get_node_id(olddir);
|
||||
args.in_numargs = 3;
|
||||
args.in_args[0].size = argsize;
|
||||
args.in_args[0].value = &inarg;
|
||||
args.in_args[1].size = oldent->d_name.len + 1;
|
||||
args.in_args[1].value = oldent->d_name.name;
|
||||
args.in_args[2].size = newent->d_name.len + 1;
|
||||
args.in_args[2].value = newent->d_name.name;
|
||||
err = fuse_simple_request(fc, &args);
|
||||
if (!err) {
|
||||
/* ctime changes */
|
||||
|
@ -796,12 +843,12 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
|
|||
|
||||
memset(&inarg, 0, sizeof(inarg));
|
||||
inarg.oldnodeid = get_node_id(inode);
|
||||
args.in.h.opcode = FUSE_LINK;
|
||||
args.in.numargs = 2;
|
||||
args.in.args[0].size = sizeof(inarg);
|
||||
args.in.args[0].value = &inarg;
|
||||
args.in.args[1].size = newent->d_name.len + 1;
|
||||
args.in.args[1].value = newent->d_name.name;
|
||||
args.opcode = FUSE_LINK;
|
||||
args.in_numargs = 2;
|
||||
args.in_args[0].size = sizeof(inarg);
|
||||
args.in_args[0].value = &inarg;
|
||||
args.in_args[1].size = newent->d_name.len + 1;
|
||||
args.in_args[1].value = newent->d_name.name;
|
||||
err = create_new_entry(fc, &args, newdir, newent, inode->i_mode);
|
||||
/* Contrary to "normal" filesystems it can happen that link
|
||||
makes two "logical" inodes point to the same "physical"
|
||||
|
@ -884,14 +931,14 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
|
|||
inarg.getattr_flags |= FUSE_GETATTR_FH;
|
||||
inarg.fh = ff->fh;
|
||||
}
|
||||
args.in.h.opcode = FUSE_GETATTR;
|
||||
args.in.h.nodeid = get_node_id(inode);
|
||||
args.in.numargs = 1;
|
||||
args.in.args[0].size = sizeof(inarg);
|
||||
args.in.args[0].value = &inarg;
|
||||
args.out.numargs = 1;
|
||||
args.out.args[0].size = sizeof(outarg);
|
||||
args.out.args[0].value = &outarg;
|
||||
args.opcode = FUSE_GETATTR;
|
||||
args.nodeid = get_node_id(inode);
|
||||
args.in_numargs = 1;
|
||||
args.in_args[0].size = sizeof(inarg);
|
||||
args.in_args[0].value = &inarg;
|
||||
args.out_numargs = 1;
|
||||
args.out_args[0].size = sizeof(outarg);
|
||||
args.out_args[0].value = &outarg;
|
||||
err = fuse_simple_request(fc, &args);
|
||||
if (!err) {
|
||||
if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
|
||||
|
@ -1056,11 +1103,11 @@ static int fuse_access(struct inode *inode, int mask)
|
|||
|
||||
memset(&inarg, 0, sizeof(inarg));
|
||||
inarg.mask = mask & (MAY_READ | MAY_WRITE | MAY_EXEC);
|
||||
args.in.h.opcode = FUSE_ACCESS;
|
||||
args.in.h.nodeid = get_node_id(inode);
|
||||
args.in.numargs = 1;
|
||||
args.in.args[0].size = sizeof(inarg);
|
||||
args.in.args[0].value = &inarg;
|
||||
args.opcode = FUSE_ACCESS;
|
||||
args.nodeid = get_node_id(inode);
|
||||
args.in_numargs = 1;
|
||||
args.in_args[0].size = sizeof(inarg);
|
||||
args.in_args[0].value = &inarg;
|
||||
err = fuse_simple_request(fc, &args);
|
||||
if (err == -ENOSYS) {
|
||||
fc->no_access = 1;
|
||||
|
@ -1152,38 +1199,36 @@ static int fuse_permission(struct inode *inode, int mask)
|
|||
static int fuse_readlink_page(struct inode *inode, struct page *page)
|
||||
{
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
struct fuse_req *req;
|
||||
int err;
|
||||
struct fuse_page_desc desc = { .length = PAGE_SIZE - 1 };
|
||||
struct fuse_args_pages ap = {
|
||||
.num_pages = 1,
|
||||
.pages = &page,
|
||||
.descs = &desc,
|
||||
};
|
||||
char *link;
|
||||
ssize_t res;
|
||||
|
||||
req = fuse_get_req(fc, 1);
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
ap.args.opcode = FUSE_READLINK;
|
||||
ap.args.nodeid = get_node_id(inode);
|
||||
ap.args.out_pages = true;
|
||||
ap.args.out_argvar = true;
|
||||
ap.args.page_zeroing = true;
|
||||
ap.args.out_numargs = 1;
|
||||
ap.args.out_args[0].size = desc.length;
|
||||
res = fuse_simple_request(fc, &ap.args);
|
||||
|
||||
req->out.page_zeroing = 1;
|
||||
req->out.argpages = 1;
|
||||
req->num_pages = 1;
|
||||
req->pages[0] = page;
|
||||
req->page_descs[0].length = PAGE_SIZE - 1;
|
||||
req->in.h.opcode = FUSE_READLINK;
|
||||
req->in.h.nodeid = get_node_id(inode);
|
||||
req->out.argvar = 1;
|
||||
req->out.numargs = 1;
|
||||
req->out.args[0].size = PAGE_SIZE - 1;
|
||||
fuse_request_send(fc, req);
|
||||
err = req->out.h.error;
|
||||
|
||||
if (!err) {
|
||||
char *link = page_address(page);
|
||||
size_t len = req->out.args[0].size;
|
||||
|
||||
BUG_ON(len >= PAGE_SIZE);
|
||||
link[len] = '\0';
|
||||
}
|
||||
|
||||
fuse_put_request(fc, req);
|
||||
fuse_invalidate_atime(inode);
|
||||
|
||||
return err;
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
if (WARN_ON(res >= PAGE_SIZE))
|
||||
return -EIO;
|
||||
|
||||
link = page_address(page);
|
||||
link[res] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *fuse_get_link(struct dentry *dentry, struct inode *inode,
|
||||
|
@ -1383,14 +1428,14 @@ static void fuse_setattr_fill(struct fuse_conn *fc, struct fuse_args *args,
|
|||
struct fuse_setattr_in *inarg_p,
|
||||
struct fuse_attr_out *outarg_p)
|
||||
{
|
||||
args->in.h.opcode = FUSE_SETATTR;
|
||||
args->in.h.nodeid = get_node_id(inode);
|
||||
args->in.numargs = 1;
|
||||
args->in.args[0].size = sizeof(*inarg_p);
|
||||
args->in.args[0].value = inarg_p;
|
||||
args->out.numargs = 1;
|
||||
args->out.args[0].size = sizeof(*outarg_p);
|
||||
args->out.args[0].value = outarg_p;
|
||||
args->opcode = FUSE_SETATTR;
|
||||
args->nodeid = get_node_id(inode);
|
||||
args->in_numargs = 1;
|
||||
args->in_args[0].size = sizeof(*inarg_p);
|
||||
args->in_args[0].value = inarg_p;
|
||||
args->out_numargs = 1;
|
||||
args->out_args[0].size = sizeof(*outarg_p);
|
||||
args->out_args[0].value = outarg_p;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
1233
fs/fuse/file.c
1233
fs/fuse/file.c
File diff suppressed because it is too large
Load Diff
358
fs/fuse/fuse_i.h
358
fs/fuse/fuse_i.h
|
@ -47,9 +47,6 @@
|
|||
/** Number of dentries for each connection in the control filesystem */
|
||||
#define FUSE_CTL_NUM_DENTRIES 5
|
||||
|
||||
/** 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;
|
||||
|
||||
|
@ -164,17 +161,15 @@ enum {
|
|||
};
|
||||
|
||||
struct fuse_conn;
|
||||
struct fuse_release_args;
|
||||
|
||||
/** FUSE specific file data */
|
||||
struct fuse_file {
|
||||
/** Fuse connection for this file */
|
||||
struct fuse_conn *fc;
|
||||
|
||||
/*
|
||||
* Request reserved for flush and release.
|
||||
* Modified under relative fuse_inode::lock.
|
||||
*/
|
||||
struct fuse_req *reserved_req;
|
||||
/* Argument space reserved for release */
|
||||
struct fuse_release_args *release_args;
|
||||
|
||||
/** Kernel file handle guaranteed to be unique */
|
||||
u64 kh;
|
||||
|
@ -229,57 +224,12 @@ struct fuse_in_arg {
|
|||
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[2];
|
||||
};
|
||||
|
||||
/** FUSE page descriptor */
|
||||
struct fuse_page_desc {
|
||||
unsigned int length;
|
||||
|
@ -287,20 +237,28 @@ struct fuse_page_desc {
|
|||
};
|
||||
|
||||
struct fuse_args {
|
||||
struct {
|
||||
struct {
|
||||
uint32_t opcode;
|
||||
uint64_t nodeid;
|
||||
} h;
|
||||
unsigned numargs;
|
||||
struct fuse_in_arg args[3];
|
||||
uint64_t nodeid;
|
||||
uint32_t opcode;
|
||||
unsigned short in_numargs;
|
||||
unsigned short out_numargs;
|
||||
bool force:1;
|
||||
bool noreply:1;
|
||||
bool nocreds:1;
|
||||
bool in_pages:1;
|
||||
bool out_pages:1;
|
||||
bool out_argvar:1;
|
||||
bool page_zeroing:1;
|
||||
bool page_replace:1;
|
||||
struct fuse_in_arg in_args[3];
|
||||
struct fuse_arg out_args[2];
|
||||
void (*end)(struct fuse_conn *fc, struct fuse_args *args, int error);
|
||||
};
|
||||
|
||||
} in;
|
||||
struct {
|
||||
unsigned argvar:1;
|
||||
unsigned numargs;
|
||||
struct fuse_arg args[2];
|
||||
} out;
|
||||
struct fuse_args_pages {
|
||||
struct fuse_args args;
|
||||
struct page **pages;
|
||||
struct fuse_page_desc *descs;
|
||||
unsigned int num_pages;
|
||||
};
|
||||
|
||||
#define FUSE_ARGS(args) struct fuse_args args = {}
|
||||
|
@ -373,83 +331,70 @@ struct fuse_req {
|
|||
/** Entry on the interrupts list */
|
||||
struct list_head intr_entry;
|
||||
|
||||
/* Input/output arguments */
|
||||
struct fuse_args *args;
|
||||
|
||||
/** refcount */
|
||||
refcount_t count;
|
||||
|
||||
/* Request flags, updated with test/set/clear_bit() */
|
||||
unsigned long flags;
|
||||
|
||||
/** The request input */
|
||||
struct fuse_in in;
|
||||
/* The request input header */
|
||||
struct {
|
||||
struct fuse_in_header h;
|
||||
} in;
|
||||
|
||||
/** The request output */
|
||||
struct fuse_out out;
|
||||
/* The request output header */
|
||||
struct {
|
||||
struct fuse_out_header h;
|
||||
} out;
|
||||
|
||||
/** Used to wake up the task waiting for completion of request*/
|
||||
wait_queue_head_t waitq;
|
||||
|
||||
/** Data for asynchronous requests */
|
||||
union {
|
||||
struct {
|
||||
struct fuse_release_in in;
|
||||
struct inode *inode;
|
||||
} 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;
|
||||
} 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;
|
||||
};
|
||||
|
||||
struct fuse_iqueue;
|
||||
|
||||
/**
|
||||
* Input queue callbacks
|
||||
*
|
||||
* Input queue signalling is device-specific. For example, the /dev/fuse file
|
||||
* uses fiq->waitq and fasync to wake processes that are waiting on queue
|
||||
* readiness. These callbacks allow other device types to respond to input
|
||||
* queue activity.
|
||||
*/
|
||||
struct fuse_iqueue_ops {
|
||||
/**
|
||||
* Signal that a forget has been queued
|
||||
*/
|
||||
void (*wake_forget_and_unlock)(struct fuse_iqueue *fiq)
|
||||
__releases(fiq->lock);
|
||||
|
||||
/**
|
||||
* Signal that an INTERRUPT request has been queued
|
||||
*/
|
||||
void (*wake_interrupt_and_unlock)(struct fuse_iqueue *fiq)
|
||||
__releases(fiq->lock);
|
||||
|
||||
/**
|
||||
* Signal that a request has been queued
|
||||
*/
|
||||
void (*wake_pending_and_unlock)(struct fuse_iqueue *fiq)
|
||||
__releases(fiq->lock);
|
||||
};
|
||||
|
||||
/** /dev/fuse input queue operations */
|
||||
extern const struct fuse_iqueue_ops fuse_dev_fiq_ops;
|
||||
|
||||
struct fuse_iqueue {
|
||||
/** Connection established */
|
||||
unsigned connected;
|
||||
|
||||
/** Lock protecting accesses to members of this structure */
|
||||
spinlock_t lock;
|
||||
|
||||
/** Readers of the connection are waiting on this */
|
||||
wait_queue_head_t waitq;
|
||||
|
||||
|
@ -471,6 +416,12 @@ struct fuse_iqueue {
|
|||
|
||||
/** O_ASYNC requests */
|
||||
struct fasync_struct *fasync;
|
||||
|
||||
/** Device-specific callbacks */
|
||||
const struct fuse_iqueue_ops *ops;
|
||||
|
||||
/** Device-specific state */
|
||||
void *priv;
|
||||
};
|
||||
|
||||
#define FUSE_PQ_HASH_BITS 8
|
||||
|
@ -504,6 +455,29 @@ struct fuse_dev {
|
|||
struct list_head entry;
|
||||
};
|
||||
|
||||
struct fuse_fs_context {
|
||||
int fd;
|
||||
unsigned int rootmode;
|
||||
kuid_t user_id;
|
||||
kgid_t group_id;
|
||||
bool is_bdev:1;
|
||||
bool fd_present:1;
|
||||
bool rootmode_present:1;
|
||||
bool user_id_present:1;
|
||||
bool group_id_present:1;
|
||||
bool default_permissions:1;
|
||||
bool allow_other:1;
|
||||
bool destroy:1;
|
||||
bool no_control:1;
|
||||
bool no_force_umount:1;
|
||||
unsigned int max_read;
|
||||
unsigned int blksize;
|
||||
const char *subtype;
|
||||
|
||||
/* fuse_dev pointer to fill in, should contain NULL on entry */
|
||||
void **fudptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* A Fuse connection.
|
||||
*
|
||||
|
@ -584,9 +558,6 @@ struct fuse_conn {
|
|||
/** waitq for blocked connection */
|
||||
wait_queue_head_t blocked_waitq;
|
||||
|
||||
/** waitq for reserved requests */
|
||||
wait_queue_head_t reserved_req_waitq;
|
||||
|
||||
/** Connection established, cleared on umount, connection
|
||||
abort and device release */
|
||||
unsigned connected;
|
||||
|
@ -721,6 +692,18 @@ struct fuse_conn {
|
|||
/** Does the filesystem support copy_file_range? */
|
||||
unsigned no_copy_file_range:1;
|
||||
|
||||
/* Send DESTROY request */
|
||||
unsigned int destroy:1;
|
||||
|
||||
/* Delete dentries that have gone stale */
|
||||
unsigned int delete_stale:1;
|
||||
|
||||
/** Do not create entry in fusectl fs */
|
||||
unsigned int no_control:1;
|
||||
|
||||
/** Do not allow MNT_FORCE umount */
|
||||
unsigned int no_force_umount:1;
|
||||
|
||||
/** The number of requests waiting for completion */
|
||||
atomic_t num_waiting;
|
||||
|
||||
|
@ -742,9 +725,6 @@ struct fuse_conn {
|
|||
/** 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 */
|
||||
atomic64_t attr_version;
|
||||
|
||||
|
@ -820,14 +800,32 @@ void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget,
|
|||
|
||||
struct fuse_forget_link *fuse_alloc_forget(void);
|
||||
|
||||
/* Used by READDIRPLUS */
|
||||
void fuse_force_forget(struct file *file, u64 nodeid);
|
||||
struct fuse_forget_link *fuse_dequeue_forget(struct fuse_iqueue *fiq,
|
||||
unsigned int max,
|
||||
unsigned int *countp);
|
||||
|
||||
/**
|
||||
/*
|
||||
* Initialize READ or READDIR request
|
||||
*/
|
||||
void fuse_read_fill(struct fuse_req *req, struct file *file,
|
||||
loff_t pos, size_t count, int opcode);
|
||||
struct fuse_io_args {
|
||||
union {
|
||||
struct {
|
||||
struct fuse_read_in in;
|
||||
u64 attr_ver;
|
||||
} read;
|
||||
struct {
|
||||
struct fuse_write_in in;
|
||||
struct fuse_write_out out;
|
||||
} write;
|
||||
};
|
||||
struct fuse_args_pages ap;
|
||||
struct fuse_io_priv *io;
|
||||
struct fuse_file *ff;
|
||||
};
|
||||
|
||||
void fuse_read_args_fill(struct fuse_io_args *ia, struct file *file, loff_t pos,
|
||||
size_t count, int opcode);
|
||||
|
||||
|
||||
/**
|
||||
* Send OPEN or OPENDIR request
|
||||
|
@ -899,62 +897,17 @@ 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);
|
||||
|
||||
bool fuse_req_realloc_pages(struct fuse_conn *fc, struct fuse_req *req,
|
||||
gfp_t flags);
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Simple request sending that does request allocation and freeing
|
||||
*/
|
||||
ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args);
|
||||
int fuse_simple_background(struct fuse_conn *fc, struct fuse_args *args,
|
||||
gfp_t gfp_flags);
|
||||
|
||||
/**
|
||||
* Send a request in the background
|
||||
* End a finished request
|
||||
*/
|
||||
void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req);
|
||||
bool fuse_request_queue_background(struct fuse_conn *fc, struct fuse_req *req);
|
||||
void fuse_request_end(struct fuse_conn *fc, struct fuse_req *req);
|
||||
|
||||
/* Abort all requests */
|
||||
void fuse_abort_conn(struct fuse_conn *fc);
|
||||
|
@ -980,15 +933,33 @@ struct fuse_conn *fuse_conn_get(struct fuse_conn *fc);
|
|||
/**
|
||||
* Initialize fuse_conn
|
||||
*/
|
||||
void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns);
|
||||
void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns,
|
||||
const struct fuse_iqueue_ops *fiq_ops, void *fiq_priv);
|
||||
|
||||
/**
|
||||
* Release reference to fuse_conn
|
||||
*/
|
||||
void fuse_conn_put(struct fuse_conn *fc);
|
||||
|
||||
struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc);
|
||||
struct fuse_dev *fuse_dev_alloc_install(struct fuse_conn *fc);
|
||||
struct fuse_dev *fuse_dev_alloc(void);
|
||||
void fuse_dev_install(struct fuse_dev *fud, struct fuse_conn *fc);
|
||||
void fuse_dev_free(struct fuse_dev *fud);
|
||||
void fuse_send_init(struct fuse_conn *fc);
|
||||
|
||||
/**
|
||||
* Fill in superblock and initialize fuse connection
|
||||
* @sb: partially-initialized superblock to fill in
|
||||
* @ctx: mount context
|
||||
*/
|
||||
int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx);
|
||||
|
||||
/**
|
||||
* Disassociate fuse connection from superblock and kill the superblock
|
||||
*
|
||||
* Calls kill_anon_super(), do not use with bdev mounts.
|
||||
*/
|
||||
void fuse_kill_sb_anon(struct super_block *sb);
|
||||
|
||||
/**
|
||||
* Add connection to control filesystem
|
||||
|
@ -1093,4 +1064,15 @@ int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type);
|
|||
/* readdir.c */
|
||||
int fuse_readdir(struct file *file, struct dir_context *ctx);
|
||||
|
||||
/**
|
||||
* Return the number of bytes in an arguments list
|
||||
*/
|
||||
unsigned int fuse_len_args(unsigned int numargs, struct fuse_arg *args);
|
||||
|
||||
/**
|
||||
* Get the next unique ID for a request
|
||||
*/
|
||||
u64 fuse_get_unique(struct fuse_iqueue *fiq);
|
||||
void fuse_free_conn(struct fuse_conn *fc);
|
||||
|
||||
#endif /* _FS_FUSE_I_H */
|
||||
|
|
563
fs/fuse/inode.c
563
fs/fuse/inode.c
|
@ -15,7 +15,8 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/fs_context.h>
|
||||
#include <linux/fs_parser.h>
|
||||
#include <linux/statfs.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/sched.h>
|
||||
|
@ -59,24 +60,13 @@ MODULE_PARM_DESC(max_user_congthresh,
|
|||
/** Congestion starts at 75% of maximum */
|
||||
#define FUSE_DEFAULT_CONGESTION_THRESHOLD (FUSE_DEFAULT_MAX_BACKGROUND * 3 / 4)
|
||||
|
||||
struct fuse_mount_data {
|
||||
int fd;
|
||||
unsigned rootmode;
|
||||
kuid_t user_id;
|
||||
kgid_t group_id;
|
||||
unsigned fd_present:1;
|
||||
unsigned rootmode_present:1;
|
||||
unsigned user_id_present:1;
|
||||
unsigned group_id_present:1;
|
||||
unsigned default_permissions:1;
|
||||
unsigned allow_other:1;
|
||||
unsigned max_read;
|
||||
unsigned blksize;
|
||||
};
|
||||
#ifdef CONFIG_BLOCK
|
||||
static struct file_system_type fuseblk_fs_type;
|
||||
#endif
|
||||
|
||||
struct fuse_forget_link *fuse_alloc_forget(void)
|
||||
{
|
||||
return kzalloc(sizeof(struct fuse_forget_link), GFP_KERNEL);
|
||||
return kzalloc(sizeof(struct fuse_forget_link), GFP_KERNEL_ACCOUNT);
|
||||
}
|
||||
|
||||
static struct inode *fuse_alloc_inode(struct super_block *sb)
|
||||
|
@ -374,19 +364,21 @@ void fuse_unlock_inode(struct inode *inode, bool locked)
|
|||
|
||||
static void fuse_umount_begin(struct super_block *sb)
|
||||
{
|
||||
fuse_abort_conn(get_fuse_conn_super(sb));
|
||||
struct fuse_conn *fc = get_fuse_conn_super(sb);
|
||||
|
||||
if (!fc->no_force_umount)
|
||||
fuse_abort_conn(fc);
|
||||
}
|
||||
|
||||
static void fuse_send_destroy(struct fuse_conn *fc)
|
||||
{
|
||||
struct fuse_req *req = fc->destroy_req;
|
||||
if (req && fc->conn_init) {
|
||||
fc->destroy_req = NULL;
|
||||
req->in.h.opcode = FUSE_DESTROY;
|
||||
__set_bit(FR_FORCE, &req->flags);
|
||||
__clear_bit(FR_BACKGROUND, &req->flags);
|
||||
fuse_request_send(fc, req);
|
||||
fuse_put_request(fc, req);
|
||||
if (fc->conn_init) {
|
||||
FUSE_ARGS(args);
|
||||
|
||||
args.opcode = FUSE_DESTROY;
|
||||
args.force = true;
|
||||
args.nocreds = true;
|
||||
fuse_simple_request(fc, &args);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -430,12 +422,12 @@ static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|||
}
|
||||
|
||||
memset(&outarg, 0, sizeof(outarg));
|
||||
args.in.numargs = 0;
|
||||
args.in.h.opcode = FUSE_STATFS;
|
||||
args.in.h.nodeid = get_node_id(d_inode(dentry));
|
||||
args.out.numargs = 1;
|
||||
args.out.args[0].size = sizeof(outarg);
|
||||
args.out.args[0].value = &outarg;
|
||||
args.in_numargs = 0;
|
||||
args.opcode = FUSE_STATFS;
|
||||
args.nodeid = get_node_id(d_inode(dentry));
|
||||
args.out_numargs = 1;
|
||||
args.out_args[0].size = sizeof(outarg);
|
||||
args.out_args[0].value = &outarg;
|
||||
err = fuse_simple_request(fc, &args);
|
||||
if (!err)
|
||||
convert_fuse_statfs(buf, &outarg.st);
|
||||
|
@ -443,6 +435,8 @@ static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|||
}
|
||||
|
||||
enum {
|
||||
OPT_SOURCE,
|
||||
OPT_SUBTYPE,
|
||||
OPT_FD,
|
||||
OPT_ROOTMODE,
|
||||
OPT_USER_ID,
|
||||
|
@ -454,111 +448,109 @@ enum {
|
|||
OPT_ERR
|
||||
};
|
||||
|
||||
static const match_table_t tokens = {
|
||||
{OPT_FD, "fd=%u"},
|
||||
{OPT_ROOTMODE, "rootmode=%o"},
|
||||
{OPT_USER_ID, "user_id=%u"},
|
||||
{OPT_GROUP_ID, "group_id=%u"},
|
||||
{OPT_DEFAULT_PERMISSIONS, "default_permissions"},
|
||||
{OPT_ALLOW_OTHER, "allow_other"},
|
||||
{OPT_MAX_READ, "max_read=%u"},
|
||||
{OPT_BLKSIZE, "blksize=%u"},
|
||||
{OPT_ERR, NULL}
|
||||
static const struct fs_parameter_spec fuse_param_specs[] = {
|
||||
fsparam_string ("source", OPT_SOURCE),
|
||||
fsparam_u32 ("fd", OPT_FD),
|
||||
fsparam_u32oct ("rootmode", OPT_ROOTMODE),
|
||||
fsparam_u32 ("user_id", OPT_USER_ID),
|
||||
fsparam_u32 ("group_id", OPT_GROUP_ID),
|
||||
fsparam_flag ("default_permissions", OPT_DEFAULT_PERMISSIONS),
|
||||
fsparam_flag ("allow_other", OPT_ALLOW_OTHER),
|
||||
fsparam_u32 ("max_read", OPT_MAX_READ),
|
||||
fsparam_u32 ("blksize", OPT_BLKSIZE),
|
||||
fsparam_string ("subtype", OPT_SUBTYPE),
|
||||
{}
|
||||
};
|
||||
|
||||
static int fuse_match_uint(substring_t *s, unsigned int *res)
|
||||
static const struct fs_parameter_description fuse_fs_parameters = {
|
||||
.name = "fuse",
|
||||
.specs = fuse_param_specs,
|
||||
};
|
||||
|
||||
static int fuse_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
{
|
||||
int err = -ENOMEM;
|
||||
char *buf = match_strdup(s);
|
||||
if (buf) {
|
||||
err = kstrtouint(buf, 10, res);
|
||||
kfree(buf);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
struct fs_parse_result result;
|
||||
struct fuse_fs_context *ctx = fc->fs_private;
|
||||
int opt;
|
||||
|
||||
static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev,
|
||||
struct user_namespace *user_ns)
|
||||
{
|
||||
char *p;
|
||||
memset(d, 0, sizeof(struct fuse_mount_data));
|
||||
d->max_read = ~0;
|
||||
d->blksize = FUSE_DEFAULT_BLKSIZE;
|
||||
opt = fs_parse(fc, &fuse_fs_parameters, param, &result);
|
||||
if (opt < 0)
|
||||
return opt;
|
||||
|
||||
while ((p = strsep(&opt, ",")) != NULL) {
|
||||
int token;
|
||||
int value;
|
||||
unsigned uv;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
if (!*p)
|
||||
continue;
|
||||
switch (opt) {
|
||||
case OPT_SOURCE:
|
||||
if (fc->source)
|
||||
return invalf(fc, "fuse: Multiple sources specified");
|
||||
fc->source = param->string;
|
||||
param->string = NULL;
|
||||
break;
|
||||
|
||||
token = match_token(p, tokens, args);
|
||||
switch (token) {
|
||||
case OPT_FD:
|
||||
if (match_int(&args[0], &value))
|
||||
return 0;
|
||||
d->fd = value;
|
||||
d->fd_present = 1;
|
||||
break;
|
||||
|
||||
case OPT_ROOTMODE:
|
||||
if (match_octal(&args[0], &value))
|
||||
return 0;
|
||||
if (!fuse_valid_type(value))
|
||||
return 0;
|
||||
d->rootmode = value;
|
||||
d->rootmode_present = 1;
|
||||
break;
|
||||
|
||||
case OPT_USER_ID:
|
||||
if (fuse_match_uint(&args[0], &uv))
|
||||
return 0;
|
||||
d->user_id = make_kuid(user_ns, uv);
|
||||
if (!uid_valid(d->user_id))
|
||||
return 0;
|
||||
d->user_id_present = 1;
|
||||
break;
|
||||
|
||||
case OPT_GROUP_ID:
|
||||
if (fuse_match_uint(&args[0], &uv))
|
||||
return 0;
|
||||
d->group_id = make_kgid(user_ns, uv);
|
||||
if (!gid_valid(d->group_id))
|
||||
return 0;
|
||||
d->group_id_present = 1;
|
||||
break;
|
||||
|
||||
case OPT_DEFAULT_PERMISSIONS:
|
||||
d->default_permissions = 1;
|
||||
break;
|
||||
|
||||
case OPT_ALLOW_OTHER:
|
||||
d->allow_other = 1;
|
||||
break;
|
||||
|
||||
case OPT_MAX_READ:
|
||||
if (match_int(&args[0], &value))
|
||||
return 0;
|
||||
d->max_read = value;
|
||||
break;
|
||||
|
||||
case OPT_BLKSIZE:
|
||||
if (!is_bdev || match_int(&args[0], &value))
|
||||
return 0;
|
||||
d->blksize = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!d->fd_present || !d->rootmode_present ||
|
||||
!d->user_id_present || !d->group_id_present)
|
||||
case OPT_SUBTYPE:
|
||||
if (ctx->subtype)
|
||||
return invalf(fc, "fuse: Multiple subtypes specified");
|
||||
ctx->subtype = param->string;
|
||||
param->string = NULL;
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
case OPT_FD:
|
||||
ctx->fd = result.uint_32;
|
||||
ctx->fd_present = 1;
|
||||
break;
|
||||
|
||||
case OPT_ROOTMODE:
|
||||
if (!fuse_valid_type(result.uint_32))
|
||||
return invalf(fc, "fuse: Invalid rootmode");
|
||||
ctx->rootmode = result.uint_32;
|
||||
ctx->rootmode_present = 1;
|
||||
break;
|
||||
|
||||
case OPT_USER_ID:
|
||||
ctx->user_id = make_kuid(fc->user_ns, result.uint_32);
|
||||
if (!uid_valid(ctx->user_id))
|
||||
return invalf(fc, "fuse: Invalid user_id");
|
||||
ctx->user_id_present = 1;
|
||||
break;
|
||||
|
||||
case OPT_GROUP_ID:
|
||||
ctx->group_id = make_kgid(fc->user_ns, result.uint_32);
|
||||
if (!gid_valid(ctx->group_id))
|
||||
return invalf(fc, "fuse: Invalid group_id");
|
||||
ctx->group_id_present = 1;
|
||||
break;
|
||||
|
||||
case OPT_DEFAULT_PERMISSIONS:
|
||||
ctx->default_permissions = 1;
|
||||
break;
|
||||
|
||||
case OPT_ALLOW_OTHER:
|
||||
ctx->allow_other = 1;
|
||||
break;
|
||||
|
||||
case OPT_MAX_READ:
|
||||
ctx->max_read = result.uint_32;
|
||||
break;
|
||||
|
||||
case OPT_BLKSIZE:
|
||||
if (!ctx->is_bdev)
|
||||
return invalf(fc, "fuse: blksize only supported for fuseblk");
|
||||
ctx->blksize = result.uint_32;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fuse_free_fc(struct fs_context *fc)
|
||||
{
|
||||
struct fuse_fs_context *ctx = fc->fs_private;
|
||||
|
||||
if (ctx) {
|
||||
kfree(ctx->subtype);
|
||||
kfree(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static int fuse_show_options(struct seq_file *m, struct dentry *root)
|
||||
|
@ -579,14 +571,19 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void fuse_iqueue_init(struct fuse_iqueue *fiq)
|
||||
static void fuse_iqueue_init(struct fuse_iqueue *fiq,
|
||||
const struct fuse_iqueue_ops *ops,
|
||||
void *priv)
|
||||
{
|
||||
memset(fiq, 0, sizeof(struct fuse_iqueue));
|
||||
spin_lock_init(&fiq->lock);
|
||||
init_waitqueue_head(&fiq->waitq);
|
||||
INIT_LIST_HEAD(&fiq->pending);
|
||||
INIT_LIST_HEAD(&fiq->interrupts);
|
||||
fiq->forget_list_tail = &fiq->forget_list_head;
|
||||
fiq->connected = 1;
|
||||
fiq->ops = ops;
|
||||
fiq->priv = priv;
|
||||
}
|
||||
|
||||
static void fuse_pqueue_init(struct fuse_pqueue *fpq)
|
||||
|
@ -600,7 +597,8 @@ static void fuse_pqueue_init(struct fuse_pqueue *fpq)
|
|||
fpq->connected = 1;
|
||||
}
|
||||
|
||||
void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns)
|
||||
void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns,
|
||||
const struct fuse_iqueue_ops *fiq_ops, void *fiq_priv)
|
||||
{
|
||||
memset(fc, 0, sizeof(*fc));
|
||||
spin_lock_init(&fc->lock);
|
||||
|
@ -609,8 +607,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns)
|
|||
refcount_set(&fc->count, 1);
|
||||
atomic_set(&fc->dev_count, 1);
|
||||
init_waitqueue_head(&fc->blocked_waitq);
|
||||
init_waitqueue_head(&fc->reserved_req_waitq);
|
||||
fuse_iqueue_init(&fc->iq);
|
||||
fuse_iqueue_init(&fc->iq, fiq_ops, fiq_priv);
|
||||
INIT_LIST_HEAD(&fc->bg_queue);
|
||||
INIT_LIST_HEAD(&fc->entry);
|
||||
INIT_LIST_HEAD(&fc->devices);
|
||||
|
@ -633,8 +630,6 @@ EXPORT_SYMBOL_GPL(fuse_conn_init);
|
|||
void fuse_conn_put(struct fuse_conn *fc)
|
||||
{
|
||||
if (refcount_dec_and_test(&fc->count)) {
|
||||
if (fc->destroy_req)
|
||||
fuse_request_free(fc->destroy_req);
|
||||
put_pid_ns(fc->pid_ns);
|
||||
put_user_ns(fc->user_ns);
|
||||
fc->release(fc);
|
||||
|
@ -822,9 +817,12 @@ static const struct super_operations fuse_super_operations = {
|
|||
|
||||
static void sanitize_global_limit(unsigned *limit)
|
||||
{
|
||||
/*
|
||||
* The default maximum number of async requests is calculated to consume
|
||||
* 1/2^13 of the total memory, assuming 392 bytes per request.
|
||||
*/
|
||||
if (*limit == 0)
|
||||
*limit = ((totalram_pages() << PAGE_SHIFT) >> 13) /
|
||||
sizeof(struct fuse_req);
|
||||
*limit = ((totalram_pages() << PAGE_SHIFT) >> 13) / 392;
|
||||
|
||||
if (*limit >= 1 << 16)
|
||||
*limit = (1 << 16) - 1;
|
||||
|
@ -870,11 +868,19 @@ static void process_init_limits(struct fuse_conn *fc, struct fuse_init_out *arg)
|
|||
spin_unlock(&fc->bg_lock);
|
||||
}
|
||||
|
||||
static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
|
||||
{
|
||||
struct fuse_init_out *arg = &req->misc.init_out;
|
||||
struct fuse_init_args {
|
||||
struct fuse_args args;
|
||||
struct fuse_init_in in;
|
||||
struct fuse_init_out out;
|
||||
};
|
||||
|
||||
if (req->out.h.error || arg->major != FUSE_KERNEL_VERSION)
|
||||
static void process_init_reply(struct fuse_conn *fc, struct fuse_args *args,
|
||||
int error)
|
||||
{
|
||||
struct fuse_init_args *ia = container_of(args, typeof(*ia), args);
|
||||
struct fuse_init_out *arg = &ia->out;
|
||||
|
||||
if (error || arg->major != FUSE_KERNEL_VERSION)
|
||||
fc->conn_error = 1;
|
||||
else {
|
||||
unsigned long ra_pages;
|
||||
|
@ -951,18 +957,23 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
|
|||
fc->max_write = max_t(unsigned, 4096, fc->max_write);
|
||||
fc->conn_init = 1;
|
||||
}
|
||||
kfree(ia);
|
||||
|
||||
fuse_set_initialized(fc);
|
||||
wake_up_all(&fc->blocked_waitq);
|
||||
}
|
||||
|
||||
static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
|
||||
void fuse_send_init(struct fuse_conn *fc)
|
||||
{
|
||||
struct fuse_init_in *arg = &req->misc.init_in;
|
||||
struct fuse_init_args *ia;
|
||||
|
||||
arg->major = FUSE_KERNEL_VERSION;
|
||||
arg->minor = FUSE_KERNEL_MINOR_VERSION;
|
||||
arg->max_readahead = fc->sb->s_bdi->ra_pages * PAGE_SIZE;
|
||||
arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC |
|
||||
ia = kzalloc(sizeof(*ia), GFP_KERNEL | __GFP_NOFAIL);
|
||||
|
||||
ia->in.major = FUSE_KERNEL_VERSION;
|
||||
ia->in.minor = FUSE_KERNEL_MINOR_VERSION;
|
||||
ia->in.max_readahead = fc->sb->s_bdi->ra_pages * PAGE_SIZE;
|
||||
ia->in.flags |=
|
||||
FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC |
|
||||
FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK |
|
||||
FUSE_SPLICE_WRITE | FUSE_SPLICE_MOVE | FUSE_SPLICE_READ |
|
||||
FUSE_FLOCK_LOCKS | FUSE_HAS_IOCTL_DIR | FUSE_AUTO_INVAL_DATA |
|
||||
|
@ -971,26 +982,32 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
|
|||
FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
|
||||
FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS |
|
||||
FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA;
|
||||
req->in.h.opcode = FUSE_INIT;
|
||||
req->in.numargs = 1;
|
||||
req->in.args[0].size = sizeof(*arg);
|
||||
req->in.args[0].value = arg;
|
||||
req->out.numargs = 1;
|
||||
ia->args.opcode = FUSE_INIT;
|
||||
ia->args.in_numargs = 1;
|
||||
ia->args.in_args[0].size = sizeof(ia->in);
|
||||
ia->args.in_args[0].value = &ia->in;
|
||||
ia->args.out_numargs = 1;
|
||||
/* Variable length argument used for backward compatibility
|
||||
with interface version < 7.5. Rest of init_out is zeroed
|
||||
by do_get_request(), so a short reply is not a problem */
|
||||
req->out.argvar = 1;
|
||||
req->out.args[0].size = sizeof(struct fuse_init_out);
|
||||
req->out.args[0].value = &req->misc.init_out;
|
||||
req->end = process_init_reply;
|
||||
fuse_request_send_background(fc, req);
|
||||
}
|
||||
ia->args.out_argvar = 1;
|
||||
ia->args.out_args[0].size = sizeof(ia->out);
|
||||
ia->args.out_args[0].value = &ia->out;
|
||||
ia->args.force = true;
|
||||
ia->args.nocreds = true;
|
||||
ia->args.end = process_init_reply;
|
||||
|
||||
static void fuse_free_conn(struct fuse_conn *fc)
|
||||
if (fuse_simple_background(fc, &ia->args, GFP_KERNEL) != 0)
|
||||
process_init_reply(fc, &ia->args, -ENOTCONN);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fuse_send_init);
|
||||
|
||||
void fuse_free_conn(struct fuse_conn *fc)
|
||||
{
|
||||
WARN_ON(!list_empty(&fc->devices));
|
||||
kfree_rcu(fc, rcu);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fuse_free_conn);
|
||||
|
||||
static int fuse_bdi_init(struct fuse_conn *fc, struct super_block *sb)
|
||||
{
|
||||
|
@ -1032,7 +1049,7 @@ static int fuse_bdi_init(struct fuse_conn *fc, struct super_block *sb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc)
|
||||
struct fuse_dev *fuse_dev_alloc(void)
|
||||
{
|
||||
struct fuse_dev *fud;
|
||||
struct list_head *pq;
|
||||
|
@ -1048,17 +1065,34 @@ struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc)
|
|||
}
|
||||
|
||||
fud->pq.processing = pq;
|
||||
fud->fc = fuse_conn_get(fc);
|
||||
fuse_pqueue_init(&fud->pq);
|
||||
|
||||
spin_lock(&fc->lock);
|
||||
list_add_tail(&fud->entry, &fc->devices);
|
||||
spin_unlock(&fc->lock);
|
||||
|
||||
return fud;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fuse_dev_alloc);
|
||||
|
||||
void fuse_dev_install(struct fuse_dev *fud, struct fuse_conn *fc)
|
||||
{
|
||||
fud->fc = fuse_conn_get(fc);
|
||||
spin_lock(&fc->lock);
|
||||
list_add_tail(&fud->entry, &fc->devices);
|
||||
spin_unlock(&fc->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fuse_dev_install);
|
||||
|
||||
struct fuse_dev *fuse_dev_alloc_install(struct fuse_conn *fc)
|
||||
{
|
||||
struct fuse_dev *fud;
|
||||
|
||||
fud = fuse_dev_alloc();
|
||||
if (!fud)
|
||||
return NULL;
|
||||
|
||||
fuse_dev_install(fud, fc);
|
||||
return fud;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fuse_dev_alloc_install);
|
||||
|
||||
void fuse_dev_free(struct fuse_dev *fud)
|
||||
{
|
||||
struct fuse_conn *fc = fud->fc;
|
||||
|
@ -1075,17 +1109,13 @@ void fuse_dev_free(struct fuse_dev *fud)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(fuse_dev_free);
|
||||
|
||||
static int fuse_fill_super(struct super_block *sb, void *data, int silent)
|
||||
int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
|
||||
{
|
||||
struct fuse_dev *fud;
|
||||
struct fuse_conn *fc;
|
||||
struct fuse_conn *fc = get_fuse_conn_super(sb);
|
||||
struct inode *root;
|
||||
struct fuse_mount_data d;
|
||||
struct file *file;
|
||||
struct dentry *root_dentry;
|
||||
struct fuse_req *init_req;
|
||||
int err;
|
||||
int is_bdev = sb->s_bdev != NULL;
|
||||
|
||||
err = -EINVAL;
|
||||
if (sb->s_flags & SB_MANDLOCK)
|
||||
|
@ -1093,19 +1123,19 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
|
|||
|
||||
sb->s_flags &= ~(SB_NOSEC | SB_I_VERSION);
|
||||
|
||||
if (!parse_fuse_opt(data, &d, is_bdev, sb->s_user_ns))
|
||||
goto err;
|
||||
|
||||
if (is_bdev) {
|
||||
if (ctx->is_bdev) {
|
||||
#ifdef CONFIG_BLOCK
|
||||
err = -EINVAL;
|
||||
if (!sb_set_blocksize(sb, d.blksize))
|
||||
if (!sb_set_blocksize(sb, ctx->blksize))
|
||||
goto err;
|
||||
#endif
|
||||
} else {
|
||||
sb->s_blocksize = PAGE_SIZE;
|
||||
sb->s_blocksize_bits = PAGE_SHIFT;
|
||||
}
|
||||
|
||||
sb->s_subtype = ctx->subtype;
|
||||
ctx->subtype = NULL;
|
||||
sb->s_magic = FUSE_SUPER_MAGIC;
|
||||
sb->s_op = &fuse_super_operations;
|
||||
sb->s_xattr = fuse_xattr_handlers;
|
||||
|
@ -1116,19 +1146,6 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
|
|||
if (sb->s_user_ns != &init_user_ns)
|
||||
sb->s_iflags |= SB_I_UNTRUSTED_MOUNTER;
|
||||
|
||||
file = fget(d.fd);
|
||||
err = -EINVAL;
|
||||
if (!file)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* Require mount to happen from the same user namespace which
|
||||
* opened /dev/fuse to prevent potential attacks.
|
||||
*/
|
||||
if (file->f_op != &fuse_dev_operations ||
|
||||
file->f_cred->user_ns != sb->s_user_ns)
|
||||
goto err_fput;
|
||||
|
||||
/*
|
||||
* If we are not in the initial user namespace posix
|
||||
* acls must be translated.
|
||||
|
@ -1136,17 +1153,9 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
|
|||
if (sb->s_user_ns != &init_user_ns)
|
||||
sb->s_xattr = fuse_no_acl_xattr_handlers;
|
||||
|
||||
fc = kmalloc(sizeof(*fc), GFP_KERNEL);
|
||||
err = -ENOMEM;
|
||||
if (!fc)
|
||||
goto err_fput;
|
||||
|
||||
fuse_conn_init(fc, sb->s_user_ns);
|
||||
fc->release = fuse_free_conn;
|
||||
|
||||
fud = fuse_dev_alloc(fc);
|
||||
fud = fuse_dev_alloc_install(fc);
|
||||
if (!fud)
|
||||
goto err_put_conn;
|
||||
goto err;
|
||||
|
||||
fc->dev = sb->s_dev;
|
||||
fc->sb = sb;
|
||||
|
@ -1159,17 +1168,17 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
|
|||
fc->dont_mask = 1;
|
||||
sb->s_flags |= SB_POSIXACL;
|
||||
|
||||
fc->default_permissions = d.default_permissions;
|
||||
fc->allow_other = d.allow_other;
|
||||
fc->user_id = d.user_id;
|
||||
fc->group_id = d.group_id;
|
||||
fc->max_read = max_t(unsigned, 4096, d.max_read);
|
||||
|
||||
/* Used by get_root_inode() */
|
||||
sb->s_fs_info = fc;
|
||||
fc->default_permissions = ctx->default_permissions;
|
||||
fc->allow_other = ctx->allow_other;
|
||||
fc->user_id = ctx->user_id;
|
||||
fc->group_id = ctx->group_id;
|
||||
fc->max_read = max_t(unsigned, 4096, ctx->max_read);
|
||||
fc->destroy = ctx->destroy;
|
||||
fc->no_control = ctx->no_control;
|
||||
fc->no_force_umount = ctx->no_force_umount;
|
||||
|
||||
err = -ENOMEM;
|
||||
root = fuse_get_root_inode(sb, d.rootmode);
|
||||
root = fuse_get_root_inode(sb, ctx->rootmode);
|
||||
sb->s_d_op = &fuse_root_dentry_operations;
|
||||
root_dentry = d_make_root(root);
|
||||
if (!root_dentry)
|
||||
|
@ -1177,20 +1186,9 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
|
|||
/* Root dentry doesn't have .d_revalidate */
|
||||
sb->s_d_op = &fuse_dentry_operations;
|
||||
|
||||
init_req = fuse_request_alloc(0);
|
||||
if (!init_req)
|
||||
goto err_put_root;
|
||||
__set_bit(FR_BACKGROUND, &init_req->flags);
|
||||
|
||||
if (is_bdev) {
|
||||
fc->destroy_req = fuse_request_alloc(0);
|
||||
if (!fc->destroy_req)
|
||||
goto err_free_init_req;
|
||||
}
|
||||
|
||||
mutex_lock(&fuse_mutex);
|
||||
err = -EINVAL;
|
||||
if (file->private_data)
|
||||
if (*ctx->fudptr)
|
||||
goto err_unlock;
|
||||
|
||||
err = fuse_ctl_add_conn(fc);
|
||||
|
@ -1199,27 +1197,62 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
|
|||
|
||||
list_add_tail(&fc->entry, &fuse_conn_list);
|
||||
sb->s_root = root_dentry;
|
||||
file->private_data = fud;
|
||||
*ctx->fudptr = fud;
|
||||
mutex_unlock(&fuse_mutex);
|
||||
return 0;
|
||||
|
||||
err_unlock:
|
||||
mutex_unlock(&fuse_mutex);
|
||||
dput(root_dentry);
|
||||
err_dev_free:
|
||||
fuse_dev_free(fud);
|
||||
err:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fuse_fill_super_common);
|
||||
|
||||
static int fuse_fill_super(struct super_block *sb, struct fs_context *fsc)
|
||||
{
|
||||
struct fuse_fs_context *ctx = fsc->fs_private;
|
||||
struct file *file;
|
||||
int err;
|
||||
struct fuse_conn *fc;
|
||||
|
||||
err = -EINVAL;
|
||||
file = fget(ctx->fd);
|
||||
if (!file)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* Require mount to happen from the same user namespace which
|
||||
* opened /dev/fuse to prevent potential attacks.
|
||||
*/
|
||||
if ((file->f_op != &fuse_dev_operations) ||
|
||||
(file->f_cred->user_ns != sb->s_user_ns))
|
||||
goto err_fput;
|
||||
ctx->fudptr = &file->private_data;
|
||||
|
||||
fc = kmalloc(sizeof(*fc), GFP_KERNEL);
|
||||
err = -ENOMEM;
|
||||
if (!fc)
|
||||
goto err_fput;
|
||||
|
||||
fuse_conn_init(fc, sb->s_user_ns, &fuse_dev_fiq_ops, NULL);
|
||||
fc->release = fuse_free_conn;
|
||||
sb->s_fs_info = fc;
|
||||
|
||||
err = fuse_fill_super_common(sb, ctx);
|
||||
if (err)
|
||||
goto err_put_conn;
|
||||
/*
|
||||
* atomic_dec_and_test() in fput() provides the necessary
|
||||
* memory barrier for file->private_data to be visible on all
|
||||
* CPUs after this
|
||||
*/
|
||||
fput(file);
|
||||
|
||||
fuse_send_init(fc, init_req);
|
||||
|
||||
fuse_send_init(get_fuse_conn_super(sb));
|
||||
return 0;
|
||||
|
||||
err_unlock:
|
||||
mutex_unlock(&fuse_mutex);
|
||||
err_free_init_req:
|
||||
fuse_request_free(init_req);
|
||||
err_put_root:
|
||||
dput(root_dentry);
|
||||
err_dev_free:
|
||||
fuse_dev_free(fud);
|
||||
err_put_conn:
|
||||
fuse_conn_put(fc);
|
||||
sb->s_fs_info = NULL;
|
||||
|
@ -1229,11 +1262,52 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
|
|||
return err;
|
||||
}
|
||||
|
||||
static struct dentry *fuse_mount(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name,
|
||||
void *raw_data)
|
||||
static int fuse_get_tree(struct fs_context *fc)
|
||||
{
|
||||
return mount_nodev(fs_type, flags, raw_data, fuse_fill_super);
|
||||
struct fuse_fs_context *ctx = fc->fs_private;
|
||||
|
||||
if (!ctx->fd_present || !ctx->rootmode_present ||
|
||||
!ctx->user_id_present || !ctx->group_id_present)
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef CONFIG_BLOCK
|
||||
if (ctx->is_bdev)
|
||||
return get_tree_bdev(fc, fuse_fill_super);
|
||||
#endif
|
||||
|
||||
return get_tree_nodev(fc, fuse_fill_super);
|
||||
}
|
||||
|
||||
static const struct fs_context_operations fuse_context_ops = {
|
||||
.free = fuse_free_fc,
|
||||
.parse_param = fuse_parse_param,
|
||||
.get_tree = fuse_get_tree,
|
||||
};
|
||||
|
||||
/*
|
||||
* Set up the filesystem mount context.
|
||||
*/
|
||||
static int fuse_init_fs_context(struct fs_context *fc)
|
||||
{
|
||||
struct fuse_fs_context *ctx;
|
||||
|
||||
ctx = kzalloc(sizeof(struct fuse_fs_context), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->max_read = ~0;
|
||||
ctx->blksize = FUSE_DEFAULT_BLKSIZE;
|
||||
|
||||
#ifdef CONFIG_BLOCK
|
||||
if (fc->fs_type == &fuseblk_fs_type) {
|
||||
ctx->is_bdev = true;
|
||||
ctx->destroy = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
fc->fs_private = ctx;
|
||||
fc->ops = &fuse_context_ops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fuse_sb_destroy(struct super_block *sb)
|
||||
|
@ -1241,7 +1315,8 @@ static void fuse_sb_destroy(struct super_block *sb)
|
|||
struct fuse_conn *fc = get_fuse_conn_super(sb);
|
||||
|
||||
if (fc) {
|
||||
fuse_send_destroy(fc);
|
||||
if (fc->destroy)
|
||||
fuse_send_destroy(fc);
|
||||
|
||||
fuse_abort_conn(fc);
|
||||
fuse_wait_aborted(fc);
|
||||
|
@ -1252,29 +1327,24 @@ static void fuse_sb_destroy(struct super_block *sb)
|
|||
}
|
||||
}
|
||||
|
||||
static void fuse_kill_sb_anon(struct super_block *sb)
|
||||
void fuse_kill_sb_anon(struct super_block *sb)
|
||||
{
|
||||
fuse_sb_destroy(sb);
|
||||
kill_anon_super(sb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fuse_kill_sb_anon);
|
||||
|
||||
static struct file_system_type fuse_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "fuse",
|
||||
.fs_flags = FS_HAS_SUBTYPE | FS_USERNS_MOUNT,
|
||||
.mount = fuse_mount,
|
||||
.init_fs_context = fuse_init_fs_context,
|
||||
.parameters = &fuse_fs_parameters,
|
||||
.kill_sb = fuse_kill_sb_anon,
|
||||
};
|
||||
MODULE_ALIAS_FS("fuse");
|
||||
|
||||
#ifdef CONFIG_BLOCK
|
||||
static struct dentry *fuse_mount_blk(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name,
|
||||
void *raw_data)
|
||||
{
|
||||
return mount_bdev(fs_type, flags, dev_name, raw_data, fuse_fill_super);
|
||||
}
|
||||
|
||||
static void fuse_kill_sb_blk(struct super_block *sb)
|
||||
{
|
||||
fuse_sb_destroy(sb);
|
||||
|
@ -1284,7 +1354,8 @@ static void fuse_kill_sb_blk(struct super_block *sb)
|
|||
static struct file_system_type fuseblk_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "fuseblk",
|
||||
.mount = fuse_mount_blk,
|
||||
.init_fs_context = fuse_init_fs_context,
|
||||
.parameters = &fuse_fs_parameters,
|
||||
.kill_sb = fuse_kill_sb_blk,
|
||||
.fs_flags = FS_REQUIRES_DEV | FS_HAS_SUBTYPE,
|
||||
};
|
||||
|
|
|
@ -249,6 +249,27 @@ retry:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void fuse_force_forget(struct file *file, u64 nodeid)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
struct fuse_forget_in inarg;
|
||||
FUSE_ARGS(args);
|
||||
|
||||
memset(&inarg, 0, sizeof(inarg));
|
||||
inarg.nlookup = 1;
|
||||
args.opcode = FUSE_FORGET;
|
||||
args.nodeid = nodeid;
|
||||
args.in_numargs = 1;
|
||||
args.in_args[0].size = sizeof(inarg);
|
||||
args.in_args[0].value = &inarg;
|
||||
args.force = true;
|
||||
args.noreply = true;
|
||||
|
||||
fuse_simple_request(fc, &args);
|
||||
/* ignore errors */
|
||||
}
|
||||
|
||||
static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file,
|
||||
struct dir_context *ctx, u64 attr_version)
|
||||
{
|
||||
|
@ -295,62 +316,55 @@ static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file,
|
|||
|
||||
static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx)
|
||||
{
|
||||
int plus, err;
|
||||
size_t nbytes;
|
||||
int plus;
|
||||
ssize_t res;
|
||||
struct page *page;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
struct fuse_req *req;
|
||||
struct fuse_io_args ia = {};
|
||||
struct fuse_args_pages *ap = &ia.ap;
|
||||
struct fuse_page_desc desc = { .length = PAGE_SIZE };
|
||||
u64 attr_version = 0;
|
||||
bool locked;
|
||||
|
||||
req = fuse_get_req(fc, 1);
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
|
||||
page = alloc_page(GFP_KERNEL);
|
||||
if (!page) {
|
||||
fuse_put_request(fc, req);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
plus = fuse_use_readdirplus(inode, ctx);
|
||||
req->out.argpages = 1;
|
||||
req->num_pages = 1;
|
||||
req->pages[0] = page;
|
||||
req->page_descs[0].length = PAGE_SIZE;
|
||||
ap->args.out_pages = 1;
|
||||
ap->num_pages = 1;
|
||||
ap->pages = &page;
|
||||
ap->descs = &desc;
|
||||
if (plus) {
|
||||
attr_version = fuse_get_attr_version(fc);
|
||||
fuse_read_fill(req, file, ctx->pos, PAGE_SIZE,
|
||||
FUSE_READDIRPLUS);
|
||||
fuse_read_args_fill(&ia, file, ctx->pos, PAGE_SIZE,
|
||||
FUSE_READDIRPLUS);
|
||||
} else {
|
||||
fuse_read_fill(req, file, ctx->pos, PAGE_SIZE,
|
||||
FUSE_READDIR);
|
||||
fuse_read_args_fill(&ia, file, ctx->pos, PAGE_SIZE,
|
||||
FUSE_READDIR);
|
||||
}
|
||||
locked = fuse_lock_inode(inode);
|
||||
fuse_request_send(fc, req);
|
||||
res = fuse_simple_request(fc, &ap->args);
|
||||
fuse_unlock_inode(inode, locked);
|
||||
nbytes = req->out.args[0].size;
|
||||
err = req->out.h.error;
|
||||
fuse_put_request(fc, req);
|
||||
if (!err) {
|
||||
if (!nbytes) {
|
||||
if (res >= 0) {
|
||||
if (!res) {
|
||||
struct fuse_file *ff = file->private_data;
|
||||
|
||||
if (ff->open_flags & FOPEN_CACHE_DIR)
|
||||
fuse_readdir_cache_end(file, ctx->pos);
|
||||
} else if (plus) {
|
||||
err = parse_dirplusfile(page_address(page), nbytes,
|
||||
res = parse_dirplusfile(page_address(page), res,
|
||||
file, ctx, attr_version);
|
||||
} else {
|
||||
err = parse_dirfile(page_address(page), nbytes, file,
|
||||
res = parse_dirfile(page_address(page), res, file,
|
||||
ctx);
|
||||
}
|
||||
}
|
||||
|
||||
__free_page(page);
|
||||
fuse_invalidate_atime(inode);
|
||||
return err;
|
||||
return res;
|
||||
}
|
||||
|
||||
enum fuse_parse_result {
|
||||
|
@ -372,11 +386,13 @@ static enum fuse_parse_result fuse_parse_cache(struct fuse_file *ff,
|
|||
for (;;) {
|
||||
struct fuse_dirent *dirent = addr + offset;
|
||||
unsigned int nbytes = size - offset;
|
||||
size_t reclen = FUSE_DIRENT_SIZE(dirent);
|
||||
size_t reclen;
|
||||
|
||||
if (nbytes < FUSE_NAME_OFFSET || !dirent->namelen)
|
||||
break;
|
||||
|
||||
reclen = FUSE_DIRENT_SIZE(dirent); /* derefs ->namelen */
|
||||
|
||||
if (WARN_ON(dirent->namelen > FUSE_NAME_MAX))
|
||||
return FOUND_ERR;
|
||||
if (WARN_ON(reclen > nbytes))
|
||||
|
|
|
@ -25,15 +25,15 @@ int fuse_setxattr(struct inode *inode, const char *name, const void *value,
|
|||
memset(&inarg, 0, sizeof(inarg));
|
||||
inarg.size = size;
|
||||
inarg.flags = flags;
|
||||
args.in.h.opcode = FUSE_SETXATTR;
|
||||
args.in.h.nodeid = get_node_id(inode);
|
||||
args.in.numargs = 3;
|
||||
args.in.args[0].size = sizeof(inarg);
|
||||
args.in.args[0].value = &inarg;
|
||||
args.in.args[1].size = strlen(name) + 1;
|
||||
args.in.args[1].value = name;
|
||||
args.in.args[2].size = size;
|
||||
args.in.args[2].value = value;
|
||||
args.opcode = FUSE_SETXATTR;
|
||||
args.nodeid = get_node_id(inode);
|
||||
args.in_numargs = 3;
|
||||
args.in_args[0].size = sizeof(inarg);
|
||||
args.in_args[0].value = &inarg;
|
||||
args.in_args[1].size = strlen(name) + 1;
|
||||
args.in_args[1].value = name;
|
||||
args.in_args[2].size = size;
|
||||
args.in_args[2].value = value;
|
||||
err = fuse_simple_request(fc, &args);
|
||||
if (err == -ENOSYS) {
|
||||
fc->no_setxattr = 1;
|
||||
|
@ -60,22 +60,22 @@ ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value,
|
|||
|
||||
memset(&inarg, 0, sizeof(inarg));
|
||||
inarg.size = size;
|
||||
args.in.h.opcode = FUSE_GETXATTR;
|
||||
args.in.h.nodeid = get_node_id(inode);
|
||||
args.in.numargs = 2;
|
||||
args.in.args[0].size = sizeof(inarg);
|
||||
args.in.args[0].value = &inarg;
|
||||
args.in.args[1].size = strlen(name) + 1;
|
||||
args.in.args[1].value = name;
|
||||
args.opcode = FUSE_GETXATTR;
|
||||
args.nodeid = get_node_id(inode);
|
||||
args.in_numargs = 2;
|
||||
args.in_args[0].size = sizeof(inarg);
|
||||
args.in_args[0].value = &inarg;
|
||||
args.in_args[1].size = strlen(name) + 1;
|
||||
args.in_args[1].value = name;
|
||||
/* This is really two different operations rolled into one */
|
||||
args.out.numargs = 1;
|
||||
args.out_numargs = 1;
|
||||
if (size) {
|
||||
args.out.argvar = 1;
|
||||
args.out.args[0].size = size;
|
||||
args.out.args[0].value = value;
|
||||
args.out_argvar = true;
|
||||
args.out_args[0].size = size;
|
||||
args.out_args[0].value = value;
|
||||
} else {
|
||||
args.out.args[0].size = sizeof(outarg);
|
||||
args.out.args[0].value = &outarg;
|
||||
args.out_args[0].size = sizeof(outarg);
|
||||
args.out_args[0].value = &outarg;
|
||||
}
|
||||
ret = fuse_simple_request(fc, &args);
|
||||
if (!ret && !size)
|
||||
|
@ -121,20 +121,20 @@ ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
|
|||
|
||||
memset(&inarg, 0, sizeof(inarg));
|
||||
inarg.size = size;
|
||||
args.in.h.opcode = FUSE_LISTXATTR;
|
||||
args.in.h.nodeid = get_node_id(inode);
|
||||
args.in.numargs = 1;
|
||||
args.in.args[0].size = sizeof(inarg);
|
||||
args.in.args[0].value = &inarg;
|
||||
args.opcode = FUSE_LISTXATTR;
|
||||
args.nodeid = get_node_id(inode);
|
||||
args.in_numargs = 1;
|
||||
args.in_args[0].size = sizeof(inarg);
|
||||
args.in_args[0].value = &inarg;
|
||||
/* This is really two different operations rolled into one */
|
||||
args.out.numargs = 1;
|
||||
args.out_numargs = 1;
|
||||
if (size) {
|
||||
args.out.argvar = 1;
|
||||
args.out.args[0].size = size;
|
||||
args.out.args[0].value = list;
|
||||
args.out_argvar = true;
|
||||
args.out_args[0].size = size;
|
||||
args.out_args[0].value = list;
|
||||
} else {
|
||||
args.out.args[0].size = sizeof(outarg);
|
||||
args.out.args[0].value = &outarg;
|
||||
args.out_args[0].size = sizeof(outarg);
|
||||
args.out_args[0].value = &outarg;
|
||||
}
|
||||
ret = fuse_simple_request(fc, &args);
|
||||
if (!ret && !size)
|
||||
|
@ -157,11 +157,11 @@ int fuse_removexattr(struct inode *inode, const char *name)
|
|||
if (fc->no_removexattr)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
args.in.h.opcode = FUSE_REMOVEXATTR;
|
||||
args.in.h.nodeid = get_node_id(inode);
|
||||
args.in.numargs = 1;
|
||||
args.in.args[0].size = strlen(name) + 1;
|
||||
args.in.args[0].value = name;
|
||||
args.opcode = FUSE_REMOVEXATTR;
|
||||
args.nodeid = get_node_id(inode);
|
||||
args.in_numargs = 1;
|
||||
args.in_args[0].size = strlen(name) + 1;
|
||||
args.in_args[0].value = name;
|
||||
err = fuse_simple_request(fc, &args);
|
||||
if (err == -ENOSYS) {
|
||||
fc->no_removexattr = 1;
|
||||
|
|
|
@ -2802,8 +2802,6 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags,
|
|||
put_filesystem(type);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
subtype = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ static inline void mangle(struct seq_file *m, const char *s)
|
|||
static void show_type(struct seq_file *m, struct super_block *sb)
|
||||
{
|
||||
mangle(m, sb->s_type->name);
|
||||
if (sb->s_subtype && sb->s_subtype[0]) {
|
||||
if (sb->s_subtype) {
|
||||
seq_putc(m, '.');
|
||||
mangle(m, sb->s_subtype);
|
||||
}
|
||||
|
|
|
@ -1555,11 +1555,6 @@ int vfs_get_tree(struct fs_context *fc)
|
|||
sb = fc->root->d_sb;
|
||||
WARN_ON(!sb->s_bdi);
|
||||
|
||||
if (fc->subtype && !sb->s_subtype) {
|
||||
sb->s_subtype = fc->subtype;
|
||||
fc->subtype = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write barrier is for super_cache_count(). We place it before setting
|
||||
* SB_BORN as the data dependency between the two functions is the
|
||||
|
|
|
@ -95,7 +95,6 @@ struct fs_context {
|
|||
const struct cred *cred; /* The mounter's credentials */
|
||||
struct fc_log *log; /* Logging buffer */
|
||||
const char *source; /* The source name (eg. dev path) */
|
||||
const char *subtype; /* The subtype to set on the superblock */
|
||||
void *security; /* Linux S&M options */
|
||||
void *s_fs_info; /* Proposed s_fs_info */
|
||||
unsigned int sb_flags; /* Proposed superblock flags (SB_*) */
|
||||
|
|
|
@ -425,6 +425,10 @@ enum fuse_opcode {
|
|||
|
||||
/* CUSE specific operations */
|
||||
CUSE_INIT = 4096,
|
||||
|
||||
/* Reserved opcodes: helpful to detect structure endian-ness */
|
||||
CUSE_INIT_BSWAP_RESERVED = 1048576, /* CUSE_INIT << 8 */
|
||||
FUSE_INIT_BSWAP_RESERVED = 436207616, /* FUSE_INIT << 24 */
|
||||
};
|
||||
|
||||
enum fuse_notify_code {
|
||||
|
|
Loading…
Reference in New Issue