From: Tommi Rantala tommi.t.rantala@nokia.com
[ Upstream commit 3d28e7e278913a267b1de360efcd5e5274065ce2 ]
Commit 263dde869bd09 ("xfs: cleanup xfs_dir2_block_getdents") introduced a getdents regression, when it converted the pointer arithmetics to offset calculations: offset is updated in the loop already for the next iteration, but the updated offset value is used incorrectly in two places, where we should have used the not-yet-updated value.
This caused for example "git clean -ffdx" failures to cleanup certain directory structures when running in a container.
Fix the regression by making sure we use proper offset in the loop body. Thanks to Christoph Hellwig for suggestion how to best fix the code.
Cc: Christoph Hellwig hch@lst.de Fixes: 263dde869bd09 ("xfs: cleanup xfs_dir2_block_getdents") Signed-off-by: Tommi Rantala tommi.t.rantala@nokia.com Reviewed-by: Christoph Hellwig hch@lst.de Reviewed-by: Darrick J. Wong darrick.wong@oracle.com Signed-off-by: Darrick J. Wong darrick.wong@oracle.com Reviewed-by: Dave Chinner dchinner@redhat.com Signed-off-by: Sasha Levin sashal@kernel.org --- fs/xfs/xfs_dir2_readdir.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/fs/xfs/xfs_dir2_readdir.c b/fs/xfs/xfs_dir2_readdir.c index 0d3b640cf1cce..871ec22c9aee9 100644 --- a/fs/xfs/xfs_dir2_readdir.c +++ b/fs/xfs/xfs_dir2_readdir.c @@ -147,7 +147,7 @@ xfs_dir2_block_getdents( xfs_off_t cook; struct xfs_da_geometry *geo = args->geo; int lock_mode; - unsigned int offset; + unsigned int offset, next_offset; unsigned int end;
/* @@ -173,9 +173,10 @@ xfs_dir2_block_getdents( * Loop over the data portion of the block. * Each object is a real entry (dep) or an unused one (dup). */ - offset = geo->data_entry_offset; end = xfs_dir3_data_end_offset(geo, bp->b_addr); - while (offset < end) { + for (offset = geo->data_entry_offset; + offset < end; + offset = next_offset) { struct xfs_dir2_data_unused *dup = bp->b_addr + offset; struct xfs_dir2_data_entry *dep = bp->b_addr + offset; uint8_t filetype; @@ -184,14 +185,15 @@ xfs_dir2_block_getdents( * Unused, skip it. */ if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { - offset += be16_to_cpu(dup->length); + next_offset = offset + be16_to_cpu(dup->length); continue; }
/* * Bump pointer for the next iteration. */ - offset += xfs_dir2_data_entsize(dp->i_mount, dep->namelen); + next_offset = offset + + xfs_dir2_data_entsize(dp->i_mount, dep->namelen);
/* * The entry is before the desired starting point, skip it.