]>
Commit | Line | Data |
---|---|---|
0b61f8a4 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
c2fc338c DW |
2 | /* |
3 | * Copyright (C) 2017 Oracle. All Rights Reserved. | |
c2fc338c | 4 | * Author: Darrick J. Wong <[email protected]> |
c2fc338c DW |
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. */ | |
32 | static inline uint | |
c517b3aa | 33 | xchk_quota_to_dqtype( |
1d8a748a | 34 | struct xfs_scrub *sc) |
c2fc338c DW |
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. */ | |
49 | int | |
c517b3aa | 50 | xchk_setup_quota( |
1d8a748a | 51 | struct xfs_scrub *sc, |
032d91f9 | 52 | struct xfs_inode *ip) |
c2fc338c | 53 | { |
032d91f9 DW |
54 | uint dqtype; |
55 | int error; | |
eb41c93f DW |
56 | |
57 | if (!XFS_IS_QUOTA_RUNNING(sc->mp) || !XFS_IS_QUOTA_ON(sc->mp)) | |
58 | return -ENOENT; | |
c2fc338c | 59 | |
c517b3aa | 60 | dqtype = xchk_quota_to_dqtype(sc); |
c2fc338c DW |
61 | if (dqtype == 0) |
62 | return -EINVAL; | |
eb41c93f DW |
63 | sc->has_quotaofflock = true; |
64 | mutex_lock(&sc->mp->m_quotainfo->qi_quotaofflock); | |
c2fc338c DW |
65 | if (!xfs_this_quota_on(sc->mp, dqtype)) |
66 | return -ENOENT; | |
c517b3aa | 67 | error = xchk_setup_fs(sc, ip); |
eb41c93f DW |
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; | |
c2fc338c DW |
73 | return 0; |
74 | } | |
75 | ||
76 | /* Quotas. */ | |
77 | ||
c517b3aa | 78 | struct xchk_quota_info { |
1d8a748a | 79 | struct xfs_scrub *sc; |
032d91f9 | 80 | xfs_dqid_t last_id; |
554ba965 DW |
81 | }; |
82 | ||
c2fc338c | 83 | /* Scrub the fields in an individual quota item. */ |
554ba965 | 84 | STATIC int |
c517b3aa | 85 | xchk_quota_item( |
032d91f9 DW |
86 | struct xfs_dquot *dq, |
87 | uint dqtype, | |
88 | void *priv) | |
c2fc338c | 89 | { |
032d91f9 | 90 | struct xchk_quota_info *sqi = priv; |
1d8a748a | 91 | struct xfs_scrub *sc = sqi->sc; |
032d91f9 DW |
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); | |
c2fc338c DW |
107 | |
108 | /* | |
554ba965 DW |
109 | * Except for the root dquot, the actual dquot we got must either have |
110 | * the same or higher id as we saw before. | |
c2fc338c | 111 | */ |
554ba965 DW |
112 | offset = id / qi->qi_dqperchunk; |
113 | if (id && id <= sqi->last_id) | |
c517b3aa | 114 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
c2fc338c | 115 | |
554ba965 DW |
116 | sqi->last_id = id; |
117 | ||
c2fc338c DW |
118 | /* Did we get the dquot type we wanted? */ |
119 | if (dqtype != (d->d_flags & XFS_DQ_ALLTYPES)) | |
c517b3aa | 120 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
c2fc338c DW |
121 | |
122 | if (d->d_pad0 != cpu_to_be32(0) || d->d_pad != cpu_to_be16(0)) | |
c517b3aa | 123 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
c2fc338c DW |
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) | |
c517b3aa | 143 | xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); |
c2fc338c | 144 | if (bsoft > bhard) |
c517b3aa | 145 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
c2fc338c DW |
146 | |
147 | if (ihard > mp->m_maxicount) | |
c517b3aa | 148 | xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); |
c2fc338c | 149 | if (isoft > ihard) |
c517b3aa | 150 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
c2fc338c DW |
151 | |
152 | if (rhard > mp->m_sb.sb_rblocks) | |
c517b3aa | 153 | xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); |
c2fc338c | 154 | if (rsoft > rhard) |
c517b3aa | 155 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
c2fc338c DW |
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) | |
c517b3aa | 170 | xchk_fblock_set_warning(sc, XFS_DATA_FORK, |
c2fc338c DW |
171 | offset); |
172 | } else { | |
173 | if (mp->m_sb.sb_dblocks < bcount) | |
c517b3aa | 174 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, |
c2fc338c DW |
175 | offset); |
176 | } | |
177 | if (icount > fs_icount || rcount > mp->m_sb.sb_rblocks) | |
c517b3aa | 178 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
c2fc338c DW |
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) | |
c517b3aa | 186 | xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); |
c2fc338c | 187 | if (id != 0 && ihard != 0 && icount > ihard) |
c517b3aa | 188 | xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); |
c2fc338c | 189 | if (id != 0 && rhard != 0 && rcount > rhard) |
c517b3aa | 190 | xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); |
554ba965 DW |
191 | |
192 | return 0; | |
c2fc338c DW |
193 | } |
194 | ||
87d9d609 DW |
195 | /* Check the quota's data fork. */ |
196 | STATIC int | |
c517b3aa | 197 | xchk_quota_data_fork( |
1d8a748a | 198 | struct xfs_scrub *sc) |
c2fc338c | 199 | { |
032d91f9 DW |
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; | |
c2fc338c | 206 | |
87d9d609 | 207 | /* Invoke the fork scrubber. */ |
c517b3aa | 208 | error = xchk_metadata_inode_forks(sc); |
87d9d609 DW |
209 | if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) |
210 | return error; | |
c2fc338c | 211 | |
87d9d609 | 212 | /* Check for data fork problems that apply only to quota files. */ |
c2fc338c | 213 | max_dqid_off = ((xfs_dqid_t)-1) / qi->qi_dqperchunk; |
87d9d609 DW |
214 | ifp = XFS_IFORK_PTR(sc->ip, XFS_DATA_FORK); |
215 | for_each_xfs_iext(ifp, &icur, &irec) { | |
c517b3aa | 216 | if (xchk_should_terminate(sc, &error)) |
c2fc338c | 217 | break; |
c2fc338c | 218 | /* |
87d9d609 | 219 | * delalloc extents or blocks mapped above the highest |
c2fc338c DW |
220 | * quota id shouldn't happen. |
221 | */ | |
222 | if (isnullstartblock(irec.br_startblock) || | |
223 | irec.br_startoff > max_dqid_off || | |
87d9d609 | 224 | irec.br_startoff + irec.br_blockcount - 1 > max_dqid_off) { |
c517b3aa | 225 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, |
87d9d609 DW |
226 | irec.br_startoff); |
227 | break; | |
228 | } | |
c2fc338c | 229 | } |
87d9d609 DW |
230 | |
231 | return error; | |
232 | } | |
233 | ||
234 | /* Scrub all of a quota type's items. */ | |
235 | int | |
c517b3aa | 236 | xchk_quota( |
1d8a748a | 237 | struct xfs_scrub *sc) |
87d9d609 | 238 | { |
032d91f9 DW |
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; | |
87d9d609 | 244 | |
c517b3aa | 245 | dqtype = xchk_quota_to_dqtype(sc); |
87d9d609 DW |
246 | |
247 | /* Look for problem extents. */ | |
c517b3aa | 248 | error = xchk_quota_data_fork(sc); |
87d9d609 DW |
249 | if (error) |
250 | goto out; | |
c2fc338c DW |
251 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
252 | goto out; | |
253 | ||
eb41c93f DW |
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; | |
554ba965 DW |
261 | sqi.sc = sc; |
262 | sqi.last_id = 0; | |
c517b3aa | 263 | error = xfs_qm_dqiterate(mp, dqtype, xchk_quota_item, &sqi); |
eb41c93f DW |
264 | sc->ilock_flags = XFS_ILOCK_EXCL; |
265 | xfs_ilock(sc->ip, sc->ilock_flags); | |
c517b3aa | 266 | if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, |
554ba965 DW |
267 | sqi.last_id * qi->qi_dqperchunk, &error)) |
268 | goto out; | |
c2fc338c DW |
269 | |
270 | out: | |
c2fc338c | 271 | return error; |
c2fc338c | 272 | } |