xfs: convert to SPDX license tags
[linux-2.6-block.git] / fs / xfs / scrub / quota.c
index 6ba465e6c885d9b06142e2a360b7689f74070bb6..6ff906aa0a3b76cae5a052657b156449ac4b6d4c 100644 (file)
@@ -1,21 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (C) 2017 Oracle.  All Rights Reserved.
- *
  * Author: Darrick J. Wong <darrick.wong@oracle.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 #include "xfs.h"
 #include "xfs_fs.h"
@@ -66,25 +52,43 @@ xfs_scrub_setup_quota(
        struct xfs_inode                *ip)
 {
        uint                            dqtype;
+       int                             error;
+
+       if (!XFS_IS_QUOTA_RUNNING(sc->mp) || !XFS_IS_QUOTA_ON(sc->mp))
+               return -ENOENT;
 
        dqtype = xfs_scrub_quota_to_dqtype(sc);
        if (dqtype == 0)
                return -EINVAL;
+       sc->has_quotaofflock = true;
+       mutex_lock(&sc->mp->m_quotainfo->qi_quotaofflock);
        if (!xfs_this_quota_on(sc->mp, dqtype))
                return -ENOENT;
+       error = xfs_scrub_setup_fs(sc, ip);
+       if (error)
+               return error;
+       sc->ip = xfs_quota_inode(sc->mp, dqtype);
+       xfs_ilock(sc->ip, XFS_ILOCK_EXCL);
+       sc->ilock_flags = XFS_ILOCK_EXCL;
        return 0;
 }
 
 /* Quotas. */
 
+struct xfs_scrub_quota_info {
+       struct xfs_scrub_context        *sc;
+       xfs_dqid_t                      last_id;
+};
+
 /* Scrub the fields in an individual quota item. */
-STATIC void
+STATIC int
 xfs_scrub_quota_item(
-       struct xfs_scrub_context        *sc,
-       uint                            dqtype,
        struct xfs_dquot                *dq,
-       xfs_dqid_t                      id)
+       uint                            dqtype,
+       void                            *priv)
 {
+       struct xfs_scrub_quota_info     *sqi = priv;
+       struct xfs_scrub_context        *sc = sqi->sc;
        struct xfs_mount                *mp = sc->mp;
        struct xfs_disk_dquot           *d = &dq->q_core;
        struct xfs_quotainfo            *qi = mp->m_quotainfo;
@@ -99,17 +103,18 @@ xfs_scrub_quota_item(
        unsigned long long              icount;
        unsigned long long              rcount;
        xfs_ino_t                       fs_icount;
-
-       offset = id / qi->qi_dqperchunk;
+       xfs_dqid_t                      id = be32_to_cpu(d->d_id);
 
        /*
-        * We fed $id and DQNEXT into the xfs_qm_dqget call, which means
-        * that the actual dquot we got must either have the same id or
-        * the next higher id.
+        * Except for the root dquot, the actual dquot we got must either have
+        * the same or higher id as we saw before.
         */
-       if (id > be32_to_cpu(d->d_id))
+       offset = id / qi->qi_dqperchunk;
+       if (id && id <= sqi->last_id)
                xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
 
+       sqi->last_id = id;
+
        /* Did we get the dquot type we wanted? */
        if (dqtype != (d->d_flags & XFS_DQ_ALLTYPES))
                xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
@@ -183,115 +188,85 @@ xfs_scrub_quota_item(
                xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
        if (id != 0 && rhard != 0 && rcount > rhard)
                xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
+
+       return 0;
 }
 
-/* Scrub all of a quota type's items. */
-int
-xfs_scrub_quota(
+/* Check the quota's data fork. */
+STATIC int
+xfs_scrub_quota_data_fork(
        struct xfs_scrub_context        *sc)
 {
        struct xfs_bmbt_irec            irec = { 0 };
-       struct xfs_mount                *mp = sc->mp;
-       struct xfs_inode                *ip;
-       struct xfs_quotainfo            *qi = mp->m_quotainfo;
-       struct xfs_dquot                *dq;
+       struct xfs_iext_cursor          icur;
+       struct xfs_quotainfo            *qi = sc->mp->m_quotainfo;
+       struct xfs_ifork                *ifp;
        xfs_fileoff_t                   max_dqid_off;
-       xfs_fileoff_t                   off = 0;
-       xfs_dqid_t                      id = 0;
-       uint                            dqtype;
-       int                             nimaps;
        int                             error = 0;
 
-       if (!XFS_IS_QUOTA_RUNNING(mp) || !XFS_IS_QUOTA_ON(mp))
-               return -ENOENT;
-
-       mutex_lock(&qi->qi_quotaofflock);
-       dqtype = xfs_scrub_quota_to_dqtype(sc);
-       if (!xfs_this_quota_on(sc->mp, dqtype)) {
-               error = -ENOENT;
-               goto out_unlock_quota;
-       }
-
-       /* Attach to the quota inode and set sc->ip so that reporting works. */
-       ip = xfs_quota_inode(sc->mp, dqtype);
-       sc->ip = ip;
+       /* Invoke the fork scrubber. */
+       error = xfs_scrub_metadata_inode_forks(sc);
+       if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
+               return error;
 
-       /* Look for problem extents. */
-       xfs_ilock(ip, XFS_ILOCK_EXCL);
-       if (ip->i_d.di_flags & XFS_DIFLAG_REALTIME) {
-               xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino);
-               goto out_unlock_inode;
-       }
+       /* Check for data fork problems that apply only to quota files. */
        max_dqid_off = ((xfs_dqid_t)-1) / qi->qi_dqperchunk;
-       while (1) {
+       ifp = XFS_IFORK_PTR(sc->ip, XFS_DATA_FORK);
+       for_each_xfs_iext(ifp, &icur, &irec) {
                if (xfs_scrub_should_terminate(sc, &error))
                        break;
-
-               off = irec.br_startoff + irec.br_blockcount;
-               nimaps = 1;
-               error = xfs_bmapi_read(ip, off, -1, &irec, &nimaps,
-                               XFS_BMAPI_ENTIRE);
-               if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, off,
-                               &error))
-                       goto out_unlock_inode;
-               if (!nimaps)
-                       break;
-               if (irec.br_startblock == HOLESTARTBLOCK)
-                       continue;
-
-               /* Check the extent record doesn't point to crap. */
-               if (irec.br_startblock + irec.br_blockcount <=
-                   irec.br_startblock)
-                       xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK,
-                                       irec.br_startoff);
-               if (!xfs_verify_fsbno(mp, irec.br_startblock) ||
-                   !xfs_verify_fsbno(mp, irec.br_startblock +
-                                       irec.br_blockcount - 1))
-                       xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK,
-                                       irec.br_startoff);
-
                /*
-                * Unwritten extents or blocks mapped above the highest
+                * delalloc extents or blocks mapped above the highest
                 * quota id shouldn't happen.
                 */
                if (isnullstartblock(irec.br_startblock) ||
                    irec.br_startoff > max_dqid_off ||
-                   irec.br_startoff + irec.br_blockcount > max_dqid_off + 1)
-                       xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, off);
+                   irec.br_startoff + irec.br_blockcount - 1 > max_dqid_off) {
+                       xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK,
+                                       irec.br_startoff);
+                       break;
+               }
        }
