]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs: log the inode on directory sf to block format change
authorBrian Foster <bfoster@redhat.com>
Fri, 13 Dec 2019 00:54:33 +0000 (19:54 -0500)
committerEric Sandeen <sandeen@redhat.com>
Fri, 13 Dec 2019 00:54:33 +0000 (19:54 -0500)
Source kernel commit: 0b10d8a89f55c416f6a1f6a616669543fa8bdb69

When a directory changes from shortform (sf) to block format, the sf
format is copied to a temporary buffer, the inode format is modified
and the updated format filled with the dentries from the temporary
buffer. If the inode format is modified and attempt to grow the
inode fails (due to I/O error, for example), it is possible to
return an error while leaving the directory in an inconsistent state
and with an otherwise clean transaction. This results in corruption
of the associated directory and leads to xfs_dabuf_map() errors as
subsequent lookups cannot accurately determine the format of the
directory. This problem is reproduced occasionally by generic/475.

The fundamental problem is that xfs_dir2_sf_to_block() changes the
on-disk inode format without logging the inode. The inode is
eventually logged by the bmapi layer in the common case, but error
checking introduces the possibility of failing the high level
request before this happens.

Update both of the dir2 and attr callers of
xfs_bmap_local_to_extents_empty() to log the inode core as
consistent with the bmap local to extent format change codepath.
This ensures that any subsequent errors after the format has changed
cause the transaction to abort.

Signed-off-by: Brian Foster <bfoster@redhat.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>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
libxfs/xfs_attr_leaf.c
libxfs/xfs_dir2_block.c

index a91e5e1ed85f3ae5581f6713e431177125ed021b..f53ffab551a71bafa75f2d2a3ebe1c54824f9a5b 100644 (file)
@@ -823,6 +823,7 @@ xfs_attr_shortform_to_leaf(
 
        xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
        xfs_bmap_local_to_extents_empty(dp, XFS_ATTR_FORK);
+       xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
 
        bp = NULL;
        error = xfs_da_grow_inode(args, &blkno);
index 23898e3677c24785ab1a25a50fc210c85a5262d3..72e4f7eff54ccf4e3b7729525bb58b2eb6bbc43f 100644 (file)
@@ -1095,6 +1095,7 @@ xfs_dir2_sf_to_block(
        xfs_idata_realloc(dp, -ifp->if_bytes, XFS_DATA_FORK);
        xfs_bmap_local_to_extents_empty(dp, XFS_DATA_FORK);
        dp->i_d.di_size = 0;
+       xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
 
        /*
         * Add block 0 to the inode.