Enable checking of the contents of a metadata buffer
immediately before it is written out. This hook can also be
used for calculating the CRC on metadata buffers as we are
certain at the tim eof the callback that nothing can change
the contents of the buffer during the callback except us.

Initially only hook up inode buffer checking to prove the concept
is sound.

Signed-off-by: Dave Chinner <dgc@sgi.com>
---
 fs/xfs/linux-2.6/xfs_buf.c |    7 +++
 fs/xfs/linux-2.6/xfs_buf.h |    8 ++++
 fs/xfs/xfs_inode.c         |   79 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 94 insertions(+)

Index: 2.6.x-xfs-new/fs/xfs/linux-2.6/xfs_buf.c
===================================================================
--- 2.6.x-xfs-new.orig/fs/xfs/linux-2.6/xfs_buf.c	2008-05-16 23:08:07.000000000 +1000
+++ 2.6.x-xfs-new/fs/xfs/linux-2.6/xfs_buf.c	2008-05-16 23:16:37.073099889 +1000
@@ -1176,6 +1176,13 @@ _xfs_buf_ioapply(
 		     (bp->b_flags & XBF_READ_AHEAD) ? READA : READ;
 	}
 
+	/*
+	 * call out to buffer specific pre-I/o functions. Used for
+	 * things like pre-write validation of buffers and CRC calculations
+	 */
+	if (bp->b_io_callback && (bp->b_flags & XBF_WRITE))
+		bp->b_io_callback(bp);
+
 	/* Special code path for reading a sub page size buffer in --
 	 * we populate up the whole page, and hence the other metadata
 	 * in the same page.  This optimization is only valid when the
Index: 2.6.x-xfs-new/fs/xfs/linux-2.6/xfs_buf.h
===================================================================
--- 2.6.x-xfs-new.orig/fs/xfs/linux-2.6/xfs_buf.h	2008-05-16 23:08:07.000000000 +1000
+++ 2.6.x-xfs-new/fs/xfs/linux-2.6/xfs_buf.h	2008-05-16 23:16:37.077099368 +1000
@@ -167,6 +167,8 @@ typedef struct xfs_buf {
 	unsigned int		b_offset;	/* page offset in first page */
 	struct page		**b_pages;	/* array of page pointers */
 	struct page		*b_page_array[XB_PAGES]; /* inline pages */
+	void			(*b_io_callback)(struct xfs_buf *);
+						/* pre-I/O callout function */
 #ifdef XFS_BUF_LOCK_TRACKING
 	int			b_last_holder;
 #endif
@@ -228,6 +230,12 @@ static inline int xfs_buf_geterror(xfs_b
 /* Buffer Utility Routines */
 extern xfs_caddr_t xfs_buf_offset(xfs_buf_t *, size_t);
 
+static inline void
+xfs_buf_set_io_callback(xfs_buf_t *bp, void (*func)(xfs_buf_t *))
+{
+	bp->b_io_callback = func;
+}
+
 /* Pinning Buffer Storage in Memory */
 extern void xfs_buf_pin(xfs_buf_t *);
 extern void xfs_buf_unpin(xfs_buf_t *);
Index: 2.6.x-xfs-new/fs/xfs/xfs_inode.c
===================================================================
--- 2.6.x-xfs-new.orig/fs/xfs/xfs_inode.c	2008-05-16 23:13:51.000000000 +1000
+++ 2.6.x-xfs-new/fs/xfs/xfs_inode.c	2008-05-16 23:19:06.365675452 +1000
@@ -125,6 +125,83 @@ xfs_inobp_check(
 #endif
 
 /*
+ * Add a crc to the inode field. It is assumed that the
+ * inode has already been converted to a version 2 inode at
+ * this point.
+ */
+void
+xfs_inode_crc_add(
+	xfs_dinode_t	*dip)
+{
+	/* TODO */
+}
+
+/*
+ * Writeback time inode cluster buffer checking function.
+ */
+void
+xfs_inobp_wbcheck(
+	xfs_buf_t	*bp)
+{
+	xfs_mount_t	*mp;
+	int		i, j;
+	xfs_dinode_t	*dip;
+	char		str[64];
+
+	mp = XFS_BUF_FSPRIVATE3(bp, xfs_mount_t *);
+	ASSERT(mp != NULL);
+
+	j = mp->m_inode_cluster_size >> mp->m_sb.sb_inodelog;
+
+	memset(str, 0, 64);
+	for (i = 0; i < j; i++)  {
+		int	di_ok;
+		dip = (xfs_dinode_t *)xfs_buf_offset(bp,
+					i * mp->m_sb.sb_inodesize);
+
+		di_ok = be16_to_cpu(dip->di_core.di_magic) == XFS_DINODE_MAGIC &&
+			    XFS_DINODE_GOOD_VERSION(dip->di_core.di_version);
+		if (!di_ok) {
+			snprintf(str, 64, "Bad magic # 0x%x ",
+						dip->di_core.di_magic);
+			goto corrupt_inode;
+
+		}
+		if (dip->di_next_unlinked == 0)  {
+			snprintf(str, 64, "Bad next_unlinked field (0)");
+			goto corrupt_inode;
+		}
+
+		/*
+		 * XXX: other checks:
+		 *
+		 *	- inode version - crc's mean version 2 only
+		 *	- link counts sane
+		 *	- next_unlinked points to an inode within the AG size
+		 *	- inline vs extent vs btree data fork literal area valid
+		 *	- inline vs extent vs btree attr fork literal area valid
+		 *	- size/blocks sane
+		 *	- forkoff sane
+		 *	- flags valid
+		 *	- dmapi states/masks valid
+		 */
+
+		xfs_inode_crc_add(dip);
+	}
+
+	return;
+
+corrupt_inode:
+	cmn_err(CE_WARN,
+		"%s in XFS inode buffer 0x%p, starting blkno %lld, offset 0x%x",
+		str, bp, (unsigned long long)bp->b_bn,
+		i * mp->m_sb.sb_inodesize);
+	xfs_fs_cmn_err(CE_WARN, mp,
+		"corrupt, unmount and run xfs_repair");
+	return;
+}
+
+/*
  * Find the buffer associated with the given inode map
  * We do basic validation checks on the buffer once it has been
  * retrieved from disk.
@@ -201,8 +278,10 @@ xfs_imap_to_bp(
 
 	/*
 	 * Mark the buffer as an inode buffer now that it looks good
+	 * and attach the writeback checking callback.
 	 */
 	XFS_BUF_SET_VTYPE(bp, B_FS_INO);
+	xfs_buf_set_io_callback(bp, xfs_inobp_wbcheck);
 
 	*bpp = bp;
 	return 0;
