1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 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_bit.h" 10 #include "xfs_format.h" 11 #include "xfs_trans_resv.h" 12 #include "xfs_mount.h" 13 #include "xfs_log_format.h" 14 #include "xfs_trans.h" 15 #include "xfs_inode.h" 16 #include "xfs_quota.h" 17 #include "xfs_qm.h" 18 #include "xfs_bmap.h" 19 #include "scrub/scrub.h" 20 #include "scrub/common.h" 21 #include "scrub/quota.h" 22 #include "scrub/trace.h" 23 24 /* Initialize a dquot iteration cursor. */ 25 void 26 xchk_dqiter_init( 27 struct xchk_dqiter *cursor, 28 struct xfs_scrub *sc, 29 xfs_dqtype_t dqtype) 30 { 31 cursor->sc = sc; 32 cursor->bmap.br_startoff = NULLFILEOFF; 33 cursor->dqtype = dqtype & XFS_DQTYPE_REC_MASK; 34 cursor->quota_ip = xfs_quota_inode(sc->mp, cursor->dqtype); 35 cursor->id = 0; 36 } 37 38 /* 39 * Ensure that the cached data fork mapping for the dqiter cursor is fresh and 40 * covers the dquot pointed to by the scan cursor. 41 */ 42 STATIC int 43 xchk_dquot_iter_revalidate_bmap( 44 struct xchk_dqiter *cursor) 45 { 46 struct xfs_quotainfo *qi = cursor->sc->mp->m_quotainfo; 47 struct xfs_ifork *ifp = xfs_ifork_ptr(cursor->quota_ip, 48 XFS_DATA_FORK); 49 xfs_fileoff_t fileoff; 50 xfs_dqid_t this_id = cursor->id; 51 int nmaps = 1; 52 int error; 53 54 fileoff = this_id / qi->qi_dqperchunk; 55 56 /* 57 * If we have a mapping for cursor->id and it's still fresh, there's 58 * no need to reread the bmbt. 59 */ 60 if (cursor->bmap.br_startoff != NULLFILEOFF && 61 cursor->if_seq == ifp->if_seq && 62 cursor->bmap.br_startoff + cursor->bmap.br_blockcount > fileoff) 63 return 0; 64 65 /* Look up the data fork mapping for the dquot id of interest. */ 66 error = xfs_bmapi_read(cursor->quota_ip, fileoff, 67 XFS_MAX_FILEOFF - fileoff, &cursor->bmap, &nmaps, 0); 68 if (error) 69 return error; 70 if (!nmaps) { 71 ASSERT(nmaps > 0); 72 return -EFSCORRUPTED; 73 } 74 if (cursor->bmap.br_startoff > fileoff) { 75 ASSERT(cursor->bmap.br_startoff == fileoff); 76 return -EFSCORRUPTED; 77 } 78 79 cursor->if_seq = ifp->if_seq; 80 trace_xchk_dquot_iter_revalidate_bmap(cursor, cursor->id); 81 return 0; 82 } 83 84 /* Advance the dqiter cursor to the next non-sparse region of the quota file. */ 85 STATIC int 86 xchk_dquot_iter_advance_bmap( 87 struct xchk_dqiter *cursor, 88 uint64_t *next_ondisk_id) 89 { 90 struct xfs_quotainfo *qi = cursor->sc->mp->m_quotainfo; 91 struct xfs_ifork *ifp = xfs_ifork_ptr(cursor->quota_ip, 92 XFS_DATA_FORK); 93 xfs_fileoff_t fileoff; 94 uint64_t next_id; 95 int nmaps = 1; 96 int error; 97 98 /* Find the dquot id for the next non-hole mapping. */ 99 do { 100 fileoff = cursor->bmap.br_startoff + cursor->bmap.br_blockcount; 101 if (fileoff > XFS_DQ_ID_MAX / qi->qi_dqperchunk) { 102 /* The hole goes beyond the max dquot id, we're done */ 103 *next_ondisk_id = -1ULL; 104 return 0; 105 } 106 107 error = xfs_bmapi_read(cursor->quota_ip, fileoff, 108 XFS_MAX_FILEOFF - fileoff, &cursor->bmap, 109 &nmaps, 0); 110 if (error) 111 return error; 112 if (!nmaps) { 113 /* Must have reached the end of the mappings. */ 114 *next_ondisk_id = -1ULL; 115 return 0; 116 } 117 if (cursor->bmap.br_startoff > fileoff) { 118 ASSERT(cursor->bmap.br_startoff == fileoff); 119 return -EFSCORRUPTED; 120 } 121 } while (!xfs_bmap_is_real_extent(&cursor->bmap)); 122 123 next_id = cursor->bmap.br_startoff * qi->qi_dqperchunk; 124 if (next_id > XFS_DQ_ID_MAX) { 125 /* The hole goes beyond the max dquot id, we're done */ 126 *next_ondisk_id = -1ULL; 127 return 0; 128 } 129 130 /* Propose jumping forward to the dquot in the next allocated block. */ 131 *next_ondisk_id = next_id; 132 cursor->if_seq = ifp->if_seq; 133 trace_xchk_dquot_iter_advance_bmap(cursor, *next_ondisk_id); 134 return 0; 135 } 136 137 /* 138 * Find the id of the next highest incore dquot. Normally this will correspond 139 * exactly with the quota file block mappings, but repair might have erased a 140 * mapping because it was crosslinked; in that case, we need to re-allocate the 141 * space so that we can reset q_blkno. 142 */ 143 STATIC void 144 xchk_dquot_iter_advance_incore( 145 struct xchk_dqiter *cursor, 146 uint64_t *next_incore_id) 147 { 148 struct xfs_quotainfo *qi = cursor->sc->mp->m_quotainfo; 149 struct radix_tree_root *tree = xfs_dquot_tree(qi, cursor->dqtype); 150 struct xfs_dquot *dq; 151 unsigned int nr_found; 152 153 *next_incore_id = -1ULL; 154 155 mutex_lock(&qi->qi_tree_lock); 156 nr_found = radix_tree_gang_lookup(tree, (void **)&dq, cursor->id, 1); 157 if (nr_found) 158 *next_incore_id = dq->q_id; 159 mutex_unlock(&qi->qi_tree_lock); 160 161 trace_xchk_dquot_iter_advance_incore(cursor, *next_incore_id); 162 } 163 164 /* 165 * Walk all incore dquots of this filesystem. Caller must set *@cursorp to 166 * zero before the first call, and must not hold the quota file ILOCK. 167 * Returns 1 and a valid *@dqpp; 0 and *@dqpp == NULL when there are no more 168 * dquots to iterate; or a negative errno. 169 */ 170 int 171 xchk_dquot_iter( 172 struct xchk_dqiter *cursor, 173 struct xfs_dquot **dqpp) 174 { 175 struct xfs_mount *mp = cursor->sc->mp; 176 struct xfs_dquot *dq = NULL; 177 uint64_t next_ondisk, next_incore = -1ULL; 178 unsigned int lock_mode; 179 int error = 0; 180 181 if (cursor->id > XFS_DQ_ID_MAX) 182 return 0; 183 next_ondisk = cursor->id; 184 185 /* Revalidate and/or advance the cursor. */ 186 lock_mode = xfs_ilock_data_map_shared(cursor->quota_ip); 187 error = xchk_dquot_iter_revalidate_bmap(cursor); 188 if (!error && !xfs_bmap_is_real_extent(&cursor->bmap)) 189 error = xchk_dquot_iter_advance_bmap(cursor, &next_ondisk); 190 xfs_iunlock(cursor->quota_ip, lock_mode); 191 if (error) 192 return error; 193 194 if (next_ondisk > cursor->id) 195 xchk_dquot_iter_advance_incore(cursor, &next_incore); 196 197 /* Pick the next dquot in the sequence and return it. */ 198 cursor->id = min(next_ondisk, next_incore); 199 if (cursor->id > XFS_DQ_ID_MAX) 200 return 0; 201 202 trace_xchk_dquot_iter(cursor, cursor->id); 203 204 error = xfs_qm_dqget(mp, cursor->id, cursor->dqtype, false, &dq); 205 if (error) 206 return error; 207 208 cursor->id = dq->q_id + 1; 209 *dqpp = dq; 210 return 1; 211 } 212