linux/fs/xfs/scrub/quota.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2017 Oracle.  All Rights Reserved.
   4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
   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_defer.h"
  13#include "xfs_btree.h"
  14#include "xfs_bit.h"
  15#include "xfs_log_format.h"
  16#include "xfs_trans.h"
  17#include "xfs_sb.h"
  18#include "xfs_inode.h"
  19#include "xfs_inode_fork.h"
  20#include "xfs_alloc.h"
  21#include "xfs_bmap.h"
  22#include "xfs_quota.h"
  23#include "xfs_qm.h"
  24#include "xfs_dquot.h"
  25#include "xfs_dquot_item.h"
  26#include "scrub/xfs_scrub.h"
  27#include "scrub/scrub.h"
  28#include "scrub/common.h"
  29#include "scrub/trace.h"
  30
  31/* Convert a scrub type code to a DQ flag, or return 0 if error. */
  32static inline uint
  33xchk_quota_to_dqtype(
  34        struct xfs_scrub        *sc)
  35{
  36        switch (sc->sm->sm_type) {
  37        case XFS_SCRUB_TYPE_UQUOTA:
  38                return XFS_DQ_USER;
  39        case XFS_SCRUB_TYPE_GQUOTA:
  40                return XFS_DQ_GROUP;
  41        case XFS_SCRUB_TYPE_PQUOTA:
  42                return XFS_DQ_PROJ;
  43        default:
  44                return 0;
  45        }
  46}
  47
  48/* Set us up to scrub a quota. */
  49int
  50xchk_setup_quota(
  51        struct xfs_scrub        *sc,
  52        struct xfs_inode        *ip)
  53{
  54        uint                    dqtype;
  55        int                     error;
  56
  57        if (!XFS_IS_QUOTA_RUNNING(sc->mp) || !XFS_IS_QUOTA_ON(sc->mp))
  58                return -ENOENT;
  59
  60        dqtype = xchk_quota_to_dqtype(sc);
  61        if (dqtype == 0)
  62                return -EINVAL;
  63        sc->has_quotaofflock = true;
  64        mutex_lock(&sc->mp->m_quotainfo->qi_quotaofflock);
  65        if (!xfs_this_quota_on(sc->mp, dqtype))
  66                return -ENOENT;
  67        error = xchk_setup_fs(sc, ip);
  68        if (error)
  69                return error;
  70        sc->ip = xfs_quota_inode(sc->mp, dqtype);
  71        xfs_ilock(sc->ip, XFS_ILOCK_EXCL);
  72        sc->ilock_flags = XFS_ILOCK_EXCL;
  73        return 0;
  74}
  75
  76/* Quotas. */
  77
  78struct xchk_quota_info {
  79        struct xfs_scrub        *sc;
  80        xfs_dqid_t              last_id;
  81};
  82
  83/* Scrub the fields in an individual quota item. */
  84STATIC int
  85xchk_quota_item(
  86        struct xfs_dquot        *dq,
  87        uint                    dqtype,
  88        void                    *priv)
  89{
  90        struct xchk_quota_info  *sqi = priv;
  91        struct xfs_scrub        *sc = sqi->sc;
  92        struct xfs_mount        *mp = sc->mp;
  93        struct xfs_disk_dquot   *d = &dq->q_core;
  94        struct xfs_quotainfo    *qi = mp->m_quotainfo;
  95        xfs_fileoff_t           offset;
  96        unsigned long long      bsoft;
  97        unsigned long long      isoft;
  98        unsigned long long      rsoft;
  99        unsigned long long      bhard;
 100        unsigned long long      ihard;
 101        unsigned long long      rhard;
 102        unsigned long long      bcount;
 103        unsigned long long      icount;
 104        unsigned long long      rcount;
 105        xfs_ino_t               fs_icount;
 106        xfs_dqid_t              id = be32_to_cpu(d->d_id);
 107
 108        /*
 109         * Except for the root dquot, the actual dquot we got must either have
 110         * the same or higher id as we saw before.
 111         */
 112        offset = id / qi->qi_dqperchunk;
 113        if (id && id <= sqi->last_id)
 114                xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
 115
 116        sqi->last_id = id;
 117
 118        /* Did we get the dquot type we wanted? */
 119        if (dqtype != (d->d_flags & XFS_DQ_ALLTYPES))
 120                xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
 121
 122        if (d->d_pad0 != cpu_to_be32(0) || d->d_pad != cpu_to_be16(0))
 123                xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
 124
 125        /* Check the limits. */
 126        bhard = be64_to_cpu(d->d_blk_hardlimit);
 127        ihard = be64_to_cpu(d->d_ino_hardlimit);
 128        rhard = be64_to_cpu(d->d_rtb_hardlimit);
 129
 130        bsoft = be64_to_cpu(d->d_blk_softlimit);
 131        isoft = be64_to_cpu(d->d_ino_softlimit);
 132        rsoft = be64_to_cpu(d->d_rtb_softlimit);
 133
 134        /*
 135         * Warn if the hard limits are larger than the fs.
 136         * Administrators can do this, though in production this seems
 137         * suspect, which is why we flag it for review.
 138         *
 139         * Complain about corruption if the soft limit is greater than
 140         * the hard limit.
 141         */
 142        if (bhard > mp->m_sb.sb_dblocks)
 143                xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
 144        if (bsoft > bhard)
 145                xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
 146
 147        if (ihard > mp->m_maxicount)
 148                xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
 149        if (isoft > ihard)
 150                xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
 151
 152        if (rhard > mp->m_sb.sb_rblocks)
 153                xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
 154        if (rsoft > rhard)
 155                xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
 156
 157        /* Check the resource counts. */
 158        bcount = be64_to_cpu(d->d_bcount);
 159        icount = be64_to_cpu(d->d_icount);
 160        rcount = be64_to_cpu(d->d_rtbcount);
 161        fs_icount = percpu_counter_sum(&mp->m_icount);
 162
 163        /*
 164         * Check that usage doesn't exceed physical limits.  However, on
 165         * a reflink filesystem we're allowed to exceed physical space
 166         * if there are no quota limits.
 167         */
 168        if (xfs_sb_version_hasreflink(&mp->m_sb)) {
 169                if (mp->m_sb.sb_dblocks < bcount)
 170                        xchk_fblock_set_warning(sc, XFS_DATA_FORK,
 171                                        offset);
 172        } else {
 173                if (mp->m_sb.sb_dblocks < bcount)
 174                        xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
 175                                        offset);
 176        }
 177        if (icount > fs_icount || rcount > mp->m_sb.sb_rblocks)
 178                xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
 179
 180        /*
 181         * We can violate the hard limits if the admin suddenly sets a
 182         * lower limit than the actual usage.  However, we flag it for
 183         * admin review.
 184         */
 185        if (id != 0 && bhard != 0 && bcount > bhard)
 186                xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
 187        if (id != 0 && ihard != 0 && icount > ihard)
 188                xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
 189        if (id != 0 && rhard != 0 && rcount > rhard)
 190                xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
 191
 192        return 0;
 193}
 194
 195/* Check the quota's data fork. */
 196STATIC int
 197xchk_quota_data_fork(
 198        struct xfs_scrub        *sc)
 199{
 200        struct xfs_bmbt_irec    irec = { 0 };
 201        struct xfs_iext_cursor  icur;
 202        struct xfs_quotainfo    *qi = sc->mp->m_quotainfo;
 203        struct xfs_ifork        *ifp;
 204        xfs_fileoff_t           max_dqid_off;
 205        int                     error = 0;
 206
 207        /* Invoke the fork scrubber. */
 208        error = xchk_metadata_inode_forks(sc);
 209        if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
 210                return error;
 211
 212        /* Check for data fork problems that apply only to quota files. */
 213        max_dqid_off = ((xfs_dqid_t)-1) / qi->qi_dqperchunk;
 214        ifp = XFS_IFORK_PTR(sc->ip, XFS_DATA_FORK);
 215        for_each_xfs_iext(ifp, &icur, &irec) {
 216                if (xchk_should_terminate(sc, &error))
 217                        break;
 218                /*
 219                 * delalloc extents or blocks mapped above the highest
 220                 * quota id shouldn't happen.
 221                 */
 222                if (isnullstartblock(irec.br_startblock) ||
 223                    irec.br_startoff > max_dqid_off ||
 224                    irec.br_startoff + irec.br_blockcount - 1 > max_dqid_off) {
 225                        xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
 226                                        irec.br_startoff);
 227                        break;
 228                }
 229        }
 230
 231        return error;
 232}
 233
 234/* Scrub all of a quota type's items. */
 235int
 236xchk_quota(
 237        struct xfs_scrub        *sc)
 238{
 239        struct xchk_quota_info  sqi;
 240        struct xfs_mount        *mp = sc->mp;
 241        struct xfs_quotainfo    *qi = mp->m_quotainfo;
 242        uint                    dqtype;
 243        int                     error = 0;
 244
 245        dqtype = xchk_quota_to_dqtype(sc);
 246
 247        /* Look for problem extents. */
 248        error = xchk_quota_data_fork(sc);
 249        if (error)
 250                goto out;
 251        if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 252                goto out;
 253
 254        /*
 255         * Check all the quota items.  Now that we've checked the quota inode
 256         * data fork we have to drop ILOCK_EXCL to use the regular dquot
 257         * functions.
 258         */
 259        xfs_iunlock(sc->ip, sc->ilock_flags);
 260        sc->ilock_flags = 0;
 261        sqi.sc = sc;
 262        sqi.last_id = 0;
 263        error = xfs_qm_dqiterate(mp, dqtype, xchk_quota_item, &sqi);
 264        sc->ilock_flags = XFS_ILOCK_EXCL;
 265        xfs_ilock(sc->ip, sc->ilock_flags);
 266        if (!xchk_fblock_process_error(sc, XFS_DATA_FORK,
 267                        sqi.last_id * qi->qi_dqperchunk, &error))
 268                goto out;
 269
 270out:
 271        return error;
 272}
 273