ANDROID: fuse-bpf: Fix readdir for getdents

If you call getdents with a buffer size less than a page,
entries can be skipped. This correctly sets the point to continue from.

Bug: 325550828
Test: getdents with low buffer size
Signed-off-by: Daniel Rosenberg <drosen@google.com>
(cherry picked from https://android-review.googlesource.com/q/commit:506cf3f0742432c1995117f83b2528a23944989b)
Merged-In: I324e7e815d31742bd4e2d70c5d07c2b09a67a7c2
Change-Id: I324e7e815d31742bd4e2d70c5d07c2b09a67a7c2
This commit is contained in:
Daniel Rosenberg
2024-03-05 16:20:32 -08:00
committed by Todd Kjos
parent 599c52e842
commit 3bd1e69c2c

View File

@@ -2366,8 +2366,11 @@ static bool filldir(struct dir_context *ctx, const char *name, int namelen,
return true; return true;
} }
static int parse_dirfile(char *buf, size_t nbytes, struct dir_context *ctx) static int parse_dirfile(char *buf, size_t nbytes, struct dir_context *ctx,
loff_t next_offset)
{ {
char *buffstart = buf;
while (nbytes >= FUSE_NAME_OFFSET) { while (nbytes >= FUSE_NAME_OFFSET) {
struct fuse_dirent *dirent = (struct fuse_dirent *) buf; struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
size_t reclen = FUSE_DIRENT_SIZE(dirent); size_t reclen = FUSE_DIRENT_SIZE(dirent);
@@ -2381,12 +2384,18 @@ static int parse_dirfile(char *buf, size_t nbytes, struct dir_context *ctx)
ctx->pos = dirent->off; ctx->pos = dirent->off;
if (!dir_emit(ctx, dirent->name, dirent->namelen, dirent->ino, if (!dir_emit(ctx, dirent->name, dirent->namelen, dirent->ino,
dirent->type)) dirent->type)) {
break; // If we can't make any progress, user buffer is too small
if (buf == buffstart)
return -EINVAL;
else
return 0;
}
buf += reclen; buf += reclen;
nbytes -= reclen; nbytes -= reclen;
} }
ctx->pos = next_offset;
return 0; return 0;
} }
@@ -2433,13 +2442,12 @@ void *fuse_readdir_finalize(struct fuse_bpf_args *fa,
struct file *backing_dir = ff->backing_file; struct file *backing_dir = ff->backing_file;
int err = 0; int err = 0;
err = parse_dirfile(fa->out_args[1].value, fa->out_args[1].size, ctx); err = parse_dirfile(fa->out_args[1].value, fa->out_args[1].size, ctx, fro->offset);
*force_again = !!fro->again; *force_again = !!fro->again;
if (*force_again && !*allow_force) if (*force_again && !*allow_force)
err = -EINVAL; err = -EINVAL;
ctx->pos = fro->offset; backing_dir->f_pos = ctx->pos;
backing_dir->f_pos = fro->offset;
free_page((unsigned long) fa->out_args[1].value); free_page((unsigned long) fa->out_args[1].value);
return ERR_PTR(err); return ERR_PTR(err);