xref: /linux/fs/xfs/scrub/dir_repair.c (revision c771600c6af14749609b49565ffb4cac2959710d)
1b1991ee3SDarrick J. Wong // SPDX-License-Identifier: GPL-2.0-or-later
2b1991ee3SDarrick J. Wong /*
3b1991ee3SDarrick J. Wong  * Copyright (c) 2020-2024 Oracle.  All Rights Reserved.
4b1991ee3SDarrick J. Wong  * Author: Darrick J. Wong <djwong@kernel.org>
5b1991ee3SDarrick J. Wong  */
6b1991ee3SDarrick J. Wong #include "xfs.h"
7b1991ee3SDarrick J. Wong #include "xfs_fs.h"
8b1991ee3SDarrick J. Wong #include "xfs_shared.h"
9b1991ee3SDarrick J. Wong #include "xfs_format.h"
10b1991ee3SDarrick J. Wong #include "xfs_trans_resv.h"
11b1991ee3SDarrick J. Wong #include "xfs_mount.h"
12b1991ee3SDarrick J. Wong #include "xfs_defer.h"
13b1991ee3SDarrick J. Wong #include "xfs_bit.h"
14b1991ee3SDarrick J. Wong #include "xfs_log_format.h"
15b1991ee3SDarrick J. Wong #include "xfs_trans.h"
16b1991ee3SDarrick J. Wong #include "xfs_sb.h"
17b1991ee3SDarrick J. Wong #include "xfs_inode.h"
18b1991ee3SDarrick J. Wong #include "xfs_icache.h"
19b1991ee3SDarrick J. Wong #include "xfs_da_format.h"
20b1991ee3SDarrick J. Wong #include "xfs_da_btree.h"
21b1991ee3SDarrick J. Wong #include "xfs_dir2.h"
22b1991ee3SDarrick J. Wong #include "xfs_dir2_priv.h"
23b1991ee3SDarrick J. Wong #include "xfs_bmap.h"
24b1991ee3SDarrick J. Wong #include "xfs_quota.h"
25b1991ee3SDarrick J. Wong #include "xfs_bmap_btree.h"
26b1991ee3SDarrick J. Wong #include "xfs_trans_space.h"
27b1991ee3SDarrick J. Wong #include "xfs_bmap_util.h"
28b1991ee3SDarrick J. Wong #include "xfs_exchmaps.h"
29b1991ee3SDarrick J. Wong #include "xfs_exchrange.h"
30b1991ee3SDarrick J. Wong #include "xfs_ag.h"
3176fc23b6SDarrick J. Wong #include "xfs_parent.h"
32b1991ee3SDarrick J. Wong #include "scrub/xfs_scrub.h"
33b1991ee3SDarrick J. Wong #include "scrub/scrub.h"
34b1991ee3SDarrick J. Wong #include "scrub/common.h"
35b1991ee3SDarrick J. Wong #include "scrub/trace.h"
36b1991ee3SDarrick J. Wong #include "scrub/repair.h"
37b1991ee3SDarrick J. Wong #include "scrub/tempfile.h"
38b1991ee3SDarrick J. Wong #include "scrub/tempexch.h"
39b1991ee3SDarrick J. Wong #include "scrub/xfile.h"
40b1991ee3SDarrick J. Wong #include "scrub/xfarray.h"
41b1991ee3SDarrick J. Wong #include "scrub/xfblob.h"
42a07b4557SDarrick J. Wong #include "scrub/iscan.h"
43b1991ee3SDarrick J. Wong #include "scrub/readdir.h"
44b1991ee3SDarrick J. Wong #include "scrub/reap.h"
45a07b4557SDarrick J. Wong #include "scrub/findparent.h"
461e58a8ccSDarrick J. Wong #include "scrub/orphanage.h"
4776fc23b6SDarrick J. Wong #include "scrub/listxattr.h"
48b1991ee3SDarrick J. Wong 
49b1991ee3SDarrick J. Wong /*
50b1991ee3SDarrick J. Wong  * Directory Repair
51b1991ee3SDarrick J. Wong  * ================
52b1991ee3SDarrick J. Wong  *
53b1991ee3SDarrick J. Wong  * We repair directories by reading the directory data blocks looking for
54b1991ee3SDarrick J. Wong  * directory entries that look salvageable (name passes verifiers, entry points
55b1991ee3SDarrick J. Wong  * to a valid allocated inode, etc).  Each entry worth salvaging is stashed in
56b1991ee3SDarrick J. Wong  * memory, and the stashed entries are periodically replayed into a temporary
57b1991ee3SDarrick J. Wong  * directory to constrain memory use.  Batching the construction of the
58b1991ee3SDarrick J. Wong  * temporary directory in this fashion reduces lock cycling of the directory
59b1991ee3SDarrick J. Wong  * being repaired and the temporary directory, and will later become important
60b1991ee3SDarrick J. Wong  * for parent pointer scanning.
61b1991ee3SDarrick J. Wong  *
6276fc23b6SDarrick J. Wong  * If parent pointers are enabled on this filesystem, we instead reconstruct
6376fc23b6SDarrick J. Wong  * the directory by visiting each parent pointer of each file in the filesystem
6476fc23b6SDarrick J. Wong  * and translating the relevant parent pointer records into dirents.  In this
6576fc23b6SDarrick J. Wong  * case, it is advantageous to stash all directory entries created from parent
6676fc23b6SDarrick J. Wong  * pointers for a single child file before replaying them into the temporary
6776fc23b6SDarrick J. Wong  * directory.  To save memory, the live filesystem scan reuses the findparent
6876fc23b6SDarrick J. Wong  * fields.  Directory repair chooses either parent pointer scanning or
6976fc23b6SDarrick J. Wong  * directory entry salvaging, but not both.
7076fc23b6SDarrick J. Wong  *
71b1991ee3SDarrick J. Wong  * Directory entries added to the temporary directory do not elevate the link
72b1991ee3SDarrick J. Wong  * counts of the inodes found.  When salvaging completes, the remaining stashed
73b1991ee3SDarrick J. Wong  * entries are replayed to the temporary directory.  An atomic mapping exchange
74b1991ee3SDarrick J. Wong  * is used to commit the new directory blocks to the directory being repaired.
75b1991ee3SDarrick J. Wong  * This will disrupt readdir cursors.
76b1991ee3SDarrick J. Wong  *
77b1991ee3SDarrick J. Wong  * Locking Issues
78b1991ee3SDarrick J. Wong  * --------------
79b1991ee3SDarrick J. Wong  *
80b1991ee3SDarrick J. Wong  * If /a, /a/b, and /c are all directories, the VFS does not take i_rwsem on
81b1991ee3SDarrick J. Wong  * /a/b for a "mv /a/b /c/" operation.  This means that only b's ILOCK protects
82b1991ee3SDarrick J. Wong  * b's dotdot update.  This is in contrast to every other dotdot update (link,
83b1991ee3SDarrick J. Wong  * remove, mkdir).  If the repair code drops the ILOCK, it must either
84b1991ee3SDarrick J. Wong  * revalidate the dotdot entry or use dirent hooks to capture updates from
85b1991ee3SDarrick J. Wong  * other threads.
86b1991ee3SDarrick J. Wong  */
87b1991ee3SDarrick J. Wong 
888559b21aSDarrick J. Wong /* Create a dirent in the tempdir. */
898559b21aSDarrick J. Wong #define XREP_DIRENT_ADD		(1)
908559b21aSDarrick J. Wong 
918559b21aSDarrick J. Wong /* Remove a dirent from the tempdir. */
928559b21aSDarrick J. Wong #define XREP_DIRENT_REMOVE	(2)
938559b21aSDarrick J. Wong 
94b1991ee3SDarrick J. Wong /* Directory entry to be restored in the new directory. */
95b1991ee3SDarrick J. Wong struct xrep_dirent {
96b1991ee3SDarrick J. Wong 	/* Cookie for retrieval of the dirent name. */
97b1991ee3SDarrick J. Wong 	xfblob_cookie		name_cookie;
98b1991ee3SDarrick J. Wong 
99b1991ee3SDarrick J. Wong 	/* Target inode number. */
100b1991ee3SDarrick J. Wong 	xfs_ino_t		ino;
101b1991ee3SDarrick J. Wong 
102b1991ee3SDarrick J. Wong 	/* Length of the dirent name. */
103b1991ee3SDarrick J. Wong 	uint8_t			namelen;
104b1991ee3SDarrick J. Wong 
105b1991ee3SDarrick J. Wong 	/* File type of the dirent. */
106b1991ee3SDarrick J. Wong 	uint8_t			ftype;
1078559b21aSDarrick J. Wong 
1088559b21aSDarrick J. Wong 	/* XREP_DIRENT_{ADD,REMOVE} */
1098559b21aSDarrick J. Wong 	uint8_t			action;
110b1991ee3SDarrick J. Wong };
111b1991ee3SDarrick J. Wong 
112b1991ee3SDarrick J. Wong /*
113b1991ee3SDarrick J. Wong  * Stash up to 8 pages of recovered dirent data in dir_entries and dir_names
114b1991ee3SDarrick J. Wong  * before we write them to the temp dir.
115b1991ee3SDarrick J. Wong  */
116b1991ee3SDarrick J. Wong #define XREP_DIR_MAX_STASH_BYTES	(PAGE_SIZE * 8)
117b1991ee3SDarrick J. Wong 
118b1991ee3SDarrick J. Wong struct xrep_dir {
119b1991ee3SDarrick J. Wong 	struct xfs_scrub	*sc;
120b1991ee3SDarrick J. Wong 
121b1991ee3SDarrick J. Wong 	/* Fixed-size array of xrep_dirent structures. */
122b1991ee3SDarrick J. Wong 	struct xfarray		*dir_entries;
123b1991ee3SDarrick J. Wong 
124b1991ee3SDarrick J. Wong 	/* Blobs containing directory entry names. */
125b1991ee3SDarrick J. Wong 	struct xfblob		*dir_names;
126b1991ee3SDarrick J. Wong 
127b1991ee3SDarrick J. Wong 	/* Information for exchanging data forks at the end. */
128b1991ee3SDarrick J. Wong 	struct xrep_tempexch	tx;
129b1991ee3SDarrick J. Wong 
130b1991ee3SDarrick J. Wong 	/* Preallocated args struct for performing dir operations */
131b1991ee3SDarrick J. Wong 	struct xfs_da_args	args;
132b1991ee3SDarrick J. Wong 
133b1991ee3SDarrick J. Wong 	/*
134a07b4557SDarrick J. Wong 	 * Information used to scan the filesystem to find the inumber of the
13576fc23b6SDarrick J. Wong 	 * dotdot entry for this directory.  For directory salvaging when
13676fc23b6SDarrick J. Wong 	 * parent pointers are not enabled, we use the findparent_* functions
13776fc23b6SDarrick J. Wong 	 * on this object and access only the parent_ino field directly.
13876fc23b6SDarrick J. Wong 	 *
13976fc23b6SDarrick J. Wong 	 * When parent pointers are enabled, however, the pptr scanner uses the
14076fc23b6SDarrick J. Wong 	 * iscan, hooks, lock, and parent_ino fields of this object directly.
14176fc23b6SDarrick J. Wong 	 * @pscan.lock coordinates access to dir_entries, dir_names,
14276fc23b6SDarrick J. Wong 	 * parent_ino, subdirs, dirents, and args.  This reduces the memory
14376fc23b6SDarrick J. Wong 	 * requirements of this structure.
144b1991ee3SDarrick J. Wong 	 */
145a07b4557SDarrick J. Wong 	struct xrep_parent_scan_info pscan;
146b1991ee3SDarrick J. Wong 
1471e58a8ccSDarrick J. Wong 	/*
1481e58a8ccSDarrick J. Wong 	 * Context information for attaching this directory to the lost+found
1491e58a8ccSDarrick J. Wong 	 * if this directory does not have a parent.
1501e58a8ccSDarrick J. Wong 	 */
1511e58a8ccSDarrick J. Wong 	struct xrep_adoption	adoption;
1521e58a8ccSDarrick J. Wong 
153b1991ee3SDarrick J. Wong 	/* How many subdirectories did we find? */
154b1991ee3SDarrick J. Wong 	uint64_t		subdirs;
155b1991ee3SDarrick J. Wong 
156b1991ee3SDarrick J. Wong 	/* How many dirents did we find? */
157b1991ee3SDarrick J. Wong 	unsigned int		dirents;
158b1991ee3SDarrick J. Wong 
1591e58a8ccSDarrick J. Wong 	/* Should we move this directory to the orphanage? */
1601e58a8ccSDarrick J. Wong 	bool			needs_adoption;
1611e58a8ccSDarrick J. Wong 
162b1991ee3SDarrick J. Wong 	/* Directory entry name, plus the trailing null. */
163b1991ee3SDarrick J. Wong 	struct xfs_name		xname;
164b1991ee3SDarrick J. Wong 	unsigned char		namebuf[MAXNAMELEN];
165b1991ee3SDarrick J. Wong };
166b1991ee3SDarrick J. Wong 
167b1991ee3SDarrick J. Wong /* Tear down all the incore stuff we created. */
168b1991ee3SDarrick J. Wong static void
169b1991ee3SDarrick J. Wong xrep_dir_teardown(
170b1991ee3SDarrick J. Wong 	struct xfs_scrub	*sc)
171b1991ee3SDarrick J. Wong {
172b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd = sc->buf;
173b1991ee3SDarrick J. Wong 
174a07b4557SDarrick J. Wong 	xrep_findparent_scan_teardown(&rd->pscan);
175b1991ee3SDarrick J. Wong 	xfblob_destroy(rd->dir_names);
176b1991ee3SDarrick J. Wong 	xfarray_destroy(rd->dir_entries);
177b1991ee3SDarrick J. Wong }
178b1991ee3SDarrick J. Wong 
179b1991ee3SDarrick J. Wong /* Set up for a directory repair. */
180b1991ee3SDarrick J. Wong int
181b1991ee3SDarrick J. Wong xrep_setup_directory(
182b1991ee3SDarrick J. Wong 	struct xfs_scrub	*sc)
183b1991ee3SDarrick J. Wong {
184b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd;
185b1991ee3SDarrick J. Wong 	int			error;
186b1991ee3SDarrick J. Wong 
187a07b4557SDarrick J. Wong 	xchk_fsgates_enable(sc, XCHK_FSGATES_DIRENTS);
188a07b4557SDarrick J. Wong 
1891e58a8ccSDarrick J. Wong 	error = xrep_orphanage_try_create(sc);
1901e58a8ccSDarrick J. Wong 	if (error)
1911e58a8ccSDarrick J. Wong 		return error;
1921e58a8ccSDarrick J. Wong 
193b1991ee3SDarrick J. Wong 	error = xrep_tempfile_create(sc, S_IFDIR);
194b1991ee3SDarrick J. Wong 	if (error)
195b1991ee3SDarrick J. Wong 		return error;
196b1991ee3SDarrick J. Wong 
197b1991ee3SDarrick J. Wong 	rd = kvzalloc(sizeof(struct xrep_dir), XCHK_GFP_FLAGS);
198b1991ee3SDarrick J. Wong 	if (!rd)
199b1991ee3SDarrick J. Wong 		return -ENOMEM;
200b1991ee3SDarrick J. Wong 	rd->sc = sc;
201b1991ee3SDarrick J. Wong 	rd->xname.name = rd->namebuf;
202b1991ee3SDarrick J. Wong 	sc->buf = rd;
203b1991ee3SDarrick J. Wong 
204b1991ee3SDarrick J. Wong 	return 0;
205b1991ee3SDarrick J. Wong }
206b1991ee3SDarrick J. Wong 
207b1991ee3SDarrick J. Wong /*
208a07b4557SDarrick J. Wong  * Look up the dotdot entry and confirm that it's really the parent.
209a07b4557SDarrick J. Wong  * Returns NULLFSINO if we don't know what to do.
210b1991ee3SDarrick J. Wong  */
211b1991ee3SDarrick J. Wong static inline xfs_ino_t
212b1991ee3SDarrick J. Wong xrep_dir_lookup_parent(
213b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd)
214b1991ee3SDarrick J. Wong {
215b1991ee3SDarrick J. Wong 	struct xfs_scrub	*sc = rd->sc;
216b1991ee3SDarrick J. Wong 	xfs_ino_t		ino;
217b1991ee3SDarrick J. Wong 	int			error;
218b1991ee3SDarrick J. Wong 
219b1991ee3SDarrick J. Wong 	error = xfs_dir_lookup(sc->tp, sc->ip, &xfs_name_dotdot, &ino, NULL);
220b1991ee3SDarrick J. Wong 	if (error)
221b1991ee3SDarrick J. Wong 		return NULLFSINO;
222b1991ee3SDarrick J. Wong 	if (!xfs_verify_dir_ino(sc->mp, ino))
223b1991ee3SDarrick J. Wong 		return NULLFSINO;
224b1991ee3SDarrick J. Wong 
225a07b4557SDarrick J. Wong 	error = xrep_findparent_confirm(sc, &ino);
226a07b4557SDarrick J. Wong 	if (error)
227a07b4557SDarrick J. Wong 		return NULLFSINO;
228a07b4557SDarrick J. Wong 
229b1991ee3SDarrick J. Wong 	return ino;
230b1991ee3SDarrick J. Wong }
231b1991ee3SDarrick J. Wong 
23234c9382cSDarrick J. Wong /*
23334c9382cSDarrick J. Wong  * Look up '..' in the dentry cache and confirm that it's really the parent.
23434c9382cSDarrick J. Wong  * Returns NULLFSINO if the dcache misses or if the hit is implausible.
23534c9382cSDarrick J. Wong  */
23634c9382cSDarrick J. Wong static inline xfs_ino_t
23734c9382cSDarrick J. Wong xrep_dir_dcache_parent(
23834c9382cSDarrick J. Wong 	struct xrep_dir		*rd)
23934c9382cSDarrick J. Wong {
24034c9382cSDarrick J. Wong 	struct xfs_scrub	*sc = rd->sc;
24134c9382cSDarrick J. Wong 	xfs_ino_t		parent_ino;
24234c9382cSDarrick J. Wong 	int			error;
24334c9382cSDarrick J. Wong 
24434c9382cSDarrick J. Wong 	parent_ino = xrep_findparent_from_dcache(sc);
24534c9382cSDarrick J. Wong 	if (parent_ino == NULLFSINO)
24634c9382cSDarrick J. Wong 		return parent_ino;
24734c9382cSDarrick J. Wong 
24834c9382cSDarrick J. Wong 	error = xrep_findparent_confirm(sc, &parent_ino);
24934c9382cSDarrick J. Wong 	if (error)
25034c9382cSDarrick J. Wong 		return NULLFSINO;
25134c9382cSDarrick J. Wong 
25234c9382cSDarrick J. Wong 	return parent_ino;
25334c9382cSDarrick J. Wong }
25434c9382cSDarrick J. Wong 
255a07b4557SDarrick J. Wong /* Try to find the parent of the directory being repaired. */
256b1991ee3SDarrick J. Wong STATIC int
257b1991ee3SDarrick J. Wong xrep_dir_find_parent(
258b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd)
259b1991ee3SDarrick J. Wong {
260b1991ee3SDarrick J. Wong 	xfs_ino_t		ino;
261b1991ee3SDarrick J. Wong 
262a07b4557SDarrick J. Wong 	ino = xrep_findparent_self_reference(rd->sc);
263b1991ee3SDarrick J. Wong 	if (ino != NULLFSINO) {
264a07b4557SDarrick J. Wong 		xrep_findparent_scan_finish_early(&rd->pscan, ino);
265b1991ee3SDarrick J. Wong 		return 0;
266b1991ee3SDarrick J. Wong 	}
267b1991ee3SDarrick J. Wong 
26834c9382cSDarrick J. Wong 	ino = xrep_dir_dcache_parent(rd);
26934c9382cSDarrick J. Wong 	if (ino != NULLFSINO) {
27034c9382cSDarrick J. Wong 		xrep_findparent_scan_finish_early(&rd->pscan, ino);
27134c9382cSDarrick J. Wong 		return 0;
27234c9382cSDarrick J. Wong 	}
27334c9382cSDarrick J. Wong 
274b1991ee3SDarrick J. Wong 	ino = xrep_dir_lookup_parent(rd);
275b1991ee3SDarrick J. Wong 	if (ino != NULLFSINO) {
276a07b4557SDarrick J. Wong 		xrep_findparent_scan_finish_early(&rd->pscan, ino);
277b1991ee3SDarrick J. Wong 		return 0;
278b1991ee3SDarrick J. Wong 	}
279b1991ee3SDarrick J. Wong 
280a07b4557SDarrick J. Wong 	/*
281a07b4557SDarrick J. Wong 	 * A full filesystem scan is the last resort.  On a busy filesystem,
282a07b4557SDarrick J. Wong 	 * the scan can fail with -EBUSY if we cannot grab IOLOCKs.  That means
283a07b4557SDarrick J. Wong 	 * that we don't know what who the parent is, so we should return to
284a07b4557SDarrick J. Wong 	 * userspace.
285a07b4557SDarrick J. Wong 	 */
286a07b4557SDarrick J. Wong 	return xrep_findparent_scan(&rd->pscan);
287b1991ee3SDarrick J. Wong }
288b1991ee3SDarrick J. Wong 
289b1991ee3SDarrick J. Wong /*
290b1991ee3SDarrick J. Wong  * Decide if we want to salvage this entry.  We don't bother with oversized
291b1991ee3SDarrick J. Wong  * names or the dot entry.
292b1991ee3SDarrick J. Wong  */
293b1991ee3SDarrick J. Wong STATIC int
294b1991ee3SDarrick J. Wong xrep_dir_want_salvage(
295b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd,
296b1991ee3SDarrick J. Wong 	const char		*name,
297b1991ee3SDarrick J. Wong 	int			namelen,
298b1991ee3SDarrick J. Wong 	xfs_ino_t		ino)
299b1991ee3SDarrick J. Wong {
300b1991ee3SDarrick J. Wong 	struct xfs_mount	*mp = rd->sc->mp;
301b1991ee3SDarrick J. Wong 
302b1991ee3SDarrick J. Wong 	/* No pointers to ourselves or to garbage. */
303b1991ee3SDarrick J. Wong 	if (ino == rd->sc->ip->i_ino)
304b1991ee3SDarrick J. Wong 		return false;
305b1991ee3SDarrick J. Wong 	if (!xfs_verify_dir_ino(mp, ino))
306b1991ee3SDarrick J. Wong 		return false;
307b1991ee3SDarrick J. Wong 
308b1991ee3SDarrick J. Wong 	/* No weird looking names or dot entries. */
309b1991ee3SDarrick J. Wong 	if (namelen >= MAXNAMELEN || namelen <= 0)
310b1991ee3SDarrick J. Wong 		return false;
311b1991ee3SDarrick J. Wong 	if (namelen == 1 && name[0] == '.')
312b1991ee3SDarrick J. Wong 		return false;
313b1991ee3SDarrick J. Wong 	if (!xfs_dir2_namecheck(name, namelen))
314b1991ee3SDarrick J. Wong 		return false;
315b1991ee3SDarrick J. Wong 
316b1991ee3SDarrick J. Wong 	return true;
317b1991ee3SDarrick J. Wong }
318b1991ee3SDarrick J. Wong 
319b1991ee3SDarrick J. Wong /*
320b1991ee3SDarrick J. Wong  * Remember that we want to create a dirent in the tempdir.  These stashed
321b1991ee3SDarrick J. Wong  * actions will be replayed later.
322b1991ee3SDarrick J. Wong  */
323b1991ee3SDarrick J. Wong STATIC int
324b1991ee3SDarrick J. Wong xrep_dir_stash_createname(
325b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd,
326b1991ee3SDarrick J. Wong 	const struct xfs_name	*name,
327b1991ee3SDarrick J. Wong 	xfs_ino_t		ino)
328b1991ee3SDarrick J. Wong {
329b1991ee3SDarrick J. Wong 	struct xrep_dirent	dirent = {
3308559b21aSDarrick J. Wong 		.action		= XREP_DIRENT_ADD,
331b1991ee3SDarrick J. Wong 		.ino		= ino,
332b1991ee3SDarrick J. Wong 		.namelen	= name->len,
333b1991ee3SDarrick J. Wong 		.ftype		= name->type,
334b1991ee3SDarrick J. Wong 	};
335b1991ee3SDarrick J. Wong 	int			error;
336b1991ee3SDarrick J. Wong 
337b1991ee3SDarrick J. Wong 	trace_xrep_dir_stash_createname(rd->sc->tempip, name, ino);
338b1991ee3SDarrick J. Wong 
339b1991ee3SDarrick J. Wong 	error = xfblob_storename(rd->dir_names, &dirent.name_cookie, name);
340b1991ee3SDarrick J. Wong 	if (error)
341b1991ee3SDarrick J. Wong 		return error;
342b1991ee3SDarrick J. Wong 
343b1991ee3SDarrick J. Wong 	return xfarray_append(rd->dir_entries, &dirent);
344b1991ee3SDarrick J. Wong }
345b1991ee3SDarrick J. Wong 
3468559b21aSDarrick J. Wong /*
3478559b21aSDarrick J. Wong  * Remember that we want to remove a dirent from the tempdir.  These stashed
3488559b21aSDarrick J. Wong  * actions will be replayed later.
3498559b21aSDarrick J. Wong  */
3508559b21aSDarrick J. Wong STATIC int
3518559b21aSDarrick J. Wong xrep_dir_stash_removename(
3528559b21aSDarrick J. Wong 	struct xrep_dir		*rd,
3538559b21aSDarrick J. Wong 	const struct xfs_name	*name,
3548559b21aSDarrick J. Wong 	xfs_ino_t		ino)
3558559b21aSDarrick J. Wong {
3568559b21aSDarrick J. Wong 	struct xrep_dirent	dirent = {
3578559b21aSDarrick J. Wong 		.action		= XREP_DIRENT_REMOVE,
3588559b21aSDarrick J. Wong 		.ino		= ino,
3598559b21aSDarrick J. Wong 		.namelen	= name->len,
3608559b21aSDarrick J. Wong 		.ftype		= name->type,
3618559b21aSDarrick J. Wong 	};
3628559b21aSDarrick J. Wong 	int			error;
3638559b21aSDarrick J. Wong 
3648559b21aSDarrick J. Wong 	trace_xrep_dir_stash_removename(rd->sc->tempip, name, ino);
3658559b21aSDarrick J. Wong 
3668559b21aSDarrick J. Wong 	error = xfblob_storename(rd->dir_names, &dirent.name_cookie, name);
3678559b21aSDarrick J. Wong 	if (error)
3688559b21aSDarrick J. Wong 		return error;
3698559b21aSDarrick J. Wong 
3708559b21aSDarrick J. Wong 	return xfarray_append(rd->dir_entries, &dirent);
3718559b21aSDarrick J. Wong }
3728559b21aSDarrick J. Wong 
373b1991ee3SDarrick J. Wong /* Allocate an in-core record to hold entries while we rebuild the dir data. */
374b1991ee3SDarrick J. Wong STATIC int
375b1991ee3SDarrick J. Wong xrep_dir_salvage_entry(
376b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd,
377b1991ee3SDarrick J. Wong 	unsigned char		*name,
378b1991ee3SDarrick J. Wong 	unsigned int		namelen,
379b1991ee3SDarrick J. Wong 	xfs_ino_t		ino)
380b1991ee3SDarrick J. Wong {
381b1991ee3SDarrick J. Wong 	struct xfs_name		xname = {
382b1991ee3SDarrick J. Wong 		.name		= name,
383b1991ee3SDarrick J. Wong 	};
384b1991ee3SDarrick J. Wong 	struct xfs_scrub	*sc = rd->sc;
385b1991ee3SDarrick J. Wong 	struct xfs_inode	*ip;
386b1991ee3SDarrick J. Wong 	unsigned int		i = 0;
387b1991ee3SDarrick J. Wong 	int			error = 0;
388b1991ee3SDarrick J. Wong 
389b1991ee3SDarrick J. Wong 	if (xchk_should_terminate(sc, &error))
390b1991ee3SDarrick J. Wong 		return error;
391b1991ee3SDarrick J. Wong 
392b1991ee3SDarrick J. Wong 	/*
393b1991ee3SDarrick J. Wong 	 * Truncate the name to the first character that would trip namecheck.
394b1991ee3SDarrick J. Wong 	 * If we no longer have a name after that, ignore this entry.
395b1991ee3SDarrick J. Wong 	 */
396b1991ee3SDarrick J. Wong 	while (i < namelen && name[i] != 0 && name[i] != '/')
397b1991ee3SDarrick J. Wong 		i++;
398b1991ee3SDarrick J. Wong 	if (i == 0)
399b1991ee3SDarrick J. Wong 		return 0;
400b1991ee3SDarrick J. Wong 	xname.len = i;
401b1991ee3SDarrick J. Wong 
402b1991ee3SDarrick J. Wong 	/* Ignore '..' entries; we already picked the new parent. */
403b1991ee3SDarrick J. Wong 	if (xname.len == 2 && name[0] == '.' && name[1] == '.') {
404b1991ee3SDarrick J. Wong 		trace_xrep_dir_salvaged_parent(sc->ip, ino);
405b1991ee3SDarrick J. Wong 		return 0;
406b1991ee3SDarrick J. Wong 	}
407b1991ee3SDarrick J. Wong 
408b1991ee3SDarrick J. Wong 	trace_xrep_dir_salvage_entry(sc->ip, &xname, ino);
409b1991ee3SDarrick J. Wong 
410b1991ee3SDarrick J. Wong 	/*
411b1991ee3SDarrick J. Wong 	 * Compute the ftype or dump the entry if we can't.  We don't lock the
412b1991ee3SDarrick J. Wong 	 * inode because inodes can't change type while we have a reference.
413b1991ee3SDarrick J. Wong 	 */
414b1991ee3SDarrick J. Wong 	error = xchk_iget(sc, ino, &ip);
415b1991ee3SDarrick J. Wong 	if (error)
416b1991ee3SDarrick J. Wong 		return 0;
417b1991ee3SDarrick J. Wong 
4183d2c3411SDarrick J. Wong 	/* Don't mix metadata and regular directory trees. */
4193d2c3411SDarrick J. Wong 	if (xfs_is_metadir_inode(ip) != xfs_is_metadir_inode(rd->sc->ip)) {
4203d2c3411SDarrick J. Wong 		xchk_irele(sc, ip);
4213d2c3411SDarrick J. Wong 		return 0;
4223d2c3411SDarrick J. Wong 	}
4233d2c3411SDarrick J. Wong 
424b1991ee3SDarrick J. Wong 	xname.type = xfs_mode_to_ftype(VFS_I(ip)->i_mode);
425b1991ee3SDarrick J. Wong 	xchk_irele(sc, ip);
426b1991ee3SDarrick J. Wong 
427b1991ee3SDarrick J. Wong 	return xrep_dir_stash_createname(rd, &xname, ino);
428b1991ee3SDarrick J. Wong }
429b1991ee3SDarrick J. Wong 
430b1991ee3SDarrick J. Wong /* Record a shortform directory entry for later reinsertion. */
431b1991ee3SDarrick J. Wong STATIC int
432b1991ee3SDarrick J. Wong xrep_dir_salvage_sf_entry(
433b1991ee3SDarrick J. Wong 	struct xrep_dir			*rd,
434b1991ee3SDarrick J. Wong 	struct xfs_dir2_sf_hdr		*sfp,
435b1991ee3SDarrick J. Wong 	struct xfs_dir2_sf_entry	*sfep)
436b1991ee3SDarrick J. Wong {
437b1991ee3SDarrick J. Wong 	xfs_ino_t			ino;
438b1991ee3SDarrick J. Wong 
439b1991ee3SDarrick J. Wong 	ino = xfs_dir2_sf_get_ino(rd->sc->mp, sfp, sfep);
440b1991ee3SDarrick J. Wong 	if (!xrep_dir_want_salvage(rd, sfep->name, sfep->namelen, ino))
441b1991ee3SDarrick J. Wong 		return 0;
442b1991ee3SDarrick J. Wong 
443b1991ee3SDarrick J. Wong 	return xrep_dir_salvage_entry(rd, sfep->name, sfep->namelen, ino);
444b1991ee3SDarrick J. Wong }
445b1991ee3SDarrick J. Wong 
446b1991ee3SDarrick J. Wong /* Record a regular directory entry for later reinsertion. */
447b1991ee3SDarrick J. Wong STATIC int
448b1991ee3SDarrick J. Wong xrep_dir_salvage_data_entry(
449b1991ee3SDarrick J. Wong 	struct xrep_dir			*rd,
450b1991ee3SDarrick J. Wong 	struct xfs_dir2_data_entry	*dep)
451b1991ee3SDarrick J. Wong {
452b1991ee3SDarrick J. Wong 	xfs_ino_t			ino;
453b1991ee3SDarrick J. Wong 
454b1991ee3SDarrick J. Wong 	ino = be64_to_cpu(dep->inumber);
455b1991ee3SDarrick J. Wong 	if (!xrep_dir_want_salvage(rd, dep->name, dep->namelen, ino))
456b1991ee3SDarrick J. Wong 		return 0;
457b1991ee3SDarrick J. Wong 
458b1991ee3SDarrick J. Wong 	return xrep_dir_salvage_entry(rd, dep->name, dep->namelen, ino);
459b1991ee3SDarrick J. Wong }
460b1991ee3SDarrick J. Wong 
461b1991ee3SDarrick J. Wong /* Try to recover block/data format directory entries. */
462b1991ee3SDarrick J. Wong STATIC int
463b1991ee3SDarrick J. Wong xrep_dir_recover_data(
464b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd,
465b1991ee3SDarrick J. Wong 	struct xfs_buf		*bp)
466b1991ee3SDarrick J. Wong {
467b1991ee3SDarrick J. Wong 	struct xfs_da_geometry	*geo = rd->sc->mp->m_dir_geo;
468b1991ee3SDarrick J. Wong 	unsigned int		offset;
469b1991ee3SDarrick J. Wong 	unsigned int		end;
470b1991ee3SDarrick J. Wong 	int			error = 0;
471b1991ee3SDarrick J. Wong 
472b1991ee3SDarrick J. Wong 	/*
473b1991ee3SDarrick J. Wong 	 * Loop over the data portion of the block.
474b1991ee3SDarrick J. Wong 	 * Each object is a real entry (dep) or an unused one (dup).
475b1991ee3SDarrick J. Wong 	 */
476b1991ee3SDarrick J. Wong 	offset = geo->data_entry_offset;
477b1991ee3SDarrick J. Wong 	end = min_t(unsigned int, BBTOB(bp->b_length),
478b1991ee3SDarrick J. Wong 			xfs_dir3_data_end_offset(geo, bp->b_addr));
479b1991ee3SDarrick J. Wong 
480b1991ee3SDarrick J. Wong 	while (offset < end) {
481b1991ee3SDarrick J. Wong 		struct xfs_dir2_data_unused	*dup = bp->b_addr + offset;
482b1991ee3SDarrick J. Wong 		struct xfs_dir2_data_entry	*dep = bp->b_addr + offset;
483b1991ee3SDarrick J. Wong 
484b1991ee3SDarrick J. Wong 		if (xchk_should_terminate(rd->sc, &error))
485b1991ee3SDarrick J. Wong 			return error;
486b1991ee3SDarrick J. Wong 
487b1991ee3SDarrick J. Wong 		/* Skip unused entries. */
488b1991ee3SDarrick J. Wong 		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
489b1991ee3SDarrick J. Wong 			offset += be16_to_cpu(dup->length);
490b1991ee3SDarrick J. Wong 			continue;
491b1991ee3SDarrick J. Wong 		}
492b1991ee3SDarrick J. Wong 
493b1991ee3SDarrick J. Wong 		/* Don't walk off the end of the block. */
494b1991ee3SDarrick J. Wong 		offset += xfs_dir2_data_entsize(rd->sc->mp, dep->namelen);
495b1991ee3SDarrick J. Wong 		if (offset > end)
496b1991ee3SDarrick J. Wong 			break;
497b1991ee3SDarrick J. Wong 
498b1991ee3SDarrick J. Wong 		/* Ok, let's save this entry. */
499b1991ee3SDarrick J. Wong 		error = xrep_dir_salvage_data_entry(rd, dep);
500b1991ee3SDarrick J. Wong 		if (error)
501b1991ee3SDarrick J. Wong 			return error;
502b1991ee3SDarrick J. Wong 
503b1991ee3SDarrick J. Wong 	}
504b1991ee3SDarrick J. Wong 
505b1991ee3SDarrick J. Wong 	return 0;
506b1991ee3SDarrick J. Wong }
507b1991ee3SDarrick J. Wong 
508b1991ee3SDarrick J. Wong /* Try to recover shortform directory entries. */
509b1991ee3SDarrick J. Wong STATIC int
510b1991ee3SDarrick J. Wong xrep_dir_recover_sf(
511b1991ee3SDarrick J. Wong 	struct xrep_dir			*rd)
512b1991ee3SDarrick J. Wong {
513b1991ee3SDarrick J. Wong 	struct xfs_dir2_sf_hdr		*hdr;
514b1991ee3SDarrick J. Wong 	struct xfs_dir2_sf_entry	*sfep;
515b1991ee3SDarrick J. Wong 	struct xfs_dir2_sf_entry	*next;
516b1991ee3SDarrick J. Wong 	struct xfs_ifork		*ifp;
517b1991ee3SDarrick J. Wong 	xfs_ino_t			ino;
518b1991ee3SDarrick J. Wong 	unsigned char			*end;
519b1991ee3SDarrick J. Wong 	int				error = 0;
520b1991ee3SDarrick J. Wong 
521b1991ee3SDarrick J. Wong 	ifp = xfs_ifork_ptr(rd->sc->ip, XFS_DATA_FORK);
522b1991ee3SDarrick J. Wong 	hdr = ifp->if_data;
523b1991ee3SDarrick J. Wong 	end = (unsigned char *)ifp->if_data + ifp->if_bytes;
524b1991ee3SDarrick J. Wong 
525b1991ee3SDarrick J. Wong 	ino = xfs_dir2_sf_get_parent_ino(hdr);
526b1991ee3SDarrick J. Wong 	trace_xrep_dir_salvaged_parent(rd->sc->ip, ino);
527b1991ee3SDarrick J. Wong 
528b1991ee3SDarrick J. Wong 	sfep = xfs_dir2_sf_firstentry(hdr);
529b1991ee3SDarrick J. Wong 	while ((unsigned char *)sfep < end) {
530b1991ee3SDarrick J. Wong 		if (xchk_should_terminate(rd->sc, &error))
531b1991ee3SDarrick J. Wong 			return error;
532b1991ee3SDarrick J. Wong 
533b1991ee3SDarrick J. Wong 		next = xfs_dir2_sf_nextentry(rd->sc->mp, hdr, sfep);
534b1991ee3SDarrick J. Wong 		if ((unsigned char *)next > end)
535b1991ee3SDarrick J. Wong 			break;
536b1991ee3SDarrick J. Wong 
537b1991ee3SDarrick J. Wong 		/* Ok, let's save this entry. */
538b1991ee3SDarrick J. Wong 		error = xrep_dir_salvage_sf_entry(rd, hdr, sfep);
539b1991ee3SDarrick J. Wong 		if (error)
540b1991ee3SDarrick J. Wong 			return error;
541b1991ee3SDarrick J. Wong 
542b1991ee3SDarrick J. Wong 		sfep = next;
543b1991ee3SDarrick J. Wong 	}
544b1991ee3SDarrick J. Wong 
545b1991ee3SDarrick J. Wong 	return 0;
546b1991ee3SDarrick J. Wong }
547b1991ee3SDarrick J. Wong 
548b1991ee3SDarrick J. Wong /*
549b1991ee3SDarrick J. Wong  * Try to figure out the format of this directory from the data fork mappings
550b1991ee3SDarrick J. Wong  * and the directory size.  If we can be reasonably sure of format, we can be
551b1991ee3SDarrick J. Wong  * more aggressive in salvaging directory entries.  On return, @magic_guess
552b1991ee3SDarrick J. Wong  * will be set to DIR3_BLOCK_MAGIC if we think this is a "block format"
553b1991ee3SDarrick J. Wong  * directory; DIR3_DATA_MAGIC if we think this is a "data format" directory,
554b1991ee3SDarrick J. Wong  * and 0 if we can't tell.
555b1991ee3SDarrick J. Wong  */
556b1991ee3SDarrick J. Wong STATIC void
557b1991ee3SDarrick J. Wong xrep_dir_guess_format(
558b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd,
559b1991ee3SDarrick J. Wong 	__be32			*magic_guess)
560b1991ee3SDarrick J. Wong {
561b1991ee3SDarrick J. Wong 	struct xfs_inode	*dp = rd->sc->ip;
562b1991ee3SDarrick J. Wong 	struct xfs_mount	*mp = rd->sc->mp;
563b1991ee3SDarrick J. Wong 	struct xfs_da_geometry	*geo = mp->m_dir_geo;
564b1991ee3SDarrick J. Wong 	xfs_fileoff_t		last;
565b1991ee3SDarrick J. Wong 	int			error;
566b1991ee3SDarrick J. Wong 
567b1991ee3SDarrick J. Wong 	ASSERT(xfs_has_crc(mp));
568b1991ee3SDarrick J. Wong 
569b1991ee3SDarrick J. Wong 	*magic_guess = 0;
570b1991ee3SDarrick J. Wong 
571b1991ee3SDarrick J. Wong 	/*
572b1991ee3SDarrick J. Wong 	 * If there's a single directory block and the directory size is
573b1991ee3SDarrick J. Wong 	 * exactly one block, this has to be a single block format directory.
574b1991ee3SDarrick J. Wong 	 */
575b1991ee3SDarrick J. Wong 	error = xfs_bmap_last_offset(dp, &last, XFS_DATA_FORK);
576b1991ee3SDarrick J. Wong 	if (!error && XFS_FSB_TO_B(mp, last) == geo->blksize &&
577b1991ee3SDarrick J. Wong 	    dp->i_disk_size == geo->blksize) {
578b1991ee3SDarrick J. Wong 		*magic_guess = cpu_to_be32(XFS_DIR3_BLOCK_MAGIC);
579b1991ee3SDarrick J. Wong 		return;
580b1991ee3SDarrick J. Wong 	}
581b1991ee3SDarrick J. Wong 
582b1991ee3SDarrick J. Wong 	/*
583b1991ee3SDarrick J. Wong 	 * If the last extent before the leaf offset matches the directory
584b1991ee3SDarrick J. Wong 	 * size and the directory size is larger than 1 block, this is a
585b1991ee3SDarrick J. Wong 	 * data format directory.
586b1991ee3SDarrick J. Wong 	 */
587b1991ee3SDarrick J. Wong 	last = geo->leafblk;
588b1991ee3SDarrick J. Wong 	error = xfs_bmap_last_before(rd->sc->tp, dp, &last, XFS_DATA_FORK);
589b1991ee3SDarrick J. Wong 	if (!error &&
590b1991ee3SDarrick J. Wong 	    XFS_FSB_TO_B(mp, last) > geo->blksize &&
591b1991ee3SDarrick J. Wong 	    XFS_FSB_TO_B(mp, last) == dp->i_disk_size) {
592b1991ee3SDarrick J. Wong 		*magic_guess = cpu_to_be32(XFS_DIR3_DATA_MAGIC);
593b1991ee3SDarrick J. Wong 		return;
594b1991ee3SDarrick J. Wong 	}
595b1991ee3SDarrick J. Wong }
596b1991ee3SDarrick J. Wong 
597b1991ee3SDarrick J. Wong /* Recover directory entries from a specific directory block. */
598b1991ee3SDarrick J. Wong STATIC int
599b1991ee3SDarrick J. Wong xrep_dir_recover_dirblock(
600b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd,
601b1991ee3SDarrick J. Wong 	__be32			magic_guess,
602b1991ee3SDarrick J. Wong 	xfs_dablk_t		dabno)
603b1991ee3SDarrick J. Wong {
604b1991ee3SDarrick J. Wong 	struct xfs_dir2_data_hdr *hdr;
605b1991ee3SDarrick J. Wong 	struct xfs_buf		*bp;
606b1991ee3SDarrick J. Wong 	__be32			oldmagic;
607b1991ee3SDarrick J. Wong 	int			error;
608b1991ee3SDarrick J. Wong 
609b1991ee3SDarrick J. Wong 	/*
610b1991ee3SDarrick J. Wong 	 * Try to read buffer.  We invalidate them in the next step so we don't
611b1991ee3SDarrick J. Wong 	 * bother to set a buffer type or ops.
612b1991ee3SDarrick J. Wong 	 */
613b1991ee3SDarrick J. Wong 	error = xfs_da_read_buf(rd->sc->tp, rd->sc->ip, dabno,
614b1991ee3SDarrick J. Wong 			XFS_DABUF_MAP_HOLE_OK, &bp, XFS_DATA_FORK, NULL);
615b1991ee3SDarrick J. Wong 	if (error || !bp)
616b1991ee3SDarrick J. Wong 		return error;
617b1991ee3SDarrick J. Wong 
618b1991ee3SDarrick J. Wong 	hdr = bp->b_addr;
619b1991ee3SDarrick J. Wong 	oldmagic = hdr->magic;
620b1991ee3SDarrick J. Wong 
621b1991ee3SDarrick J. Wong 	trace_xrep_dir_recover_dirblock(rd->sc->ip, dabno,
622b1991ee3SDarrick J. Wong 			be32_to_cpu(hdr->magic), be32_to_cpu(magic_guess));
623b1991ee3SDarrick J. Wong 
624b1991ee3SDarrick J. Wong 	/*
625b1991ee3SDarrick J. Wong 	 * If we're sure of the block's format, proceed with the salvage
626b1991ee3SDarrick J. Wong 	 * operation using the specified magic number.
627b1991ee3SDarrick J. Wong 	 */
628b1991ee3SDarrick J. Wong 	if (magic_guess) {
629b1991ee3SDarrick J. Wong 		hdr->magic = magic_guess;
630b1991ee3SDarrick J. Wong 		goto recover;
631b1991ee3SDarrick J. Wong 	}
632b1991ee3SDarrick J. Wong 
633b1991ee3SDarrick J. Wong 	/*
634b1991ee3SDarrick J. Wong 	 * If we couldn't guess what type of directory this is, then we will
635b1991ee3SDarrick J. Wong 	 * only salvage entries from directory blocks that match the magic
636b1991ee3SDarrick J. Wong 	 * number and pass verifiers.
637b1991ee3SDarrick J. Wong 	 */
638b1991ee3SDarrick J. Wong 	switch (hdr->magic) {
639b1991ee3SDarrick J. Wong 	case cpu_to_be32(XFS_DIR2_BLOCK_MAGIC):
640b1991ee3SDarrick J. Wong 	case cpu_to_be32(XFS_DIR3_BLOCK_MAGIC):
641b1991ee3SDarrick J. Wong 		if (!xrep_buf_verify_struct(bp, &xfs_dir3_block_buf_ops))
642b1991ee3SDarrick J. Wong 			goto out;
643b1991ee3SDarrick J. Wong 		if (xfs_dir3_block_header_check(bp, rd->sc->ip->i_ino) != NULL)
644b1991ee3SDarrick J. Wong 			goto out;
645b1991ee3SDarrick J. Wong 		break;
646b1991ee3SDarrick J. Wong 	case cpu_to_be32(XFS_DIR2_DATA_MAGIC):
647b1991ee3SDarrick J. Wong 	case cpu_to_be32(XFS_DIR3_DATA_MAGIC):
648b1991ee3SDarrick J. Wong 		if (!xrep_buf_verify_struct(bp, &xfs_dir3_data_buf_ops))
649b1991ee3SDarrick J. Wong 			goto out;
650b1991ee3SDarrick J. Wong 		if (xfs_dir3_data_header_check(bp, rd->sc->ip->i_ino) != NULL)
651b1991ee3SDarrick J. Wong 			goto out;
652b1991ee3SDarrick J. Wong 		break;
653b1991ee3SDarrick J. Wong 	default:
654b1991ee3SDarrick J. Wong 		goto out;
655b1991ee3SDarrick J. Wong 	}
656b1991ee3SDarrick J. Wong 
657b1991ee3SDarrick J. Wong recover:
658b1991ee3SDarrick J. Wong 	error = xrep_dir_recover_data(rd, bp);
659b1991ee3SDarrick J. Wong 
660b1991ee3SDarrick J. Wong out:
661b1991ee3SDarrick J. Wong 	hdr->magic = oldmagic;
662b1991ee3SDarrick J. Wong 	xfs_trans_brelse(rd->sc->tp, bp);
663b1991ee3SDarrick J. Wong 	return error;
664b1991ee3SDarrick J. Wong }
665b1991ee3SDarrick J. Wong 
666b1991ee3SDarrick J. Wong static inline void
667b1991ee3SDarrick J. Wong xrep_dir_init_args(
668b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd,
669b1991ee3SDarrick J. Wong 	struct xfs_inode	*dp,
670b1991ee3SDarrick J. Wong 	const struct xfs_name	*name)
671b1991ee3SDarrick J. Wong {
672b1991ee3SDarrick J. Wong 	memset(&rd->args, 0, sizeof(struct xfs_da_args));
673b1991ee3SDarrick J. Wong 	rd->args.geo = rd->sc->mp->m_dir_geo;
674b1991ee3SDarrick J. Wong 	rd->args.whichfork = XFS_DATA_FORK;
675b1991ee3SDarrick J. Wong 	rd->args.owner = rd->sc->ip->i_ino;
676b1991ee3SDarrick J. Wong 	rd->args.trans = rd->sc->tp;
677b1991ee3SDarrick J. Wong 	rd->args.dp = dp;
678b1991ee3SDarrick J. Wong 	if (!name)
679b1991ee3SDarrick J. Wong 		return;
680b1991ee3SDarrick J. Wong 	rd->args.name = name->name;
681b1991ee3SDarrick J. Wong 	rd->args.namelen = name->len;
682b1991ee3SDarrick J. Wong 	rd->args.filetype = name->type;
683b1991ee3SDarrick J. Wong 	rd->args.hashval = xfs_dir2_hashname(rd->sc->mp, name);
684b1991ee3SDarrick J. Wong }
685b1991ee3SDarrick J. Wong 
686b1991ee3SDarrick J. Wong /* Replay a stashed createname into the temporary directory. */
687b1991ee3SDarrick J. Wong STATIC int
688b1991ee3SDarrick J. Wong xrep_dir_replay_createname(
689b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd,
690b1991ee3SDarrick J. Wong 	const struct xfs_name	*name,
691b1991ee3SDarrick J. Wong 	xfs_ino_t		inum,
692b1991ee3SDarrick J. Wong 	xfs_extlen_t		total)
693b1991ee3SDarrick J. Wong {
694b1991ee3SDarrick J. Wong 	struct xfs_scrub	*sc = rd->sc;
695b1991ee3SDarrick J. Wong 	struct xfs_inode	*dp = rd->sc->tempip;
696b1991ee3SDarrick J. Wong 	int			error;
697b1991ee3SDarrick J. Wong 
698b1991ee3SDarrick J. Wong 	ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
699b1991ee3SDarrick J. Wong 
700b1991ee3SDarrick J. Wong 	error = xfs_dir_ino_validate(sc->mp, inum);
701b1991ee3SDarrick J. Wong 	if (error)
702b1991ee3SDarrick J. Wong 		return error;
703b1991ee3SDarrick J. Wong 
704b1991ee3SDarrick J. Wong 	trace_xrep_dir_replay_createname(dp, name, inum);
705b1991ee3SDarrick J. Wong 
706b1991ee3SDarrick J. Wong 	xrep_dir_init_args(rd, dp, name);
707b1991ee3SDarrick J. Wong 	rd->args.inumber = inum;
708b1991ee3SDarrick J. Wong 	rd->args.total = total;
709b1991ee3SDarrick J. Wong 	rd->args.op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT;
7104d893a40SChristoph Hellwig 	return xfs_dir_createname_args(&rd->args);
711b1991ee3SDarrick J. Wong }
712b1991ee3SDarrick J. Wong 
7138559b21aSDarrick J. Wong /* Replay a stashed removename onto the temporary directory. */
7148559b21aSDarrick J. Wong STATIC int
7158559b21aSDarrick J. Wong xrep_dir_replay_removename(
7168559b21aSDarrick J. Wong 	struct xrep_dir		*rd,
7178559b21aSDarrick J. Wong 	const struct xfs_name	*name,
7188559b21aSDarrick J. Wong 	xfs_extlen_t		total)
7198559b21aSDarrick J. Wong {
7208559b21aSDarrick J. Wong 	struct xfs_inode	*dp = rd->args.dp;
7218559b21aSDarrick J. Wong 
7228559b21aSDarrick J. Wong 	ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
7238559b21aSDarrick J. Wong 
7248559b21aSDarrick J. Wong 	xrep_dir_init_args(rd, dp, name);
7258559b21aSDarrick J. Wong 	rd->args.op_flags = 0;
7268559b21aSDarrick J. Wong 	rd->args.total = total;
7278559b21aSDarrick J. Wong 
7288559b21aSDarrick J. Wong 	trace_xrep_dir_replay_removename(dp, name, 0);
7293866e6e6SChristoph Hellwig 	return xfs_dir_removename_args(&rd->args);
7308559b21aSDarrick J. Wong }
7318559b21aSDarrick J. Wong 
732b1991ee3SDarrick J. Wong /*
733b1991ee3SDarrick J. Wong  * Add this stashed incore directory entry to the temporary directory.
734b1991ee3SDarrick J. Wong  * The caller must hold the tempdir's IOLOCK, must not hold any ILOCKs, and
735b1991ee3SDarrick J. Wong  * must not be in transaction context.
736b1991ee3SDarrick J. Wong  */
737b1991ee3SDarrick J. Wong STATIC int
738b1991ee3SDarrick J. Wong xrep_dir_replay_update(
739b1991ee3SDarrick J. Wong 	struct xrep_dir			*rd,
740b1991ee3SDarrick J. Wong 	const struct xfs_name		*xname,
741b1991ee3SDarrick J. Wong 	const struct xrep_dirent	*dirent)
742b1991ee3SDarrick J. Wong {
743b1991ee3SDarrick J. Wong 	struct xfs_mount		*mp = rd->sc->mp;
744b1991ee3SDarrick J. Wong #ifdef DEBUG
745b1991ee3SDarrick J. Wong 	xfs_ino_t			ino;
746b1991ee3SDarrick J. Wong #endif
747b1991ee3SDarrick J. Wong 	uint				resblks;
748b1991ee3SDarrick J. Wong 	int				error;
749b1991ee3SDarrick J. Wong 
750f1097be2SAllison Henderson 	resblks = xfs_link_space_res(mp, xname->len);
751b1991ee3SDarrick J. Wong 	error = xchk_trans_alloc(rd->sc, resblks);
752b1991ee3SDarrick J. Wong 	if (error)
753b1991ee3SDarrick J. Wong 		return error;
754b1991ee3SDarrick J. Wong 
755b1991ee3SDarrick J. Wong 	/* Lock the temporary directory and join it to the transaction */
756b1991ee3SDarrick J. Wong 	xrep_tempfile_ilock(rd->sc);
757b1991ee3SDarrick J. Wong 	xfs_trans_ijoin(rd->sc->tp, rd->sc->tempip, 0);
758b1991ee3SDarrick J. Wong 
7598559b21aSDarrick J. Wong 	switch (dirent->action) {
7608559b21aSDarrick J. Wong 	case XREP_DIRENT_ADD:
761b1991ee3SDarrick J. Wong 		/*
7628559b21aSDarrick J. Wong 		 * Create a replacement dirent in the temporary directory.
7638559b21aSDarrick J. Wong 		 * Note that _createname doesn't check for existing entries.
7648559b21aSDarrick J. Wong 		 * There shouldn't be any in the temporary dir, but we'll
7658559b21aSDarrick J. Wong 		 * verify this in debug mode.
766b1991ee3SDarrick J. Wong 		 */
767b1991ee3SDarrick J. Wong #ifdef DEBUG
768b1991ee3SDarrick J. Wong 		error = xchk_dir_lookup(rd->sc, rd->sc->tempip, xname, &ino);
769b1991ee3SDarrick J. Wong 		if (error != -ENOENT) {
770b1991ee3SDarrick J. Wong 			ASSERT(error != -ENOENT);
771b1991ee3SDarrick J. Wong 			goto out_cancel;
772b1991ee3SDarrick J. Wong 		}
773b1991ee3SDarrick J. Wong #endif
774b1991ee3SDarrick J. Wong 
7758559b21aSDarrick J. Wong 		error = xrep_dir_replay_createname(rd, xname, dirent->ino,
7768559b21aSDarrick J. Wong 				resblks);
777b1991ee3SDarrick J. Wong 		if (error)
778b1991ee3SDarrick J. Wong 			goto out_cancel;
779b1991ee3SDarrick J. Wong 
780b1991ee3SDarrick J. Wong 		if (xname->type == XFS_DIR3_FT_DIR)
781b1991ee3SDarrick J. Wong 			rd->subdirs++;
782b1991ee3SDarrick J. Wong 		rd->dirents++;
7838559b21aSDarrick J. Wong 		break;
7848559b21aSDarrick J. Wong 	case XREP_DIRENT_REMOVE:
7858559b21aSDarrick J. Wong 		/*
7868559b21aSDarrick J. Wong 		 * Remove a dirent from the temporary directory.  Note that
7878559b21aSDarrick J. Wong 		 * _removename doesn't check the inode target of the exist
7888559b21aSDarrick J. Wong 		 * entry.  There should be a perfect match in the temporary
7898559b21aSDarrick J. Wong 		 * dir, but we'll verify this in debug mode.
7908559b21aSDarrick J. Wong 		 */
7918559b21aSDarrick J. Wong #ifdef DEBUG
7928559b21aSDarrick J. Wong 		error = xchk_dir_lookup(rd->sc, rd->sc->tempip, xname, &ino);
7938559b21aSDarrick J. Wong 		if (error) {
7948559b21aSDarrick J. Wong 			ASSERT(error != 0);
7958559b21aSDarrick J. Wong 			goto out_cancel;
7968559b21aSDarrick J. Wong 		}
7978559b21aSDarrick J. Wong 		if (ino != dirent->ino) {
7988559b21aSDarrick J. Wong 			ASSERT(ino == dirent->ino);
7998559b21aSDarrick J. Wong 			error = -EIO;
8008559b21aSDarrick J. Wong 			goto out_cancel;
8018559b21aSDarrick J. Wong 		}
8028559b21aSDarrick J. Wong #endif
8038559b21aSDarrick J. Wong 
8048559b21aSDarrick J. Wong 		error = xrep_dir_replay_removename(rd, xname, resblks);
8058559b21aSDarrick J. Wong 		if (error)
8068559b21aSDarrick J. Wong 			goto out_cancel;
8078559b21aSDarrick J. Wong 
8088559b21aSDarrick J. Wong 		if (xname->type == XFS_DIR3_FT_DIR)
8098559b21aSDarrick J. Wong 			rd->subdirs--;
8108559b21aSDarrick J. Wong 		rd->dirents--;
8118559b21aSDarrick J. Wong 		break;
8128559b21aSDarrick J. Wong 	default:
8138559b21aSDarrick J. Wong 		ASSERT(0);
8148559b21aSDarrick J. Wong 		error = -EIO;
8158559b21aSDarrick J. Wong 		goto out_cancel;
8168559b21aSDarrick J. Wong 	}
817b1991ee3SDarrick J. Wong 
818b1991ee3SDarrick J. Wong 	/* Commit and unlock. */
819b1991ee3SDarrick J. Wong 	error = xrep_trans_commit(rd->sc);
820b1991ee3SDarrick J. Wong 	if (error)
821b1991ee3SDarrick J. Wong 		return error;
822b1991ee3SDarrick J. Wong 
823b1991ee3SDarrick J. Wong 	xrep_tempfile_iunlock(rd->sc);
824b1991ee3SDarrick J. Wong 	return 0;
825b1991ee3SDarrick J. Wong out_cancel:
826b1991ee3SDarrick J. Wong 	xchk_trans_cancel(rd->sc);
827b1991ee3SDarrick J. Wong 	xrep_tempfile_iunlock(rd->sc);
828b1991ee3SDarrick J. Wong 	return error;
829b1991ee3SDarrick J. Wong }
830b1991ee3SDarrick J. Wong 
831b1991ee3SDarrick J. Wong /*
832b1991ee3SDarrick J. Wong  * Flush stashed incore dirent updates that have been recorded by the scanner.
833b1991ee3SDarrick J. Wong  * This is done to reduce the memory requirements of the directory rebuild,
834b1991ee3SDarrick J. Wong  * since directories can contain up to 32GB of directory data.
835b1991ee3SDarrick J. Wong  *
836b1991ee3SDarrick J. Wong  * Caller must not hold transactions or ILOCKs.  Caller must hold the tempdir
837b1991ee3SDarrick J. Wong  * IOLOCK.
838b1991ee3SDarrick J. Wong  */
839b1991ee3SDarrick J. Wong STATIC int
840b1991ee3SDarrick J. Wong xrep_dir_replay_updates(
841b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd)
842b1991ee3SDarrick J. Wong {
843b1991ee3SDarrick J. Wong 	xfarray_idx_t		array_cur;
844b1991ee3SDarrick J. Wong 	int			error;
845b1991ee3SDarrick J. Wong 
846b1991ee3SDarrick J. Wong 	/* Add all the salvaged dirents to the temporary directory. */
84776fc23b6SDarrick J. Wong 	mutex_lock(&rd->pscan.lock);
848b1991ee3SDarrick J. Wong 	foreach_xfarray_idx(rd->dir_entries, array_cur) {
849b1991ee3SDarrick J. Wong 		struct xrep_dirent	dirent;
850b1991ee3SDarrick J. Wong 
851b1991ee3SDarrick J. Wong 		error = xfarray_load(rd->dir_entries, array_cur, &dirent);
852b1991ee3SDarrick J. Wong 		if (error)
85376fc23b6SDarrick J. Wong 			goto out_unlock;
854b1991ee3SDarrick J. Wong 
855b1991ee3SDarrick J. Wong 		error = xfblob_loadname(rd->dir_names, dirent.name_cookie,
856b1991ee3SDarrick J. Wong 				&rd->xname, dirent.namelen);
857b1991ee3SDarrick J. Wong 		if (error)
85876fc23b6SDarrick J. Wong 			goto out_unlock;
859b1991ee3SDarrick J. Wong 		rd->xname.type = dirent.ftype;
86076fc23b6SDarrick J. Wong 		mutex_unlock(&rd->pscan.lock);
861b1991ee3SDarrick J. Wong 
862b1991ee3SDarrick J. Wong 		error = xrep_dir_replay_update(rd, &rd->xname, &dirent);
863b1991ee3SDarrick J. Wong 		if (error)
864b1991ee3SDarrick J. Wong 			return error;
86576fc23b6SDarrick J. Wong 		mutex_lock(&rd->pscan.lock);
866b1991ee3SDarrick J. Wong 	}
867b1991ee3SDarrick J. Wong 
868b1991ee3SDarrick J. Wong 	/* Empty out both arrays now that we've added the entries. */
869b1991ee3SDarrick J. Wong 	xfarray_truncate(rd->dir_entries);
870b1991ee3SDarrick J. Wong 	xfblob_truncate(rd->dir_names);
87176fc23b6SDarrick J. Wong 	mutex_unlock(&rd->pscan.lock);
872b1991ee3SDarrick J. Wong 	return 0;
87376fc23b6SDarrick J. Wong out_unlock:
87476fc23b6SDarrick J. Wong 	mutex_unlock(&rd->pscan.lock);
87576fc23b6SDarrick J. Wong 	return error;
876b1991ee3SDarrick J. Wong }
877b1991ee3SDarrick J. Wong 
878b1991ee3SDarrick J. Wong /*
879b1991ee3SDarrick J. Wong  * Periodically flush stashed directory entries to the temporary dir.  This
880b1991ee3SDarrick J. Wong  * is done to reduce the memory requirements of the directory rebuild, since
881b1991ee3SDarrick J. Wong  * directories can contain up to 32GB of directory data.
882b1991ee3SDarrick J. Wong  */
883b1991ee3SDarrick J. Wong STATIC int
884b1991ee3SDarrick J. Wong xrep_dir_flush_stashed(
885b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd)
886b1991ee3SDarrick J. Wong {
887b1991ee3SDarrick J. Wong 	int			error;
888b1991ee3SDarrick J. Wong 
889b1991ee3SDarrick J. Wong 	/*
890b1991ee3SDarrick J. Wong 	 * Entering this function, the scrub context has a reference to the
891b1991ee3SDarrick J. Wong 	 * inode being repaired, the temporary file, and a scrub transaction
892b1991ee3SDarrick J. Wong 	 * that we use during dirent salvaging to avoid livelocking if there
893b1991ee3SDarrick J. Wong 	 * are cycles in the directory structures.  We hold ILOCK_EXCL on both
894b1991ee3SDarrick J. Wong 	 * the inode being repaired and the temporary file, though they are
895b1991ee3SDarrick J. Wong 	 * not ijoined to the scrub transaction.
896b1991ee3SDarrick J. Wong 	 *
897b1991ee3SDarrick J. Wong 	 * To constrain kernel memory use, we occasionally write salvaged
898b1991ee3SDarrick J. Wong 	 * dirents from the xfarray and xfblob structures into the temporary
899b1991ee3SDarrick J. Wong 	 * directory in preparation for exchanging the directory structures at
900b1991ee3SDarrick J. Wong 	 * the end.  Updating the temporary file requires a transaction, so we
901b1991ee3SDarrick J. Wong 	 * commit the scrub transaction and drop the two ILOCKs so that
902b1991ee3SDarrick J. Wong 	 * we can allocate whatever transaction we want.
903b1991ee3SDarrick J. Wong 	 *
904b1991ee3SDarrick J. Wong 	 * We still hold IOLOCK_EXCL on the inode being repaired, which
905b1991ee3SDarrick J. Wong 	 * prevents anyone from accessing the damaged directory data while we
906b1991ee3SDarrick J. Wong 	 * repair it.
907b1991ee3SDarrick J. Wong 	 */
908b1991ee3SDarrick J. Wong 	error = xrep_trans_commit(rd->sc);
909b1991ee3SDarrick J. Wong 	if (error)
910b1991ee3SDarrick J. Wong 		return error;
911b1991ee3SDarrick J. Wong 	xchk_iunlock(rd->sc, XFS_ILOCK_EXCL);
912b1991ee3SDarrick J. Wong 
913b1991ee3SDarrick J. Wong 	/*
914b1991ee3SDarrick J. Wong 	 * Take the IOLOCK of the temporary file while we modify dirents.  This
915b1991ee3SDarrick J. Wong 	 * isn't strictly required because the temporary file is never revealed
916b1991ee3SDarrick J. Wong 	 * to userspace, but we follow the same locking rules.  We still hold
917b1991ee3SDarrick J. Wong 	 * sc->ip's IOLOCK.
918b1991ee3SDarrick J. Wong 	 */
919b1991ee3SDarrick J. Wong 	error = xrep_tempfile_iolock_polled(rd->sc);
920b1991ee3SDarrick J. Wong 	if (error)
921b1991ee3SDarrick J. Wong 		return error;
922b1991ee3SDarrick J. Wong 
923b1991ee3SDarrick J. Wong 	/* Write to the tempdir all the updates that we've stashed. */
924b1991ee3SDarrick J. Wong 	error = xrep_dir_replay_updates(rd);
925b1991ee3SDarrick J. Wong 	xrep_tempfile_iounlock(rd->sc);
926b1991ee3SDarrick J. Wong 	if (error)
927b1991ee3SDarrick J. Wong 		return error;
928b1991ee3SDarrick J. Wong 
929b1991ee3SDarrick J. Wong 	/*
930b1991ee3SDarrick J. Wong 	 * Recreate the salvage transaction and relock the dir we're salvaging.
931b1991ee3SDarrick J. Wong 	 */
932b1991ee3SDarrick J. Wong 	error = xchk_trans_alloc(rd->sc, 0);
933b1991ee3SDarrick J. Wong 	if (error)
934b1991ee3SDarrick J. Wong 		return error;
935b1991ee3SDarrick J. Wong 	xchk_ilock(rd->sc, XFS_ILOCK_EXCL);
936b1991ee3SDarrick J. Wong 	return 0;
937b1991ee3SDarrick J. Wong }
938b1991ee3SDarrick J. Wong 
939b1991ee3SDarrick J. Wong /* Decide if we've stashed too much dirent data in memory. */
940b1991ee3SDarrick J. Wong static inline bool
941b1991ee3SDarrick J. Wong xrep_dir_want_flush_stashed(
942b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd)
943b1991ee3SDarrick J. Wong {
944b1991ee3SDarrick J. Wong 	unsigned long long	bytes;
945b1991ee3SDarrick J. Wong 
946b1991ee3SDarrick J. Wong 	bytes = xfarray_bytes(rd->dir_entries) + xfblob_bytes(rd->dir_names);
947b1991ee3SDarrick J. Wong 	return bytes > XREP_DIR_MAX_STASH_BYTES;
948b1991ee3SDarrick J. Wong }
949b1991ee3SDarrick J. Wong 
950b1991ee3SDarrick J. Wong /* Extract as many directory entries as we can. */
951b1991ee3SDarrick J. Wong STATIC int
952b1991ee3SDarrick J. Wong xrep_dir_recover(
953b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd)
954b1991ee3SDarrick J. Wong {
955b1991ee3SDarrick J. Wong 	struct xfs_bmbt_irec	got;
956b1991ee3SDarrick J. Wong 	struct xfs_scrub	*sc = rd->sc;
957b1991ee3SDarrick J. Wong 	struct xfs_da_geometry	*geo = sc->mp->m_dir_geo;
958b1991ee3SDarrick J. Wong 	xfs_fileoff_t		offset;
959b1991ee3SDarrick J. Wong 	xfs_dablk_t		dabno;
960b1991ee3SDarrick J. Wong 	__be32			magic_guess;
961b1991ee3SDarrick J. Wong 	int			nmap;
962b1991ee3SDarrick J. Wong 	int			error;
963b1991ee3SDarrick J. Wong 
964b1991ee3SDarrick J. Wong 	xrep_dir_guess_format(rd, &magic_guess);
965b1991ee3SDarrick J. Wong 
966b1991ee3SDarrick J. Wong 	/* Iterate each directory data block in the data fork. */
967b1991ee3SDarrick J. Wong 	for (offset = 0;
968b1991ee3SDarrick J. Wong 	     offset < geo->leafblk;
969b1991ee3SDarrick J. Wong 	     offset = got.br_startoff + got.br_blockcount) {
970b1991ee3SDarrick J. Wong 		nmap = 1;
971b1991ee3SDarrick J. Wong 		error = xfs_bmapi_read(sc->ip, offset, geo->leafblk - offset,
972b1991ee3SDarrick J. Wong 				&got, &nmap, 0);
973b1991ee3SDarrick J. Wong 		if (error)
974b1991ee3SDarrick J. Wong 			return error;
975b1991ee3SDarrick J. Wong 		if (nmap != 1)
976b1991ee3SDarrick J. Wong 			return -EFSCORRUPTED;
977b1991ee3SDarrick J. Wong 		if (!xfs_bmap_is_written_extent(&got))
978b1991ee3SDarrick J. Wong 			continue;
979b1991ee3SDarrick J. Wong 
980b1991ee3SDarrick J. Wong 		for (dabno = round_up(got.br_startoff, geo->fsbcount);
981b1991ee3SDarrick J. Wong 		     dabno < got.br_startoff + got.br_blockcount;
982b1991ee3SDarrick J. Wong 		     dabno += geo->fsbcount) {
983b1991ee3SDarrick J. Wong 			if (xchk_should_terminate(rd->sc, &error))
984b1991ee3SDarrick J. Wong 				return error;
985b1991ee3SDarrick J. Wong 
986b1991ee3SDarrick J. Wong 			error = xrep_dir_recover_dirblock(rd,
987b1991ee3SDarrick J. Wong 					magic_guess, dabno);
988b1991ee3SDarrick J. Wong 			if (error)
989b1991ee3SDarrick J. Wong 				return error;
990b1991ee3SDarrick J. Wong 
991b1991ee3SDarrick J. Wong 			/* Flush dirents to constrain memory usage. */
992b1991ee3SDarrick J. Wong 			if (xrep_dir_want_flush_stashed(rd)) {
993b1991ee3SDarrick J. Wong 				error = xrep_dir_flush_stashed(rd);
994b1991ee3SDarrick J. Wong 				if (error)
995b1991ee3SDarrick J. Wong 					return error;
996b1991ee3SDarrick J. Wong 			}
997b1991ee3SDarrick J. Wong 		}
998b1991ee3SDarrick J. Wong 	}
999b1991ee3SDarrick J. Wong 
1000b1991ee3SDarrick J. Wong 	return 0;
1001b1991ee3SDarrick J. Wong }
1002b1991ee3SDarrick J. Wong 
1003b1991ee3SDarrick J. Wong /*
1004b1991ee3SDarrick J. Wong  * Find all the directory entries for this inode by scraping them out of the
1005b1991ee3SDarrick J. Wong  * directory leaf blocks by hand, and flushing them into the temp dir.
1006b1991ee3SDarrick J. Wong  */
1007b1991ee3SDarrick J. Wong STATIC int
1008b1991ee3SDarrick J. Wong xrep_dir_find_entries(
1009b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd)
1010b1991ee3SDarrick J. Wong {
1011b1991ee3SDarrick J. Wong 	struct xfs_inode	*dp = rd->sc->ip;
1012b1991ee3SDarrick J. Wong 	int			error;
1013b1991ee3SDarrick J. Wong 
1014b1991ee3SDarrick J. Wong 	/*
1015b1991ee3SDarrick J. Wong 	 * Salvage directory entries from the old directory, and write them to
1016b1991ee3SDarrick J. Wong 	 * the temporary directory.
1017b1991ee3SDarrick J. Wong 	 */
1018b1991ee3SDarrick J. Wong 	if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
1019b1991ee3SDarrick J. Wong 		error = xrep_dir_recover_sf(rd);
1020b1991ee3SDarrick J. Wong 	} else {
1021b1991ee3SDarrick J. Wong 		error = xfs_iread_extents(rd->sc->tp, dp, XFS_DATA_FORK);
1022b1991ee3SDarrick J. Wong 		if (error)
1023b1991ee3SDarrick J. Wong 			return error;
1024b1991ee3SDarrick J. Wong 
1025b1991ee3SDarrick J. Wong 		error = xrep_dir_recover(rd);
1026b1991ee3SDarrick J. Wong 	}
1027b1991ee3SDarrick J. Wong 	if (error)
1028b1991ee3SDarrick J. Wong 		return error;
1029b1991ee3SDarrick J. Wong 
1030b1991ee3SDarrick J. Wong 	return xrep_dir_flush_stashed(rd);
1031b1991ee3SDarrick J. Wong }
1032b1991ee3SDarrick J. Wong 
1033b1991ee3SDarrick J. Wong /* Scan all files in the filesystem for dirents. */
1034b1991ee3SDarrick J. Wong STATIC int
1035b1991ee3SDarrick J. Wong xrep_dir_salvage_entries(
1036b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd)
1037b1991ee3SDarrick J. Wong {
1038b1991ee3SDarrick J. Wong 	struct xfs_scrub	*sc = rd->sc;
1039b1991ee3SDarrick J. Wong 	int			error;
1040b1991ee3SDarrick J. Wong 
1041b1991ee3SDarrick J. Wong 	/*
1042b1991ee3SDarrick J. Wong 	 * Drop the ILOCK on this directory so that we can scan for this
1043b1991ee3SDarrick J. Wong 	 * directory's parent.  Figure out who is going to be the parent of
1044b1991ee3SDarrick J. Wong 	 * this directory, then retake the ILOCK so that we can salvage
1045b1991ee3SDarrick J. Wong 	 * directory entries.
1046b1991ee3SDarrick J. Wong 	 */
1047b1991ee3SDarrick J. Wong 	xchk_iunlock(sc, XFS_ILOCK_EXCL);
1048b1991ee3SDarrick J. Wong 	error = xrep_dir_find_parent(rd);
1049b1991ee3SDarrick J. Wong 	xchk_ilock(sc, XFS_ILOCK_EXCL);
1050b1991ee3SDarrick J. Wong 	if (error)
1051b1991ee3SDarrick J. Wong 		return error;
1052b1991ee3SDarrick J. Wong 
1053b1991ee3SDarrick J. Wong 	/*
1054b1991ee3SDarrick J. Wong 	 * Collect directory entries by parsing raw leaf blocks to salvage
1055b1991ee3SDarrick J. Wong 	 * whatever we can.  When we're done, free the staging memory before
1056b1991ee3SDarrick J. Wong 	 * exchanging the directories to reduce memory usage.
1057b1991ee3SDarrick J. Wong 	 */
1058b1991ee3SDarrick J. Wong 	error = xrep_dir_find_entries(rd);
1059b1991ee3SDarrick J. Wong 	if (error)
1060b1991ee3SDarrick J. Wong 		return error;
1061b1991ee3SDarrick J. Wong 
1062b1991ee3SDarrick J. Wong 	/*
1063b1991ee3SDarrick J. Wong 	 * Cancel the repair transaction and drop the ILOCK so that we can
1064b1991ee3SDarrick J. Wong 	 * (later) use the atomic mapping exchange functions to compute the
1065b1991ee3SDarrick J. Wong 	 * correct block reservations and re-lock the inodes.
1066b1991ee3SDarrick J. Wong 	 *
1067b1991ee3SDarrick J. Wong 	 * We still hold IOLOCK_EXCL (aka i_rwsem) which will prevent directory
1068b1991ee3SDarrick J. Wong 	 * modifications, but there's nothing to prevent userspace from reading
1069b1991ee3SDarrick J. Wong 	 * the directory until we're ready for the exchange operation.  Reads
1070b1991ee3SDarrick J. Wong 	 * will return -EIO without shutting down the fs, so we're ok with
1071b1991ee3SDarrick J. Wong 	 * that.
1072a07b4557SDarrick J. Wong 	 *
1073a07b4557SDarrick J. Wong 	 * The VFS can change dotdot on us, but the findparent scan will keep
1074a07b4557SDarrick J. Wong 	 * our incore parent inode up to date.  See the note on locking issues
1075a07b4557SDarrick J. Wong 	 * for more details.
1076b1991ee3SDarrick J. Wong 	 */
1077b1991ee3SDarrick J. Wong 	error = xrep_trans_commit(sc);
1078b1991ee3SDarrick J. Wong 	if (error)
1079b1991ee3SDarrick J. Wong 		return error;
1080b1991ee3SDarrick J. Wong 
1081b1991ee3SDarrick J. Wong 	xchk_iunlock(sc, XFS_ILOCK_EXCL);
1082b1991ee3SDarrick J. Wong 	return 0;
1083b1991ee3SDarrick J. Wong }
1084b1991ee3SDarrick J. Wong 
1085b1991ee3SDarrick J. Wong 
1086b1991ee3SDarrick J. Wong /*
108776fc23b6SDarrick J. Wong  * Examine a parent pointer of a file.  If it leads us back to the directory
108876fc23b6SDarrick J. Wong  * that we're rebuilding, create an incore dirent from the parent pointer and
108976fc23b6SDarrick J. Wong  * stash it.
109076fc23b6SDarrick J. Wong  */
109176fc23b6SDarrick J. Wong STATIC int
109276fc23b6SDarrick J. Wong xrep_dir_scan_pptr(
109376fc23b6SDarrick J. Wong 	struct xfs_scrub		*sc,
109476fc23b6SDarrick J. Wong 	struct xfs_inode		*ip,
109576fc23b6SDarrick J. Wong 	unsigned int			attr_flags,
109676fc23b6SDarrick J. Wong 	const unsigned char		*name,
109776fc23b6SDarrick J. Wong 	unsigned int			namelen,
109876fc23b6SDarrick J. Wong 	const void			*value,
109976fc23b6SDarrick J. Wong 	unsigned int			valuelen,
110076fc23b6SDarrick J. Wong 	void				*priv)
110176fc23b6SDarrick J. Wong {
110276fc23b6SDarrick J. Wong 	struct xfs_name			xname = {
110376fc23b6SDarrick J. Wong 		.name			= name,
110476fc23b6SDarrick J. Wong 		.len			= namelen,
110576fc23b6SDarrick J. Wong 		.type			= xfs_mode_to_ftype(VFS_I(ip)->i_mode),
110676fc23b6SDarrick J. Wong 	};
110776fc23b6SDarrick J. Wong 	xfs_ino_t			parent_ino;
110876fc23b6SDarrick J. Wong 	uint32_t			parent_gen;
110976fc23b6SDarrick J. Wong 	struct xrep_dir			*rd = priv;
111076fc23b6SDarrick J. Wong 	int				error;
111176fc23b6SDarrick J. Wong 
111276fc23b6SDarrick J. Wong 	if (!(attr_flags & XFS_ATTR_PARENT))
111376fc23b6SDarrick J. Wong 		return 0;
111476fc23b6SDarrick J. Wong 
111576fc23b6SDarrick J. Wong 	/*
111676fc23b6SDarrick J. Wong 	 * Ignore parent pointers that point back to a different dir, list the
111776fc23b6SDarrick J. Wong 	 * wrong generation number, or are invalid.
111876fc23b6SDarrick J. Wong 	 */
111976fc23b6SDarrick J. Wong 	error = xfs_parent_from_attr(sc->mp, attr_flags, name, namelen, value,
112076fc23b6SDarrick J. Wong 			valuelen, &parent_ino, &parent_gen);
112176fc23b6SDarrick J. Wong 	if (error)
112276fc23b6SDarrick J. Wong 		return error;
112376fc23b6SDarrick J. Wong 
112476fc23b6SDarrick J. Wong 	if (parent_ino != sc->ip->i_ino ||
112576fc23b6SDarrick J. Wong 	    parent_gen != VFS_I(sc->ip)->i_generation)
112676fc23b6SDarrick J. Wong 		return 0;
112776fc23b6SDarrick J. Wong 
112876fc23b6SDarrick J. Wong 	mutex_lock(&rd->pscan.lock);
112976fc23b6SDarrick J. Wong 	error = xrep_dir_stash_createname(rd, &xname, ip->i_ino);
113076fc23b6SDarrick J. Wong 	mutex_unlock(&rd->pscan.lock);
113176fc23b6SDarrick J. Wong 	return error;
113276fc23b6SDarrick J. Wong }
113376fc23b6SDarrick J. Wong 
113476fc23b6SDarrick J. Wong /*
113576fc23b6SDarrick J. Wong  * If this child dirent points to the directory being repaired, remember that
113676fc23b6SDarrick J. Wong  * fact so that we can reset the dotdot entry if necessary.
113776fc23b6SDarrick J. Wong  */
113876fc23b6SDarrick J. Wong STATIC int
113976fc23b6SDarrick J. Wong xrep_dir_scan_dirent(
114076fc23b6SDarrick J. Wong 	struct xfs_scrub	*sc,
114176fc23b6SDarrick J. Wong 	struct xfs_inode	*dp,
114276fc23b6SDarrick J. Wong 	xfs_dir2_dataptr_t	dapos,
114376fc23b6SDarrick J. Wong 	const struct xfs_name	*name,
114476fc23b6SDarrick J. Wong 	xfs_ino_t		ino,
114576fc23b6SDarrick J. Wong 	void			*priv)
114676fc23b6SDarrick J. Wong {
114776fc23b6SDarrick J. Wong 	struct xrep_dir		*rd = priv;
114876fc23b6SDarrick J. Wong 
114976fc23b6SDarrick J. Wong 	/* Dirent doesn't point to this directory. */
115076fc23b6SDarrick J. Wong 	if (ino != rd->sc->ip->i_ino)
115176fc23b6SDarrick J. Wong 		return 0;
115276fc23b6SDarrick J. Wong 
115376fc23b6SDarrick J. Wong 	/* Ignore garbage inum. */
115476fc23b6SDarrick J. Wong 	if (!xfs_verify_dir_ino(rd->sc->mp, ino))
115576fc23b6SDarrick J. Wong 		return 0;
115676fc23b6SDarrick J. Wong 
115776fc23b6SDarrick J. Wong 	/* No weird looking names. */
115876fc23b6SDarrick J. Wong 	if (name->len >= MAXNAMELEN || name->len <= 0)
115976fc23b6SDarrick J. Wong 		return 0;
116076fc23b6SDarrick J. Wong 
116176fc23b6SDarrick J. Wong 	/* Don't pick up dot or dotdot entries; we only want child dirents. */
116276fc23b6SDarrick J. Wong 	if (xfs_dir2_samename(name, &xfs_name_dotdot) ||
116376fc23b6SDarrick J. Wong 	    xfs_dir2_samename(name, &xfs_name_dot))
116476fc23b6SDarrick J. Wong 		return 0;
116576fc23b6SDarrick J. Wong 
116676fc23b6SDarrick J. Wong 	trace_xrep_dir_stash_createname(sc->tempip, &xfs_name_dotdot,
116776fc23b6SDarrick J. Wong 			dp->i_ino);
116876fc23b6SDarrick J. Wong 
116976fc23b6SDarrick J. Wong 	xrep_findparent_scan_found(&rd->pscan, dp->i_ino);
117076fc23b6SDarrick J. Wong 	return 0;
117176fc23b6SDarrick J. Wong }
117276fc23b6SDarrick J. Wong 
117376fc23b6SDarrick J. Wong /*
117476fc23b6SDarrick J. Wong  * Decide if we want to look for child dirents or parent pointers in this file.
117576fc23b6SDarrick J. Wong  * Skip the dir being repaired and any files being used to stage repairs.
117676fc23b6SDarrick J. Wong  */
117776fc23b6SDarrick J. Wong static inline bool
117876fc23b6SDarrick J. Wong xrep_dir_want_scan(
117976fc23b6SDarrick J. Wong 	struct xrep_dir		*rd,
118076fc23b6SDarrick J. Wong 	const struct xfs_inode	*ip)
118176fc23b6SDarrick J. Wong {
118276fc23b6SDarrick J. Wong 	return ip != rd->sc->ip && !xrep_is_tempfile(ip);
118376fc23b6SDarrick J. Wong }
118476fc23b6SDarrick J. Wong 
118576fc23b6SDarrick J. Wong /*
118676fc23b6SDarrick J. Wong  * Take ILOCK on a file that we want to scan.
118776fc23b6SDarrick J. Wong  *
118876fc23b6SDarrick J. Wong  * Select ILOCK_EXCL if the file is a directory with an unloaded data bmbt or
118976fc23b6SDarrick J. Wong  * has an unloaded attr bmbt.  Otherwise, take ILOCK_SHARED.
119076fc23b6SDarrick J. Wong  */
119176fc23b6SDarrick J. Wong static inline unsigned int
119276fc23b6SDarrick J. Wong xrep_dir_scan_ilock(
119376fc23b6SDarrick J. Wong 	struct xrep_dir		*rd,
119476fc23b6SDarrick J. Wong 	struct xfs_inode	*ip)
119576fc23b6SDarrick J. Wong {
119676fc23b6SDarrick J. Wong 	uint			lock_mode = XFS_ILOCK_SHARED;
119776fc23b6SDarrick J. Wong 
119876fc23b6SDarrick J. Wong 	/* Need to take the shared ILOCK to advance the iscan cursor. */
119976fc23b6SDarrick J. Wong 	if (!xrep_dir_want_scan(rd, ip))
120076fc23b6SDarrick J. Wong 		goto lock;
120176fc23b6SDarrick J. Wong 
120276fc23b6SDarrick J. Wong 	if (S_ISDIR(VFS_I(ip)->i_mode) && xfs_need_iread_extents(&ip->i_df)) {
120376fc23b6SDarrick J. Wong 		lock_mode = XFS_ILOCK_EXCL;
120476fc23b6SDarrick J. Wong 		goto lock;
120576fc23b6SDarrick J. Wong 	}
120676fc23b6SDarrick J. Wong 
120776fc23b6SDarrick J. Wong 	if (xfs_inode_has_attr_fork(ip) && xfs_need_iread_extents(&ip->i_af))
120876fc23b6SDarrick J. Wong 		lock_mode = XFS_ILOCK_EXCL;
120976fc23b6SDarrick J. Wong 
121076fc23b6SDarrick J. Wong lock:
121176fc23b6SDarrick J. Wong 	xfs_ilock(ip, lock_mode);
121276fc23b6SDarrick J. Wong 	return lock_mode;
121376fc23b6SDarrick J. Wong }
121476fc23b6SDarrick J. Wong 
121576fc23b6SDarrick J. Wong /*
121676fc23b6SDarrick J. Wong  * Scan this file for relevant child dirents or parent pointers that point to
121776fc23b6SDarrick J. Wong  * the directory we're rebuilding.
121876fc23b6SDarrick J. Wong  */
121976fc23b6SDarrick J. Wong STATIC int
122076fc23b6SDarrick J. Wong xrep_dir_scan_file(
122176fc23b6SDarrick J. Wong 	struct xrep_dir		*rd,
122276fc23b6SDarrick J. Wong 	struct xfs_inode	*ip)
122376fc23b6SDarrick J. Wong {
122476fc23b6SDarrick J. Wong 	unsigned int		lock_mode;
122576fc23b6SDarrick J. Wong 	int			error = 0;
122676fc23b6SDarrick J. Wong 
122776fc23b6SDarrick J. Wong 	lock_mode = xrep_dir_scan_ilock(rd, ip);
122876fc23b6SDarrick J. Wong 
122976fc23b6SDarrick J. Wong 	if (!xrep_dir_want_scan(rd, ip))
123076fc23b6SDarrick J. Wong 		goto scan_done;
123176fc23b6SDarrick J. Wong 
123276fc23b6SDarrick J. Wong 	/*
123376fc23b6SDarrick J. Wong 	 * If the extended attributes look as though they has been zapped by
123476fc23b6SDarrick J. Wong 	 * the inode record repair code, we cannot scan for parent pointers.
123576fc23b6SDarrick J. Wong 	 */
123676fc23b6SDarrick J. Wong 	if (xchk_pptr_looks_zapped(ip)) {
123776fc23b6SDarrick J. Wong 		error = -EBUSY;
123876fc23b6SDarrick J. Wong 		goto scan_done;
123976fc23b6SDarrick J. Wong 	}
124076fc23b6SDarrick J. Wong 
12416efbbdebSDarrick J. Wong 	error = xchk_xattr_walk(rd->sc, ip, xrep_dir_scan_pptr, NULL, rd);
124276fc23b6SDarrick J. Wong 	if (error)
124376fc23b6SDarrick J. Wong 		goto scan_done;
124476fc23b6SDarrick J. Wong 
124576fc23b6SDarrick J. Wong 	if (S_ISDIR(VFS_I(ip)->i_mode)) {
124676fc23b6SDarrick J. Wong 		/*
124776fc23b6SDarrick J. Wong 		 * If the directory looks as though it has been zapped by the
124876fc23b6SDarrick J. Wong 		 * inode record repair code, we cannot scan for child dirents.
124976fc23b6SDarrick J. Wong 		 */
125076fc23b6SDarrick J. Wong 		if (xchk_dir_looks_zapped(ip)) {
125176fc23b6SDarrick J. Wong 			error = -EBUSY;
125276fc23b6SDarrick J. Wong 			goto scan_done;
125376fc23b6SDarrick J. Wong 		}
125476fc23b6SDarrick J. Wong 
125576fc23b6SDarrick J. Wong 		error = xchk_dir_walk(rd->sc, ip, xrep_dir_scan_dirent, rd);
125676fc23b6SDarrick J. Wong 		if (error)
125776fc23b6SDarrick J. Wong 			goto scan_done;
125876fc23b6SDarrick J. Wong 	}
125976fc23b6SDarrick J. Wong 
126076fc23b6SDarrick J. Wong scan_done:
126176fc23b6SDarrick J. Wong 	xchk_iscan_mark_visited(&rd->pscan.iscan, ip);
126276fc23b6SDarrick J. Wong 	xfs_iunlock(ip, lock_mode);
126376fc23b6SDarrick J. Wong 	return error;
126476fc23b6SDarrick J. Wong }
126576fc23b6SDarrick J. Wong 
126676fc23b6SDarrick J. Wong /*
126776fc23b6SDarrick J. Wong  * Scan all files in the filesystem for parent pointers that we can turn into
126876fc23b6SDarrick J. Wong  * replacement dirents, and a dirent that we can use to set the dotdot pointer.
126976fc23b6SDarrick J. Wong  */
127076fc23b6SDarrick J. Wong STATIC int
127176fc23b6SDarrick J. Wong xrep_dir_scan_dirtree(
127276fc23b6SDarrick J. Wong 	struct xrep_dir		*rd)
127376fc23b6SDarrick J. Wong {
127476fc23b6SDarrick J. Wong 	struct xfs_scrub	*sc = rd->sc;
127576fc23b6SDarrick J. Wong 	struct xfs_inode	*ip;
127676fc23b6SDarrick J. Wong 	int			error;
127776fc23b6SDarrick J. Wong 
127876fc23b6SDarrick J. Wong 	/* Roots of directory trees are their own parents. */
1279679b098bSDarrick J. Wong 	if (xchk_inode_is_dirtree_root(sc->ip))
128076fc23b6SDarrick J. Wong 		xrep_findparent_scan_found(&rd->pscan, sc->ip->i_ino);
128176fc23b6SDarrick J. Wong 
128276fc23b6SDarrick J. Wong 	/*
128376fc23b6SDarrick J. Wong 	 * Filesystem scans are time consuming.  Drop the directory ILOCK and
128476fc23b6SDarrick J. Wong 	 * all other resources for the duration of the scan and hope for the
128576fc23b6SDarrick J. Wong 	 * best.  The live update hooks will keep our scan information up to
128676fc23b6SDarrick J. Wong 	 * date even though we've dropped the locks.
128776fc23b6SDarrick J. Wong 	 */
128876fc23b6SDarrick J. Wong 	xchk_trans_cancel(sc);
128976fc23b6SDarrick J. Wong 	if (sc->ilock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL))
129076fc23b6SDarrick J. Wong 		xchk_iunlock(sc, sc->ilock_flags & (XFS_ILOCK_SHARED |
129176fc23b6SDarrick J. Wong 						    XFS_ILOCK_EXCL));
129276fc23b6SDarrick J. Wong 	error = xchk_trans_alloc_empty(sc);
129376fc23b6SDarrick J. Wong 	if (error)
129476fc23b6SDarrick J. Wong 		return error;
129576fc23b6SDarrick J. Wong 
129676fc23b6SDarrick J. Wong 	while ((error = xchk_iscan_iter(&rd->pscan.iscan, &ip)) == 1) {
129776fc23b6SDarrick J. Wong 		bool		flush;
129876fc23b6SDarrick J. Wong 
129976fc23b6SDarrick J. Wong 		error = xrep_dir_scan_file(rd, ip);
130076fc23b6SDarrick J. Wong 		xchk_irele(sc, ip);
130176fc23b6SDarrick J. Wong 		if (error)
130276fc23b6SDarrick J. Wong 			break;
130376fc23b6SDarrick J. Wong 
130476fc23b6SDarrick J. Wong 		/* Flush stashed dirent updates to constrain memory usage. */
130576fc23b6SDarrick J. Wong 		mutex_lock(&rd->pscan.lock);
130676fc23b6SDarrick J. Wong 		flush = xrep_dir_want_flush_stashed(rd);
130776fc23b6SDarrick J. Wong 		mutex_unlock(&rd->pscan.lock);
130876fc23b6SDarrick J. Wong 		if (flush) {
130976fc23b6SDarrick J. Wong 			xchk_trans_cancel(sc);
131076fc23b6SDarrick J. Wong 
131176fc23b6SDarrick J. Wong 			error = xrep_tempfile_iolock_polled(sc);
131276fc23b6SDarrick J. Wong 			if (error)
131376fc23b6SDarrick J. Wong 				break;
131476fc23b6SDarrick J. Wong 
131576fc23b6SDarrick J. Wong 			error = xrep_dir_replay_updates(rd);
131676fc23b6SDarrick J. Wong 			xrep_tempfile_iounlock(sc);
131776fc23b6SDarrick J. Wong 			if (error)
131876fc23b6SDarrick J. Wong 				break;
131976fc23b6SDarrick J. Wong 
132076fc23b6SDarrick J. Wong 			error = xchk_trans_alloc_empty(sc);
132176fc23b6SDarrick J. Wong 			if (error)
132276fc23b6SDarrick J. Wong 				break;
132376fc23b6SDarrick J. Wong 		}
132476fc23b6SDarrick J. Wong 
132576fc23b6SDarrick J. Wong 		if (xchk_should_terminate(sc, &error))
132676fc23b6SDarrick J. Wong 			break;
132776fc23b6SDarrick J. Wong 	}
132876fc23b6SDarrick J. Wong 	xchk_iscan_iter_finish(&rd->pscan.iscan);
132976fc23b6SDarrick J. Wong 	if (error) {
133076fc23b6SDarrick J. Wong 		/*
133176fc23b6SDarrick J. Wong 		 * If we couldn't grab an inode that was busy with a state
133276fc23b6SDarrick J. Wong 		 * change, change the error code so that we exit to userspace
133376fc23b6SDarrick J. Wong 		 * as quickly as possible.
133476fc23b6SDarrick J. Wong 		 */
133576fc23b6SDarrick J. Wong 		if (error == -EBUSY)
133676fc23b6SDarrick J. Wong 			return -ECANCELED;
133776fc23b6SDarrick J. Wong 		return error;
133876fc23b6SDarrick J. Wong 	}
133976fc23b6SDarrick J. Wong 
134076fc23b6SDarrick J. Wong 	/*
134176fc23b6SDarrick J. Wong 	 * Cancel the empty transaction so that we can (later) use the atomic
134276fc23b6SDarrick J. Wong 	 * file mapping exchange functions to lock files and commit the new
134376fc23b6SDarrick J. Wong 	 * directory.
134476fc23b6SDarrick J. Wong 	 */
134576fc23b6SDarrick J. Wong 	xchk_trans_cancel(rd->sc);
134676fc23b6SDarrick J. Wong 	return 0;
134776fc23b6SDarrick J. Wong }
134876fc23b6SDarrick J. Wong 
134976fc23b6SDarrick J. Wong /*
13508559b21aSDarrick J. Wong  * Capture dirent updates being made by other threads which are relevant to the
13518559b21aSDarrick J. Wong  * directory being repaired.
13528559b21aSDarrick J. Wong  */
13538559b21aSDarrick J. Wong STATIC int
13548559b21aSDarrick J. Wong xrep_dir_live_update(
13558559b21aSDarrick J. Wong 	struct notifier_block		*nb,
13568559b21aSDarrick J. Wong 	unsigned long			action,
13578559b21aSDarrick J. Wong 	void				*data)
13588559b21aSDarrick J. Wong {
13598559b21aSDarrick J. Wong 	struct xfs_dir_update_params	*p = data;
13608559b21aSDarrick J. Wong 	struct xrep_dir			*rd;
13618559b21aSDarrick J. Wong 	struct xfs_scrub		*sc;
13628559b21aSDarrick J. Wong 	int				error = 0;
13638559b21aSDarrick J. Wong 
13648559b21aSDarrick J. Wong 	rd = container_of(nb, struct xrep_dir, pscan.dhook.dirent_hook.nb);
13658559b21aSDarrick J. Wong 	sc = rd->sc;
13668559b21aSDarrick J. Wong 
13678559b21aSDarrick J. Wong 	/*
13688559b21aSDarrick J. Wong 	 * This thread updated a child dirent in the directory that we're
13698559b21aSDarrick J. Wong 	 * rebuilding.  Stash the update for replay against the temporary
13708559b21aSDarrick J. Wong 	 * directory.
13718559b21aSDarrick J. Wong 	 */
13728559b21aSDarrick J. Wong 	if (p->dp->i_ino == sc->ip->i_ino &&
13738559b21aSDarrick J. Wong 	    xchk_iscan_want_live_update(&rd->pscan.iscan, p->ip->i_ino)) {
13748559b21aSDarrick J. Wong 		mutex_lock(&rd->pscan.lock);
13758559b21aSDarrick J. Wong 		if (p->delta > 0)
13768559b21aSDarrick J. Wong 			error = xrep_dir_stash_createname(rd, p->name,
13778559b21aSDarrick J. Wong 					p->ip->i_ino);
13788559b21aSDarrick J. Wong 		else
13798559b21aSDarrick J. Wong 			error = xrep_dir_stash_removename(rd, p->name,
13808559b21aSDarrick J. Wong 					p->ip->i_ino);
13818559b21aSDarrick J. Wong 		mutex_unlock(&rd->pscan.lock);
13828559b21aSDarrick J. Wong 		if (error)
13838559b21aSDarrick J. Wong 			goto out_abort;
13848559b21aSDarrick J. Wong 	}
13858559b21aSDarrick J. Wong 
13868559b21aSDarrick J. Wong 	/*
13878559b21aSDarrick J. Wong 	 * This thread updated another directory's child dirent that points to
13888559b21aSDarrick J. Wong 	 * the directory that we're rebuilding, so remember the new dotdot
13898559b21aSDarrick J. Wong 	 * target.
13908559b21aSDarrick J. Wong 	 */
13918559b21aSDarrick J. Wong 	if (p->ip->i_ino == sc->ip->i_ino &&
13928559b21aSDarrick J. Wong 	    xchk_iscan_want_live_update(&rd->pscan.iscan, p->dp->i_ino)) {
13938559b21aSDarrick J. Wong 		if (p->delta > 0) {
13948559b21aSDarrick J. Wong 			trace_xrep_dir_stash_createname(sc->tempip,
13958559b21aSDarrick J. Wong 					&xfs_name_dotdot,
13968559b21aSDarrick J. Wong 					p->dp->i_ino);
13978559b21aSDarrick J. Wong 
13988559b21aSDarrick J. Wong 			xrep_findparent_scan_found(&rd->pscan, p->dp->i_ino);
13998559b21aSDarrick J. Wong 		} else {
14008559b21aSDarrick J. Wong 			trace_xrep_dir_stash_removename(sc->tempip,
14018559b21aSDarrick J. Wong 					&xfs_name_dotdot,
14028559b21aSDarrick J. Wong 					rd->pscan.parent_ino);
14038559b21aSDarrick J. Wong 
14048559b21aSDarrick J. Wong 			xrep_findparent_scan_found(&rd->pscan, NULLFSINO);
14058559b21aSDarrick J. Wong 		}
14068559b21aSDarrick J. Wong 	}
14078559b21aSDarrick J. Wong 
14088559b21aSDarrick J. Wong 	return NOTIFY_DONE;
14098559b21aSDarrick J. Wong out_abort:
14108559b21aSDarrick J. Wong 	xchk_iscan_abort(&rd->pscan.iscan);
14118559b21aSDarrick J. Wong 	return NOTIFY_DONE;
14128559b21aSDarrick J. Wong }
14138559b21aSDarrick J. Wong 
14148559b21aSDarrick J. Wong /*
1415b1991ee3SDarrick J. Wong  * Free all the directory blocks and reset the data fork.  The caller must
1416b1991ee3SDarrick J. Wong  * join the inode to the transaction.  This function returns with the inode
1417b1991ee3SDarrick J. Wong  * joined to a clean scrub transaction.
1418b1991ee3SDarrick J. Wong  */
1419b1991ee3SDarrick J. Wong STATIC int
1420b1991ee3SDarrick J. Wong xrep_dir_reset_fork(
1421b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd,
1422b1991ee3SDarrick J. Wong 	xfs_ino_t		parent_ino)
1423b1991ee3SDarrick J. Wong {
1424b1991ee3SDarrick J. Wong 	struct xfs_scrub	*sc = rd->sc;
1425b1991ee3SDarrick J. Wong 	struct xfs_ifork	*ifp = xfs_ifork_ptr(sc->tempip, XFS_DATA_FORK);
1426b1991ee3SDarrick J. Wong 	int			error;
1427b1991ee3SDarrick J. Wong 
1428b1991ee3SDarrick J. Wong 	/* Unmap all the directory buffers. */
1429b1991ee3SDarrick J. Wong 	if (xfs_ifork_has_extents(ifp)) {
1430b1991ee3SDarrick J. Wong 		error = xrep_reap_ifork(sc, sc->tempip, XFS_DATA_FORK);
1431b1991ee3SDarrick J. Wong 		if (error)
1432b1991ee3SDarrick J. Wong 			return error;
1433b1991ee3SDarrick J. Wong 	}
1434b1991ee3SDarrick J. Wong 
1435b1991ee3SDarrick J. Wong 	trace_xrep_dir_reset_fork(sc->tempip, parent_ino);
1436b1991ee3SDarrick J. Wong 
1437b1991ee3SDarrick J. Wong 	/* Reset the data fork to an empty data fork. */
1438b1991ee3SDarrick J. Wong 	xfs_idestroy_fork(ifp);
1439b1991ee3SDarrick J. Wong 	ifp->if_bytes = 0;
1440b1991ee3SDarrick J. Wong 	sc->tempip->i_disk_size = 0;
1441b1991ee3SDarrick J. Wong 
1442b1991ee3SDarrick J. Wong 	/* Reinitialize the short form directory. */
1443b1991ee3SDarrick J. Wong 	xrep_dir_init_args(rd, sc->tempip, NULL);
1444b1991ee3SDarrick J. Wong 	return xfs_dir2_sf_create(&rd->args, parent_ino);
1445b1991ee3SDarrick J. Wong }
1446b1991ee3SDarrick J. Wong 
1447b1991ee3SDarrick J. Wong /*
1448b1991ee3SDarrick J. Wong  * Prepare both inodes' directory forks for exchanging mappings.  Promote the
1449b1991ee3SDarrick J. Wong  * tempfile from short format to leaf format, and if the file being repaired
1450b1991ee3SDarrick J. Wong  * has a short format data fork, turn it into an empty extent list.
1451b1991ee3SDarrick J. Wong  */
1452b1991ee3SDarrick J. Wong STATIC int
1453b1991ee3SDarrick J. Wong xrep_dir_swap_prep(
1454b1991ee3SDarrick J. Wong 	struct xfs_scrub	*sc,
1455b1991ee3SDarrick J. Wong 	bool			temp_local,
1456b1991ee3SDarrick J. Wong 	bool			ip_local)
1457b1991ee3SDarrick J. Wong {
1458b1991ee3SDarrick J. Wong 	int			error;
1459b1991ee3SDarrick J. Wong 
1460b1991ee3SDarrick J. Wong 	/*
1461b1991ee3SDarrick J. Wong 	 * If the tempfile's directory is in shortform format, convert that to
1462b1991ee3SDarrick J. Wong 	 * a single leaf extent so that we can use the atomic mapping exchange.
1463b1991ee3SDarrick J. Wong 	 */
1464b1991ee3SDarrick J. Wong 	if (temp_local) {
1465b1991ee3SDarrick J. Wong 		struct xfs_da_args	args = {
1466b1991ee3SDarrick J. Wong 			.dp		= sc->tempip,
1467b1991ee3SDarrick J. Wong 			.geo		= sc->mp->m_dir_geo,
1468b1991ee3SDarrick J. Wong 			.whichfork	= XFS_DATA_FORK,
1469b1991ee3SDarrick J. Wong 			.trans		= sc->tp,
1470b1991ee3SDarrick J. Wong 			.total		= 1,
1471b1991ee3SDarrick J. Wong 			.owner		= sc->ip->i_ino,
1472b1991ee3SDarrick J. Wong 		};
1473b1991ee3SDarrick J. Wong 
1474b1991ee3SDarrick J. Wong 		error = xfs_dir2_sf_to_block(&args);
1475b1991ee3SDarrick J. Wong 		if (error)
1476b1991ee3SDarrick J. Wong 			return error;
1477b1991ee3SDarrick J. Wong 
1478b1991ee3SDarrick J. Wong 		/*
1479b1991ee3SDarrick J. Wong 		 * Roll the deferred log items to get us back to a clean
1480b1991ee3SDarrick J. Wong 		 * transaction.
1481b1991ee3SDarrick J. Wong 		 */
1482b1991ee3SDarrick J. Wong 		error = xfs_defer_finish(&sc->tp);
1483b1991ee3SDarrick J. Wong 		if (error)
1484b1991ee3SDarrick J. Wong 			return error;
1485b1991ee3SDarrick J. Wong 	}
1486b1991ee3SDarrick J. Wong 
1487b1991ee3SDarrick J. Wong 	/*
1488b1991ee3SDarrick J. Wong 	 * If the file being repaired had a shortform data fork, convert that
1489b1991ee3SDarrick J. Wong 	 * to an empty extent list in preparation for the atomic mapping
1490b1991ee3SDarrick J. Wong 	 * exchange.
1491b1991ee3SDarrick J. Wong 	 */
1492b1991ee3SDarrick J. Wong 	if (ip_local) {
1493b1991ee3SDarrick J. Wong 		struct xfs_ifork	*ifp;
1494b1991ee3SDarrick J. Wong 
1495b1991ee3SDarrick J. Wong 		ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK);
1496b1991ee3SDarrick J. Wong 		xfs_idestroy_fork(ifp);
1497b1991ee3SDarrick J. Wong 		ifp->if_format = XFS_DINODE_FMT_EXTENTS;
1498b1991ee3SDarrick J. Wong 		ifp->if_nextents = 0;
1499b1991ee3SDarrick J. Wong 		ifp->if_bytes = 0;
1500b1991ee3SDarrick J. Wong 		ifp->if_data = NULL;
1501b1991ee3SDarrick J. Wong 		ifp->if_height = 0;
1502b1991ee3SDarrick J. Wong 
1503b1991ee3SDarrick J. Wong 		xfs_trans_log_inode(sc->tp, sc->ip,
1504b1991ee3SDarrick J. Wong 				XFS_ILOG_CORE | XFS_ILOG_DDATA);
1505b1991ee3SDarrick J. Wong 	}
1506b1991ee3SDarrick J. Wong 
1507b1991ee3SDarrick J. Wong 	return 0;
1508b1991ee3SDarrick J. Wong }
1509b1991ee3SDarrick J. Wong 
1510b1991ee3SDarrick J. Wong /*
1511b1991ee3SDarrick J. Wong  * Replace the inode number of a directory entry.
1512b1991ee3SDarrick J. Wong  */
1513b1991ee3SDarrick J. Wong static int
1514b1991ee3SDarrick J. Wong xrep_dir_replace(
1515b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd,
1516b1991ee3SDarrick J. Wong 	struct xfs_inode	*dp,
1517b1991ee3SDarrick J. Wong 	const struct xfs_name	*name,
1518b1991ee3SDarrick J. Wong 	xfs_ino_t		inum,
1519b1991ee3SDarrick J. Wong 	xfs_extlen_t		total)
1520b1991ee3SDarrick J. Wong {
1521b1991ee3SDarrick J. Wong 	struct xfs_scrub	*sc = rd->sc;
1522b1991ee3SDarrick J. Wong 	int			error;
1523b1991ee3SDarrick J. Wong 
1524b1991ee3SDarrick J. Wong 	ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
1525b1991ee3SDarrick J. Wong 
1526b1991ee3SDarrick J. Wong 	error = xfs_dir_ino_validate(sc->mp, inum);
1527b1991ee3SDarrick J. Wong 	if (error)
1528b1991ee3SDarrick J. Wong 		return error;
1529b1991ee3SDarrick J. Wong 
1530b1991ee3SDarrick J. Wong 	xrep_dir_init_args(rd, dp, name);
1531b1991ee3SDarrick J. Wong 	rd->args.inumber = inum;
1532b1991ee3SDarrick J. Wong 	rd->args.total = total;
1533dfe5febeSChristoph Hellwig 	return xfs_dir_replace_args(&rd->args);
1534b1991ee3SDarrick J. Wong }
1535b1991ee3SDarrick J. Wong 
1536b1991ee3SDarrick J. Wong /*
1537b1991ee3SDarrick J. Wong  * Reset the link count of this directory and adjust the unlinked list pointers
1538b1991ee3SDarrick J. Wong  * as needed.
1539b1991ee3SDarrick J. Wong  */
1540b1991ee3SDarrick J. Wong STATIC int
1541b1991ee3SDarrick J. Wong xrep_dir_set_nlink(
1542b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd)
1543b1991ee3SDarrick J. Wong {
1544b1991ee3SDarrick J. Wong 	struct xfs_scrub	*sc = rd->sc;
1545b1991ee3SDarrick J. Wong 	struct xfs_inode	*dp = sc->ip;
1546b1991ee3SDarrick J. Wong 	struct xfs_perag	*pag;
15475f204051SDarrick J. Wong 	unsigned int		new_nlink = min_t(unsigned long long,
15485f204051SDarrick J. Wong 						  rd->subdirs + 2,
15495f204051SDarrick J. Wong 						  XFS_NLINK_PINNED);
1550b1991ee3SDarrick J. Wong 	int			error;
1551b1991ee3SDarrick J. Wong 
1552b1991ee3SDarrick J. Wong 	/*
1553b1991ee3SDarrick J. Wong 	 * The directory is not on the incore unlinked list, which means that
1554b1991ee3SDarrick J. Wong 	 * it needs to be reachable via the directory tree.  Update the nlink
15551e58a8ccSDarrick J. Wong 	 * with our observed link count.  If the directory has no parent, it
15561e58a8ccSDarrick J. Wong 	 * will be moved to the orphanage.
1557b1991ee3SDarrick J. Wong 	 */
1558b1991ee3SDarrick J. Wong 	if (!xfs_inode_on_unlinked_list(dp))
1559b1991ee3SDarrick J. Wong 		goto reset_nlink;
1560b1991ee3SDarrick J. Wong 
1561b1991ee3SDarrick J. Wong 	/*
1562b1991ee3SDarrick J. Wong 	 * The directory is on the unlinked list and we did not find any
1563b1991ee3SDarrick J. Wong 	 * dirents.  Set the link count to zero and let the directory
1564b1991ee3SDarrick J. Wong 	 * inactivate when the last reference drops.
1565b1991ee3SDarrick J. Wong 	 */
1566b1991ee3SDarrick J. Wong 	if (rd->dirents == 0) {
15671e58a8ccSDarrick J. Wong 		rd->needs_adoption = false;
1568b1991ee3SDarrick J. Wong 		new_nlink = 0;
1569b1991ee3SDarrick J. Wong 		goto reset_nlink;
1570b1991ee3SDarrick J. Wong 	}
1571b1991ee3SDarrick J. Wong 
1572b1991ee3SDarrick J. Wong 	/*
1573b1991ee3SDarrick J. Wong 	 * The directory is on the unlinked list and we found dirents.  This
1574b1991ee3SDarrick J. Wong 	 * directory needs to be reachable via the directory tree.  Remove the
1575b1991ee3SDarrick J. Wong 	 * dir from the unlinked list and update nlink with the observed link
15761e58a8ccSDarrick J. Wong 	 * count.  If the directory has no parent, it will be moved to the
15771e58a8ccSDarrick J. Wong 	 * orphanage.
1578b1991ee3SDarrick J. Wong 	 */
1579b1991ee3SDarrick J. Wong 	pag = xfs_perag_get(sc->mp, XFS_INO_TO_AGNO(sc->mp, dp->i_ino));
1580b1991ee3SDarrick J. Wong 	if (!pag) {
1581b1991ee3SDarrick J. Wong 		ASSERT(0);
1582b1991ee3SDarrick J. Wong 		return -EFSCORRUPTED;
1583b1991ee3SDarrick J. Wong 	}
1584b1991ee3SDarrick J. Wong 
1585b1991ee3SDarrick J. Wong 	error = xfs_iunlink_remove(sc->tp, pag, dp);
1586b1991ee3SDarrick J. Wong 	xfs_perag_put(pag);
1587b1991ee3SDarrick J. Wong 	if (error)
1588b1991ee3SDarrick J. Wong 		return error;
1589b1991ee3SDarrick J. Wong 
1590b1991ee3SDarrick J. Wong reset_nlink:
1591b1991ee3SDarrick J. Wong 	if (VFS_I(dp)->i_nlink != new_nlink)
1592b1991ee3SDarrick J. Wong 		set_nlink(VFS_I(dp), new_nlink);
1593b1991ee3SDarrick J. Wong 	return 0;
1594b1991ee3SDarrick J. Wong }
1595b1991ee3SDarrick J. Wong 
159676fc23b6SDarrick J. Wong /*
159776fc23b6SDarrick J. Wong  * Finish replaying stashed dirent updates, allocate a transaction for
159876fc23b6SDarrick J. Wong  * exchanging data fork mappings, and take the ILOCKs of both directories
159976fc23b6SDarrick J. Wong  * before we commit the new directory structure.
160076fc23b6SDarrick J. Wong  */
160176fc23b6SDarrick J. Wong STATIC int
160276fc23b6SDarrick J. Wong xrep_dir_finalize_tempdir(
160376fc23b6SDarrick J. Wong 	struct xrep_dir		*rd)
160476fc23b6SDarrick J. Wong {
160576fc23b6SDarrick J. Wong 	struct xfs_scrub	*sc = rd->sc;
160676fc23b6SDarrick J. Wong 	int			error;
160776fc23b6SDarrick J. Wong 
160876fc23b6SDarrick J. Wong 	if (!xfs_has_parent(sc->mp))
160976fc23b6SDarrick J. Wong 		return xrep_tempexch_trans_alloc(sc, XFS_DATA_FORK, &rd->tx);
161076fc23b6SDarrick J. Wong 
161176fc23b6SDarrick J. Wong 	/*
161276fc23b6SDarrick J. Wong 	 * Repair relies on the ILOCK to quiesce all possible dirent updates.
161376fc23b6SDarrick J. Wong 	 * Replay all queued dirent updates into the tempdir before exchanging
161476fc23b6SDarrick J. Wong 	 * the contents, even if that means dropping the ILOCKs and the
161576fc23b6SDarrick J. Wong 	 * transaction.
161676fc23b6SDarrick J. Wong 	 */
161776fc23b6SDarrick J. Wong 	do {
161876fc23b6SDarrick J. Wong 		error = xrep_dir_replay_updates(rd);
161976fc23b6SDarrick J. Wong 		if (error)
162076fc23b6SDarrick J. Wong 			return error;
162176fc23b6SDarrick J. Wong 
162276fc23b6SDarrick J. Wong 		error = xrep_tempexch_trans_alloc(sc, XFS_DATA_FORK, &rd->tx);
162376fc23b6SDarrick J. Wong 		if (error)
162476fc23b6SDarrick J. Wong 			return error;
162576fc23b6SDarrick J. Wong 
162676fc23b6SDarrick J. Wong 		if (xfarray_length(rd->dir_entries) == 0)
162776fc23b6SDarrick J. Wong 			break;
162876fc23b6SDarrick J. Wong 
162976fc23b6SDarrick J. Wong 		xchk_trans_cancel(sc);
163076fc23b6SDarrick J. Wong 		xrep_tempfile_iunlock_both(sc);
163176fc23b6SDarrick J. Wong 	} while (!xchk_should_terminate(sc, &error));
163276fc23b6SDarrick J. Wong 	return error;
163376fc23b6SDarrick J. Wong }
163476fc23b6SDarrick J. Wong 
1635b1991ee3SDarrick J. Wong /* Exchange the temporary directory's data fork with the one being repaired. */
1636b1991ee3SDarrick J. Wong STATIC int
1637b1991ee3SDarrick J. Wong xrep_dir_swap(
1638b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd)
1639b1991ee3SDarrick J. Wong {
1640b1991ee3SDarrick J. Wong 	struct xfs_scrub	*sc = rd->sc;
1641*87b7c205SDarrick J. Wong 	xfs_ino_t		ino;
1642b1991ee3SDarrick J. Wong 	bool			ip_local, temp_local;
1643b1991ee3SDarrick J. Wong 	int			error = 0;
1644b1991ee3SDarrick J. Wong 
1645b1991ee3SDarrick J. Wong 	/*
16461e58a8ccSDarrick J. Wong 	 * If we never found the parent for this directory, temporarily assign
16471e58a8ccSDarrick J. Wong 	 * the root dir as the parent; we'll move this to the orphanage after
16481e58a8ccSDarrick J. Wong 	 * exchanging the dir contents.  We hold the ILOCK of the dir being
16491e58a8ccSDarrick J. Wong 	 * repaired, so we're not worried about racy updates of dotdot.
1650a07b4557SDarrick J. Wong 	 */
1651a07b4557SDarrick J. Wong 	ASSERT(sc->ilock_flags & XFS_ILOCK_EXCL);
16521e58a8ccSDarrick J. Wong 	if (rd->pscan.parent_ino == NULLFSINO) {
16531e58a8ccSDarrick J. Wong 		rd->needs_adoption = true;
16541e58a8ccSDarrick J. Wong 		rd->pscan.parent_ino = rd->sc->mp->m_sb.sb_rootino;
16551e58a8ccSDarrick J. Wong 	}
1656a07b4557SDarrick J. Wong 
1657a07b4557SDarrick J. Wong 	/*
1658b1991ee3SDarrick J. Wong 	 * Reset the temporary directory's '..' entry to point to the parent
1659*87b7c205SDarrick J. Wong 	 * that we found.  The dirent replace code asserts if the dirent
1660*87b7c205SDarrick J. Wong 	 * already points at the new inumber, so we look it up here.
1661b1991ee3SDarrick J. Wong 	 *
1662b1991ee3SDarrick J. Wong 	 * It's also possible that this replacement could also expand a sf
1663b1991ee3SDarrick J. Wong 	 * tempdir into block format.
1664b1991ee3SDarrick J. Wong 	 */
1665*87b7c205SDarrick J. Wong 	error = xchk_dir_lookup(sc, rd->sc->tempip, &xfs_name_dotdot, &ino);
1666*87b7c205SDarrick J. Wong 	if (error)
1667*87b7c205SDarrick J. Wong 		return error;
1668*87b7c205SDarrick J. Wong 
1669*87b7c205SDarrick J. Wong 	if (rd->pscan.parent_ino != ino) {
1670b1991ee3SDarrick J. Wong 		error = xrep_dir_replace(rd, rd->sc->tempip, &xfs_name_dotdot,
1671a07b4557SDarrick J. Wong 				rd->pscan.parent_ino, rd->tx.req.resblks);
1672b1991ee3SDarrick J. Wong 		if (error)
1673b1991ee3SDarrick J. Wong 			return error;
1674b1991ee3SDarrick J. Wong 	}
1675b1991ee3SDarrick J. Wong 
1676b1991ee3SDarrick J. Wong 	/*
1677b1991ee3SDarrick J. Wong 	 * Changing the dot and dotdot entries could have changed the shape of
1678b1991ee3SDarrick J. Wong 	 * the directory, so we recompute these.
1679b1991ee3SDarrick J. Wong 	 */
1680b1991ee3SDarrick J. Wong 	ip_local = sc->ip->i_df.if_format == XFS_DINODE_FMT_LOCAL;
1681b1991ee3SDarrick J. Wong 	temp_local = sc->tempip->i_df.if_format == XFS_DINODE_FMT_LOCAL;
1682b1991ee3SDarrick J. Wong 
1683b1991ee3SDarrick J. Wong 	/*
1684b1991ee3SDarrick J. Wong 	 * If the both files have a local format data fork and the rebuilt
1685b1991ee3SDarrick J. Wong 	 * directory data would fit in the repaired file's data fork, copy
1686b1991ee3SDarrick J. Wong 	 * the contents from the tempfile and update the directory link count.
1687b1991ee3SDarrick J. Wong 	 * We're done now.
1688b1991ee3SDarrick J. Wong 	 */
1689b1991ee3SDarrick J. Wong 	if (ip_local && temp_local &&
1690b1991ee3SDarrick J. Wong 	    sc->tempip->i_disk_size <= xfs_inode_data_fork_size(sc->ip)) {
1691b1991ee3SDarrick J. Wong 		xrep_tempfile_copyout_local(sc, XFS_DATA_FORK);
1692b1991ee3SDarrick J. Wong 		return xrep_dir_set_nlink(rd);
1693b1991ee3SDarrick J. Wong 	}
1694b1991ee3SDarrick J. Wong 
1695b1991ee3SDarrick J. Wong 	/*
1696b1991ee3SDarrick J. Wong 	 * Clean the transaction before we start working on exchanging
1697b1991ee3SDarrick J. Wong 	 * directory contents.
1698b1991ee3SDarrick J. Wong 	 */
1699b1991ee3SDarrick J. Wong 	error = xrep_tempfile_roll_trans(rd->sc);
1700b1991ee3SDarrick J. Wong 	if (error)
1701b1991ee3SDarrick J. Wong 		return error;
1702b1991ee3SDarrick J. Wong 
1703b1991ee3SDarrick J. Wong 	/* Otherwise, make sure both data forks are in block-mapping mode. */
1704b1991ee3SDarrick J. Wong 	error = xrep_dir_swap_prep(sc, temp_local, ip_local);
1705b1991ee3SDarrick J. Wong 	if (error)
1706b1991ee3SDarrick J. Wong 		return error;
1707b1991ee3SDarrick J. Wong 
1708b1991ee3SDarrick J. Wong 	/*
1709b1991ee3SDarrick J. Wong 	 * Set nlink of the directory in the same transaction sequence that
1710b1991ee3SDarrick J. Wong 	 * (atomically) commits the new directory data.
1711b1991ee3SDarrick J. Wong 	 */
1712b1991ee3SDarrick J. Wong 	error = xrep_dir_set_nlink(rd);
1713b1991ee3SDarrick J. Wong 	if (error)
1714b1991ee3SDarrick J. Wong 		return error;
1715b1991ee3SDarrick J. Wong 
1716b1991ee3SDarrick J. Wong 	return xrep_tempexch_contents(sc, &rd->tx);
1717b1991ee3SDarrick J. Wong }
1718b1991ee3SDarrick J. Wong 
1719b1991ee3SDarrick J. Wong /*
1720b1991ee3SDarrick J. Wong  * Exchange the new directory contents (which we created in the tempfile) with
1721b1991ee3SDarrick J. Wong  * the directory being repaired.
1722b1991ee3SDarrick J. Wong  */
1723b1991ee3SDarrick J. Wong STATIC int
1724b1991ee3SDarrick J. Wong xrep_dir_rebuild_tree(
1725b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd)
1726b1991ee3SDarrick J. Wong {
1727b1991ee3SDarrick J. Wong 	struct xfs_scrub	*sc = rd->sc;
1728b1991ee3SDarrick J. Wong 	int			error;
1729b1991ee3SDarrick J. Wong 
1730a07b4557SDarrick J. Wong 	trace_xrep_dir_rebuild_tree(sc->ip, rd->pscan.parent_ino);
1731b1991ee3SDarrick J. Wong 
1732b1991ee3SDarrick J. Wong 	/*
1733b1991ee3SDarrick J. Wong 	 * Take the IOLOCK on the temporary file so that we can run dir
1734b1991ee3SDarrick J. Wong 	 * operations with the same locks held as we would for a normal file.
1735b1991ee3SDarrick J. Wong 	 * We still hold sc->ip's IOLOCK.
1736b1991ee3SDarrick J. Wong 	 */
1737b1991ee3SDarrick J. Wong 	error = xrep_tempfile_iolock_polled(rd->sc);
1738b1991ee3SDarrick J. Wong 	if (error)
1739b1991ee3SDarrick J. Wong 		return error;
1740b1991ee3SDarrick J. Wong 
174176fc23b6SDarrick J. Wong 	/*
174276fc23b6SDarrick J. Wong 	 * Allocate transaction, lock inodes, and make sure that we've replayed
174376fc23b6SDarrick J. Wong 	 * all the stashed dirent updates to the tempdir.  After this point,
174476fc23b6SDarrick J. Wong 	 * we're ready to exchange data fork mappings.
174576fc23b6SDarrick J. Wong 	 */
174676fc23b6SDarrick J. Wong 	error = xrep_dir_finalize_tempdir(rd);
1747b1991ee3SDarrick J. Wong 	if (error)
1748b1991ee3SDarrick J. Wong 		return error;
1749b1991ee3SDarrick J. Wong 
17508559b21aSDarrick J. Wong 	if (xchk_iscan_aborted(&rd->pscan.iscan))
17518559b21aSDarrick J. Wong 		return -ECANCELED;
17528559b21aSDarrick J. Wong 
1753b1991ee3SDarrick J. Wong 	/*
1754b1991ee3SDarrick J. Wong 	 * Exchange the tempdir's data fork with the file being repaired.  This
1755b1991ee3SDarrick J. Wong 	 * recreates the transaction and re-takes the ILOCK in the scrub
1756b1991ee3SDarrick J. Wong 	 * context.
1757b1991ee3SDarrick J. Wong 	 */
1758b1991ee3SDarrick J. Wong 	error = xrep_dir_swap(rd);
1759b1991ee3SDarrick J. Wong 	if (error)
1760b1991ee3SDarrick J. Wong 		return error;
1761b1991ee3SDarrick J. Wong 
1762b1991ee3SDarrick J. Wong 	/*
1763b1991ee3SDarrick J. Wong 	 * Release the old directory blocks and reset the data fork of the temp
1764b1991ee3SDarrick J. Wong 	 * directory to an empty shortform directory because inactivation does
1765b1991ee3SDarrick J. Wong 	 * nothing for directories.
1766b1991ee3SDarrick J. Wong 	 */
1767b1991ee3SDarrick J. Wong 	error = xrep_dir_reset_fork(rd, sc->mp->m_rootip->i_ino);
1768b1991ee3SDarrick J. Wong 	if (error)
1769b1991ee3SDarrick J. Wong 		return error;
1770b1991ee3SDarrick J. Wong 
1771b1991ee3SDarrick J. Wong 	/*
1772b1991ee3SDarrick J. Wong 	 * Roll to get a transaction without any inodes joined to it.  Then we
1773b1991ee3SDarrick J. Wong 	 * can drop the tempfile's ILOCK and IOLOCK before doing more work on
1774b1991ee3SDarrick J. Wong 	 * the scrub target directory.
1775b1991ee3SDarrick J. Wong 	 */
1776b1991ee3SDarrick J. Wong 	error = xfs_trans_roll(&sc->tp);
1777b1991ee3SDarrick J. Wong 	if (error)
1778b1991ee3SDarrick J. Wong 		return error;
1779b1991ee3SDarrick J. Wong 
1780b1991ee3SDarrick J. Wong 	xrep_tempfile_iunlock(sc);
1781b1991ee3SDarrick J. Wong 	xrep_tempfile_iounlock(sc);
1782b1991ee3SDarrick J. Wong 	return 0;
1783b1991ee3SDarrick J. Wong }
1784b1991ee3SDarrick J. Wong 
1785b1991ee3SDarrick J. Wong /* Set up the filesystem scan so we can regenerate directory entries. */
1786b1991ee3SDarrick J. Wong STATIC int
1787b1991ee3SDarrick J. Wong xrep_dir_setup_scan(
1788b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd)
1789b1991ee3SDarrick J. Wong {
1790b1991ee3SDarrick J. Wong 	struct xfs_scrub	*sc = rd->sc;
1791b1991ee3SDarrick J. Wong 	char			*descr;
1792b1991ee3SDarrick J. Wong 	int			error;
1793b1991ee3SDarrick J. Wong 
1794b1991ee3SDarrick J. Wong 	/* Set up some staging memory for salvaging dirents. */
1795b1991ee3SDarrick J. Wong 	descr = xchk_xfile_ino_descr(sc, "directory entries");
1796b1991ee3SDarrick J. Wong 	error = xfarray_create(descr, 0, sizeof(struct xrep_dirent),
1797b1991ee3SDarrick J. Wong 			&rd->dir_entries);
1798b1991ee3SDarrick J. Wong 	kfree(descr);
1799b1991ee3SDarrick J. Wong 	if (error)
1800b1991ee3SDarrick J. Wong 		return error;
1801b1991ee3SDarrick J. Wong 
1802b1991ee3SDarrick J. Wong 	descr = xchk_xfile_ino_descr(sc, "directory entry names");
1803b1991ee3SDarrick J. Wong 	error = xfblob_create(descr, &rd->dir_names);
1804b1991ee3SDarrick J. Wong 	kfree(descr);
1805b1991ee3SDarrick J. Wong 	if (error)
1806b1991ee3SDarrick J. Wong 		goto out_xfarray;
1807b1991ee3SDarrick J. Wong 
18088559b21aSDarrick J. Wong 	if (xfs_has_parent(sc->mp))
18098559b21aSDarrick J. Wong 		error = __xrep_findparent_scan_start(sc, &rd->pscan,
18108559b21aSDarrick J. Wong 				xrep_dir_live_update);
18118559b21aSDarrick J. Wong 	else
1812a07b4557SDarrick J. Wong 		error = xrep_findparent_scan_start(sc, &rd->pscan);
1813a07b4557SDarrick J. Wong 	if (error)
1814a07b4557SDarrick J. Wong 		goto out_xfblob;
1815a07b4557SDarrick J. Wong 
1816b1991ee3SDarrick J. Wong 	return 0;
1817b1991ee3SDarrick J. Wong 
1818a07b4557SDarrick J. Wong out_xfblob:
1819a07b4557SDarrick J. Wong 	xfblob_destroy(rd->dir_names);
1820a07b4557SDarrick J. Wong 	rd->dir_names = NULL;
1821b1991ee3SDarrick J. Wong out_xfarray:
1822b1991ee3SDarrick J. Wong 	xfarray_destroy(rd->dir_entries);
1823b1991ee3SDarrick J. Wong 	rd->dir_entries = NULL;
1824b1991ee3SDarrick J. Wong 	return error;
1825b1991ee3SDarrick J. Wong }
1826b1991ee3SDarrick J. Wong 
1827b1991ee3SDarrick J. Wong /*
18281e58a8ccSDarrick J. Wong  * Move the current file to the orphanage.
18291e58a8ccSDarrick J. Wong  *
18301e58a8ccSDarrick J. Wong  * Caller must hold IOLOCK_EXCL on @sc->ip, and no other inode locks.  Upon
18311e58a8ccSDarrick J. Wong  * successful return, the scrub transaction will have enough extra reservation
18321e58a8ccSDarrick J. Wong  * to make the move; it will hold IOLOCK_EXCL and ILOCK_EXCL of @sc->ip and the
18331e58a8ccSDarrick J. Wong  * orphanage; and both inodes will be ijoined.
18341e58a8ccSDarrick J. Wong  */
18351e58a8ccSDarrick J. Wong STATIC int
18361e58a8ccSDarrick J. Wong xrep_dir_move_to_orphanage(
18371e58a8ccSDarrick J. Wong 	struct xrep_dir		*rd)
18381e58a8ccSDarrick J. Wong {
18391e58a8ccSDarrick J. Wong 	struct xfs_scrub	*sc = rd->sc;
18401e58a8ccSDarrick J. Wong 	xfs_ino_t		orig_parent, new_parent;
18411e58a8ccSDarrick J. Wong 	int			error;
18421e58a8ccSDarrick J. Wong 
18431e58a8ccSDarrick J. Wong 	/*
18441e58a8ccSDarrick J. Wong 	 * We are about to drop the ILOCK on sc->ip to lock the orphanage and
18451e58a8ccSDarrick J. Wong 	 * prepare for the adoption.  Therefore, look up the old dotdot entry
18461e58a8ccSDarrick J. Wong 	 * for sc->ip so that we can compare it after we re-lock sc->ip.
18471e58a8ccSDarrick J. Wong 	 */
18481e58a8ccSDarrick J. Wong 	error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &orig_parent);
18491e58a8ccSDarrick J. Wong 	if (error)
18501e58a8ccSDarrick J. Wong 		return error;
18511e58a8ccSDarrick J. Wong 
18521e58a8ccSDarrick J. Wong 	/*
18531e58a8ccSDarrick J. Wong 	 * Drop the ILOCK on the scrub target and commit the transaction.
18541e58a8ccSDarrick J. Wong 	 * Adoption computes its own resource requirements and gathers the
18551e58a8ccSDarrick J. Wong 	 * necessary components.
18561e58a8ccSDarrick J. Wong 	 */
18571e58a8ccSDarrick J. Wong 	error = xrep_trans_commit(sc);
18581e58a8ccSDarrick J. Wong 	if (error)
18591e58a8ccSDarrick J. Wong 		return error;
18601e58a8ccSDarrick J. Wong 	xchk_iunlock(sc, XFS_ILOCK_EXCL);
18611e58a8ccSDarrick J. Wong 
18621e58a8ccSDarrick J. Wong 	/* If we can take the orphanage's iolock then we're ready to move. */
18631e58a8ccSDarrick J. Wong 	if (!xrep_orphanage_ilock_nowait(sc, XFS_IOLOCK_EXCL)) {
18641e58a8ccSDarrick J. Wong 		xchk_iunlock(sc, sc->ilock_flags);
18651e58a8ccSDarrick J. Wong 		error = xrep_orphanage_iolock_two(sc);
18661e58a8ccSDarrick J. Wong 		if (error)
18671e58a8ccSDarrick J. Wong 			return error;
18681e58a8ccSDarrick J. Wong 	}
18691e58a8ccSDarrick J. Wong 
18701e58a8ccSDarrick J. Wong 	/* Grab transaction and ILOCK the two files. */
18711e58a8ccSDarrick J. Wong 	error = xrep_adoption_trans_alloc(sc, &rd->adoption);
18721e58a8ccSDarrick J. Wong 	if (error)
18731e58a8ccSDarrick J. Wong 		return error;
18741e58a8ccSDarrick J. Wong 
18751e58a8ccSDarrick J. Wong 	error = xrep_adoption_compute_name(&rd->adoption, &rd->xname);
18761e58a8ccSDarrick J. Wong 	if (error)
18771e58a8ccSDarrick J. Wong 		return error;
18781e58a8ccSDarrick J. Wong 
18791e58a8ccSDarrick J. Wong 	/*
18801e58a8ccSDarrick J. Wong 	 * Now that we've reacquired the ILOCK on sc->ip, look up the dotdot
18811e58a8ccSDarrick J. Wong 	 * entry again.  If the parent changed or the child was unlinked while
18821e58a8ccSDarrick J. Wong 	 * the child directory was unlocked, we don't need to move the child to
18831e58a8ccSDarrick J. Wong 	 * the orphanage after all.
18841e58a8ccSDarrick J. Wong 	 */
18851e58a8ccSDarrick J. Wong 	error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &new_parent);
18861e58a8ccSDarrick J. Wong 	if (error)
18871e58a8ccSDarrick J. Wong 		return error;
18881e58a8ccSDarrick J. Wong 
18891e58a8ccSDarrick J. Wong 	/*
18901e58a8ccSDarrick J. Wong 	 * Attach to the orphanage if we still have a linked directory and it
18911e58a8ccSDarrick J. Wong 	 * hasn't been moved.
18921e58a8ccSDarrick J. Wong 	 */
18931e58a8ccSDarrick J. Wong 	if (orig_parent == new_parent && VFS_I(sc->ip)->i_nlink > 0) {
18941e58a8ccSDarrick J. Wong 		error = xrep_adoption_move(&rd->adoption);
18951e58a8ccSDarrick J. Wong 		if (error)
18961e58a8ccSDarrick J. Wong 			return error;
18971e58a8ccSDarrick J. Wong 	}
18981e58a8ccSDarrick J. Wong 
18991e58a8ccSDarrick J. Wong 	/*
19001e58a8ccSDarrick J. Wong 	 * Launder the scrub transaction so we can drop the orphanage ILOCK
19011e58a8ccSDarrick J. Wong 	 * and IOLOCK.  Return holding the scrub target's ILOCK and IOLOCK.
19021e58a8ccSDarrick J. Wong 	 */
19031e58a8ccSDarrick J. Wong 	error = xrep_adoption_trans_roll(&rd->adoption);
19041e58a8ccSDarrick J. Wong 	if (error)
19051e58a8ccSDarrick J. Wong 		return error;
19061e58a8ccSDarrick J. Wong 
19071e58a8ccSDarrick J. Wong 	xrep_orphanage_iunlock(sc, XFS_ILOCK_EXCL);
19081e58a8ccSDarrick J. Wong 	xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);
19091e58a8ccSDarrick J. Wong 	return 0;
19101e58a8ccSDarrick J. Wong }
19111e58a8ccSDarrick J. Wong 
19121e58a8ccSDarrick J. Wong /*
1913b1991ee3SDarrick J. Wong  * Repair the directory metadata.
1914b1991ee3SDarrick J. Wong  *
1915b1991ee3SDarrick J. Wong  * XXX: Directory entry buffers can be multiple fsblocks in size.  The buffer
1916b1991ee3SDarrick J. Wong  * cache in XFS can't handle aliased multiblock buffers, so this might
1917b1991ee3SDarrick J. Wong  * misbehave if the directory blocks are crosslinked with other filesystem
1918b1991ee3SDarrick J. Wong  * metadata.
1919b1991ee3SDarrick J. Wong  *
1920b1991ee3SDarrick J. Wong  * XXX: Is it necessary to check the dcache for this directory to make sure
1921b1991ee3SDarrick J. Wong  * that we always recreate every cached entry?
1922b1991ee3SDarrick J. Wong  */
1923b1991ee3SDarrick J. Wong int
1924b1991ee3SDarrick J. Wong xrep_directory(
1925b1991ee3SDarrick J. Wong 	struct xfs_scrub	*sc)
1926b1991ee3SDarrick J. Wong {
1927b1991ee3SDarrick J. Wong 	struct xrep_dir		*rd = sc->buf;
1928b1991ee3SDarrick J. Wong 	int			error;
1929b1991ee3SDarrick J. Wong 
1930b1991ee3SDarrick J. Wong 	/* The rmapbt is required to reap the old data fork. */
1931b1991ee3SDarrick J. Wong 	if (!xfs_has_rmapbt(sc->mp))
1932b1991ee3SDarrick J. Wong 		return -EOPNOTSUPP;
19336d335233SDarrick J. Wong 	/* We require atomic file exchange range to rebuild anything. */
19346d335233SDarrick J. Wong 	if (!xfs_has_exchange_range(sc->mp))
19356d335233SDarrick J. Wong 		return -EOPNOTSUPP;
1936b1991ee3SDarrick J. Wong 
1937b1991ee3SDarrick J. Wong 	error = xrep_dir_setup_scan(rd);
1938b1991ee3SDarrick J. Wong 	if (error)
1939b1991ee3SDarrick J. Wong 		return error;
1940b1991ee3SDarrick J. Wong 
194176fc23b6SDarrick J. Wong 	if (xfs_has_parent(sc->mp))
194276fc23b6SDarrick J. Wong 		error = xrep_dir_scan_dirtree(rd);
194376fc23b6SDarrick J. Wong 	else
1944b1991ee3SDarrick J. Wong 		error = xrep_dir_salvage_entries(rd);
1945b1991ee3SDarrick J. Wong 	if (error)
1946b1991ee3SDarrick J. Wong 		goto out_teardown;
1947b1991ee3SDarrick J. Wong 
1948b1991ee3SDarrick J. Wong 	/* Last chance to abort before we start committing fixes. */
1949b1991ee3SDarrick J. Wong 	if (xchk_should_terminate(sc, &error))
1950b1991ee3SDarrick J. Wong 		goto out_teardown;
1951b1991ee3SDarrick J. Wong 
1952b1991ee3SDarrick J. Wong 	error = xrep_dir_rebuild_tree(rd);
1953b1991ee3SDarrick J. Wong 	if (error)
1954b1991ee3SDarrick J. Wong 		goto out_teardown;
1955b1991ee3SDarrick J. Wong 
19561e58a8ccSDarrick J. Wong 	if (rd->needs_adoption) {
19571e58a8ccSDarrick J. Wong 		if (!xrep_orphanage_can_adopt(rd->sc))
19581e58a8ccSDarrick J. Wong 			error = -EFSCORRUPTED;
19591e58a8ccSDarrick J. Wong 		else
19601e58a8ccSDarrick J. Wong 			error = xrep_dir_move_to_orphanage(rd);
19611e58a8ccSDarrick J. Wong 		if (error)
19621e58a8ccSDarrick J. Wong 			goto out_teardown;
19631e58a8ccSDarrick J. Wong 	}
19641e58a8ccSDarrick J. Wong 
1965b1991ee3SDarrick J. Wong out_teardown:
1966b1991ee3SDarrick J. Wong 	xrep_dir_teardown(sc);
1967b1991ee3SDarrick J. Wong 	return error;
1968b1991ee3SDarrick J. Wong }
1969