162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2022-2023 Oracle.  All Rights Reserved.
462306a36Sopenharmony_ci * Author: Darrick J. Wong <djwong@kernel.org>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include "xfs.h"
762306a36Sopenharmony_ci#include "xfs_fs.h"
862306a36Sopenharmony_ci#include "xfs_shared.h"
962306a36Sopenharmony_ci#include "xfs_format.h"
1062306a36Sopenharmony_ci#include "xfs_log_format.h"
1162306a36Sopenharmony_ci#include "xfs_trans_resv.h"
1262306a36Sopenharmony_ci#include "xfs_mount.h"
1362306a36Sopenharmony_ci#include "xfs_inode.h"
1462306a36Sopenharmony_ci#include "xfs_dir2.h"
1562306a36Sopenharmony_ci#include "xfs_dir2_priv.h"
1662306a36Sopenharmony_ci#include "xfs_trace.h"
1762306a36Sopenharmony_ci#include "xfs_bmap.h"
1862306a36Sopenharmony_ci#include "xfs_trans.h"
1962306a36Sopenharmony_ci#include "xfs_error.h"
2062306a36Sopenharmony_ci#include "scrub/scrub.h"
2162306a36Sopenharmony_ci#include "scrub/readdir.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* Call a function for every entry in a shortform directory. */
2462306a36Sopenharmony_ciSTATIC int
2562306a36Sopenharmony_cixchk_dir_walk_sf(
2662306a36Sopenharmony_ci	struct xfs_scrub	*sc,
2762306a36Sopenharmony_ci	struct xfs_inode	*dp,
2862306a36Sopenharmony_ci	xchk_dirent_fn		dirent_fn,
2962306a36Sopenharmony_ci	void			*priv)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct xfs_name		name = {
3262306a36Sopenharmony_ci		.name		= ".",
3362306a36Sopenharmony_ci		.len		= 1,
3462306a36Sopenharmony_ci		.type		= XFS_DIR3_FT_DIR,
3562306a36Sopenharmony_ci	};
3662306a36Sopenharmony_ci	struct xfs_mount	*mp = dp->i_mount;
3762306a36Sopenharmony_ci	struct xfs_da_geometry	*geo = mp->m_dir_geo;
3862306a36Sopenharmony_ci	struct xfs_dir2_sf_entry *sfep;
3962306a36Sopenharmony_ci	struct xfs_dir2_sf_hdr	*sfp;
4062306a36Sopenharmony_ci	xfs_ino_t		ino;
4162306a36Sopenharmony_ci	xfs_dir2_dataptr_t	dapos;
4262306a36Sopenharmony_ci	unsigned int		i;
4362306a36Sopenharmony_ci	int			error;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	ASSERT(dp->i_df.if_bytes == dp->i_disk_size);
4662306a36Sopenharmony_ci	ASSERT(dp->i_df.if_u1.if_data != NULL);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	sfp = (struct xfs_dir2_sf_hdr *)dp->i_df.if_u1.if_data;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	/* dot entry */
5162306a36Sopenharmony_ci	dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
5262306a36Sopenharmony_ci			geo->data_entry_offset);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	error = dirent_fn(sc, dp, dapos, &name, dp->i_ino, priv);
5562306a36Sopenharmony_ci	if (error)
5662306a36Sopenharmony_ci		return error;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	/* dotdot entry */
5962306a36Sopenharmony_ci	dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
6062306a36Sopenharmony_ci			geo->data_entry_offset +
6162306a36Sopenharmony_ci			xfs_dir2_data_entsize(mp, sizeof(".") - 1));
6262306a36Sopenharmony_ci	ino = xfs_dir2_sf_get_parent_ino(sfp);
6362306a36Sopenharmony_ci	name.name = "..";
6462306a36Sopenharmony_ci	name.len = 2;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	error = dirent_fn(sc, dp, dapos, &name, ino, priv);
6762306a36Sopenharmony_ci	if (error)
6862306a36Sopenharmony_ci		return error;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* iterate everything else */
7162306a36Sopenharmony_ci	sfep = xfs_dir2_sf_firstentry(sfp);
7262306a36Sopenharmony_ci	for (i = 0; i < sfp->count; i++) {
7362306a36Sopenharmony_ci		dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
7462306a36Sopenharmony_ci				xfs_dir2_sf_get_offset(sfep));
7562306a36Sopenharmony_ci		ino = xfs_dir2_sf_get_ino(mp, sfp, sfep);
7662306a36Sopenharmony_ci		name.name = sfep->name;
7762306a36Sopenharmony_ci		name.len = sfep->namelen;
7862306a36Sopenharmony_ci		name.type = xfs_dir2_sf_get_ftype(mp, sfep);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		error = dirent_fn(sc, dp, dapos, &name, ino, priv);
8162306a36Sopenharmony_ci		if (error)
8262306a36Sopenharmony_ci			return error;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci		sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return 0;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/* Call a function for every entry in a block directory. */
9162306a36Sopenharmony_ciSTATIC int
9262306a36Sopenharmony_cixchk_dir_walk_block(
9362306a36Sopenharmony_ci	struct xfs_scrub	*sc,
9462306a36Sopenharmony_ci	struct xfs_inode	*dp,
9562306a36Sopenharmony_ci	xchk_dirent_fn		dirent_fn,
9662306a36Sopenharmony_ci	void			*priv)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct xfs_mount	*mp = dp->i_mount;
9962306a36Sopenharmony_ci	struct xfs_da_geometry	*geo = mp->m_dir_geo;
10062306a36Sopenharmony_ci	struct xfs_buf		*bp;
10162306a36Sopenharmony_ci	unsigned int		off, next_off, end;
10262306a36Sopenharmony_ci	int			error;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	error = xfs_dir3_block_read(sc->tp, dp, &bp);
10562306a36Sopenharmony_ci	if (error)
10662306a36Sopenharmony_ci		return error;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* Walk each directory entry. */
10962306a36Sopenharmony_ci	end = xfs_dir3_data_end_offset(geo, bp->b_addr);
11062306a36Sopenharmony_ci	for (off = geo->data_entry_offset; off < end; off = next_off) {
11162306a36Sopenharmony_ci		struct xfs_name			name = { };
11262306a36Sopenharmony_ci		struct xfs_dir2_data_unused	*dup = bp->b_addr + off;
11362306a36Sopenharmony_ci		struct xfs_dir2_data_entry	*dep = bp->b_addr + off;
11462306a36Sopenharmony_ci		xfs_ino_t			ino;
11562306a36Sopenharmony_ci		xfs_dir2_dataptr_t		dapos;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		/* Skip an empty entry. */
11862306a36Sopenharmony_ci		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
11962306a36Sopenharmony_ci			next_off = off + be16_to_cpu(dup->length);
12062306a36Sopenharmony_ci			continue;
12162306a36Sopenharmony_ci		}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci		/* Otherwise, find the next entry and report it. */
12462306a36Sopenharmony_ci		next_off = off + xfs_dir2_data_entsize(mp, dep->namelen);
12562306a36Sopenharmony_ci		if (next_off > end)
12662306a36Sopenharmony_ci			break;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, off);
12962306a36Sopenharmony_ci		ino = be64_to_cpu(dep->inumber);
13062306a36Sopenharmony_ci		name.name = dep->name;
13162306a36Sopenharmony_ci		name.len = dep->namelen;
13262306a36Sopenharmony_ci		name.type = xfs_dir2_data_get_ftype(mp, dep);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		error = dirent_fn(sc, dp, dapos, &name, ino, priv);
13562306a36Sopenharmony_ci		if (error)
13662306a36Sopenharmony_ci			break;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	xfs_trans_brelse(sc->tp, bp);
14062306a36Sopenharmony_ci	return error;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/* Read a leaf-format directory buffer. */
14462306a36Sopenharmony_ciSTATIC int
14562306a36Sopenharmony_cixchk_read_leaf_dir_buf(
14662306a36Sopenharmony_ci	struct xfs_trans	*tp,
14762306a36Sopenharmony_ci	struct xfs_inode	*dp,
14862306a36Sopenharmony_ci	struct xfs_da_geometry	*geo,
14962306a36Sopenharmony_ci	xfs_dir2_off_t		*curoff,
15062306a36Sopenharmony_ci	struct xfs_buf		**bpp)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	struct xfs_iext_cursor	icur;
15362306a36Sopenharmony_ci	struct xfs_bmbt_irec	map;
15462306a36Sopenharmony_ci	struct xfs_ifork	*ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK);
15562306a36Sopenharmony_ci	xfs_dablk_t		last_da;
15662306a36Sopenharmony_ci	xfs_dablk_t		map_off;
15762306a36Sopenharmony_ci	xfs_dir2_off_t		new_off;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	*bpp = NULL;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/*
16262306a36Sopenharmony_ci	 * Look for mapped directory blocks at or above the current offset.
16362306a36Sopenharmony_ci	 * Truncate down to the nearest directory block to start the scanning
16462306a36Sopenharmony_ci	 * operation.
16562306a36Sopenharmony_ci	 */
16662306a36Sopenharmony_ci	last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET);
16762306a36Sopenharmony_ci	map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *curoff));
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (!xfs_iext_lookup_extent(dp, ifp, map_off, &icur, &map))
17062306a36Sopenharmony_ci		return 0;
17162306a36Sopenharmony_ci	if (map.br_startoff >= last_da)
17262306a36Sopenharmony_ci		return 0;
17362306a36Sopenharmony_ci	xfs_trim_extent(&map, map_off, last_da - map_off);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/* Read the directory block of that first mapping. */
17662306a36Sopenharmony_ci	new_off = xfs_dir2_da_to_byte(geo, map.br_startoff);
17762306a36Sopenharmony_ci	if (new_off > *curoff)
17862306a36Sopenharmony_ci		*curoff = new_off;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return xfs_dir3_data_read(tp, dp, map.br_startoff, 0, bpp);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci/* Call a function for every entry in a leaf directory. */
18462306a36Sopenharmony_ciSTATIC int
18562306a36Sopenharmony_cixchk_dir_walk_leaf(
18662306a36Sopenharmony_ci	struct xfs_scrub	*sc,
18762306a36Sopenharmony_ci	struct xfs_inode	*dp,
18862306a36Sopenharmony_ci	xchk_dirent_fn		dirent_fn,
18962306a36Sopenharmony_ci	void			*priv)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct xfs_mount	*mp = dp->i_mount;
19262306a36Sopenharmony_ci	struct xfs_da_geometry	*geo = mp->m_dir_geo;
19362306a36Sopenharmony_ci	struct xfs_buf		*bp = NULL;
19462306a36Sopenharmony_ci	xfs_dir2_off_t		curoff = 0;
19562306a36Sopenharmony_ci	unsigned int		offset = 0;
19662306a36Sopenharmony_ci	int			error;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/* Iterate every directory offset in this directory. */
19962306a36Sopenharmony_ci	while (curoff < XFS_DIR2_LEAF_OFFSET) {
20062306a36Sopenharmony_ci		struct xfs_name			name = { };
20162306a36Sopenharmony_ci		struct xfs_dir2_data_unused	*dup;
20262306a36Sopenharmony_ci		struct xfs_dir2_data_entry	*dep;
20362306a36Sopenharmony_ci		xfs_ino_t			ino;
20462306a36Sopenharmony_ci		unsigned int			length;
20562306a36Sopenharmony_ci		xfs_dir2_dataptr_t		dapos;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		/*
20862306a36Sopenharmony_ci		 * If we have no buffer, or we're off the end of the
20962306a36Sopenharmony_ci		 * current buffer, need to get another one.
21062306a36Sopenharmony_ci		 */
21162306a36Sopenharmony_ci		if (!bp || offset >= geo->blksize) {
21262306a36Sopenharmony_ci			if (bp) {
21362306a36Sopenharmony_ci				xfs_trans_brelse(sc->tp, bp);
21462306a36Sopenharmony_ci				bp = NULL;
21562306a36Sopenharmony_ci			}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci			error = xchk_read_leaf_dir_buf(sc->tp, dp, geo, &curoff,
21862306a36Sopenharmony_ci					&bp);
21962306a36Sopenharmony_ci			if (error || !bp)
22062306a36Sopenharmony_ci				break;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci			/*
22362306a36Sopenharmony_ci			 * Find our position in the block.
22462306a36Sopenharmony_ci			 */
22562306a36Sopenharmony_ci			offset = geo->data_entry_offset;
22662306a36Sopenharmony_ci			curoff += geo->data_entry_offset;
22762306a36Sopenharmony_ci		}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		/* Skip an empty entry. */
23062306a36Sopenharmony_ci		dup = bp->b_addr + offset;
23162306a36Sopenharmony_ci		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
23262306a36Sopenharmony_ci			length = be16_to_cpu(dup->length);
23362306a36Sopenharmony_ci			offset += length;
23462306a36Sopenharmony_ci			curoff += length;
23562306a36Sopenharmony_ci			continue;
23662306a36Sopenharmony_ci		}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		/* Otherwise, find the next entry and report it. */
23962306a36Sopenharmony_ci		dep = bp->b_addr + offset;
24062306a36Sopenharmony_ci		length = xfs_dir2_data_entsize(mp, dep->namelen);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		dapos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;
24362306a36Sopenharmony_ci		ino = be64_to_cpu(dep->inumber);
24462306a36Sopenharmony_ci		name.name = dep->name;
24562306a36Sopenharmony_ci		name.len = dep->namelen;
24662306a36Sopenharmony_ci		name.type = xfs_dir2_data_get_ftype(mp, dep);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		error = dirent_fn(sc, dp, dapos, &name, ino, priv);
24962306a36Sopenharmony_ci		if (error)
25062306a36Sopenharmony_ci			break;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci		/* Advance to the next entry. */
25362306a36Sopenharmony_ci		offset += length;
25462306a36Sopenharmony_ci		curoff += length;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (bp)
25862306a36Sopenharmony_ci		xfs_trans_brelse(sc->tp, bp);
25962306a36Sopenharmony_ci	return error;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci/*
26362306a36Sopenharmony_ci * Call a function for every entry in a directory.
26462306a36Sopenharmony_ci *
26562306a36Sopenharmony_ci * Callers must hold the ILOCK.  File types are XFS_DIR3_FT_*.
26662306a36Sopenharmony_ci */
26762306a36Sopenharmony_ciint
26862306a36Sopenharmony_cixchk_dir_walk(
26962306a36Sopenharmony_ci	struct xfs_scrub	*sc,
27062306a36Sopenharmony_ci	struct xfs_inode	*dp,
27162306a36Sopenharmony_ci	xchk_dirent_fn		dirent_fn,
27262306a36Sopenharmony_ci	void			*priv)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct xfs_da_args	args = {
27562306a36Sopenharmony_ci		.dp		= dp,
27662306a36Sopenharmony_ci		.geo		= dp->i_mount->m_dir_geo,
27762306a36Sopenharmony_ci		.trans		= sc->tp,
27862306a36Sopenharmony_ci	};
27962306a36Sopenharmony_ci	bool			isblock;
28062306a36Sopenharmony_ci	int			error;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (xfs_is_shutdown(dp->i_mount))
28362306a36Sopenharmony_ci		return -EIO;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
28662306a36Sopenharmony_ci	ASSERT(xfs_isilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
28962306a36Sopenharmony_ci		return xchk_dir_walk_sf(sc, dp, dirent_fn, priv);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	/* dir2 functions require that the data fork is loaded */
29262306a36Sopenharmony_ci	error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK);
29362306a36Sopenharmony_ci	if (error)
29462306a36Sopenharmony_ci		return error;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	error = xfs_dir2_isblock(&args, &isblock);
29762306a36Sopenharmony_ci	if (error)
29862306a36Sopenharmony_ci		return error;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if (isblock)
30162306a36Sopenharmony_ci		return xchk_dir_walk_block(sc, dp, dirent_fn, priv);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	return xchk_dir_walk_leaf(sc, dp, dirent_fn, priv);
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci/*
30762306a36Sopenharmony_ci * Look up the inode number for an exact name in a directory.
30862306a36Sopenharmony_ci *
30962306a36Sopenharmony_ci * Callers must hold the ILOCK.  File types are XFS_DIR3_FT_*.  Names are not
31062306a36Sopenharmony_ci * checked for correctness.
31162306a36Sopenharmony_ci */
31262306a36Sopenharmony_ciint
31362306a36Sopenharmony_cixchk_dir_lookup(
31462306a36Sopenharmony_ci	struct xfs_scrub	*sc,
31562306a36Sopenharmony_ci	struct xfs_inode	*dp,
31662306a36Sopenharmony_ci	const struct xfs_name	*name,
31762306a36Sopenharmony_ci	xfs_ino_t		*ino)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct xfs_da_args	args = {
32062306a36Sopenharmony_ci		.dp		= dp,
32162306a36Sopenharmony_ci		.geo		= dp->i_mount->m_dir_geo,
32262306a36Sopenharmony_ci		.trans		= sc->tp,
32362306a36Sopenharmony_ci		.name		= name->name,
32462306a36Sopenharmony_ci		.namelen	= name->len,
32562306a36Sopenharmony_ci		.filetype	= name->type,
32662306a36Sopenharmony_ci		.hashval	= xfs_dir2_hashname(dp->i_mount, name),
32762306a36Sopenharmony_ci		.whichfork	= XFS_DATA_FORK,
32862306a36Sopenharmony_ci		.op_flags	= XFS_DA_OP_OKNOENT,
32962306a36Sopenharmony_ci	};
33062306a36Sopenharmony_ci	bool			isblock, isleaf;
33162306a36Sopenharmony_ci	int			error;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	if (xfs_is_shutdown(dp->i_mount))
33462306a36Sopenharmony_ci		return -EIO;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
33762306a36Sopenharmony_ci	ASSERT(xfs_isilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
34062306a36Sopenharmony_ci		error = xfs_dir2_sf_lookup(&args);
34162306a36Sopenharmony_ci		goto out_check_rval;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	/* dir2 functions require that the data fork is loaded */
34562306a36Sopenharmony_ci	error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK);
34662306a36Sopenharmony_ci	if (error)
34762306a36Sopenharmony_ci		return error;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	error = xfs_dir2_isblock(&args, &isblock);
35062306a36Sopenharmony_ci	if (error)
35162306a36Sopenharmony_ci		return error;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	if (isblock) {
35462306a36Sopenharmony_ci		error = xfs_dir2_block_lookup(&args);
35562306a36Sopenharmony_ci		goto out_check_rval;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	error = xfs_dir2_isleaf(&args, &isleaf);
35962306a36Sopenharmony_ci	if (error)
36062306a36Sopenharmony_ci		return error;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (isleaf) {
36362306a36Sopenharmony_ci		error = xfs_dir2_leaf_lookup(&args);
36462306a36Sopenharmony_ci		goto out_check_rval;
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	error = xfs_dir2_node_lookup(&args);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ciout_check_rval:
37062306a36Sopenharmony_ci	if (error == -EEXIST)
37162306a36Sopenharmony_ci		error = 0;
37262306a36Sopenharmony_ci	if (!error)
37362306a36Sopenharmony_ci		*ino = args.inumber;
37462306a36Sopenharmony_ci	return error;
37562306a36Sopenharmony_ci}
376