321 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (c) 2022-2024 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_da_format.h"15#include "xfs_da_btree.h"16#include "xfs_attr.h"17#include "xfs_attr_leaf.h"18#include "xfs_attr_sf.h"19#include "xfs_trans.h"20#include "scrub/scrub.h"21#include "scrub/bitmap.h"22#include "scrub/dab_bitmap.h"23#include "scrub/listxattr.h"24 25/* Call a function for every entry in a shortform xattr structure. */26STATIC int27xchk_xattr_walk_sf(28 struct xfs_scrub *sc,29 struct xfs_inode *ip,30 xchk_xattr_fn attr_fn,31 void *priv)32{33 struct xfs_attr_sf_hdr *hdr = ip->i_af.if_data;34 struct xfs_attr_sf_entry *sfe;35 unsigned int i;36 int error;37 38 sfe = xfs_attr_sf_firstentry(hdr);39 for (i = 0; i < hdr->count; i++) {40 error = attr_fn(sc, ip, sfe->flags, sfe->nameval, sfe->namelen,41 &sfe->nameval[sfe->namelen], sfe->valuelen,42 priv);43 if (error)44 return error;45 46 sfe = xfs_attr_sf_nextentry(sfe);47 }48 49 return 0;50}51 52/* Call a function for every entry in this xattr leaf block. */53STATIC int54xchk_xattr_walk_leaf_entries(55 struct xfs_scrub *sc,56 struct xfs_inode *ip,57 xchk_xattr_fn attr_fn,58 struct xfs_buf *bp,59 void *priv)60{61 struct xfs_attr3_icleaf_hdr ichdr;62 struct xfs_mount *mp = sc->mp;63 struct xfs_attr_leafblock *leaf = bp->b_addr;64 struct xfs_attr_leaf_entry *entry;65 unsigned int i;66 int error;67 68 xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr, leaf);69 entry = xfs_attr3_leaf_entryp(leaf);70 71 for (i = 0; i < ichdr.count; entry++, i++) {72 void *value;73 unsigned char *name;74 unsigned int namelen, valuelen;75 76 if (entry->flags & XFS_ATTR_LOCAL) {77 struct xfs_attr_leaf_name_local *name_loc;78 79 name_loc = xfs_attr3_leaf_name_local(leaf, i);80 name = name_loc->nameval;81 namelen = name_loc->namelen;82 value = &name_loc->nameval[name_loc->namelen];83 valuelen = be16_to_cpu(name_loc->valuelen);84 } else {85 struct xfs_attr_leaf_name_remote *name_rmt;86 87 name_rmt = xfs_attr3_leaf_name_remote(leaf, i);88 name = name_rmt->name;89 namelen = name_rmt->namelen;90 value = NULL;91 valuelen = be32_to_cpu(name_rmt->valuelen);92 }93 94 error = attr_fn(sc, ip, entry->flags, name, namelen, value,95 valuelen, priv);96 if (error)97 return error;98 99 }100 101 return 0;102}103 104/*105 * Call a function for every entry in a leaf-format xattr structure. Avoid106 * memory allocations for the loop detector since there's only one block.107 */108STATIC int109xchk_xattr_walk_leaf(110 struct xfs_scrub *sc,111 struct xfs_inode *ip,112 xchk_xattr_fn attr_fn,113 void *priv)114{115 struct xfs_buf *leaf_bp;116 int error;117 118 error = xfs_attr3_leaf_read(sc->tp, ip, ip->i_ino, 0, &leaf_bp);119 if (error)120 return error;121 122 error = xchk_xattr_walk_leaf_entries(sc, ip, attr_fn, leaf_bp, priv);123 xfs_trans_brelse(sc->tp, leaf_bp);124 return error;125}126 127/* Find the leftmost leaf in the xattr dabtree. */128STATIC int129xchk_xattr_find_leftmost_leaf(130 struct xfs_scrub *sc,131 struct xfs_inode *ip,132 struct xdab_bitmap *seen_dablks,133 struct xfs_buf **leaf_bpp)134{135 struct xfs_da3_icnode_hdr nodehdr;136 struct xfs_mount *mp = sc->mp;137 struct xfs_trans *tp = sc->tp;138 struct xfs_da_intnode *node;139 struct xfs_da_node_entry *btree;140 struct xfs_buf *bp;141 xfs_failaddr_t fa;142 xfs_dablk_t blkno = 0;143 unsigned int expected_level = 0;144 int error;145 146 for (;;) {147 xfs_extlen_t len = 1;148 uint16_t magic;149 150 /* Make sure we haven't seen this new block already. */151 if (xdab_bitmap_test(seen_dablks, blkno, &len))152 return -EFSCORRUPTED;153 154 error = xfs_da3_node_read(tp, ip, blkno, &bp, XFS_ATTR_FORK);155 if (error)156 return error;157 158 node = bp->b_addr;159 magic = be16_to_cpu(node->hdr.info.magic);160 if (magic == XFS_ATTR_LEAF_MAGIC ||161 magic == XFS_ATTR3_LEAF_MAGIC)162 break;163 164 error = -EFSCORRUPTED;165 if (magic != XFS_DA_NODE_MAGIC &&166 magic != XFS_DA3_NODE_MAGIC)167 goto out_buf;168 169 fa = xfs_da3_node_header_check(bp, ip->i_ino);170 if (fa)171 goto out_buf;172 173 xfs_da3_node_hdr_from_disk(mp, &nodehdr, node);174 175 if (nodehdr.count == 0 || nodehdr.level >= XFS_DA_NODE_MAXDEPTH)176 goto out_buf;177 178 /* Check the level from the root node. */179 if (blkno == 0)180 expected_level = nodehdr.level - 1;181 else if (expected_level != nodehdr.level)182 goto out_buf;183 else184 expected_level--;185 186 /* Remember that we've seen this node. */187 error = xdab_bitmap_set(seen_dablks, blkno, 1);188 if (error)189 goto out_buf;190 191 /* Find the next level towards the leaves of the dabtree. */192 btree = nodehdr.btree;193 blkno = be32_to_cpu(btree->before);194 xfs_trans_brelse(tp, bp);195 }196 197 error = -EFSCORRUPTED;198 fa = xfs_attr3_leaf_header_check(bp, ip->i_ino);199 if (fa)200 goto out_buf;201 202 if (expected_level != 0)203 goto out_buf;204 205 /* Remember that we've seen this leaf. */206 error = xdab_bitmap_set(seen_dablks, blkno, 1);207 if (error)208 goto out_buf;209 210 *leaf_bpp = bp;211 return 0;212 213out_buf:214 xfs_trans_brelse(tp, bp);215 return error;216}217 218/* Call a function for every entry in a node-format xattr structure. */219STATIC int220xchk_xattr_walk_node(221 struct xfs_scrub *sc,222 struct xfs_inode *ip,223 xchk_xattr_fn attr_fn,224 xchk_xattrleaf_fn leaf_fn,225 void *priv)226{227 struct xfs_attr3_icleaf_hdr leafhdr;228 struct xdab_bitmap seen_dablks;229 struct xfs_mount *mp = sc->mp;230 struct xfs_attr_leafblock *leaf;231 struct xfs_buf *leaf_bp;232 int error;233 234 xdab_bitmap_init(&seen_dablks);235 236 error = xchk_xattr_find_leftmost_leaf(sc, ip, &seen_dablks, &leaf_bp);237 if (error)238 goto out_bitmap;239 240 for (;;) {241 xfs_extlen_t len;242 243 error = xchk_xattr_walk_leaf_entries(sc, ip, attr_fn, leaf_bp,244 priv);245 if (error)246 goto out_leaf;247 248 /* Find the right sibling of this leaf block. */249 leaf = leaf_bp->b_addr;250 xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);251 if (leafhdr.forw == 0)252 goto out_leaf;253 254 xfs_trans_brelse(sc->tp, leaf_bp);255 256 if (leaf_fn) {257 error = leaf_fn(sc, priv);258 if (error)259 goto out_bitmap;260 }261 262 /* Make sure we haven't seen this new leaf already. */263 len = 1;264 if (xdab_bitmap_test(&seen_dablks, leafhdr.forw, &len)) {265 error = -EFSCORRUPTED;266 goto out_bitmap;267 }268 269 error = xfs_attr3_leaf_read(sc->tp, ip, ip->i_ino,270 leafhdr.forw, &leaf_bp);271 if (error)272 goto out_bitmap;273 274 /* Remember that we've seen this new leaf. */275 error = xdab_bitmap_set(&seen_dablks, leafhdr.forw, 1);276 if (error)277 goto out_leaf;278 }279 280out_leaf:281 xfs_trans_brelse(sc->tp, leaf_bp);282out_bitmap:283 xdab_bitmap_destroy(&seen_dablks);284 return error;285}286 287/*288 * Call a function for every extended attribute in a file.289 *290 * Callers must hold the ILOCK. No validation or cursor restarts allowed.291 * Returns -EFSCORRUPTED on any problem, including loops in the dabtree.292 */293int294xchk_xattr_walk(295 struct xfs_scrub *sc,296 struct xfs_inode *ip,297 xchk_xattr_fn attr_fn,298 xchk_xattrleaf_fn leaf_fn,299 void *priv)300{301 int error;302 303 xfs_assert_ilocked(ip, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL);304 305 if (!xfs_inode_hasattr(ip))306 return 0;307 308 if (ip->i_af.if_format == XFS_DINODE_FMT_LOCAL)309 return xchk_xattr_walk_sf(sc, ip, attr_fn, priv);310 311 /* attr functions require that the attr fork is loaded */312 error = xfs_iread_extents(sc->tp, ip, XFS_ATTR_FORK);313 if (error)314 return error;315 316 if (xfs_attr_is_leaf(ip))317 return xchk_xattr_walk_leaf(sc, ip, attr_fn, priv);318 319 return xchk_xattr_walk_node(sc, ip, attr_fn, leaf_fn, priv);320}321