diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index c4f101e9444d..68f820d5188c 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -2366,8 +2366,11 @@ static bool filldir(struct dir_context *ctx, const char *name, int namelen, 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) { struct fuse_dirent *dirent = (struct fuse_dirent *) buf; 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; if (!dir_emit(ctx, dirent->name, dirent->namelen, dirent->ino, - dirent->type)) - break; + dirent->type)) { + // If we can't make any progress, user buffer is too small + if (buf == buffstart) + return -EINVAL; + else + return 0; + } buf += reclen; nbytes -= reclen; } + ctx->pos = next_offset; return 0; } @@ -2433,13 +2442,12 @@ void *fuse_readdir_finalize(struct fuse_bpf_args *fa, struct file *backing_dir = ff->backing_file; 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; if (*force_again && !*allow_force) err = -EINVAL; - ctx->pos = fro->offset; - backing_dir->f_pos = fro->offset; + backing_dir->f_pos = ctx->pos; free_page((unsigned long) fa->out_args[1].value); return ERR_PTR(err);