179 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			179 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* SPDX-License-Identifier: GPL-2.0-or-later */
 | |
| /*
 | |
|  * Copyright (c) 2023-2024 Oracle.  All Rights Reserved.
 | |
|  * Author: Darrick J. Wong <djwong@kernel.org>
 | |
|  */
 | |
| #ifndef __XFS_SCRUB_DIRTREE_H__
 | |
| #define __XFS_SCRUB_DIRTREE_H__
 | |
| 
 | |
| /*
 | |
|  * Each of these represents one parent pointer path step in a chain going
 | |
|  * up towards the directory tree root.  These are stored inside an xfarray.
 | |
|  */
 | |
| struct xchk_dirpath_step {
 | |
| 	/* Directory entry name associated with this parent link. */
 | |
| 	xfblob_cookie		name_cookie;
 | |
| 	unsigned int		name_len;
 | |
| 
 | |
| 	/* Handle of the parent directory. */
 | |
| 	struct xfs_parent_rec	pptr_rec;
 | |
| };
 | |
| 
 | |
| enum xchk_dirpath_outcome {
 | |
| 	XCHK_DIRPATH_SCANNING = 0,	/* still being put together */
 | |
| 	XCHK_DIRPATH_DELETE,		/* delete this path */
 | |
| 	XCHK_DIRPATH_CORRUPT,		/* corruption detected in path */
 | |
| 	XCHK_DIRPATH_LOOP,		/* cycle detected further up */
 | |
| 	XCHK_DIRPATH_STALE,		/* path is stale */
 | |
| 	XCHK_DIRPATH_OK,		/* path reaches the root */
 | |
| 
 | |
| 	XREP_DIRPATH_DELETING,		/* path is being deleted */
 | |
| 	XREP_DIRPATH_DELETED,		/* path has been deleted */
 | |
| 	XREP_DIRPATH_ADOPTING,		/* path is being adopted */
 | |
| 	XREP_DIRPATH_ADOPTED,		/* path has been adopted */
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * Each of these represents one parent pointer path out of the directory being
 | |
|  * scanned.  These exist in-core, and hopefully there aren't more than a
 | |
|  * handful of them.
 | |
|  */
 | |
| struct xchk_dirpath {
 | |
| 	struct list_head	list;
 | |
| 
 | |
| 	/* Index of the first step in this path. */
 | |
| 	xfarray_idx_t		first_step;
 | |
| 
 | |
| 	/* Index of the second step in this path. */
 | |
| 	xfarray_idx_t		second_step;
 | |
| 
 | |
| 	/* Inodes seen while walking this path. */
 | |
| 	struct xino_bitmap	seen_inodes;
 | |
| 
 | |
| 	/* Number of steps in this path. */
 | |
| 	unsigned int		nr_steps;
 | |
| 
 | |
| 	/* Which path is this? */
 | |
| 	unsigned int		path_nr;
 | |
| 
 | |
| 	/* What did we conclude from following this path? */
 | |
| 	enum xchk_dirpath_outcome outcome;
 | |
| };
 | |
| 
 | |
| struct xchk_dirtree_outcomes {
 | |
| 	/* Number of XCHK_DIRPATH_DELETE */
 | |
| 	unsigned int		bad;
 | |
| 
 | |
| 	/* Number of XCHK_DIRPATH_CORRUPT or XCHK_DIRPATH_LOOP */
 | |
| 	unsigned int		suspect;
 | |
| 
 | |
| 	/* Number of XCHK_DIRPATH_OK */
 | |
| 	unsigned int		good;
 | |
| 
 | |
| 	/* Directory needs to be added to lost+found */
 | |
| 	bool			needs_adoption;
 | |
| };
 | |
| 
 | |
| struct xchk_dirtree {
 | |
| 	struct xfs_scrub	*sc;
 | |
| 
 | |
| 	/* Root inode that we're looking for. */
 | |
| 	xfs_ino_t		root_ino;
 | |
| 
 | |
| 	/*
 | |
| 	 * This is the inode that we're scanning.  The live update hook can
 | |
| 	 * continue to be called after xchk_teardown drops sc->ip but before
 | |
| 	 * it calls buf_cleanup, so we keep a copy.
 | |
| 	 */
 | |
| 	xfs_ino_t		scan_ino;
 | |
| 
 | |
| 	/*
 | |
| 	 * If we start deleting redundant paths to this subdirectory, this is
 | |
| 	 * the inode number of the surviving parent and the dotdot entry will
 | |
| 	 * be set to this value.  If the value is NULLFSINO, then use @root_ino
 | |
| 	 * as a stand-in until the orphanage can adopt the subdirectory.
 | |
| 	 */
 | |
| 	xfs_ino_t		parent_ino;
 | |
| 
 | |
| 	/* Scratch buffer for scanning pptr xattrs */
 | |
| 	struct xfs_parent_rec	pptr_rec;
 | |
| 	struct xfs_da_args	pptr_args;
 | |
| 
 | |
| 	/* Name buffer */
 | |
| 	struct xfs_name		xname;
 | |
| 	char			namebuf[MAXNAMELEN];
 | |
| 
 | |
| 	/* Information for reparenting this directory. */
 | |
| 	struct xrep_adoption	adoption;
 | |
| 
 | |
| 	/*
 | |
| 	 * Hook into directory updates so that we can receive live updates
 | |
| 	 * from other writer threads.
 | |
| 	 */
 | |
| 	struct xfs_dir_hook	dhook;
 | |
| 
 | |
| 	/* Parent pointer update arguments. */
 | |
| 	struct xfs_parent_args	ppargs;
 | |
| 
 | |
| 	/* lock for everything below here */
 | |
| 	struct mutex		lock;
 | |
| 
 | |
| 	/* buffer for the live update functions to use for dirent names */
 | |
| 	struct xfs_name		hook_xname;
 | |
| 	unsigned char		hook_namebuf[MAXNAMELEN];
 | |
| 
 | |
| 	/*
 | |
| 	 * All path steps observed during this scan.  Each of the path
 | |
| 	 * steps for a particular pathwalk are recorded in sequential
 | |
| 	 * order in the xfarray.  A pathwalk ends either with a step
 | |
| 	 * pointing to the root directory (success) or pointing to NULLFSINO
 | |
| 	 * (loop detected, empty dir detected, etc).
 | |
| 	 */
 | |
| 	struct xfarray		*path_steps;
 | |
| 
 | |
| 	/* All names observed during this scan. */
 | |
| 	struct xfblob		*path_names;
 | |
| 
 | |
| 	/* All paths being tracked by this scanner. */
 | |
| 	struct list_head	path_list;
 | |
| 
 | |
| 	/* Number of paths in path_list. */
 | |
| 	unsigned int		nr_paths;
 | |
| 
 | |
| 	/* Number of parents found by a pptr scan. */
 | |
| 	unsigned int		parents_found;
 | |
| 
 | |
| 	/* Have the path data been invalidated by a concurrent update? */
 | |
| 	bool			stale:1;
 | |
| 
 | |
| 	/* Has the scan been aborted? */
 | |
| 	bool			aborted:1;
 | |
| };
 | |
| 
 | |
| #define xchk_dirtree_for_each_path_safe(dl, path, n) \
 | |
| 	list_for_each_entry_safe((path), (n), &(dl)->path_list, list)
 | |
| 
 | |
| #define xchk_dirtree_for_each_path(dl, path) \
 | |
| 	list_for_each_entry((path), &(dl)->path_list, list)
 | |
| 
 | |
| static inline bool
 | |
| xchk_dirtree_parentless(const struct xchk_dirtree *dl)
 | |
| {
 | |
| 	struct xfs_scrub	*sc = dl->sc;
 | |
| 
 | |
| 	if (sc->ip == sc->mp->m_rootip)
 | |
| 		return true;
 | |
| 	if (VFS_I(sc->ip)->i_nlink == 0)
 | |
| 		return true;
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| int xchk_dirtree_find_paths_to_root(struct xchk_dirtree *dl);
 | |
| int xchk_dirpath_append(struct xchk_dirtree *dl, struct xfs_inode *ip,
 | |
| 		struct xchk_dirpath *path, const struct xfs_name *name,
 | |
| 		const struct xfs_parent_rec *pptr);
 | |
| void xchk_dirtree_evaluate(struct xchk_dirtree *dl,
 | |
| 		struct xchk_dirtree_outcomes *oc);
 | |
| 
 | |
| #endif /* __XFS_SCRUB_DIRTREE_H__ */
 |