| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (c) 2022-2024 Oracle.
- * All rights reserved.
- */
- #include "xfs.h"
- #include "xfs_fs.h"
- #include "xfs_format.h"
- #include "xfs_da_format.h"
- #include "xfs_log_format.h"
- #include "xfs_shared.h"
- #include "xfs_trans_resv.h"
- #include "xfs_mount.h"
- #include "xfs_bmap_btree.h"
- #include "xfs_inode.h"
- #include "xfs_error.h"
- #include "xfs_trace.h"
- #include "xfs_trans.h"
- #include "xfs_da_btree.h"
- #include "xfs_attr.h"
- #include "xfs_dir2.h"
- #include "xfs_dir2_priv.h"
- #include "xfs_attr_sf.h"
- #include "xfs_bmap.h"
- #include "xfs_defer.h"
- #include "xfs_log.h"
- #include "xfs_xattr.h"
- #include "xfs_parent.h"
- #include "xfs_trans_space.h"
- #include "xfs_attr_item.h"
- #include "xfs_health.h"
- struct kmem_cache *xfs_parent_args_cache;
- /*
- * Parent pointer attribute handling.
- *
- * Because the attribute name is a filename component, it will never be longer
- * than 255 bytes and must not contain nulls or slashes. These are roughly the
- * same constraints that apply to attribute names.
- *
- * The attribute value must always be a struct xfs_parent_rec. This means the
- * attribute will never be in remote format because 12 bytes is nowhere near
- * xfs_attr_leaf_entsize_local_max() (~75% of block size).
- *
- * Creating a new parent attribute will always create a new attribute - there
- * should never, ever be an existing attribute in the tree for a new inode.
- * ENOSPC behavior is problematic - creating the inode without the parent
- * pointer is effectively a corruption, so we allow parent attribute creation
- * to dip into the reserve block pool to avoid unexpected ENOSPC errors from
- * occurring.
- */
- /* Return true if parent pointer attr name is valid. */
- bool
- xfs_parent_namecheck(
- unsigned int attr_flags,
- const void *name,
- size_t length)
- {
- /*
- * Parent pointers always use logged operations, so there should never
- * be incomplete xattrs.
- */
- if (attr_flags & XFS_ATTR_INCOMPLETE)
- return false;
- return xfs_dir2_namecheck(name, length);
- }
- /* Return true if parent pointer attr value is valid. */
- bool
- xfs_parent_valuecheck(
- struct xfs_mount *mp,
- const void *value,
- size_t valuelen)
- {
- const struct xfs_parent_rec *rec = value;
- if (!xfs_has_parent(mp))
- return false;
- /* The xattr value must be a parent record. */
- if (valuelen != sizeof(struct xfs_parent_rec))
- return false;
- /* The parent record must be local. */
- if (value == NULL)
- return false;
- /* The parent inumber must be valid. */
- if (!xfs_verify_dir_ino(mp, be64_to_cpu(rec->p_ino)))
- return false;
- return true;
- }
- /* Compute the attribute name hash for a parent pointer. */
- xfs_dahash_t
- xfs_parent_hashval(
- struct xfs_mount *mp,
- const uint8_t *name,
- int namelen,
- xfs_ino_t parent_ino)
- {
- struct xfs_name xname = {
- .name = name,
- .len = namelen,
- };
- /*
- * Use the same dirent name hash as would be used on the directory, but
- * mix in the parent inode number to avoid collisions on hardlinked
- * files with identical names but different parents.
- */
- return xfs_dir2_hashname(mp, &xname) ^
- upper_32_bits(parent_ino) ^ lower_32_bits(parent_ino);
- }
- /* Compute the attribute name hash from the xattr components. */
- xfs_dahash_t
- xfs_parent_hashattr(
- struct xfs_mount *mp,
- const uint8_t *name,
- int namelen,
- const void *value,
- int valuelen)
- {
- const struct xfs_parent_rec *rec = value;
- /* Requires a local attr value in xfs_parent_rec format */
- if (valuelen != sizeof(struct xfs_parent_rec)) {
- ASSERT(valuelen == sizeof(struct xfs_parent_rec));
- return 0;
- }
- if (!value) {
- ASSERT(value != NULL);
- return 0;
- }
- return xfs_parent_hashval(mp, name, namelen, be64_to_cpu(rec->p_ino));
- }
- /*
- * Initialize the parent pointer arguments structure. Caller must have zeroed
- * the contents of @args. @tp is only required for updates.
- */
- static void
- xfs_parent_da_args_init(
- struct xfs_da_args *args,
- struct xfs_trans *tp,
- struct xfs_parent_rec *rec,
- struct xfs_inode *child,
- xfs_ino_t owner,
- const struct xfs_name *parent_name)
- {
- args->geo = child->i_mount->m_attr_geo;
- args->whichfork = XFS_ATTR_FORK;
- args->attr_filter = XFS_ATTR_PARENT;
- args->op_flags = XFS_DA_OP_LOGGED | XFS_DA_OP_OKNOENT;
- args->trans = tp;
- args->dp = child;
- args->owner = owner;
- args->name = parent_name->name;
- args->namelen = parent_name->len;
- args->value = rec;
- args->valuelen = sizeof(struct xfs_parent_rec);
- xfs_attr_sethash(args);
- }
- /* Make sure the incore state is ready for a parent pointer query/update. */
- static inline int
- xfs_parent_iread_extents(
- struct xfs_trans *tp,
- struct xfs_inode *child)
- {
- /* Parent pointers require that the attr fork must exist. */
- if (XFS_IS_CORRUPT(child->i_mount, !xfs_inode_has_attr_fork(child))) {
- xfs_inode_mark_sick(child, XFS_SICK_INO_PARENT);
- return -EFSCORRUPTED;
- }
- return xfs_iread_extents(tp, child, XFS_ATTR_FORK);
- }
- /* Add a parent pointer to reflect a dirent addition. */
- int
- xfs_parent_addname(
- struct xfs_trans *tp,
- struct xfs_parent_args *ppargs,
- struct xfs_inode *dp,
- const struct xfs_name *parent_name,
- struct xfs_inode *child)
- {
- int error;
- error = xfs_parent_iread_extents(tp, child);
- if (error)
- return error;
- xfs_inode_to_parent_rec(&ppargs->rec, dp);
- xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child,
- child->i_ino, parent_name);
- xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_SET);
- return 0;
- }
- /* Remove a parent pointer to reflect a dirent removal. */
- int
- xfs_parent_removename(
- struct xfs_trans *tp,
- struct xfs_parent_args *ppargs,
- struct xfs_inode *dp,
- const struct xfs_name *parent_name,
- struct xfs_inode *child)
- {
- int error;
- error = xfs_parent_iread_extents(tp, child);
- if (error)
- return error;
- xfs_inode_to_parent_rec(&ppargs->rec, dp);
- xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child,
- child->i_ino, parent_name);
- xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REMOVE);
- return 0;
- }
- /* Replace one parent pointer with another to reflect a rename. */
- int
- xfs_parent_replacename(
- struct xfs_trans *tp,
- struct xfs_parent_args *ppargs,
- struct xfs_inode *old_dp,
- const struct xfs_name *old_name,
- struct xfs_inode *new_dp,
- const struct xfs_name *new_name,
- struct xfs_inode *child)
- {
- int error;
- error = xfs_parent_iread_extents(tp, child);
- if (error)
- return error;
- xfs_inode_to_parent_rec(&ppargs->rec, old_dp);
- xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child,
- child->i_ino, old_name);
- xfs_inode_to_parent_rec(&ppargs->new_rec, new_dp);
- ppargs->args.new_name = new_name->name;
- ppargs->args.new_namelen = new_name->len;
- ppargs->args.new_value = &ppargs->new_rec;
- ppargs->args.new_valuelen = sizeof(struct xfs_parent_rec);
- xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REPLACE);
- return 0;
- }
- /*
- * Extract parent pointer information from any parent pointer xattr into
- * @parent_ino/gen. The last two parameters can be NULL pointers.
- *
- * Returns 0 if this is not a parent pointer xattr at all; or -EFSCORRUPTED for
- * garbage.
- */
- int
- xfs_parent_from_attr(
- struct xfs_mount *mp,
- unsigned int attr_flags,
- const unsigned char *name,
- unsigned int namelen,
- const void *value,
- unsigned int valuelen,
- xfs_ino_t *parent_ino,
- uint32_t *parent_gen)
- {
- const struct xfs_parent_rec *rec = value;
- ASSERT(attr_flags & XFS_ATTR_PARENT);
- if (!xfs_parent_namecheck(attr_flags, name, namelen))
- return -EFSCORRUPTED;
- if (!xfs_parent_valuecheck(mp, value, valuelen))
- return -EFSCORRUPTED;
- if (parent_ino)
- *parent_ino = be64_to_cpu(rec->p_ino);
- if (parent_gen)
- *parent_gen = be32_to_cpu(rec->p_gen);
- return 0;
- }
- /*
- * Look up a parent pointer record (@parent_name -> @pptr) of @ip.
- *
- * Caller must hold at least ILOCK_SHARED. The scratchpad need not be
- * initialized.
- *
- * Returns 0 if the pointer is found, -ENOATTR if there is no match, or a
- * negative errno.
- */
- int
- xfs_parent_lookup(
- struct xfs_trans *tp,
- struct xfs_inode *ip,
- const struct xfs_name *parent_name,
- struct xfs_parent_rec *pptr,
- struct xfs_da_args *scratch)
- {
- memset(scratch, 0, sizeof(struct xfs_da_args));
- xfs_parent_da_args_init(scratch, tp, pptr, ip, ip->i_ino, parent_name);
- return xfs_attr_get_ilocked(scratch);
- }
- /* Sanity-check a parent pointer before we try to perform repairs. */
- static inline bool
- xfs_parent_sanity_check(
- struct xfs_mount *mp,
- const struct xfs_name *parent_name,
- const struct xfs_parent_rec *pptr)
- {
- if (!xfs_parent_namecheck(XFS_ATTR_PARENT, parent_name->name,
- parent_name->len))
- return false;
- if (!xfs_parent_valuecheck(mp, pptr, sizeof(*pptr)))
- return false;
- return true;
- }
- /*
- * Attach the parent pointer (@parent_name -> @pptr) to @ip immediately.
- * Caller must not have a transaction or hold the ILOCK. This is for
- * specialized repair functions only. The scratchpad need not be initialized.
- */
- int
- xfs_parent_set(
- struct xfs_inode *ip,
- xfs_ino_t owner,
- const struct xfs_name *parent_name,
- struct xfs_parent_rec *pptr,
- struct xfs_da_args *scratch)
- {
- if (!xfs_parent_sanity_check(ip->i_mount, parent_name, pptr)) {
- ASSERT(0);
- return -EFSCORRUPTED;
- }
- memset(scratch, 0, sizeof(struct xfs_da_args));
- xfs_parent_da_args_init(scratch, NULL, pptr, ip, owner, parent_name);
- return xfs_attr_set(scratch, XFS_ATTRUPDATE_CREATE, false);
- }
- /*
- * Remove the parent pointer (@parent_name -> @pptr) from @ip immediately.
- * Caller must not have a transaction or hold the ILOCK. This is for
- * specialized repair functions only. The scratchpad need not be initialized.
- */
- int
- xfs_parent_unset(
- struct xfs_inode *ip,
- xfs_ino_t owner,
- const struct xfs_name *parent_name,
- struct xfs_parent_rec *pptr,
- struct xfs_da_args *scratch)
- {
- if (!xfs_parent_sanity_check(ip->i_mount, parent_name, pptr)) {
- ASSERT(0);
- return -EFSCORRUPTED;
- }
- memset(scratch, 0, sizeof(struct xfs_da_args));
- xfs_parent_da_args_init(scratch, NULL, pptr, ip, owner, parent_name);
- return xfs_attr_set(scratch, XFS_ATTRUPDATE_REMOVE, false);
- }
|