Linux Audio

Check our new training course

Loading...
v6.9.4
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright (C) 2022-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_log_format.h"
 11#include "xfs_trans_resv.h"
 12#include "xfs_mount.h"
 13#include "xfs_inode.h"
 14#include "xfs_dir2.h"
 15#include "xfs_dir2_priv.h"
 16#include "xfs_trace.h"
 17#include "xfs_bmap.h"
 18#include "xfs_trans.h"
 19#include "xfs_error.h"
 20#include "scrub/scrub.h"
 
 21#include "scrub/readdir.h"
 22
 23/* Call a function for every entry in a shortform directory. */
 24STATIC int
 25xchk_dir_walk_sf(
 26	struct xfs_scrub	*sc,
 27	struct xfs_inode	*dp,
 28	xchk_dirent_fn		dirent_fn,
 29	void			*priv)
 30{
 31	struct xfs_name		name = {
 32		.name		= ".",
 33		.len		= 1,
 34		.type		= XFS_DIR3_FT_DIR,
 35	};
 36	struct xfs_mount	*mp = dp->i_mount;
 37	struct xfs_da_geometry	*geo = mp->m_dir_geo;
 38	struct xfs_dir2_sf_entry *sfep;
 39	struct xfs_dir2_sf_hdr	*sfp = dp->i_df.if_data;
 40	xfs_ino_t		ino;
 41	xfs_dir2_dataptr_t	dapos;
 42	unsigned int		i;
 43	int			error;
 44
 45	ASSERT(dp->i_df.if_bytes == dp->i_disk_size);
 46	ASSERT(sfp != NULL);
 47
 48	/* dot entry */
 49	dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
 50			geo->data_entry_offset);
 51
 52	error = dirent_fn(sc, dp, dapos, &name, dp->i_ino, priv);
 53	if (error)
 54		return error;
 55
 56	/* dotdot entry */
 57	dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
 58			geo->data_entry_offset +
 59			xfs_dir2_data_entsize(mp, sizeof(".") - 1));
 60	ino = xfs_dir2_sf_get_parent_ino(sfp);
 61	name.name = "..";
 62	name.len = 2;
 63
 64	error = dirent_fn(sc, dp, dapos, &name, ino, priv);
 65	if (error)
 66		return error;
 67
 68	/* iterate everything else */
 69	sfep = xfs_dir2_sf_firstentry(sfp);
 70	for (i = 0; i < sfp->count; i++) {
 71		dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
 72				xfs_dir2_sf_get_offset(sfep));
 73		ino = xfs_dir2_sf_get_ino(mp, sfp, sfep);
 74		name.name = sfep->name;
 75		name.len = sfep->namelen;
 76		name.type = xfs_dir2_sf_get_ftype(mp, sfep);
 77
 78		error = dirent_fn(sc, dp, dapos, &name, ino, priv);
 79		if (error)
 80			return error;
 81
 82		sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
 83	}
 84
 85	return 0;
 86}
 87
 88/* Call a function for every entry in a block directory. */
 89STATIC int
 90xchk_dir_walk_block(
 91	struct xfs_scrub	*sc,
 92	struct xfs_inode	*dp,
 93	xchk_dirent_fn		dirent_fn,
 94	void			*priv)
 95{
 96	struct xfs_mount	*mp = dp->i_mount;
 97	struct xfs_da_geometry	*geo = mp->m_dir_geo;
 98	struct xfs_buf		*bp;
 99	unsigned int		off, next_off, end;
100	int			error;
101
102	error = xfs_dir3_block_read(sc->tp, dp, &bp);
103	if (error)
104		return error;
105
106	/* Walk each directory entry. */
107	end = xfs_dir3_data_end_offset(geo, bp->b_addr);
108	for (off = geo->data_entry_offset; off < end; off = next_off) {
109		struct xfs_name			name = { };
110		struct xfs_dir2_data_unused	*dup = bp->b_addr + off;
111		struct xfs_dir2_data_entry	*dep = bp->b_addr + off;
112		xfs_ino_t			ino;
113		xfs_dir2_dataptr_t		dapos;
114
115		/* Skip an empty entry. */
116		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
117			next_off = off + be16_to_cpu(dup->length);
118			continue;
119		}
120
121		/* Otherwise, find the next entry and report it. */
122		next_off = off + xfs_dir2_data_entsize(mp, dep->namelen);
123		if (next_off > end)
124			break;
125
126		dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, off);
127		ino = be64_to_cpu(dep->inumber);
128		name.name = dep->name;
129		name.len = dep->namelen;
130		name.type = xfs_dir2_data_get_ftype(mp, dep);
131
132		error = dirent_fn(sc, dp, dapos, &name, ino, priv);
133		if (error)
134			break;
135	}
136
137	xfs_trans_brelse(sc->tp, bp);
138	return error;
139}
140
141/* Read a leaf-format directory buffer. */
142STATIC int
143xchk_read_leaf_dir_buf(
144	struct xfs_trans	*tp,
145	struct xfs_inode	*dp,
146	struct xfs_da_geometry	*geo,
147	xfs_dir2_off_t		*curoff,
148	struct xfs_buf		**bpp)
149{
150	struct xfs_iext_cursor	icur;
151	struct xfs_bmbt_irec	map;
152	struct xfs_ifork	*ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK);
153	xfs_dablk_t		last_da;
154	xfs_dablk_t		map_off;
155	xfs_dir2_off_t		new_off;
156
157	*bpp = NULL;
158
159	/*
160	 * Look for mapped directory blocks at or above the current offset.
161	 * Truncate down to the nearest directory block to start the scanning
162	 * operation.
163	 */
164	last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET);
165	map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *curoff));
166
167	if (!xfs_iext_lookup_extent(dp, ifp, map_off, &icur, &map))
168		return 0;
169	if (map.br_startoff >= last_da)
170		return 0;
171	xfs_trim_extent(&map, map_off, last_da - map_off);
172
173	/* Read the directory block of that first mapping. */
174	new_off = xfs_dir2_da_to_byte(geo, map.br_startoff);
175	if (new_off > *curoff)
176		*curoff = new_off;
177
178	return xfs_dir3_data_read(tp, dp, map.br_startoff, 0, bpp);
179}
180
181/* Call a function for every entry in a leaf directory. */
182STATIC int
183xchk_dir_walk_leaf(
184	struct xfs_scrub	*sc,
185	struct xfs_inode	*dp,
186	xchk_dirent_fn		dirent_fn,
187	void			*priv)
188{
189	struct xfs_mount	*mp = dp->i_mount;
190	struct xfs_da_geometry	*geo = mp->m_dir_geo;
191	struct xfs_buf		*bp = NULL;
192	xfs_dir2_off_t		curoff = 0;
193	unsigned int		offset = 0;
194	int			error;
195
196	/* Iterate every directory offset in this directory. */
197	while (curoff < XFS_DIR2_LEAF_OFFSET) {
198		struct xfs_name			name = { };
199		struct xfs_dir2_data_unused	*dup;
200		struct xfs_dir2_data_entry	*dep;
201		xfs_ino_t			ino;
202		unsigned int			length;
203		xfs_dir2_dataptr_t		dapos;
204
205		/*
206		 * If we have no buffer, or we're off the end of the
207		 * current buffer, need to get another one.
208		 */
209		if (!bp || offset >= geo->blksize) {
210			if (bp) {
211				xfs_trans_brelse(sc->tp, bp);
212				bp = NULL;
213			}
214
215			error = xchk_read_leaf_dir_buf(sc->tp, dp, geo, &curoff,
216					&bp);
217			if (error || !bp)
218				break;
219
220			/*
221			 * Find our position in the block.
222			 */
223			offset = geo->data_entry_offset;
224			curoff += geo->data_entry_offset;
225		}
226
227		/* Skip an empty entry. */
228		dup = bp->b_addr + offset;
229		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
230			length = be16_to_cpu(dup->length);
231			offset += length;
232			curoff += length;
233			continue;
234		}
235
236		/* Otherwise, find the next entry and report it. */
237		dep = bp->b_addr + offset;
238		length = xfs_dir2_data_entsize(mp, dep->namelen);
239
240		dapos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;
241		ino = be64_to_cpu(dep->inumber);
242		name.name = dep->name;
243		name.len = dep->namelen;
244		name.type = xfs_dir2_data_get_ftype(mp, dep);
245
246		error = dirent_fn(sc, dp, dapos, &name, ino, priv);
247		if (error)
248			break;
249
250		/* Advance to the next entry. */
251		offset += length;
252		curoff += length;
253	}
254
255	if (bp)
256		xfs_trans_brelse(sc->tp, bp);
257	return error;
258}
259
260/*
261 * Call a function for every entry in a directory.
262 *
263 * Callers must hold the ILOCK.  File types are XFS_DIR3_FT_*.
264 */
265int
266xchk_dir_walk(
267	struct xfs_scrub	*sc,
268	struct xfs_inode	*dp,
269	xchk_dirent_fn		dirent_fn,
270	void			*priv)
271{
272	struct xfs_da_args	args = {
273		.dp		= dp,
274		.geo		= dp->i_mount->m_dir_geo,
275		.trans		= sc->tp,
 
276	};
277	bool			isblock;
278	int			error;
279
280	if (xfs_is_shutdown(dp->i_mount))
281		return -EIO;
282
283	ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
284	xfs_assert_ilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL);
285
286	if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
 
