mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 19:08:57 +09:00
ANDROID: fuse-bpf: Move fd operations to be synchronous
Bug: 222619123 Test: fuse_test passes, on cuttlefish CtsCameraTestCases passes Signed-off-by: Paul Lawrence <paullawrence@google.com> Change-Id: I54c148206b5ad5ae5737939bcb076cbe6c40129c
This commit is contained in:
@@ -13,53 +13,12 @@
|
||||
|
||||
#include "../internal.h"
|
||||
|
||||
/* Reimplement these functions since fget_task is not exported */
|
||||
static struct file *fuse__fget_files(struct files_struct *files,
|
||||
unsigned int fd, fmode_t mask, unsigned int refs)
|
||||
struct bpf_prog *fuse_get_bpf_prog(struct file *file)
|
||||
{
|
||||
struct file *file;
|
||||
|
||||
rcu_read_lock();
|
||||
loop:
|
||||
file = fcheck_files(files, fd);
|
||||
if (file) {
|
||||
/* File object ref couldn't be taken.
|
||||
* dup2() atomicity guarantee is the reason
|
||||
* we loop to catch the new file (or NULL pointer)
|
||||
*/
|
||||
if (file->f_mode & mask)
|
||||
file = NULL;
|
||||
else if (!get_file_rcu_many(file, refs))
|
||||
goto loop;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
return file;
|
||||
}
|
||||
|
||||
static struct file *fuse_fget_task(struct task_struct *task, unsigned int fd)
|
||||
{
|
||||
struct file *file = NULL;
|
||||
|
||||
task_lock(task);
|
||||
if (task->files)
|
||||
file = fuse__fget_files(task->files, fd, 0, 1);
|
||||
task_unlock(task);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
struct file *fuse_fget(struct fuse_conn *fc, unsigned int fd)
|
||||
{
|
||||
return fuse_fget_task(fc->task, fd);
|
||||
}
|
||||
|
||||
struct bpf_prog *fuse_get_bpf_prog(struct fuse_conn *fc, unsigned int fd)
|
||||
{
|
||||
struct file *bpf_file = fuse_fget(fc, fd);
|
||||
struct bpf_prog *bpf_prog = ERR_PTR(-EINVAL);
|
||||
|
||||
if (!bpf_file)
|
||||
goto out;
|
||||
if (!file || IS_ERR(file))
|
||||
return bpf_prog;
|
||||
/**
|
||||
* Two ways of getting a bpf prog from another task's fd, since
|
||||
* bpf_prog_get_type_dev only works with an fd
|
||||
@@ -75,10 +34,10 @@ struct bpf_prog *fuse_get_bpf_prog(struct fuse_conn *fc, unsigned int fd)
|
||||
* compilable as a module.
|
||||
*/
|
||||
#if 0
|
||||
if (bpf_file->f_op != &bpf_prog_fops)
|
||||
if (file->f_op != &bpf_prog_fops)
|
||||
goto out;
|
||||
|
||||
bpf_prog = bpf_file->private_data;
|
||||
bpf_prog = file->private_data;
|
||||
if (bpf_prog->type == BPF_PROG_TYPE_FUSE)
|
||||
bpf_prog_inc(bpf_prog);
|
||||
else
|
||||
@@ -86,24 +45,25 @@ struct bpf_prog *fuse_get_bpf_prog(struct fuse_conn *fc, unsigned int fd)
|
||||
|
||||
#else
|
||||
{
|
||||
int task_fd = get_unused_fd_flags(bpf_file->f_flags);
|
||||
int task_fd = get_unused_fd_flags(file->f_flags);
|
||||
|
||||
if (task_fd < 0)
|
||||
goto out;
|
||||
fd_install(task_fd, bpf_file);
|
||||
|
||||
fd_install(task_fd, file);
|
||||
|
||||
bpf_prog = bpf_prog_get_type_dev(task_fd, BPF_PROG_TYPE_FUSE,
|
||||
false);
|
||||
__close_fd(current->files, task_fd);
|
||||
|
||||
/* TODO I think this file is probably being leaked */
|
||||
bpf_file = NULL;
|
||||
/* Close the fd, which also closes the file */
|
||||
__close_fd(current->files, task_fd);
|
||||
file = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
out:
|
||||
if (bpf_file)
|
||||
fput(bpf_file);
|
||||
if (file)
|
||||
fput(file);
|
||||
return bpf_prog;
|
||||
}
|
||||
|
||||
@@ -992,8 +952,11 @@ struct dentry *fuse_lookup_finalize(struct fuse_args *fa, struct inode *dir,
|
||||
break;
|
||||
|
||||
case FUSE_ACTION_REPLACE: {
|
||||
struct fuse_conn *fc = get_fuse_mount(dir)->fc;
|
||||
struct bpf_prog *bpf_prog = fuse_get_bpf_prog(fc, febo->bpf_fd);
|
||||
struct file *bpf_file = (struct file*) febo->bpf_fd;
|
||||
struct bpf_prog *bpf_prog = ERR_PTR(-EINVAL);
|
||||
|
||||
if (bpf_file && !IS_ERR(bpf_file))
|
||||
bpf_prog = fuse_get_bpf_prog(bpf_file);
|
||||
|
||||
if (IS_ERR(bpf_prog))
|
||||
return ERR_PTR(PTR_ERR(bpf_prog));
|
||||
@@ -1022,9 +985,8 @@ struct dentry *fuse_lookup_finalize(struct fuse_args *fa, struct inode *dir,
|
||||
struct file *backing_file;
|
||||
|
||||
fc = get_fuse_mount(dir)->fc;
|
||||
backing_file = fuse_fget(fc, febo->backing_fd);
|
||||
__close_fd(fc->task->files, febo->backing_fd);
|
||||
if (!backing_file)
|
||||
backing_file = (struct file *) febo->backing_fd;
|
||||
if (!backing_file || IS_ERR(backing_file))
|
||||
return ERR_PTR(-EIO);
|
||||
|
||||
iput(get_fuse_inode(inode)->backing_inode);
|
||||
|
||||
@@ -1943,6 +1943,18 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
|
||||
kern_path(path, 0, req->args->canonical_path);
|
||||
}
|
||||
|
||||
if (!err && (req->in.h.opcode == FUSE_LOOKUP ||
|
||||
req->in.h.opcode == (FUSE_LOOKUP | FUSE_POSTFILTER)) &&
|
||||
req->args->out_args[1].size == sizeof(struct fuse_entry_bpf_out)) {
|
||||
struct fuse_entry_bpf_out *febo = (struct fuse_entry_bpf_out *)
|
||||
req->args->out_args[1].value;
|
||||
|
||||
if (febo->backing_action == FUSE_ACTION_REPLACE)
|
||||
febo->backing_fd = (uint64_t) fget(febo->backing_fd);
|
||||
if (febo->bpf_action == FUSE_ACTION_REPLACE)
|
||||
febo->bpf_fd = (uint64_t) fget(febo->bpf_fd);
|
||||
}
|
||||
|
||||
spin_lock(&fpq->lock);
|
||||
clear_bit(FR_LOCKED, &req->flags);
|
||||
if (!fpq->connected)
|
||||
|
||||
@@ -243,8 +243,19 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
|
||||
* change the backing file ever, so not sure what is correct
|
||||
* here yet, especially as we can't return an error to user
|
||||
*/
|
||||
if (bpf_outarg.backing_action == FUSE_ACTION_REPLACE)
|
||||
__close_fd(fm->fc->task->files, bpf_outarg.backing_fd);
|
||||
if (bpf_outarg.backing_action == FUSE_ACTION_REPLACE) {
|
||||
struct file *file = (struct file *) bpf_outarg.backing_fd;
|
||||
|
||||
if (file && !IS_ERR(file))
|
||||
fput(file);
|
||||
}
|
||||
|
||||
if (bpf_outarg.bpf_action == FUSE_ACTION_REPLACE) {
|
||||
struct file *file = (struct file *) bpf_outarg.bpf_fd;
|
||||
|
||||
if (file && !IS_ERR(file))
|
||||
fput(file);
|
||||
}
|
||||
|
||||
/* Zero nodeid is same as -ENOENT */
|
||||
if (!ret && !outarg.nodeid)
|
||||
@@ -529,26 +540,21 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
|
||||
if (bpf_outarg->backing_action != FUSE_ACTION_REPLACE)
|
||||
goto out_queue_forget;
|
||||
|
||||
backing_file = fuse_fget(fm->fc, bpf_outarg->backing_fd);
|
||||
if (!backing_file)
|
||||
backing_file = (struct file *) bpf_outarg->backing_fd;
|
||||
if (!backing_file || IS_ERR(backing_file))
|
||||
goto out_queue_forget;
|
||||
|
||||
/* TODO userspace doesn't really know when the right time to
|
||||
* close the passed fd is. This because after replying to the
|
||||
* driver request, so assume that after a lookup with bpf_args,
|
||||
* the daemon passes the fd ownership to the kernel, which also
|
||||
* takes care of closing it at the right time.
|
||||
*/
|
||||
__close_fd(fm->fc->task->files, bpf_outarg->backing_fd);
|
||||
|
||||
backing_inode = backing_file->f_inode;
|
||||
*inode = fuse_iget_backing(sb, backing_inode);
|
||||
if (!*inode)
|
||||
goto bpf_outarg_out;
|
||||
|
||||
if (bpf_outarg->bpf_action == FUSE_ACTION_REPLACE) {
|
||||
struct bpf_prog *bpf_prog = fuse_get_bpf_prog(fm->fc,
|
||||
bpf_outarg->bpf_fd);
|
||||
struct file *bpf_file = (struct file*) bpf_outarg->bpf_fd;
|
||||
struct bpf_prog *bpf_prog = ERR_PTR(-EINVAL);
|
||||
|
||||
if (bpf_file && !IS_ERR(bpf_file))
|
||||
bpf_prog = fuse_get_bpf_prog(bpf_file);;
|
||||
|
||||
if (IS_ERR(bpf_prog)) {
|
||||
iput(*inode);
|
||||
|
||||
@@ -845,9 +845,6 @@ struct fuse_conn {
|
||||
|
||||
/** Protects passthrough_req */
|
||||
spinlock_t passthrough_req_lock;
|
||||
|
||||
/** task_struct for fd lookups in fuse-bpf */
|
||||
struct task_struct *task;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1310,8 +1307,7 @@ ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma);
|
||||
|
||||
/* backing.c */
|
||||
|
||||
struct file *fuse_fget(struct fuse_conn *fc, unsigned int fd);
|
||||
struct bpf_prog *fuse_get_bpf_prog(struct fuse_conn *fc, unsigned int fd);
|
||||
struct bpf_prog *fuse_get_bpf_prog(struct file *file);
|
||||
|
||||
/*
|
||||
* Dummy io passed to fuse_bpf_backing when io operation needs no scratch space
|
||||
|
||||
@@ -1218,7 +1218,6 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
|
||||
fc->minor = arg->minor;
|
||||
fc->max_write = arg->minor < 5 ? 4096 : arg->max_write;
|
||||
fc->max_write = max_t(unsigned, 4096, fc->max_write);
|
||||
fc->task = get_task_struct(current);
|
||||
fc->conn_init = 1;
|
||||
}
|
||||
kfree(ia);
|
||||
@@ -1295,8 +1294,6 @@ void fuse_free_conn(struct fuse_conn *fc)
|
||||
idr_for_each(&fc->passthrough_req, free_fuse_passthrough, NULL);
|
||||
idr_destroy(&fc->passthrough_req);
|
||||
kfree_rcu(fc, rcu);
|
||||
if (fc->task)
|
||||
put_task_struct(fc->task);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fuse_free_conn);
|
||||
|
||||
|
||||
@@ -1145,6 +1145,7 @@ static int bpf_test_set_backing(const char *mount_dir)
|
||||
}));
|
||||
read(fuse_dev, bytes_in, sizeof(bytes_in));
|
||||
TESTSYSCALL(close(bpf_fd));
|
||||
TESTSYSCALL(close(backing_fd));
|
||||
FUSE_DONE
|
||||
|
||||
result = TEST_SUCCESS;
|
||||
@@ -1234,8 +1235,7 @@ static int bpf_test_remove_backing(const char *mount_dir)
|
||||
|
||||
while (read(fuse_dev, bytes_in, sizeof(bytes_in)) != -1)
|
||||
;
|
||||
TESTEQUAL(close(backing_fd), -1);
|
||||
TESTEQUAL(errno, EBADF);
|
||||
TESTSYSCALL(close(backing_fd));
|
||||
FUSE_DONE
|
||||
|
||||
result = TEST_SUCCESS;
|
||||
|
||||
Reference in New Issue
Block a user