LCOV - code coverage report
Current view: top level - fs/xfs/scrub - quota.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc3-djwx @ Mon Jul 31 20:08:22 PDT 2023 Lines: 81 108 75.0 %
Date: 2023-07-31 20:08:22 Functions: 4 4 100.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-or-later
       2             : /*
       3             :  * Copyright (C) 2017-2023 Oracle.  All Rights Reserved.
       4             :  * Author: Darrick J. Wong <djwong@kernel.org>
       5             :  */
       6             : #include "xfs.h"
       7             : #include "xfs_fs.h"
       8             : #include "xfs_shared.h"
       9             : #include "xfs_format.h"
      10             : #include "xfs_trans_resv.h"
      11             : #include "xfs_mount.h"
      12             : #include "xfs_log_format.h"
      13             : #include "xfs_trans.h"
      14             : #include "xfs_inode.h"
      15             : #include "xfs_quota.h"
      16             : #include "xfs_qm.h"
      17             : #include "xfs_bmap.h"
      18             : #include "scrub/scrub.h"
      19             : #include "scrub/common.h"
      20             : 
      21             : /* Convert a scrub type code to a DQ flag, or return 0 if error. */
      22             : static inline xfs_dqtype_t
      23             : xchk_quota_to_dqtype(
      24             :         struct xfs_scrub        *sc)
      25             : {
      26      698659 :         switch (sc->sm->sm_type) {
      27             :         case XFS_SCRUB_TYPE_UQUOTA:
      28             :                 return XFS_DQTYPE_USER;
      29             :         case XFS_SCRUB_TYPE_GQUOTA:
      30             :                 return XFS_DQTYPE_GROUP;
      31             :         case XFS_SCRUB_TYPE_PQUOTA:
      32             :                 return XFS_DQTYPE_PROJ;
      33             :         default:
      34             :                 return 0;
      35             :         }
      36             : }
      37             : 
      38             : /* Set us up to scrub a quota. */
      39             : int
      40      500268 : xchk_setup_quota(
      41             :         struct xfs_scrub        *sc)
      42             : {
      43      500268 :         xfs_dqtype_t            dqtype;
      44      500268 :         int                     error;
      45             : 
      46      500268 :         if (!XFS_IS_QUOTA_ON(sc->mp))
      47             :                 return -ENOENT;
      48             : 
      49      349503 :         dqtype = xchk_quota_to_dqtype(sc);
      50      349503 :         if (dqtype == 0)
      51             :                 return -EINVAL;
      52             : 
      53      349503 :         if (!xfs_this_quota_on(sc->mp, dqtype))
      54             :                 return -ENOENT;
      55             : 
      56      349300 :         if (xchk_need_intent_drain(sc))
      57           0 :                 xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN);
      58             : 
      59      349300 :         error = xchk_setup_fs(sc);
      60      349300 :         if (error)
      61             :                 return error;
      62      349156 :         sc->ip = xfs_quota_inode(sc->mp, dqtype);
      63      349156 :         xfs_ilock(sc->ip, XFS_ILOCK_EXCL);
      64      349156 :         sc->ilock_flags = XFS_ILOCK_EXCL;
      65      349156 :         return 0;
      66             : }
      67             : 
      68             : /* Quotas. */
      69             : 
      70             : struct xchk_quota_info {
      71             :         struct xfs_scrub        *sc;
      72             :         xfs_dqid_t              last_id;
      73             : };
      74             : 
      75             : /* Scrub the fields in an individual quota item. */
      76             : STATIC int
      77      349155 : xchk_quota_item(
      78             :         struct xfs_dquot        *dq,
      79             :         xfs_dqtype_t            dqtype,
      80             :         void                    *priv)
      81             : {
      82      349155 :         struct xchk_quota_info  *sqi = priv;
      83      349155 :         struct xfs_scrub        *sc = sqi->sc;
      84      349155 :         struct xfs_mount        *mp = sc->mp;
      85      349155 :         struct xfs_quotainfo    *qi = mp->m_quotainfo;
      86      349155 :         xfs_fileoff_t           offset;
      87      349155 :         xfs_ino_t               fs_icount;
      88      349155 :         int                     error = 0;
      89             : 
      90      349155 :         if (xchk_should_terminate(sc, &error))
      91           0 :                 return error;
      92             : 
      93             :         /*
      94             :          * Except for the root dquot, the actual dquot we got must either have
      95             :          * the same or higher id as we saw before.
      96             :          */
      97      349155 :         offset = dq->q_id / qi->qi_dqperchunk;
      98      349155 :         if (dq->q_id && dq->q_id <= sqi->last_id)
      99           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
     100             : 
     101      349155 :         sqi->last_id = dq->q_id;
     102             : 
     103             :         /*
     104             :          * Warn if the hard limits are larger than the fs.
     105             :          * Administrators can do this, though in production this seems
     106             :          * suspect, which is why we flag it for review.
     107             :          *
     108             :          * Complain about corruption if the soft limit is greater than
     109             :          * the hard limit.
     110             :          */
     111      349155 :         if (dq->q_blk.hardlimit > mp->m_sb.sb_dblocks)
     112           0 :                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
     113      349155 :         if (dq->q_blk.softlimit > dq->q_blk.hardlimit)
     114           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
     115             : 
     116      349155 :         if (dq->q_ino.hardlimit > M_IGEO(mp)->maxicount)
     117           0 :                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
     118      349155 :         if (dq->q_ino.softlimit > dq->q_ino.hardlimit)
     119           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
     120             : 
     121      349155 :         if (dq->q_rtb.hardlimit > mp->m_sb.sb_rblocks)
     122           0 :                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
     123      349155 :         if (dq->q_rtb.softlimit > dq->q_rtb.hardlimit)
     124           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
     125             : 
     126             :         /* Check the resource counts. */
     127      349155 :         fs_icount = percpu_counter_sum(&mp->m_icount);
     128             : 
     129             :         /*
     130             :          * Check that usage doesn't exceed physical limits.  However, on
     131             :          * a reflink filesystem we're allowed to exceed physical space
     132             :          * if there are no quota limits.
     133             :          */
     134      349155 :         if (xfs_has_reflink(mp)) {
     135      349155 :                 if (mp->m_sb.sb_dblocks < dq->q_blk.count)
     136         147 :                         xchk_fblock_set_warning(sc, XFS_DATA_FORK,
     137             :                                         offset);
     138             :         } else {
     139           0 :                 if (mp->m_sb.sb_dblocks < dq->q_blk.count)
     140           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
     141             :                                         offset);
     142             :         }
     143      349155 :         if (dq->q_ino.count > fs_icount || dq->q_rtb.count > mp->m_sb.sb_rblocks)
     144           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
     145             : 
     146             :         /*
     147             :          * We can violate the hard limits if the admin suddenly sets a
     148             :          * lower limit than the actual usage.  However, we flag it for
     149             :          * admin review.
     150             :          */
     151      349155 :         if (dq->q_id == 0)
     152      349155 :                 goto out;
     153             : 
     154           0 :         if (dq->q_blk.hardlimit != 0 &&
     155           0 :             dq->q_blk.count > dq->q_blk.hardlimit)
     156           0 :                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
     157             : 
     158           0 :         if (dq->q_ino.hardlimit != 0 &&
     159           0 :             dq->q_ino.count > dq->q_ino.hardlimit)
     160           0 :                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
     161             : 
     162           0 :         if (dq->q_rtb.hardlimit != 0 &&
     163           0 :             dq->q_rtb.count > dq->q_rtb.hardlimit)
     164           0 :                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
     165             : 
     166           0 : out:
     167      349155 :         if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     168           0 :                 return -ECANCELED;
     169             : 
     170             :         return 0;
     171             : }
     172             : 
     173             : /* Check the quota's data fork. */
     174             : STATIC int
     175      349156 : xchk_quota_data_fork(
     176             :         struct xfs_scrub        *sc)
     177             : {
     178      349156 :         struct xfs_bmbt_irec    irec = { 0 };
     179      349156 :         struct xfs_iext_cursor  icur;
     180      349156 :         struct xfs_quotainfo    *qi = sc->mp->m_quotainfo;
     181      349156 :         struct xfs_ifork        *ifp;
     182      349156 :         xfs_fileoff_t           max_dqid_off;
     183      349156 :         int                     error = 0;
     184             : 
     185             :         /* Invoke the fork scrubber. */
     186      349156 :         error = xchk_metadata_inode_forks(sc);
     187      349156 :         if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
     188             :                 return error;
     189             : 
     190             :         /* Check for data fork problems that apply only to quota files. */
     191      349155 :         max_dqid_off = ((xfs_dqid_t)-1) / qi->qi_dqperchunk;
     192      349155 :         ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK);
     193   102053768 :         for_each_xfs_iext(ifp, &icur, &irec) {
     194   101704613 :                 if (xchk_should_terminate(sc, &error))
     195             :                         break;
     196             : 
     197             :                 /*
     198             :                  * delalloc/unwritten extents or blocks mapped above the highest
     199             :                  * quota id shouldn't happen.
     200             :                  */
     201   101704613 :                 if (!xfs_bmap_is_written_extent(&irec) ||
     202   101704613 :                     irec.br_startoff > max_dqid_off ||
     203   101704613 :                     irec.br_startoff + irec.br_blockcount - 1 > max_dqid_off) {
     204           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
     205             :                                         irec.br_startoff);
     206           0 :                         break;
     207             :                 }
     208             :         }
     209             : 
     210      349155 :         return error;
     211             : }
     212             : 
     213             : /* Scrub all of a quota type's items. */
     214             : int
     215      349156 : xchk_quota(
     216             :         struct xfs_scrub        *sc)
     217             : {
     218      349156 :         struct xchk_quota_info  sqi;
     219      349156 :         struct xfs_mount        *mp = sc->mp;
     220      349156 :         struct xfs_quotainfo    *qi = mp->m_quotainfo;
     221      349156 :         xfs_dqtype_t            dqtype;
     222      349156 :         int                     error = 0;
     223             : 
     224      349156 :         dqtype = xchk_quota_to_dqtype(sc);
     225             : 
     226             :         /* Look for problem extents. */
     227      349156 :         error = xchk_quota_data_fork(sc);
     228      349156 :         if (error)
     229           1 :                 goto out;
     230      349155 :         if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     231           0 :                 goto out;
     232             : 
     233             :         /*
     234             :          * Check all the quota items.  Now that we've checked the quota inode
     235             :          * data fork we have to drop ILOCK_EXCL to use the regular dquot
     236             :          * functions.
     237             :          */
     238      349155 :         xfs_iunlock(sc->ip, sc->ilock_flags);
     239      349155 :         sc->ilock_flags = 0;
     240      349155 :         sqi.sc = sc;
     241      349155 :         sqi.last_id = 0;
     242      349155 :         error = xfs_qm_dqiterate(mp, dqtype, xchk_quota_item, &sqi);
     243      349155 :         sc->ilock_flags = XFS_ILOCK_EXCL;
     244      349155 :         xfs_ilock(sc->ip, sc->ilock_flags);
     245      349155 :         if (error == -ECANCELED)
     246           0 :                 error = 0;
     247      349155 :         if (!xchk_fblock_process_error(sc, XFS_DATA_FORK,
     248      349155 :                         sqi.last_id * qi->qi_dqperchunk, &error))
     249             :                 goto out;
     250             : 
     251      349155 : out:
     252      349156 :         return error;
     253             : }

Generated by: LCOV version 1.14