287		return xchk_dir_walk_sf(sc, dp, dirent_fn, priv);
288
289	/* dir2 functions require that the data fork is loaded */
290	error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK);
291	if (error)
292		return error;
293
294	error = xfs_dir2_isblock(&args, &isblock);
295	if (error)
296		return error;
297
298	if (isblock)
299		return xchk_dir_walk_block(sc, dp, dirent_fn, priv);
300
301	return xchk_dir_walk_leaf(sc, dp, dirent_fn, priv);
 
 
 
 
302}
303
304/*
305 * Look up the inode number for an exact name in a directory.
306 *
307 * Callers must hold the ILOCK.  File types are XFS_DIR3_FT_*.  Names are not
308 * checked for correctness.
309 */
310int
311xchk_dir_lookup(
312	struct xfs_scrub	*sc,
313	struct xfs_inode	*dp,
314	const struct xfs_name	*name,
315	xfs_ino_t		*ino)
316{
317	struct xfs_da_args	args = {
318		.dp		= dp,
319		.geo		= dp->i_mount->m_dir_geo,
320		.trans		= sc->tp,
321		.name		= name->name,
322		.namelen	= name->len,
323		.filetype	= name->type,
324		.hashval	= xfs_dir2_hashname(dp->i_mount, name),
325		.whichfork	= XFS_DATA_FORK,
326		.op_flags	= XFS_DA_OP_OKNOENT,
 
327	};
328	bool			isblock, isleaf;
329	int			error;
330
331	if (xfs_is_shutdown(dp->i_mount))
332		return -EIO;
333
 
 
 
 
 
 
 
334	ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
335	xfs_assert_ilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL);
336
337	if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
338		error = xfs_dir2_sf_lookup(&args);
339		goto out_check_rval;
340	}
 
