Skip to content

Commit e6a9467

Browse files
djwongtorvalds
authored andcommitted
ocfs2: fix inode bh swapping mixup in ocfs2_reflink_inodes_lock
ocfs2_reflink_inodes_lock() can swap the inode1/inode2 variables so that we always grab cluster locks in order of increasing inode number. Unfortunately, we forget to swap the inode record buffer head pointers when we've done this, which leads to incorrect bookkeepping when we're trying to make the two inodes have the same refcount tree. This has the effect of causing filesystem shutdowns if you're trying to reflink data from inode 100 into inode 97, where inode 100 already has a refcount tree attached and inode 97 doesn't. The reflink code decides to copy the refcount tree pointer from 100 to 97, but uses inode 97's inode record to open the tree root (which it doesn't have) and blows up. This issue causes filesystem shutdowns and metadata corruption! Link: http://lkml.kernel.org/r/20190312214910.GK20533@magnolia Fixes: 29ac8e8 ("ocfs2: implement the VFS clone_range, copy_range, and dedupe_range features") Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Joseph Qi <[email protected]> Cc: Mark Fasheh <[email protected]> Cc: Joel Becker <[email protected]> Cc: Junxiao Bi <[email protected]> Cc: Joseph Qi <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 9b7ea46 commit e6a9467

File tree

1 file changed

+24
-18
lines changed

1 file changed

+24
-18
lines changed

fs/ocfs2/refcounttree.c

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4719,22 +4719,23 @@ loff_t ocfs2_reflink_remap_blocks(struct inode *s_inode,
47194719

47204720
/* Lock an inode and grab a bh pointing to the inode. */
47214721
int ocfs2_reflink_inodes_lock(struct inode *s_inode,
4722-
struct buffer_head **bh1,
4722+
struct buffer_head **bh_s,
47234723
struct inode *t_inode,
4724-
struct buffer_head **bh2)
4724+
struct buffer_head **bh_t)
47254725
{
4726-
struct inode *inode1;
4727-
struct inode *inode2;
4726+
struct inode *inode1 = s_inode;
4727+
struct inode *inode2 = t_inode;
47284728
struct ocfs2_inode_info *oi1;
47294729
struct ocfs2_inode_info *oi2;
4730+
struct buffer_head *bh1 = NULL;
4731+
struct buffer_head *bh2 = NULL;
47304732
bool same_inode = (s_inode == t_inode);
4733+
bool need_swap = (inode1->i_ino > inode2->i_ino);
47314734
int status;
47324735

47334736
/* First grab the VFS and rw locks. */
47344737
lock_two_nondirectories(s_inode, t_inode);
4735-
inode1 = s_inode;
4736-
inode2 = t_inode;
4737-
if (inode1->i_ino > inode2->i_ino)
4738+
if (need_swap)
47384739
swap(inode1, inode2);
47394740

47404741
status = ocfs2_rw_lock(inode1, 1);
@@ -4757,17 +4758,13 @@ int ocfs2_reflink_inodes_lock(struct inode *s_inode,
47574758
trace_ocfs2_double_lock((unsigned long long)oi1->ip_blkno,
47584759
(unsigned long long)oi2->ip_blkno);
47594760

4760-
if (*bh1)
4761-
*bh1 = NULL;
4762-
if (*bh2)
4763-
*bh2 = NULL;
4764-
47654761
/* We always want to lock the one with the lower lockid first. */
47664762
if (oi1->ip_blkno > oi2->ip_blkno)
47674763
mlog_errno(-ENOLCK);
47684764

47694765
/* lock id1 */
4770-
status = ocfs2_inode_lock_nested(inode1, bh1, 1, OI_LS_REFLINK_TARGET);
4766+
status = ocfs2_inode_lock_nested(inode1, &bh1, 1,
4767+
OI_LS_REFLINK_TARGET);
47714768
if (status < 0) {
47724769
if (status != -ENOENT)
47734770
mlog_errno(status);
@@ -4776,15 +4773,25 @@ int ocfs2_reflink_inodes_lock(struct inode *s_inode,
47764773

47774774
/* lock id2 */
47784775
if (!same_inode) {
4779-
status = ocfs2_inode_lock_nested(inode2, bh2, 1,
4776+
status = ocfs2_inode_lock_nested(inode2, &bh2, 1,
47804777
OI_LS_REFLINK_TARGET);
47814778
if (status < 0) {
47824779
if (status != -ENOENT)
47834780
mlog_errno(status);
47844781
goto out_cl1;
47854782
}
4786-
} else
4787-
*bh2 = *bh1;
4783+
} else {
4784+
bh2 = bh1;
4785+
}
4786+
4787+
/*
4788+
* If we swapped inode order above, we have to swap the buffer heads
4789+
* before passing them back to the caller.
4790+
*/
4791+
if (need_swap)
4792+
swap(bh1, bh2);
4793+
*bh_s = bh1;
4794+
*bh_t = bh2;
47884795

47894796
trace_ocfs2_double_lock_end(
47904797
(unsigned long long)oi1->ip_blkno,
@@ -4794,8 +4801,7 @@ int ocfs2_reflink_inodes_lock(struct inode *s_inode,
47944801

47954802
out_cl1:
47964803
ocfs2_inode_unlock(inode1, 1);
4797-
brelse(*bh1);
4798-
*bh1 = NULL;
4804+
brelse(bh1);
47994805
out_rw2:
48004806
ocfs2_rw_unlock(inode2, 1);
48014807
out_i2:

0 commit comments

Comments
 (0)