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-xfsx @ Mon Jul 31 20:08:34 PDT 2023 Lines: 83 110 75.5 %
Date: 2023-07-31 20:08:34 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       47112 : xchk_quota_to_dqtype(
      25             :         struct xfs_scrub        *sc)
      26             : {
      27       47112 :         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      241913 : xchk_setup_quota(
      42             :         struct xfs_scrub        *sc)
      43             : {
      44      241913 :         xfs_dqtype_t            dqtype;
      45      241913 :         int                     error;
      46             : 
      47      241913 :         if (!XFS_IS_QUOTA_ON(sc->mp))
      48             :                 return -ENOENT;
      49             : 
      50      238523 :         dqtype = xchk_quota_to_dqtype(sc);
      51      238525 :         if (dqtype == 0)
      52             :                 return -EINVAL;
      53             : 
      54      238525 :         if (!xfs_this_quota_on(sc->mp, dqtype))
      55             :                 return -ENOENT;
      56             : 
      57      238098 :         if (xchk_need_intent_drain(sc))
      58           0 :                 xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN);
      59             : 
      60      238098 :         error = xchk_setup_fs(sc);
      61      238090 :         if (error)
      62             :                 return error;
      63             : 
      64      237908 :         error = xchk_install_live_inode(sc, xfs_quota_inode(sc->mp, dqtype));
      65      237945 :         if (error)
      66             :                 return error;
      67             : 
      68      237945 :         xchk_ilock(sc, XFS_ILOCK_EXCL);
      69      237945 :         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      237941 : xchk_quota_item(
      82             :         struct xfs_dquot        *dq,
      83             :         xfs_dqtype_t            dqtype,
      84             :         void                    *priv)
      85             : {
      86      237941 :         struct xchk_quota_info  *sqi = priv;
      87      237941 :         struct xfs_scrub        *sc = sqi->sc;
      88      237941 :         struct xfs_mount        *mp = sc->mp;
      89      237941 :         struct xfs_quotainfo    *qi = mp->m_quotainfo;
      90      237941 :         xfs_fileoff_t           offset;
      91      237941 :         xfs_ino_t               fs_icount;
      92      237941 :         int                     error = 0;
      93             : 
      94      237941 :         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      237942 :         offset = dq->q_id / qi->qi_dqperchunk;
     102      237942 :         if (dq->q_id && dq->q_id <= sqi->last_id)
     103           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
     104             : 
     105      237942 :         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      237942 :         if (dq->q_blk.hardlimit > mp->m_sb.sb_dblocks)
     116           0 :                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
     117      237942 :         if (dq->q_blk.softlimit > dq->q_blk.hardlimit)
     118           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
     119             : 
     120      237942 :         if (dq->q_ino.hardlimit > M_IGEO(mp)->maxicount)
     121           0 :                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
     122      237942 :         if (dq->q_ino.softlimit > dq->q_ino.hardlimit)
     123           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
     124             : 
     125      237942 :         if (dq->q_rtb.hardlimit > mp->m_sb.sb_rblocks)
     126           0 :                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
     127      237942 :         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      237942 :         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      237949 :         if (xfs_has_reflink(mp)) {
     139      230983 :                 if (mp->m_sb.sb_dblocks < dq->q_blk.count)
     140         177 :                         xchk_fblock_set_warning(sc, XFS_DATA_FORK,
     141             :                                         offset);
     142      230983 :                 if (mp->m_sb.sb_rblocks < dq->q_rtb.count)
     143         276 :                         xchk_fblock_set_warning(sc, XFS_DATA_FORK,
     144             :                                         offset);
     145             :         } else {
     146        6966 :                 if (mp->m_sb.sb_dblocks < dq->q_blk.count)
     147           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
     148             :                                         offset);
     149        6966 :                 if (mp->m_sb.sb_rblocks < dq->q_rtb.count)
     150           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
     151             :                                         offset);
     152             :         }
     153      237949 :         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      237949 :         if (dq->q_id == 0)
     162      237949 :                 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      237949 :         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      237910 : xchk_quota_data_fork(
     186             :         struct xfs_scrub        *sc)
     187             : {
     188      237910 :         struct xfs_bmbt_irec    irec = { 0 };
     189      237910 :         struct xfs_iext_cursor  icur;
     190      237910 :         struct xfs_quotainfo    *qi = sc->mp->m_quotainfo;
     191      237910 :         struct xfs_ifork        *ifp;
     192      237910 :         xfs_fileoff_t           max_dqid_off;
     193      237910 :         int                     error = 0;
     194             : 
     195             :         /* Invoke the fork scrubber. */
     196      237910 :         error = xchk_metadata_inode_forks(sc);
     197      237954 :         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      237952 :         max_dqid_off = ((xfs_dqid_t)-1) / qi->qi_dqperchunk;
     202      237952 :         ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK);
     203   305284802 :         for_each_xfs_iext(ifp, &icur, &irec) {
     204   305026474 :                 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   305046850 :                 if (!xfs_bmap_is_written_extent(&irec) ||
     212   305046850 :                     irec.br_startoff > max_dqid_off ||
     213   305046850 :                     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      237952 :         return error;
     221             : }
     222             : 
     223             : /* Scrub all of a quota type's items. */
     224             : int
     225      237927 : xchk_quota(
     226             :         struct xfs_scrub        *sc)
     227             : {
     228      237927 :         struct xchk_quota_info  sqi;
     229      237927 :         struct xfs_mount        *mp = sc->mp;
     230      237927 :         struct xfs_quotainfo    *qi = mp->m_quotainfo;
     231      237927 :         xfs_dqtype_t            dqtype;
     232      237927 :         int                     error = 0;
     233             : 
     234      237927 :         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      237927 :         error = xchk_quota_data_fork(sc);
     241      237954 :         if (error)
     242           4 :                 goto out;
     243      237950 :         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      237950 :         xchk_iunlock(sc, sc->ilock_flags);
     252             : 
     253             :         /* Now look for things that the quota verifiers won't complain about. */
     254      237950 :         sqi.sc = sc;
     255      237950 :         sqi.last_id = 0;
     256      237950 :         error = xfs_qm_dqiterate(mp, dqtype, xchk_quota_item, &sqi);
     257      237949 :         if (error == -ECANCELED)
     258           0 :                 error = 0;
     259      237949 :         if (!xchk_fblock_process_error(sc, XFS_DATA_FORK,
     260      237949 :                         sqi.last_id * qi->qi_dqperchunk, &error))
     261             :                 goto out;
     262             : 
     263      237937 : out:
     264      237941 :         return error;
     265             : }

Generated by: LCOV version 1.14