1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2022-2023 Oracle. All Rights Reserved. 4 * Author: Darrick J. Wong <djwong@kernel.org> 5 */ 6 #include "xfs.h" 7 #include "xfs_fs.h" 8 #include "xfs_shared.h" 9 #include "xfs_format.h" 10 #include "xfs_log_format.h" 11 #include "xfs_trans_resv.h" 12 #include "xfs_mount.h" 13 #include "xfs_inode.h" 14 #include "xfs_dir2.h" 15 #include "xfs_dir2_priv.h" 16 #include "xfs_trace.h" 17 #include "xfs_bmap.h" 18 #include "xfs_trans.h" 19 #include "xfs_error.h" 20 #include "scrub/scrub.h" 21 #include "scrub/readdir.h" 22 23 /* Call a function for every entry in a shortform directory. */ 24 STATIC int 25 xchk_dir_walk_sf( 26 struct xfs_scrub *sc, 27 struct xfs_inode *dp, 28 xchk_dirent_fn dirent_fn, 29 void *priv) 30 { 31 struct xfs_name name = { 32 .name = ".", 33 .len = 1, 34 .type = XFS_DIR3_FT_DIR, 35 }; 36 struct xfs_mount *mp = dp->i_mount; 37 struct xfs_da_geometry *geo = mp->m_dir_geo; 38 struct xfs_dir2_sf_entry *sfep; 39 struct xfs_dir2_sf_hdr *sfp = dp->i_df.if_data; 40 xfs_ino_t ino; 41 xfs_dir2_dataptr_t dapos; 42 unsigned int i; 43 int error; 44 45 ASSERT(dp->i_df.if_bytes == dp->i_disk_size); 46 ASSERT(sfp != NULL); 47 48 /* dot entry */ 49 dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, 50 geo->data_entry_offset); 51 52 error = dirent_fn(sc, dp, dapos, &name, dp->i_ino, priv); 53 if (error) 54 return error; 55 56 /* dotdot entry */ 57 dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, 58 geo->data_entry_offset + 59 xfs_dir2_data_entsize(mp, sizeof(".") - 1)); 60 ino = xfs_dir2_sf_get_parent_ino(sfp); 61 name.name = ".."; 62 name.len = 2; 63 64 error = dirent_fn(sc, dp, dapos, &name, ino, priv); 65 if (error) 66 return error; 67 68 /* iterate everything else */ 69 sfep = xfs_dir2_sf_firstentry(sfp); 70 for (i = 0; i < sfp->count; i++) { 71 dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, 72 xfs_dir2_sf_get_offset(sfep)); 73 ino = xfs_dir2_sf_get_ino(mp, sfp, sfep); 74 name.name = sfep->name; 75 name.len = sfep->namelen; 76 name.type = xfs_dir2_sf_get_ftype(mp, sfep); 77 78 error = dirent_fn(sc, dp, dapos, &name, ino, priv); 79 if (error) 80 return error; 81 82 sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep); 83 } 84 85 return 0; 86 } 87 88 /* Call a function for every entry in a block directory. */ 89 STATIC int 90 xchk_dir_walk_block( 91 struct xfs_scrub *sc, 92 struct xfs_inode *dp, 93 xchk_dirent_fn dirent_fn, 94 void *priv) 95 { 96 struct xfs_mount *mp = dp->i_mount; 97 struct xfs_da_geometry *geo = mp->m_dir_geo; 98 struct xfs_buf *bp; 99 unsigned int off, next_off, end; 100 int error; 101 102 error = xfs_dir3_block_read(sc->tp, dp, &bp); 103 if (error) 104 return error; 105 106 /* Walk each directory entry. */ 107 end = xfs_dir3_data_end_offset(geo, bp->b_addr); 108 for (off = geo->data_entry_offset; off < end; off = next_off) { 109 struct xfs_name name = { }; 110 struct xfs_dir2_data_unused *dup = bp->b_addr + off; 111 struct xfs_dir2_data_entry *dep = bp->b_addr + off; 112 xfs_ino_t ino; 113 xfs_dir2_dataptr_t dapos; 114 115 /* Skip an empty entry. */ 116 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { 117 next_off = off + be16_to_cpu(dup->length); 118 continue; 119 } 120 121 /* Otherwise, find the next entry and report it. */ 122 next_off = off + xfs_dir2_data_entsize(mp, dep->namelen); 123 if (next_off > end) 124 break; 125 126 dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, off); 127 ino = be64_to_cpu(dep->inumber); 128 name.name = dep->name; 129 name.len = dep->namelen; 130 name.type = xfs_dir2_data_get_ftype(mp, dep); 131 132 error = dirent_fn(sc, dp, dapos, &name, ino, priv); 133 if (error) 134 break; 135 } 136 137 xfs_trans_brelse(sc->tp, bp); 138 return error; 139 } 140 141 /* Read a leaf-format directory buffer. */ 142 STATIC int 143 xchk_read_leaf_dir_buf( 144 struct xfs_trans *tp, 145 struct xfs_inode *dp, 146 struct xfs_da_geometry *geo, 147 xfs_dir2_off_t *curoff, 148 struct xfs_buf **bpp) 149 { 150 struct xfs_iext_cursor icur; 151 struct xfs_bmbt_irec map; 152 struct xfs_ifork *ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK); 153 xfs_dablk_t last_da; 154 xfs_dablk_t map_off; 155 xfs_dir2_off_t new_off; 156 157 *bpp = NULL; 158 159 /* 160 * Look for mapped directory blocks at or above the current offset. 161 * Truncate down to the nearest directory block to start the scanning 162 * operation. 163 */ 164 last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET); 165 map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *curoff)); 166 167 if (!xfs_iext_lookup_extent(dp, ifp, map_off, &icur, &map)) 168 return 0; 169 if (map.br_startoff >= last_da) 170 return 0; 171 xfs_trim_extent(&map, map_off, last_da - map_off); 172 173 /* Read the directory block of that first mapping. */ 174 new_off = xfs_dir2_da_to_byte(geo, map.br_startoff); 175 if (new_off > *curoff) 176 *curoff = new_off; 177 178 return xfs_dir3_data_read(tp, dp, map.br_startoff, 0, bpp); 179 } 180 181 /* Call a function for every entry in a leaf directory. */ 182 STATIC int 183 xchk_dir_walk_leaf( 184 struct xfs_scrub *sc, 185 struct xfs_inode *dp, 186 xchk_dirent_fn dirent_fn, 187 void *priv) 188 { 189 struct xfs_mount *mp = dp->i_mount; 190 struct xfs_da_geometry *geo = mp->m_dir_geo; 191 struct xfs_buf *bp = NULL; 192 xfs_dir2_off_t curoff = 0; 193 unsigned int offset = 0; 194 int error; 195 196 /* Iterate every directory offset in this directory. */ 197 while (curoff < XFS_DIR2_LEAF_OFFSET) { 198 struct xfs_name name = { }; 199 struct xfs_dir2_data_unused *dup; 200 struct xfs_dir2_data_entry *dep; 201 xfs_ino_t ino; 202 unsigned int length; 203 xfs_dir2_dataptr_t dapos; 204 205 /* 206 * If we have no buffer, or we're off the end of the 207 * current buffer, need to get another one. 208 */ 209 if (!bp || offset >= geo->blksize) { 210 if (bp) { 211 xfs_trans_brelse(sc->tp, bp); 212 bp = NULL; 213 } 214 215 error = xchk_read_leaf_dir_buf(sc->tp, dp, geo, &curoff, 216 &bp); 217 if (error || !bp) 218 break; 219 220 /* 221 * Find our position in the block. 222 */ 223 offset = geo->data_entry_offset; 224 curoff += geo->data_entry_offset; 225 } 226 227 /* Skip an empty entry. */ 228 dup = bp->b_addr + offset; 229 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { 230 length = be16_to_cpu(dup->length); 231 offset += length; 232 curoff += length; 233 continue; 234 } 235 236 /* Otherwise, find the next entry and report it. */ 237 dep = bp->b_addr + offset; 238 length = xfs_dir2_data_entsize(mp, dep->namelen); 239 240 dapos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff; 241 ino = be64_to_cpu(dep->inumber); 242 name.name = dep->name; 243 name.len = dep->namelen; 244 name.type = xfs_dir2_data_get_ftype(mp, dep); 245 246 error = dirent_fn(sc, dp, dapos, &name, ino, priv); 247 if (error) 248 break; 249 250 /* Advance to the next entry. */ 251 offset += length; 252 curoff += length; 253 } 254 255 if (bp) 256 xfs_trans_brelse(sc->tp, bp); 257 return error; 258 } 259 260 /* 261 * Call a function for every entry in a directory. 262 * 263 * Callers must hold the ILOCK. File types are XFS_DIR3_FT_*. 264 */ 265 int 266 xchk_dir_walk( 267 struct xfs_scrub *sc, 268 struct xfs_inode *dp, 269 xchk_dirent_fn dirent_fn, 270 void *priv) 271 { 272 struct xfs_da_args args = { 273 .dp = dp, 274 .geo = dp->i_mount->m_dir_geo, 275 .trans = sc->tp, 276 }; 277 bool isblock; 278 int error; 279 280 if (xfs_is_shutdown(dp->i_mount)) 281 return -EIO; 282 283 ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); 284 xfs_assert_ilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL); 285 286 if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) 287 return xchk_dir_walk_sf(sc, dp, dirent_fn, priv); 288 289 /* dir2 functions require that the data fork is loaded */ 290 error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK); 291 if (error) 292 return error; 293 294 error = xfs_dir2_isblock(&args, &isblock); 295 if (error) 296 return error; 297 298 if (isblock) 299 return xchk_dir_walk_block(sc, dp, dirent_fn, priv); 300 301 return xchk_dir_walk_leaf(sc, dp, dirent_fn, priv); 302 } 303 304 /* 305 * Look up the inode number for an exact name in a directory. 306 * 307 * Callers must hold the ILOCK. File types are XFS_DIR3_FT_*. Names are not 308 * checked for correctness. 309 */ 310 int 311 xchk_dir_lookup( 312 struct xfs_scrub *sc, 313 struct xfs_inode *dp, 314 const struct xfs_name *name, 315 xfs_ino_t *ino) 316 { 317 struct xfs_da_args args = { 318 .dp = dp, 319 .geo = dp->i_mount->m_dir_geo, 320 .trans = sc->tp, 321 .name = name->name, 322 .namelen = name->len, 323 .filetype = name->type, 324 .hashval = xfs_dir2_hashname(dp->i_mount, name), 325 .whichfork = XFS_DATA_FORK, 326 .op_flags = XFS_DA_OP_OKNOENT, 327 }; 328 bool isblock, isleaf; 329 int error; 330 331 if (xfs_is_shutdown(dp->i_mount)) 332 return -EIO; 333 334 ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); 335 xfs_assert_ilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL); 336 337 if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) { 338 error = xfs_dir2_sf_lookup(&args); 339 goto out_check_rval; 340 } 341 342 /* dir2 functions require that the data fork is loaded */ 343 error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK); 344 if (error) 345 return error; 346 347 error = xfs_dir2_isblock(&args, &isblock); 348 if (error) 349 return error; 350 351 if (isblock) { 352 error = xfs_dir2_block_lookup(&args); 353 goto out_check_rval; 354 } 355 356 error = xfs_dir2_isleaf(&args, &isleaf); 357 if (error) 358 return error; 359 360 if (isleaf) { 361 error = xfs_dir2_leaf_lookup(&args); 362 goto out_check_rval; 363 } 364 365 error = xfs_dir2_node_lookup(&args); 366 367 out_check_rval: 368 if (error == -EEXIST) 369 error = 0; 370 if (!error) 371 *ino = args.inumber; 372 return error; 373 } 374