diff options
-rw-r--r-- | fs/xfs/xfs_dquot.c | 117 | ||||
-rw-r--r-- | fs/xfs/xfs_dquot.h | 1 | ||||
-rw-r--r-- | fs/xfs/xfs_qm.c | 3 |
3 files changed, 98 insertions, 23 deletions
diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index e95f800333d4..0ba0f0992d6e 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c | |||
@@ -360,6 +360,89 @@ xfs_qm_dqalloc( | |||
360 | return (error); | 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 | * Maps a dquot to the buffer containing its on-disk version. | 447 | * Maps a dquot to the buffer containing its on-disk version. |
365 | * This returns a ptr to the buffer containing the on-disk dquot | 448 | * This returns a ptr to the buffer containing the on-disk dquot |
@@ -378,7 +461,6 @@ xfs_qm_dqtobp( | |||
378 | xfs_buf_t *bp; | 461 | xfs_buf_t *bp; |
379 | xfs_inode_t *quotip = XFS_DQ_TO_QIP(dqp); | 462 | xfs_inode_t *quotip = XFS_DQ_TO_QIP(dqp); |
380 | xfs_mount_t *mp = dqp->q_mount; | 463 | xfs_mount_t *mp = dqp->q_mount; |
381 | xfs_disk_dquot_t *ddq; | ||
382 | xfs_dqid_t id = be32_to_cpu(dqp->q_core.d_id); | 464 | xfs_dqid_t id = be32_to_cpu(dqp->q_core.d_id); |
383 | xfs_trans_t *tp = (tpp ? *tpp : NULL); | 465 | xfs_trans_t *tp = (tpp ? *tpp : NULL); |
384 | 466 | ||
@@ -439,33 +521,24 @@ xfs_qm_dqtobp( | |||
439 | error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, | 521 | error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, |
440 | dqp->q_blkno, | 522 | dqp->q_blkno, |
441 | mp->m_quotainfo->qi_dqchunklen, | 523 | mp->m_quotainfo->qi_dqchunklen, |
442 | 0, &bp, NULL); | 524 | 0, &bp, xfs_dquot_read_verify); |
443 | if (error || !bp) | ||
444 | return XFS_ERROR(error); | ||
445 | } | ||
446 | 525 | ||
447 | ASSERT(xfs_buf_islocked(bp)); | 526 | if (error == EFSCORRUPTED && (flags & XFS_QMOPT_DQREPAIR)) { |
448 | 527 | xfs_dqid_t firstid = (xfs_dqid_t)map.br_startoff * | |
449 | /* | 528 | mp->m_quotainfo->qi_dqperchunk; |
450 | * calculate the location of the dquot inside the buffer. | 529 | ASSERT(bp == NULL); |
451 | */ | 530 | error = xfs_qm_dqrepair(mp, tp, dqp, firstid, &bp); |
452 | ddq = bp->b_addr + dqp->q_bufoffset; | 531 | } |
453 | 532 | ||
454 | /* | 533 | if (error) { |
455 | * A simple sanity check in case we got a corrupted dquot... | 534 | ASSERT(bp == NULL); |
456 | */ | 535 | return XFS_ERROR(error); |
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); | ||
464 | } | 536 | } |
465 | } | 537 | } |
466 | 538 | ||
539 | ASSERT(xfs_buf_islocked(bp)); | ||
467 | *O_bpp = bp; | 540 | *O_bpp = bp; |
468 | *O_ddpp = ddq; | 541 | *O_ddpp = bp->b_addr + dqp->q_bufoffset; |
469 | 542 | ||
470 | return (0); | 543 | return (0); |
471 | } | 544 | } |
diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index 7d20af27346d..a08ba92d7da0 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h | |||
@@ -140,6 +140,7 @@ static inline xfs_dquot_t *xfs_inode_dquot(struct xfs_inode *ip, int type) | |||
140 | 140 | ||
141 | extern int xfs_qm_dqread(struct xfs_mount *, xfs_dqid_t, uint, | 141 | extern int xfs_qm_dqread(struct xfs_mount *, xfs_dqid_t, uint, |
142 | uint, struct xfs_dquot **); | 142 | uint, struct xfs_dquot **); |
143 | extern void xfs_dquot_read_verify(struct xfs_buf *bp); | ||
143 | extern void xfs_qm_dqdestroy(xfs_dquot_t *); | 144 | extern void xfs_qm_dqdestroy(xfs_dquot_t *); |
144 | extern int xfs_qm_dqflush(struct xfs_dquot *, struct xfs_buf **); | 145 | extern int xfs_qm_dqflush(struct xfs_dquot *, struct xfs_buf **); |
145 | extern void xfs_qm_dqunpin_wait(xfs_dquot_t *); | 146 | extern void xfs_qm_dqunpin_wait(xfs_dquot_t *); |
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 688f608b3668..a6dfb97490cc 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c | |||
@@ -892,7 +892,8 @@ xfs_qm_dqiter_bufs( | |||
892 | while (blkcnt--) { | 892 | while (blkcnt--) { |
893 | error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp, | 893 | error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp, |
894 | XFS_FSB_TO_DADDR(mp, bno), | 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 | if (error) | 897 | if (error) |
897 | break; | 898 | break; |
898 | 899 | ||