Commit 3fe58f30b4fc3f8a9084b035a02bc0c67bee8d00

Authored by Christoph Hellwig
Committed by Ben Myers
1 parent 983d09ffe3

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>

Showing 5 changed files with 141 additions and 17 deletions Side-by-side Diff

... ... @@ -36,6 +36,7 @@
36 36 #include "xfs_trans_space.h"
37 37 #include "xfs_trans_priv.h"
38 38 #include "xfs_qm.h"
  39 +#include "xfs_cksum.h"
39 40 #include "xfs_trace.h"
40 41  
41 42 /*
... ... @@ -248,6 +249,8 @@
248 249 d->dd_diskdq.d_version = XFS_DQUOT_VERSION;
249 250 d->dd_diskdq.d_id = cpu_to_be32(curid);
250 251 d->dd_diskdq.d_flags = type;
  252 + if (xfs_sb_version_hascrc(&mp->m_sb))
  253 + uuid_copy(&d->dd_uuid, &mp->m_sb.sb_uuid);
251 254 }
252 255  
253 256 xfs_trans_dquot_buf(tp, bp,
254 257  
255 258  
256 259  
257 260  
258 261  
259 262  
... ... @@ -283,25 +286,87 @@
283 286 dqp->q_low_space[XFS_QLOWSP_5_PCNT] = space * 5;
284 287 }
285 288  
286   -static void
  289 +STATIC void
  290 +xfs_dquot_buf_calc_crc(
  291 + struct xfs_mount *mp,
  292 + struct xfs_buf *bp)
  293 +{
  294 + struct xfs_dqblk *d = (struct xfs_dqblk *)bp->b_addr;
  295 + int i;
  296 +
  297 + if (!xfs_sb_version_hascrc(&mp->m_sb))
  298 + return;
  299 +
  300 + for (i = 0; i < mp->m_quotainfo->qi_dqperchunk; i++, d++) {
  301 + xfs_update_cksum((char *)d, sizeof(struct xfs_dqblk),
  302 + offsetof(struct xfs_dqblk, dd_crc));
  303 + }
  304 +}
  305 +
  306 +STATIC bool
  307 +xfs_dquot_buf_verify_crc(
  308 + struct xfs_mount *mp,
  309 + struct xfs_buf *bp)
  310 +{
  311 + struct xfs_dqblk *d = (struct xfs_dqblk *)bp->b_addr;
  312 + int ndquots;
  313 + int i;
  314 +
  315 + if (!xfs_sb_version_hascrc(&mp->m_sb))
  316 + return true;
  317 +
  318 + /*
  319 + * if we are in log recovery, the quota subsystem has not been
  320 + * initialised so we have no quotainfo structure. In that case, we need
  321 + * to manually calculate the number of dquots in the buffer.
  322 + */
  323 + if (mp->m_quotainfo)
  324 + ndquots = mp->m_quotainfo->qi_dqperchunk;
  325 + else
  326 + ndquots = xfs_qm_calc_dquots_per_chunk(mp,
  327 + XFS_BB_TO_FSB(mp, bp->b_length));
  328 +
  329 + for (i = 0; i < ndquots; i++, d++) {
  330 + if (!xfs_verify_cksum((char *)d, sizeof(struct xfs_dqblk),
  331 + offsetof(struct xfs_dqblk, dd_crc)))
  332 + return false;
  333 + if (!uuid_equal(&d->dd_uuid, &mp->m_sb.sb_uuid))
  334 + return false;
  335 + }
  336 +
  337 + return true;
  338 +}
  339 +
  340 +STATIC bool
