xref: /openbmc/linux/fs/xfs/scrub/readdir.c (revision ad4455c6)
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. */
24 STATIC int
25 xchk_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. */
91 STATIC int
92 xchk_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. */
144 STATIC int
145 xchk_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. */
184 STATIC int
185 xchk_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  */
267 int
268 xchk_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  */
312 int
313 xchk_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 
369 out_check_rval:
370 	if (error == -EEXIST)
371 		error = 0;
372 	if (!error)
373 		*ino = args.inumber;
374 	return error;
375 }
376