Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3fe58f30 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Ben Myers
Browse files

xfs: add CRC checks for quota blocks



Use the reserved space in struct xfs_dqblk to store a UUID and a crc
for the quota blocks.

[dchinner@redhat.com] Add a LSN field and update for current verifier
infrastructure.

Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarBen Myers <bpm@sgi.com>
Signed-off-by: default avatarBen Myers <bpm@sgi.com>
parent 983d09ff
Loading
Loading
Loading
Loading
+99 −13
Original line number Original line Diff line number Diff line
@@ -36,6 +36,7 @@
#include "xfs_trans_space.h"
#include "xfs_trans_space.h"
#include "xfs_trans_priv.h"
#include "xfs_trans_priv.h"
#include "xfs_qm.h"
#include "xfs_qm.h"
#include "xfs_cksum.h"
#include "xfs_trace.h"
#include "xfs_trace.h"


/*
/*
@@ -248,6 +249,8 @@ xfs_qm_init_dquot_blk(
		d->dd_diskdq.d_version = XFS_DQUOT_VERSION;
		d->dd_diskdq.d_version = XFS_DQUOT_VERSION;
		d->dd_diskdq.d_id = cpu_to_be32(curid);
		d->dd_diskdq.d_id = cpu_to_be32(curid);
		d->dd_diskdq.d_flags = type;
		d->dd_diskdq.d_flags = type;
		if (xfs_sb_version_hascrc(&mp->m_sb))
			uuid_copy(&d->dd_uuid, &mp->m_sb.sb_uuid);
	}
	}


	xfs_trans_dquot_buf(tp, bp,
	xfs_trans_dquot_buf(tp, bp,
@@ -283,16 +286,77 @@ xfs_dquot_set_prealloc_limits(struct xfs_dquot *dqp)
	dqp->q_low_space[XFS_QLOWSP_5_PCNT] = space * 5;
	dqp->q_low_space[XFS_QLOWSP_5_PCNT] = space * 5;
}
}


static void
STATIC void
xfs_dquot_buf_calc_crc(
	struct xfs_mount	*mp,
	struct xfs_buf		*bp)
{
	struct xfs_dqblk	*d = (struct xfs_dqblk *)bp->b_addr;
	int			i;

	if (!xfs_sb_version_hascrc(&mp->m_sb))
		return;

	for (i = 0; i < mp->m_quotainfo->qi_dqperchunk; i++, d++) {
		xfs_update_cksum((char *)d, sizeof(struct xfs_dqblk),
				 offsetof(struct xfs_dqblk, dd_crc));
	}
}

STATIC bool
xfs_dquot_buf_verify_crc(
	struct xfs_mount	*mp,
	struct xfs_buf		*bp)
{
	struct xfs_dqblk	*d = (struct xfs_dqblk *)bp->b_addr;
	int			ndquots;
	int			i;

	if (!xfs_sb_version_hascrc(&mp->m_sb))
		return true;

	/*
	 * if we are in log recovery, the quota subsystem has not been
	 * initialised so we have no quotainfo structure. In that case, we need
	 * to manually calculate the number of dquots in the buffer.
	 */
	if (mp->m_quotainfo)
		ndquots = mp->m_quotainfo->qi_dqperchunk;
	else
		ndquots = xfs_qm_calc_dquots_per_chunk(mp,
					XFS_BB_TO_FSB(mp, bp->b_length));

	for (i = 0; i < ndquots; i++, d++) {
		if (!xfs_verify_cksum((char *)d, sizeof(struct xfs_dqblk),
				 offsetof(struct xfs_dqblk, dd_crc)))
			return false;
		if (!uuid_equal(&d->dd_uuid, &mp->m_sb.sb_uuid))
			return false;
	}

	return true;
}

