LCOV - code coverage report
Current view: top level - fs/xfs/scrub - quota.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc4-xfsa @ Mon Jul 31 20:08:27 PDT 2023 Lines: 81 110 73.6 %
Date: 2023-07-31 20:08:27 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       30427 : xchk_quota_to_dqtype(
      25             :         struct xfs_scrub        *sc)
      26             : {
      27       30427 :         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      141108 : xchk_setup_quota(
      42             :         struct xfs_scrub        *sc)
      43             : {
      44      141108 :         xfs_dqtype_t            dqtype;
      45      141108 :         int                     error;
      46             : 
      47      141108 :         if (!XFS_IS_QUOTA_ON(sc->mp))
      48             :                 return -ENOENT;
      49             : 
      50      141060 :         dqtype = xchk_quota_to_dqtype(sc);
      51      141055 :         if (dqtype == 0)
      52             :                 return -EINVAL;
      53             : 
      54      141055 :         if (!xfs_this_quota_on(sc->mp, dqtype))
      55             :                 return -ENOENT;
      56             : 
      57      140977 :         if (xchk_need_intent_drain(sc))
      58           0 :                 xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN);
      59             : 
      60      140977 :         error = xchk_setup_fs(sc);
      61      140974 :         if (error)
      62             :                 return error;
      63             : 
      64      140974 :         error = xchk_install_live_inode(sc, xfs_quota_inode(sc->mp, dqtype));
      65      140974 :         if (error)
      66             :                 return error;
      67             : 
      68      140976 :         xchk_ilock(sc, XFS_ILOCK_EXCL);
      69      140976 :         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      140985 : xchk_quota_item(
      82             :         struct xfs_dquot        *dq,
      83             :         xfs_dqtype_t            dqtype,
      84             :         void                    *priv)
      85             : {
      86      140985 :         struct xchk_quota_info  *sqi = priv;
      87      140985 :         struct xfs_scrub        *sc = sqi->sc;
      88      140985 :         struct xfs_mount        *mp = sc->mp;
      89      140985 :         struct xfs_quotainfo    *qi = mp->m_quotainfo;
      90      140985 :         xfs_fileoff_t           offset;
      91      140985 :         xfs_ino_t               fs_icount;
      92      140985 :         int                     error = 0;
      93             : 
      94      140985 :         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      140985 :         offset = dq->q_id / qi->qi_dqperchunk;
     102      140985 :         if (dq->q_id && dq->q_id <= sqi->last_id)
     103           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
     104             : 
     105      140985 :         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      140985 :         if (dq->q_blk.hardlimit > mp->m_sb.sb_dblocks)
     116           0 :                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
     117      140985 :         if (dq->q_blk.softlimit > dq->q_blk.hardlimit)
     118           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
     119             : 
     120      140985 :         if (dq->q_ino.hardlimit > M_IGEO(mp)->maxicount)
     121           0 :                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
     122      140985 :         if (dq->q_ino.softlimit > dq->q_ino.hardlimit)
     123           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
     124             : 
     125      140985 :         if (dq->q_rtb.hardlimit > mp->m_sb.sb_rblocks)
     126           0 :                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
     127      140985 :         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      140985 :         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      140985 :         if (xfs_has_reflink(mp)) {
     139      140967 :                 if (mp->m_sb.sb_dblocks < dq->q_blk.count)
     140          27 :                         xchk_fblock_set_warning(sc, XFS_DATA_FORK,
     141             :                                         offset);
     142      140967 :                 if (mp->m_sb.sb_rblocks < dq->q_rtb.count)
     143           0 :                         xchk_fblock_set_warning(sc, XFS_DATA_FORK,
     144             :                                         offset);
     145             :         } else {
     146          18 :                 if (mp->m_sb.sb_dblocks < dq->q_blk.count)
     147           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
     148             :                                         offset);
     149          18 :                 if (mp->m_sb.sb_rblocks < dq->q_rtb.count)
     150           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
     151             :                                         offset);
     152             :         }
     153      140985 :         if (dq->q_ino.count > fs_icount)
     154           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
     155             : 
     156             :         /*
     157             :          * We can violate the hard limits if the admin suddenly sets a
     158             :          * lower limit than the actual usage.  However, we flag it for
     159             :          * admin review.
     160             :          */
     161      140985 :         if (dq->q_id == 0)
     162      140985 :                 goto out;
     163             : 
     164           0 :         if (dq->q_blk.hardlimit != 0 &&
     165           0 :             dq->q_blk.count > dq->q_blk.hardlimit)
     166           0 :                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
     167             : 
     168           0 :         if (dq->q_ino.hardlimit != 0 &&
     169           0 :             dq->q_ino.count > dq->q_ino.hardlimit)
     170           0 :                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
     171             : 
     172           0 :         if (dq->q_rtb.hardlimit != 0 &&
     173           0 :             dq->q_rtb.count > dq->q_rtb.hardlimit)
     174           0 :                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
     175             : 
     176           0 : out:
     177      140985 :         if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     178           0 :                 return -ECANCELED;
     179             : 
     180             :         return 0;
     181             : }
     182             : 
     183             : /* Check the quota's data fork. */
     184             : STATIC int
     185      140982 : xchk_quota_data_fork(
     186             :         struct xfs_scrub        *sc)
     187             : {
     188      140982 :         struct xfs_bmbt_irec    irec = { 0 };
     189      140982 :         struct xfs_iext_cursor  icur;
     190      140982 :         struct xfs_quotainfo    *qi = sc->mp->m_quotainfo;
     191      140982 :         struct xfs_ifork        *ifp;
     192      140982 :         xfs_fileoff_t           max_dqid_off;
     193      140982 :         int                     error = 0;
     194             : 
     195             :         /* Invoke the fork scrubber. */
     196      140982 :         error = xchk_metadata_inode_forks(sc);
     197      140985 :         if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
     198             :                 return error;
     199             : 
     200             :         /* Check for data fork problems that apply only to quota files. */
     201      140984 :         max_dqid_off = ((xfs_dqid_t)-1) / qi->qi_dqperchunk;
     202      140984 :         ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK);
     203   320108197 :         for_each_xfs_iext(ifp, &icur, &irec) {
     204   319957615 :                 if (xchk_should_terminate(sc, &error))
     205             :                         break;
     206             : 
     207             :                 /*
     208             :                  * delalloc/unwritten extents or blocks mapped above the highest
     209             :                  * quota id shouldn't happen.
     210             :                  */
     211   319967213 :                 if (!xfs_bmap_is_written_extent(&irec) ||
     212   319967213 :                     irec.br_startoff > max_dqid_off ||
     213   319967213 :                     irec.br_startoff + irec.br_blockcount - 1 > max_dqid_off) {
     214           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
     215             :                                         irec.br_startoff);
     216           0 :                         break;
     217             :                 }
     218             :         }
     219             : 
     220      140985 :         return error;
     221             : }
     222             : 
     223             : /* Scrub all of a quota type's items. */
     224             : int
     225      140978 : xchk_quota(
     226             :         struct xfs_scrub        *sc)
     227             : {
     228      140978 :         struct xchk_quota_info  sqi;
     229      140978 :         struct xfs_mount        *mp = sc->mp;
     230      140978 :         struct xfs_quotainfo    *qi = mp->m_quotainfo;
     231      140978 :         xfs_dqtype_t            dqtype;
     232      140978 :         int                     error = 0;
     233             : 
     234      140978 :         dqtype = xchk_quota_to_dqtype(sc);
     235             : 
     236             :         /*
     237             :          * Look for problem extents.  Leave the quota inode ILOCKd if we find
     238             :          * any.
     239             :          */
     240      140978 :         error = xchk_quota_data_fork(sc);
     241      140985 :         if (error)
     242           0 :                 goto out;
     243      140985 :         if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     244           0 :                 goto out;
     245             : 
     246             :         /*
     247             :          * Check all the quota items.  Now that we've checked the quota inode
     248             :          * data fork we have to drop ILOCK_EXCL to use the regular dquot
     249             :          * functions.
     250             :          */
     251      140985 :         xchk_iunlock(sc, sc->ilock_flags);
     252             : 
     253             :         /* Now look for things that the quota verifiers won't complain about. */
     254      140985 :         sqi.sc = sc;
     255      140985 :         sqi.last_id = 0;
     256      140985 :         error = xfs_qm_dqiterate(mp, dqtype, xchk_quota_item, &sqi);
     257      140985 :         if (error == -ECANCELED)
     258           0 :                 error = 0;
     259      140985 :         if (!xchk_fblock_process_error(sc, XFS_DATA_FORK,
     260      140985 :                         sqi.last_id * qi->qi_dqperchunk, &error))
     261             :                 goto out;
     262             : 
     263      140985 : out:
     264      140985 :         return error;
     265             : }

Generated by: LCOV version 1.14