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