STATIC bool
xfs_dquot_buf_verify(
xfs_dquot_buf_verify(
	struct xfs_mount	*mp,
	struct xfs_buf		*bp)
	struct xfs_buf		*bp)
{
{
	struct xfs_mount	*mp = bp->b_target->bt_mount;
	struct xfs_dqblk	*d = (struct xfs_dqblk *)bp->b_addr;
	struct xfs_dqblk	*d = (struct xfs_dqblk *)bp->b_addr;
	struct xfs_disk_dquot	*ddq;
	xfs_dqid_t		id = 0;
	xfs_dqid_t		id = 0;
	int			ndquots;
	int			i;
	int			i;


	/*
	 * if we are in log recovery, the quota subsystem has not been
	 * initialised so we have no quotainfo structure. In that case, we need
	 * to manually calculate the number of dquots in the buffer.
	 */
	if (mp->m_quotainfo)
		ndquots = mp->m_quotainfo->qi_dqperchunk;
	else
		ndquots = xfs_qm_calc_dquots_per_chunk(mp, bp->b_length);

	/*
	/*
	 * On the first read of the buffer, verify that each dquot is valid.
	 * On the first read of the buffer, verify that each dquot is valid.
	 * We don't know what the id of the dquot is supposed to be, just that
	 * We don't know what the id of the dquot is supposed to be, just that
@@ -300,7 +364,8 @@ xfs_dquot_buf_verify(
	 * first id is corrupt, then it will fail on the second dquot in the
	 * first id is corrupt, then it will fail on the second dquot in the
	 * buffer so corruptions could point to the wrong dquot in this case.
	 * buffer so corruptions could point to the wrong dquot in this case.
	 */
	 */
	for (i = 0; i < mp->m_quotainfo->qi_dqperchunk; i++) {
	for (i = 0; i < ndquots; i++) {
		struct xfs_disk_dquot	*ddq;
		int			error;
		int			error;


		ddq = &d[i].dd_diskdq;
		ddq = &d[i].dd_diskdq;
@@ -309,27 +374,37 @@ xfs_dquot_buf_verify(
			id = be32_to_cpu(ddq->d_id);
			id = be32_to_cpu(ddq->d_id);


		error = xfs_qm_dqcheck(mp, ddq, id + i, 0, XFS_QMOPT_DOWARN,
		error = xfs_qm_dqcheck(mp, ddq, id + i, 0, XFS_QMOPT_DOWARN,
					"xfs_dquot_read_verify");
				       "xfs_dquot_buf_verify");
		if (error) {
		if (error)
			XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, d);
			return false;
			xfs_buf_ioerror(bp, EFSCORRUPTED);
			break;
		}
	}
	}
	return true;
}
}


static void
static void
xfs_dquot_buf_read_verify(
xfs_dquot_buf_read_verify(
	struct xfs_buf	*bp)
	struct xfs_buf	*bp)
{
{
	xfs_dquot_buf_verify(bp);
	struct xfs_mount	*mp = bp->b_target->bt_mount;

	if (!xfs_dquot_buf_verify_crc(mp, bp) || !xfs_dquot_buf_verify(mp, bp)) {
		XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
		xfs_buf_ioerror(bp, EFSCORRUPTED);
	}
}
}


void
void
xfs_dquot_buf_write_verify(
xfs_dquot_buf_write_verify(
	struct xfs_buf	*bp)
	struct xfs_buf	*bp)
{
{
	xfs_dquot_buf_verify(bp);
	struct xfs_mount	*mp = bp->b_target->bt_mount;

	if (!xfs_dquot_buf_verify(mp, bp)) {
		XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
		xfs_buf_ioerror(bp, EFSCORRUPTED);
		return;
	}
	xfs_dquot_buf_calc_crc(mp, bp);
}
}