341
342	/* dir2 functions require that the data fork is loaded */
343	error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK);
344	if (error)
345		return error;
 
 
 
 
 
 
 
 
346
347	error = xfs_dir2_isblock(&args, &isblock);
348	if (error)
349		return error;
350
351	if (isblock) {
352		error = xfs_dir2_block_lookup(&args);
353		goto out_check_rval;
354	}
 
 
 
 
 
 
 
 
 
355
356	error = xfs_dir2_isleaf(&args, &isleaf);
357	if (error)
358		return error;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
360	if (isleaf) {
361		error = xfs_dir2_leaf_lookup(&args);
362		goto out_check_rval;
 
 
 
 
 
 
 
 
363	}
364
365	error = xfs_dir2_node_lookup(&args);
 
 
 
366
367out_check_rval:
368	if (error == -EEXIST)
369		error = 0;
370	if (!error)
371		*ino = args.inumber;
372	return error;
373}
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright (C) 2022-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_log_format.h"
 11#include "xfs_trans_resv.h"
 12#include "xfs_mount.h"
 13#include "xfs_inode.h"
 14#include "xfs_dir2.h"
 15#include "xfs_dir2_priv.h"
 16#include "xfs_trace.h"
 17#include "xfs_bmap.h"
 18#include "xfs_trans.h"
 19#include "xfs_error.h"
 20#include "scrub/scrub.h"
 21#include "scrub/common.h"
 22#include "scrub/readdir.h"
 23
 24/* Call a function for every entry in a shortform directory. */
 25STATIC int
 26xchk_dir_walk_sf(
 27	struct xfs_scrub	*sc,
 28	struct xfs_inode	*dp,
 29	xchk_dirent_fn		dirent_fn,
 30	void			*priv)
 31{
 32	struct xfs_name		name = {
 33		.name		= ".",
 34		.len		= 1,
 35		.type		= XFS_DIR3_FT_DIR,
 36	};
 37	struct xfs_mount	*mp = dp->i_mount;
 38	struct xfs_da_geometry	*geo = mp->m_dir_geo;
 39	struct xfs_dir2_sf_entry *sfep;
 40	struct xfs_dir2_sf_hdr	*sfp = dp->i_df.if_data;
 41	xfs_ino_t		ino;
 42	xfs_dir2_dataptr_t	dapos;
 43	unsigned int		i;
 44	int			error;
 45
 46	ASSERT(dp->i_df.if_bytes == dp->i_disk_size);
 47	ASSERT(sfp != NULL);
 48
 49	/* dot entry */
 50	dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
 51			geo->data_entry_offset);
 52
 53	error = dirent_fn(sc, dp, dapos, &name, dp->i_ino, priv);
 54	if (error)
 55		return error;
 56
 57	/* dotdot entry */
 58	dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
 59			geo->data_entry_offset +
 60			xfs_dir2_data_entsize(mp, sizeof(".") - 1));
 61	ino = xfs_dir2_sf_get_parent_ino(sfp);
 62	name.name = "..";
 63	name.len = 2;
 64
 65	error = dirent_fn(sc, dp, dapos, &name, ino, priv);
 66	if (error)
 67		return error;
 68
 69	/* iterate everything else */
 70	sfep = xfs_dir2_sf_firstentry(sfp);
 71	for (i = 0; i < sfp->count; i++) {
 72		dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
 73				xfs_dir2_sf_get_offset(sfep));
 74		ino = xfs_dir2_sf_get_ino(mp, sfp, sfep);
 75		name.name = sfep->name;
 76		name.len = sfep->namelen;
 77		name.type = xfs_dir2_sf_get_ftype(mp, sfep);
 78
 79		error = dirent_fn(sc, dp, dapos, &name, ino, priv);
 80		if (error)
 81			return error;
 82
 83		sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
 84	}
 85
 86	return 0;
 87}
 88
 89/* Call a function for every entry in a block directory. */
 90STATIC int
 91xchk_dir_walk_block(
 92	struct xfs_scrub	*sc,
 93	struct xfs_inode	*dp,
 94	xchk_dirent_fn		dirent_fn,
 95	void			*priv)
 96{
 97	struct xfs_mount	*mp = dp->i_mount;
 98	struct xfs_da_geometry	*geo = mp->m_dir_geo;
 99	struct xfs_buf		*bp;
100	unsigned int		off, next_off, end;
101	int			error;
102
103	error = xfs_dir3_block_read(sc->tp, dp, dp->i_ino, &bp);
104	if (error)
105		return error;
106
107	/* Walk each directory entry. */
108	end = xfs_dir3_data_end_offset(geo, bp->b_addr);
109	for (off = geo->data_entry_offset; off < end; off = next_off) {
110		struct xfs_name			name = { };
111		struct xfs_dir2_data_unused	*dup = bp->b_addr + off;
112		struct xfs_dir2_data_entry	*dep = bp->b_addr + off;
113		xfs_ino_t			ino;
114		xfs_dir2_dataptr_t		dapos;
115
116		/* Skip an empty entry. */
117		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
118			next_off = off + be16_to_cpu(dup->length);
119			continue;
120		}
121
122		/* Otherwise, find the next entry and report it. */
123		next_off = off + xfs_dir2_data_entsize(mp, dep->namelen);
124		if (next_off > end)
125			break;
126
127		dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, off);
128		ino = be64_to_cpu(dep->inumber);
129		name.name = dep->name;
130		name.len = dep->namelen;
131		name.type = xfs_dir2_data_get_ftype(mp, dep);
132
133		error = dirent_fn(sc, dp, dapos, &name, ino, priv);
134		if (error)
135			break;
136	}
137
138	xfs_trans_brelse(sc->tp, bp);
139	return error;
140}
141
142/* Read a leaf-format directory buffer. */
143STATIC int
144xchk_read_leaf_dir_buf(
145	struct xfs_trans	*tp,
146	struct xfs_inode	*dp,
147	struct xfs_da_geometry	*geo,
148	xfs_dir2_off_t		*curoff,
149	struct xfs_buf		**bpp)
150{
151	struct xfs_iext_cursor	icur;
152	struct xfs_bmbt_irec	map;
153	struct xfs_ifork	*ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK);
154	xfs_dablk_t		last_da;
155	xfs_dablk_t		map_off;
156	xfs_dir2_off_t		new_off;
157
158	*bpp = NULL;
159
160	/*
161	 * Look for mapped directory blocks at or above the current offset.
162	 * Truncate down to the nearest directory block to start the scanning
163	 * operation.
164	 */
165	last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET);
166	map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *curoff));
167
168	if (!xfs_iext_lookup_extent(dp, ifp, map_off, &icur, &map))
169		return 0;
170	if (map.br_startoff >= last_da)
171		return 0;
172	xfs_trim_extent(&map, map_off, last_da - map_off);
173
174	/* Read the directory block of that first mapping. */
175	new_off = xfs_dir2_da_to_byte(geo, map.br_startoff);
176	if (new_off > *curoff)
177		*curoff = new_off;
178
179	return xfs_dir3_data_read(tp, dp, dp->i_ino, map.br_startoff, 0, bpp);
180}
181
182/* Call a function for every entry in a leaf directory. */
183STATIC int
184xchk_dir_walk_leaf(
185	struct xfs_scrub	*sc,
186	struct xfs_inode	*dp,
187	xchk_dirent_fn		dirent_fn,
188	void			*priv)
189{
190	struct xfs_mount	*mp = dp->i_mount;
191	struct xfs_da_geometry	*geo = mp->m_dir_geo;
192	struct xfs_buf		*bp = NULL;
193	xfs_dir2_off_t		curoff = 0;
194	unsigned int		offset = 0;
195	int			error;
196
197	/* Iterate every directory offset in this directory. */
198	while (curoff < XFS_DIR2_LEAF_OFFSET) {
199		struct xfs_name			name = { };
200		struct xfs_dir2_data_unused	*dup;
201		struct xfs_dir2_data_entry	*dep;
202		xfs_ino_t			ino;
203		unsigned int			length;
204		xfs_dir2_dataptr_t		dapos;
205
206		/*
207		 * If we have no buffer, or we're off the end of the
208		 * current buffer, need to get another one.
209		 */
210		if (!bp || offset >= geo->blksize) {
211			if (bp) {
212				xfs_trans_brelse(sc->tp, bp);
213				bp = NULL;
214			}
215
216			error = xchk_read_leaf_dir_buf(sc->tp, dp, geo, &curoff,
217					&bp);
218			if (error || !bp)
219				break;
220
221			/*
222			 * Find our position in the block.
223			 */
224			offset = geo->data_entry_offset;
225			curoff += geo->data_entry_offset;
226		}
227
228		/* Skip an empty entry. */
229		dup = bp->b_addr + offset;
230		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
231			length = be16_to_cpu(dup->length);
232			offset += length;
233			curoff += length;
234			continue;
235		}
236
237		/* Otherwise, find the next entry and report it. */
238		dep = bp->b_addr + offset;
239		length = xfs_dir2_data_entsize(mp, dep->namelen);
240
241		dapos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;
242		ino = be64_to_cpu(dep->inumber);
243		name.name = dep->name;
244		name.len = dep->namelen;
245		name.type = xfs_dir2_data_get_ftype(mp, dep);
246
247		error = dirent_fn(sc, dp, dapos, &name, ino, priv);
248		if (error)
249			break;
250
251		/* Advance to the next entry. */
252		offset += length;
253		curoff += length;
254	}
255
256	if (bp)
257		xfs_trans_brelse(sc->tp, bp);
258	return error;
259}
260
261/*
262 * Call a function for every entry in a directory.
263 *
264 * Callers must hold the ILOCK.  File types are XFS_DIR3_FT_*.
265 */
266int
267xchk_dir_walk(
268	struct xfs_scrub	*sc,
269	struct xfs_inode	*dp,
270	xchk_dirent_fn		dirent_fn,
271	void			*priv)
272{
273	struct xfs_da_args	args = {
274		.dp		= dp,
275		.geo		= dp->i_mount->m_dir_geo,
276		.trans		= sc->tp,
277		.owner		= dp->i_ino,
278	};
 
279	int			error;
280
281	if (xfs_is_shutdown(dp->i_mount))
282		return -EIO;
283
284	ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
285	xfs_assert_ilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL);
286
287	switch (xfs_dir2_format(&args, &error)) {
288	case XFS_DIR2_FMT_SF:
289		return xchk_dir_walk_sf(sc, dp, dirent_fn, priv);
290	case XFS_DIR2_FMT_BLOCK:
 
 
 
 
 
 
 
 
 
 
291		return xchk_dir_walk_block(sc, dp, dirent_fn, priv);
292	case XFS_DIR2_FMT_LEAF:
293	case XFS_DIR2_FMT_NODE:
294		return xchk_dir_walk_leaf(sc, dp, dirent_fn, priv);
295	default:
296		return error;
297	}
298}
299
300/*
301 * Look up the inode number for an exact name in a directory.
302 *
303 * Callers must hold the ILOCK.  File types are XFS_DIR3_FT_*.  Names are not
304 * checked for correctness.
305 */
306int
307xchk_dir_lookup(
308	struct xfs_scrub	*sc,
309	struct xfs_inode	*dp,
310	const struct xfs_name	*name,
311	xfs_ino_t		*ino)
312{
313	struct xfs_da_args	args = {
314		.dp		= dp,
315		.geo		= dp->i_mount->m_dir_geo,
316		.trans		= sc->tp,
317		.name		= name->name,
318		.namelen	= name->len,
319		.filetype	= name->type,
320		.hashval	= xfs_dir2_hashname(dp->i_mount, name),
321		.whichfork	= XFS_DATA_FORK,
322		.op_flags	= XFS_DA_OP_OKNOENT,
323		.owner		= dp->i_ino,
324	};
 
325	int			error;
326
327	if (xfs_is_shutdown(dp->i_mount))
328		return -EIO;
329
330	/*
331	 * A temporary directory's block headers are written with the owner
332	 * set to sc->ip, so we must switch the owner here for the lookup.
333	 */
334	if (dp == sc->tempip)
335		args.owner = sc->ip->i_ino;
336
337	ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
338	xfs_assert_ilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL);
339
340	error = xfs_dir_lookup_args(&args);
341	if (!error)
342		*ino = args.inumber;
343	return error;
344}
345
346/*
347 * Try to grab the IOLOCK and ILOCK of sc->ip and ip, returning @ip's lock
348 * state.  The caller may have a transaction, so we must use trylock for both
349 * IOLOCKs.
350 */
351static inline unsigned int
352xchk_dir_trylock_both(
353	struct xfs_scrub	*sc,
354	struct xfs_inode	*ip)
355{
356	if (!xchk_ilock_nowait(sc, XFS_IOLOCK_EXCL))
357		return 0;
358
359	if (!xfs_ilock_nowait(ip, XFS_IOLOCK_SHARED))
360		goto parent_iolock;
 
361
362	xchk_ilock(sc, XFS_ILOCK_EXCL);
363	if (!xfs_ilock_nowait(ip, XFS_ILOCK_EXCL))
364		goto parent_ilock;
365
366	return XFS_IOLOCK_SHARED | XFS_ILOCK_EXCL;
367
368parent_ilock:
369	xchk_iunlock(sc, XFS_ILOCK_EXCL);
370	xfs_iunlock(ip, XFS_IOLOCK_SHARED);
371parent_iolock:
372	xchk_iunlock(sc, XFS_IOLOCK_EXCL);
373	return 0;
374}
375
376/*
377 * Try for a limited time to grab the IOLOCK and ILOCK of both the scrub target
378 * (@sc->ip) and the inode at the other end (@ip) of a directory or parent
379 * pointer link so that we can check that link.
380 *
381 * We do not know ahead of time that the directory tree is /not/ corrupt, so we
382 * cannot use the "lock two inode" functions because we do not know that there
383 * is not a racing thread trying to take the locks in opposite order.  First
384 * take IOLOCK_EXCL of the scrub target, and then try to take IOLOCK_SHARED
385 * of @ip to synchronize with the VFS.  Next, take ILOCK_EXCL of the scrub
386 * target and @ip to synchronize with XFS.
387 *
388 * If the trylocks succeed, *lockmode will be set to the locks held for @ip;
389 * @sc->ilock_flags will be set for the locks held for @sc->ip; and zero will
390 * be returned.  If not, returns -EDEADLOCK to try again; or -ETIMEDOUT if
391 * XCHK_TRY_HARDER was set.  Returns -EINTR if the process has been killed.
392 */
393int
394xchk_dir_trylock_for_pptrs(
395	struct xfs_scrub	*sc,
396	struct xfs_inode	*ip,
397	unsigned int		*lockmode)
398{
399	unsigned int		nr;
400	int			error = 0;
401
402	ASSERT(sc->ilock_flags == 0);
403
404	for (nr = 0; nr < HZ; nr++) {
405		*lockmode = xchk_dir_trylock_both(sc, ip);
406		if (*lockmode)
407			return 0;
408
409		if (xchk_should_terminate(sc, &error))
410			return error;
411
412		delay(1);
413	}
414
415	if (sc->flags & XCHK_TRY_HARDER) {
416		xchk_set_incomplete(sc);
417		return -ETIMEDOUT;
418	}
419
420	return -EDEADLOCK;
 
 
 
 
 
421}