brintos

brintos / linux-shallow public Read only

0
0
Text · 22.4 KiB · 91e7b51 Raw
938 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_icache.h"16#include "xfs_dir2.h"17#include "xfs_dir2_priv.h"18#include "xfs_attr.h"19#include "xfs_parent.h"20#include "scrub/scrub.h"21#include "scrub/common.h"22#include "scrub/readdir.h"23#include "scrub/tempfile.h"24#include "scrub/repair.h"25#include "scrub/listxattr.h"26#include "scrub/xfile.h"27#include "scrub/xfarray.h"28#include "scrub/xfblob.h"29#include "scrub/trace.h"30 31/* Set us up to scrub parents. */32int33xchk_setup_parent(34	struct xfs_scrub	*sc)35{36	int			error;37 38	if (xchk_could_repair(sc)) {39		error = xrep_setup_parent(sc);40		if (error)41			return error;42	}43 44	return xchk_setup_inode_contents(sc, 0);45}46 47/* Parent pointers */48 49/* Look for an entry in a parent pointing to this inode. */50 51struct xchk_parent_ctx {52	struct xfs_scrub	*sc;53	xfs_nlink_t		nlink;54};55 56/* Look for a single entry in a directory pointing to an inode. */57STATIC int58xchk_parent_actor(59	struct xfs_scrub	*sc,60	struct xfs_inode	*dp,61	xfs_dir2_dataptr_t	dapos,62	const struct xfs_name	*name,63	xfs_ino_t		ino,64	void			*priv)65{66	struct xchk_parent_ctx	*spc = priv;67	int			error = 0;68 69	/* Does this name make sense? */70	if (!xfs_dir2_namecheck(name->name, name->len))71		error = -EFSCORRUPTED;72	if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))73		return error;74 75	if (sc->ip->i_ino == ino)76		spc->nlink++;77 78	if (xchk_should_terminate(spc->sc, &error))79		return error;80 81	return 0;82}83 84/*85 * Try to lock a parent directory for checking dirents.  Returns the inode86 * flags for the locks we now hold, or zero if we failed.87 */88STATIC unsigned int89xchk_parent_ilock_dir(90	struct xfs_inode	*dp)91{92	if (!xfs_ilock_nowait(dp, XFS_ILOCK_SHARED))93		return 0;94 95	if (!xfs_need_iread_extents(&dp->i_df))96		return XFS_ILOCK_SHARED;97 98	xfs_iunlock(dp, XFS_ILOCK_SHARED);99 100	if (!xfs_ilock_nowait(dp, XFS_ILOCK_EXCL))101		return 0;102 103	return XFS_ILOCK_EXCL;104}105 106/*107 * Given the inode number of the alleged parent of the inode being scrubbed,108 * try to validate that the parent has exactly one directory entry pointing109 * back to the inode being scrubbed.  Returns -EAGAIN if we need to revalidate110 * the dotdot entry.111 */112STATIC int113xchk_parent_validate(114	struct xfs_scrub	*sc,115	xfs_ino_t		parent_ino)116{117	struct xchk_parent_ctx	spc = {118		.sc		= sc,119		.nlink		= 0,120	};121	struct xfs_mount	*mp = sc->mp;122	struct xfs_inode	*dp = NULL;123	xfs_nlink_t		expected_nlink;124	unsigned int		lock_mode;125	int			error = 0;126 127	/* Is this the root dir?  Then '..' must point to itself. */128	if (sc->ip == mp->m_rootip) {129		if (sc->ip->i_ino != mp->m_sb.sb_rootino ||130		    sc->ip->i_ino != parent_ino)131			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);132		return 0;133	}134 135	/* '..' must not point to ourselves. */136	if (sc->ip->i_ino == parent_ino) {137		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);138		return 0;139	}140 141	/*142	 * If we're an unlinked directory, the parent /won't/ have a link143	 * to us.  Otherwise, it should have one link.144	 */145	expected_nlink = VFS_I(sc->ip)->i_nlink == 0 ? 0 : 1;146 147	/*148	 * Grab the parent directory inode.  This must be released before we149	 * cancel the scrub transaction.150	 *151	 * If _iget returns -EINVAL or -ENOENT then the parent inode number is152	 * garbage and the directory is corrupt.  If the _iget returns153	 * -EFSCORRUPTED or -EFSBADCRC then the parent is corrupt which is a154	 *  cross referencing error.  Any other error is an operational error.155	 */156	error = xchk_iget(sc, parent_ino, &dp);157	if (error == -EINVAL || error == -ENOENT) {158		error = -EFSCORRUPTED;159		xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error);160		return error;161	}162	if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))163		return error;164	if (dp == sc->ip || xrep_is_tempfile(dp) ||165	    !S_ISDIR(VFS_I(dp)->i_mode)) {166		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);167		goto out_rele;168	}169 170	lock_mode = xchk_parent_ilock_dir(dp);171	if (!lock_mode) {172		xchk_iunlock(sc, XFS_ILOCK_EXCL);173		xchk_ilock(sc, XFS_ILOCK_EXCL);174		error = -EAGAIN;175		goto out_rele;176	}177 178	/*179	 * We cannot yet validate this parent pointer if the directory looks as180	 * though it has been zapped by the inode record repair code.181	 */182	if (xchk_dir_looks_zapped(dp)) {183		error = -EBUSY;184		xchk_set_incomplete(sc);185		goto out_unlock;186	}187 188	/* Look for a directory entry in the parent pointing to the child. */189	error = xchk_dir_walk(sc, dp, xchk_parent_actor, &spc);190	if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))191		goto out_unlock;192 193	/*194	 * Ensure that the parent has as many links to the child as the child195	 * thinks it has to the parent.196	 */197	if (spc.nlink != expected_nlink)198		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);199 200out_unlock:201	xfs_iunlock(dp, lock_mode);202out_rele:203	xchk_irele(sc, dp);204	return error;205}206 207/*208 * Checking of Parent Pointers209 * ===========================210 *211 * On filesystems with directory parent pointers, we check the referential212 * integrity by visiting each parent pointer of a child file and checking that213 * the directory referenced by the pointer actually has a dirent pointing214 * forward to the child file.215 */216 217/* Deferred parent pointer entry that we saved for later. */218struct xchk_pptr {219	/* Cookie for retrieval of the pptr name. */220	xfblob_cookie		name_cookie;221 222	/* Parent pointer record. */223	struct xfs_parent_rec	pptr_rec;224 225	/* Length of the pptr name. */226	uint8_t			namelen;227};228 229struct xchk_pptrs {230	struct xfs_scrub	*sc;231 232	/* How many parent pointers did we find at the end? */233	unsigned long long	pptrs_found;234 235	/* Parent of this directory. */236	xfs_ino_t		parent_ino;237 238	/* Fixed-size array of xchk_pptr structures. */239	struct xfarray		*pptr_entries;240 241	/* Blobs containing parent pointer names. */242	struct xfblob		*pptr_names;243 244	/* Scratch buffer for scanning pptr xattrs */245	struct xfs_da_args	pptr_args;246 247	/* If we've cycled the ILOCK, we must revalidate all deferred pptrs. */248	bool			need_revalidate;249 250	/* Name buffer */251	struct xfs_name		xname;252	char			namebuf[MAXNAMELEN];253};254 255/* Does this parent pointer match the dotdot entry? */256STATIC int257xchk_parent_scan_dotdot(258	struct xfs_scrub		*sc,259	struct xfs_inode		*ip,260	unsigned int			attr_flags,261	const unsigned char		*name,262	unsigned int			namelen,263	const void			*value,264	unsigned int			valuelen,265	void				*priv)266{267	struct xchk_pptrs		*pp = priv;268	xfs_ino_t			parent_ino;269	int				error;270 271	if (!(attr_flags & XFS_ATTR_PARENT))272		return 0;273 274	error = xfs_parent_from_attr(sc->mp, attr_flags, name, namelen, value,275			valuelen, &parent_ino, NULL);276	if (error)277		return error;278 279	if (pp->parent_ino == parent_ino)280		return -ECANCELED;281 282	return 0;283}284 285/* Look up the dotdot entry so that we can check it as we walk the pptrs. */286STATIC int287xchk_parent_pptr_and_dotdot(288	struct xchk_pptrs	*pp)289{290	struct xfs_scrub	*sc = pp->sc;291	int			error;292 293	/* Look up '..' */294	error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &pp->parent_ino);295	if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))296		return error;297	if (!xfs_verify_dir_ino(sc->mp, pp->parent_ino)) {298		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);299		return 0;300	}301 302	/* Is this the root dir?  Then '..' must point to itself. */303	if (sc->ip == sc->mp->m_rootip) {304		if (sc->ip->i_ino != pp->parent_ino)305			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);306		return 0;307	}308 309	/*310	 * If this is now an unlinked directory, the dotdot value is311	 * meaningless as long as it points to a valid inode.312	 */313	if (VFS_I(sc->ip)->i_nlink == 0)314		return 0;315 316	if (pp->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)317		return 0;318 319	/* Otherwise, walk the pptrs again, and check. */320	error = xchk_xattr_walk(sc, sc->ip, xchk_parent_scan_dotdot, NULL, pp);321	if (error == -ECANCELED) {322		/* Found a parent pointer that matches dotdot. */323		return 0;324	}325	if (!error || error == -EFSCORRUPTED) {326		/* Found a broken parent pointer or no match. */327		xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);328		return 0;329	}330	return error;331}332 333/*334 * Try to lock a parent directory for checking dirents.  Returns the inode335 * flags for the locks we now hold, or zero if we failed.336 */337STATIC unsigned int338xchk_parent_lock_dir(339	struct xfs_scrub	*sc,340	struct xfs_inode	*dp)341{342	if (!xfs_ilock_nowait(dp, XFS_IOLOCK_SHARED))343		return 0;344 345	if (!xfs_ilock_nowait(dp, XFS_ILOCK_SHARED)) {346		xfs_iunlock(dp, XFS_IOLOCK_SHARED);347		return 0;348	}349 350	if (!xfs_need_iread_extents(&dp->i_df))351		return XFS_IOLOCK_SHARED | XFS_ILOCK_SHARED;352 353	xfs_iunlock(dp, XFS_ILOCK_SHARED);354 355	if (!xfs_ilock_nowait(dp, XFS_ILOCK_EXCL)) {356		xfs_iunlock(dp, XFS_IOLOCK_SHARED);357		return 0;358	}359 360	return XFS_IOLOCK_SHARED | XFS_ILOCK_EXCL;361}362 363/* Check the forward link (dirent) associated with this parent pointer. */364STATIC int365xchk_parent_dirent(366	struct xchk_pptrs	*pp,367	const struct xfs_name	*xname,368	struct xfs_inode	*dp)369{370	struct xfs_scrub	*sc = pp->sc;371	xfs_ino_t		child_ino;372	int			error;373 374	/*375	 * Use the name attached to this parent pointer to look up the376	 * directory entry in the alleged parent.377	 */378	error = xchk_dir_lookup(sc, dp, xname, &child_ino);379	if (error == -ENOENT) {380		xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);381		return 0;382	}383	if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &error))384		return error;385 386	/* Does the inode number match? */387	if (child_ino != sc->ip->i_ino) {388		xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);389		return 0;390	}391 392	return 0;393}394 395/* Try to grab a parent directory. */396STATIC int397xchk_parent_iget(398	struct xchk_pptrs	*pp,399	const struct xfs_parent_rec	*pptr,400	struct xfs_inode	**dpp)401{402	struct xfs_scrub	*sc = pp->sc;403	struct xfs_inode	*ip;404	xfs_ino_t		parent_ino = be64_to_cpu(pptr->p_ino);405	int			error;406 407	/* Validate inode number. */408	error = xfs_dir_ino_validate(sc->mp, parent_ino);409	if (error) {410		xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);411		return -ECANCELED;412	}413 414	error = xchk_iget(sc, parent_ino, &ip);415	if (error == -EINVAL || error == -ENOENT) {416		xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);417		return -ECANCELED;418	}419	if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &error))420		return error;421 422	/* The parent must be a directory. */423	if (!S_ISDIR(VFS_I(ip)->i_mode)) {424		xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);425		goto out_rele;426	}427 428	/* Validate generation number. */429	if (VFS_I(ip)->i_generation != be32_to_cpu(pptr->p_gen)) {430		xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);431		goto out_rele;432	}433 434	*dpp = ip;435	return 0;436out_rele:437	xchk_irele(sc, ip);438	return 0;439}440 441/*442 * Walk an xattr of a file.  If this xattr is a parent pointer, follow it up443 * to a parent directory and check that the parent has a dirent pointing back444 * to us.445 */446STATIC int447xchk_parent_scan_attr(448	struct xfs_scrub	*sc,449	struct xfs_inode	*ip,450	unsigned int		attr_flags,451	const unsigned char	*name,452	unsigned int		namelen,453	const void		*value,454	unsigned int		valuelen,455	void			*priv)456{457	struct xfs_name		xname = {458		.name		= name,459		.len		= namelen,460	};461	struct xchk_pptrs	*pp = priv;462	struct xfs_inode	*dp = NULL;463	const struct xfs_parent_rec *pptr_rec = value;464	xfs_ino_t		parent_ino;465	unsigned int		lockmode;466	int			error;467 468	if (!(attr_flags & XFS_ATTR_PARENT))469		return 0;470 471	error = xfs_parent_from_attr(sc->mp, attr_flags, name, namelen, value,472			valuelen, &parent_ino, NULL);473	if (error) {474		xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);475		return error;476	}477 478	/* No self-referential parent pointers. */479	if (parent_ino == sc->ip->i_ino) {480		xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);481		return -ECANCELED;482	}483 484	pp->pptrs_found++;485 486	error = xchk_parent_iget(pp, pptr_rec, &dp);487	if (error)488		return error;489	if (!dp)490		return 0;491 492	/* Try to lock the inode. */493	lockmode = xchk_parent_lock_dir(sc, dp);494	if (!lockmode) {495		struct xchk_pptr	save_pp = {496			.pptr_rec	= *pptr_rec, /* struct copy */497			.namelen	= namelen,498		};499 500		/* Couldn't lock the inode, so save the pptr for later. */501		trace_xchk_parent_defer(sc->ip, &xname, dp->i_ino);502 503		error = xfblob_storename(pp->pptr_names, &save_pp.name_cookie,504				&xname);505		if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0,506					&error))507			goto out_rele;508 509		error = xfarray_append(pp->pptr_entries, &save_pp);510		if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0,511					&error))512			goto out_rele;513 514		goto out_rele;515	}516 517	error = xchk_parent_dirent(pp, &xname, dp);518	if (error)519		goto out_unlock;520 521out_unlock:522	xfs_iunlock(dp, lockmode);523out_rele:524	xchk_irele(sc, dp);525	return error;526}527 528/*529 * Revalidate a parent pointer that we collected in the past but couldn't check530 * because of lock contention.  Returns 0 if the parent pointer is still valid,531 * -ENOENT if it has gone away on us, or a negative errno.532 */533STATIC int534xchk_parent_revalidate_pptr(535	struct xchk_pptrs		*pp,536	const struct xfs_name		*xname,537	struct xfs_parent_rec		*pptr)538{539	struct xfs_scrub		*sc = pp->sc;540	int				error;541 542	error = xfs_parent_lookup(sc->tp, sc->ip, xname, pptr, &pp->pptr_args);543	if (error == -ENOATTR) {544		/* Parent pointer went away, nothing to revalidate. */545		return -ENOENT;546	}547 548	return error;549}550 551/*552 * Check a parent pointer the slow way, which means we cycle locks a bunch553 * and put up with revalidation until we get it done.554 */555STATIC int556xchk_parent_slow_pptr(557	struct xchk_pptrs	*pp,558	const struct xfs_name	*xname,559	struct xfs_parent_rec	*pptr)560{561	struct xfs_scrub	*sc = pp->sc;562	struct xfs_inode	*dp = NULL;563	unsigned int		lockmode;564	int			error;565 566	/* Check that the deferred parent pointer still exists. */567	if (pp->need_revalidate) {568		error = xchk_parent_revalidate_pptr(pp, xname, pptr);569		if (error == -ENOENT)570			return 0;571		if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0,572					&error))573			return error;574	}575 576	error = xchk_parent_iget(pp, pptr, &dp);577	if (error)578		return error;579	if (!dp)580		return 0;581 582	/*583	 * If we can grab both IOLOCK and ILOCK of the alleged parent, we584	 * can proceed with the validation.585	 */586	lockmode = xchk_parent_lock_dir(sc, dp);587	if (lockmode) {588		trace_xchk_parent_slowpath(sc->ip, xname, dp->i_ino);589		goto check_dirent;590	}591 592	/*593	 * We couldn't lock the parent dir.  Drop all the locks and try to594	 * get them again, one at a time.595	 */596	xchk_iunlock(sc, sc->ilock_flags);597	pp->need_revalidate = true;598 599	trace_xchk_parent_ultraslowpath(sc->ip, xname, dp->i_ino);600 601	error = xchk_dir_trylock_for_pptrs(sc, dp, &lockmode);602	if (error)603		goto out_rele;604 605	/* Revalidate the parent pointer now that we cycled locks. */606	error = xchk_parent_revalidate_pptr(pp, xname, pptr);607	if (error == -ENOENT) {608		error = 0;609		goto out_unlock;610	}611	if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &error))612		goto out_unlock;613 614check_dirent:615	error = xchk_parent_dirent(pp, xname, dp);616out_unlock:617	xfs_iunlock(dp, lockmode);618out_rele:619	xchk_irele(sc, dp);620	return error;621}622 623/* Check all the parent pointers that we deferred the first time around. */624STATIC int625xchk_parent_finish_slow_pptrs(626	struct xchk_pptrs	*pp)627{628	xfarray_idx_t		array_cur;629	int			error;630 631	foreach_xfarray_idx(pp->pptr_entries, array_cur) {632		struct xchk_pptr	pptr;633 634		if (pp->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)635			return 0;636 637		error = xfarray_load(pp->pptr_entries, array_cur, &pptr);638		if (error)639			return error;640 641		error = xfblob_loadname(pp->pptr_names, pptr.name_cookie,642				&pp->xname, pptr.namelen);643		if (error)644			return error;645 646		error = xchk_parent_slow_pptr(pp, &pp->xname, &pptr.pptr_rec);647		if (error)648			return error;649	}650 651	/* Empty out both xfiles now that we've checked everything. */652	xfarray_truncate(pp->pptr_entries);653	xfblob_truncate(pp->pptr_names);654	return 0;655}656 657/* Count the number of parent pointers. */658STATIC int659xchk_parent_count_pptr(660	struct xfs_scrub		*sc,661	struct xfs_inode		*ip,662	unsigned int			attr_flags,663	const unsigned char		*name,664	unsigned int			namelen,665	const void			*value,666	unsigned int			valuelen,667	void				*priv)668{669	struct xchk_pptrs		*pp = priv;670	int				error;671 672	if (!(attr_flags & XFS_ATTR_PARENT))673		return 0;674 675	error = xfs_parent_from_attr(sc->mp, attr_flags, name, namelen, value,676			valuelen, NULL, NULL);677	if (error)678		return error;679 680	pp->pptrs_found++;681	return 0;682}683 684/*685 * Compare the number of parent pointers to the link count.  For686 * non-directories these should be the same.  For unlinked directories the687 * count should be zero; for linked directories, it should be nonzero.688 */689STATIC int690xchk_parent_count_pptrs(691	struct xchk_pptrs	*pp)692{693	struct xfs_scrub	*sc = pp->sc;694	int			error;695 696	/*697	 * If we cycled the ILOCK while cross-checking parent pointers with698	 * dirents, then we need to recalculate the number of parent pointers.699	 */700	if (pp->need_revalidate) {701		pp->pptrs_found = 0;702		error = xchk_xattr_walk(sc, sc->ip, xchk_parent_count_pptr,703				NULL, pp);704		if (error == -EFSCORRUPTED) {705			/* Found a bad parent pointer */706			xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);707			return 0;708		}709		if (error)710			return error;711	}712 713	if (S_ISDIR(VFS_I(sc->ip)->i_mode)) {714		if (sc->ip == sc->mp->m_rootip)715			pp->pptrs_found++;716 717		if (VFS_I(sc->ip)->i_nlink == 0 && pp->pptrs_found > 0)718			xchk_ino_set_corrupt(sc, sc->ip->i_ino);719		else if (VFS_I(sc->ip)->i_nlink > 0 &&720			 pp->pptrs_found == 0)721			xchk_ino_set_corrupt(sc, sc->ip->i_ino);722	} else {723		if (VFS_I(sc->ip)->i_nlink != pp->pptrs_found)724			xchk_ino_set_corrupt(sc, sc->ip->i_ino);725	}726 727	return 0;728}729 730/* Check parent pointers of a file. */731STATIC int732xchk_parent_pptr(733	struct xfs_scrub	*sc)734{735	struct xchk_pptrs	*pp;736	char			*descr;737	int			error;738 739	pp = kvzalloc(sizeof(struct xchk_pptrs), XCHK_GFP_FLAGS);740	if (!pp)741		return -ENOMEM;742	pp->sc = sc;743	pp->xname.name = pp->namebuf;744 745	/*746	 * Set up some staging memory for parent pointers that we can't check747	 * due to locking contention.748	 */749	descr = xchk_xfile_ino_descr(sc, "slow parent pointer entries");750	error = xfarray_create(descr, 0, sizeof(struct xchk_pptr),751			&pp->pptr_entries);752	kfree(descr);753	if (error)754		goto out_pp;755 756	descr = xchk_xfile_ino_descr(sc, "slow parent pointer names");757	error = xfblob_create(descr, &pp->pptr_names);758	kfree(descr);759	if (error)760		goto out_entries;761 762	error = xchk_xattr_walk(sc, sc->ip, xchk_parent_scan_attr, NULL, pp);763	if (error == -ECANCELED) {764		error = 0;765		goto out_names;766	}767	if (error)768		goto out_names;769 770	error = xchk_parent_finish_slow_pptrs(pp);771	if (error == -ETIMEDOUT) {772		/* Couldn't grab a lock, scrub was marked incomplete */773		error = 0;774		goto out_names;775	}776	if (error)777		goto out_names;778 779	if (pp->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)780		goto out_names;781 782	/*783	 * For subdirectories, make sure the dotdot entry references the same784	 * inode as the parent pointers.785	 *786	 * If we're scanning a /consistent/ directory, there should only be787	 * one parent pointer, and it should point to the same directory as788	 * the dotdot entry.789	 *790	 * However, a corrupt directory tree might feature a subdirectory with791	 * multiple parents.  The directory loop scanner is responsible for792	 * correcting that kind of problem, so for now we only validate that793	 * the dotdot entry matches /one/ of the parents.794	 */795	if (S_ISDIR(VFS_I(sc->ip)->i_mode)) {796		error = xchk_parent_pptr_and_dotdot(pp);797		if (error)798			goto out_names;799	}800 801	if (pp->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)802		goto out_names;803 804	/*805	 * Complain if the number of parent pointers doesn't match the link806	 * count.  This could be a sign of missing parent pointers (or an807	 * incorrect link count).808	 */809	error = xchk_parent_count_pptrs(pp);810	if (error)811		goto out_names;812 813out_names:814	xfblob_destroy(pp->pptr_names);815out_entries:816	xfarray_destroy(pp->pptr_entries);817out_pp:818	kvfree(pp);819	return error;820}821 822/* Scrub a parent pointer. */823int824xchk_parent(825	struct xfs_scrub	*sc)826{827	struct xfs_mount	*mp = sc->mp;828	xfs_ino_t		parent_ino;829	int			error = 0;830 831	if (xfs_has_parent(mp))832		return xchk_parent_pptr(sc);833 834	/*835	 * If we're a directory, check that the '..' link points up to836	 * a directory that has one entry pointing to us.837	 */838	if (!S_ISDIR(VFS_I(sc->ip)->i_mode))839		return -ENOENT;840 841	/* We're not a special inode, are we? */842	if (!xfs_verify_dir_ino(mp, sc->ip->i_ino)) {843		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);844		return 0;845	}846 847	do {848		if (xchk_should_terminate(sc, &error))849			break;850 851		/* Look up '..' */852		error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot,853				&parent_ino);854		if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))855			return error;856		if (!xfs_verify_dir_ino(mp, parent_ino)) {857			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);858			return 0;859		}860 861		/*862		 * Check that the dotdot entry points to a parent directory863		 * containing a dirent pointing to this subdirectory.864		 */865		error = xchk_parent_validate(sc, parent_ino);866	} while (error == -EAGAIN);867	if (error == -EBUSY) {868		/*869		 * We could not scan a directory, so we marked the check870		 * incomplete.  No further error return is necessary.871		 */872		return 0;873	}874 875	return error;876}877 878/*879 * Decide if this file's extended attributes (and therefore its parent880 * pointers) have been zapped to satisfy the inode and ifork verifiers.881 * Checking and repairing should be postponed until the extended attribute882 * structure is fixed.883 */884bool885xchk_pptr_looks_zapped(886	struct xfs_inode	*ip)887{888	struct xfs_mount	*mp = ip->i_mount;889	struct inode		*inode = VFS_I(ip);890 891	ASSERT(xfs_has_parent(mp));892 893	/*894	 * Temporary files that cannot be linked into the directory tree do not895	 * have attr forks because they cannot ever have parents.896	 */897	if (inode->i_nlink == 0 && !(inode->i_state & I_LINKABLE))898		return false;899 900	/*901	 * Directory tree roots do not have parents, so the expected outcome902	 * of a parent pointer scan is always the empty set.  It's safe to scan903	 * them even if the attr fork was zapped.904	 */905	if (ip == mp->m_rootip)906		return false;907 908	/*909	 * Metadata inodes are all rooted in the superblock and do not have910	 * any parents.  Hence the attr fork will not be initialized, but911	 * there are no parent pointers that might have been zapped.912	 */913	if (xfs_is_metadata_inode(ip))914		return false;915 916	/*917	 * Linked and linkable non-rootdir files should always have an918	 * attribute fork because that is where parent pointers are919	 * stored.  If the fork is absent, something is amiss.920	 */921	if (!xfs_inode_has_attr_fork(ip))922		return true;923 924	/* Repair zapped this file's attr fork a short time ago */925	if (xfs_ifork_zapped(ip, XFS_ATTR_FORK))926		return true;927 928	/*929	 * If the dinode repair found a bad attr fork, it will reset the fork930	 * to extents format with zero records and wait for the bmapbta931	 * scrubber to reconstruct the block mappings.  The extended attribute932	 * structure always contain some content when parent pointers are933	 * enabled, so this is a clear sign of a zapped attr fork.934	 */935	return ip->i_af.if_format == XFS_DINODE_FMT_EXTENTS &&936	       ip->i_af.if_nextents == 0;937}938