const struct xfs_buf_ops xfs_dquot_buf_ops = {
const struct xfs_buf_ops xfs_dquot_buf_ops = {
@@ -1072,6 +1147,17 @@ xfs_qm_dqflush(
	xfs_trans_ail_copy_lsn(mp->m_ail, &dqp->q_logitem.qli_flush_lsn,
	xfs_trans_ail_copy_lsn(mp->m_ail, &dqp->q_logitem.qli_flush_lsn,
					&dqp->q_logitem.qli_item.li_lsn);
					&dqp->q_logitem.qli_item.li_lsn);


	/*
	 * copy the lsn into the on-disk dquot now while we have the in memory
	 * dquot here. This can't be done later in the write verifier as we
	 * can't get access to the log item at that point in time.
	 */
	if (xfs_sb_version_hascrc(&mp->m_sb)) {
		struct xfs_dqblk *dqb = (struct xfs_dqblk *)ddqp;

		dqb->dd_lsn = cpu_to_be64(dqp->q_logitem.qli_item.li_lsn);
	}

	/*
	/*
	 * Attach an iodone routine so that we can remove this dquot from the
	 * Attach an iodone routine so that we can remove this dquot from the
	 * AIL and release the flush lock once the dquot is synced to disk.
	 * AIL and release the flush lock once the dquot is synced to disk.
+10 −0
Original line number Original line Diff line number Diff line
@@ -1979,6 +1979,16 @@ xlog_recover_do_reg_buffer(
		}
		}
		bp->b_ops = &xfs_agi_buf_ops;
		bp->b_ops = &xfs_agi_buf_ops;
		break;
		break;
	case XFS_BLF_UDQUOT_BUF:
	case XFS_BLF_PDQUOT_BUF:
	case XFS_BLF_GDQUOT_BUF:
		if (*(__be16 *)bp->b_addr != cpu_to_be16(XFS_DQUOT_MAGIC)) {
			xfs_warn(mp, "Bad DQUOT block magic!");
			ASSERT(0);
			break;
		}
		bp->b_ops = &xfs_dquot_buf_ops;
		break;
	default:
	default:
		break;
		break;
	}
	}
+20 −3
Original line number Original line Diff line number Diff line
@@ -617,6 +617,20 @@ xfs_qm_dqdetach(
	}
	}
}
}


int
xfs_qm_calc_dquots_per_chunk(
	struct xfs_mount	*mp,
	unsigned int		nbblks)	/* basic block units */
{
	unsigned int	ndquots;

	ASSERT(nbblks > 0);
	ndquots = BBTOB(nbblks);
	do_div(ndquots, sizeof(xfs_dqblk_t));

	return ndquots;
}

/*
/*
 * This initializes all the quota information that's kept in the
 * This initializes all the quota information that's kept in the
 * mount structure
 * mount structure
@@ -656,9 +670,8 @@ xfs_qm_init_quotainfo(


	/* Precalc some constants */
	/* Precalc some constants */
	qinf->qi_dqchunklen = XFS_FSB_TO_BB(mp, XFS_DQUOT_CLUSTER_SIZE_FSB);
	qinf->qi_dqchunklen = XFS_FSB_TO_BB(mp, XFS_DQUOT_CLUSTER_SIZE_FSB);
	ASSERT(qinf->qi_dqchunklen);
	qinf->qi_dqperchunk = xfs_qm_calc_dquots_per_chunk(mp,
	qinf->qi_dqperchunk = BBTOB(qinf->qi_dqchunklen);
							qinf->qi_dqchunklen);
	do_div(qinf->qi_dqperchunk, sizeof(xfs_dqblk_t));


	mp->m_qflags |= (mp->m_sb.sb_qflags & XFS_ALL_QUOTA_CHKD);
	mp->m_qflags |= (mp->m_sb.sb_qflags & XFS_ALL_QUOTA_CHKD);


@@ -897,6 +910,10 @@ xfs_qm_dqiter_bufs(
		if (error)
		if (error)
			break;
			break;


		/*
		 * XXX(hch): need to figure out if it makes sense to validate
		 *	     the CRC here.
		 */
		xfs_qm_reset_dqcounts(mp, bp, firstid, type);
		xfs_qm_reset_dqcounts(mp, bp, firstid, type);
		xfs_buf_delwri_queue(bp, buffer_list);
		xfs_buf_delwri_queue(bp, buffer_list);
		xfs_buf_relse(bp);
		xfs_buf_relse(bp);
+2 −0
Original line number Original line Diff line number Diff line
@@ -75,6 +75,8 @@ typedef struct xfs_quotainfo {
	 &((qi)->qi_gquota_tree))
	 &((qi)->qi_gquota_tree))




extern int	xfs_qm_calc_dquots_per_chunk(struct xfs_mount *mp,
					     unsigned int nbblks);
extern void	xfs_trans_mod_dquot(xfs_trans_t *, xfs_dquot_t *, uint, long);
extern void	xfs_trans_mod_dquot(xfs_trans_t *, xfs_dquot_t *, uint, long);
extern int	xfs_trans_reserve_quota_bydquots(xfs_trans_t *, xfs_mount_t *,
extern int	xfs_trans_reserve_quota_bydquots(xfs_trans_t *, xfs_mount_t *,
			xfs_dquot_t *, xfs_dquot_t *, long, long, uint);
			xfs_dquot_t *, xfs_dquot_t *, long, long, uint);
+10 −1
Original line number Original line Diff line number Diff line
@@ -77,7 +77,14 @@ typedef struct xfs_disk_dquot {
 */
 */
typedef struct xfs_dqblk {
typedef struct xfs_dqblk {
	xfs_disk_dquot_t  dd_diskdq;	/* portion that lives incore as well */
	xfs_disk_dquot_t  dd_diskdq;	/* portion that lives incore as well */
	char		  dd_fill[32];	/* filling for posterity */
	char		  dd_fill[4];	/* filling for posterity */

	/*
	 * These two are only present on filesystems with the CRC bits set.
	 */
	__be32		  dd_crc;	/* checksum */
	__be64		  dd_lsn;	/* last modification in log */
	uuid_t		  dd_uuid;	/* location information */
} xfs_dqblk_t;
} xfs_dqblk_t;


/*
/*
@@ -380,5 +387,7 @@ extern int xfs_qm_dqcheck(struct xfs_mount *, xfs_disk_dquot_t *,
				xfs_dqid_t, uint, uint, char *);
				xfs_dqid_t, uint, uint, char *);
extern int xfs_mount_reset_sbqflags(struct xfs_mount *);
extern int xfs_mount_reset_sbqflags(struct xfs_mount *);


extern const struct xfs_buf_ops xfs_dquot_buf_ops;

#endif	/* __KERNEL__ */
#endif	/* __KERNEL__ */
#endif	/* __XFS_QUOTA_H__ */
#endif	/* __XFS_QUOTA_H__ */