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

Generated by: LCOV version 1.14