Skip to content

Commit

Permalink
xfs: parent pointer attribute creation
Browse files Browse the repository at this point in the history
Add parent pointer attribute during xfs_create, and subroutines to
initialize attributes

[bfoster: rebase, use VFS inode generation]
[achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t,
           fixed some null pointer bugs,
           merged error handling patch,
           added subroutines to handle attribute initialization,
           remove unnecessary ENOSPC handling in xfs_attr_set_first_parent]

Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
  • Loading branch information
allisonhenderson committed Jul 7, 2021
1 parent 4801b66 commit 6f41023
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 24 deletions.
1 change: 1 addition & 0 deletions fs/xfs/Makefile
Expand Up @@ -40,6 +40,7 @@ xfs-y += $(addprefix libxfs/, \
xfs_inode_fork.o \
xfs_inode_buf.o \
xfs_log_rlimit.o \
xfs_parent.o \
xfs_ag_resv.o \
xfs_rmap.o \
xfs_rmap_btree.o \
Expand Down
15 changes: 14 additions & 1 deletion fs/xfs/libxfs/xfs_attr.c
Expand Up @@ -357,12 +357,25 @@ xfs_attr_set_iter(
struct xfs_buf **leaf_bp = &attr->xattri_leaf_bp;
struct xfs_inode *dp = args->dp;
struct xfs_buf *bp = NULL;
int forkoff, error = 0;
int sf_size, forkoff, error = 0;
struct xfs_mount *mp = args->dp->i_mount;

/* State machine switch */
switch (attr->xattri_dela_state) {
case XFS_DAS_UNINIT:
/*
* New inodes may not have an attribute fork yet. So set the
* attribute fork appropriately
*/
if (XFS_IFORK_Q((args->dp)) == 0) {
sf_size = sizeof(struct xfs_attr_sf_hdr) +
xfs_attr_sf_entsize_byname(args->namelen,
args->valuelen);
xfs_bmap_set_attrforkoff(args->dp, sf_size, NULL);
args->dp->i_afp = kmem_cache_zalloc(xfs_ifork_zone, 0);
args->dp->i_afp->if_format = XFS_DINODE_FMT_EXTENTS;
}

/*
* If the fork is shortform, attempt to add the attr. If there
* is no space, this converts to leaf format and returns
Expand Down
2 changes: 1 addition & 1 deletion fs/xfs/libxfs/xfs_bmap.c
Expand Up @@ -1029,7 +1029,7 @@ xfs_bmap_add_attrfork_local(
/*
* Set an inode attr fork offset based on the format of the data fork.
*/
static int
int
xfs_bmap_set_attrforkoff(
struct xfs_inode *ip,
int size,
Expand Down
1 change: 1 addition & 0 deletions fs/xfs/libxfs/xfs_bmap.h
Expand Up @@ -187,6 +187,7 @@ void xfs_trim_extent(struct xfs_bmbt_irec *irec, xfs_fileoff_t bno,
xfs_filblks_t len);
unsigned int xfs_bmap_compute_attr_offset(struct xfs_mount *mp);
int xfs_bmap_add_attrfork(struct xfs_inode *ip, int size, int rsvd);
int xfs_bmap_set_attrforkoff(struct xfs_inode *ip, int size, int *version);
void xfs_bmap_local_to_extents_empty(struct xfs_trans *tp,
struct xfs_inode *ip, int whichfork);
void __xfs_bmap_add_free(struct xfs_trans *tp, xfs_fsblock_t bno,
Expand Down
77 changes: 77 additions & 0 deletions fs/xfs/libxfs/xfs_parent.c
@@ -0,0 +1,77 @@
/*
* Copyright (c) 2015 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation
*/
#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_da_btree.h"
#include "xfs_attr_sf.h"
#include "xfs_bmap.h"

/*
* Parent pointer attribute handling.
*
* Because the attribute value is a filename component, it will never be longer
* than 255 bytes. This means the attribute will always be a local format
* attribute as it is xfs_attr_leaf_entsize_local_max() for v5 filesystems will
* always be larger than this (max is 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.
*/


/* Initializes a xfs_parent_name_rec to be stored as an attribute name */
void
xfs_init_parent_name_rec(
struct xfs_parent_name_rec *rec,
struct xfs_inode *ip,
uint32_t p_diroffset)
{
xfs_ino_t p_ino = ip->i_ino;
uint32_t p_gen = VFS_I(ip)->i_generation;

rec->p_ino = cpu_to_be64(p_ino);
rec->p_gen = cpu_to_be32(p_gen);
rec->p_diroffset = cpu_to_be32(p_diroffset);
}

/* Initializes a xfs_parent_name_irec from an xfs_parent_name_rec */
void
xfs_init_parent_name_irec(
struct xfs_parent_name_irec *irec,
struct xfs_parent_name_rec *rec)
{
irec->p_ino = be64_to_cpu(rec->p_ino);
irec->p_gen = be32_to_cpu(rec->p_gen);
irec->p_diroffset = be32_to_cpu(rec->p_diroffset);
}
31 changes: 31 additions & 0 deletions fs/xfs/libxfs/xfs_parent.h
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2018 Oracle, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation Inc.
*/
#ifndef __XFS_PARENT_H__
#define __XFS_PARENT_H__

#include "xfs_da_format.h"
#include "xfs_format.h"

/*
* Parent pointer attribute prototypes
*/
void xfs_init_parent_name_rec(struct xfs_parent_name_rec *rec,
struct xfs_inode *ip,
uint32_t p_diroffset);
void xfs_init_parent_name_irec(struct xfs_parent_name_irec *irec,
struct xfs_parent_name_rec *rec);
#endif /* __XFS_PARENT_H__ */
74 changes: 52 additions & 22 deletions fs/xfs/xfs_inode.c
Expand Up @@ -35,6 +35,7 @@
#include "xfs_bmap_btree.h"
#include "xfs_reflink.h"
#include "xfs_ag.h"
#include "xfs_parent.h"

kmem_zone_t *xfs_inode_zone;

Expand Down Expand Up @@ -954,27 +955,40 @@ xfs_bumplink(

int
xfs_create(
struct user_namespace *mnt_userns,
xfs_inode_t *dp,
struct xfs_name *name,
umode_t mode,
dev_t rdev,
bool init_xattrs,
xfs_inode_t **ipp)
struct user_namespace *mnt_userns,
xfs_inode_t *dp,
struct xfs_name *name,
umode_t mode,
dev_t rdev,
bool init_xattrs,
xfs_inode_t **ipp)
{
int is_dir = S_ISDIR(mode);
struct xfs_mount *mp = dp->i_mount;
struct xfs_inode *ip = NULL;
struct xfs_trans *tp = NULL;
int error;
bool unlock_dp_on_error = false;
prid_t prid;
struct xfs_dquot *udqp = NULL;
struct xfs_dquot *gdqp = NULL;
struct xfs_dquot *pdqp = NULL;
struct xfs_trans_res *tres;
uint resblks;
xfs_ino_t ino;
int is_dir = S_ISDIR(mode);
struct xfs_mount *mp = dp->i_mount;
struct xfs_inode *ip = NULL;
struct xfs_trans *tp = NULL;
int error;
bool unlock_dp_on_error = false;
prid_t prid;
struct xfs_dquot *udqp = NULL;
struct xfs_dquot *gdqp = NULL;
struct xfs_dquot *pdqp = NULL;
struct xfs_trans_res *tres;
uint resblks;
xfs_ino_t ino;
xfs_dir2_dataptr_t diroffset;
struct xfs_parent_name_rec rec;
struct xfs_da_args args = {
.dp = dp,
.geo = mp->m_attr_geo,
.whichfork = XFS_ATTR_FORK,
.attr_filter = XFS_ATTR_PARENT,
.op_flags = XFS_DA_OP_OKNOENT,
.name = (const uint8_t *)&rec,
.namelen = sizeof(rec),
.value = (void *)name->name,
.valuelen = name->len,
};

trace_xfs_create(dp, name);

Expand Down Expand Up @@ -1045,11 +1059,12 @@ xfs_create(
* the transaction cancel unlocking dp so don't do it explicitly in the
* error path.
*/
xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, dp, 0);
unlock_dp_on_error = false;

error = xfs_dir_createname(tp, dp, name, ip->i_ino,
resblks - XFS_IALLOC_SPACE_RES(mp), NULL);
resblks - XFS_IALLOC_SPACE_RES(mp),
&diroffset);
if (error) {
ASSERT(error != -ENOSPC);
goto out_trans_cancel;
Expand All @@ -1065,6 +1080,20 @@ xfs_create(
xfs_bumplink(tp, dp);
}

/*
* If we have parent pointers, we need to add the attribute containing
* the parent information now.
*/
if (xfs_sb_version_hasparent(&mp->m_sb)) {
xfs_init_parent_name_rec(&rec, dp, diroffset);
args.dp = ip;
args.trans = tp;
args.hashval = xfs_da_hashname(args.name, args.namelen);
error = xfs_attr_set_deferred(&args);
if (error)
goto out_trans_cancel;
}

/*
* If this is a synchronous mount, make sure that the
* create transaction goes to disk before returning to
Expand All @@ -1090,6 +1119,7 @@ xfs_create(

*ipp = ip;
xfs_iunlock(ip, XFS_ILOCK_EXCL);
xfs_iunlock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
return 0;

out_trans_cancel:
Expand Down

0 comments on commit 6f41023

Please sign in to comment.