Re: [PATCH v3 24/26] xfs: Add parent pointer ioctl

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Sep 21, 2022 at 10:44:56PM -0700, allison.henderson@xxxxxxxxxx wrote:
> From: Allison Henderson <allison.henderson@xxxxxxxxxx>
> 
> This patch adds a new file ioctl to retrieve the parent pointer of a
> given inode
> 
> Signed-off-by: Allison Henderson <allison.henderson@xxxxxxxxxx>

To recap from the v2 thread:

Parent pointers are backwards links through the directory tree.  xfsdump
already records the forward links in the dump file.  xfsrestore uses
those forward links to rebuild the directory tree, which recreates the
parent pointers automatically.  Hence we don't need ATTRMULTI to reveal
(or recreate) the parent pointer xattrs; the kernel does that when we
create the directory tree.

The second reason I can think of why we don't want to expose the parent
pointers through the xattr APIs is that we don't want to reveal ondisk
metadata directly to users -- some day we might want to change wthat's
stored on disk, or store them in a totally separate structure, or
whatever.

If we force the interface to be the GETPARENTS ioctl, then we've
decoupled the front and backends.  I conclude that the /only/ userspace
API that should ever touch parent pointers is XFS_IOC_GETPARENTS.

> ---
>  fs/xfs/Makefile            |   1 +
>  fs/xfs/libxfs/xfs_fs.h     |  59 +++++++++++++++++
>  fs/xfs/libxfs/xfs_parent.c |  10 +++
>  fs/xfs/libxfs/xfs_parent.h |   2 +
>  fs/xfs/xfs_ioctl.c         | 106 ++++++++++++++++++++++++++++++-
>  fs/xfs/xfs_ondisk.h        |   4 ++
>  fs/xfs/xfs_parent_utils.c  | 126 +++++++++++++++++++++++++++++++++++++
>  fs/xfs/xfs_parent_utils.h  |  11 ++++
>  8 files changed, 316 insertions(+), 3 deletions(-)
> 
> diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
> index e2b2cf50ffcf..42d0496fdad7 100644
> --- a/fs/xfs/Makefile
> +++ b/fs/xfs/Makefile
> @@ -86,6 +86,7 @@ xfs-y				+= xfs_aops.o \
>  				   xfs_mount.o \
>  				   xfs_mru_cache.o \
>  				   xfs_pwork.o \
> +				   xfs_parent_utils.o \
>  				   xfs_reflink.o \
>  				   xfs_stats.o \
>  				   xfs_super.o \
> diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
> index b0b4d7a3aa15..42bb343f6952 100644
> --- a/fs/xfs/libxfs/xfs_fs.h
> +++ b/fs/xfs/libxfs/xfs_fs.h
> @@ -574,6 +574,7 @@ typedef struct xfs_fsop_handlereq {
>  #define XFS_IOC_ATTR_SECURE	0x0008	/* use attrs in security namespace */
>  #define XFS_IOC_ATTR_CREATE	0x0010	/* fail if attr already exists */
>  #define XFS_IOC_ATTR_REPLACE	0x0020	/* fail if attr does not exist */
> +#define XFS_IOC_ATTR_PARENT	0x0040  /* use attrs in parent namespace */

We definitely don't need this in the userspace API anymore.

>  typedef struct xfs_attrlist_cursor {
>  	__u32		opaque[4];
> @@ -752,6 +753,63 @@ struct xfs_scrub_metadata {
>  				 XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED)
>  #define XFS_SCRUB_FLAGS_ALL	(XFS_SCRUB_FLAGS_IN | XFS_SCRUB_FLAGS_OUT)
>  
> +#define XFS_PPTR_MAXNAMELEN				256
> +
> +/* return parents of the handle, not the open fd */
> +#define XFS_PPTR_IFLAG_HANDLE  (1U << 0)
> +
> +/* target was the root directory */
> +#define XFS_PPTR_OFLAG_ROOT    (1U << 1)
> +
> +/* Cursor is done iterating pptrs */
> +#define XFS_PPTR_OFLAG_DONE    (1U << 2)
> +
> + #define XFS_PPTR_FLAG_ALL     (XFS_PPTR_IFLAG_HANDLE | XFS_PPTR_OFLAG_ROOT | \
> +				XFS_PPTR_OFLAG_DONE)
> +
> +/* Get an inode parent pointer through ioctl */
> +struct xfs_parent_ptr {
> +	__u64		xpp_ino;			/* Inode */
> +	__u32		xpp_gen;			/* Inode generation */
> +	__u32		xpp_diroffset;			/* Directory offset */
> +	__u32		xpp_rsvd;			/* Reserved */
> +	__u32		xpp_pad;

No need for two empty __u32, right?

	__u64		xpp_reserved;

> +	__u8		xpp_name[XFS_PPTR_MAXNAMELEN];	/* File name */
> +};
> +
> +/* Iterate through an inodes parent pointers */
> +struct xfs_pptr_info {

The fields in here ought to have short comments:

/* File handle, if XFS_PPTR_IFLAG_HANDLE is set */
> +	struct xfs_handle		pi_handle;

/*
 * Structure to track progress in iterating the parent pointers.
 * Must be initialized to zeroes before the first ioctl call, and
 * not touched by callers after that.
 */
> +	struct xfs_attrlist_cursor	pi_cursor;

/* Operational flags: XFS_PPTR_*FLAG* */
> +	__u32				pi_flags;

/* Must be set to zero */
> +	__u32				pi_reserved;

/* # of entries in array */
> +	__u32				pi_ptrs_size;

/* # of entries filled in (output) */
> +	__u32				pi_ptrs_used;

/* Must be set to zero */
> +	__u64				pi_reserved2[6];
> +
> +	/*
> +	 * An array of struct xfs_parent_ptr follows the header
> +	 * information. Use XFS_PPINFO_TO_PP() to access the

s/XFS_PPINFO_TO_PP/xfs_ppinfo_to_pp/

> +	 * parent pointer array entries.
> +	 */
> +	struct xfs_parent_ptr		pi_parents[];
> +};
> +
> +static inline size_t
> +xfs_pptr_info_sizeof(int nr_ptrs)
> +{
> +	return sizeof(struct xfs_pptr_info) +
> +	       (nr_ptrs * sizeof(struct xfs_parent_ptr));
> +}
> +
> +static inline struct xfs_parent_ptr*
> +xfs_ppinfo_to_pp(
> +	struct xfs_pptr_info	*info,
> +	int			idx)
> +{
> +	return &info->pi_parents[idx];
> +}
> +
>  /*
>   * ioctl limits
>   */
> @@ -797,6 +855,7 @@ struct xfs_scrub_metadata {
>  /*	XFS_IOC_GETFSMAP ------ hoisted 59         */
>  #define XFS_IOC_SCRUB_METADATA	_IOWR('X', 60, struct xfs_scrub_metadata)
>  #define XFS_IOC_AG_GEOMETRY	_IOWR('X', 61, struct xfs_ag_geometry)
> +#define XFS_IOC_GETPARENTS	_IOWR('X', 62, struct xfs_parent_ptr)
>  
>  /*
>   * ioctl commands that replace IRIX syssgi()'s
> diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c
> index 7db1570e1841..58382a5c40a6 100644
> --- a/fs/xfs/libxfs/xfs_parent.c
> +++ b/fs/xfs/libxfs/xfs_parent.c
> @@ -26,6 +26,16 @@
>  #include "xfs_xattr.h"
>  #include "xfs_parent.h"
>  
> +/* Initializes a xfs_parent_ptr from an xfs_parent_name_rec */
> +void
> +xfs_init_parent_ptr(struct xfs_parent_ptr		*xpp,
> +		    const struct xfs_parent_name_rec	*rec)
> +{
> +	xpp->xpp_ino = be64_to_cpu(rec->p_ino);
> +	xpp->xpp_gen = be32_to_cpu(rec->p_gen);
> +	xpp->xpp_diroffset = be32_to_cpu(rec->p_diroffset);
> +}
> +
>  /*
>   * Parent pointer attribute handling.
>   *
> diff --git a/fs/xfs/libxfs/xfs_parent.h b/fs/xfs/libxfs/xfs_parent.h
> index b2ed4f373799..99765e65af8d 100644
> --- a/fs/xfs/libxfs/xfs_parent.h
> +++ b/fs/xfs/libxfs/xfs_parent.h
> @@ -23,6 +23,8 @@ void xfs_init_parent_name_rec(struct xfs_parent_name_rec *rec,
>  			      uint32_t p_diroffset);
>  void xfs_init_parent_name_irec(struct xfs_parent_name_irec *irec,
>  			       struct xfs_parent_name_rec *rec);
> +void xfs_init_parent_ptr(struct xfs_parent_ptr *xpp,
> +			 const struct xfs_parent_name_rec *rec);
>  int xfs_parent_init(xfs_mount_t *mp, struct xfs_parent_defer **parentp);
>  int xfs_parent_defer_add(struct xfs_trans *tp, struct xfs_parent_defer *parent,
>  			 struct xfs_inode *dp, struct xfs_name *parent_name,
> diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
> index 5b600d3f7981..7dc9f37d96cb 100644
> --- a/fs/xfs/xfs_ioctl.c
> +++ b/fs/xfs/xfs_ioctl.c
> @@ -37,6 +37,7 @@
>  #include "xfs_health.h"
>  #include "xfs_reflink.h"
>  #include "xfs_ioctl.h"
> +#include "xfs_parent_utils.h"
>  #include "xfs_xattr.h"
>  
>  #include <linux/mount.h>
> @@ -355,6 +356,8 @@ xfs_attr_filter(
>  		return XFS_ATTR_ROOT;
>  	if (ioc_flags & XFS_IOC_ATTR_SECURE)
>  		return XFS_ATTR_SECURE;
> +	if (ioc_flags & XFS_IOC_ATTR_PARENT)
> +		return XFS_ATTR_PARENT;
>  	return 0;
>  }
>  
> @@ -422,7 +425,8 @@ xfs_ioc_attr_list(
>  	/*
>  	 * Reject flags, only allow namespaces.
>  	 */
> -	if (flags & ~(XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE))
> +	if (flags & ~(XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE |
> +		      XFS_IOC_ATTR_PARENT))
>  		return -EINVAL;
>  	if (flags == (XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE))
>  		return -EINVAL;
> @@ -538,6 +542,9 @@ xfs_attrmulti_attr_set(
>  	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
>  		return -EPERM;
>  
> +	if (flags & XFS_IOC_ATTR_PARENT)
> +		return -EINVAL;
> +
>  	if (ubuf) {
>  		if (len > XFS_XATTR_SIZE_MAX)
>  			return -EINVAL;
> @@ -567,7 +574,9 @@ xfs_ioc_attrmulti_one(
>  	unsigned char		*name;
>  	int			error;
>  
> -	if ((flags & XFS_IOC_ATTR_ROOT) && (flags & XFS_IOC_ATTR_SECURE))
> +	if (((flags & XFS_IOC_ATTR_ROOT) &&
> +	    ((flags & XFS_IOC_ATTR_SECURE) || (flags & XFS_IOC_ATTR_PARENT))) ||
> +	    ((flags & XFS_IOC_ATTR_SECURE) && (flags & XFS_IOC_ATTR_PARENT)))

All these bits go away since XFS_IOC_ATTR_PARENT is no longer
necessary...

>  		return -EINVAL;
>  
>  	name = strndup_user(uname, MAXNAMELEN);
> @@ -1679,6 +1688,96 @@ xfs_ioc_scrub_metadata(
>  	return 0;
>  }
>  
> +/*
> + * IOCTL routine to get the parent pointers of an inode and return it to user
> + * space.  Caller must pass a buffer space containing a struct xfs_pptr_info,
> + * followed by a region large enough to contain an array of struct
> + * xfs_parent_ptr of a size specified in pi_ptrs_size.  If the inode contains
> + * more parent pointers than can fit in the buffer space, caller may re-call
> + * the function using the returned pi_cursor to resume iteration.  The
> + * number of xfs_parent_ptr returned will be stored in pi_ptrs_used.
> + *
> + * Returns 0 on success or non-zero on failure
> + */
> +STATIC int
> +xfs_ioc_get_parent_pointer(
> +	struct file			*filp,
> +	void				__user *arg)
> +{
> +	struct xfs_pptr_info		*ppi = NULL;
> +	int				error = 0;
> +	struct xfs_inode		*ip = XFS_I(file_inode(filp));
> +	struct xfs_mount		*mp = ip->i_mount;
> +
> +	if (!capable(CAP_SYS_ADMIN))
> +		return -EPERM;
> +
> +	/* Allocate an xfs_pptr_info to put the user data */
> +	ppi = kmalloc(sizeof(struct xfs_pptr_info), 0);
> +	if (!ppi)
> +		return -ENOMEM;
> +
> +	/* Copy the data from the user */
> +	error = copy_from_user(ppi, arg, sizeof(struct xfs_pptr_info));
> +	if (error) {
> +		error = -EFAULT;
> +		goto out;
> +	}
> +
> +	/* Check size of buffer requested by user */
> +	if (xfs_pptr_info_sizeof(ppi->pi_ptrs_size) > XFS_XATTR_LIST_MAX) {
> +		error = -ENOMEM;
> +		goto out;
> +	}
> +
> +	if (ppi->pi_flags & ~XFS_PPTR_FLAG_ALL) {
> +		error = -EINVAL;
> +		goto out;
> +	}
> +	ppi->pi_flags &= ~(XFS_PPTR_OFLAG_ROOT | XFS_PPTR_OFLAG_DONE);
> +
> +	/*
> +	 * Now that we know how big the trailing buffer is, expand
> +	 * our kernel xfs_pptr_info to be the same size
> +	 */
> +	ppi = krealloc(ppi, xfs_pptr_info_sizeof(ppi->pi_ptrs_size), 0);
> +	if (!ppi)
> +		return -ENOMEM;
> +
> +	if (ppi->pi_flags & XFS_PPTR_IFLAG_HANDLE) {
> +		error = xfs_iget(mp, NULL, ppi->pi_handle.ha_fid.fid_ino,
> +				0, 0, &ip);
> +		if (error)
> +			goto out;
> +
> +		if (VFS_I(ip)->i_generation != ppi->pi_handle.ha_fid.fid_gen) {
> +			error = -EINVAL;
> +			goto out;
> +		}
> +	}
> +
> +	if (ip->i_ino == mp->m_sb.sb_rootino)
> +		ppi->pi_flags |= XFS_PPTR_OFLAG_ROOT;
> +
> +	/* Get the parent pointers */
> +	error = xfs_attr_get_parent_pointer(ip, ppi);
> +
> +	if (error)
> +		goto out;
> +
> +	/* Copy the parent pointers back to the user */
> +	error = copy_to_user(arg, ppi,
> +			xfs_pptr_info_sizeof(ppi->pi_ptrs_size));
> +	if (error) {
> +		error = -EFAULT;
> +		goto out;
> +	}
> +
> +out:
> +	kmem_free(ppi);
> +	return error;
> +}
> +
>  int
>  xfs_ioc_swapext(
>  	xfs_swapext_t	*sxp)
> @@ -1968,7 +2067,8 @@ xfs_file_ioctl(
>  
>  	case XFS_IOC_FSGETXATTRA:
>  		return xfs_ioc_fsgetxattra(ip, arg);
> -
> +	case XFS_IOC_GETPARENTS:
> +		return xfs_ioc_get_parent_pointer(filp, arg);
>  	case XFS_IOC_GETBMAP:
>  	case XFS_IOC_GETBMAPA:
>  	case XFS_IOC_GETBMAPX:
> diff --git a/fs/xfs/xfs_ondisk.h b/fs/xfs/xfs_ondisk.h
> index 758702b9495f..765eb514a917 100644
> --- a/fs/xfs/xfs_ondisk.h
> +++ b/fs/xfs/xfs_ondisk.h
> @@ -135,6 +135,10 @@ xfs_check_ondisk_structs(void)
>  	XFS_CHECK_STRUCT_SIZE(struct xfs_attri_log_format,	40);
>  	XFS_CHECK_STRUCT_SIZE(struct xfs_attrd_log_format,	16);
>  
> +	/* parent pointer ioctls */
> +	XFS_CHECK_STRUCT_SIZE(struct xfs_parent_ptr,            280);
> +	XFS_CHECK_STRUCT_SIZE(struct xfs_pptr_info,             104);
> +
>  	/*
>  	 * The v5 superblock format extended several v4 header structures with
>  	 * additional data. While new fields are only accessible on v5
> diff --git a/fs/xfs/xfs_parent_utils.c b/fs/xfs/xfs_parent_utils.c
> new file mode 100644
> index 000000000000..fd7156addd38
> --- /dev/null
> +++ b/fs/xfs/xfs_parent_utils.c
> @@ -0,0 +1,126 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2022 Oracle, Inc.
> + * All rights reserved.
> + */
> +#include "xfs.h"
> +#include "xfs_fs.h"
> +#include "xfs_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_format.h"
> +#include "xfs_da_btree.h"
> +#include "xfs_attr.h"
> +#include "xfs_ioctl.h"
> +#include "xfs_parent.h"
> +#include "xfs_da_btree.h"
> +
> +/*
> + * Get the parent pointers for a given inode
> + *
> + * Returns 0 on success and non zero on error
> + */
> +int
> +xfs_attr_get_parent_pointer(
> +	struct xfs_inode		*ip,
> +	struct xfs_pptr_info		*ppi)
> +{
> +
> +	struct xfs_attrlist		*alist;
> +	struct xfs_attrlist_ent		*aent;
> +	struct xfs_parent_ptr		*xpp;
> +	struct xfs_parent_name_rec	*xpnr;
> +	char				*namebuf;
> +	unsigned int			namebuf_size;
> +	int				name_len, i, error = 0;
> +	unsigned int			ioc_flags = XFS_IOC_ATTR_PARENT;
> +	unsigned int			lock_mode, flags = XFS_ATTR_PARENT;
> +	struct xfs_attr_list_context	context;
> +
> +	/* Allocate a buffer to store the attribute names */
> +	namebuf_size = sizeof(struct xfs_attrlist) +
> +		       (ppi->pi_ptrs_size) * sizeof(struct xfs_attrlist_ent);
> +	namebuf = kvzalloc(namebuf_size, GFP_KERNEL);
> +	if (!namebuf)
> +		return -ENOMEM;
> +
> +	memset(&context, 0, sizeof(struct xfs_attr_list_context));
> +	error = xfs_ioc_attr_list_context_init(ip, namebuf, namebuf_size,
> +			ioc_flags, &context);
> +	if (error)
> +		goto out_kfree;

And now that we don't have XFS_IOC_ATTR_PARENT anymore, change this to:

	memset(&context, 0, sizeof(struct xfs_attr_list_context));
	error = xfs_ioc_attr_list_context_init(ip, namebuf,
			namebuf_size, 0, &context);
	if (error)
		goto out_kfree;
	context.attr_flags = XFS_ATTR_PARENT;

This way you can reuse the xfs_attr_list* infrastructure without needing
to add flags to the userspace xattr APIs or then have to filter that out
from all the incoming xattr calls.

> +
> +	/* Copy the cursor provided by caller */
> +	memcpy(&context.cursor, &ppi->pi_cursor,
> +		sizeof(struct xfs_attrlist_cursor));
> +	context.attr_filter = XFS_ATTR_PARENT;
> +
> +	lock_mode = xfs_ilock_attr_map_shared(ip);
> +
> +	error = xfs_attr_list_ilocked(&context);
> +	if (error)
> +		goto out_kfree;
> +
> +	alist = (struct xfs_attrlist *)namebuf;
> +	for (i = 0; i < alist->al_count; i++) {
> +		struct xfs_da_args args = {
> +			.geo = ip->i_mount->m_attr_geo,
> +			.whichfork = XFS_ATTR_FORK,
> +			.dp = ip,
> +			.namelen = sizeof(struct xfs_parent_name_rec),
> +			.attr_filter = flags,
> +			.op_flags = XFS_DA_OP_OKNOENT,

We hold the ILOCK between the list and the attr getting, so we shouldn't
need OKNOENT here to avoid error returns, right?

> +		};
> +
> +		xpp = xfs_ppinfo_to_pp(ppi, i);
> +		memset(xpp, 0, sizeof(struct xfs_parent_ptr));
> +		aent = (struct xfs_attrlist_ent *)
> +			&namebuf[alist->al_offset[i]];
> +		xpnr = (struct xfs_parent_name_rec *)(aent->a_name);
> +
> +		if (aent->a_valuelen > XFS_PPTR_MAXNAMELEN) {
> +			error = -EFSCORRUPTED;
> +			goto out_kfree;
> +		}
> +		name_len = aent->a_valuelen;
> +
> +		args.name = (char *)xpnr;
> +		args.hashval = xfs_da_hashname(args.name, args.namelen),
> +		args.value = (unsigned char *)(xpp->xpp_name);
> +		args.valuelen = name_len;
> +
> +		error = xfs_attr_get_ilocked(&args);
> +		error = (error == -EEXIST ? 0 : error);
> +		if (error) {
> +			error = -EFSCORRUPTED;
> +			goto out_kfree;
> +		}
> +
> +		xfs_init_parent_ptr(xpp, xpnr);
> +		if(!xfs_verify_ino(args.dp->i_mount, xpp->xpp_ino)) {

Space before the '('

> +			error = -EFSCORRUPTED;
> +			goto out_kfree;
> +		}
> +	}
> +	ppi->pi_ptrs_used = alist->al_count;
> +	if (!alist->al_more)
> +		ppi->pi_flags |= XFS_PPTR_OFLAG_DONE;
> +
> +	/* Update the caller with the current cursor position */
> +	memcpy(&ppi->pi_cursor, &context.cursor,
> +		sizeof(struct xfs_attrlist_cursor));

Two tabs for the continuation line.

--D

> +
> +out_kfree:
> +	xfs_iunlock(ip, lock_mode);
> +	kvfree(namebuf);
> +
> +	return error;
> +}
> +
> diff --git a/fs/xfs/xfs_parent_utils.h b/fs/xfs/xfs_parent_utils.h
> new file mode 100644
> index 000000000000..ad60baee8b2a
> --- /dev/null
> +++ b/fs/xfs/xfs_parent_utils.h
> @@ -0,0 +1,11 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2022 Oracle, Inc.
> + * All rights reserved.
> + */
> +#ifndef	__XFS_PARENT_UTILS_H__
> +#define	__XFS_PARENT_UTILS_H__
> +
> +int xfs_attr_get_parent_pointer(struct xfs_inode *ip,
> +				struct xfs_pptr_info *ppi);
> +#endif	/* __XFS_PARENT_UTILS_H__ */
> -- 
> 2.25.1
> 



[Index of Archives]     [XFS Filesystem Development (older mail)]     [Linux Filesystem Development]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux RAID]     [Linux SCSI]


  Powered by Linux