brintos

brintos / linux-shallow public Read only

0
0
Text · 9.8 KiB · 69366c4 Raw
380 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2022-2024 Oracle.4 * All rights reserved.5 */6#include "xfs.h"7#include "xfs_fs.h"8#include "xfs_format.h"9#include "xfs_da_format.h"10#include "xfs_log_format.h"11#include "xfs_shared.h"12#include "xfs_trans_resv.h"13#include "xfs_mount.h"14#include "xfs_bmap_btree.h"15#include "xfs_inode.h"16#include "xfs_error.h"17#include "xfs_trace.h"18#include "xfs_trans.h"19#include "xfs_da_btree.h"20#include "xfs_attr.h"21#include "xfs_dir2.h"22#include "xfs_dir2_priv.h"23#include "xfs_attr_sf.h"24#include "xfs_bmap.h"25#include "xfs_defer.h"26#include "xfs_log.h"27#include "xfs_xattr.h"28#include "xfs_parent.h"29#include "xfs_trans_space.h"30#include "xfs_attr_item.h"31#include "xfs_health.h"32 33struct kmem_cache		*xfs_parent_args_cache;34 35/*36 * Parent pointer attribute handling.37 *38 * Because the attribute name is a filename component, it will never be longer39 * than 255 bytes and must not contain nulls or slashes.  These are roughly the40 * same constraints that apply to attribute names.41 *42 * The attribute value must always be a struct xfs_parent_rec.  This means the43 * attribute will never be in remote format because 12 bytes is nowhere near44 * xfs_attr_leaf_entsize_local_max() (~75% of block size).45 *46 * Creating a new parent attribute will always create a new attribute - there47 * should never, ever be an existing attribute in the tree for a new inode.48 * ENOSPC behavior is problematic - creating the inode without the parent49 * pointer is effectively a corruption, so we allow parent attribute creation50 * to dip into the reserve block pool to avoid unexpected ENOSPC errors from51 * occurring.52 */53 54/* Return true if parent pointer attr name is valid. */55bool56xfs_parent_namecheck(57	unsigned int			attr_flags,58	const void			*name,59	size_t				length)60{61	/*62	 * Parent pointers always use logged operations, so there should never63	 * be incomplete xattrs.64	 */65	if (attr_flags & XFS_ATTR_INCOMPLETE)66		return false;67 68	return xfs_dir2_namecheck(name, length);69}70 71/* Return true if parent pointer attr value is valid. */72bool73xfs_parent_valuecheck(74	struct xfs_mount		*mp,75	const void			*value,76	size_t				valuelen)77{78	const struct xfs_parent_rec	*rec = value;79 80	if (!xfs_has_parent(mp))81		return false;82 83	/* The xattr value must be a parent record. */84	if (valuelen != sizeof(struct xfs_parent_rec))85		return false;86 87	/* The parent record must be local. */88	if (value == NULL)89		return false;90 91	/* The parent inumber must be valid. */92	if (!xfs_verify_dir_ino(mp, be64_to_cpu(rec->p_ino)))93		return false;94 95	return true;96}97 98/* Compute the attribute name hash for a parent pointer. */99xfs_dahash_t100xfs_parent_hashval(101	struct xfs_mount		*mp,102	const uint8_t			*name,103	int				namelen,104	xfs_ino_t			parent_ino)105{106	struct xfs_name			xname = {107		.name			= name,108		.len			= namelen,109	};110 111	/*112	 * Use the same dirent name hash as would be used on the directory, but113	 * mix in the parent inode number to avoid collisions on hardlinked114	 * files with identical names but different parents.115	 */116	return xfs_dir2_hashname(mp, &xname) ^117		upper_32_bits(parent_ino) ^ lower_32_bits(parent_ino);118}119 120/* Compute the attribute name hash from the xattr components. */121xfs_dahash_t122xfs_parent_hashattr(123	struct xfs_mount		*mp,124	const uint8_t			*name,125	int				namelen,126	const void			*value,127	int				valuelen)128{129	const struct xfs_parent_rec	*rec = value;130 131	/* Requires a local attr value in xfs_parent_rec format */132	if (valuelen != sizeof(struct xfs_parent_rec)) {133		ASSERT(valuelen == sizeof(struct xfs_parent_rec));134		return 0;135	}136 137	if (!value) {138		ASSERT(value != NULL);139		return 0;140	}141 142	return xfs_parent_hashval(mp, name, namelen, be64_to_cpu(rec->p_ino));143}144 145/*146 * Initialize the parent pointer arguments structure.  Caller must have zeroed147 * the contents of @args.  @tp is only required for updates.148 */149static void150xfs_parent_da_args_init(151	struct xfs_da_args	*args,152	struct xfs_trans	*tp,153	struct xfs_parent_rec	*rec,154	struct xfs_inode	*child,155	xfs_ino_t		owner,156	const struct xfs_name	*parent_name)157{158	args->geo = child->i_mount->m_attr_geo;159	args->whichfork = XFS_ATTR_FORK;160	args->attr_filter = XFS_ATTR_PARENT;161	args->op_flags = XFS_DA_OP_LOGGED | XFS_DA_OP_OKNOENT;162	args->trans = tp;163	args->dp = child;164	args->owner = owner;165	args->name = parent_name->name;166	args->namelen = parent_name->len;167	args->value = rec;168	args->valuelen = sizeof(struct xfs_parent_rec);169	xfs_attr_sethash(args);170}171 172/* Make sure the incore state is ready for a parent pointer query/update. */173static inline int174xfs_parent_iread_extents(175	struct xfs_trans	*tp,176	struct xfs_inode	*child)177{178	/* Parent pointers require that the attr fork must exist. */179	if (XFS_IS_CORRUPT(child->i_mount, !xfs_inode_has_attr_fork(child))) {180		xfs_inode_mark_sick(child, XFS_SICK_INO_PARENT);181		return -EFSCORRUPTED;182	}183 184	return xfs_iread_extents(tp, child, XFS_ATTR_FORK);185}186 187/* Add a parent pointer to reflect a dirent addition. */188int189xfs_parent_addname(190	struct xfs_trans	*tp,191	struct xfs_parent_args	*ppargs,192	struct xfs_inode	*dp,193	const struct xfs_name	*parent_name,194	struct xfs_inode	*child)195{196	int			error;197 198	error = xfs_parent_iread_extents(tp, child);199	if (error)200		return error;201 202	xfs_inode_to_parent_rec(&ppargs->rec, dp);203	xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child,204			child->i_ino, parent_name);205	xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_SET);206	return 0;207}208 209/* Remove a parent pointer to reflect a dirent removal. */210int211xfs_parent_removename(212	struct xfs_trans	*tp,213	struct xfs_parent_args	*ppargs,214	struct xfs_inode	*dp,215	const struct xfs_name	*parent_name,216	struct xfs_inode	*child)217{218	int			error;219 220	error = xfs_parent_iread_extents(tp, child);221	if (error)222		return error;223 224	xfs_inode_to_parent_rec(&ppargs->rec, dp);225	xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child,226			child->i_ino, parent_name);227	xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REMOVE);228	return 0;229}230 231/* Replace one parent pointer with another to reflect a rename. */232int233xfs_parent_replacename(234	struct xfs_trans	*tp,235	struct xfs_parent_args	*ppargs,236	struct xfs_inode	*old_dp,237	const struct xfs_name	*old_name,238	struct xfs_inode	*new_dp,239	const struct xfs_name	*new_name,240	struct xfs_inode	*child)241{242	int			error;243 244	error = xfs_parent_iread_extents(tp, child);245	if (error)246		return error;247 248	xfs_inode_to_parent_rec(&ppargs->rec, old_dp);249	xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child,250			child->i_ino, old_name);251 252	xfs_inode_to_parent_rec(&ppargs->new_rec, new_dp);253	ppargs->args.new_name = new_name->name;254	ppargs->args.new_namelen = new_name->len;255	ppargs->args.new_value = &ppargs->new_rec;256	ppargs->args.new_valuelen = sizeof(struct xfs_parent_rec);257	xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REPLACE);258	return 0;259}260 261/*262 * Extract parent pointer information from any parent pointer xattr into263 * @parent_ino/gen.  The last two parameters can be NULL pointers.264 *265 * Returns 0 if this is not a parent pointer xattr at all; or -EFSCORRUPTED for266 * garbage.267 */268int269xfs_parent_from_attr(270	struct xfs_mount	*mp,271	unsigned int		attr_flags,272	const unsigned char	*name,273	unsigned int		namelen,274	const void		*value,275	unsigned int		valuelen,276	xfs_ino_t		*parent_ino,277	uint32_t		*parent_gen)278{279	const struct xfs_parent_rec	*rec = value;280 281	ASSERT(attr_flags & XFS_ATTR_PARENT);282 283	if (!xfs_parent_namecheck(attr_flags, name, namelen))284		return -EFSCORRUPTED;285	if (!xfs_parent_valuecheck(mp, value, valuelen))286		return -EFSCORRUPTED;287 288	if (parent_ino)289		*parent_ino = be64_to_cpu(rec->p_ino);290	if (parent_gen)291		*parent_gen = be32_to_cpu(rec->p_gen);292	return 0;293}294 295/*296 * Look up a parent pointer record (@parent_name -> @pptr) of @ip.297 *298 * Caller must hold at least ILOCK_SHARED.  The scratchpad need not be299 * initialized.300 *301 * Returns 0 if the pointer is found, -ENOATTR if there is no match, or a302 * negative errno.303 */304int305xfs_parent_lookup(306	struct xfs_trans		*tp,307	struct xfs_inode		*ip,308	const struct xfs_name		*parent_name,309	struct xfs_parent_rec		*pptr,310	struct xfs_da_args		*scratch)311{312	memset(scratch, 0, sizeof(struct xfs_da_args));313	xfs_parent_da_args_init(scratch, tp, pptr, ip, ip->i_ino, parent_name);314	return xfs_attr_get_ilocked(scratch);315}316 317/* Sanity-check a parent pointer before we try to perform repairs. */318static inline bool319xfs_parent_sanity_check(320	struct xfs_mount		*mp,321	const struct xfs_name		*parent_name,322	const struct xfs_parent_rec	*pptr)323{324	if (!xfs_parent_namecheck(XFS_ATTR_PARENT, parent_name->name,325				parent_name->len))326		return false;327 328	if (!xfs_parent_valuecheck(mp, pptr, sizeof(*pptr)))329		return false;330 331	return true;332}333 334 335/*336 * Attach the parent pointer (@parent_name -> @pptr) to @ip immediately.337 * Caller must not have a transaction or hold the ILOCK.  This is for338 * specialized repair functions only.  The scratchpad need not be initialized.339 */340int341xfs_parent_set(342	struct xfs_inode	*ip,343	xfs_ino_t		owner,344	const struct xfs_name	*parent_name,345	struct xfs_parent_rec	*pptr,346	struct xfs_da_args	*scratch)347{348	if (!xfs_parent_sanity_check(ip->i_mount, parent_name, pptr)) {349		ASSERT(0);350		return -EFSCORRUPTED;351	}352 353	memset(scratch, 0, sizeof(struct xfs_da_args));354	xfs_parent_da_args_init(scratch, NULL, pptr, ip, owner, parent_name);355	return xfs_attr_set(scratch, XFS_ATTRUPDATE_CREATE, false);356}357 358/*359 * Remove the parent pointer (@parent_name -> @pptr) from @ip immediately.360 * Caller must not have a transaction or hold the ILOCK.  This is for361 * specialized repair functions only.  The scratchpad need not be initialized.362 */363int364xfs_parent_unset(365	struct xfs_inode		*ip,366	xfs_ino_t			owner,367	const struct xfs_name		*parent_name,368	struct xfs_parent_rec		*pptr,369	struct xfs_da_args		*scratch)370{371	if (!xfs_parent_sanity_check(ip->i_mount, parent_name, pptr)) {372		ASSERT(0);373		return -EFSCORRUPTED;374	}375 376	memset(scratch, 0, sizeof(struct xfs_da_args));377	xfs_parent_da_args_init(scratch, NULL, pptr, ip, owner, parent_name);378	return xfs_attr_set(scratch, XFS_ATTRUPDATE_REMOVE, false);379}380