-       xfs_iunlock(ip, XFS_ILOCK_EXCL);
-       if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
-               goto out;
 
-       /* Check all the quota items. */
-       while (id < ((xfs_dqid_t)-1ULL)) {
-               if (xfs_scrub_should_terminate(sc, &error))
-                       break;
+       return error;
+}
 
-               error = xfs_qm_dqget(mp, NULL, id, dqtype, XFS_QMOPT_DQNEXT,
-                               &dq);
-               if (error == -ENOENT)
-                       break;
-               if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK,
-                               id * qi->qi_dqperchunk, &error))
-                       break;
+/* Scrub all of a quota type's items. */
+int
+xfs_scrub_quota(
+       struct xfs_scrub_context        *sc)
+{
+       struct xfs_scrub_quota_info     sqi;
+       struct xfs_mount                *mp = sc->mp;
+       struct xfs_quotainfo            *qi = mp->m_quotainfo;
+       uint                            dqtype;
+       int                             error = 0;
 
-               xfs_scrub_quota_item(sc, dqtype, dq, id);
+       dqtype = xfs_scrub_quota_to_dqtype(sc);
 
-               id = be32_to_cpu(dq->q_core.d_id) + 1;
-               xfs_qm_dqput(dq);
-               if (!id)
-                       break;
-       }
+       /* Look for problem extents. */
+       error = xfs_scrub_quota_data_fork(sc);
+       if (error)
+               goto out;
+       if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+               goto out;
+
+       /*
+        * Check all the quota items.  Now that we've checked the quota inode
+        * data fork we have to drop ILOCK_EXCL to use the regular dquot
+        * functions.
+        */
+       xfs_iunlock(sc->ip, sc->ilock_flags);
+       sc->ilock_flags = 0;
+       sqi.sc = sc;
+       sqi.last_id = 0;
+       error = xfs_qm_dqiterate(mp, dqtype, xfs_scrub_quota_item, &sqi);
+       sc->ilock_flags = XFS_ILOCK_EXCL;
+       xfs_ilock(sc->ip, sc->ilock_flags);
+       if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK,
+                       sqi.last_id * qi->qi_dqperchunk, &error))
+               goto out;
 
 out:
-       /* We set sc->ip earlier, so make sure we clear it now. */
-       sc->ip = NULL;
-out_unlock_quota:
-       mutex_unlock(&qi->qi_quotaofflock);
        return error;
-
-out_unlock_inode:
-       xfs_iunlock(ip, XFS_ILOCK_EXCL);
-       goto out;
 }