Fix readdir for .zfs/snapshot directory

dmu_snapshot_list_next stores the index of the next snapshot entry to the offp
argument, which zpl_snapdir_iterate then uses for the dir_emit. This
result in an off-by-one error. Therefore a temporary variable should be
used.

This was a regression introduced in commit zfsonlinux/zfs@0f37d0c.

Signed-off-by: Andrey Vesnovaty <andrey.vesnovaty@gmail.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #2930
This commit is contained in:
Andrey Vesnovaty 2013-10-08 10:28:40 +02:00 committed by Brian Behlendorf
parent 3941503c0a
commit 5f15fa2216

View File

@ -252,7 +252,7 @@ zpl_snapdir_iterate(struct file *filp, struct dir_context *ctx)
zfs_sb_t *zsb = ITOZSB(filp->f_path.dentry->d_inode); zfs_sb_t *zsb = ITOZSB(filp->f_path.dentry->d_inode);
char snapname[MAXNAMELEN]; char snapname[MAXNAMELEN];
boolean_t case_conflict; boolean_t case_conflict;
uint64_t id; uint64_t id, cookie;
int error = 0; int error = 0;
ZFS_ENTER(zsb); ZFS_ENTER(zsb);
@ -260,10 +260,11 @@ zpl_snapdir_iterate(struct file *filp, struct dir_context *ctx)
if (!dir_emit_dots(filp, ctx)) if (!dir_emit_dots(filp, ctx))
goto out; goto out;
cookie = ctx->pos;
while (error == 0) { while (error == 0) {
dsl_pool_config_enter(dmu_objset_pool(zsb->z_os), FTAG); dsl_pool_config_enter(dmu_objset_pool(zsb->z_os), FTAG);
error = -dmu_snapshot_list_next(zsb->z_os, MAXNAMELEN, error = -dmu_snapshot_list_next(zsb->z_os, MAXNAMELEN,
snapname, &id, &ctx->pos, &case_conflict); snapname, &id, &cookie, &case_conflict);
dsl_pool_config_exit(dmu_objset_pool(zsb->z_os), FTAG); dsl_pool_config_exit(dmu_objset_pool(zsb->z_os), FTAG);
if (error) if (error)
goto out; goto out;
@ -271,6 +272,8 @@ zpl_snapdir_iterate(struct file *filp, struct dir_context *ctx)
if (!dir_emit(ctx, snapname, strlen(snapname), if (!dir_emit(ctx, snapname, strlen(snapname),
ZFSCTL_INO_SHARES - id, DT_DIR)) ZFSCTL_INO_SHARES - id, DT_DIR))
goto out; goto out;
ctx->pos = cookie;
} }
out: out:
ZFS_EXIT(zsb); ZFS_EXIT(zsb);