Commit c6319198702350a2215a8c0cacd6cc4283728a1b
Committed by
Ben Myers
1 parent
3d3e6f64e2
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
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
fs/xfs/xfs_dquot.c
... | ... | @@ -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 | } |
fs/xfs/xfs_dquot.h
... | ... | @@ -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 *); |
fs/xfs/xfs_qm.c
... | ... | @@ -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 |