xref: /linux/fs/xfs/scrub/metapath.c (revision 9b7280010366dbe32791acd498a37dc522f568db)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2023-2024 Oracle.  All Rights Reserved.
4  * Author: Darrick J. Wong <djwong@kernel.org>
5  */
6 #include "xfs.h"
7 #include "xfs_fs.h"
8 #include "xfs_shared.h"
9 #include "xfs_format.h"
10 #include "xfs_trans_resv.h"
11 #include "xfs_mount.h"
12 #include "xfs_log_format.h"
13 #include "xfs_trans.h"
14 #include "xfs_inode.h"
15 #include "xfs_metafile.h"
16 #include "xfs_quota.h"
17 #include "xfs_qm.h"
18 #include "xfs_dir2.h"
19 #include "xfs_parent.h"
20 #include "xfs_bmap_btree.h"
21 #include "xfs_trans_space.h"
22 #include "xfs_attr.h"
23 #include "xfs_rtgroup.h"
24 #include "scrub/scrub.h"
25 #include "scrub/common.h"
26 #include "scrub/trace.h"
27 #include "scrub/readdir.h"
28 #include "scrub/repair.h"
29 
30 /*
31  * Metadata Directory Tree Paths
32  * =============================
33  *
34  * A filesystem with metadir enabled expects to find metadata structures
35  * attached to files that are accessible by walking a path down the metadata
36  * directory tree.  Given the metadir path and the incore inode storing the
37  * metadata, this scrubber ensures that the ondisk metadir path points to the
38  * ondisk inode represented by the incore inode.
39  */
40 
41 struct xchk_metapath {
42 	struct xfs_scrub		*sc;
43 
44 	/* Name for lookup */
45 	struct xfs_name			xname;
46 
47 	/* Directory update for repairs */
48 	struct xfs_dir_update		du;
49 
50 	/* Path down to this metadata file from the parent directory */
51 	const char			*path;
52 
53 	/* Directory parent of the metadata file. */
54 	struct xfs_inode		*dp;
55 
56 	/* Locks held on dp */
57 	unsigned int			dp_ilock_flags;
58 
59 	/* Transaction block reservations */
60 	unsigned int			link_resblks;
61 	unsigned int			unlink_resblks;
62 
63 	/* Parent pointer updates */
64 	struct xfs_parent_args		link_ppargs;
65 	struct xfs_parent_args		unlink_ppargs;
66 
67 	/* Scratchpads for removing links */
68 	struct xfs_da_args		pptr_args;
69 };
70 
71 /* Release resources tracked in the buffer. */
72 static inline void
73 xchk_metapath_cleanup(
74 	void			*buf)
75 {
76 	struct xchk_metapath	*mpath = buf;
77 
78 	if (mpath->dp_ilock_flags)
79 		xfs_iunlock(mpath->dp, mpath->dp_ilock_flags);
80 	kfree(mpath->path);
81 }
82 
83 /* Set up a metadir path scan.  @path must be dynamically allocated. */
84 static inline int
85 xchk_setup_metapath_scan(
86 	struct xfs_scrub	*sc,
87 	struct xfs_inode	*dp,
88 	const char		*path,
89 	struct xfs_inode	*ip)
90 {
91 	struct xchk_metapath	*mpath;
92 	int			error;
93 
94 	if (!path)
95 		return -ENOMEM;
96 
97 	error = xchk_install_live_inode(sc, ip);
98 	if (error) {
99 		kfree(path);
100 		return error;
101 	}
102 
103 	mpath = kzalloc(sizeof(struct xchk_metapath), XCHK_GFP_FLAGS);
104 	if (!mpath) {
105 		kfree(path);
106 		return -ENOMEM;
107 	}
108 
109 	mpath->sc = sc;
110 	sc->buf = mpath;
111 	sc->buf_cleanup = xchk_metapath_cleanup;
112 
113 	mpath->dp = dp;
114 	mpath->path = path; /* path is now owned by mpath */
115 
116 	mpath->xname.name = mpath->path;
117 	mpath->xname.len = strlen(mpath->path);
118 	mpath->xname.type = xfs_mode_to_ftype(VFS_I(ip)->i_mode);
119 
120 	return 0;
121 }
122 
123 #ifdef CONFIG_XFS_RT
124 /* Scan the /rtgroups directory itself. */
125 static int
126 xchk_setup_metapath_rtdir(
127 	struct xfs_scrub	*sc)
128 {
129 	if (!sc->mp->m_rtdirip)
130 		return -ENOENT;
131 
132 	return xchk_setup_metapath_scan(sc, sc->mp->m_metadirip,
133 			kasprintf(GFP_KERNEL, "rtgroups"), sc->mp->m_rtdirip);
134 }
135 
136 /* Scan a rtgroup inode under the /rtgroups directory. */
137 static int
138 xchk_setup_metapath_rtginode(
139 	struct xfs_scrub	*sc,
140 	enum xfs_rtg_inodes	type)
141 {
142 	struct xfs_rtgroup	*rtg;
143 	struct xfs_inode	*ip;
144 	int			error;
145 
146 	rtg = xfs_rtgroup_get(sc->mp, sc->sm->sm_agno);
147 	if (!rtg)
148 		return -ENOENT;
149 
150 	ip = rtg->rtg_inodes[type];
151 	if (!ip) {
152 		error = -ENOENT;
153 		goto out_put_rtg;
154 	}
155 
156 	error = xchk_setup_metapath_scan(sc, sc->mp->m_rtdirip,
157 			xfs_rtginode_path(rtg_rgno(rtg), type), ip);
158 
159 out_put_rtg:
160 	xfs_rtgroup_put(rtg);
161 	return error;
162 }
163 #else
164 # define xchk_setup_metapath_rtdir(...)		(-ENOENT)
165 # define xchk_setup_metapath_rtginode(...)	(-ENOENT)
166 #endif /* CONFIG_XFS_RT */
167 
168 #ifdef CONFIG_XFS_QUOTA
169 /* Scan the /quota directory itself. */
170 static int
171 xchk_setup_metapath_quotadir(
172 	struct xfs_scrub	*sc)
173 {
174 	struct xfs_trans	*tp;
175 	struct xfs_inode	*dp = NULL;
176 	int			error;
177 
178 	error = xfs_trans_alloc_empty(sc->mp, &tp);
179 	if (error)
180 		return error;
181 
182 	error = xfs_dqinode_load_parent(tp, &dp);
183 	xfs_trans_cancel(tp);
184 	if (error)
185 		return error;
186 
187 	error = xchk_setup_metapath_scan(sc, sc->mp->m_metadirip,
188 			kasprintf(GFP_KERNEL, "quota"), dp);
189 	xfs_irele(dp);
190 	return error;
191 }
192 
193 /* Scan a quota inode under the /quota directory. */
194 static int
195 xchk_setup_metapath_dqinode(
196 	struct xfs_scrub	*sc,
197 	xfs_dqtype_t		type)
198 {
199 	struct xfs_quotainfo	*qi = sc->mp->m_quotainfo;
200 	struct xfs_trans	*tp = NULL;
201 	struct xfs_inode	*dp = NULL;
202 	struct xfs_inode	*ip = NULL;
203 	int			error;
204 
205 	if (!qi)
206 		return -ENOENT;
207 
208 	switch (type) {
209 	case XFS_DQTYPE_USER:
210 		ip = qi->qi_uquotaip;
211 		break;
212 	case XFS_DQTYPE_GROUP:
213 		ip = qi->qi_gquotaip;
214 		break;
215 	case XFS_DQTYPE_PROJ:
216 		ip = qi->qi_pquotaip;
217 		break;
218 	default:
219 		ASSERT(0);
220 		return -EINVAL;
221 	}
222 	if (!ip)
223 		return -ENOENT;
224 
225 	error = xfs_trans_alloc_empty(sc->mp, &tp);
226 	if (error)
227 		return error;
228 
229 	error = xfs_dqinode_load_parent(tp, &dp);
230 	xfs_trans_cancel(tp);
231 	if (error)
232 		return error;
233 
234 	error = xchk_setup_metapath_scan(sc, dp,
235 			kstrdup(xfs_dqinode_path(type), GFP_KERNEL), ip);
236 
237 	xfs_irele(dp);
238 	return error;
239 }
240 #else
241 # define xchk_setup_metapath_quotadir(...)	(-ENOENT)
242 # define xchk_setup_metapath_dqinode(...)	(-ENOENT)
243 #endif /* CONFIG_XFS_QUOTA */
244 
245 int
246 xchk_setup_metapath(
247 	struct xfs_scrub	*sc)
248 {
249 	if (!xfs_has_metadir(sc->mp))
250 		return -ENOENT;
251 	if (sc->sm->sm_gen)
252 		return -EINVAL;
253 
254 	switch (sc->sm->sm_ino) {
255 	case XFS_SCRUB_METAPATH_PROBE:
256 		/* Just probing, nothing else to do. */
257 		if (sc->sm->sm_agno)
258 			return -EINVAL;
259 		return 0;
260 	case XFS_SCRUB_METAPATH_RTDIR:
261 		return xchk_setup_metapath_rtdir(sc);
262 	case XFS_SCRUB_METAPATH_RTBITMAP:
263 		return xchk_setup_metapath_rtginode(sc, XFS_RTGI_BITMAP);
264 	case XFS_SCRUB_METAPATH_RTSUMMARY:
265 		return xchk_setup_metapath_rtginode(sc, XFS_RTGI_SUMMARY);
266 	case XFS_SCRUB_METAPATH_QUOTADIR:
267 		return xchk_setup_metapath_quotadir(sc);
268 	case XFS_SCRUB_METAPATH_USRQUOTA:
269 		return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_USER);
270 	case XFS_SCRUB_METAPATH_GRPQUOTA:
271 		return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_GROUP);
272 	case XFS_SCRUB_METAPATH_PRJQUOTA:
273 		return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_PROJ);
274 	default:
275 		return -ENOENT;
276 	}
277 }
278 
279 /*
280  * Take the ILOCK on the metadata directory parent and child.  We do not know
281  * that the metadata directory is not corrupt, so we lock the parent and try
282  * to lock the child.  Returns 0 if successful, or -EINTR to abort the scrub.
283  */
284 STATIC int
285 xchk_metapath_ilock_both(
286 	struct xchk_metapath	*mpath)
287 {
288 	struct xfs_scrub	*sc = mpath->sc;
289 	int			error = 0;
290 
291 	while (true) {
292 		xfs_ilock(mpath->dp, XFS_ILOCK_EXCL);
293 		if (xchk_ilock_nowait(sc, XFS_ILOCK_EXCL)) {
294 			mpath->dp_ilock_flags |= XFS_ILOCK_EXCL;
295 			return 0;
296 		}
297 		xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
298 
299 		if (xchk_should_terminate(sc, &error))
300 			return error;
301 
302 		delay(1);
303 	}
304 
305 	ASSERT(0);
306 	return -EINTR;
307 }
308 
309 /* Unlock parent and child inodes. */
310 static inline void
311 xchk_metapath_iunlock(
312 	struct xchk_metapath	*mpath)
313 {
314 	struct xfs_scrub	*sc = mpath->sc;
315 
316 	xchk_iunlock(sc, XFS_ILOCK_EXCL);
317 
318 	mpath->dp_ilock_flags &= ~XFS_ILOCK_EXCL;
319 	xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
320 }
321 
322 int
323 xchk_metapath(
324 	struct xfs_scrub	*sc)
325 {
326 	struct xchk_metapath	*mpath = sc->buf;
327 	xfs_ino_t		ino = NULLFSINO;
328 	int			error;
329 
330 	/* Just probing, nothing else to do. */
331 	if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE)
332 		return 0;
333 
334 	/* Parent required to do anything else. */
335 	if (mpath->dp == NULL) {
336 		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
337 		return 0;
338 	}
339 
340 	error = xchk_trans_alloc_empty(sc);
341 	if (error)
342 		return error;
343 
344 	error = xchk_metapath_ilock_both(mpath);
345 	if (error)
346 		goto out_cancel;
347 
348 	/* Make sure the parent dir has a dirent pointing to this file. */
349 	error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
350 	trace_xchk_metapath_lookup(sc, mpath->path, mpath->dp, ino);
351 	if (error == -ENOENT) {
352 		/* No directory entry at all */
353 		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
354 		error = 0;
355 		goto out_ilock;
356 	}
357 	if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
358 		goto out_ilock;
359 	if (ino != sc->ip->i_ino) {
360 		/* Pointing to wrong inode */
361 		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
362 	}
363 
364 out_ilock:
365 	xchk_metapath_iunlock(mpath);
366 out_cancel:
367 	xchk_trans_cancel(sc);
368 	return error;
369 }
370 
371 #ifdef CONFIG_XFS_ONLINE_REPAIR
372 /* Create the dirent represented by the final component of the path. */
373 STATIC int
374 xrep_metapath_link(
375 	struct xchk_metapath	*mpath)
376 {
377 	struct xfs_scrub	*sc = mpath->sc;
378 
379 	mpath->du.dp = mpath->dp;
380 	mpath->du.name = &mpath->xname;
381 	mpath->du.ip = sc->ip;
382 
383 	if (xfs_has_parent(sc->mp))
384 		mpath->du.ppargs = &mpath->link_ppargs;
385 	else
386 		mpath->du.ppargs = NULL;
387 
388 	trace_xrep_metapath_link(sc, mpath->path, mpath->dp, sc->ip->i_ino);
389 
390 	return xfs_dir_add_child(sc->tp, mpath->link_resblks, &mpath->du);
391 }
392 
393 /* Remove the dirent at the final component of the path. */
394 STATIC int
395 xrep_metapath_unlink(
396 	struct xchk_metapath	*mpath,
397 	xfs_ino_t		ino,
398 	struct xfs_inode	*ip)
399 {
400 	struct xfs_parent_rec	rec;
401 	struct xfs_scrub	*sc = mpath->sc;
402 	struct xfs_mount	*mp = sc->mp;
403 	int			error;
404 
405 	trace_xrep_metapath_unlink(sc, mpath->path, mpath->dp, ino);
406 
407 	if (!ip) {
408 		/* The child inode isn't allocated.  Junk the dirent. */
409 		xfs_trans_log_inode(sc->tp, mpath->dp, XFS_ILOG_CORE);
410 		return xfs_dir_removename(sc->tp, mpath->dp, &mpath->xname,
411 				ino, mpath->unlink_resblks);
412 	}
413 
414 	mpath->du.dp = mpath->dp;
415 	mpath->du.name = &mpath->xname;
416 	mpath->du.ip = ip;
417 	mpath->du.ppargs = NULL;
418 
419 	/* Figure out if we're removing a parent pointer too. */
420 	if (xfs_has_parent(mp)) {
421 		xfs_inode_to_parent_rec(&rec, ip);
422 		error = xfs_parent_lookup(sc->tp, ip, &mpath->xname, &rec,
423 				&mpath->pptr_args);
424 		switch (error) {
425 		case -ENOATTR:
426 			break;
427 		case 0:
428 			mpath->du.ppargs = &mpath->unlink_ppargs;
429 			break;
430 		default:
431 			return error;
432 		}
433 	}
434 
435 	return xfs_dir_remove_child(sc->tp, mpath->unlink_resblks, &mpath->du);
436 }
437 
438 /*
439  * Try to create a dirent in @mpath->dp with the name @mpath->xname that points
440  * to @sc->ip.  Returns:
441  *
442  * -EEXIST and an @alleged_child if the dirent that points to the wrong inode;
443  * 0 if there is now a dirent pointing to @sc->ip; or
444  * A negative errno on error.
445  */
446 STATIC int
447 xrep_metapath_try_link(
448 	struct xchk_metapath	*mpath,
449 	xfs_ino_t		*alleged_child)
450 {
451 	struct xfs_scrub	*sc = mpath->sc;
452 	xfs_ino_t		ino;
453 	int			error;
454 
455 	/* Allocate transaction, lock inodes, join to transaction. */
456 	error = xchk_trans_alloc(sc, mpath->link_resblks);
457 	if (error)
458 		return error;
459 
460 	error = xchk_metapath_ilock_both(mpath);
461 	if (error) {
462 		xchk_trans_cancel(sc);
463 		return error;
464 	}
465 	xfs_trans_ijoin(sc->tp, mpath->dp, 0);
466 	xfs_trans_ijoin(sc->tp, sc->ip, 0);
467 
468 	error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
469 	trace_xrep_metapath_lookup(sc, mpath->path, mpath->dp, ino);
470 	if (error == -ENOENT) {
471 		/*
472 		 * There is no dirent in the directory.  Create an entry
473 		 * pointing to @sc->ip.
474 		 */
475 		error = xrep_metapath_link(mpath);
476 		if (error)
477 			goto out_cancel;
478 
479 		error = xrep_trans_commit(sc);
480 		xchk_metapath_iunlock(mpath);
481 		return error;
482 	}
483 	if (error)
484 		goto out_cancel;
485 
486 	if (ino == sc->ip->i_ino) {
487 		/* The dirent already points to @sc->ip; we're done. */
488 		error = 0;
489 		goto out_cancel;
490 	}
491 
492 	/*
493 	 * The dirent points elsewhere; pass that back so that the caller
494 	 * can try to remove the dirent.
495 	 */
496 	*alleged_child = ino;
497 	error = -EEXIST;
498 
499 out_cancel:
500 	xchk_trans_cancel(sc);
501 	xchk_metapath_iunlock(mpath);
502 	return error;
503 }
504 
505 /*
506  * Take the ILOCK on the metadata directory parent and a bad child, if one is
507  * supplied.  We do not know that the metadata directory is not corrupt, so we
508  * lock the parent and try to lock the child.  Returns 0 if successful, or
509  * -EINTR to abort the repair.  The lock state of @dp is not recorded in @mpath.
510  */
511 STATIC int
512 xchk_metapath_ilock_parent_and_child(
513 	struct xchk_metapath	*mpath,
514 	struct xfs_inode	*ip)
515 {
516 	struct xfs_scrub	*sc = mpath->sc;
517 	int			error = 0;
518 
519 	while (true) {
520 		xfs_ilock(mpath->dp, XFS_ILOCK_EXCL);
521 		if (!ip || xfs_ilock_nowait(ip, XFS_ILOCK_EXCL))
522 			return 0;
523 		xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
524 
525 		if (xchk_should_terminate(sc, &error))
526 			return error;
527 
528 		delay(1);
529 	}
530 
531 	ASSERT(0);
532 	return -EINTR;
533 }
534 
535 /*
536  * Try to remove a dirent in @mpath->dp with the name @mpath->xname that points
537  * to @alleged_child.  Returns:
538  *
539  * 0 if there is no longer a dirent;
540  * -EEXIST if the dirent points to @sc->ip;
541  * -EAGAIN and an updated @alleged_child if the dirent points elsewhere; or
542  * A negative errno for any other error.
543  */
544 STATIC int
545 xrep_metapath_try_unlink(
546 	struct xchk_metapath	*mpath,
547 	xfs_ino_t		*alleged_child)
548 {
549 	struct xfs_scrub	*sc = mpath->sc;
550 	struct xfs_inode	*ip = NULL;
551 	xfs_ino_t		ino;
552 	int			error;
553 
554 	ASSERT(*alleged_child != sc->ip->i_ino);
555 
556 	trace_xrep_metapath_try_unlink(sc, mpath->path, mpath->dp,
557 			*alleged_child);
558 
559 	/*
560 	 * Allocate transaction, grab the alleged child inode, lock inodes,
561 	 * join to transaction.
562 	 */
563 	error = xchk_trans_alloc(sc, mpath->unlink_resblks);
564 	if (error)
565 		return error;
566 
567 	error = xchk_iget(sc, *alleged_child, &ip);
568 	if (error == -EINVAL || error == -ENOENT) {
569 		/* inode number is bogus, junk the dirent */
570 		error = 0;
571 	}
572 	if (error) {
573 		xchk_trans_cancel(sc);
574 		return error;
575 	}
576 
577 	error = xchk_metapath_ilock_parent_and_child(mpath, ip);
578 	if (error) {
579 		xchk_trans_cancel(sc);
580 		return error;
581 	}
582 	xfs_trans_ijoin(sc->tp, mpath->dp, 0);
583 	if (ip)
584 		xfs_trans_ijoin(sc->tp, ip, 0);
585 
586 	error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
587 	trace_xrep_metapath_lookup(sc, mpath->path, mpath->dp, ino);
588 	if (error == -ENOENT) {
589 		/*
590 		 * There is no dirent in the directory anymore.  We're ready to
591 		 * try the link operation again.
592 		 */
593 		error = 0;
594 		goto out_cancel;
595 	}
596 	if (error)
597 		goto out_cancel;
598 
599 	if (ino == sc->ip->i_ino) {
600 		/* The dirent already points to @sc->ip; we're done. */
601 		error = -EEXIST;
602 		goto out_cancel;
603 	}
604 
605 	/*
606 	 * The dirent does not point to the alleged child.  Update the caller
607 	 * and signal that we want to be called again.
608 	 */
609 	if (ino != *alleged_child) {
610 		*alleged_child = ino;
611 		error = -EAGAIN;
612 		goto out_cancel;
613 	}
614 
615 	/* Remove the link to the child. */
616 	error = xrep_metapath_unlink(mpath, ino, ip);
617 	if (error)
618 		goto out_cancel;
619 
620 	error = xrep_trans_commit(sc);
621 	goto out_unlock;
622 
623 out_cancel:
624 	xchk_trans_cancel(sc);
625 out_unlock:
626 	xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
627 	if (ip) {
628 		xfs_iunlock(ip, XFS_ILOCK_EXCL);
629 		xchk_irele(sc, ip);
630 	}
631 	return error;
632 }
633 
634 /*
635  * Make sure the metadata directory path points to the child being examined.
636  *
637  * Repair needs to be able to create a directory structure, create its own
638  * transactions, and take ILOCKs.  This function /must/ be called after all
639  * other repairs have completed.
640  */
641 int
642 xrep_metapath(
643 	struct xfs_scrub	*sc)
644 {
645 	struct xchk_metapath	*mpath = sc->buf;
646 	struct xfs_mount	*mp = sc->mp;
647 	int			error = 0;
648 
649 	/* Just probing, nothing to repair. */
650 	if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE)
651 		return 0;
652 
653 	/* Parent required to do anything else. */
654 	if (mpath->dp == NULL)
655 		return -EFSCORRUPTED;
656 
657 	/*
658 	 * Make sure the child file actually has an attr fork to receive a new
659 	 * parent pointer if the fs has parent pointers.
660 	 */
661 	if (xfs_has_parent(mp)) {
662 		error = xfs_attr_add_fork(sc->ip,
663 				sizeof(struct xfs_attr_sf_hdr), 1);
664 		if (error)
665 			return error;
666 	}
667 
668 	/* Compute block reservation required to unlink and link a file. */
669 	mpath->unlink_resblks = xfs_remove_space_res(mp, MAXNAMELEN);
670 	mpath->link_resblks = xfs_link_space_res(mp, MAXNAMELEN);
671 
672 	do {
673 		xfs_ino_t	alleged_child;
674 
675 		/* Re-establish the link, or tell us which inode to remove. */
676 		error = xrep_metapath_try_link(mpath, &alleged_child);
677 		if (!error)
678 			return 0;
679 		if (error != -EEXIST)
680 			return error;
681 
682 		/*
683 		 * Remove an incorrect link to an alleged child, or tell us
684 		 * which inode to remove.
685 		 */
686 		do {
687 			error = xrep_metapath_try_unlink(mpath, &alleged_child);
688 		} while (error == -EAGAIN);
689 		if (error == -EEXIST) {
690 			/* Link established; we're done. */
691 			error = 0;
692 			break;
693 		}
694 	} while (!error);
695 
696 	return error;
697 }
698 #endif /* CONFIG_XFS_ONLINE_REPAIR */
699