#include "xfs_platform.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"
#include "xfs_attr_leaf.h"
struct kmem_cache *xfs_parent_args_cache;
bool
xfs_parent_namecheck(
unsigned int attr_flags,
const void *name,
size_t length)
{
if (attr_flags & XFS_ATTR_INCOMPLETE)
return false;
return xfs_dir2_namecheck(name, length);
}
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;
if (valuelen != sizeof(struct xfs_parent_rec))
return false;
if (value == NULL)
return false;
if (!xfs_verify_dir_ino(mp, be64_to_cpu(rec->p_ino)))
return false;
return true;
}
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,
};
return xfs_dir2_hashname(mp, &xname) ^
upper_32_bits(parent_ino) ^ lower_32_bits(parent_ino);
}
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;
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));
}
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);
}
static inline int
xfs_parent_iread_extents(
struct xfs_trans *tp,
struct xfs_inode *child)
{
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);
}
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);
return xfs_attr_setname(&ppargs->args, 0);
}
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);
return xfs_attr_removename(&ppargs->args);
}
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);
return xfs_attr_replacename(&ppargs->args, 0);
}
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;
}
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);
}
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;
}
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);
}
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);
}