Commit d2fbb2b5 authored by Jeff Mahoney's avatar Jeff Mahoney Committed by David Sterba

btrfs: increment ctx->pos for every emitted or skipped dirent in readdir

If we process the last item in the leaf and hit an I/O error while
reading the next leaf, we return -EIO without having adjusted the
position.  Since we have emitted dirents, getdents() will return
the byte count to the user instead of the error.  Subsequent callers
will emit the last successful dirent again, and return -EIO again,
with the same result.  Callers loop forever.

Instead, if we always increment ctx->pos after emitting or skipping
the dirent, we'll be sure that we won't hit the same one again.  When
we go to process the next leaf, we won't have emitted any dirents
and the -EIO will be returned to the user properly.  We also don't
need to track if we've emitted a dirent already or if we've changed
the position yet.
Signed-off-by: default avatarJeff Mahoney <jeffm@suse.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent c2951f32
...@@ -1686,7 +1686,7 @@ int btrfs_should_delete_dir_index(struct list_head *del_list, ...@@ -1686,7 +1686,7 @@ int btrfs_should_delete_dir_index(struct list_head *del_list,
* *
*/ */
int btrfs_readdir_delayed_dir_index(struct dir_context *ctx, int btrfs_readdir_delayed_dir_index(struct dir_context *ctx,
struct list_head *ins_list, bool *emitted) struct list_head *ins_list)
{ {
struct btrfs_dir_item *di; struct btrfs_dir_item *di;
struct btrfs_delayed_item *curr, *next; struct btrfs_delayed_item *curr, *next;
...@@ -1730,7 +1730,6 @@ int btrfs_readdir_delayed_dir_index(struct dir_context *ctx, ...@@ -1730,7 +1730,6 @@ int btrfs_readdir_delayed_dir_index(struct dir_context *ctx,
if (over) if (over)
return 1; return 1;
*emitted = true;
} }
return 0; return 0;
} }
......
...@@ -146,7 +146,7 @@ void btrfs_readdir_put_delayed_items(struct inode *inode, ...@@ -146,7 +146,7 @@ void btrfs_readdir_put_delayed_items(struct inode *inode,
int btrfs_should_delete_dir_index(struct list_head *del_list, int btrfs_should_delete_dir_index(struct list_head *del_list,
u64 index); u64 index);
int btrfs_readdir_delayed_dir_index(struct dir_context *ctx, int btrfs_readdir_delayed_dir_index(struct dir_context *ctx,
struct list_head *ins_list, bool *emitted); struct list_head *ins_list);
/* for init */ /* for init */
int __init btrfs_delayed_inode_init(void); int __init btrfs_delayed_inode_init(void);
......
...@@ -5807,8 +5807,6 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) ...@@ -5807,8 +5807,6 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
char tmp_name[32]; char tmp_name[32];
char *name_ptr; char *name_ptr;
int name_len; int name_len;
int is_curr = 0; /* ctx->pos points to the current index? */
bool emitted;
bool put = false; bool put = false;
struct btrfs_key location; struct btrfs_key location;
...@@ -5833,7 +5831,6 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) ...@@ -5833,7 +5831,6 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
if (ret < 0) if (ret < 0)
goto err; goto err;
emitted = false;
while (1) { while (1) {
leaf = path->nodes[0]; leaf = path->nodes[0];
slot = path->slots[0]; slot = path->slots[0];
...@@ -5859,7 +5856,6 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) ...@@ -5859,7 +5856,6 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
goto next; goto next;
ctx->pos = found_key.offset; ctx->pos = found_key.offset;
is_curr = 1;
di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item); di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
if (verify_dir_item(root, leaf, di)) if (verify_dir_item(root, leaf, di))
...@@ -5887,31 +5883,17 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) ...@@ -5887,31 +5883,17 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
if (name_ptr != tmp_name) if (name_ptr != tmp_name)
kfree(name_ptr); kfree(name_ptr);
emitted = true;
if (over) if (over)
goto nopos; goto nopos;
ctx->pos++;
next: next:
path->slots[0]++; path->slots[0]++;
} }
if (is_curr) ret = btrfs_readdir_delayed_dir_index(ctx, &ins_list);
ctx->pos++;
ret = btrfs_readdir_delayed_dir_index(ctx, &ins_list, &emitted);
if (ret) if (ret)
goto nopos; goto nopos;
/*
* If we haven't emitted any dir entry, we must not touch ctx->pos as
* it was was set to the termination value in previous call. We assume
* that "." and ".." were emitted if we reach this point and set the
* termination value as well for an empty directory.
*/
if (ctx->pos > 2 && !emitted)
goto nopos;
/* Reached end of directory/root. Bump pos past the last item. */
ctx->pos++;
/* /*
* Stop new entries from being returned after we return the last * Stop new entries from being returned after we return the last
* entry. * entry.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment