forked from Minki/linux
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: Christoph Hellwig <hch@lst.de> Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Ben Myers <bpm@sgi.com> Signed-off-by: Ben Myers <bpm@sgi.com>
This commit is contained in:
parent
983d09ffe3
commit
3fe58f30b4
@ -36,6 +36,7 @@
|
||||
#include "xfs_trans_space.h"
|
||||
#include "xfs_trans_priv.h"
|
||||
#include "xfs_qm.h"
|
||||
#include "xfs_cksum.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_id = cpu_to_be32(curid);
|
||||
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,
|
||||
@ -283,16 +286,77 @@ xfs_dquot_set_prealloc_limits(struct xfs_dquot *dqp)
|
||||
dqp->q_low_space[XFS_QLOWSP_5_PCNT] = space * 5;
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dquot_buf_verify(
|
||||
STATIC void
|
||||
xfs_dquot_buf_calc_crc(
|
||||
struct xfs_mount *mp,
|
||||
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_disk_dquot *ddq;
|
||||
xfs_dqid_t id = 0;
|
||||
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(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_dqblk *d = (struct xfs_dqblk *)bp->b_addr;
|
||||
xfs_dqid_t id = 0;
|
||||
int ndquots;
|
||||
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.
|
||||
* We don't know what the id of the dquot is supposed to be, just that
|
||||
@ -300,8 +364,9 @@ xfs_dquot_buf_verify(
|
||||
* 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.
|
||||
*/
|
||||
for (i = 0; i < mp->m_quotainfo->qi_dqperchunk; i++) {
|
||||
int error;
|
||||
for (i = 0; i < ndquots; i++) {
|
||||
struct xfs_disk_dquot *ddq;
|
||||
int error;
|
||||
|
||||
ddq = &d[i].dd_diskdq;
|
||||
|
||||
@ -309,27 +374,37 @@ xfs_dquot_buf_verify(
|
||||
id = be32_to_cpu(ddq->d_id);
|
||||
|
||||
error = xfs_qm_dqcheck(mp, ddq, id + i, 0, XFS_QMOPT_DOWARN,
|
||||
"xfs_dquot_read_verify");
|
||||
if (error) {
|
||||
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, d);
|
||||
xfs_buf_ioerror(bp, EFSCORRUPTED);
|
||||
break;
|
||||
}
|
||||
"xfs_dquot_buf_verify");
|
||||
if (error)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dquot_buf_read_verify(
|
||||
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
|
||||
xfs_dquot_buf_write_verify(
|
||||
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 = {
|
||||
@ -1072,6 +1147,17 @@ xfs_qm_dqflush(
|
||||
xfs_trans_ail_copy_lsn(mp->m_ail, &dqp->q_logitem.qli_flush_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
|
||||
* AIL and release the flush lock once the dquot is synced to disk.
|
||||
|
@ -1979,6 +1979,16 @@ xlog_recover_do_reg_buffer(
|
||||
}
|
||||
bp->b_ops = &xfs_agi_buf_ops;
|
||||
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:
|
||||
break;
|
||||
}
|
||||
|
@ -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
|
||||
* mount structure
|
||||
@ -656,9 +670,8 @@ xfs_qm_init_quotainfo(
|
||||
|
||||
/* Precalc some constants */
|
||||
qinf->qi_dqchunklen = XFS_FSB_TO_BB(mp, XFS_DQUOT_CLUSTER_SIZE_FSB);
|
||||
ASSERT(qinf->qi_dqchunklen);
|
||||
qinf->qi_dqperchunk = BBTOB(qinf->qi_dqchunklen);
|
||||
do_div(qinf->qi_dqperchunk, sizeof(xfs_dqblk_t));
|
||||
qinf->qi_dqperchunk = xfs_qm_calc_dquots_per_chunk(mp,
|
||||
qinf->qi_dqchunklen);
|
||||
|
||||
mp->m_qflags |= (mp->m_sb.sb_qflags & XFS_ALL_QUOTA_CHKD);
|
||||
|
||||
@ -897,6 +910,10 @@ xfs_qm_dqiter_bufs(
|
||||
if (error)
|
||||
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_buf_delwri_queue(bp, buffer_list);
|
||||
xfs_buf_relse(bp);
|
||||
|
@ -75,6 +75,8 @@ typedef struct xfs_quotainfo {
|
||||
&((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 int xfs_trans_reserve_quota_bydquots(xfs_trans_t *, xfs_mount_t *,
|
||||
xfs_dquot_t *, xfs_dquot_t *, long, long, uint);
|
||||
|
@ -77,7 +77,14 @@ typedef struct xfs_disk_dquot {
|
||||
*/
|
||||
typedef struct xfs_dqblk {
|
||||
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;
|
||||
|
||||
/*
|
||||
@ -380,5 +387,7 @@ extern int xfs_qm_dqcheck(struct xfs_mount *, xfs_disk_dquot_t *,
|
||||
xfs_dqid_t, uint, uint, char *);
|
||||
extern int xfs_mount_reset_sbqflags(struct xfs_mount *);
|
||||
|
||||
extern const struct xfs_buf_ops xfs_dquot_buf_ops;
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* __XFS_QUOTA_H__ */
|
||||
|
Loading…
Reference in New Issue
Block a user