xref: /linux/fs/xfs/scrub/metapath.c (revision 8cbd01ba9c38eb16f3a572300da486ac544519b7)
1b3c03efaSDarrick J. Wong // SPDX-License-Identifier: GPL-2.0-or-later
2b3c03efaSDarrick J. Wong /*
3b3c03efaSDarrick J. Wong  * Copyright (c) 2023-2024 Oracle.  All Rights Reserved.
4b3c03efaSDarrick J. Wong  * Author: Darrick J. Wong <djwong@kernel.org>
5b3c03efaSDarrick J. Wong  */
6b3c03efaSDarrick J. Wong #include "xfs.h"
7b3c03efaSDarrick J. Wong #include "xfs_fs.h"
8b3c03efaSDarrick J. Wong #include "xfs_shared.h"
9b3c03efaSDarrick J. Wong #include "xfs_format.h"
10b3c03efaSDarrick J. Wong #include "xfs_trans_resv.h"
11b3c03efaSDarrick J. Wong #include "xfs_mount.h"
12b3c03efaSDarrick J. Wong #include "xfs_log_format.h"
13b3c03efaSDarrick J. Wong #include "xfs_trans.h"
14b3c03efaSDarrick J. Wong #include "xfs_inode.h"
15b3c03efaSDarrick J. Wong #include "xfs_metafile.h"
16b3c03efaSDarrick J. Wong #include "xfs_quota.h"
17b3c03efaSDarrick J. Wong #include "xfs_qm.h"
18b3c03efaSDarrick J. Wong #include "xfs_dir2.h"
190d2c636eSDarrick J. Wong #include "xfs_parent.h"
200d2c636eSDarrick J. Wong #include "xfs_bmap_btree.h"
210d2c636eSDarrick J. Wong #include "xfs_trans_space.h"
220d2c636eSDarrick J. Wong #include "xfs_attr.h"
23a7492333SDarrick J. Wong #include "xfs_rtgroup.h"
24b3c03efaSDarrick J. Wong #include "scrub/scrub.h"
25b3c03efaSDarrick J. Wong #include "scrub/common.h"
26b3c03efaSDarrick J. Wong #include "scrub/trace.h"
27b3c03efaSDarrick J. Wong #include "scrub/readdir.h"
280d2c636eSDarrick J. Wong #include "scrub/repair.h"
29b3c03efaSDarrick J. Wong 
30b3c03efaSDarrick J. Wong /*
31b3c03efaSDarrick J. Wong  * Metadata Directory Tree Paths
32b3c03efaSDarrick J. Wong  * =============================
33b3c03efaSDarrick J. Wong  *
34b3c03efaSDarrick J. Wong  * A filesystem with metadir enabled expects to find metadata structures
35b3c03efaSDarrick J. Wong  * attached to files that are accessible by walking a path down the metadata
36b3c03efaSDarrick J. Wong  * directory tree.  Given the metadir path and the incore inode storing the
37b3c03efaSDarrick J. Wong  * metadata, this scrubber ensures that the ondisk metadir path points to the
38b3c03efaSDarrick J. Wong  * ondisk inode represented by the incore inode.
39b3c03efaSDarrick J. Wong  */
40b3c03efaSDarrick J. Wong 
41b3c03efaSDarrick J. Wong struct xchk_metapath {
42b3c03efaSDarrick J. Wong 	struct xfs_scrub		*sc;
43b3c03efaSDarrick J. Wong 
44b3c03efaSDarrick J. Wong 	/* Name for lookup */
45b3c03efaSDarrick J. Wong 	struct xfs_name			xname;
46b3c03efaSDarrick J. Wong 
470d2c636eSDarrick J. Wong 	/* Directory update for repairs */
480d2c636eSDarrick J. Wong 	struct xfs_dir_update		du;
490d2c636eSDarrick J. Wong 
500d2c636eSDarrick J. Wong 	/* Path down to this metadata file from the parent directory */
51b3c03efaSDarrick J. Wong 	const char			*path;
52b3c03efaSDarrick J. Wong 
53b3c03efaSDarrick J. Wong 	/* Directory parent of the metadata file. */
54b3c03efaSDarrick J. Wong 	struct xfs_inode		*dp;
55b3c03efaSDarrick J. Wong 
56b3c03efaSDarrick J. Wong 	/* Locks held on dp */
57b3c03efaSDarrick J. Wong 	unsigned int			dp_ilock_flags;
580d2c636eSDarrick J. Wong 
590d2c636eSDarrick J. Wong 	/* Transaction block reservations */
600d2c636eSDarrick J. Wong 	unsigned int			link_resblks;
610d2c636eSDarrick J. Wong 	unsigned int			unlink_resblks;
620d2c636eSDarrick J. Wong 
630d2c636eSDarrick J. Wong 	/* Parent pointer updates */
640d2c636eSDarrick J. Wong 	struct xfs_parent_args		link_ppargs;
650d2c636eSDarrick J. Wong 	struct xfs_parent_args		unlink_ppargs;
660d2c636eSDarrick J. Wong 
670d2c636eSDarrick J. Wong 	/* Scratchpads for removing links */
680d2c636eSDarrick J. Wong 	struct xfs_da_args		pptr_args;
69b3c03efaSDarrick J. Wong };
70b3c03efaSDarrick J. Wong 
71b3c03efaSDarrick J. Wong /* Release resources tracked in the buffer. */
72b3c03efaSDarrick J. Wong static inline void
xchk_metapath_cleanup(void * buf)73b3c03efaSDarrick J. Wong xchk_metapath_cleanup(
74b3c03efaSDarrick J. Wong 	void			*buf)
75b3c03efaSDarrick J. Wong {
76b3c03efaSDarrick J. Wong 	struct xchk_metapath	*mpath = buf;
77b3c03efaSDarrick J. Wong 
78b3c03efaSDarrick J. Wong 	if (mpath->dp_ilock_flags)
79b3c03efaSDarrick J. Wong 		xfs_iunlock(mpath->dp, mpath->dp_ilock_flags);
80b3c03efaSDarrick J. Wong 	kfree(mpath->path);
81b3c03efaSDarrick J. Wong }
82b3c03efaSDarrick J. Wong 
83a7492333SDarrick J. Wong /* Set up a metadir path scan.  @path must be dynamically allocated. */
84a7492333SDarrick J. Wong static inline int
xchk_setup_metapath_scan(struct xfs_scrub * sc,struct xfs_inode * dp,const char * path,struct xfs_inode * ip)85a7492333SDarrick J. Wong xchk_setup_metapath_scan(
86a7492333SDarrick J. Wong 	struct xfs_scrub	*sc,
87a7492333SDarrick J. Wong 	struct xfs_inode	*dp,
88a7492333SDarrick J. Wong 	const char		*path,
89a7492333SDarrick J. Wong 	struct xfs_inode	*ip)
90a7492333SDarrick J. Wong {
91a7492333SDarrick J. Wong 	struct xchk_metapath	*mpath;
92a7492333SDarrick J. Wong 	int			error;
93a7492333SDarrick J. Wong 
94a7492333SDarrick J. Wong 	if (!path)
95a7492333SDarrick J. Wong 		return -ENOMEM;
96a7492333SDarrick J. Wong 
97a7492333SDarrick J. Wong 	error = xchk_install_live_inode(sc, ip);
98a7492333SDarrick J. Wong 	if (error) {
99a7492333SDarrick J. Wong 		kfree(path);
100a7492333SDarrick J. Wong 		return error;
101a7492333SDarrick J. Wong 	}
102a7492333SDarrick J. Wong 
103a7492333SDarrick J. Wong 	mpath = kzalloc(sizeof(struct xchk_metapath), XCHK_GFP_FLAGS);
104a7492333SDarrick J. Wong 	if (!mpath) {
105a7492333SDarrick J. Wong 		kfree(path);
106a7492333SDarrick J. Wong 		return -ENOMEM;
107a7492333SDarrick J. Wong 	}
108a7492333SDarrick J. Wong 
109a7492333SDarrick J. Wong 	mpath->sc = sc;
110a7492333SDarrick J. Wong 	sc->buf = mpath;
111a7492333SDarrick J. Wong 	sc->buf_cleanup = xchk_metapath_cleanup;
112a7492333SDarrick J. Wong 
113a7492333SDarrick J. Wong 	mpath->dp = dp;
114a7492333SDarrick J. Wong 	mpath->path = path; /* path is now owned by mpath */
115a7492333SDarrick J. Wong 
116a7492333SDarrick J. Wong 	mpath->xname.name = mpath->path;
117a7492333SDarrick J. Wong 	mpath->xname.len = strlen(mpath->path);
118a7492333SDarrick J. Wong 	mpath->xname.type = xfs_mode_to_ftype(VFS_I(ip)->i_mode);
119a7492333SDarrick J. Wong 
120a7492333SDarrick J. Wong 	return 0;
121a7492333SDarrick J. Wong }
122a7492333SDarrick J. Wong 
123a7492333SDarrick J. Wong #ifdef CONFIG_XFS_RT
124a7492333SDarrick J. Wong /* Scan the /rtgroups directory itself. */
125a7492333SDarrick J. Wong static int
xchk_setup_metapath_rtdir(struct xfs_scrub * sc)126a7492333SDarrick J. Wong xchk_setup_metapath_rtdir(
127a7492333SDarrick J. Wong 	struct xfs_scrub	*sc)
128a7492333SDarrick J. Wong {
129a7492333SDarrick J. Wong 	if (!sc->mp->m_rtdirip)
130a7492333SDarrick J. Wong 		return -ENOENT;
131a7492333SDarrick J. Wong 
132a7492333SDarrick J. Wong 	return xchk_setup_metapath_scan(sc, sc->mp->m_metadirip,
133a7492333SDarrick J. Wong 			kasprintf(GFP_KERNEL, "rtgroups"), sc->mp->m_rtdirip);
134a7492333SDarrick J. Wong }
135a7492333SDarrick J. Wong 
136a7492333SDarrick J. Wong /* Scan a rtgroup inode under the /rtgroups directory. */
137a7492333SDarrick J. Wong static int
xchk_setup_metapath_rtginode(struct xfs_scrub * sc,enum xfs_rtg_inodes type)138a7492333SDarrick J. Wong xchk_setup_metapath_rtginode(
139a7492333SDarrick J. Wong 	struct xfs_scrub	*sc,
140a7492333SDarrick J. Wong 	enum xfs_rtg_inodes	type)
141a7492333SDarrick J. Wong {
142a7492333SDarrick J. Wong 	struct xfs_rtgroup	*rtg;
143a7492333SDarrick J. Wong 	struct xfs_inode	*ip;
144a7492333SDarrick J. Wong 	int			error;
145a7492333SDarrick J. Wong 
146a7492333SDarrick J. Wong 	rtg = xfs_rtgroup_get(sc->mp, sc->sm->sm_agno);
147a7492333SDarrick J. Wong 	if (!rtg)
148a7492333SDarrick J. Wong 		return -ENOENT;
149a7492333SDarrick J. Wong 
150a7492333SDarrick J. Wong 	ip = rtg->rtg_inodes[type];
151a7492333SDarrick J. Wong 	if (!ip) {
152a7492333SDarrick J. Wong 		error = -ENOENT;
153a7492333SDarrick J. Wong 		goto out_put_rtg;
154a7492333SDarrick J. Wong 	}
155a7492333SDarrick J. Wong 
156a7492333SDarrick J. Wong 	error = xchk_setup_metapath_scan(sc, sc->mp->m_rtdirip,
157a7492333SDarrick J. Wong 			xfs_rtginode_path(rtg_rgno(rtg), type), ip);
158a7492333SDarrick J. Wong 
159a7492333SDarrick J. Wong out_put_rtg:
160a7492333SDarrick J. Wong 	xfs_rtgroup_put(rtg);
161a7492333SDarrick J. Wong 	return error;
162a7492333SDarrick J. Wong }
163a7492333SDarrick J. Wong #else
164a7492333SDarrick J. Wong # define xchk_setup_metapath_rtdir(...)		(-ENOENT)
165a7492333SDarrick J. Wong # define xchk_setup_metapath_rtginode(...)	(-ENOENT)
166a7492333SDarrick J. Wong #endif /* CONFIG_XFS_RT */
167a7492333SDarrick J. Wong 
168128a0552SDarrick J. Wong #ifdef CONFIG_XFS_QUOTA
169128a0552SDarrick J. Wong /* Scan the /quota directory itself. */
170128a0552SDarrick J. Wong static int
xchk_setup_metapath_quotadir(struct xfs_scrub * sc)171128a0552SDarrick J. Wong xchk_setup_metapath_quotadir(
172128a0552SDarrick J. Wong 	struct xfs_scrub	*sc)
173128a0552SDarrick J. Wong {
174*e1d8602bSDarrick J. Wong 	struct xfs_quotainfo	*qi = sc->mp->m_quotainfo;
175128a0552SDarrick J. Wong 
176*e1d8602bSDarrick J. Wong 	if (!qi || !qi->qi_dirip)
177*e1d8602bSDarrick J. Wong 		return -ENOENT;
178128a0552SDarrick J. Wong 
179*e1d8602bSDarrick J. Wong 	return xchk_setup_metapath_scan(sc, sc->mp->m_metadirip,
180*e1d8602bSDarrick J. Wong 			kstrdup("quota", GFP_KERNEL), qi->qi_dirip);
181128a0552SDarrick J. Wong }
182128a0552SDarrick J. Wong 
183128a0552SDarrick J. Wong /* Scan a quota inode under the /quota directory. */
184128a0552SDarrick J. Wong static int
xchk_setup_metapath_dqinode(struct xfs_scrub * sc,xfs_dqtype_t type)185128a0552SDarrick J. Wong xchk_setup_metapath_dqinode(
186128a0552SDarrick J. Wong 	struct xfs_scrub	*sc,
187128a0552SDarrick J. Wong 	xfs_dqtype_t		type)
188128a0552SDarrick J. Wong {
1899b728001SDarrick J. Wong 	struct xfs_quotainfo	*qi = sc->mp->m_quotainfo;
190128a0552SDarrick J. Wong 	struct xfs_inode	*ip = NULL;
191128a0552SDarrick J. Wong 
1929b728001SDarrick J. Wong 	if (!qi)
1939b728001SDarrick J. Wong 		return -ENOENT;
1949b728001SDarrick J. Wong 
1959b728001SDarrick J. Wong 	switch (type) {
1969b728001SDarrick J. Wong 	case XFS_DQTYPE_USER:
1979b728001SDarrick J. Wong 		ip = qi->qi_uquotaip;
1989b728001SDarrick J. Wong 		break;
1999b728001SDarrick J. Wong 	case XFS_DQTYPE_GROUP:
2009b728001SDarrick J. Wong 		ip = qi->qi_gquotaip;
2019b728001SDarrick J. Wong 		break;
2029b728001SDarrick J. Wong 	case XFS_DQTYPE_PROJ:
2039b728001SDarrick J. Wong 		ip = qi->qi_pquotaip;
2049b728001SDarrick J. Wong 		break;
2059b728001SDarrick J. Wong 	default:
2069b728001SDarrick J. Wong 		ASSERT(0);
2079b728001SDarrick J. Wong 		return -EINVAL;
2089b728001SDarrick J. Wong 	}
2099b728001SDarrick J. Wong 	if (!ip)
2109b728001SDarrick J. Wong 		return -ENOENT;
2119b728001SDarrick J. Wong 
212*e1d8602bSDarrick J. Wong 	return xchk_setup_metapath_scan(sc, qi->qi_dirip,
2139b728001SDarrick J. Wong 			kstrdup(xfs_dqinode_path(type), GFP_KERNEL), ip);
214128a0552SDarrick J. Wong }
215128a0552SDarrick J. Wong #else
216128a0552SDarrick J. Wong # define xchk_setup_metapath_quotadir(...)	(-ENOENT)
217128a0552SDarrick J. Wong # define xchk_setup_metapath_dqinode(...)	(-ENOENT)
218128a0552SDarrick J. Wong #endif /* CONFIG_XFS_QUOTA */
219128a0552SDarrick J. Wong 
220b3c03efaSDarrick J. Wong int
xchk_setup_metapath(struct xfs_scrub * sc)221b3c03efaSDarrick J. Wong xchk_setup_metapath(
222b3c03efaSDarrick J. Wong 	struct xfs_scrub	*sc)
223b3c03efaSDarrick J. Wong {
224b3c03efaSDarrick J. Wong 	if (!xfs_has_metadir(sc->mp))
225b3c03efaSDarrick J. Wong 		return -ENOENT;
226b3c03efaSDarrick J. Wong 	if (sc->sm->sm_gen)
227b3c03efaSDarrick J. Wong 		return -EINVAL;
228b3c03efaSDarrick J. Wong 
229b3c03efaSDarrick J. Wong 	switch (sc->sm->sm_ino) {
230b3c03efaSDarrick J. Wong 	case XFS_SCRUB_METAPATH_PROBE:
231b3c03efaSDarrick J. Wong 		/* Just probing, nothing else to do. */
232b3c03efaSDarrick J. Wong 		if (sc->sm->sm_agno)
233b3c03efaSDarrick J. Wong 			return -EINVAL;
234b3c03efaSDarrick J. Wong 		return 0;
235a7492333SDarrick J. Wong 	case XFS_SCRUB_METAPATH_RTDIR:
236a7492333SDarrick J. Wong 		return xchk_setup_metapath_rtdir(sc);
237a7492333SDarrick J. Wong 	case XFS_SCRUB_METAPATH_RTBITMAP:
238a7492333SDarrick J. Wong 		return xchk_setup_metapath_rtginode(sc, XFS_RTGI_BITMAP);
239a7492333SDarrick J. Wong 	case XFS_SCRUB_METAPATH_RTSUMMARY:
240a7492333SDarrick J. Wong 		return xchk_setup_metapath_rtginode(sc, XFS_RTGI_SUMMARY);
241128a0552SDarrick J. Wong 	case XFS_SCRUB_METAPATH_QUOTADIR:
242128a0552SDarrick J. Wong 		return xchk_setup_metapath_quotadir(sc);
243128a0552SDarrick J. Wong 	case XFS_SCRUB_METAPATH_USRQUOTA:
244128a0552SDarrick J. Wong 		return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_USER);
245128a0552SDarrick J. Wong 	case XFS_SCRUB_METAPATH_GRPQUOTA:
246128a0552SDarrick J. Wong 		return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_GROUP);
247128a0552SDarrick J. Wong 	case XFS_SCRUB_METAPATH_PRJQUOTA:
248128a0552SDarrick J. Wong 		return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_PROJ);
249b3c03efaSDarrick J. Wong 	default:
250b3c03efaSDarrick J. Wong 		return -ENOENT;
251b3c03efaSDarrick J. Wong 	}
252b3c03efaSDarrick J. Wong }
253b3c03efaSDarrick J. Wong 
254b3c03efaSDarrick J. Wong /*
255b3c03efaSDarrick J. Wong  * Take the ILOCK on the metadata directory parent and child.  We do not know
256b3c03efaSDarrick J. Wong  * that the metadata directory is not corrupt, so we lock the parent and try
257b3c03efaSDarrick J. Wong  * to lock the child.  Returns 0 if successful, or -EINTR to abort the scrub.
258b3c03efaSDarrick J. Wong  */
259b3c03efaSDarrick J. Wong STATIC int
xchk_metapath_ilock_both(struct xchk_metapath * mpath)260b3c03efaSDarrick J. Wong xchk_metapath_ilock_both(
261b3c03efaSDarrick J. Wong 	struct xchk_metapath	*mpath)
262b3c03efaSDarrick J. Wong {
263b3c03efaSDarrick J. Wong 	struct xfs_scrub	*sc = mpath->sc;
264b3c03efaSDarrick J. Wong 	int			error = 0;
265b3c03efaSDarrick J. Wong 
266b3c03efaSDarrick J. Wong 	while (true) {
267b3c03efaSDarrick J. Wong 		xfs_ilock(mpath->dp, XFS_ILOCK_EXCL);
268b3c03efaSDarrick J. Wong 		if (xchk_ilock_nowait(sc, XFS_ILOCK_EXCL)) {
269b3c03efaSDarrick J. Wong 			mpath->dp_ilock_flags |= XFS_ILOCK_EXCL;
270b3c03efaSDarrick J. Wong 			return 0;
271b3c03efaSDarrick J. Wong 		}
272b3c03efaSDarrick J. Wong 		xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
273b3c03efaSDarrick J. Wong 
274b3c03efaSDarrick J. Wong 		if (xchk_should_terminate(sc, &error))
275b3c03efaSDarrick J. Wong 			return error;
276b3c03efaSDarrick J. Wong 
277b3c03efaSDarrick J. Wong 		delay(1);
278b3c03efaSDarrick J. Wong 	}
279b3c03efaSDarrick J. Wong 
280b3c03efaSDarrick J. Wong 	ASSERT(0);
281b3c03efaSDarrick J. Wong 	return -EINTR;
282b3c03efaSDarrick J. Wong }
283b3c03efaSDarrick J. Wong 
284b3c03efaSDarrick J. Wong /* Unlock parent and child inodes. */
285b3c03efaSDarrick J. Wong static inline void
xchk_metapath_iunlock(struct xchk_metapath * mpath)286b3c03efaSDarrick J. Wong xchk_metapath_iunlock(
287b3c03efaSDarrick J. Wong 	struct xchk_metapath	*mpath)
288b3c03efaSDarrick J. Wong {
289b3c03efaSDarrick J. Wong 	struct xfs_scrub	*sc = mpath->sc;
290b3c03efaSDarrick J. Wong 
291b3c03efaSDarrick J. Wong 	xchk_iunlock(sc, XFS_ILOCK_EXCL);
292b3c03efaSDarrick J. Wong 
293b3c03efaSDarrick J. Wong 	mpath->dp_ilock_flags &= ~XFS_ILOCK_EXCL;
294b3c03efaSDarrick J. Wong 	xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
295b3c03efaSDarrick J. Wong }
296b3c03efaSDarrick J. Wong 
297b3c03efaSDarrick J. Wong int
xchk_metapath(struct xfs_scrub * sc)298b3c03efaSDarrick J. Wong xchk_metapath(
299b3c03efaSDarrick J. Wong 	struct xfs_scrub	*sc)
300b3c03efaSDarrick J. Wong {
301b3c03efaSDarrick J. Wong 	struct xchk_metapath	*mpath = sc->buf;
302b3c03efaSDarrick J. Wong 	xfs_ino_t		ino = NULLFSINO;
303b3c03efaSDarrick J. Wong 	int			error;
304b3c03efaSDarrick J. Wong 
305b3c03efaSDarrick J. Wong 	/* Just probing, nothing else to do. */
306b3c03efaSDarrick J. Wong 	if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE)
307b3c03efaSDarrick J. Wong 		return 0;
308b3c03efaSDarrick J. Wong 
309b3c03efaSDarrick J. Wong 	/* Parent required to do anything else. */
310b3c03efaSDarrick J. Wong 	if (mpath->dp == NULL) {
311b3c03efaSDarrick J. Wong 		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
312b3c03efaSDarrick J. Wong 		return 0;
313b3c03efaSDarrick J. Wong 	}
314b3c03efaSDarrick J. Wong 
315b3c03efaSDarrick J. Wong 	error = xchk_trans_alloc_empty(sc);
316b3c03efaSDarrick J. Wong 	if (error)
317b3c03efaSDarrick J. Wong 		return error;
318b3c03efaSDarrick J. Wong 
319b3c03efaSDarrick J. Wong 	error = xchk_metapath_ilock_both(mpath);
320b3c03efaSDarrick J. Wong 	if (error)
321b3c03efaSDarrick J. Wong 		goto out_cancel;
322b3c03efaSDarrick J. Wong 
323b3c03efaSDarrick J. Wong 	/* Make sure the parent dir has a dirent pointing to this file. */
324b3c03efaSDarrick J. Wong 	error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
325b3c03efaSDarrick J. Wong 	trace_xchk_metapath_lookup(sc, mpath->path, mpath->dp, ino);
326b3c03efaSDarrick J. Wong 	if (error == -ENOENT) {
327b3c03efaSDarrick J. Wong 		/* No directory entry at all */
328b3c03efaSDarrick J. Wong 		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
329b3c03efaSDarrick J. Wong 		error = 0;
330b3c03efaSDarrick J. Wong 		goto out_ilock;
331b3c03efaSDarrick J. Wong 	}
332b3c03efaSDarrick J. Wong 	if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
333b3c03efaSDarrick J. Wong 		goto out_ilock;
334b3c03efaSDarrick J. Wong 	if (ino != sc->ip->i_ino) {
335b3c03efaSDarrick J. Wong 		/* Pointing to wrong inode */
336b3c03efaSDarrick J. Wong 		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
337b3c03efaSDarrick J. Wong 	}
338b3c03efaSDarrick J. Wong 
339b3c03efaSDarrick J. Wong out_ilock:
340b3c03efaSDarrick J. Wong 	xchk_metapath_iunlock(mpath);
341b3c03efaSDarrick J. Wong out_cancel:
342b3c03efaSDarrick J. Wong 	xchk_trans_cancel(sc);
343b3c03efaSDarrick J. Wong 	return error;
344b3c03efaSDarrick J. Wong }
3450d2c636eSDarrick J. Wong 
3460d2c636eSDarrick J. Wong #ifdef CONFIG_XFS_ONLINE_REPAIR
3470d2c636eSDarrick J. Wong /* Create the dirent represented by the final component of the path. */
3480d2c636eSDarrick J. Wong STATIC int
xrep_metapath_link(struct xchk_metapath * mpath)3490d2c636eSDarrick J. Wong xrep_metapath_link(
3500d2c636eSDarrick J. Wong 	struct xchk_metapath	*mpath)
3510d2c636eSDarrick J. Wong {
3520d2c636eSDarrick J. Wong 	struct xfs_scrub	*sc = mpath->sc;
3530d2c636eSDarrick J. Wong 
3540d2c636eSDarrick J. Wong 	mpath->du.dp = mpath->dp;
3550d2c636eSDarrick J. Wong 	mpath->du.name = &mpath->xname;
3560d2c636eSDarrick J. Wong 	mpath->du.ip = sc->ip;
3570d2c636eSDarrick J. Wong 
3580d2c636eSDarrick J. Wong 	if (xfs_has_parent(sc->mp))
3590d2c636eSDarrick J. Wong 		mpath->du.ppargs = &mpath->link_ppargs;
3600d2c636eSDarrick J. Wong 	else
3610d2c636eSDarrick J. Wong 		mpath->du.ppargs = NULL;
3620d2c636eSDarrick J. Wong 
3630d2c636eSDarrick J. Wong 	trace_xrep_metapath_link(sc, mpath->path, mpath->dp, sc->ip->i_ino);
3640d2c636eSDarrick J. Wong 
3650d2c636eSDarrick J. Wong 	return xfs_dir_add_child(sc->tp, mpath->link_resblks, &mpath->du);
3660d2c636eSDarrick J. Wong }
3670d2c636eSDarrick J. Wong 
3680d2c636eSDarrick J. Wong /* Remove the dirent at the final component of the path. */
3690d2c636eSDarrick J. Wong STATIC int
xrep_metapath_unlink(struct xchk_metapath * mpath,xfs_ino_t ino,struct xfs_inode * ip)3700d2c636eSDarrick J. Wong xrep_metapath_unlink(
3710d2c636eSDarrick J. Wong 	struct xchk_metapath	*mpath,
3720d2c636eSDarrick J. Wong 	xfs_ino_t		ino,
3730d2c636eSDarrick J. Wong 	struct xfs_inode	*ip)
3740d2c636eSDarrick J. Wong {
3750d2c636eSDarrick J. Wong 	struct xfs_parent_rec	rec;
3760d2c636eSDarrick J. Wong 	struct xfs_scrub	*sc = mpath->sc;
3770d2c636eSDarrick J. Wong 	struct xfs_mount	*mp = sc->mp;
3780d2c636eSDarrick J. Wong 	int			error;
3790d2c636eSDarrick J. Wong 
3800d2c636eSDarrick J. Wong 	trace_xrep_metapath_unlink(sc, mpath->path, mpath->dp, ino);
3810d2c636eSDarrick J. Wong 
3820d2c636eSDarrick J. Wong 	if (!ip) {
3830d2c636eSDarrick J. Wong 		/* The child inode isn't allocated.  Junk the dirent. */
3840d2c636eSDarrick J. Wong 		xfs_trans_log_inode(sc->tp, mpath->dp, XFS_ILOG_CORE);
3850d2c636eSDarrick J. Wong 		return xfs_dir_removename(sc->tp, mpath->dp, &mpath->xname,
3860d2c636eSDarrick J. Wong 				ino, mpath->unlink_resblks);
3870d2c636eSDarrick J. Wong 	}
3880d2c636eSDarrick J. Wong 
3890d2c636eSDarrick J. Wong 	mpath->du.dp = mpath->dp;
3900d2c636eSDarrick J. Wong 	mpath->du.name = &mpath->xname;
3910d2c636eSDarrick J. Wong 	mpath->du.ip = ip;
3920d2c636eSDarrick J. Wong 	mpath->du.ppargs = NULL;
3930d2c636eSDarrick J. Wong 
3940d2c636eSDarrick J. Wong 	/* Figure out if we're removing a parent pointer too. */
3950d2c636eSDarrick J. Wong 	if (xfs_has_parent(mp)) {
3960d2c636eSDarrick J. Wong 		xfs_inode_to_parent_rec(&rec, ip);
3970d2c636eSDarrick J. Wong 		error = xfs_parent_lookup(sc->tp, ip, &mpath->xname, &rec,
3980d2c636eSDarrick J. Wong 				&mpath->pptr_args);
3990d2c636eSDarrick J. Wong 		switch (error) {
4000d2c636eSDarrick J. Wong 		case -ENOATTR:
4010d2c636eSDarrick J. Wong 			break;
4020d2c636eSDarrick J. Wong 		case 0:
4030d2c636eSDarrick J. Wong 			mpath->du.ppargs = &mpath->unlink_ppargs;
4040d2c636eSDarrick J. Wong 			break;
4050d2c636eSDarrick J. Wong 		default:
4060d2c636eSDarrick J. Wong 			return error;
4070d2c636eSDarrick J. Wong 		}
4080d2c636eSDarrick J. Wong 	}
4090d2c636eSDarrick J. Wong 
4100d2c636eSDarrick J. Wong 	return xfs_dir_remove_child(sc->tp, mpath->unlink_resblks, &mpath->du);
4110d2c636eSDarrick J. Wong }
4120d2c636eSDarrick J. Wong 
4130d2c636eSDarrick J. Wong /*
4140d2c636eSDarrick J. Wong  * Try to create a dirent in @mpath->dp with the name @mpath->xname that points
4150d2c636eSDarrick J. Wong  * to @sc->ip.  Returns:
4160d2c636eSDarrick J. Wong  *
4170d2c636eSDarrick J. Wong  * -EEXIST and an @alleged_child if the dirent that points to the wrong inode;
4180d2c636eSDarrick J. Wong  * 0 if there is now a dirent pointing to @sc->ip; or
4190d2c636eSDarrick J. Wong  * A negative errno on error.
4200d2c636eSDarrick J. Wong  */
4210d2c636eSDarrick J. Wong STATIC int
xrep_metapath_try_link(struct xchk_metapath * mpath,xfs_ino_t * alleged_child)4220d2c636eSDarrick J. Wong xrep_metapath_try_link(
4230d2c636eSDarrick J. Wong 	struct xchk_metapath	*mpath,
4240d2c636eSDarrick J. Wong 	xfs_ino_t		*alleged_child)
4250d2c636eSDarrick J. Wong {
4260d2c636eSDarrick J. Wong 	struct xfs_scrub	*sc = mpath->sc;
4270d2c636eSDarrick J. Wong 	xfs_ino_t		ino;
4280d2c636eSDarrick J. Wong 	int			error;
4290d2c636eSDarrick J. Wong 
4300d2c636eSDarrick J. Wong 	/* Allocate transaction, lock inodes, join to transaction. */
4310d2c636eSDarrick J. Wong 	error = xchk_trans_alloc(sc, mpath->link_resblks);
4320d2c636eSDarrick J. Wong 	if (error)
4330d2c636eSDarrick J. Wong 		return error;
4340d2c636eSDarrick J. Wong 
4350d2c636eSDarrick J. Wong 	error = xchk_metapath_ilock_both(mpath);
4360d2c636eSDarrick J. Wong 	if (error) {
4370d2c636eSDarrick J. Wong 		xchk_trans_cancel(sc);
4380d2c636eSDarrick J. Wong 		return error;
4390d2c636eSDarrick J. Wong 	}
4400d2c636eSDarrick J. Wong 	xfs_trans_ijoin(sc->tp, mpath->dp, 0);
4410d2c636eSDarrick J. Wong 	xfs_trans_ijoin(sc->tp, sc->ip, 0);
4420d2c636eSDarrick J. Wong 
4430d2c636eSDarrick J. Wong 	error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
4440d2c636eSDarrick J. Wong 	trace_xrep_metapath_lookup(sc, mpath->path, mpath->dp, ino);
4450d2c636eSDarrick J. Wong 	if (error == -ENOENT) {
4460d2c636eSDarrick J. Wong 		/*
4470d2c636eSDarrick J. Wong 		 * There is no dirent in the directory.  Create an entry
4480d2c636eSDarrick J. Wong 		 * pointing to @sc->ip.
4490d2c636eSDarrick J. Wong 		 */
4500d2c636eSDarrick J. Wong 		error = xrep_metapath_link(mpath);
4510d2c636eSDarrick J. Wong 		if (error)
4520d2c636eSDarrick J. Wong 			goto out_cancel;
4530d2c636eSDarrick J. Wong 
4540d2c636eSDarrick J. Wong 		error = xrep_trans_commit(sc);
4550d2c636eSDarrick J. Wong 		xchk_metapath_iunlock(mpath);
4560d2c636eSDarrick J. Wong 		return error;
4570d2c636eSDarrick J. Wong 	}
4580d2c636eSDarrick J. Wong 	if (error)
4590d2c636eSDarrick J. Wong 		goto out_cancel;
4600d2c636eSDarrick J. Wong 
4610d2c636eSDarrick J. Wong 	if (ino == sc->ip->i_ino) {
4620d2c636eSDarrick J. Wong 		/* The dirent already points to @sc->ip; we're done. */
4630d2c636eSDarrick J. Wong 		error = 0;
4640d2c636eSDarrick J. Wong 		goto out_cancel;
4650d2c636eSDarrick J. Wong 	}
4660d2c636eSDarrick J. Wong 
4670d2c636eSDarrick J. Wong 	/*
4680d2c636eSDarrick J. Wong 	 * The dirent points elsewhere; pass that back so that the caller
4690d2c636eSDarrick J. Wong 	 * can try to remove the dirent.
4700d2c636eSDarrick J. Wong 	 */
4710d2c636eSDarrick J. Wong 	*alleged_child = ino;
4720d2c636eSDarrick J. Wong 	error = -EEXIST;
4730d2c636eSDarrick J. Wong 
4740d2c636eSDarrick J. Wong out_cancel:
4750d2c636eSDarrick J. Wong 	xchk_trans_cancel(sc);
4760d2c636eSDarrick J. Wong 	xchk_metapath_iunlock(mpath);
4770d2c636eSDarrick J. Wong 	return error;
4780d2c636eSDarrick J. Wong }
4790d2c636eSDarrick J. Wong 
4800d2c636eSDarrick J. Wong /*
4810d2c636eSDarrick J. Wong  * Take the ILOCK on the metadata directory parent and a bad child, if one is
4820d2c636eSDarrick J. Wong  * supplied.  We do not know that the metadata directory is not corrupt, so we
4830d2c636eSDarrick J. Wong  * lock the parent and try to lock the child.  Returns 0 if successful, or
4840d2c636eSDarrick J. Wong  * -EINTR to abort the repair.  The lock state of @dp is not recorded in @mpath.
4850d2c636eSDarrick J. Wong  */
4860d2c636eSDarrick J. Wong STATIC int
xchk_metapath_ilock_parent_and_child(struct xchk_metapath * mpath,struct xfs_inode * ip)4870d2c636eSDarrick J. Wong xchk_metapath_ilock_parent_and_child(
4880d2c636eSDarrick J. Wong 	struct xchk_metapath	*mpath,
4890d2c636eSDarrick J. Wong 	struct xfs_inode	*ip)
4900d2c636eSDarrick J. Wong {
4910d2c636eSDarrick J. Wong 	struct xfs_scrub	*sc = mpath->sc;
4920d2c636eSDarrick J. Wong 	int			error = 0;
4930d2c636eSDarrick J. Wong 
4940d2c636eSDarrick J. Wong 	while (true) {
4950d2c636eSDarrick J. Wong 		xfs_ilock(mpath->dp, XFS_ILOCK_EXCL);
4960d2c636eSDarrick J. Wong 		if (!ip || xfs_ilock_nowait(ip, XFS_ILOCK_EXCL))
4970d2c636eSDarrick J. Wong 			return 0;
4980d2c636eSDarrick J. Wong 		xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
4990d2c636eSDarrick J. Wong 
5000d2c636eSDarrick J. Wong 		if (xchk_should_terminate(sc, &error))
5010d2c636eSDarrick J. Wong 			return error;
5020d2c636eSDarrick J. Wong 
5030d2c636eSDarrick J. Wong 		delay(1);
5040d2c636eSDarrick J. Wong 	}
5050d2c636eSDarrick J. Wong 
5060d2c636eSDarrick J. Wong 	ASSERT(0);
5070d2c636eSDarrick J. Wong 	return -EINTR;
5080d2c636eSDarrick J. Wong }
5090d2c636eSDarrick J. Wong 
5100d2c636eSDarrick J. Wong /*
5110d2c636eSDarrick J. Wong  * Try to remove a dirent in @mpath->dp with the name @mpath->xname that points
5120d2c636eSDarrick J. Wong  * to @alleged_child.  Returns:
5130d2c636eSDarrick J. Wong  *
5140d2c636eSDarrick J. Wong  * 0 if there is no longer a dirent;
5150d2c636eSDarrick J. Wong  * -EEXIST if the dirent points to @sc->ip;
5160d2c636eSDarrick J. Wong  * -EAGAIN and an updated @alleged_child if the dirent points elsewhere; or
5170d2c636eSDarrick J. Wong  * A negative errno for any other error.
5180d2c636eSDarrick J. Wong  */
5190d2c636eSDarrick J. Wong STATIC int
xrep_metapath_try_unlink(struct xchk_metapath * mpath,xfs_ino_t * alleged_child)5200d2c636eSDarrick J. Wong xrep_metapath_try_unlink(
5210d2c636eSDarrick J. Wong 	struct xchk_metapath	*mpath,
5220d2c636eSDarrick J. Wong 	xfs_ino_t		*alleged_child)
5230d2c636eSDarrick J. Wong {
5240d2c636eSDarrick J. Wong 	struct xfs_scrub	*sc = mpath->sc;
5250d2c636eSDarrick J. Wong 	struct xfs_inode	*ip = NULL;
5260d2c636eSDarrick J. Wong 	xfs_ino_t		ino;
5270d2c636eSDarrick J. Wong 	int			error;
5280d2c636eSDarrick J. Wong 
5290d2c636eSDarrick J. Wong 	ASSERT(*alleged_child != sc->ip->i_ino);
5300d2c636eSDarrick J. Wong 
5310d2c636eSDarrick J. Wong 	trace_xrep_metapath_try_unlink(sc, mpath->path, mpath->dp,
5320d2c636eSDarrick J. Wong 			*alleged_child);
5330d2c636eSDarrick J. Wong 
5340d2c636eSDarrick J. Wong 	/*
5350d2c636eSDarrick J. Wong 	 * Allocate transaction, grab the alleged child inode, lock inodes,
5360d2c636eSDarrick J. Wong 	 * join to transaction.
5370d2c636eSDarrick J. Wong 	 */
5380d2c636eSDarrick J. Wong 	error = xchk_trans_alloc(sc, mpath->unlink_resblks);
5390d2c636eSDarrick J. Wong 	if (error)
5400d2c636eSDarrick J. Wong 		return error;
5410d2c636eSDarrick J. Wong 
5420d2c636eSDarrick J. Wong 	error = xchk_iget(sc, *alleged_child, &ip);
5430d2c636eSDarrick J. Wong 	if (error == -EINVAL || error == -ENOENT) {
5440d2c636eSDarrick J. Wong 		/* inode number is bogus, junk the dirent */
5450d2c636eSDarrick J. Wong 		error = 0;
5460d2c636eSDarrick J. Wong 	}
5470d2c636eSDarrick J. Wong 	if (error) {
5480d2c636eSDarrick J. Wong 		xchk_trans_cancel(sc);
5490d2c636eSDarrick J. Wong 		return error;
5500d2c636eSDarrick J. Wong 	}
5510d2c636eSDarrick J. Wong 
5520d2c636eSDarrick J. Wong 	error = xchk_metapath_ilock_parent_and_child(mpath, ip);
5530d2c636eSDarrick J. Wong 	if (error) {
5540d2c636eSDarrick J. Wong 		xchk_trans_cancel(sc);
5550d2c636eSDarrick J. Wong 		return error;
5560d2c636eSDarrick J. Wong 	}
5570d2c636eSDarrick J. Wong 	xfs_trans_ijoin(sc->tp, mpath->dp, 0);
5580d2c636eSDarrick J. Wong 	if (ip)
5590d2c636eSDarrick J. Wong 		xfs_trans_ijoin(sc->tp, ip, 0);
5600d2c636eSDarrick J. Wong 
5610d2c636eSDarrick J. Wong 	error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
5620d2c636eSDarrick J. Wong 	trace_xrep_metapath_lookup(sc, mpath->path, mpath->dp, ino);
5630d2c636eSDarrick J. Wong 	if (error == -ENOENT) {
5640d2c636eSDarrick J. Wong 		/*
5650d2c636eSDarrick J. Wong 		 * There is no dirent in the directory anymore.  We're ready to
5660d2c636eSDarrick J. Wong 		 * try the link operation again.
5670d2c636eSDarrick J. Wong 		 */
5680d2c636eSDarrick J. Wong 		error = 0;
5690d2c636eSDarrick J. Wong 		goto out_cancel;
5700d2c636eSDarrick J. Wong 	}
5710d2c636eSDarrick J. Wong 	if (error)
5720d2c636eSDarrick J. Wong 		goto out_cancel;
5730d2c636eSDarrick J. Wong 
5740d2c636eSDarrick J. Wong 	if (ino == sc->ip->i_ino) {
5750d2c636eSDarrick J. Wong 		/* The dirent already points to @sc->ip; we're done. */
5760d2c636eSDarrick J. Wong 		error = -EEXIST;
5770d2c636eSDarrick J. Wong 		goto out_cancel;
5780d2c636eSDarrick J. Wong 	}
5790d2c636eSDarrick J. Wong 
5800d2c636eSDarrick J. Wong 	/*
5810d2c636eSDarrick J. Wong 	 * The dirent does not point to the alleged child.  Update the caller
5820d2c636eSDarrick J. Wong 	 * and signal that we want to be called again.
5830d2c636eSDarrick J. Wong 	 */
5840d2c636eSDarrick J. Wong 	if (ino != *alleged_child) {
5850d2c636eSDarrick J. Wong 		*alleged_child = ino;
5860d2c636eSDarrick J. Wong 		error = -EAGAIN;
5870d2c636eSDarrick J. Wong 		goto out_cancel;
5880d2c636eSDarrick J. Wong 	}
5890d2c636eSDarrick J. Wong 
5900d2c636eSDarrick J. Wong 	/* Remove the link to the child. */
5910d2c636eSDarrick J. Wong 	error = xrep_metapath_unlink(mpath, ino, ip);
5920d2c636eSDarrick J. Wong 	if (error)
5930d2c636eSDarrick J. Wong 		goto out_cancel;
5940d2c636eSDarrick J. Wong 
5950d2c636eSDarrick J. Wong 	error = xrep_trans_commit(sc);
5960d2c636eSDarrick J. Wong 	goto out_unlock;
5970d2c636eSDarrick J. Wong 
5980d2c636eSDarrick J. Wong out_cancel:
5990d2c636eSDarrick J. Wong 	xchk_trans_cancel(sc);
6000d2c636eSDarrick J. Wong out_unlock:
6010d2c636eSDarrick J. Wong 	xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
6020d2c636eSDarrick J. Wong 	if (ip) {
6030d2c636eSDarrick J. Wong 		xfs_iunlock(ip, XFS_ILOCK_EXCL);
6040d2c636eSDarrick J. Wong 		xchk_irele(sc, ip);
6050d2c636eSDarrick J. Wong 	}
6060d2c636eSDarrick J. Wong 	return error;
6070d2c636eSDarrick J. Wong }
6080d2c636eSDarrick J. Wong 
6090d2c636eSDarrick J. Wong /*
6100d2c636eSDarrick J. Wong  * Make sure the metadata directory path points to the child being examined.
6110d2c636eSDarrick J. Wong  *
6120d2c636eSDarrick J. Wong  * Repair needs to be able to create a directory structure, create its own
6130d2c636eSDarrick J. Wong  * transactions, and take ILOCKs.  This function /must/ be called after all
6140d2c636eSDarrick J. Wong  * other repairs have completed.
6150d2c636eSDarrick J. Wong  */
6160d2c636eSDarrick J. Wong int
xrep_metapath(struct xfs_scrub * sc)6170d2c636eSDarrick J. Wong xrep_metapath(
6180d2c636eSDarrick J. Wong 	struct xfs_scrub	*sc)
6190d2c636eSDarrick J. Wong {
6200d2c636eSDarrick J. Wong 	struct xchk_metapath	*mpath = sc->buf;
6210d2c636eSDarrick J. Wong 	struct xfs_mount	*mp = sc->mp;
6220d2c636eSDarrick J. Wong 	int			error = 0;
6230d2c636eSDarrick J. Wong 
6240d2c636eSDarrick J. Wong 	/* Just probing, nothing to repair. */
6250d2c636eSDarrick J. Wong 	if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE)
6260d2c636eSDarrick J. Wong 		return 0;
6270d2c636eSDarrick J. Wong 
6280d2c636eSDarrick J. Wong 	/* Parent required to do anything else. */
6290d2c636eSDarrick J. Wong 	if (mpath->dp == NULL)
6300d2c636eSDarrick J. Wong 		return -EFSCORRUPTED;
6310d2c636eSDarrick J. Wong 
6320d2c636eSDarrick J. Wong 	/*
6330d2c636eSDarrick J. Wong 	 * Make sure the child file actually has an attr fork to receive a new
6340d2c636eSDarrick J. Wong 	 * parent pointer if the fs has parent pointers.
6350d2c636eSDarrick J. Wong 	 */
6360d2c636eSDarrick J. Wong 	if (xfs_has_parent(mp)) {
6370d2c636eSDarrick J. Wong 		error = xfs_attr_add_fork(sc->ip,
6380d2c636eSDarrick J. Wong 				sizeof(struct xfs_attr_sf_hdr), 1);
6390d2c636eSDarrick J. Wong 		if (error)
6400d2c636eSDarrick J. Wong 			return error;
6410d2c636eSDarrick J. Wong 	}
6420d2c636eSDarrick J. Wong 
6430d2c636eSDarrick J. Wong 	/* Compute block reservation required to unlink and link a file. */
6440d2c636eSDarrick J. Wong 	mpath->unlink_resblks = xfs_remove_space_res(mp, MAXNAMELEN);
6450d2c636eSDarrick J. Wong 	mpath->link_resblks = xfs_link_space_res(mp, MAXNAMELEN);
6460d2c636eSDarrick J. Wong 
6470d2c636eSDarrick J. Wong 	do {
6480d2c636eSDarrick J. Wong 		xfs_ino_t	alleged_child;
6490d2c636eSDarrick J. Wong 
6500d2c636eSDarrick J. Wong 		/* Re-establish the link, or tell us which inode to remove. */
6510d2c636eSDarrick J. Wong 		error = xrep_metapath_try_link(mpath, &alleged_child);
6520d2c636eSDarrick J. Wong 		if (!error)
6530d2c636eSDarrick J. Wong 			return 0;
6540d2c636eSDarrick J. Wong 		if (error != -EEXIST)
6550d2c636eSDarrick J. Wong 			return error;
6560d2c636eSDarrick J. Wong 
6570d2c636eSDarrick J. Wong 		/*
6580d2c636eSDarrick J. Wong 		 * Remove an incorrect link to an alleged child, or tell us
6590d2c636eSDarrick J. Wong 		 * which inode to remove.
6600d2c636eSDarrick J. Wong 		 */
6610d2c636eSDarrick J. Wong 		do {
6620d2c636eSDarrick J. Wong 			error = xrep_metapath_try_unlink(mpath, &alleged_child);
6630d2c636eSDarrick J. Wong 		} while (error == -EAGAIN);
6640d2c636eSDarrick J. Wong 		if (error == -EEXIST) {
6650d2c636eSDarrick J. Wong 			/* Link established; we're done. */
6660d2c636eSDarrick J. Wong 			error = 0;
6670d2c636eSDarrick J. Wong 			break;
6680d2c636eSDarrick J. Wong 		}
6690d2c636eSDarrick J. Wong 	} while (!error);
6700d2c636eSDarrick J. Wong 
6710d2c636eSDarrick J. Wong 	return error;
6720d2c636eSDarrick J. Wong }
6730d2c636eSDarrick J. Wong #endif /* CONFIG_XFS_ONLINE_REPAIR */
674