1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*
3 * Copyright (c) 2021-2024 Oracle.  All Rights Reserved.
4 * Author: Darrick J. Wong <djwong@kernel.org>
5 */
6#ifndef __XFS_SCRUB_NLINKS_H__
7#define __XFS_SCRUB_NLINKS_H__
8
9/* Live link count control structure. */
10struct xchk_nlink_ctrs {
11	struct xfs_scrub	*sc;
12
13	/* Shadow link count data and its mutex. */
14	struct xfarray		*nlinks;
15	struct mutex		lock;
16
17	/*
18	 * The collection step uses a separate iscan context from the compare
19	 * step because the collection iscan coordinates live updates to the
20	 * observation data while this scanner is running.  The compare iscan
21	 * is secondary and can be reinitialized as needed.
22	 */
23	struct xchk_iscan	collect_iscan;
24	struct xchk_iscan	compare_iscan;
25
26	/*
27	 * Hook into directory updates so that we can receive live updates
28	 * from other writer threads.
29	 */
30	struct xfs_dir_hook	dhook;
31};
32
33/*
34 * In-core link counts for a given inode in the filesystem.
35 *
36 * For an empty rootdir, the directory entries and the field to which they are
37 * accounted are as follows:
38 *
39 * Root directory:
40 *
41 * . points to self		(root.child)
42 * .. points to self		(root.parent)
43 * f1 points to a child file	(f1.parent)
44 * d1 points to a child dir	(d1.parent, root.child)
45 *
46 * Subdirectory d1:
47 *
48 * . points to self		(d1.child)
49 * .. points to root dir	(root.backref)
50 * f2 points to child file	(f2.parent)
51 * f3 points to root.f1		(f1.parent)
52 *
53 * root.nlink == 3 (root.dot, root.dotdot, root.d1)
54 * d1.nlink == 2 (root.d1, d1.dot)
55 * f1.nlink == 2 (root.f1, d1.f3)
56 * f2.nlink == 1 (d1.f2)
57 */
58struct xchk_nlink {
59	/* Count of forward links from parent directories to this file. */
60	xfs_nlink_t		parents;
61
62	/*
63	 * Count of back links to this parent directory from child
64	 * subdirectories.
65	 */
66	xfs_nlink_t		backrefs;
67
68	/*
69	 * Count of forward links from this directory to all child files and
70	 * the number of dot entries.  Should be zero for non-directories.
71	 */
72	xfs_nlink_t		children;
73
74	/* Record state flags */
75	unsigned int		flags;
76};
77
78/*
79 * This incore link count has been written at least once.  We never want to
80 * store an xchk_nlink that looks uninitialized.
81 */
82#define XCHK_NLINK_WRITTEN		(1U << 0)
83
84/* Already checked this link count record. */
85#define XCHK_NLINK_COMPARE_SCANNED	(1U << 1)
86
87/* Already made a repair with this link count record. */
88#define XREP_NLINK_DIRTY		(1U << 2)
89
90/* Compute total link count, using large enough variables to detect overflow. */
91static inline uint64_t
92xchk_nlink_total(struct xfs_inode *ip, const struct xchk_nlink *live)
93{
94	uint64_t	ret = live->parents;
95
96	/* Add one link count for the dot entry of any linked directory. */
97	if (ip && S_ISDIR(VFS_I(ip)->i_mode) && VFS_I(ip)->i_nlink)
98		ret++;
99	return ret + live->children;
100}
101
102#endif /* __XFS_SCRUB_NLINKS_H__ */
103