0001
0002
0003
0004
0005
0006 #include "xfs.h"
0007 #include "xfs_fs.h"
0008 #include "xfs_shared.h"
0009 #include "xfs_format.h"
0010 #include "xfs_trans_resv.h"
0011 #include "xfs_mount.h"
0012 #include "xfs_log_format.h"
0013 #include "xfs_trans.h"
0014 #include "xfs_inode.h"
0015 #include "xfs_icache.h"
0016 #include "xfs_dir2.h"
0017 #include "xfs_dir2_priv.h"
0018 #include "scrub/scrub.h"
0019 #include "scrub/common.h"
0020 #include "scrub/dabtree.h"
0021
0022
0023 int
0024 xchk_setup_directory(
0025 struct xfs_scrub *sc)
0026 {
0027 return xchk_setup_inode_contents(sc, 0);
0028 }
0029
0030
0031
0032
0033
0034 struct xchk_dir_ctx {
0035
0036 struct dir_context dir_iter;
0037
0038 struct xfs_scrub *sc;
0039 };
0040
0041
0042 STATIC int
0043 xchk_dir_check_ftype(
0044 struct xchk_dir_ctx *sdc,
0045 xfs_fileoff_t offset,
0046 xfs_ino_t inum,
0047 int dtype)
0048 {
0049 struct xfs_mount *mp = sdc->sc->mp;
0050 struct xfs_inode *ip;
0051 int ino_dtype;
0052 int error = 0;
0053
0054 if (!xfs_has_ftype(mp)) {
0055 if (dtype != DT_UNKNOWN && dtype != DT_DIR)
0056 xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK,
0057 offset);
0058 goto out;
0059 }
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074 error = xfs_iget(mp, sdc->sc->tp, inum, 0, 0, &ip);
0075 if (error == -EINVAL || error == -ENOENT) {
0076 error = -EFSCORRUPTED;
0077 xchk_fblock_process_error(sdc->sc, XFS_DATA_FORK, 0, &error);
0078 goto out;
0079 }
0080 if (!xchk_fblock_xref_process_error(sdc->sc, XFS_DATA_FORK, offset,
0081 &error))
0082 goto out;
0083
0084
0085 ino_dtype = xfs_dir3_get_dtype(mp,
0086 xfs_mode_to_ftype(VFS_I(ip)->i_mode));
0087 if (ino_dtype != dtype)
0088 xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, offset);
0089 xfs_irele(ip);
0090 out:
0091 return error;
0092 }
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102 STATIC int
0103 xchk_dir_actor(
0104 struct dir_context *dir_iter,
0105 const char *name,
0106 int namelen,
0107 loff_t pos,
0108 u64 ino,
0109 unsigned type)
0110 {
0111 struct xfs_mount *mp;
0112 struct xfs_inode *ip;
0113 struct xchk_dir_ctx *sdc;
0114 struct xfs_name xname;
0115 xfs_ino_t lookup_ino;
0116 xfs_dablk_t offset;
0117 bool checked_ftype = false;
0118 int error = 0;
0119
0120 sdc = container_of(dir_iter, struct xchk_dir_ctx, dir_iter);
0121 ip = sdc->sc->ip;
0122 mp = ip->i_mount;
0123 offset = xfs_dir2_db_to_da(mp->m_dir_geo,
0124 xfs_dir2_dataptr_to_db(mp->m_dir_geo, pos));
0125
0126 if (xchk_should_terminate(sdc->sc, &error))
0127 return error;
0128
0129
0130 if (!xfs_verify_dir_ino(mp, ino)) {
0131 xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, offset);
0132 goto out;
0133 }
0134
0135
0136 if (!xfs_dir2_namecheck(name, namelen)) {
0137 xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, offset);
0138 goto out;
0139 }
0140
0141 if (!strncmp(".", name, namelen)) {
0142
0143 if (xfs_has_ftype(mp) && type != DT_DIR)
0144 xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK,
0145 offset);
0146 checked_ftype = true;
0147 if (ino != ip->i_ino)
0148 xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK,
0149 offset);
0150 } else if (!strncmp("..", name, namelen)) {
0151
0152
0153
0154
0155 if (xfs_has_ftype(mp) && type != DT_DIR)
0156 xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK,
0157 offset);
0158 checked_ftype = true;
0159 if (ip->i_ino == mp->m_sb.sb_rootino && ino != ip->i_ino)
0160 xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK,
0161 offset);
0162 }
0163
0164
0165 xname.name = name;
0166 xname.len = namelen;
0167 xname.type = XFS_DIR3_FT_UNKNOWN;
0168
0169 error = xfs_dir_lookup(sdc->sc->tp, ip, &xname, &lookup_ino, NULL);
0170
0171 if (error == -ENOENT)
0172 error = -EFSCORRUPTED;
0173 if (!xchk_fblock_process_error(sdc->sc, XFS_DATA_FORK, offset,
0174 &error))
0175 goto out;
0176 if (lookup_ino != ino) {
0177 xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, offset);
0178 goto out;
0179 }
0180
0181
0182 if (!checked_ftype) {
0183 error = xchk_dir_check_ftype(sdc, offset, lookup_ino, type);
0184 if (error)
0185 goto out;
0186 }
0187 out:
0188
0189
0190
0191
0192
0193 if (error == 0 && sdc->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
0194 return -EFSCORRUPTED;
0195 return error;
0196 }
0197
0198
0199 STATIC int
0200 xchk_dir_rec(
0201 struct xchk_da_btree *ds,
0202 int level)
0203 {
0204 struct xfs_da_state_blk *blk = &ds->state->path.blk[level];
0205 struct xfs_mount *mp = ds->state->mp;
0206 struct xfs_inode *dp = ds->dargs.dp;
0207 struct xfs_da_geometry *geo = mp->m_dir_geo;
0208 struct xfs_dir2_data_entry *dent;
0209 struct xfs_buf *bp;
0210 struct xfs_dir2_leaf_entry *ent;
0211 unsigned int end;
0212 unsigned int iter_off;
0213 xfs_ino_t ino;
0214 xfs_dablk_t rec_bno;
0215 xfs_dir2_db_t db;
0216 xfs_dir2_data_aoff_t off;
0217 xfs_dir2_dataptr_t ptr;
0218 xfs_dahash_t calc_hash;
0219 xfs_dahash_t hash;
0220 struct xfs_dir3_icleaf_hdr hdr;
0221 unsigned int tag;
0222 int error;
0223
0224 ASSERT(blk->magic == XFS_DIR2_LEAF1_MAGIC ||
0225 blk->magic == XFS_DIR2_LEAFN_MAGIC);
0226
0227 xfs_dir2_leaf_hdr_from_disk(mp, &hdr, blk->bp->b_addr);
0228 ent = hdr.ents + blk->index;
0229
0230
0231 error = xchk_da_btree_hash(ds, level, &ent->hashval);
0232 if (error)
0233 goto out;
0234
0235
0236 ptr = be32_to_cpu(ent->address);
0237 if (ptr == 0)
0238 return 0;
0239
0240
0241 db = xfs_dir2_dataptr_to_db(geo, ptr);
0242 off = xfs_dir2_dataptr_to_off(geo, ptr);
0243 rec_bno = xfs_dir2_db_to_da(geo, db);
0244
0245 if (rec_bno >= geo->leafblk) {
0246 xchk_da_set_corrupt(ds, level);
0247 goto out;
0248 }
0249 error = xfs_dir3_data_read(ds->dargs.trans, dp, rec_bno,
0250 XFS_DABUF_MAP_HOLE_OK, &bp);
0251 if (!xchk_fblock_process_error(ds->sc, XFS_DATA_FORK, rec_bno,
0252 &error))
0253 goto out;
0254 if (!bp) {
0255 xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
0256 goto out;
0257 }
0258 xchk_buffer_recheck(ds->sc, bp);
0259
0260 if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
0261 goto out_relse;
0262
0263 dent = bp->b_addr + off;
0264
0265
0266 iter_off = geo->data_entry_offset;
0267 end = xfs_dir3_data_end_offset(geo, bp->b_addr);
0268 if (!end) {
0269 xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
0270 goto out_relse;
0271 }
0272 for (;;) {
0273 struct xfs_dir2_data_entry *dep = bp->b_addr + iter_off;
0274 struct xfs_dir2_data_unused *dup = bp->b_addr + iter_off;
0275
0276 if (iter_off >= end) {
0277 xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
0278 goto out_relse;
0279 }
0280
0281 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
0282 iter_off += be16_to_cpu(dup->length);
0283 continue;
0284 }
0285 if (dep == dent)
0286 break;
0287 iter_off += xfs_dir2_data_entsize(mp, dep->namelen);
0288 }
0289
0290
0291 ino = be64_to_cpu(dent->inumber);
0292 hash = be32_to_cpu(ent->hashval);
0293 tag = be16_to_cpup(xfs_dir2_data_entry_tag_p(mp, dent));
0294 if (!xfs_verify_dir_ino(mp, ino) || tag != off)
0295 xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
0296 if (dent->namelen == 0) {
0297 xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
0298 goto out_relse;
0299 }
0300 calc_hash = xfs_da_hashname(dent->name, dent->namelen);
0301 if (calc_hash != hash)
0302 xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
0303
0304 out_relse:
0305 xfs_trans_brelse(ds->dargs.trans, bp);
0306 out:
0307 return error;
0308 }
0309
0310
0311
0312
0313
0314
0315 STATIC void
0316 xchk_directory_check_free_entry(
0317 struct xfs_scrub *sc,
0318 xfs_dablk_t lblk,
0319 struct xfs_dir2_data_free *bf,
0320 struct xfs_dir2_data_unused *dup)
0321 {
0322 struct xfs_dir2_data_free *dfp;
0323 unsigned int dup_length;
0324
0325 dup_length = be16_to_cpu(dup->length);
0326
0327
0328 if (dup_length < be16_to_cpu(bf[XFS_DIR2_DATA_FD_COUNT - 1].length))
0329 return;
0330
0331 for (dfp = &bf[XFS_DIR2_DATA_FD_COUNT - 1]; dfp >= bf; dfp--)
0332 if (dup_length == be16_to_cpu(dfp->length))
0333 return;
0334
0335
0336 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0337 }
0338
0339
0340 STATIC int
0341 xchk_directory_data_bestfree(
0342 struct xfs_scrub *sc,
0343 xfs_dablk_t lblk,
0344 bool is_block)
0345 {
0346 struct xfs_dir2_data_unused *dup;
0347 struct xfs_dir2_data_free *dfp;
0348 struct xfs_buf *bp;
0349 struct xfs_dir2_data_free *bf;
0350 struct xfs_mount *mp = sc->mp;
0351 u16 tag;
0352 unsigned int nr_bestfrees = 0;
0353 unsigned int nr_frees = 0;
0354 unsigned int smallest_bestfree;
0355 int newlen;
0356 unsigned int offset;
0357 unsigned int end;
0358 int error;
0359
0360 if (is_block) {
0361
0362 if (lblk != XFS_B_TO_FSBT(mp, XFS_DIR2_DATA_OFFSET))
0363 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0364 error = xfs_dir3_block_read(sc->tp, sc->ip, &bp);
0365 } else {
0366
0367 error = xfs_dir3_data_read(sc->tp, sc->ip, lblk, 0, &bp);
0368 }
0369 if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))
0370 goto out;
0371 xchk_buffer_recheck(sc, bp);
0372
0373
0374
0375 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
0376 goto out_buf;
0377
0378
0379 bf = xfs_dir2_data_bestfree_p(mp, bp->b_addr);
0380 smallest_bestfree = UINT_MAX;
0381 for (dfp = &bf[0]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT]; dfp++) {
0382 offset = be16_to_cpu(dfp->offset);
0383 if (offset == 0)
0384 continue;
0385 if (offset >= mp->m_dir_geo->blksize) {
0386 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0387 goto out_buf;
0388 }
0389 dup = bp->b_addr + offset;
0390 tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup));
0391
0392
0393 if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG) ||
0394 be16_to_cpu(dup->length) != be16_to_cpu(dfp->length) ||
0395 tag != offset) {
0396 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0397 goto out_buf;
0398 }
0399
0400
0401 if (smallest_bestfree < be16_to_cpu(dfp->length)) {
0402 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0403 goto out_buf;
0404 }
0405
0406 smallest_bestfree = be16_to_cpu(dfp->length);
0407 nr_bestfrees++;
0408 }
0409
0410
0411 offset = mp->m_dir_geo->data_entry_offset;
0412 end = xfs_dir3_data_end_offset(mp->m_dir_geo, bp->b_addr);
0413
0414
0415 while (offset < end) {
0416 dup = bp->b_addr + offset;
0417
0418
0419 if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG)) {
0420 struct xfs_dir2_data_entry *dep = bp->b_addr + offset;
0421
0422 newlen = xfs_dir2_data_entsize(mp, dep->namelen);
0423 if (newlen <= 0) {
0424 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
0425 lblk);
0426 goto out_buf;
0427 }
0428 offset += newlen;
0429 continue;
0430 }
0431
0432
0433 tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup));
0434 if (tag != offset) {
0435 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0436 goto out_buf;
0437 }
0438
0439
0440
0441
0442
0443 xchk_directory_check_free_entry(sc, lblk, bf, dup);
0444 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
0445 goto out_buf;
0446
0447
0448 newlen = be16_to_cpu(dup->length);
0449 if (newlen <= 0) {
0450 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0451 goto out_buf;
0452 }
0453 offset += newlen;
0454 if (offset <= end)
0455 nr_frees++;
0456 }
0457
0458
0459 if (offset != end)
0460 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0461
0462
0463 if (nr_frees < nr_bestfrees)
0464 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0465 out_buf:
0466 xfs_trans_brelse(sc->tp, bp);
0467 out:
0468 return error;
0469 }
0470
0471
0472
0473
0474
0475
0476
0477 STATIC void
0478 xchk_directory_check_freesp(
0479 struct xfs_scrub *sc,
0480 xfs_dablk_t lblk,
0481 struct xfs_buf *dbp,
0482 unsigned int len)
0483 {
0484 struct xfs_dir2_data_free *dfp;
0485
0486 dfp = xfs_dir2_data_bestfree_p(sc->mp, dbp->b_addr);
0487
0488 if (len != be16_to_cpu(dfp->length))
0489 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0490
0491 if (len > 0 && be16_to_cpu(dfp->offset) == 0)
0492 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0493 }
0494
0495
0496 STATIC int
0497 xchk_directory_leaf1_bestfree(
0498 struct xfs_scrub *sc,
0499 struct xfs_da_args *args,
0500 xfs_dir2_db_t last_data_db,
0501 xfs_dablk_t lblk)
0502 {
0503 struct xfs_dir3_icleaf_hdr leafhdr;
0504 struct xfs_dir2_leaf_tail *ltp;
0505 struct xfs_dir2_leaf *leaf;
0506 struct xfs_buf *dbp;
0507 struct xfs_buf *bp;
0508 struct xfs_da_geometry *geo = sc->mp->m_dir_geo;
0509 __be16 *bestp;
0510 __u16 best;
0511 __u32 hash;
0512 __u32 lasthash = 0;
0513 __u32 bestcount;
0514 unsigned int stale = 0;
0515 int i;
0516 int error;
0517
0518
0519 error = xfs_dir3_leaf_read(sc->tp, sc->ip, lblk, &bp);
0520 if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))
0521 return error;
0522 xchk_buffer_recheck(sc, bp);
0523
0524 leaf = bp->b_addr;
0525 xfs_dir2_leaf_hdr_from_disk(sc->ip->i_mount, &leafhdr, leaf);
0526 ltp = xfs_dir2_leaf_tail_p(geo, leaf);
0527 bestcount = be32_to_cpu(ltp->bestcount);
0528 bestp = xfs_dir2_leaf_bests_p(ltp);
0529
0530 if (xfs_has_crc(sc->mp)) {
0531 struct xfs_dir3_leaf_hdr *hdr3 = bp->b_addr;
0532
0533 if (hdr3->pad != cpu_to_be32(0))
0534 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0535 }
0536
0537
0538
0539
0540
0541
0542
0543
0544
0545 if (bestcount != last_data_db + 1) {
0546 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0547 goto out;
0548 }
0549
0550
0551 if (leafhdr.count > geo->leaf_max_ents) {
0552 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0553 goto out;
0554 }
0555
0556
0557 if ((char *)&leafhdr.ents[leafhdr.count] > (char *)bestp) {
0558 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0559 goto out;
0560 }
0561
0562
0563 for (i = 0; i < leafhdr.count; i++) {
0564 hash = be32_to_cpu(leafhdr.ents[i].hashval);
0565 if (i > 0 && lasthash > hash)
0566 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0567 lasthash = hash;
0568 if (leafhdr.ents[i].address ==
0569 cpu_to_be32(XFS_DIR2_NULL_DATAPTR))
0570 stale++;
0571 }
0572 if (leafhdr.stale != stale)
0573 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0574 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
0575 goto out;
0576
0577
0578 for (i = 0; i < bestcount; i++, bestp++) {
0579 best = be16_to_cpu(*bestp);
0580 error = xfs_dir3_data_read(sc->tp, sc->ip,
0581 xfs_dir2_db_to_da(args->geo, i),
0582 XFS_DABUF_MAP_HOLE_OK,
0583 &dbp);
0584 if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk,
0585 &error))
0586 break;
0587
0588 if (!dbp) {
0589 if (best != NULLDATAOFF) {
0590 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
0591 lblk);
0592 break;
0593 }
0594 continue;
0595 }
0596
0597 if (best == NULLDATAOFF)
0598 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0599 else
0600 xchk_directory_check_freesp(sc, lblk, dbp, best);
0601 xfs_trans_brelse(sc->tp, dbp);
0602 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
0603 break;
0604 }
0605 out:
0606 xfs_trans_brelse(sc->tp, bp);
0607 return error;
0608 }
0609
0610
0611 STATIC int
0612 xchk_directory_free_bestfree(
0613 struct xfs_scrub *sc,
0614 struct xfs_da_args *args,
0615 xfs_dablk_t lblk)
0616 {
0617 struct xfs_dir3_icfree_hdr freehdr;
0618 struct xfs_buf *dbp;
0619 struct xfs_buf *bp;
0620 __u16 best;
0621 unsigned int stale = 0;
0622 int i;
0623 int error;
0624
0625
0626 error = xfs_dir2_free_read(sc->tp, sc->ip, lblk, &bp);
0627 if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))
0628 return error;
0629 xchk_buffer_recheck(sc, bp);
0630
0631 if (xfs_has_crc(sc->mp)) {
0632 struct xfs_dir3_free_hdr *hdr3 = bp->b_addr;
0633
0634 if (hdr3->pad != cpu_to_be32(0))
0635 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0636 }
0637
0638
0639 xfs_dir2_free_hdr_from_disk(sc->ip->i_mount, &freehdr, bp->b_addr);
0640 for (i = 0; i < freehdr.nvalid; i++) {
0641 best = be16_to_cpu(freehdr.bests[i]);
0642 if (best == NULLDATAOFF) {
0643 stale++;
0644 continue;
0645 }
0646 error = xfs_dir3_data_read(sc->tp, sc->ip,
0647 (freehdr.firstdb + i) * args->geo->fsbcount,
0648 0, &dbp);
0649 if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk,
0650 &error))
0651 goto out;
0652 xchk_directory_check_freesp(sc, lblk, dbp, best);
0653 xfs_trans_brelse(sc->tp, dbp);
0654 }
0655
0656 if (freehdr.nused + stale != freehdr.nvalid)
0657 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0658 out:
0659 xfs_trans_brelse(sc->tp, bp);
0660 return error;
0661 }
0662
0663
0664 STATIC int
0665 xchk_directory_blocks(
0666 struct xfs_scrub *sc)
0667 {
0668 struct xfs_bmbt_irec got;
0669 struct xfs_da_args args;
0670 struct xfs_ifork *ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK);
0671 struct xfs_mount *mp = sc->mp;
0672 xfs_fileoff_t leaf_lblk;
0673 xfs_fileoff_t free_lblk;
0674 xfs_fileoff_t lblk;
0675 struct xfs_iext_cursor icur;
0676 xfs_dablk_t dabno;
0677 xfs_dir2_db_t last_data_db = 0;
0678 bool found;
0679 int is_block = 0;
0680 int error;
0681
0682
0683 if (ifp->if_format != XFS_DINODE_FMT_EXTENTS &&
0684 ifp->if_format != XFS_DINODE_FMT_BTREE)
0685 return 0;
0686
0687 lblk = XFS_B_TO_FSB(mp, XFS_DIR2_DATA_OFFSET);
0688 leaf_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_LEAF_OFFSET);
0689 free_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_FREE_OFFSET);
0690
0691
0692 args.dp = sc->ip;
0693 args.geo = mp->m_dir_geo;
0694 args.trans = sc->tp;
0695 error = xfs_dir2_isblock(&args, &is_block);
0696 if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))
0697 goto out;
0698
0699
0700 found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got);
0701 while (found && !(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) {
0702
0703 if (got.br_startoff >= leaf_lblk)
0704 break;
0705
0706
0707
0708
0709
0710
0711
0712
0713
0714
0715
0716
0717 for (lblk = roundup((xfs_dablk_t)got.br_startoff,
0718 args.geo->fsbcount);
0719 lblk < got.br_startoff + got.br_blockcount;
0720 lblk += args.geo->fsbcount) {
0721 last_data_db = xfs_dir2_da_to_db(args.geo, lblk);
0722 error = xchk_directory_data_bestfree(sc, lblk,
0723 is_block);
0724 if (error)
0725 goto out;
0726 }
0727 dabno = got.br_startoff + got.br_blockcount;
0728 lblk = roundup(dabno, args.geo->fsbcount);
0729 found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got);
0730 }
0731
0732 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
0733 goto out;
0734
0735
0736 if (xfs_iext_lookup_extent(sc->ip, ifp, leaf_lblk, &icur, &got) &&
0737 got.br_startoff == leaf_lblk &&
0738 got.br_blockcount == args.geo->fsbcount &&
0739 !xfs_iext_next_extent(ifp, &icur, &got)) {
0740 if (is_block) {
0741 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0742 goto out;
0743 }
0744 error = xchk_directory_leaf1_bestfree(sc, &args, last_data_db,
0745 leaf_lblk);
0746 if (error)
0747 goto out;
0748 }
0749
0750 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
0751 goto out;
0752
0753
0754 lblk = free_lblk;
0755 found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got);
0756 while (found && !(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) {
0757
0758
0759
0760
0761 lblk = got.br_startoff;
0762 if (lblk & ~0xFFFFFFFFULL) {
0763 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0764 goto out;
0765 }
0766 if (is_block) {
0767 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
0768 goto out;
0769 }
0770
0771
0772
0773
0774
0775
0776
0777
0778
0779
0780
0781
0782 for (lblk = roundup((xfs_dablk_t)got.br_startoff,
0783 args.geo->fsbcount);
0784 lblk < got.br_startoff + got.br_blockcount;
0785 lblk += args.geo->fsbcount) {
0786 error = xchk_directory_free_bestfree(sc, &args,
0787 lblk);
0788 if (error)
0789 goto out;
0790 }
0791 dabno = got.br_startoff + got.br_blockcount;
0792 lblk = roundup(dabno, args.geo->fsbcount);
0793 found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got);
0794 }
0795 out:
0796 return error;
0797 }
0798
0799
0800 int
0801 xchk_directory(
0802 struct xfs_scrub *sc)
0803 {
0804 struct xchk_dir_ctx sdc = {
0805 .dir_iter.actor = xchk_dir_actor,
0806 .dir_iter.pos = 0,
0807 .sc = sc,
0808 };
0809 size_t bufsize;
0810 loff_t oldpos;
0811 int error = 0;
0812
0813 if (!S_ISDIR(VFS_I(sc->ip)->i_mode))
0814 return -ENOENT;
0815
0816
0817 if (sc->ip->i_disk_size < xfs_dir2_sf_hdr_size(0)) {
0818 xchk_ino_set_corrupt(sc, sc->ip->i_ino);
0819 goto out;
0820 }
0821
0822
0823 error = xchk_da_btree(sc, XFS_DATA_FORK, xchk_dir_rec, NULL);
0824 if (error)
0825 return error;
0826
0827 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
0828 return error;
0829
0830
0831 error = xchk_directory_blocks(sc);
0832 if (error)
0833 return error;
0834
0835 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
0836 return error;
0837
0838
0839
0840
0841
0842 bufsize = (size_t)min_t(loff_t, XFS_READDIR_BUFSIZE,
0843 sc->ip->i_disk_size);
0844
0845
0846
0847
0848
0849
0850
0851
0852
0853
0854
0855
0856
0857
0858
0859
0860
0861 oldpos = 0;
0862 sc->ilock_flags &= ~XFS_ILOCK_EXCL;
0863 xfs_iunlock(sc->ip, XFS_ILOCK_EXCL);
0864 while (true) {
0865 error = xfs_readdir(sc->tp, sc->ip, &sdc.dir_iter, bufsize);
0866 if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0,
0867 &error))
0868 goto out;
0869 if (oldpos == sdc.dir_iter.pos)
0870 break;
0871 oldpos = sdc.dir_iter.pos;
0872 }
0873
0874 out:
0875 return error;
0876 }