Commit | Line | Data |
---|---|---|
a5c46e5e 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_inode.h" | |
33 | #include "xfs_icache.h" | |
34 | #include "xfs_itable.h" | |
35 | #include "xfs_da_format.h" | |
36 | #include "xfs_da_btree.h" | |
37 | #include "xfs_dir2.h" | |
38 | #include "xfs_dir2_priv.h" | |
39 | #include "xfs_ialloc.h" | |
40 | #include "scrub/xfs_scrub.h" | |
41 | #include "scrub/scrub.h" | |
42 | #include "scrub/common.h" | |
43 | #include "scrub/trace.h" | |
44 | #include "scrub/dabtree.h" | |
45 | ||
46 | /* Set us up to scrub directories. */ | |
47 | int | |
48 | xfs_scrub_setup_directory( | |
49 | struct xfs_scrub_context *sc, | |
50 | struct xfs_inode *ip) | |
51 | { | |
52 | return xfs_scrub_setup_inode_contents(sc, ip, 0); | |
53 | } | |
54 | ||
55 | /* Directories */ | |
56 | ||
57 | /* Scrub a directory entry. */ | |
58 | ||
59 | struct xfs_scrub_dir_ctx { | |
60 | /* VFS fill-directory iterator */ | |
61 | struct dir_context dir_iter; | |
62 | ||
63 | struct xfs_scrub_context *sc; | |
64 | }; | |
65 | ||
66 | /* Check that an inode's mode matches a given DT_ type. */ | |
67 | STATIC int | |
68 | xfs_scrub_dir_check_ftype( | |
69 | struct xfs_scrub_dir_ctx *sdc, | |
70 | xfs_fileoff_t offset, | |
71 | xfs_ino_t inum, | |
72 | int dtype) | |
73 | { | |
74 | struct xfs_mount *mp = sdc->sc->mp; | |
75 | struct xfs_inode *ip; | |
76 | int ino_dtype; | |
77 | int error = 0; | |
78 | ||
79 | if (!xfs_sb_version_hasftype(&mp->m_sb)) { | |
80 | if (dtype != DT_UNKNOWN && dtype != DT_DIR) | |
81 | xfs_scrub_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, | |
82 | offset); | |
83 | goto out; | |
84 | } | |
85 | ||
86 | /* | |
87 | * Grab the inode pointed to by the dirent. We release the | |
88 | * inode before we cancel the scrub transaction. Since we're | |
89 | * don't know a priori that releasing the inode won't trigger | |
90 | * eofblocks cleanup (which allocates what would be a nested | |
91 | * transaction), we can't use DONTCACHE here because DONTCACHE | |
92 | * inodes can trigger immediate inactive cleanup of the inode. | |
93 | */ | |
94 | error = xfs_iget(mp, sdc->sc->tp, inum, 0, 0, &ip); | |
9a7e2695 | 95 | if (!xfs_scrub_fblock_xref_process_error(sdc->sc, XFS_DATA_FORK, offset, |
a5c46e5e DW |
96 | &error)) |
97 | goto out; | |
98 | ||
99 | /* Convert mode to the DT_* values that dir_emit uses. */ | |
100 | ino_dtype = xfs_dir3_get_dtype(mp, | |
101 | xfs_mode_to_ftype(VFS_I(ip)->i_mode)); | |
102 | if (ino_dtype != dtype) | |
103 | xfs_scrub_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, offset); | |
104 | iput(VFS_I(ip)); | |
105 | out: | |
106 | return error; | |
107 | } | |
108 | ||
109 | /* | |
110 | * Scrub a single directory entry. | |
111 | * | |
112 | * We use the VFS directory iterator (i.e. readdir) to call this | |
113 | * function for every directory entry in a directory. Once we're here, | |
114 | * we check the inode number to make sure it's sane, then we check that | |
115 | * we can look up this filename. Finally, we check the ftype. | |
116 | */ | |
117 | STATIC int | |
118 | xfs_scrub_dir_actor( | |
119 | struct dir_context *dir_iter, | |
120 | const char *name, | |
121 | int namelen, | |
122 | loff_t pos, | |
123 | u64 ino, | |
124 | unsigned type) | |
125 | { | |
126 | struct xfs_mount *mp; | |
127 | struct xfs_inode *ip; | |
128 | struct xfs_scrub_dir_ctx *sdc; | |
129 | struct xfs_name xname; | |
130 | xfs_ino_t lookup_ino; | |
131 | xfs_dablk_t offset; | |
132 | int error = 0; | |
133 | ||
134 | sdc = container_of(dir_iter, struct xfs_scrub_dir_ctx, dir_iter); | |
135 | ip = sdc->sc->ip; | |
136 | mp = ip->i_mount; | |
137 | offset = xfs_dir2_db_to_da(mp->m_dir_geo, | |
138 | xfs_dir2_dataptr_to_db(mp->m_dir_geo, pos)); | |
139 | ||
140 | /* Does this inode number make sense? */ | |
141 | if (!xfs_verify_dir_ino(mp, ino)) { | |
142 | xfs_scrub_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, offset); | |
143 | goto out; | |
144 | } | |
145 | ||
146 | if (!strncmp(".", name, namelen)) { | |
147 | /* If this is "." then check that the inum matches the dir. */ | |
148 | if (xfs_sb_version_hasftype(&mp->m_sb) && type != DT_DIR) | |
149 | xfs_scrub_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, | |
150 | offset); | |
151 | if (ino != ip->i_ino) | |
152 | xfs_scrub_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, | |
153 | offset); | |
154 | } else if (!strncmp("..", name, namelen)) { | |
155 | /* | |
156 | * If this is ".." in the root inode, check that the inum | |
157 | * matches this dir. | |
158 | */ | |
159 | if (xfs_sb_version_hasftype(&mp->m_sb) && type != DT_DIR) | |
160 | xfs_scrub_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, | |
161 | offset); | |
162 | if (ip->i_ino == mp->m_sb.sb_rootino && ino != ip->i_ino) | |
163 | xfs_scrub_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, | |
164 | offset); | |
165 | } | |
166 | ||
167 | /* Verify that we can look up this name by hash. */ | |
168 | xname.name = name; | |
169 | xname.len = namelen; | |
170 | xname.type = XFS_DIR3_FT_UNKNOWN; | |
171 | ||
172 | error = xfs_dir_lookup(sdc->sc->tp, ip, &xname, &lookup_ino, NULL); | |
173 | if (!xfs_scrub_fblock_process_error(sdc->sc, XFS_DATA_FORK, offset, | |
174 | &error)) | |
175 | goto fail_xref; | |
176 | if (lookup_ino != ino) { | |
177 | xfs_scrub_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, offset); | |
178 | goto out; | |
179 | } | |
180 | ||
181 | /* Verify the file type. This function absorbs error codes. */ | |
182 | error = xfs_scrub_dir_check_ftype(sdc, offset, lookup_ino, type); | |
183 | if (error) | |
184 | goto out; | |
185 | out: | |
186 | return error; | |
187 | fail_xref: | |
188 | return error; | |
189 | } | |
190 | ||
191 | /* Scrub a directory btree record. */ | |
192 | STATIC int | |
193 | xfs_scrub_dir_rec( | |
194 | struct xfs_scrub_da_btree *ds, | |
195 | int level, | |
196 | void *rec) | |
197 | { | |
198 | struct xfs_mount *mp = ds->state->mp; | |
199 | struct xfs_dir2_leaf_entry *ent = rec; | |
200 | struct xfs_inode *dp = ds->dargs.dp; | |
201 | struct xfs_dir2_data_entry *dent; | |
202 | struct xfs_buf *bp; | |
ce92d29d | 203 | char *p, *endp; |
a5c46e5e DW |
204 | xfs_ino_t ino; |
205 | xfs_dablk_t rec_bno; | |
206 | xfs_dir2_db_t db; | |
207 | xfs_dir2_data_aoff_t off; | |
208 | xfs_dir2_dataptr_t ptr; | |
209 | xfs_dahash_t calc_hash; | |
210 | xfs_dahash_t hash; | |
211 | unsigned int tag; | |
212 | int error; | |
213 | ||
214 | /* Check the hash of the entry. */ | |
215 | error = xfs_scrub_da_btree_hash(ds, level, &ent->hashval); | |
216 | if (error) | |
217 | goto out; | |
218 | ||
219 | /* Valid hash pointer? */ | |
220 | ptr = be32_to_cpu(ent->address); | |
221 | if (ptr == 0) | |
222 | return 0; | |
223 | ||
224 | /* Find the directory entry's location. */ | |
225 | db = xfs_dir2_dataptr_to_db(mp->m_dir_geo, ptr); | |
226 | off = xfs_dir2_dataptr_to_off(mp->m_dir_geo, ptr); | |
227 | rec_bno = xfs_dir2_db_to_da(mp->m_dir_geo, db); | |
228 | ||
229 | if (rec_bno >= mp->m_dir_geo->leafblk) { | |
230 | xfs_scrub_da_set_corrupt(ds, level); | |
231 | goto out; | |
232 | } | |
233 | error = xfs_dir3_data_read(ds->dargs.trans, dp, rec_bno, -2, &bp); | |
234 | if (!xfs_scrub_fblock_process_error(ds->sc, XFS_DATA_FORK, rec_bno, | |
235 | &error)) | |
236 | goto out; | |
237 | if (!bp) { | |
238 | xfs_scrub_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); | |
239 | goto out; | |
240 | } | |
cf1b0b8b | 241 | xfs_scrub_buffer_recheck(ds->sc, bp); |
a5c46e5e | 242 | |
a5c46e5e | 243 | dent = (struct xfs_dir2_data_entry *)(((char *)bp->b_addr) + off); |
ce92d29d DW |
244 | |
245 | /* Make sure we got a real directory entry. */ | |
246 | p = (char *)mp->m_dir_inode_ops->data_entry_p(bp->b_addr); | |
247 | endp = xfs_dir3_data_endp(mp->m_dir_geo, bp->b_addr); | |
248 | if (!endp) { | |
249 | xfs_scrub_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); | |
250 | goto out_relse; | |
251 | } | |
252 | while (p < endp) { | |
253 | struct xfs_dir2_data_entry *dep; | |
254 | struct xfs_dir2_data_unused *dup; | |
255 | ||
256 | dup = (struct xfs_dir2_data_unused *)p; | |
257 | if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { | |
258 | p += be16_to_cpu(dup->length); | |
259 | continue; | |
260 | } | |
261 | dep = (struct xfs_dir2_data_entry *)p; | |
262 | if (dep == dent) | |
263 | break; | |
264 | p += mp->m_dir_inode_ops->data_entsize(dep->namelen); | |
265 | } | |
266 | if (p >= endp) { | |
267 | xfs_scrub_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); | |
268 | goto out_relse; | |
269 | } | |
270 | ||
271 | /* Retrieve the entry, sanity check it, and compare hashes. */ | |
a5c46e5e DW |
272 | ino = be64_to_cpu(dent->inumber); |
273 | hash = be32_to_cpu(ent->hashval); | |
274 | tag = be16_to_cpup(dp->d_ops->data_entry_tag_p(dent)); | |
275 | if (!xfs_verify_dir_ino(mp, ino) || tag != off) | |
276 | xfs_scrub_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); | |
277 | if (dent->namelen == 0) { | |
278 | xfs_scrub_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); | |
279 | goto out_relse; | |
280 | } | |
281 | calc_hash = xfs_da_hashname(dent->name, dent->namelen); | |
282 | if (calc_hash != hash) | |
283 | xfs_scrub_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); | |
284 | ||
285 | out_relse: | |
286 | xfs_trans_brelse(ds->dargs.trans, bp); | |
287 | out: | |
288 | return error; | |
289 | } | |
290 | ||
df481968 DW |
291 | /* |
292 | * Is this unused entry either in the bestfree or smaller than all of | |
293 | * them? We've already checked that the bestfrees are sorted longest to | |
294 | * shortest, and that there aren't any bogus entries. | |
295 | */ | |
296 | STATIC void | |
297 | xfs_scrub_directory_check_free_entry( | |
298 | struct xfs_scrub_context *sc, | |
299 | xfs_dablk_t lblk, | |
300 | struct xfs_dir2_data_free *bf, | |
301 | struct xfs_dir2_data_unused *dup) | |
302 | { | |
303 | struct xfs_dir2_data_free *dfp; | |
304 | unsigned int dup_length; | |
305 | ||
306 | dup_length = be16_to_cpu(dup->length); | |
307 | ||
308 | /* Unused entry is shorter than any of the bestfrees */ | |
309 | if (dup_length < be16_to_cpu(bf[XFS_DIR2_DATA_FD_COUNT - 1].length)) | |
310 | return; | |
311 | ||
312 | for (dfp = &bf[XFS_DIR2_DATA_FD_COUNT - 1]; dfp >= bf; dfp--) | |
313 | if (dup_length == be16_to_cpu(dfp->length)) | |
314 | return; | |
315 | ||
316 | /* Unused entry should be in the bestfrees but wasn't found. */ | |
317 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
318 | } | |
319 | ||
320 | /* Check free space info in a directory data block. */ | |
321 | STATIC int | |
322 | xfs_scrub_directory_data_bestfree( | |
323 | struct xfs_scrub_context *sc, | |
324 | xfs_dablk_t lblk, | |
325 | bool is_block) | |
326 | { | |
327 | struct xfs_dir2_data_unused *dup; | |
328 | struct xfs_dir2_data_free *dfp; | |
329 | struct xfs_buf *bp; | |
330 | struct xfs_dir2_data_free *bf; | |
331 | struct xfs_mount *mp = sc->mp; | |
332 | const struct xfs_dir_ops *d_ops; | |
333 | char *ptr; | |
334 | char *endptr; | |
335 | u16 tag; | |
336 | unsigned int nr_bestfrees = 0; | |
337 | unsigned int nr_frees = 0; | |
338 | unsigned int smallest_bestfree; | |
339 | int newlen; | |
340 | int offset; | |
341 | int error; | |
342 | ||
343 | d_ops = sc->ip->d_ops; | |
344 | ||
345 | if (is_block) { | |
346 | /* dir block format */ | |
347 | if (lblk != XFS_B_TO_FSBT(mp, XFS_DIR2_DATA_OFFSET)) | |
348 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
349 | error = xfs_dir3_block_read(sc->tp, sc->ip, &bp); | |
350 | } else { | |
351 | /* dir data format */ | |
352 | error = xfs_dir3_data_read(sc->tp, sc->ip, lblk, -1, &bp); | |
353 | } | |
354 | if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) | |
355 | goto out; | |
cf1b0b8b | 356 | xfs_scrub_buffer_recheck(sc, bp); |
df481968 DW |
357 | |
358 | /* XXX: Check xfs_dir3_data_hdr.pad is zero once we start setting it. */ | |
359 | ||
360 | /* Do the bestfrees correspond to actual free space? */ | |
361 | bf = d_ops->data_bestfree_p(bp->b_addr); | |
362 | smallest_bestfree = UINT_MAX; | |
363 | for (dfp = &bf[0]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT]; dfp++) { | |
364 | offset = be16_to_cpu(dfp->offset); | |
365 | if (offset == 0) | |
366 | continue; | |
367 | if (offset >= mp->m_dir_geo->blksize) { | |
368 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
369 | goto out_buf; | |
370 | } | |
371 | dup = (struct xfs_dir2_data_unused *)(bp->b_addr + offset); | |
372 | tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)); | |
373 | ||
374 | /* bestfree doesn't match the entry it points at? */ | |
375 | if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG) || | |
376 | be16_to_cpu(dup->length) != be16_to_cpu(dfp->length) || | |
377 | tag != ((char *)dup - (char *)bp->b_addr)) { | |
378 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
379 | goto out_buf; | |
380 | } | |
381 | ||
382 | /* bestfree records should be ordered largest to smallest */ | |
383 | if (smallest_bestfree < be16_to_cpu(dfp->length)) { | |
384 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
385 | goto out_buf; | |
386 | } | |
387 | ||
388 | smallest_bestfree = be16_to_cpu(dfp->length); | |
389 | nr_bestfrees++; | |
390 | } | |
391 | ||
392 | /* Make sure the bestfrees are actually the best free spaces. */ | |
393 | ptr = (char *)d_ops->data_entry_p(bp->b_addr); | |
ce92d29d | 394 | endptr = xfs_dir3_data_endp(mp->m_dir_geo, bp->b_addr); |
df481968 DW |
395 | |
396 | /* Iterate the entries, stopping when we hit or go past the end. */ | |
397 | while (ptr < endptr) { | |
398 | dup = (struct xfs_dir2_data_unused *)ptr; | |
399 | /* Skip real entries */ | |
400 | if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG)) { | |
401 | struct xfs_dir2_data_entry *dep; | |
402 | ||
403 | dep = (struct xfs_dir2_data_entry *)ptr; | |
404 | newlen = d_ops->data_entsize(dep->namelen); | |
405 | if (newlen <= 0) { | |
406 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, | |
407 | lblk); | |
408 | goto out_buf; | |
409 | } | |
410 | ptr += newlen; | |
411 | continue; | |
412 | } | |
413 | ||
414 | /* Spot check this free entry */ | |
415 | tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)); | |
416 | if (tag != ((char *)dup - (char *)bp->b_addr)) | |
417 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
418 | ||
419 | /* | |
420 | * Either this entry is a bestfree or it's smaller than | |
421 | * any of the bestfrees. | |
422 | */ | |
423 | xfs_scrub_directory_check_free_entry(sc, lblk, bf, dup); | |
424 | ||
425 | /* Move on. */ | |
426 | newlen = be16_to_cpu(dup->length); | |
427 | if (newlen <= 0) { | |
428 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
429 | goto out_buf; | |
430 | } | |
431 | ptr += newlen; | |
432 | if (ptr <= endptr) | |
433 | nr_frees++; | |
434 | } | |
435 | ||
436 | /* We're required to fill all the space. */ | |
437 | if (ptr != endptr) | |
438 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
439 | ||
440 | /* Did we see at least as many free slots as there are bestfrees? */ | |
441 | if (nr_frees < nr_bestfrees) | |
442 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
443 | out_buf: | |
444 | xfs_trans_brelse(sc->tp, bp); | |
445 | out: | |
446 | return error; | |
447 | } | |
448 | ||
449 | /* | |
450 | * Does the free space length in the free space index block ($len) match | |
451 | * the longest length in the directory data block's bestfree array? | |
452 | * Assume that we've already checked that the data block's bestfree | |
453 | * array is in order. | |
454 | */ | |
455 | STATIC void | |
456 | xfs_scrub_directory_check_freesp( | |
457 | struct xfs_scrub_context *sc, | |
458 | xfs_dablk_t lblk, | |
459 | struct xfs_buf *dbp, | |
460 | unsigned int len) | |
461 | { | |
df481968 | 462 | struct xfs_dir2_data_free *dfp; |
df481968 | 463 | |
35ce8523 | 464 | dfp = sc->ip->d_ops->data_bestfree_p(dbp->b_addr); |
df481968 | 465 | |
35ce8523 DW |
466 | if (len != be16_to_cpu(dfp->length)) |
467 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
df481968 | 468 | |
35ce8523 DW |
469 | if (len > 0 && be16_to_cpu(dfp->offset) == 0) |
470 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
df481968 DW |
471 | } |
472 | ||
473 | /* Check free space info in a directory leaf1 block. */ | |
474 | STATIC int | |
475 | xfs_scrub_directory_leaf1_bestfree( | |
476 | struct xfs_scrub_context *sc, | |
477 | struct xfs_da_args *args, | |
478 | xfs_dablk_t lblk) | |
479 | { | |
480 | struct xfs_dir3_icleaf_hdr leafhdr; | |
481 | struct xfs_dir2_leaf_entry *ents; | |
482 | struct xfs_dir2_leaf_tail *ltp; | |
483 | struct xfs_dir2_leaf *leaf; | |
484 | struct xfs_buf *dbp; | |
485 | struct xfs_buf *bp; | |
486 | const struct xfs_dir_ops *d_ops = sc->ip->d_ops; | |
487 | struct xfs_da_geometry *geo = sc->mp->m_dir_geo; | |
488 | __be16 *bestp; | |
489 | __u16 best; | |
490 | __u32 hash; | |
491 | __u32 lasthash = 0; | |
492 | __u32 bestcount; | |
493 | unsigned int stale = 0; | |
494 | int i; | |
495 | int error; | |
496 | ||
497 | /* Read the free space block. */ | |
498 | error = xfs_dir3_leaf_read(sc->tp, sc->ip, lblk, -1, &bp); | |
499 | if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) | |
500 | goto out; | |
cf1b0b8b | 501 | xfs_scrub_buffer_recheck(sc, bp); |
df481968 DW |
502 | |
503 | leaf = bp->b_addr; | |
504 | d_ops->leaf_hdr_from_disk(&leafhdr, leaf); | |
505 | ents = d_ops->leaf_ents_p(leaf); | |
506 | ltp = xfs_dir2_leaf_tail_p(geo, leaf); | |
507 | bestcount = be32_to_cpu(ltp->bestcount); | |
508 | bestp = xfs_dir2_leaf_bests_p(ltp); | |
509 | ||
510 | if (xfs_sb_version_hascrc(&sc->mp->m_sb)) { | |
511 | struct xfs_dir3_leaf_hdr *hdr3 = bp->b_addr; | |
512 | ||
513 | if (hdr3->pad != cpu_to_be32(0)) | |
514 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
515 | } | |
516 | ||
517 | /* | |
518 | * There should be as many bestfree slots as there are dir data | |
519 | * blocks that can fit under i_size. | |
520 | */ | |
521 | if (bestcount != xfs_dir2_byte_to_db(geo, sc->ip->i_d.di_size)) { | |
522 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
523 | goto out; | |
524 | } | |
525 | ||
526 | /* Is the leaf count even remotely sane? */ | |
527 | if (leafhdr.count > d_ops->leaf_max_ents(geo)) { | |
528 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
529 | goto out; | |
530 | } | |
531 | ||
532 | /* Leaves and bests don't overlap in leaf format. */ | |
533 | if ((char *)&ents[leafhdr.count] > (char *)bestp) { | |
534 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
535 | goto out; | |
536 | } | |
537 | ||
538 | /* Check hash value order, count stale entries. */ | |
539 | for (i = 0; i < leafhdr.count; i++) { | |
540 | hash = be32_to_cpu(ents[i].hashval); | |
541 | if (i > 0 && lasthash > hash) | |
542 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
543 | lasthash = hash; | |
544 | if (ents[i].address == cpu_to_be32(XFS_DIR2_NULL_DATAPTR)) | |
545 | stale++; | |
546 | } | |
547 | if (leafhdr.stale != stale) | |
548 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
549 | ||
550 | /* Check all the bestfree entries. */ | |
551 | for (i = 0; i < bestcount; i++, bestp++) { | |
552 | best = be16_to_cpu(*bestp); | |
553 | if (best == NULLDATAOFF) | |
554 | continue; | |
555 | error = xfs_dir3_data_read(sc->tp, sc->ip, | |
556 | i * args->geo->fsbcount, -1, &dbp); | |
557 | if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, lblk, | |
558 | &error)) | |
559 | continue; | |
560 | xfs_scrub_directory_check_freesp(sc, lblk, dbp, best); | |
561 | xfs_trans_brelse(sc->tp, dbp); | |
562 | } | |
563 | out: | |
564 | return error; | |
565 | } | |
566 | ||
567 | /* Check free space info in a directory freespace block. */ | |
568 | STATIC int | |
569 | xfs_scrub_directory_free_bestfree( | |
570 | struct xfs_scrub_context *sc, | |
571 | struct xfs_da_args *args, | |
572 | xfs_dablk_t lblk) | |
573 | { | |
574 | struct xfs_dir3_icfree_hdr freehdr; | |
575 | struct xfs_buf *dbp; | |
576 | struct xfs_buf *bp; | |
577 | __be16 *bestp; | |
88aa5de4 | 578 | __u16 best; |
df481968 DW |
579 | unsigned int stale = 0; |
580 | int i; | |
581 | int error; | |
582 | ||
583 | /* Read the free space block */ | |
584 | error = xfs_dir2_free_read(sc->tp, sc->ip, lblk, &bp); | |
585 | if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) | |
586 | goto out; | |
cf1b0b8b | 587 | xfs_scrub_buffer_recheck(sc, bp); |
df481968 DW |
588 | |
589 | if (xfs_sb_version_hascrc(&sc->mp->m_sb)) { | |
590 | struct xfs_dir3_free_hdr *hdr3 = bp->b_addr; | |
591 | ||
592 | if (hdr3->pad != cpu_to_be32(0)) | |
593 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
594 | } | |
595 | ||
596 | /* Check all the entries. */ | |
597 | sc->ip->d_ops->free_hdr_from_disk(&freehdr, bp->b_addr); | |
598 | bestp = sc->ip->d_ops->free_bests_p(bp->b_addr); | |
599 | for (i = 0; i < freehdr.nvalid; i++, bestp++) { | |
600 | best = be16_to_cpu(*bestp); | |
601 | if (best == NULLDATAOFF) { | |
602 | stale++; | |
603 | continue; | |
604 | } | |
605 | error = xfs_dir3_data_read(sc->tp, sc->ip, | |
606 | (freehdr.firstdb + i) * args->geo->fsbcount, | |
607 | -1, &dbp); | |
608 | if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, lblk, | |
609 | &error)) | |
610 | continue; | |
611 | xfs_scrub_directory_check_freesp(sc, lblk, dbp, best); | |
612 | xfs_trans_brelse(sc->tp, dbp); | |
613 | } | |
614 | ||
615 | if (freehdr.nused + stale != freehdr.nvalid) | |
616 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
617 | out: | |
618 | return error; | |
619 | } | |
620 | ||
621 | /* Check free space information in directories. */ | |
622 | STATIC int | |
623 | xfs_scrub_directory_blocks( | |
624 | struct xfs_scrub_context *sc) | |
625 | { | |
626 | struct xfs_bmbt_irec got; | |
627 | struct xfs_da_args args; | |
628 | struct xfs_ifork *ifp; | |
629 | struct xfs_mount *mp = sc->mp; | |
630 | xfs_fileoff_t leaf_lblk; | |
631 | xfs_fileoff_t free_lblk; | |
632 | xfs_fileoff_t lblk; | |
b2b1712a | 633 | struct xfs_iext_cursor icur; |
df481968 DW |
634 | xfs_dablk_t dabno; |
635 | bool found; | |
636 | int is_block = 0; | |
637 | int error; | |
638 | ||
639 | /* Ignore local format directories. */ | |
640 | if (sc->ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS && | |
641 | sc->ip->i_d.di_format != XFS_DINODE_FMT_BTREE) | |
642 | return 0; | |
643 | ||
644 | ifp = XFS_IFORK_PTR(sc->ip, XFS_DATA_FORK); | |
645 | lblk = XFS_B_TO_FSB(mp, XFS_DIR2_DATA_OFFSET); | |
646 | leaf_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_LEAF_OFFSET); | |
647 | free_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_FREE_OFFSET); | |
648 | ||
649 | /* Is this a block dir? */ | |
650 | args.dp = sc->ip; | |
651 | args.geo = mp->m_dir_geo; | |
652 | args.trans = sc->tp; | |
653 | error = xfs_dir2_isblock(&args, &is_block); | |
654 | if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) | |
655 | goto out; | |
656 | ||
657 | /* Iterate all the data extents in the directory... */ | |
b2b1712a | 658 | found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got); |
df481968 DW |
659 | while (found) { |
660 | /* Block directories only have a single block at offset 0. */ | |
661 | if (is_block && | |
662 | (got.br_startoff > 0 || | |
663 | got.br_blockcount != args.geo->fsbcount)) { | |
664 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, | |
665 | got.br_startoff); | |
666 | break; | |
667 | } | |
668 | ||
669 | /* No more data blocks... */ | |
670 | if (got.br_startoff >= leaf_lblk) | |
671 | break; | |
672 | ||
673 | /* | |
674 | * Check each data block's bestfree data. | |
675 | * | |
676 | * Iterate all the fsbcount-aligned block offsets in | |
677 | * this directory. The directory block reading code is | |
678 | * smart enough to do its own bmap lookups to handle | |
679 | * discontiguous directory blocks. When we're done | |
680 | * with the extent record, re-query the bmap at the | |
681 | * next fsbcount-aligned offset to avoid redundant | |
682 | * block checks. | |
683 | */ | |
684 | for (lblk = roundup((xfs_dablk_t)got.br_startoff, | |
685 | args.geo->fsbcount); | |
686 | lblk < got.br_startoff + got.br_blockcount; | |
687 | lblk += args.geo->fsbcount) { | |
688 | error = xfs_scrub_directory_data_bestfree(sc, lblk, | |
689 | is_block); | |
690 | if (error) | |
691 | goto out; | |
692 | } | |
693 | dabno = got.br_startoff + got.br_blockcount; | |
694 | lblk = roundup(dabno, args.geo->fsbcount); | |
b2b1712a | 695 | found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got); |
df481968 DW |
696 | } |
697 | ||
698 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) | |
699 | goto out; | |
700 | ||
701 | /* Look for a leaf1 block, which has free info. */ | |
b2b1712a | 702 | if (xfs_iext_lookup_extent(sc->ip, ifp, leaf_lblk, &icur, &got) && |
df481968 DW |
703 | got.br_startoff == leaf_lblk && |
704 | got.br_blockcount == args.geo->fsbcount && | |
b2b1712a | 705 | !xfs_iext_next_extent(ifp, &icur, &got)) { |
df481968 DW |
706 | if (is_block) { |
707 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
708 | goto out; | |
709 | } | |
710 | error = xfs_scrub_directory_leaf1_bestfree(sc, &args, | |
711 | leaf_lblk); | |
712 | if (error) | |
713 | goto out; | |
714 | } | |
715 | ||
716 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) | |
717 | goto out; | |
718 | ||
719 | /* Scan for free blocks */ | |
720 | lblk = free_lblk; | |
b2b1712a | 721 | found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got); |
df481968 DW |
722 | while (found) { |
723 | /* | |
724 | * Dirs can't have blocks mapped above 2^32. | |
725 | * Single-block dirs shouldn't even be here. | |
726 | */ | |
727 | lblk = got.br_startoff; | |
728 | if (lblk & ~0xFFFFFFFFULL) { | |
729 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
730 | goto out; | |
731 | } | |
732 | if (is_block) { | |
733 | xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); | |
734 | goto out; | |
735 | } | |
736 | ||
737 | /* | |
738 | * Check each dir free block's bestfree data. | |
739 | * | |
740 | * Iterate all the fsbcount-aligned block offsets in | |
741 | * this directory. The directory block reading code is | |
742 | * smart enough to do its own bmap lookups to handle | |
743 | * discontiguous directory blocks. When we're done | |
744 | * with the extent record, re-query the bmap at the | |
745 | * next fsbcount-aligned offset to avoid redundant | |
746 | * block checks. | |
747 | */ | |
748 | for (lblk = roundup((xfs_dablk_t)got.br_startoff, | |
749 | args.geo->fsbcount); | |
750 | lblk < got.br_startoff + got.br_blockcount; | |
751 | lblk += args.geo->fsbcount) { | |
752 | error = xfs_scrub_directory_free_bestfree(sc, &args, | |
753 | lblk); | |
754 | if (error) | |
755 | goto out; | |
756 | } | |
757 | dabno = got.br_startoff + got.br_blockcount; | |
758 | lblk = roundup(dabno, args.geo->fsbcount); | |
b2b1712a | 759 | found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got); |
df481968 DW |
760 | } |
761 | out: | |
762 | return error; | |
763 | } | |
764 | ||
a5c46e5e DW |
765 | /* Scrub a whole directory. */ |
766 | int | |
767 | xfs_scrub_directory( | |
768 | struct xfs_scrub_context *sc) | |
769 | { | |
770 | struct xfs_scrub_dir_ctx sdc = { | |
771 | .dir_iter.actor = xfs_scrub_dir_actor, | |
772 | .dir_iter.pos = 0, | |
773 | .sc = sc, | |
774 | }; | |
775 | size_t bufsize; | |
776 | loff_t oldpos; | |
72f76f73 | 777 | int error = 0; |
a5c46e5e DW |
778 | |
779 | if (!S_ISDIR(VFS_I(sc->ip)->i_mode)) | |
780 | return -ENOENT; | |
781 | ||
782 | /* Plausible size? */ | |
783 | if (sc->ip->i_d.di_size < xfs_dir2_sf_hdr_size(0)) { | |
7e56d9ea | 784 | xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino); |
a5c46e5e DW |
785 | goto out; |
786 | } | |
787 | ||
788 | /* Check directory tree structure */ | |
13791d3b | 789 | error = xfs_scrub_da_btree(sc, XFS_DATA_FORK, xfs_scrub_dir_rec, NULL); |
a5c46e5e DW |
790 | if (error) |
791 | return error; | |
792 | ||
793 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) | |
794 | return error; | |
795 | ||
df481968 DW |
796 | /* Check the freespace. */ |
797 | error = xfs_scrub_directory_blocks(sc); | |
798 | if (error) | |
799 | return error; | |
800 | ||
801 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) | |
802 | return error; | |
803 | ||
a5c46e5e DW |
804 | /* |
805 | * Check that every dirent we see can also be looked up by hash. | |
806 | * Userspace usually asks for a 32k buffer, so we will too. | |
807 | */ | |
808 | bufsize = (size_t)min_t(loff_t, XFS_READDIR_BUFSIZE, | |
809 | sc->ip->i_d.di_size); | |
810 | ||
811 | /* | |
812 | * Look up every name in this directory by hash. | |
813 | * | |
814 | * Use the xfs_readdir function to call xfs_scrub_dir_actor on | |
815 | * every directory entry in this directory. In _actor, we check | |
816 | * the name, inode number, and ftype (if applicable) of the | |
817 | * entry. xfs_readdir uses the VFS filldir functions to provide | |
818 | * iteration context. | |
819 | * | |
820 | * The VFS grabs a read or write lock via i_rwsem before it reads | |
821 | * or writes to a directory. If we've gotten this far we've | |
822 | * already obtained IOLOCK_EXCL, which (since 4.10) is the same as | |
823 | * getting a write lock on i_rwsem. Therefore, it is safe for us | |
824 | * to drop the ILOCK here in order to reuse the _readdir and | |
825 | * _dir_lookup routines, which do their own ILOCK locking. | |
826 | */ | |
827 | oldpos = 0; | |
828 | sc->ilock_flags &= ~XFS_ILOCK_EXCL; | |
829 | xfs_iunlock(sc->ip, XFS_ILOCK_EXCL); | |
830 | while (true) { | |
831 | error = xfs_readdir(sc->tp, sc->ip, &sdc.dir_iter, bufsize); | |
832 | if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, 0, | |
833 | &error)) | |
834 | goto out; | |
835 | if (oldpos == sdc.dir_iter.pos) | |
836 | break; | |
837 | oldpos = sdc.dir_iter.pos; | |
838 | } | |
839 | ||
840 | out: | |
841 | return error; | |
842 | } |