xfs
[Top] [All Lists]

[PATCH 2/2] xfs: inode unlinked list needs to recalculate the inode CRC

To: xfs@xxxxxxxxxxx
Subject: [PATCH 2/2] xfs: inode unlinked list needs to recalculate the inode CRC
From: Dave Chinner <david@xxxxxxxxxxxxx>
Date: Wed, 29 May 2013 06:36:13 +1000
Cc: bpm@xxxxxxx
Delivered-to: xfs@xxxxxxxxxxx
In-reply-to: <1369773373-26697-1-git-send-email-david@xxxxxxxxxxxxx>
References: <1369636707-15150-10-git-send-email-david@xxxxxxxxxxxxx> <1369773373-26697-1-git-send-email-david@xxxxxxxxxxxxx>
From: Dave Chinner <dchinner@xxxxxxxxxx>

The inode unlinked list manipulations operate directly on the inode
buffer, and so bypass the inode CRC calculation mechanisms. Hence an
inode on the unlinked list has an invalid CRC. Fix this by
recalculating the CRC whenever we modify an unlinked list pointer in
an inode, ncluding during log recovery. This is trivial to do and
results in  unlinked list operations always leaving a consistent
inode in the buffer.

Signed-off-by: Dave Chinner <dchinner@xxxxxxxxxx>
---
 fs/xfs/xfs_inode.c       |   42 ++++++++++++++++++++++++++++++++++++++----
 fs/xfs/xfs_log_recover.c |    9 +++++++++
 2 files changed, 47 insertions(+), 4 deletions(-)

diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index efbe1ac..2d993e7 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -1579,6 +1579,23 @@ out_bmap_cancel:
 }
 
 /*
+ * Helper function to calculate range of the inode to log and recalculate the
+ * on-disk inode crc if necessary.
+ */
+static int
+xfs_iunlink_dinode_calc_crc(
+       struct xfs_mount        *mp,
+       struct xfs_dinode       *dip)
+{
+       if (dip->di_version < 3)
+               return sizeof(xfs_agino_t);
+
+       xfs_dinode_calc_crc(mp, dip);
+       return offsetof(struct xfs_dinode, di_changecount) -
+               offsetof(struct xfs_dinode, di_next_unlinked);
+}
+
+/*
  * This is called when the inode's link count goes to 0.
  * We place the on-disk inode on a list in the AGI.  It
  * will be pulled from this list when the inode is freed.
@@ -1638,10 +1655,15 @@ xfs_iunlink(
                dip->di_next_unlinked = agi->agi_unlinked[bucket_index];
                offset = ip->i_imap.im_boffset +
                        offsetof(xfs_dinode_t, di_next_unlinked);
+
+               /* need to recalc the inode CRC if appropriate */
+               xfs_iunlink_dinode_calc_crc(mp, dip);
+
                xfs_trans_inode_buf(tp, ibp);
                xfs_trans_log_buf(tp, ibp, offset,
-                                 (offset + sizeof(xfs_agino_t) - 1));
+                                 offset + sizeof(xfs_agino_t) - 1);
                xfs_inobp_check(mp, ibp);
+
        }
 
        /*
@@ -1723,9 +1745,13 @@ xfs_iunlink_remove(
                        dip->di_next_unlinked = cpu_to_be32(NULLAGINO);
                        offset = ip->i_imap.im_boffset +
                                offsetof(xfs_dinode_t, di_next_unlinked);
+
+                       /* need to recalc the inode CRC if appropriate */
+                       xfs_iunlink_dinode_calc_crc(mp, dip);
+
                        xfs_trans_inode_buf(tp, ibp);
                        xfs_trans_log_buf(tp, ibp, offset,
-                                         (offset + sizeof(xfs_agino_t) - 1));
+                                         offset + sizeof(xfs_agino_t) - 1);
                        xfs_inobp_check(mp, ibp);
                } else {
                        xfs_trans_brelse(tp, ibp);
@@ -1796,9 +1822,13 @@ xfs_iunlink_remove(
                        dip->di_next_unlinked = cpu_to_be32(NULLAGINO);
                        offset = ip->i_imap.im_boffset +
                                offsetof(xfs_dinode_t, di_next_unlinked);
+
+                       /* need to recalc the inode CRC if appropriate */
+                       xfs_iunlink_dinode_calc_crc(mp, dip);
+
                        xfs_trans_inode_buf(tp, ibp);
                        xfs_trans_log_buf(tp, ibp, offset,
-                                         (offset + sizeof(xfs_agino_t) - 1));
+                                         offset + sizeof(xfs_agino_t) - 1);
                        xfs_inobp_check(mp, ibp);
                } else {
                        xfs_trans_brelse(tp, ibp);
@@ -1809,9 +1839,13 @@ xfs_iunlink_remove(
                last_dip->di_next_unlinked = cpu_to_be32(next_agino);
                ASSERT(next_agino != 0);
                offset = last_offset + offsetof(xfs_dinode_t, di_next_unlinked);
+
+               /* need to recalc the inode CRC if appropriate */
+               xfs_iunlink_dinode_calc_crc(mp, dip);
+
                xfs_trans_inode_buf(tp, last_ibp);
                xfs_trans_log_buf(tp, last_ibp, offset,
-                                 (offset + sizeof(xfs_agino_t) - 1));
+                                 offset + sizeof(xfs_agino_t) - 1);
                xfs_inobp_check(mp, last_ibp);
        }
        return 0;
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 83088d9..45a85ff 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -1912,6 +1912,15 @@ xlog_recover_do_inode_buffer(
                buffer_nextp = (xfs_agino_t *)xfs_buf_offset(bp,
                                              next_unlinked_offset);
                *buffer_nextp = *logged_nextp;
+
+               /*
+                * If necessary, recalculate the CRC in the on-disk inode. We
+                * have to leave the inode in a consistent state for whoever
+                * reads it next....
+                */
+               xfs_dinode_calc_crc(mp, (struct xfs_dinode *)
+                               xfs_buf_offset(bp, i * mp->m_sb.sb_inodesize));
+
        }
 
        return 0;
-- 
1.7.10.4

<Prev in Thread] Current Thread [Next in Thread>