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