brintos

brintos / linux-shallow public Read only

0
0
Text · 16.8 KiB · 708334f Raw
684 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2017-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_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_da_format.h"16#include "xfs_da_btree.h"17#include "xfs_attr.h"18#include "xfs_attr_leaf.h"19#include "xfs_attr_sf.h"20#include "xfs_parent.h"21#include "scrub/scrub.h"22#include "scrub/common.h"23#include "scrub/dabtree.h"24#include "scrub/attr.h"25#include "scrub/listxattr.h"26#include "scrub/repair.h"27 28/* Free the buffers linked from the xattr buffer. */29static void30xchk_xattr_buf_cleanup(31	void			*priv)32{33	struct xchk_xattr_buf	*ab = priv;34 35	kvfree(ab->freemap);36	ab->freemap = NULL;37	kvfree(ab->usedmap);38	ab->usedmap = NULL;39	kvfree(ab->value);40	ab->value = NULL;41	ab->value_sz = 0;42	kvfree(ab->name);43	ab->name = NULL;44}45 46/*47 * Allocate the free space bitmap if we're trying harder; there are leaf blocks48 * in the attr fork; or we can't tell if there are leaf blocks.49 */50static inline bool51xchk_xattr_want_freemap(52	struct xfs_scrub	*sc)53{54	struct xfs_ifork	*ifp;55 56	if (sc->flags & XCHK_TRY_HARDER)57		return true;58 59	if (!sc->ip)60		return true;61 62	ifp = xfs_ifork_ptr(sc->ip, XFS_ATTR_FORK);63	if (!ifp)64		return false;65 66	return xfs_ifork_has_extents(ifp);67}68 69/*70 * Allocate enough memory to hold an attr value and attr block bitmaps,71 * reallocating the buffer if necessary.  Buffer contents are not preserved72 * across a reallocation.73 */74int75xchk_setup_xattr_buf(76	struct xfs_scrub	*sc,77	size_t			value_size)78{79	size_t			bmp_sz;80	struct xchk_xattr_buf	*ab = sc->buf;81	void			*new_val;82 83	bmp_sz = sizeof(long) * BITS_TO_LONGS(sc->mp->m_attr_geo->blksize);84 85	if (ab)86		goto resize_value;87 88	ab = kvzalloc(sizeof(struct xchk_xattr_buf), XCHK_GFP_FLAGS);89	if (!ab)90		return -ENOMEM;91	sc->buf = ab;92	sc->buf_cleanup = xchk_xattr_buf_cleanup;93 94	ab->usedmap = kvmalloc(bmp_sz, XCHK_GFP_FLAGS);95	if (!ab->usedmap)96		return -ENOMEM;97 98	if (xchk_xattr_want_freemap(sc)) {99		ab->freemap = kvmalloc(bmp_sz, XCHK_GFP_FLAGS);100		if (!ab->freemap)101			return -ENOMEM;102	}103 104	if (xchk_could_repair(sc)) {105		ab->name = kvmalloc(XATTR_NAME_MAX + 1, XCHK_GFP_FLAGS);106		if (!ab->name)107			return -ENOMEM;108	}109 110resize_value:111	if (ab->value_sz >= value_size)112		return 0;113 114	if (ab->value) {115		kvfree(ab->value);116		ab->value = NULL;117		ab->value_sz = 0;118	}119 120	new_val = kvmalloc(value_size, XCHK_GFP_FLAGS);121	if (!new_val)122		return -ENOMEM;123 124	ab->value = new_val;125	ab->value_sz = value_size;126	return 0;127}128 129/* Set us up to scrub an inode's extended attributes. */130int131xchk_setup_xattr(132	struct xfs_scrub	*sc)133{134	int			error;135 136	if (xchk_could_repair(sc)) {137		error = xrep_setup_xattr(sc);138		if (error)139			return error;140	}141 142	/*143	 * We failed to get memory while checking attrs, so this time try to144	 * get all the memory we're ever going to need.  Allocate the buffer145	 * without the inode lock held, which means we can sleep.146	 */147	if (sc->flags & XCHK_TRY_HARDER) {148		error = xchk_setup_xattr_buf(sc, XATTR_SIZE_MAX);149		if (error)150			return error;151	}152 153	return xchk_setup_inode_contents(sc, 0);154}155 156/* Extended Attributes */157 158/*159 * Check that an extended attribute key can be looked up by hash.160 *161 * We use the extended attribute walk helper to call this function for every162 * attribute key in an inode.  Once we're here, we load the attribute value to163 * see if any errors happen, or if we get more or less data than we expected.164 */165static int166xchk_xattr_actor(167	struct xfs_scrub	*sc,168	struct xfs_inode	*ip,169	unsigned int		attr_flags,170	const unsigned char	*name,171	unsigned int		namelen,172	const void		*value,173	unsigned int		valuelen,174	void			*priv)175{176	struct xfs_da_args		args = {177		.attr_filter		= attr_flags & XFS_ATTR_NSP_ONDISK_MASK,178		.geo			= sc->mp->m_attr_geo,179		.whichfork		= XFS_ATTR_FORK,180		.dp			= ip,181		.name			= name,182		.namelen		= namelen,183		.trans			= sc->tp,184		.valuelen		= valuelen,185		.owner			= ip->i_ino,186	};187	struct xchk_xattr_buf		*ab;188	int				error = 0;189 190	ab = sc->buf;191 192	if (xchk_should_terminate(sc, &error))193		return error;194 195	if (attr_flags & ~XFS_ATTR_ONDISK_MASK) {196		xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, args.blkno);197		return -ECANCELED;198	}199 200	if (attr_flags & XFS_ATTR_INCOMPLETE) {201		/* Incomplete attr key, just mark the inode for preening. */202		xchk_ino_set_preen(sc, ip->i_ino);203		return 0;204	}205 206	/* Does this name make sense? */207	if (!xfs_attr_namecheck(attr_flags, name, namelen)) {208		xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, args.blkno);209		return -ECANCELED;210	}211 212	/* Check parent pointer record. */213	if ((attr_flags & XFS_ATTR_PARENT) &&214	    !xfs_parent_valuecheck(sc->mp, value, valuelen)) {215		xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, args.blkno);216		return -ECANCELED;217	}218 219	/*220	 * Try to allocate enough memory to extract the attr value.  If that221	 * doesn't work, return -EDEADLOCK as a signal to try again with a222	 * maximally sized buffer.223	 */224	error = xchk_setup_xattr_buf(sc, valuelen);225	if (error == -ENOMEM)226		error = -EDEADLOCK;227	if (error)228		return error;229 230	/*231	 * Parent pointers are matched on attr name and value, so we must232	 * supply the xfs_parent_rec here when confirming that the dabtree233	 * indexing works correctly.234	 */235	if (attr_flags & XFS_ATTR_PARENT)236		memcpy(ab->value, value, valuelen);237 238	args.value = ab->value;239 240	/*241	 * Get the attr value to ensure that lookup can find this attribute242	 * through the dabtree indexing and that remote value retrieval also243	 * works correctly.244	 */245	xfs_attr_sethash(&args);246	error = xfs_attr_get_ilocked(&args);247	/* ENODATA means the hash lookup failed and the attr is bad */248	if (error == -ENODATA)249		error = -EFSCORRUPTED;250	if (!xchk_fblock_process_error(sc, XFS_ATTR_FORK, args.blkno,251			&error))252		return error;253	if (args.valuelen != valuelen)254		xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, args.blkno);255 256	return 0;257}258 259/*260 * Mark a range [start, start+len) in this map.  Returns true if the261 * region was free, and false if there's a conflict or a problem.262 *263 * Within a char, the lowest bit of the char represents the byte with264 * the smallest address265 */266bool267xchk_xattr_set_map(268	struct xfs_scrub	*sc,269	unsigned long		*map,270	unsigned int		start,271	unsigned int		len)272{273	unsigned int		mapsize = sc->mp->m_attr_geo->blksize;274	bool			ret = true;275 276	if (start >= mapsize)277		return false;278	if (start + len > mapsize) {279		len = mapsize - start;280		ret = false;281	}282 283	if (find_next_bit(map, mapsize, start) < start + len)284		ret = false;285	bitmap_set(map, start, len);286 287	return ret;288}289 290/*291 * Check the leaf freemap from the usage bitmap.  Returns false if the292 * attr freemap has problems or points to used space.293 */294STATIC bool295xchk_xattr_check_freemap(296	struct xfs_scrub		*sc,297	struct xfs_attr3_icleaf_hdr	*leafhdr)298{299	struct xchk_xattr_buf		*ab = sc->buf;300	unsigned int			mapsize = sc->mp->m_attr_geo->blksize;301	int				i;302 303	/* Construct bitmap of freemap contents. */304	bitmap_zero(ab->freemap, mapsize);305	for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) {306		if (!xchk_xattr_set_map(sc, ab->freemap,307				leafhdr->freemap[i].base,308				leafhdr->freemap[i].size))309			return false;310	}311 312	/* Look for bits that are set in freemap and are marked in use. */313	return !bitmap_intersects(ab->freemap, ab->usedmap, mapsize);314}315 316/*317 * Check this leaf entry's relations to everything else.318 * Returns the number of bytes used for the name/value data.319 */320STATIC void321xchk_xattr_entry(322	struct xchk_da_btree		*ds,323	int				level,324	char				*buf_end,325	struct xfs_attr_leafblock	*leaf,326	struct xfs_attr3_icleaf_hdr	*leafhdr,327	struct xfs_attr_leaf_entry	*ent,328	int				idx,329	unsigned int			*usedbytes,330	__u32				*last_hashval)331{332	struct xfs_mount		*mp = ds->state->mp;333	struct xchk_xattr_buf		*ab = ds->sc->buf;334	char				*name_end;335	struct xfs_attr_leaf_name_local	*lentry;336	struct xfs_attr_leaf_name_remote *rentry;337	unsigned int			nameidx;338	unsigned int			namesize;339 340	if (ent->pad2 != 0)341		xchk_da_set_corrupt(ds, level);342 343	/* Hash values in order? */344	if (be32_to_cpu(ent->hashval) < *last_hashval)345		xchk_da_set_corrupt(ds, level);346	*last_hashval = be32_to_cpu(ent->hashval);347 348	nameidx = be16_to_cpu(ent->nameidx);349	if (nameidx < leafhdr->firstused ||350	    nameidx >= mp->m_attr_geo->blksize) {351		xchk_da_set_corrupt(ds, level);352		return;353	}354 355	/* Check the name information. */356	if (ent->flags & XFS_ATTR_LOCAL) {357		lentry = xfs_attr3_leaf_name_local(leaf, idx);358		namesize = xfs_attr_leaf_entsize_local(lentry->namelen,359				be16_to_cpu(lentry->valuelen));360		name_end = (char *)lentry + namesize;361		if (lentry->namelen == 0)362			xchk_da_set_corrupt(ds, level);363	} else {364		rentry = xfs_attr3_leaf_name_remote(leaf, idx);365		namesize = xfs_attr_leaf_entsize_remote(rentry->namelen);366		name_end = (char *)rentry + namesize;367		if (rentry->namelen == 0 || rentry->valueblk == 0)368			xchk_da_set_corrupt(ds, level);369	}370	if (name_end > buf_end)371		xchk_da_set_corrupt(ds, level);372 373	if (!xchk_xattr_set_map(ds->sc, ab->usedmap, nameidx, namesize))374		xchk_da_set_corrupt(ds, level);375	if (!(ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))376		*usedbytes += namesize;377}378 379/* Scrub an attribute leaf. */380STATIC int381xchk_xattr_block(382	struct xchk_da_btree		*ds,383	int				level)384{385	struct xfs_attr3_icleaf_hdr	leafhdr;386	struct xfs_mount		*mp = ds->state->mp;387	struct xfs_da_state_blk		*blk = &ds->state->path.blk[level];388	struct xfs_buf			*bp = blk->bp;389	xfs_dablk_t			*last_checked = ds->private;390	struct xfs_attr_leafblock	*leaf = bp->b_addr;391	struct xfs_attr_leaf_entry	*ent;392	struct xfs_attr_leaf_entry	*entries;393	struct xchk_xattr_buf		*ab = ds->sc->buf;394	char				*buf_end;395	size_t				off;396	__u32				last_hashval = 0;397	unsigned int			usedbytes = 0;398	unsigned int			hdrsize;399	int				i;400 401	if (*last_checked == blk->blkno)402		return 0;403 404	*last_checked = blk->blkno;405	bitmap_zero(ab->usedmap, mp->m_attr_geo->blksize);406 407	/* Check all the padding. */408	if (xfs_has_crc(ds->sc->mp)) {409		struct xfs_attr3_leafblock	*leaf3 = bp->b_addr;410 411		if (leaf3->hdr.pad1 != 0 || leaf3->hdr.pad2 != 0 ||412		    leaf3->hdr.info.hdr.pad != 0)413			xchk_da_set_corrupt(ds, level);414	} else {415		if (leaf->hdr.pad1 != 0 || leaf->hdr.info.pad != 0)416			xchk_da_set_corrupt(ds, level);417	}418 419	/* Check the leaf header */420	xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);421	hdrsize = xfs_attr3_leaf_hdr_size(leaf);422 423	/*424	 * Empty xattr leaf blocks mapped at block 0 are probably a byproduct425	 * of a race between setxattr and a log shutdown.  Anywhere else in the426	 * attr fork is a corruption.427	 */428	if (leafhdr.count == 0) {429		if (blk->blkno == 0)430			xchk_da_set_preen(ds, level);431		else432			xchk_da_set_corrupt(ds, level);433	}434	if (leafhdr.usedbytes > mp->m_attr_geo->blksize)435		xchk_da_set_corrupt(ds, level);436	if (leafhdr.firstused > mp->m_attr_geo->blksize)437		xchk_da_set_corrupt(ds, level);438	if (leafhdr.firstused < hdrsize)439		xchk_da_set_corrupt(ds, level);440	if (!xchk_xattr_set_map(ds->sc, ab->usedmap, 0, hdrsize))441		xchk_da_set_corrupt(ds, level);442	if (leafhdr.holes)443		xchk_da_set_preen(ds, level);444 445	if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)446		goto out;447 448	entries = xfs_attr3_leaf_entryp(leaf);449	if ((char *)&entries[leafhdr.count] > (char *)leaf + leafhdr.firstused)450		xchk_da_set_corrupt(ds, level);451 452	buf_end = (char *)bp->b_addr + mp->m_attr_geo->blksize;453	for (i = 0, ent = entries; i < leafhdr.count; ent++, i++) {454		/* Mark the leaf entry itself. */455		off = (char *)ent - (char *)leaf;456		if (!xchk_xattr_set_map(ds->sc, ab->usedmap, off,457				sizeof(xfs_attr_leaf_entry_t))) {458			xchk_da_set_corrupt(ds, level);459			goto out;460		}461 462		/* Check the entry and nameval. */463		xchk_xattr_entry(ds, level, buf_end, leaf, &leafhdr,464				ent, i, &usedbytes, &last_hashval);465 466		if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)467			goto out;468	}469 470	if (!xchk_xattr_check_freemap(ds->sc, &leafhdr))471		xchk_da_set_corrupt(ds, level);472 473	if (leafhdr.usedbytes != usedbytes)474		xchk_da_set_corrupt(ds, level);475 476out:477	return 0;478}479 480/* Scrub a attribute btree record. */481STATIC int482xchk_xattr_rec(483	struct xchk_da_btree		*ds,484	int				level)485{486	struct xfs_mount		*mp = ds->state->mp;487	struct xfs_da_state_blk		*blk = &ds->state->path.blk[level];488	struct xfs_attr_leaf_name_local	*lentry;489	struct xfs_attr_leaf_name_remote	*rentry;490	struct xfs_buf			*bp;491	struct xfs_attr_leaf_entry	*ent;492	xfs_dahash_t			calc_hash;493	xfs_dahash_t			hash;494	int				nameidx;495	int				hdrsize;496	int				error;497 498	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);499 500	ent = xfs_attr3_leaf_entryp(blk->bp->b_addr) + blk->index;501 502	/* Check the whole block, if necessary. */503	error = xchk_xattr_block(ds, level);504	if (error)505		goto out;506	if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)507		goto out;508 509	/* Check the hash of the entry. */510	error = xchk_da_btree_hash(ds, level, &ent->hashval);511	if (error)512		goto out;513 514	/* Find the attr entry's location. */515	bp = blk->bp;516	hdrsize = xfs_attr3_leaf_hdr_size(bp->b_addr);517	nameidx = be16_to_cpu(ent->nameidx);518	if (nameidx < hdrsize || nameidx >= mp->m_attr_geo->blksize) {519		xchk_da_set_corrupt(ds, level);520		goto out;521	}522 523	/* Retrieve the entry and check it. */524	hash = be32_to_cpu(ent->hashval);525	if (ent->flags & ~XFS_ATTR_ONDISK_MASK) {526		xchk_da_set_corrupt(ds, level);527		return 0;528	}529	if (!xfs_attr_check_namespace(ent->flags)) {530		xchk_da_set_corrupt(ds, level);531		return 0;532	}533 534	if (ent->flags & XFS_ATTR_LOCAL) {535		lentry = (struct xfs_attr_leaf_name_local *)536				(((char *)bp->b_addr) + nameidx);537		if (lentry->namelen <= 0) {538			xchk_da_set_corrupt(ds, level);539			goto out;540		}541		calc_hash = xfs_attr_hashval(mp, ent->flags, lentry->nameval,542					     lentry->namelen,543					     lentry->nameval + lentry->namelen,544					     be16_to_cpu(lentry->valuelen));545	} else {546		rentry = (struct xfs_attr_leaf_name_remote *)547				(((char *)bp->b_addr) + nameidx);548		if (rentry->namelen <= 0) {549			xchk_da_set_corrupt(ds, level);550			goto out;551		}552		if (ent->flags & XFS_ATTR_PARENT) {553			xchk_da_set_corrupt(ds, level);554			goto out;555		}556		calc_hash = xfs_attr_hashval(mp, ent->flags, rentry->name,557					     rentry->namelen, NULL,558					     be32_to_cpu(rentry->valuelen));559	}560	if (calc_hash != hash)561		xchk_da_set_corrupt(ds, level);562 563out:564	return error;565}566 567/* Check space usage of shortform attrs. */568STATIC int569xchk_xattr_check_sf(570	struct xfs_scrub		*sc)571{572	struct xchk_xattr_buf		*ab = sc->buf;573	struct xfs_ifork		*ifp = &sc->ip->i_af;574	struct xfs_attr_sf_hdr		*sf = ifp->if_data;575	struct xfs_attr_sf_entry	*sfe = xfs_attr_sf_firstentry(sf);576	struct xfs_attr_sf_entry	*next;577	unsigned char			*end = ifp->if_data + ifp->if_bytes;578	int				i;579	int				error = 0;580 581	bitmap_zero(ab->usedmap, ifp->if_bytes);582	xchk_xattr_set_map(sc, ab->usedmap, 0, sizeof(*sf));583 584	if ((unsigned char *)sfe > end) {585		xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);586		return 0;587	}588 589	for (i = 0; i < sf->count; i++) {590		unsigned char		*name = sfe->nameval;591		unsigned char		*value = &sfe->nameval[sfe->namelen];592 593		if (xchk_should_terminate(sc, &error))594			return error;595 596		next = xfs_attr_sf_nextentry(sfe);597		if ((unsigned char *)next > end) {598			xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);599			break;600		}601 602		/*603		 * Shortform entries do not set LOCAL or INCOMPLETE, so the604		 * only valid flag bits here are for namespaces.605		 */606		if (sfe->flags & ~XFS_ATTR_NSP_ONDISK_MASK) {607			xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);608			break;609		}610 611		if (!xchk_xattr_set_map(sc, ab->usedmap,612				(char *)sfe - (char *)sf,613				sizeof(struct xfs_attr_sf_entry))) {614			xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);615			break;616		}617 618		if (!xchk_xattr_set_map(sc, ab->usedmap,619				(char *)name - (char *)sf,620				sfe->namelen)) {621			xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);622			break;623		}624 625		if (!xchk_xattr_set_map(sc, ab->usedmap,626				(char *)value - (char *)sf,627				sfe->valuelen)) {628			xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);629			break;630		}631 632		sfe = next;633	}634 635	return 0;636}637 638/* Scrub the extended attribute metadata. */639int640xchk_xattr(641	struct xfs_scrub		*sc)642{643	xfs_dablk_t			last_checked = -1U;644	int				error = 0;645 646	if (!xfs_inode_hasattr(sc->ip))647		return -ENOENT;648 649	/* Allocate memory for xattr checking. */650	error = xchk_setup_xattr_buf(sc, 0);651	if (error == -ENOMEM)652		return -EDEADLOCK;653	if (error)654		return error;655 656	/* Check the physical structure of the xattr. */657	if (sc->ip->i_af.if_format == XFS_DINODE_FMT_LOCAL)658		error = xchk_xattr_check_sf(sc);659	else660		error = xchk_da_btree(sc, XFS_ATTR_FORK, xchk_xattr_rec,661				&last_checked);662	if (error)663		return error;664 665	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)666		return 0;667 668	/*669	 * Look up every xattr in this file by name and hash.670	 *671	 * The VFS only locks i_rwsem when modifying attrs, so keep all672	 * three locks held because that's the only way to ensure we're673	 * the only thread poking into the da btree.  We traverse the da674	 * btree while holding a leaf buffer locked for the xattr name675	 * iteration, which doesn't really follow the usual buffer676	 * locking order.677	 */678	error = xchk_xattr_walk(sc, sc->ip, xchk_xattr_actor, NULL, NULL);679	if (!xchk_fblock_process_error(sc, XFS_ATTR_FORK, 0, &error))680		return error;681 682	return 0;683}684