aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/inode.c
diff options
context:
space:
mode:
authorJeff Mahoney <jeffm@suse.com>2016-11-05 13:26:35 -0400
committerDavid Sterba <dsterba@suse.com>2016-11-30 13:45:19 +0100
commitd2fbb2b589ece9060635b43c2b2333d0b0a0fbf2 (patch)
tree0c966e4f6fd1b7115944f3a44c9c4c75adcc1b0d /fs/btrfs/inode.c
parentbtrfs: remove old tree_root dirent processing in btrfs_real_readdir() (diff)
downloadlinux-dev-d2fbb2b589ece9060635b43c2b2333d0b0a0fbf2.tar.xz
linux-dev-d2fbb2b589ece9060635b43c2b2333d0b0a0fbf2.zip
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: Jeff Mahoney <jeffm@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'fs/btrfs/inode.c')
-rw-r--r--fs/btrfs/inode.c22
1 files changed, 2 insertions, 20 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index df84d76f124a..0b836737d382 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -5807,8 +5807,6 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
char tmp_name[32];
char *name_ptr;
int name_len;
- int is_curr = 0; /* ctx->pos points to the current index? */
- bool emitted;
bool put = false;
struct btrfs_key location;
@@ -5833,7 +5831,6 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
if (ret < 0)
goto err;
- emitted = false;
while (1) {
leaf = path->nodes[0];
slot = path->slots[0];
@@ -5859,7 +5856,6 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
goto next;
ctx->pos = found_key.offset;
- is_curr = 1;
di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
if (verify_dir_item(root, leaf, di))
@@ -5887,32 +5883,18 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
if (name_ptr != tmp_name)
kfree(name_ptr);
- emitted = true;
if (over)
goto nopos;
+ ctx->pos++;
next:
path->slots[0]++;
}
- if (is_curr)
- ctx->pos++;
- ret = btrfs_readdir_delayed_dir_index(ctx, &ins_list, &emitted);
+ ret = btrfs_readdir_delayed_dir_index(ctx, &ins_list);
if (ret)
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
* entry.
*