287 341 xfs_dquot_buf_verify(
  342 + struct xfs_mount *mp,
288 343 struct xfs_buf *bp)
289 344 {
290   - struct xfs_mount *mp = bp->b_target->bt_mount;
291 345 struct xfs_dqblk *d = (struct xfs_dqblk *)bp->b_addr;
292   - struct xfs_disk_dquot *ddq;
293 346 xfs_dqid_t id = 0;
  347 + int ndquots;
294 348 int i;
295 349  
296 350 /*
  351 + * if we are in log recovery, the quota subsystem has not been
  352 + * initialised so we have no quotainfo structure. In that case, we need
  353 + * to manually calculate the number of dquots in the buffer.
  354 + */
  355 + if (mp->m_quotainfo)
  356 + ndquots = mp->m_quotainfo->qi_dqperchunk;
  357 + else
  358 + ndquots = xfs_qm_calc_dquots_per_chunk(mp, bp->b_length);
  359 +
  360 + /*
297 361 * On the first read of the buffer, verify that each dquot is valid.
298 362 * We don't know what the id of the dquot is supposed to be, just that
299 363 * they should be increasing monotonically within the buffer. If the
300 364 * first id is corrupt, then it will fail on the second dquot in the
301 365 * buffer so corruptions could point to the wrong dquot in this case.
302 366 */
303   - for (i = 0; i < mp->m_quotainfo->qi_dqperchunk; i++) {
304   - int error;
  367 + for (i = 0; i < ndquots; i++) {
  368 + struct xfs_disk_dquot *ddq;
  369 + int error;
305 370  
306 371 ddq = &d[i].dd_diskdq;
307 372  
308 373  
309 374  
310 375  
... ... @@ -309,27 +374,37 @@
309 374 id = be32_to_cpu(ddq->d_id);
310 375  
311 376 error = xfs_qm_dqcheck(mp, ddq, id + i, 0, XFS_QMOPT_DOWARN,
312   - "xfs_dquot_read_verify");
313   - if (error) {
314   - XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, d);
315   - xfs_buf_ioerror(bp, EFSCORRUPTED);
316   - break;
317   - }
  377 + "xfs_dquot_buf_verify");
  378 + if (error)
  379 + return false;
318 380 }
  381 + return true;
319 382 }
320 383  
321 384 static void
322 385 xfs_dquot_buf_read_verify(
323 386 struct xfs_buf *bp)
324 387 {
325   - xfs_dquot_buf_verify(bp);
  388 + struct xfs_mount *mp = bp->b_target->bt_mount;
  389 +
  390 + if (!xfs_dquot_buf_verify_crc(mp, bp) || !xfs_dquot_buf_verify(mp, bp)) {
  391 + XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
  392 + xfs_buf_ioerror(bp, EFSCORRUPTED);
  393 + }
326 394 }
327 395  
328 396 void
329 397 xfs_dquot_buf_write_verify(
330 398 struct xfs_buf *bp)
331 399 {
332   - xfs_dquot_buf_verify(bp);
  400 + struct xfs_mount *mp = bp->b_target->bt_mount;
  401 +
  402 + if (!xfs_dquot_buf_verify(mp, bp)) {
  403 + XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
  404 + xfs_buf_ioerror(bp, EFSCORRUPTED);
  405 + return;
  406 + }
  407 + xfs_dquot_buf_calc_crc(mp, bp);
333 408 }
334 409  
335 410 const struct xfs_buf_ops xfs_dquot_buf_ops = {
... ... @@ -1071,6 +1146,17 @@
1071 1146  
1072 1147 xfs_trans_ail_copy_lsn(mp->m_ail, &dqp->q_logitem.qli_flush_lsn,
1073 1148 &dqp->q_logitem.qli_item.li_lsn);
  1149 +
  1150 + /*
  1151 + * copy the lsn into the on-disk dquot now while we have the in memory
  1152 + * dquot here. This can't be done later in the write verifier as we
  1153 + * can't get access to the log item at that point in time.
  1154 + */
  1155 + if (xfs_sb_version_hascrc(&mp->m_sb)) {
  1156 + struct xfs_dqblk *dqb = (struct xfs_dqblk *)ddqp;
  1157 +
  1158 + dqb->dd_lsn = cpu_to_be64(dqp->q_logitem.qli_item.li_lsn);
  1159 + }
1074 1160  
1075 1161 /*
1076 1162 * Attach an iodone routine so that we can remove this dquot from the
fs/xfs/xfs_log_recover.c
... ... @@ -1979,6 +1979,16 @@
1979 1979 }
1980 1980 bp->b_ops = &xfs_agi_buf_ops;
1981 1981 break;
  1982 + case XFS_BLF_UDQUOT_BUF:
  1983 + case XFS_BLF_PDQUOT_BUF:
  1984 + case XFS_BLF_GDQUOT_BUF:
  1985 + if (*(__be16 *)bp->b_addr != cpu_to_be16(XFS_DQUOT_MAGIC)) {
  1986 + xfs_warn(mp, "Bad DQUOT block magic!");
  1987 + ASSERT(0);
  1988 + break;
  1989 + }
  1990 + bp->b_ops = &xfs_dquot_buf_ops;
  1991 + break;
1982 1992 default:
1983 1993 break;
1984 1994 }
... ... @@ -617,6 +617,20 @@
617 617 }
618 618 }
619 619  
  620 +int
  621 +xfs_qm_calc_dquots_per_chunk(
  622 + struct xfs_mount *mp,
  623 + unsigned int nbblks) /* basic block units */
  624 +{
  625 + unsigned int ndquots;
  626 +
  627 + ASSERT(nbblks > 0);
  628 + ndquots = BBTOB(nbblks);
  629 + do_div(ndquots, sizeof(xfs_dqblk_t));
  630 +
  631 + return ndquots;
  632 +}
  633 +
620 634 /*
621 635 * This initializes all the quota information that's kept in the
622 636 * mount structure
... ... @@ -656,9 +670,8 @@
656 670  
657 671 /* Precalc some constants */
658 672 qinf->qi_dqchunklen = XFS_FSB_TO_BB(mp, XFS_DQUOT_CLUSTER_SIZE_FSB);
659   - ASSERT(qinf->qi_dqchunklen);
660   - qinf->qi_dqperchunk = BBTOB(qinf->qi_dqchunklen);
661   - do_div(qinf->qi_dqperchunk, sizeof(xfs_dqblk_t));
  673 + qinf->qi_dqperchunk = xfs_qm_calc_dquots_per_chunk(mp,
  674 + qinf->qi_dqchunklen);
662 675  
663 676 mp->m_qflags |= (mp->m_sb.sb_qflags & XFS_ALL_QUOTA_CHKD);
664 677  
... ... @@ -897,6 +910,10 @@
897 910 if (error)
898 911 break;
899 912  
  913 + /*
  914 + * XXX(hch): need to figure out if it makes sense to validate
  915 + * the CRC here.
  916 + */
900 917 xfs_qm_reset_dqcounts(mp, bp, firstid, type);
901 918 xfs_buf_delwri_queue(bp, buffer_list);
902 919 xfs_buf_relse(bp);
... ... @@ -75,6 +75,8 @@
75 75 &((qi)->qi_gquota_tree))
76 76  
77 77  
  78 +extern int xfs_qm_calc_dquots_per_chunk(struct xfs_mount *mp,
  79 + unsigned int nbblks);
78 80 extern void xfs_trans_mod_dquot(xfs_trans_t *, xfs_dquot_t *, uint, long);
79 81 extern int xfs_trans_reserve_quota_bydquots(xfs_trans_t *, xfs_mount_t *,
80 82 xfs_dquot_t *, xfs_dquot_t *, long, long, uint);
... ... @@ -77,7 +77,14 @@
77 77 */
78 78 typedef struct xfs_dqblk {
79 79 xfs_disk_dquot_t dd_diskdq; /* portion that lives incore as well */
80   - char dd_fill[32]; /* filling for posterity */
  80 + char dd_fill[4]; /* filling for posterity */
  81 +
  82 + /*
  83 + * These two are only present on filesystems with the CRC bits set.
  84 + */
  85 + __be32 dd_crc; /* checksum */
  86 + __be64 dd_lsn; /* last modification in log */
  87 + uuid_t dd_uuid; /* location information */
81 88 } xfs_dqblk_t;
82 89  
83 90 /*
... ... @@ -379,6 +386,8 @@
379 386 extern int xfs_qm_dqcheck(struct xfs_mount *, xfs_disk_dquot_t *,
380 387 xfs_dqid_t, uint, uint, char *);
381 388 extern int xfs_mount_reset_sbqflags(struct xfs_mount *);
  389 +
  390 +extern const struct xfs_buf_ops xfs_dquot_buf_ops;
382 391  
383 392 #endif /* __KERNEL__ */
384 393 #endif /* __XFS_QUOTA_H__ */