Commit | Line | Data |
---|---|---|
c7e693d9 DW |
1 | /* |
2 | * Copyright (C) 2017 Oracle. All Rights Reserved. | |
3 | * | |
4 | * Author: Darrick J. Wong <darrick.wong@oracle.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * as published by the Free Software Foundation; either version 2 | |
9 | * of the License, or (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it would be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write the Free Software Foundation, | |
18 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | |
19 | */ | |
20 | #include "xfs.h" | |
21 | #include "xfs_fs.h" | |
22 | #include "xfs_shared.h" | |
23 | #include "xfs_format.h" | |
24 | #include "xfs_trans_resv.h" | |
25 | #include "xfs_mount.h" | |
26 | #include "xfs_defer.h" | |
27 | #include "xfs_btree.h" | |
28 | #include "xfs_bit.h" | |
29 | #include "xfs_log_format.h" | |
30 | #include "xfs_trans.h" | |
31 | #include "xfs_sb.h" | |
32 | #include "xfs_alloc.h" | |
33 | #include "xfs_ialloc.h" | |
34 | #include "xfs_rmap.h" | |
f6d5fc21 | 35 | #include "xfs_refcount.h" |
c7e693d9 DW |
36 | #include "scrub/xfs_scrub.h" |
37 | #include "scrub/scrub.h" | |
38 | #include "scrub/common.h" | |
39 | #include "scrub/btree.h" | |
40 | #include "scrub/trace.h" | |
41 | ||
42 | /* | |
43 | * Set us up to scrub reverse mapping btrees. | |
44 | */ | |
45 | int | |
46 | xfs_scrub_setup_ag_rmapbt( | |
47 | struct xfs_scrub_context *sc, | |
48 | struct xfs_inode *ip) | |
49 | { | |
50 | return xfs_scrub_setup_ag_btree(sc, ip, false); | |
51 | } | |
52 | ||
53 | /* Reverse-mapping scrubber. */ | |
54 | ||
f6d5fc21 DW |
55 | /* Cross-reference a rmap against the refcount btree. */ |
56 | STATIC void | |
57 | xfs_scrub_rmapbt_xref_refc( | |
58 | struct xfs_scrub_context *sc, | |
59 | struct xfs_rmap_irec *irec) | |
60 | { | |
61 | xfs_agblock_t fbno; | |
62 | xfs_extlen_t flen; | |
63 | bool non_inode; | |
64 | bool is_bmbt; | |
65 | bool is_attr; | |
66 | bool is_unwritten; | |
67 | int error; | |
68 | ||
69 | if (!sc->sa.refc_cur) | |
70 | return; | |
71 | ||
72 | non_inode = XFS_RMAP_NON_INODE_OWNER(irec->rm_owner); | |
73 | is_bmbt = irec->rm_flags & XFS_RMAP_BMBT_BLOCK; | |
74 | is_attr = irec->rm_flags & XFS_RMAP_ATTR_FORK; | |
75 | is_unwritten = irec->rm_flags & XFS_RMAP_UNWRITTEN; | |
76 | ||
77 | /* If this is shared, must be a data fork extent. */ | |
78 | error = xfs_refcount_find_shared(sc->sa.refc_cur, irec->rm_startblock, | |
79 | irec->rm_blockcount, &fbno, &flen, false); | |
80 | if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.refc_cur)) | |
81 | return; | |
82 | if (flen != 0 && (non_inode || is_attr || is_bmbt || is_unwritten)) | |
83 | xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0); | |
84 | } | |
85 | ||
166d7641 DW |
86 | /* Cross-reference with the other btrees. */ |
87 | STATIC void | |
88 | xfs_scrub_rmapbt_xref( | |
89 | struct xfs_scrub_context *sc, | |
90 | struct xfs_rmap_irec *irec) | |
91 | { | |
52dc4b44 DW |
92 | xfs_agblock_t agbno = irec->rm_startblock; |
93 | xfs_extlen_t len = irec->rm_blockcount; | |
94 | ||
166d7641 DW |
95 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
96 | return; | |
52dc4b44 DW |
97 | |
98 | xfs_scrub_xref_is_used_space(sc, agbno, len); | |
2e6f2756 DW |
99 | if (irec->rm_owner == XFS_RMAP_OWN_INODES) |
100 | xfs_scrub_xref_is_inode_chunk(sc, agbno, len); | |
101 | else | |
102 | xfs_scrub_xref_is_not_inode_chunk(sc, agbno, len); | |
f6d5fc21 DW |
103 | if (irec->rm_owner == XFS_RMAP_OWN_COW) |
104 | xfs_scrub_xref_is_cow_staging(sc, irec->rm_startblock, | |
105 | irec->rm_blockcount); | |
106 | else | |
107 | xfs_scrub_rmapbt_xref_refc(sc, irec); | |
166d7641 DW |
108 | } |
109 | ||
c7e693d9 DW |
110 | /* Scrub an rmapbt record. */ |
111 | STATIC int | |
112 | xfs_scrub_rmapbt_rec( | |
113 | struct xfs_scrub_btree *bs, | |
114 | union xfs_btree_rec *rec) | |
115 | { | |
116 | struct xfs_mount *mp = bs->cur->bc_mp; | |
117 | struct xfs_rmap_irec irec; | |
118 | xfs_agnumber_t agno = bs->cur->bc_private.a.agno; | |
119 | bool non_inode; | |
120 | bool is_unwritten; | |
121 | bool is_bmbt; | |
122 | bool is_attr; | |
123 | int error; | |
124 | ||
125 | error = xfs_rmap_btrec_to_irec(rec, &irec); | |
126 | if (!xfs_scrub_btree_process_error(bs->sc, bs->cur, 0, &error)) | |
127 | goto out; | |
128 | ||
129 | /* Check extent. */ | |
130 | if (irec.rm_startblock + irec.rm_blockcount <= irec.rm_startblock) | |
131 | xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0); | |
132 | ||
133 | if (irec.rm_owner == XFS_RMAP_OWN_FS) { | |
134 | /* | |
135 | * xfs_verify_agbno returns false for static fs metadata. | |
136 | * Since that only exists at the start of the AG, validate | |
137 | * that by hand. | |
138 | */ | |
139 | if (irec.rm_startblock != 0 || | |
140 | irec.rm_blockcount != XFS_AGFL_BLOCK(mp) + 1) | |
141 | xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0); | |
142 | } else { | |
143 | /* | |
144 | * Otherwise we must point somewhere past the static metadata | |
145 | * but before the end of the FS. Run the regular check. | |
146 | */ | |
147 | if (!xfs_verify_agbno(mp, agno, irec.rm_startblock) || | |
148 | !xfs_verify_agbno(mp, agno, irec.rm_startblock + | |
149 | irec.rm_blockcount - 1)) | |
150 | xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0); | |
151 | } | |
152 | ||
153 | /* Check flags. */ | |
154 | non_inode = XFS_RMAP_NON_INODE_OWNER(irec.rm_owner); | |
155 | is_bmbt = irec.rm_flags & XFS_RMAP_BMBT_BLOCK; | |
156 | is_attr = irec.rm_flags & XFS_RMAP_ATTR_FORK; | |
157 | is_unwritten = irec.rm_flags & XFS_RMAP_UNWRITTEN; | |
158 | ||
159 | if (is_bmbt && irec.rm_offset != 0) | |
160 | xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0); | |
161 | ||
162 | if (non_inode && irec.rm_offset != 0) | |
163 | xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0); | |
164 | ||
165 | if (is_unwritten && (is_bmbt || non_inode || is_attr)) | |
166 | xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0); | |
167 | ||
168 | if (non_inode && (is_bmbt || is_unwritten || is_attr)) | |
169 | xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0); | |
170 | ||
171 | if (!non_inode) { | |
172 | if (!xfs_verify_ino(mp, irec.rm_owner)) | |
173 | xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0); | |
174 | } else { | |
175 | /* Non-inode owner within the magic values? */ | |
176 | if (irec.rm_owner <= XFS_RMAP_OWN_MIN || | |
177 | irec.rm_owner > XFS_RMAP_OWN_FS) | |
178 | xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0); | |
179 | } | |
166d7641 DW |
180 | |
181 | xfs_scrub_rmapbt_xref(bs->sc, &irec); | |
c7e693d9 DW |
182 | out: |
183 | return error; | |
184 | } | |
185 | ||
186 | /* Scrub the rmap btree for some AG. */ | |
187 | int | |
188 | xfs_scrub_rmapbt( | |
189 | struct xfs_scrub_context *sc) | |
190 | { | |
191 | struct xfs_owner_info oinfo; | |
192 | ||
193 | xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_AG); | |
194 | return xfs_scrub_btree(sc, sc->sa.rmap_cur, xfs_scrub_rmapbt_rec, | |
195 | &oinfo, NULL); | |
196 | } | |
d852657c DW |
197 | |
198 | /* xref check that the extent is owned by a given owner */ | |
199 | static inline void | |
200 | xfs_scrub_xref_check_owner( | |
201 | struct xfs_scrub_context *sc, | |
202 | xfs_agblock_t bno, | |
203 | xfs_extlen_t len, | |
204 | struct xfs_owner_info *oinfo, | |
205 | bool should_have_rmap) | |
206 | { | |
207 | bool has_rmap; | |
208 | int error; | |
209 | ||
210 | if (!sc->sa.rmap_cur) | |
211 | return; | |
212 | ||
213 | error = xfs_rmap_record_exists(sc->sa.rmap_cur, bno, len, oinfo, | |
214 | &has_rmap); | |
215 | if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.rmap_cur)) | |
216 | return; | |
217 | if (has_rmap != should_have_rmap) | |
218 | xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0); | |
219 | } | |
220 | ||
221 | /* xref check that the extent is owned by a given owner */ | |
222 | void | |
223 | xfs_scrub_xref_is_owned_by( | |
224 | struct xfs_scrub_context *sc, | |
225 | xfs_agblock_t bno, | |
226 | xfs_extlen_t len, | |
227 | struct xfs_owner_info *oinfo) | |
228 | { | |
229 | xfs_scrub_xref_check_owner(sc, bno, len, oinfo, true); | |
230 | } | |
231 | ||
232 | /* xref check that the extent is not owned by a given owner */ | |
233 | void | |
234 | xfs_scrub_xref_is_not_owned_by( | |
235 | struct xfs_scrub_context *sc, | |
236 | xfs_agblock_t bno, | |
237 | xfs_extlen_t len, | |
238 | struct xfs_owner_info *oinfo) | |
239 | { | |
240 | xfs_scrub_xref_check_owner(sc, bno, len, oinfo, false); | |
241 | } | |
242 | ||
243 | /* xref check that the extent has no reverse mapping at all */ | |
244 | void | |
245 | xfs_scrub_xref_has_no_owner( | |
246 | struct xfs_scrub_context *sc, | |
247 | xfs_agblock_t bno, | |
248 | xfs_extlen_t len) | |
249 | { | |
250 | bool has_rmap; | |
251 | int error; | |
252 | ||
253 | if (!sc->sa.rmap_cur) | |
254 | return; | |
255 | ||
256 | error = xfs_rmap_has_record(sc->sa.rmap_cur, bno, len, &has_rmap); | |
257 | if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.rmap_cur)) | |
258 | return; | |
259 | if (has_rmap) | |
260 | xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0); | |
261 | } |