xfs: verify dquot blocks as they are read from disk
[deliverable/linux.git] / fs / xfs / xfs_dquot.c
index e95f800333d4fabc4b82beed1ebbc53c3d6a0da9..0ba0f0992d6e0730f6a4ec0a01c4eb4420ce02cd 100644 (file)
@@ -360,6 +360,89 @@ xfs_qm_dqalloc(
        return (error);
 }
 
+void
+xfs_dquot_read_verify(
+       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;
+
+       /*
+        * 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
+        * they should be increasing monotonically within the buffer. If the
+        * 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;
+
+               ddq = &d[i].dd_diskdq;
+
+               if (i == 0)
+                       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("xfs_dquot_read_verify",
+                                            XFS_ERRLEVEL_LOW, mp, d);
+                       xfs_buf_ioerror(bp, EFSCORRUPTED);
+                       break;
+               }
+       }
+       bp->b_iodone = NULL;
+       xfs_buf_ioend(bp, 0);
+}
+
+STATIC int
+xfs_qm_dqrepair(
+       struct xfs_mount        *mp,
+       struct xfs_trans        *tp,
+       struct xfs_dquot        *dqp,
+       xfs_dqid_t              firstid,
+       struct xfs_buf          **bpp)
+{
+       int                     error;
+       struct xfs_disk_dquot   *ddq;
+       struct xfs_dqblk        *d;
+       int                     i;
+
+       /*
+        * Read the buffer without verification so we get the corrupted
+        * buffer returned to us.
+        */
+       error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, dqp->q_blkno,
+                                  mp->m_quotainfo->qi_dqchunklen,
+                                  0, bpp, NULL);
+
+       if (error) {
+               ASSERT(*bpp == NULL);
+               return XFS_ERROR(error);
+       }
+
+       ASSERT(xfs_buf_islocked(*bpp));
+       d = (struct xfs_dqblk *)(*bpp)->b_addr;
+
+       /* Do the actual repair of dquots in this buffer */
+       for (i = 0; i < mp->m_quotainfo->qi_dqperchunk; i++) {
+               ddq = &d[i].dd_diskdq;
+               error = xfs_qm_dqcheck(mp, ddq, firstid + i,
+                                      dqp->dq_flags & XFS_DQ_ALLTYPES,
+                                      XFS_QMOPT_DQREPAIR, "xfs_qm_dqrepair");
+               if (error) {
+                       /* repair failed, we're screwed */
+                       xfs_trans_brelse(tp, *bpp);
+                       return XFS_ERROR(EIO);
+               }
+       }
+
+       return 0;
+}
+
 /*
  * Maps a dquot to the buffer containing its on-disk version.
  * This returns a ptr to the buffer containing the on-disk dquot
@@ -378,7 +461,6 @@ xfs_qm_dqtobp(
        xfs_buf_t       *bp;
        xfs_inode_t     *quotip = XFS_DQ_TO_QIP(dqp);
        xfs_mount_t     *mp = dqp->q_mount;
-       xfs_disk_dquot_t *ddq;
        xfs_dqid_t      id = be32_to_cpu(dqp->q_core.d_id);
        xfs_trans_t     *tp = (tpp ? *tpp : NULL);
 
@@ -439,33 +521,24 @@ xfs_qm_dqtobp(
                error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
                                           dqp->q_blkno,
                                           mp->m_quotainfo->qi_dqchunklen,
-                                          0, &bp, NULL);
-               if (error || !bp)
-                       return XFS_ERROR(error);
-       }
+                                          0, &bp, xfs_dquot_read_verify);
 
-       ASSERT(xfs_buf_islocked(bp));
-
-       /*
-        * calculate the location of the dquot inside the buffer.
-        */
-       ddq = bp->b_addr + dqp->q_bufoffset;
+               if (error == EFSCORRUPTED && (flags & XFS_QMOPT_DQREPAIR)) {
+                       xfs_dqid_t firstid = (xfs_dqid_t)map.br_startoff *
+                                               mp->m_quotainfo->qi_dqperchunk;
+                       ASSERT(bp == NULL);
+                       error = xfs_qm_dqrepair(mp, tp, dqp, firstid, &bp);
+               }
 
-       /*
-        * A simple sanity check in case we got a corrupted dquot...
-        */
-       error = xfs_qm_dqcheck(mp, ddq, id, dqp->dq_flags & XFS_DQ_ALLTYPES,
-                          flags & (XFS_QMOPT_DQREPAIR|XFS_QMOPT_DOWARN),
-                          "dqtobp");
-       if (error) {
-               if (!(flags & XFS_QMOPT_DQREPAIR)) {
-                       xfs_trans_brelse(tp, bp);
-                       return XFS_ERROR(EIO);
+               if (error) {
+                       ASSERT(bp == NULL);
+                       return XFS_ERROR(error);
                }
        }
 
+       ASSERT(xfs_buf_islocked(bp));
        *O_bpp = bp;
-       *O_ddpp = ddq;
+       *O_ddpp = bp->b_addr + dqp->q_bufoffset;
 
        return (0);
 }
This page took 0.030255 seconds and 5 git commands to generate.