| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * Copyright (C) 2023 Oracle. All Rights Reserved.
- * Author: Darrick J. Wong <djwong@kernel.org>
- */
- #include "xfs.h"
- #include "xfs_fs.h"
- #include "xfs_shared.h"
- #include "xfs_bit.h"
- #include "xfs_format.h"
- #include "xfs_trans_resv.h"
- #include "xfs_mount.h"
- #include "xfs_log_format.h"
- #include "xfs_trans.h"
- #include "xfs_inode.h"
- #include "xfs_quota.h"
- #include "xfs_qm.h"
- #include "xfs_bmap.h"
- #include "scrub/scrub.h"
- #include "scrub/common.h"
- #include "scrub/quota.h"
- #include "scrub/trace.h"
- /* Initialize a dquot iteration cursor. */
- void
- xchk_dqiter_init(
- struct xchk_dqiter *cursor,
- struct xfs_scrub *sc,
- xfs_dqtype_t dqtype)
- {
- cursor->sc = sc;
- cursor->bmap.br_startoff = NULLFILEOFF;
- cursor->dqtype = dqtype & XFS_DQTYPE_REC_MASK;
- cursor->quota_ip = xfs_quota_inode(sc->mp, cursor->dqtype);
- cursor->id = 0;
- }
- /*
- * Ensure that the cached data fork mapping for the dqiter cursor is fresh and
- * covers the dquot pointed to by the scan cursor.
- */
- STATIC int
- xchk_dquot_iter_revalidate_bmap(
- struct xchk_dqiter *cursor)
- {
- struct xfs_quotainfo *qi = cursor->sc->mp->m_quotainfo;
- struct xfs_ifork *ifp = xfs_ifork_ptr(cursor->quota_ip,
- XFS_DATA_FORK);
- xfs_fileoff_t fileoff;
- xfs_dqid_t this_id = cursor->id;
- int nmaps = 1;
- int error;
- fileoff = this_id / qi->qi_dqperchunk;
- /*
- * If we have a mapping for cursor->id and it's still fresh, there's
- * no need to reread the bmbt.
- */
- if (cursor->bmap.br_startoff != NULLFILEOFF &&
- cursor->if_seq == ifp->if_seq &&
- cursor->bmap.br_startoff + cursor->bmap.br_blockcount > fileoff)
- return 0;
- /* Look up the data fork mapping for the dquot id of interest. */
- error = xfs_bmapi_read(cursor->quota_ip, fileoff,
- XFS_MAX_FILEOFF - fileoff, &cursor->bmap, &nmaps, 0);
- if (error)
- return error;
- if (!nmaps) {
- ASSERT(nmaps > 0);
- return -EFSCORRUPTED;
- }
- if (cursor->bmap.br_startoff > fileoff) {
- ASSERT(cursor->bmap.br_startoff == fileoff);
- return -EFSCORRUPTED;
- }
- cursor->if_seq = ifp->if_seq;
- trace_xchk_dquot_iter_revalidate_bmap(cursor, cursor->id);
- return 0;
- }
- /* Advance the dqiter cursor to the next non-sparse region of the quota file. */
- STATIC int
- xchk_dquot_iter_advance_bmap(
- struct xchk_dqiter *cursor,
- uint64_t *next_ondisk_id)
- {
- struct xfs_quotainfo *qi = cursor->sc->mp->m_quotainfo;
- struct xfs_ifork *ifp = xfs_ifork_ptr(cursor->quota_ip,
- XFS_DATA_FORK);
- xfs_fileoff_t fileoff;
- uint64_t next_id;
- int nmaps = 1;
- int error;
- /* Find the dquot id for the next non-hole mapping. */
- do {
- fileoff = cursor->bmap.br_startoff + cursor->bmap.br_blockcount;
- if (fileoff > XFS_DQ_ID_MAX / qi->qi_dqperchunk) {
- /* The hole goes beyond the max dquot id, we're done */
- *next_ondisk_id = -1ULL;
- return 0;
- }
- error = xfs_bmapi_read(cursor->quota_ip, fileoff,
- XFS_MAX_FILEOFF - fileoff, &cursor->bmap,
- &nmaps, 0);
- if (error)
- return error;
- if (!nmaps) {
- /* Must have reached the end of the mappings. */
- *next_ondisk_id = -1ULL;
- return 0;
- }
- if (cursor->bmap.br_startoff > fileoff) {
- ASSERT(cursor->bmap.br_startoff == fileoff);
- return -EFSCORRUPTED;
- }
- } while (!xfs_bmap_is_real_extent(&cursor->bmap));
- next_id = cursor->bmap.br_startoff * qi->qi_dqperchunk;
- if (next_id > XFS_DQ_ID_MAX) {
- /* The hole goes beyond the max dquot id, we're done */
- *next_ondisk_id = -1ULL;
- return 0;
- }
- /* Propose jumping forward to the dquot in the next allocated block. */
- *next_ondisk_id = next_id;
- cursor->if_seq = ifp->if_seq;
- trace_xchk_dquot_iter_advance_bmap(cursor, *next_ondisk_id);
- return 0;
- }
- /*
- * Find the id of the next highest incore dquot. Normally this will correspond
- * exactly with the quota file block mappings, but repair might have erased a
- * mapping because it was crosslinked; in that case, we need to re-allocate the
- * space so that we can reset q_blkno.
- */
- STATIC void
- xchk_dquot_iter_advance_incore(
- struct xchk_dqiter *cursor,
- uint64_t *next_incore_id)
- {
- struct xfs_quotainfo *qi = cursor->sc->mp->m_quotainfo;
- struct radix_tree_root *tree = xfs_dquot_tree(qi, cursor->dqtype);
- struct xfs_dquot *dq;
- unsigned int nr_found;
- *next_incore_id = -1ULL;
- mutex_lock(&qi->qi_tree_lock);
- nr_found = radix_tree_gang_lookup(tree, (void **)&dq, cursor->id, 1);
- if (nr_found)
- *next_incore_id = dq->q_id;
- mutex_unlock(&qi->qi_tree_lock);
- trace_xchk_dquot_iter_advance_incore(cursor, *next_incore_id);
- }
- /*
- * Walk all incore dquots of this filesystem. Caller must set *@cursorp to
- * zero before the first call, and must not hold the quota file ILOCK.
- * Returns 1 and a valid *@dqpp; 0 and *@dqpp == NULL when there are no more
- * dquots to iterate; or a negative errno.
- */
- int
- xchk_dquot_iter(
- struct xchk_dqiter *cursor,
- struct xfs_dquot **dqpp)
- {
- struct xfs_mount *mp = cursor->sc->mp;
- struct xfs_dquot *dq = NULL;
- uint64_t next_ondisk, next_incore = -1ULL;
- unsigned int lock_mode;
- int error = 0;
- if (cursor->id > XFS_DQ_ID_MAX)
- return 0;
- next_ondisk = cursor->id;
- /* Revalidate and/or advance the cursor. */
- lock_mode = xfs_ilock_data_map_shared(cursor->quota_ip);
- error = xchk_dquot_iter_revalidate_bmap(cursor);
- if (!error && !xfs_bmap_is_real_extent(&cursor->bmap))
- error = xchk_dquot_iter_advance_bmap(cursor, &next_ondisk);
- xfs_iunlock(cursor->quota_ip, lock_mode);
- if (error)
- return error;
- if (next_ondisk > cursor->id)
- xchk_dquot_iter_advance_incore(cursor, &next_incore);
- /* Pick the next dquot in the sequence and return it. */
- cursor->id = min(next_ondisk, next_incore);
- if (cursor->id > XFS_DQ_ID_MAX)
- return 0;
- trace_xchk_dquot_iter(cursor, cursor->id);
- error = xfs_qm_dqget(mp, cursor->id, cursor->dqtype, false, &dq);
- if (error)
- return error;
- cursor->id = dq->q_id + 1;
- *dqpp = dq;
- return 1;
- }
|