xref: /kernel/linux/linux-6.6/fs/xfs/scrub/readdir.c (revision 62306a36)
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;
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(dp->i_df.if_u1.if_data != NULL);
47
48	sfp = (struct xfs_dir2_sf_hdr *)dp->i_df.if_u1.if_data;
49
50	/* dot entry */
51	dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
52			geo->data_entry_offset);
53
54	error = dirent_fn(sc, dp, dapos, &name, dp->i_ino, priv);
55	if (error)
56		return error;
57
58	/* dotdot entry */
59	dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
60			geo->data_entry_offset +
61			xfs_dir2_data_entsize(mp, sizeof(".") - 1));
62	ino = xfs_dir2_sf_get_parent_ino(sfp);
63	name.name = "..";
64	name.len = 2;
65
66	error = dirent_fn(sc, dp, dapos, &name, ino, priv);
67	if (error)
68		return error;
69
70	/* iterate everything else */
71	sfep = xfs_dir2_sf_firstentry(sfp);
72	for (i = 0; i < sfp->count; i++) {
73		dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
74				xfs_dir2_sf_get_offset(sfep));
75		ino = xfs_dir2_sf_get_ino(mp, sfp, sfep);
76		name.name = sfep->name;
77		name.len = sfep->namelen;
78		name.type = xfs_dir2_sf_get_ftype(mp, sfep);
79
80		error = dirent_fn(sc, dp, dapos, &name, ino, priv);
81		if (error)
82			return error;
83
84		sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
85	}
86
87	return 0;
88}
89
90/* Call a function for every entry in a block directory. */
91STATIC int
92xchk_dir_walk_block(
93	struct xfs_scrub	*sc,
94	struct xfs_inode	*dp,
95	xchk_dirent_fn		dirent_fn,
96	void			*priv)
97{
98	struct xfs_mount	*mp = dp->i_mount;
99	struct xfs_da_geometry	*geo = mp->m_dir_geo;
100	struct xfs_buf		*bp;
101	unsigned int		off, next_off, end;
102	int			error;
103
104	error = xfs_dir3_block_read(sc->tp, dp, &bp);
105	if (error)
106		return error;
107
108	/* Walk each directory entry. */
109	end = xfs_dir3_data_end_offset(geo, bp->b_addr);
110	for (off = geo->data_entry_offset; off < end; off = next_off) {
111		struct xfs_name			name = { };
112		struct xfs_dir2_data_unused	*dup = bp->b_addr + off;
113		struct xfs_dir2_data_entry	*dep = bp->b_addr + off;
114		xfs_ino_t			ino;
115		xfs_dir2_dataptr_t		dapos;
116
117		/* Skip an empty entry. */
118		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
119			next_off = off + be16_to_cpu(dup->length);
120			continue;
121		}
122
123		/* Otherwise, find the next entry and report it. */
124		next_off = off + xfs_dir2_data_entsize(mp, dep->namelen);
125		if (next_off > end)
126			break;
127
128		dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, off);
129		ino = be64_to_cpu(dep->inumber);
130		name.name = dep->name;
131		name.len = dep->namelen;
132		name.type = xfs_dir2_data_get_ftype(mp, dep);
133
134		error = dirent_fn(sc, dp, dapos, &name, ino, priv);
135		if (error)
136			break;
137	}
138
139	xfs_trans_brelse(sc->tp, bp);
140	return error;
141}
142
143/* Read a leaf-format directory buffer. */
144STATIC int
145xchk_read_leaf_dir_buf(
146	struct xfs_trans	*tp,
147	struct xfs_inode	*dp,
148	struct xfs_da_geometry	*geo,
149	xfs_dir2_off_t		*curoff,
150	struct xfs_buf		**bpp)
151{
152	struct xfs_iext_cursor	icur;
153	struct xfs_bmbt_irec	map;
154	struct xfs_ifork	*ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK);
155	xfs_dablk_t		last_da;
156	xfs_dablk_t		map_off;
157	xfs_dir2_off_t		new_off;
158
159	*bpp = NULL;
160
161	/*
162	 * Look for mapped directory blocks at or above the current offset.
163	 * Truncate down to the nearest directory block to start the scanning
164	 * operation.
165	 */
166	last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET);
167	map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *curoff));
168
169	if (!xfs_iext_lookup_extent(dp, ifp, map_off, &icur, &map))
170		return 0;
171	if (map.br_startoff >= last_da)
172		return 0;
173	xfs_trim_extent(&map, map_off, last_da - map_off);
174
175	/* Read the directory block of that first mapping. */
176	new_off = xfs_dir2_da_to_byte(geo, map.br_startoff);
177	if (new_off > *curoff)
178		*curoff = new_off;
179
180	return xfs_dir3_data_read(tp, dp, map.br_startoff, 0, bpp);
181}
182
183/* Call a function for every entry in a leaf directory. */
184STATIC int
185xchk_dir_walk_leaf(
186	struct xfs_scrub	*sc,
187	struct xfs_inode	*dp,
188	xchk_dirent_fn		dirent_fn,
189	void			*priv)
190{
191	struct xfs_mount	*mp = dp->i_mount;
192	struct xfs_da_geometry	*geo = mp->m_dir_geo;
193	struct xfs_buf		*bp = NULL;
194	xfs_dir2_off_t		curoff = 0;
195	unsigned int		offset = 0;
196	int			error;
197
198	/* Iterate every directory offset in this directory. */
199	while (curoff < XFS_DIR2_LEAF_OFFSET) {
200		struct xfs_name			name = { };
201		struct xfs_dir2_data_unused	*dup;
202		struct xfs_dir2_data_entry	*dep;
203		xfs_ino_t			ino;
204		unsigned int			length;
205		xfs_dir2_dataptr_t		dapos;
206
207		/*
208		 * If we have no buffer, or we're off the end of the
209		 * current buffer, need to get another one.
210		 */
211		if (!bp || offset >= geo->blksize) {
212			if (bp) {
213				xfs_trans_brelse(sc->tp, bp);
214				bp = NULL;
215			}
216
217			error = xchk_read_leaf_dir_buf(sc->tp, dp, geo, &curoff,
218					&bp);
219			if (error || !bp)
220				break;
221
222			/*
223			 * Find our position in the block.
224			 */
225			offset = geo->data_entry_offset;
226			curoff += geo->data_entry_offset;
227		}
228
229		/* Skip an empty entry. */
230		dup = bp->b_addr + offset;
231		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
232			length = be16_to_cpu(dup->length);
233			offset += length;
234			curoff += length;
235			continue;
236		}
237
238		/* Otherwise, find the next entry and report it. */
239		dep = bp->b_addr + offset;
240		length = xfs_dir2_data_entsize(mp, dep->namelen);
241
242		dapos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;
243		ino = be64_to_cpu(dep->inumber);
244		name.name = dep->name;
245		name.len = dep->namelen;
246		name.type = xfs_dir2_data_get_ftype(mp, dep);
247
248		error = dirent_fn(sc, dp, dapos, &name, ino, priv);
249		if (error)
250			break;
251
252		/* Advance to the next entry. */
253		offset += length;
254		curoff += length;
255	}
256
257	if (bp)
258		xfs_trans_brelse(sc->tp, bp);
259	return error;
260}
261
262/*
263 * Call a function for every entry in a directory.
264 *
265 * Callers must hold the ILOCK.  File types are XFS_DIR3_FT_*.
266 */
267int
268xchk_dir_walk(
269	struct xfs_scrub	*sc,
270	struct xfs_inode	*dp,
271	xchk_dirent_fn		dirent_fn,
272	void			*priv)
273{
274	struct xfs_da_args	args = {
275		.dp		= dp,
276		.geo		= dp->i_mount->m_dir_geo,
277		.trans		= sc->tp,
278	};
279	bool			isblock;
280	int			error;
281
282	if (xfs_is_shutdown(dp->i_mount))
283		return -EIO;
284
285	ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
286	ASSERT(xfs_isilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
287
288	if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
289		return xchk_dir_walk_sf(sc, dp, dirent_fn, priv);
290
291	/* dir2 functions require that the data fork is loaded */
292	error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK);
293	if (error)
294		return error;
295
296	error = xfs_dir2_isblock(&args, &isblock);
297	if (error)
298		return error;
299
300	if (isblock)
301		return xchk_dir_walk_block(sc, dp, dirent_fn, priv);
302
303	return xchk_dir_walk_leaf(sc, dp, dirent_fn, priv);
304}
305
306/*
307 * Look up the inode number for an exact name in a directory.
308 *
309 * Callers must hold the ILOCK.  File types are XFS_DIR3_FT_*.  Names are not
310 * checked for correctness.
311 */
312int
313xchk_dir_lookup(
314	struct xfs_scrub	*sc,
315	struct xfs_inode	*dp,
316	const struct xfs_name	*name,
317	xfs_ino_t		*ino)
318{
319	struct xfs_da_args	args = {
320		.dp		= dp,
321		.geo		= dp->i_mount->m_dir_geo,
322		.trans		= sc->tp,
323		.name		= name->name,
324		.namelen	= name->len,
325		.filetype	= name->type,
326		.hashval	= xfs_dir2_hashname(dp->i_mount, name),
327		.whichfork	= XFS_DATA_FORK,
328		.op_flags	= XFS_DA_OP_OKNOENT,
329	};
330	bool			isblock, isleaf;
331	int			error;
332
333	if (xfs_is_shutdown(dp->i_mount))
334		return -EIO;
335
336	ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
337	ASSERT(xfs_isilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
338
339	if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
340		error = xfs_dir2_sf_lookup(&args);
341		goto out_check_rval;
342	}
343
344	/* dir2 functions require that the data fork is loaded */
345	error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK);
346	if (error)
347		return error;
348
349	error = xfs_dir2_isblock(&args, &isblock);
350	if (error)
351		return error;
352
353	if (isblock) {
354		error = xfs_dir2_block_lookup(&args);
355		goto out_check_rval;
356	}
357
358	error = xfs_dir2_isleaf(&args, &isleaf);
359	if (error)
360		return error;
361
362	if (isleaf) {
363		error = xfs_dir2_leaf_lookup(&args);
364		goto out_check_rval;
365	}
366
367	error = xfs_dir2_node_lookup(&args);
368
369out_check_rval:
370	if (error == -EEXIST)
371		error = 0;
372	if (!error)
373		*ino = args.inumber;
374	return error;
375}
376