mirror of
https://github.com/hardkernel/linux.git
synced 2026-03-25 12:00:22 +09:00
ANDROID: fuse-bpf: Fix readdir
Fuse uses generic_file_llseek, so we must account for that in readdir to ensure we read from the correct offset in the lower filesystem. Bug: 226655281 Test: generic/257, fuse_test Signed-off-by: Daniel Rosenberg <drosen@google.com> Change-Id: Ie752c1c645e95b7c03ef9497562758a5c42b514a
This commit is contained in:
@@ -2189,7 +2189,7 @@ void *fuse_symlink_finalize(
|
||||
|
||||
int fuse_readdir_initialize(struct fuse_args *fa, struct fuse_read_io *frio,
|
||||
struct file *file, struct dir_context *ctx,
|
||||
bool *force_again, bool *allow_force)
|
||||
bool *force_again, bool *allow_force, bool is_continued)
|
||||
{
|
||||
struct fuse_file *ff = file->private_data;
|
||||
u8 *page = (u8 *)__get_free_page(GFP_KERNEL);
|
||||
@@ -2259,9 +2259,35 @@ static int filldir(struct dir_context *ctx, const char *name, int namelen,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_dirfile(char *buf, size_t nbytes, struct dir_context *ctx)
|
||||
{
|
||||
while (nbytes >= FUSE_NAME_OFFSET) {
|
||||
struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
|
||||
size_t reclen = FUSE_DIRENT_SIZE(dirent);
|
||||
|
||||
if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX)
|
||||
return -EIO;
|
||||
if (reclen > nbytes)
|
||||
break;
|
||||
if (memchr(dirent->name, '/', dirent->namelen) != NULL)
|
||||
return -EIO;
|
||||
|
||||
ctx->pos = dirent->off;
|
||||
if (!dir_emit(ctx, dirent->name, dirent->namelen, dirent->ino,
|
||||
dirent->type))
|
||||
break;
|
||||
|
||||
buf += reclen;
|
||||
nbytes -= reclen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int fuse_readdir_backing(struct fuse_args *fa,
|
||||
struct file *file, struct dir_context *ctx,
|
||||
bool *force_again, bool *allow_force)
|
||||
bool *force_again, bool *allow_force, bool is_continued)
|
||||
{
|
||||
struct fuse_file *ff = file->private_data;
|
||||
struct file *backing_dir = ff->backing_file;
|
||||
@@ -2278,6 +2304,9 @@ int fuse_readdir_backing(struct fuse_args *fa,
|
||||
if (!ec.addr)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!is_continued)
|
||||
backing_dir->f_pos = file->f_pos;
|
||||
|
||||
err = iterate_dir(backing_dir, &ec.ctx);
|
||||
if (ec.offset == 0)
|
||||
*allow_force = false;
|
||||
@@ -2290,18 +2319,19 @@ int fuse_readdir_backing(struct fuse_args *fa,
|
||||
|
||||
void *fuse_readdir_finalize(struct fuse_args *fa,
|
||||
struct file *file, struct dir_context *ctx,
|
||||
bool *force_again, bool *allow_force)
|
||||
bool *force_again, bool *allow_force, bool is_continued)
|
||||
{
|
||||
int err = 0;
|
||||
struct fuse_read_out *fro = fa->out_args[0].value;
|
||||
struct fuse_file *ff = file->private_data;
|
||||
struct file *backing_dir = ff->backing_file;
|
||||
struct fuse_read_out *fro = fa->out_args[0].value;
|
||||
int err = 0;
|
||||
|
||||
err = fuse_parse_dirfile(fa->out_args[1].value,
|
||||
fa->out_args[1].size, file, ctx);
|
||||
err = parse_dirfile(fa->out_args[1].value, fa->out_args[1].size, ctx);
|
||||
*force_again = !!fro->again;
|
||||
if (*force_again && !*allow_force)
|
||||
err = -EINVAL;
|
||||
|
||||
ctx->pos = fro->offset;
|
||||
backing_dir->f_pos = fro->offset;
|
||||
|
||||
free_page((unsigned long) fa->out_args[1].value);
|
||||
|
||||
@@ -987,10 +987,6 @@ struct fuse_io_args {
|
||||
void fuse_read_args_fill(struct fuse_io_args *ia, struct file *file, loff_t pos,
|
||||
size_t count, int opcode);
|
||||
|
||||
|
||||
int fuse_parse_dirfile(char *buf, size_t nbytes, struct file *file,
|
||||
struct dir_context *ctx);
|
||||
|
||||
/**
|
||||
* Send OPEN or OPENDIR request
|
||||
*/
|
||||
@@ -1637,13 +1633,13 @@ struct fuse_read_io {
|
||||
|
||||
int fuse_readdir_initialize(struct fuse_args *fa, struct fuse_read_io *frio,
|
||||
struct file *file, struct dir_context *ctx,
|
||||
bool *force_again, bool *allow_force);
|
||||
bool *force_again, bool *allow_force, bool is_continued);
|
||||
int fuse_readdir_backing(struct fuse_args *fa,
|
||||
struct file *file, struct dir_context *ctx,
|
||||
bool *force_again, bool *allow_force);
|
||||
bool *force_again, bool *allow_force, bool is_continued);
|
||||
void *fuse_readdir_finalize(struct fuse_args *fa,
|
||||
struct file *file, struct dir_context *ctx,
|
||||
bool *force_again, bool *allow_force);
|
||||
bool *force_again, bool *allow_force, bool is_continued);
|
||||
|
||||
int fuse_access_initialize(struct fuse_args *fa, struct fuse_access_in *fai,
|
||||
struct inode *inode, int mask);
|
||||
|
||||
@@ -121,7 +121,7 @@ static bool fuse_emit(struct file *file, struct dir_context *ctx,
|
||||
dirent->type);
|
||||
}
|
||||
|
||||
int fuse_parse_dirfile(char *buf, size_t nbytes, struct file *file,
|
||||
static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
|
||||
struct dir_context *ctx)
|
||||
{
|
||||
while (nbytes >= FUSE_NAME_OFFSET) {
|
||||
@@ -360,7 +360,7 @@ static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx)
|
||||
res = parse_dirplusfile(page_address(page), res,
|
||||
file, ctx, attr_version);
|
||||
} else {
|
||||
res = fuse_parse_dirfile(page_address(page), res, file,
|
||||
res = parse_dirfile(page_address(page), res, file,
|
||||
ctx);
|
||||
}
|
||||
}
|
||||
@@ -574,13 +574,17 @@ int fuse_readdir(struct file *file, struct dir_context *ctx)
|
||||
#ifdef CONFIG_FUSE_BPF
|
||||
struct fuse_err_ret fer;
|
||||
bool force_again, allow_force;
|
||||
bool is_continued = false;
|
||||
|
||||
again:
|
||||
fer = fuse_bpf_backing(inode, struct fuse_read_io,
|
||||
fuse_readdir_initialize, fuse_readdir_backing,
|
||||
fuse_readdir_finalize,
|
||||
file, ctx, &force_again, &allow_force);
|
||||
if (force_again && !IS_ERR(fer.result))
|
||||
file, ctx, &force_again, &allow_force, is_continued);
|
||||
if (force_again && !IS_ERR(fer.result)) {
|
||||
is_continued = true;
|
||||
goto again;
|
||||
}
|
||||
|
||||
if (fer.ret)
|
||||
return PTR_ERR(fer.result);
|
||||
|
||||
@@ -485,7 +485,7 @@ static int bpf_test_redact_readdir(const char *mount_dir)
|
||||
TESTSYSCALL(closedir(dir));
|
||||
dir = NULL;
|
||||
FUSE_DAEMON
|
||||
bool skip = true;
|
||||
bool skip = true;
|
||||
for (int i = 0; i < ARRAY_SIZE(names) + 1; i++) {
|
||||
uint8_t bytes_in[FUSE_MIN_READ_BUFFER];
|
||||
uint8_t bytes_out[FUSE_MIN_READ_BUFFER];
|
||||
|
||||
Reference in New Issue
Block a user