mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 04:44:45 +01:00
This was done entirely with mindless brute force, using
git grep -l '\<k[vmz]*alloc_objs*(.*, GFP_KERNEL)' |
xargs sed -i 's/\(alloc_objs*(.*\), GFP_KERNEL)/\1)/'
to convert the new alloc_obj() users that had a simple GFP_KERNEL
argument to just drop that argument.
Note that due to the extreme simplicity of the scripting, any slightly
more complex cases spread over multiple lines would not be triggered:
they definitely exist, but this covers the vast bulk of the cases, and
the resulting diff is also then easier to check automatically.
For the same reason the 'flex' versions will be done as a separate
conversion.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
179 lines
3.5 KiB
C
179 lines
3.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* FUSE passthrough to backing file.
|
|
*
|
|
* Copyright (c) 2023 CTERA Networks.
|
|
*/
|
|
|
|
#include "fuse_i.h"
|
|
|
|
#include <linux/file.h>
|
|
|
|
struct fuse_backing *fuse_backing_get(struct fuse_backing *fb)
|
|
{
|
|
if (fb && refcount_inc_not_zero(&fb->count))
|
|
return fb;
|
|
return NULL;
|
|
}
|
|
|
|
static void fuse_backing_free(struct fuse_backing *fb)
|
|
{
|
|
pr_debug("%s: fb=0x%p\n", __func__, fb);
|
|
|
|
if (fb->file)
|
|
fput(fb->file);
|
|
put_cred(fb->cred);
|
|
kfree_rcu(fb, rcu);
|
|
}
|
|
|
|
void fuse_backing_put(struct fuse_backing *fb)
|
|
{
|
|
if (fb && refcount_dec_and_test(&fb->count))
|
|
fuse_backing_free(fb);
|
|
}
|
|
|
|
void fuse_backing_files_init(struct fuse_conn *fc)
|
|
{
|
|
idr_init(&fc->backing_files_map);
|
|
}
|
|
|
|
static int fuse_backing_id_alloc(struct fuse_conn *fc, struct fuse_backing *fb)
|
|
{
|
|
int id;
|
|
|
|
idr_preload(GFP_KERNEL);
|
|
spin_lock(&fc->lock);
|
|
/* FIXME: xarray might be space inefficient */
|
|
id = idr_alloc_cyclic(&fc->backing_files_map, fb, 1, 0, GFP_ATOMIC);
|
|
spin_unlock(&fc->lock);
|
|
idr_preload_end();
|
|
|
|
WARN_ON_ONCE(id == 0);
|
|
return id;
|
|
}
|
|
|
|
static struct fuse_backing *fuse_backing_id_remove(struct fuse_conn *fc,
|
|
int id)
|
|
{
|
|
struct fuse_backing *fb;
|
|
|
|
spin_lock(&fc->lock);
|
|
fb = idr_remove(&fc->backing_files_map, id);
|
|
spin_unlock(&fc->lock);
|
|
|
|
return fb;
|
|
}
|
|
|
|
static int fuse_backing_id_free(int id, void *p, void *data)
|
|
{
|
|
struct fuse_backing *fb = p;
|
|
|
|
WARN_ON_ONCE(refcount_read(&fb->count) != 1);
|
|
fuse_backing_free(fb);
|
|
return 0;
|
|
}
|
|
|
|
void fuse_backing_files_free(struct fuse_conn *fc)
|
|
{
|
|
idr_for_each(&fc->backing_files_map, fuse_backing_id_free, NULL);
|
|
idr_destroy(&fc->backing_files_map);
|
|
}
|
|
|
|
int fuse_backing_open(struct fuse_conn *fc, struct fuse_backing_map *map)
|
|
{
|
|
struct file *file;
|
|
struct super_block *backing_sb;
|
|
struct fuse_backing *fb = NULL;
|
|
int res;
|
|
|
|
pr_debug("%s: fd=%d flags=0x%x\n", __func__, map->fd, map->flags);
|
|
|
|
/* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof */
|
|
res = -EPERM;
|
|
if (!fc->passthrough || !capable(CAP_SYS_ADMIN))
|
|
goto out;
|
|
|
|
res = -EINVAL;
|
|
if (map->flags || map->padding)
|
|
goto out;
|
|
|
|
file = fget_raw(map->fd);
|
|
res = -EBADF;
|
|
if (!file)
|
|
goto out;
|
|
|
|
/* read/write/splice/mmap passthrough only relevant for regular files */
|
|
res = d_is_dir(file->f_path.dentry) ? -EISDIR : -EINVAL;
|
|
if (!d_is_reg(file->f_path.dentry))
|
|
goto out_fput;
|
|
|
|
backing_sb = file_inode(file)->i_sb;
|
|
res = -ELOOP;
|
|
if (backing_sb->s_stack_depth >= fc->max_stack_depth)
|
|
goto out_fput;
|
|
|
|
fb = kmalloc_obj(struct fuse_backing);
|
|
res = -ENOMEM;
|
|
if (!fb)
|
|
goto out_fput;
|
|
|
|
fb->file = file;
|
|
fb->cred = prepare_creds();
|
|
refcount_set(&fb->count, 1);
|
|
|
|
res = fuse_backing_id_alloc(fc, fb);
|
|
if (res < 0) {
|
|
fuse_backing_free(fb);
|
|
fb = NULL;
|
|
}
|
|
|
|
out:
|
|
pr_debug("%s: fb=0x%p, ret=%i\n", __func__, fb, res);
|
|
|
|
return res;
|
|
|
|
out_fput:
|
|
fput(file);
|
|
goto out;
|
|
}
|
|
|
|
int fuse_backing_close(struct fuse_conn *fc, int backing_id)
|
|
{
|
|
struct fuse_backing *fb = NULL;
|
|
int err;
|
|
|
|
pr_debug("%s: backing_id=%d\n", __func__, backing_id);
|
|
|
|
/* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof */
|
|
err = -EPERM;
|
|
if (!fc->passthrough || !capable(CAP_SYS_ADMIN))
|
|
goto out;
|
|
|
|
err = -EINVAL;
|
|
if (backing_id <= 0)
|
|
goto out;
|
|
|
|
err = -ENOENT;
|
|
fb = fuse_backing_id_remove(fc, backing_id);
|
|
if (!fb)
|
|
goto out;
|
|
|
|
fuse_backing_put(fb);
|
|
err = 0;
|
|
out:
|
|
pr_debug("%s: fb=0x%p, err=%i\n", __func__, fb, err);
|
|
|
|
return err;
|
|
}
|
|
|
|
struct fuse_backing *fuse_backing_lookup(struct fuse_conn *fc, int backing_id)
|
|
{
|
|
struct fuse_backing *fb;
|
|
|
|
rcu_read_lock();
|
|
fb = idr_find(&fc->backing_files_map, backing_id);
|
|
fb = fuse_backing_get(fb);
|
|
rcu_read_unlock();
|
|
|
|
return fb;
|
|
}
|