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

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
...@@ -36,6 +36,7 @@ ...@@ -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( ...@@ -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) ...@@ -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,8 +364,9 @@ xfs_dquot_buf_verify( ...@@ -300,8 +364,9 @@ 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++) {
int error; struct xfs_disk_dquot *ddq;
int error;
ddq = &d[i].dd_diskdq; ddq = &d[i].dd_diskdq;
...@@ -309,27 +374,37 @@ xfs_dquot_buf_verify( ...@@ -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( ...@@ -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.
......
...@@ -1979,6 +1979,16 @@ xlog_recover_do_reg_buffer( ...@@ -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;
} }
......
...@@ -617,6 +617,20 @@ xfs_qm_dqdetach( ...@@ -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( ...@@ -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( ...@@ -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);
......
...@@ -75,6 +75,8 @@ typedef struct xfs_quotainfo { ...@@ -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);
......
...@@ -77,7 +77,14 @@ typedef struct xfs_disk_dquot { ...@@ -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 *, ...@@ -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__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment