Commit c6319198702350a2215a8c0cacd6cc4283728a1b

Authored by Dave Chinner
Committed by Ben Myers
1 parent 3d3e6f64e2

xfs: verify dquot blocks as they are read from disk

Add a dquot buffer verify callback function and pass it into the
buffer read functions. This checks all the dquots in a buffer, but
cannot completely verify the dquot ids are correct. Also, errors
cannot be repaired, so an additional function is added to repair bad
dquots in the buffer if such an error is detected in a context where
repair is allowed.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Phil White <pwhite@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>

Showing 3 changed files with 98 additions and 23 deletions Side-by-side Diff

... ... @@ -360,6 +360,89 @@
360 360 return (error);
361 361 }
362 362  
  363 +void
  364 +xfs_dquot_read_verify(
  365 + struct xfs_buf *bp)
  366 +{
  367 + struct xfs_mount *mp = bp->b_target->bt_mount;
  368 + struct xfs_dqblk *d = (struct xfs_dqblk *)bp->b_addr;
  369 + struct xfs_disk_dquot *ddq;
  370 + xfs_dqid_t id = 0;
  371 + int i;
  372 +
  373 + /*
  374 + * On the first read of the buffer, verify that each dquot is valid.
  375 + * We don't know what the id of the dquot is supposed to be, just that
  376 + * they should be increasing monotonically within the buffer. If the
  377 + * first id is corrupt, then it will fail on the second dquot in the
  378 + * buffer so corruptions could point to the wrong dquot in this case.
  379 + */
  380 + for (i = 0; i < mp->m_quotainfo->qi_dqperchunk; i++) {
  381 + int error;
  382 +
  383 + ddq = &d[i].dd_diskdq;
  384 +
  385 + if (i == 0)
  386 + id = be32_to_cpu(ddq->d_id);
  387 +
  388 + error = xfs_qm_dqcheck(mp, ddq, id + i, 0, XFS_QMOPT_DOWARN,
  389 + "xfs_dquot_read_verify");
  390 + if (error) {
  391 + XFS_CORRUPTION_ERROR("xfs_dquot_read_verify",
  392 + XFS_ERRLEVEL_LOW, mp, d);
  393 + xfs_buf_ioerror(bp, EFSCORRUPTED);
  394 + break;
  395 + }
  396 + }
  397 + bp->b_iodone = NULL;
  398 + xfs_buf_ioend(bp, 0);
  399 +}
  400 +
  401 +STATIC int
  402 +xfs_qm_dqrepair(
  403 + struct xfs_mount *mp,
  404 + struct xfs_trans *tp,
  405 + struct xfs_dquot *dqp,
  406 + xfs_dqid_t firstid,
  407 + struct xfs_buf **bpp)
  408 +{
  409 + int error;
  410 + struct xfs_disk_dquot *ddq;
  411 + struct xfs_dqblk *d;
  412 + int i;
  413 +
  414 + /*
  415 + * Read the buffer without verification so we get the corrupted
  416 + * buffer returned to us.
  417 + */
  418 + error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, dqp->q_blkno,
  419 + mp->m_quotainfo->qi_dqchunklen,
  420 + 0, bpp, NULL);
  421 +
  422 + if (error) {
  423 + ASSERT(*bpp == NULL);
  424 + return XFS_ERROR(error);
  425 + }
  426 +
  427 + ASSERT(xfs_buf_islocked(*bpp));
  428 + d = (struct xfs_dqblk *)(*bpp)->b_addr;
  429 +
  430 + /* Do the actual repair of dquots in this buffer */
  431 + for (i = 0; i < mp->m_quotainfo->qi_dqperchunk; i++) {
  432 + ddq = &d[i].dd_diskdq;
  433 + error = xfs_qm_dqcheck(mp, ddq, firstid + i,
  434 + dqp->dq_flags & XFS_DQ_ALLTYPES,
  435 + XFS_QMOPT_DQREPAIR, "xfs_qm_dqrepair");
  436 + if (error) {
  437 + /* repair failed, we're screwed */
  438 + xfs_trans_brelse(tp, *bpp);
  439 + return XFS_ERROR(EIO);
  440 + }
  441 + }
  442 +
  443 + return 0;
  444 +}
  445 +
363 446 /*
364 447 * Maps a dquot to the buffer containing its on-disk version.
365 448 * This returns a ptr to the buffer containing the on-disk dquot
... ... @@ -378,7 +461,6 @@
378 461 xfs_buf_t *bp;
379 462 xfs_inode_t *quotip = XFS_DQ_TO_QIP(dqp);
380 463 xfs_mount_t *mp = dqp->q_mount;
381   - xfs_disk_dquot_t *ddq;
382 464 xfs_dqid_t id = be32_to_cpu(dqp->q_core.d_id);
383 465 xfs_trans_t *tp = (tpp ? *tpp : NULL);
384 466  
385 467  
386 468  
387 469  
388 470  
... ... @@ -439,33 +521,24 @@
439 521 error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
440 522 dqp->q_blkno,
441 523 mp->m_quotainfo->qi_dqchunklen,
442   - 0, &bp, NULL);
443   - if (error || !bp)
444   - return XFS_ERROR(error);
445   - }
  524 + 0, &bp, xfs_dquot_read_verify);
446 525  
447   - ASSERT(xfs_buf_islocked(bp));
  526 + if (error == EFSCORRUPTED && (flags & XFS_QMOPT_DQREPAIR)) {
  527 + xfs_dqid_t firstid = (xfs_dqid_t)map.br_startoff *
  528 + mp->m_quotainfo->qi_dqperchunk;
  529 + ASSERT(bp == NULL);
  530 + error = xfs_qm_dqrepair(mp, tp, dqp, firstid, &bp);
  531 + }
448 532  
449   - /*
450   - * calculate the location of the dquot inside the buffer.
451   - */
452   - ddq = bp->b_addr + dqp->q_bufoffset;
453   -
454   - /*
455   - * A simple sanity check in case we got a corrupted dquot...
456   - */
457   - error = xfs_qm_dqcheck(mp, ddq, id, dqp->dq_flags & XFS_DQ_ALLTYPES,
458   - flags & (XFS_QMOPT_DQREPAIR|XFS_QMOPT_DOWARN),
459   - "dqtobp");
460   - if (error) {
461   - if (!(flags & XFS_QMOPT_DQREPAIR)) {
462   - xfs_trans_brelse(tp, bp);
463   - return XFS_ERROR(EIO);
  533 + if (error) {
  534 + ASSERT(bp == NULL);
  535 + return XFS_ERROR(error);
464 536 }
465 537 }
466 538  
  539 + ASSERT(xfs_buf_islocked(bp));
467 540 *O_bpp = bp;
468   - *O_ddpp = ddq;
  541 + *O_ddpp = bp->b_addr + dqp->q_bufoffset;
469 542  
470 543 return (0);
471 544 }
... ... @@ -140,6 +140,7 @@
140 140  
141 141 extern int xfs_qm_dqread(struct xfs_mount *, xfs_dqid_t, uint,
142 142 uint, struct xfs_dquot **);
  143 +extern void xfs_dquot_read_verify(struct xfs_buf *bp);
143 144 extern void xfs_qm_dqdestroy(xfs_dquot_t *);
144 145 extern int xfs_qm_dqflush(struct xfs_dquot *, struct xfs_buf **);
145 146 extern void xfs_qm_dqunpin_wait(xfs_dquot_t *);
... ... @@ -892,7 +892,8 @@
892 892 while (blkcnt--) {
893 893 error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp,
894 894 XFS_FSB_TO_DADDR(mp, bno),
895   - mp->m_quotainfo->qi_dqchunklen, 0, &bp, NULL);
  895 + mp->m_quotainfo->qi_dqchunklen, 0, &bp,
  896 + xfs_dquot_read_verify);
896 897 if (error)
897 898 break;
898 899