mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
xfs: add parent pointer ioctls
This patch adds a pair of new file ioctls to retrieve the parent pointer of a given inode. They both return the same results, but one operates on the file descriptor passed to ioctl() whereas the other allows the caller to specify a file handle for which the caller wants results. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
parent
b8c9d4253d
commit
233f4e12bb
@ -816,6 +816,78 @@ struct xfs_exchange_range {
|
|||||||
XFS_EXCHANGE_RANGE_DRY_RUN | \
|
XFS_EXCHANGE_RANGE_DRY_RUN | \
|
||||||
XFS_EXCHANGE_RANGE_FILE1_WRITTEN)
|
XFS_EXCHANGE_RANGE_FILE1_WRITTEN)
|
||||||
|
|
||||||
|
/* Iterating parent pointers of files. */
|
||||||
|
|
||||||
|
/* target was the root directory */
|
||||||
|
#define XFS_GETPARENTS_OFLAG_ROOT (1U << 0)
|
||||||
|
|
||||||
|
/* Cursor is done iterating pptrs */
|
||||||
|
#define XFS_GETPARENTS_OFLAG_DONE (1U << 1)
|
||||||
|
|
||||||
|
#define XFS_GETPARENTS_OFLAGS_ALL (XFS_GETPARENTS_OFLAG_ROOT | \
|
||||||
|
XFS_GETPARENTS_OFLAG_DONE)
|
||||||
|
|
||||||
|
#define XFS_GETPARENTS_IFLAGS_ALL (0)
|
||||||
|
|
||||||
|
struct xfs_getparents_rec {
|
||||||
|
struct xfs_handle gpr_parent; /* Handle to parent */
|
||||||
|
__u32 gpr_reclen; /* Length of entire record */
|
||||||
|
__u32 gpr_reserved; /* zero */
|
||||||
|
char gpr_name[]; /* Null-terminated filename */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Iterate through this file's directory parent pointers */
|
||||||
|
struct xfs_getparents {
|
||||||
|
/*
|
||||||
|
* 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 gp_cursor;
|
||||||
|
|
||||||
|
/* Input flags: XFS_GETPARENTS_IFLAG* */
|
||||||
|
__u16 gp_iflags;
|
||||||
|
|
||||||
|
/* Output flags: XFS_GETPARENTS_OFLAG* */
|
||||||
|
__u16 gp_oflags;
|
||||||
|
|
||||||
|
/* Size of the gp_buffer in bytes */
|
||||||
|
__u32 gp_bufsize;
|
||||||
|
|
||||||
|
/* Must be set to zero */
|
||||||
|
__u64 gp_reserved;
|
||||||
|
|
||||||
|
/* Pointer to a buffer in which to place xfs_getparents_rec */
|
||||||
|
__u64 gp_buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct xfs_getparents_rec *
|
||||||
|
xfs_getparents_first_rec(struct xfs_getparents *gp)
|
||||||
|
{
|
||||||
|
return (struct xfs_getparents_rec *)(uintptr_t)gp->gp_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct xfs_getparents_rec *
|
||||||
|
xfs_getparents_next_rec(struct xfs_getparents *gp,
|
||||||
|
struct xfs_getparents_rec *gpr)
|
||||||
|
{
|
||||||
|
void *next = ((void *)gpr + gpr->gpr_reclen);
|
||||||
|
void *end = (void *)(uintptr_t)(gp->gp_buffer + gp->gp_bufsize);
|
||||||
|
|
||||||
|
if (next >= end)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Iterate through this file handle's directory parent pointers. */
|
||||||
|
struct xfs_getparents_by_handle {
|
||||||
|
/* Handle to file whose parents we want. */
|
||||||
|
struct xfs_handle gph_handle;
|
||||||
|
|
||||||
|
struct xfs_getparents gph_request;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ioctl commands that are used by Linux filesystems
|
* ioctl commands that are used by Linux filesystems
|
||||||
*/
|
*/
|
||||||
@ -851,6 +923,8 @@ struct xfs_exchange_range {
|
|||||||
/* XFS_IOC_GETFSMAP ------ hoisted 59 */
|
/* XFS_IOC_GETFSMAP ------ hoisted 59 */
|
||||||
#define XFS_IOC_SCRUB_METADATA _IOWR('X', 60, struct xfs_scrub_metadata)
|
#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_AG_GEOMETRY _IOWR('X', 61, struct xfs_ag_geometry)
|
||||||
|
#define XFS_IOC_GETPARENTS _IOWR('X', 62, struct xfs_getparents)
|
||||||
|
#define XFS_IOC_GETPARENTS_BY_HANDLE _IOWR('X', 63, struct xfs_getparents_by_handle)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ioctl commands that replace IRIX syssgi()'s
|
* ioctl commands that replace IRIX syssgi()'s
|
||||||
|
@ -156,6 +156,11 @@ xfs_check_ondisk_structs(void)
|
|||||||
XFS_CHECK_OFFSET(struct xfs_efi_log_format_32, efi_extents, 16);
|
XFS_CHECK_OFFSET(struct xfs_efi_log_format_32, efi_extents, 16);
|
||||||
XFS_CHECK_OFFSET(struct xfs_efi_log_format_64, efi_extents, 16);
|
XFS_CHECK_OFFSET(struct xfs_efi_log_format_64, efi_extents, 16);
|
||||||
|
|
||||||
|
/* parent pointer ioctls */
|
||||||
|
XFS_CHECK_STRUCT_SIZE(struct xfs_getparents_rec, 32);
|
||||||
|
XFS_CHECK_STRUCT_SIZE(struct xfs_getparents, 40);
|
||||||
|
XFS_CHECK_STRUCT_SIZE(struct xfs_getparents_by_handle, 64);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The v5 superblock format extended several v4 header structures with
|
* The v5 superblock format extended several v4 header structures with
|
||||||
* additional data. While new fields are only accessible on v5
|
* additional data. While new fields are only accessible on v5
|
||||||
|
@ -257,3 +257,37 @@ xfs_parent_replacename(
|
|||||||
xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REPLACE);
|
xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REPLACE);
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
@ -91,4 +91,9 @@ int xfs_parent_replacename(struct xfs_trans *tp,
|
|||||||
struct xfs_inode *new_dp, const struct xfs_name *new_name,
|
struct xfs_inode *new_dp, const struct xfs_name *new_name,
|
||||||
struct xfs_inode *child);
|
struct xfs_inode *child);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
#endif /* __XFS_PARENT_H__ */
|
#endif /* __XFS_PARENT_H__ */
|
||||||
|
@ -102,7 +102,7 @@ xfs_fs_encode_fh(
|
|||||||
return fileid_type;
|
return fileid_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC struct inode *
|
struct inode *
|
||||||
xfs_nfs_get_inode(
|
xfs_nfs_get_inode(
|
||||||
struct super_block *sb,
|
struct super_block *sb,
|
||||||
u64 ino,
|
u64 ino,
|
||||||
|
@ -57,4 +57,6 @@ struct xfs_fid64 {
|
|||||||
/* This flag goes on the wire. Don't play with it. */
|
/* This flag goes on the wire. Don't play with it. */
|
||||||
#define XFS_FILEID_TYPE_64FLAG 0x80 /* NFS fileid has 64bit inodes */
|
#define XFS_FILEID_TYPE_64FLAG 0x80 /* NFS fileid has 64bit inodes */
|
||||||
|
|
||||||
|
struct inode *xfs_nfs_get_inode(struct super_block *sb, u64 ino, u32 gen);
|
||||||
|
|
||||||
#endif /* __XFS_EXPORT_H__ */
|
#endif /* __XFS_EXPORT_H__ */
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
|
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
|
||||||
|
* Copyright (c) 2022-2024 Oracle.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*/
|
*/
|
||||||
#include "xfs.h"
|
#include "xfs.h"
|
||||||
@ -178,6 +179,30 @@ xfs_khandle_to_dentry(
|
|||||||
xfs_handle_acceptable, NULL);
|
xfs_handle_acceptable, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Convert handle already copied to kernel space into an xfs_inode. */
|
||||||
|
static struct xfs_inode *
|
||||||
|
xfs_khandle_to_inode(
|
||||||
|
struct file *file,
|
||||||
|
struct xfs_handle *handle)
|
||||||
|
{
|
||||||
|
struct xfs_inode *ip = XFS_I(file_inode(file));
|
||||||
|
struct xfs_mount *mp = ip->i_mount;
|
||||||
|
struct inode *inode;
|
||||||
|
|
||||||
|
if (!S_ISDIR(VFS_I(ip)->i_mode))
|
||||||
|
return ERR_PTR(-ENOTDIR);
|
||||||
|
|
||||||
|
if (handle->ha_fid.fid_len != xfs_filehandle_fid_len())
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
inode = xfs_nfs_get_inode(mp->m_super, handle->ha_fid.fid_ino,
|
||||||
|
handle->ha_fid.fid_gen);
|
||||||
|
if (IS_ERR(inode))
|
||||||
|
return ERR_CAST(inode);
|
||||||
|
|
||||||
|
return XFS_I(inode);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert userspace handle data into a dentry.
|
* Convert userspace handle data into a dentry.
|
||||||
*/
|
*/
|
||||||
@ -652,3 +677,276 @@ xfs_attrmulti_by_handle(
|
|||||||
dput(dentry);
|
dput(dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct xfs_getparents_ctx {
|
||||||
|
struct xfs_attr_list_context context;
|
||||||
|
struct xfs_getparents_by_handle gph;
|
||||||
|
|
||||||
|
/* File to target */
|
||||||
|
struct xfs_inode *ip;
|
||||||
|
|
||||||
|
/* Internal buffer where we format records */
|
||||||
|
void *krecords;
|
||||||
|
|
||||||
|
/* Last record filled out */
|
||||||
|
struct xfs_getparents_rec *lastrec;
|
||||||
|
|
||||||
|
unsigned int count;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline unsigned int
|
||||||
|
xfs_getparents_rec_sizeof(
|
||||||
|
unsigned int namelen)
|
||||||
|
{
|
||||||
|
return round_up(sizeof(struct xfs_getparents_rec) + namelen + 1,
|
||||||
|
sizeof(uint64_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xfs_getparents_put_listent(
|
||||||
|
struct xfs_attr_list_context *context,
|
||||||
|
int flags,
|
||||||
|
unsigned char *name,
|
||||||
|
int namelen,
|
||||||
|
void *value,
|
||||||
|
int valuelen)
|
||||||
|
{
|
||||||
|
struct xfs_getparents_ctx *gpx =
|
||||||
|
container_of(context, struct xfs_getparents_ctx, context);
|
||||||
|
struct xfs_inode *ip = context->dp;
|
||||||
|
struct xfs_mount *mp = ip->i_mount;
|
||||||
|
struct xfs_getparents *gp = &gpx->gph.gph_request;
|
||||||
|
struct xfs_getparents_rec *gpr = gpx->krecords + context->firstu;
|
||||||
|
unsigned short reclen =
|
||||||
|
xfs_getparents_rec_sizeof(namelen);
|
||||||
|
xfs_ino_t ino;
|
||||||
|
uint32_t gen;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (!(flags & XFS_ATTR_PARENT))
|
||||||
|
return;
|
||||||
|
|
||||||
|
error = xfs_parent_from_attr(mp, flags, name, namelen, value, valuelen,
|
||||||
|
&ino, &gen);
|
||||||
|
if (error) {
|
||||||
|
xfs_inode_mark_sick(ip, XFS_SICK_INO_PARENT);
|
||||||
|
context->seen_enough = -EFSCORRUPTED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We found a parent pointer, but we've filled up the buffer. Signal
|
||||||
|
* to the caller that we did /not/ reach the end of the parent pointer
|
||||||
|
* recordset.
|
||||||
|
*/
|
||||||
|
if (context->firstu > context->bufsize - reclen) {
|
||||||
|
context->seen_enough = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format the parent pointer directly into the caller buffer. */
|
||||||
|
gpr->gpr_reclen = reclen;
|
||||||
|
xfs_filehandle_init(mp, ino, gen, &gpr->gpr_parent);
|
||||||
|
memcpy(gpr->gpr_name, name, namelen);
|
||||||
|
gpr->gpr_name[namelen] = 0;
|
||||||
|
|
||||||
|
trace_xfs_getparents_put_listent(ip, gp, context, gpr);
|
||||||
|
|
||||||
|
context->firstu += reclen;
|
||||||
|
gpx->count++;
|
||||||
|
gpx->lastrec = gpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Expand the last record to fill the rest of the caller's buffer. */
|
||||||
|
static inline void
|
||||||
|
xfs_getparents_expand_lastrec(
|
||||||
|
struct xfs_getparents_ctx *gpx)
|
||||||
|
{
|
||||||
|
struct xfs_getparents *gp = &gpx->gph.gph_request;
|
||||||
|
struct xfs_getparents_rec *gpr = gpx->lastrec;
|
||||||
|
|
||||||
|
if (!gpx->lastrec)
|
||||||
|
gpr = gpx->krecords;
|
||||||
|
|
||||||
|
gpr->gpr_reclen = gp->gp_bufsize - ((void *)gpr - gpx->krecords);
|
||||||
|
|
||||||
|
trace_xfs_getparents_expand_lastrec(gpx->ip, gp, &gpx->context, gpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void __user *u64_to_uptr(u64 val)
|
||||||
|
{
|
||||||
|
return (void __user *)(uintptr_t)val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retrieve the parent pointers for a given inode. */
|
||||||
|
STATIC int
|
||||||
|
xfs_getparents(
|
||||||
|
struct xfs_getparents_ctx *gpx)
|
||||||
|
{
|
||||||
|
struct xfs_getparents *gp = &gpx->gph.gph_request;
|
||||||
|
struct xfs_inode *ip = gpx->ip;
|
||||||
|
struct xfs_mount *mp = ip->i_mount;
|
||||||
|
size_t bufsize;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
/* Check size of buffer requested by user */
|
||||||
|
if (gp->gp_bufsize > XFS_XATTR_LIST_MAX)
|
||||||
|
return -ENOMEM;
|
||||||
|
if (gp->gp_bufsize < xfs_getparents_rec_sizeof(1))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (gp->gp_iflags & ~XFS_GETPARENTS_IFLAGS_ALL)
|
||||||
|
return -EINVAL;
|
||||||
|
if (gp->gp_reserved)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
bufsize = round_down(gp->gp_bufsize, sizeof(uint64_t));
|
||||||
|
gpx->krecords = kvzalloc(bufsize, GFP_KERNEL);
|
||||||
|
if (!gpx->krecords) {
|
||||||
|
bufsize = min(bufsize, PAGE_SIZE);
|
||||||
|
gpx->krecords = kvzalloc(bufsize, GFP_KERNEL);
|
||||||
|
if (!gpx->krecords)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpx->context.dp = ip;
|
||||||
|
gpx->context.resynch = 1;
|
||||||
|
gpx->context.put_listent = xfs_getparents_put_listent;
|
||||||
|
gpx->context.bufsize = bufsize;
|
||||||
|
/* firstu is used to track the bytes filled in the buffer */
|
||||||
|
gpx->context.firstu = 0;
|
||||||
|
|
||||||
|
/* Copy the cursor provided by caller */
|
||||||
|
memcpy(&gpx->context.cursor, &gp->gp_cursor,
|
||||||
|
sizeof(struct xfs_attrlist_cursor));
|
||||||
|
gpx->count = 0;
|
||||||
|
gp->gp_oflags = 0;
|
||||||
|
|
||||||
|
trace_xfs_getparents_begin(ip, gp, &gpx->context.cursor);
|
||||||
|
|
||||||
|
error = xfs_attr_list(&gpx->context);
|
||||||
|
if (error)
|
||||||
|
goto out_free_buf;
|
||||||
|
if (gpx->context.seen_enough < 0) {
|
||||||
|
error = gpx->context.seen_enough;
|
||||||
|
goto out_free_buf;
|
||||||
|
}
|
||||||
|
xfs_getparents_expand_lastrec(gpx);
|
||||||
|
|
||||||
|
/* Update the caller with the current cursor position */
|
||||||
|
memcpy(&gp->gp_cursor, &gpx->context.cursor,
|
||||||
|
sizeof(struct xfs_attrlist_cursor));
|
||||||
|
|
||||||
|
/* Is this the root directory? */
|
||||||
|
if (ip->i_ino == mp->m_sb.sb_rootino)
|
||||||
|
gp->gp_oflags |= XFS_GETPARENTS_OFLAG_ROOT;
|
||||||
|
|
||||||
|
if (gpx->context.seen_enough == 0) {
|
||||||
|
/*
|
||||||
|
* If we did not run out of buffer space, then we reached the
|
||||||
|
* end of the pptr recordset, so set the DONE flag.
|
||||||
|
*/
|
||||||
|
gp->gp_oflags |= XFS_GETPARENTS_OFLAG_DONE;
|
||||||
|
} else if (gpx->count == 0) {
|
||||||
|
/*
|
||||||
|
* If we ran out of buffer space before copying any parent
|
||||||
|
* pointers at all, the caller's buffer was too short. Tell
|
||||||
|
* userspace that, erm, the message is too long.
|
||||||
|
*/
|
||||||
|
error = -EMSGSIZE;
|
||||||
|
goto out_free_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_xfs_getparents_end(ip, gp, &gpx->context.cursor);
|
||||||
|
|
||||||
|
ASSERT(gpx->context.firstu <= gpx->gph.gph_request.gp_bufsize);
|
||||||
|
|
||||||
|
/* Copy the records to userspace. */
|
||||||
|
if (copy_to_user(u64_to_uptr(gpx->gph.gph_request.gp_buffer),
|
||||||
|
gpx->krecords, gpx->context.firstu))
|
||||||
|
error = -EFAULT;
|
||||||
|
|
||||||
|
out_free_buf:
|
||||||
|
kvfree(gpx->krecords);
|
||||||
|
gpx->krecords = NULL;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retrieve the parents of this file and pass them back to userspace. */
|
||||||
|
int
|
||||||
|
xfs_ioc_getparents(
|
||||||
|
struct file *file,
|
||||||
|
struct xfs_getparents __user *ureq)
|
||||||
|
{
|
||||||
|
struct xfs_getparents_ctx gpx = {
|
||||||
|
.ip = XFS_I(file_inode(file)),
|
||||||
|
};
|
||||||
|
struct xfs_getparents *kreq = &gpx.gph.gph_request;
|
||||||
|
struct xfs_mount *mp = gpx.ip->i_mount;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (!capable(CAP_SYS_ADMIN))
|
||||||
|
return -EPERM;
|
||||||
|
if (!xfs_has_parent(mp))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
if (copy_from_user(kreq, ureq, sizeof(*kreq)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
error = xfs_getparents(&gpx);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
if (copy_to_user(ureq, kreq, sizeof(*kreq)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retrieve the parents of this file handle and pass them back to userspace. */
|
||||||
|
int
|
||||||
|
xfs_ioc_getparents_by_handle(
|
||||||
|
struct file *file,
|
||||||
|
struct xfs_getparents_by_handle __user *ureq)
|
||||||
|
{
|
||||||
|
struct xfs_getparents_ctx gpx = { };
|
||||||
|
struct xfs_inode *ip = XFS_I(file_inode(file));
|
||||||
|
struct xfs_mount *mp = ip->i_mount;
|
||||||
|
struct xfs_getparents_by_handle *kreq = &gpx.gph;
|
||||||
|
struct xfs_handle *handle = &kreq->gph_handle;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (!capable(CAP_SYS_ADMIN))
|
||||||
|
return -EPERM;
|
||||||
|
if (!xfs_has_parent(mp))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
if (copy_from_user(kreq, ureq, sizeof(*kreq)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't use exportfs_decode_fh because it does too much work here.
|
||||||
|
* If the handle refers to a directory, the exportfs code will walk
|
||||||
|
* upwards through the directory tree to connect the dentries to the
|
||||||
|
* root directory dentry. For GETPARENTS we don't care about that
|
||||||
|
* because we're not actually going to open a file descriptor; we only
|
||||||
|
* want to open an inode and read its parent pointers.
|
||||||
|
*
|
||||||
|
* Note that xfs_scrub uses GETPARENTS to log that it will try to fix a
|
||||||
|
* corrupted file's metadata. For this usecase we would really rather
|
||||||
|
* userspace single-step the path reconstruction to avoid loops or
|
||||||
|
* other strange things if the directory tree is corrupt.
|
||||||
|
*/
|
||||||
|
gpx.ip = xfs_khandle_to_inode(file, handle);
|
||||||
|
if (IS_ERR(gpx.ip))
|
||||||
|
return PTR_ERR(gpx.ip);
|
||||||
|
|
||||||
|
error = xfs_getparents(&gpx);
|
||||||
|
if (error)
|
||||||
|
goto out_rele;
|
||||||
|
|
||||||
|
if (copy_to_user(ureq, kreq, sizeof(*kreq)))
|
||||||
|
error = -EFAULT;
|
||||||
|
|
||||||
|
out_rele:
|
||||||
|
xfs_irele(gpx.ip);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
|
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
|
||||||
|
* Copyright (c) 2022-2024 Oracle.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*/
|
*/
|
||||||
#ifndef __XFS_HANDLE_H__
|
#ifndef __XFS_HANDLE_H__
|
||||||
@ -25,4 +26,8 @@ int xfs_ioc_attr_list(struct xfs_inode *dp, void __user *ubuf,
|
|||||||
struct dentry *xfs_handle_to_dentry(struct file *parfilp, void __user *uhandle,
|
struct dentry *xfs_handle_to_dentry(struct file *parfilp, void __user *uhandle,
|
||||||
u32 hlen);
|
u32 hlen);
|
||||||
|
|
||||||
|
int xfs_ioc_getparents(struct file *file, struct xfs_getparents __user *arg);
|
||||||
|
int xfs_ioc_getparents_by_handle(struct file *file,
|
||||||
|
struct xfs_getparents_by_handle __user *arg);
|
||||||
|
|
||||||
#endif /* __XFS_HANDLE_H__ */
|
#endif /* __XFS_HANDLE_H__ */
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include "xfs_health.h"
|
#include "xfs_health.h"
|
||||||
#include "xfs_reflink.h"
|
#include "xfs_reflink.h"
|
||||||
#include "xfs_ioctl.h"
|
#include "xfs_ioctl.h"
|
||||||
|
#include "xfs_xattr.h"
|
||||||
#include "xfs_rtbitmap.h"
|
#include "xfs_rtbitmap.h"
|
||||||
#include "xfs_file.h"
|
#include "xfs_file.h"
|
||||||
#include "xfs_exchrange.h"
|
#include "xfs_exchrange.h"
|
||||||
@ -1424,7 +1425,10 @@ xfs_file_ioctl(
|
|||||||
|
|
||||||
case XFS_IOC_FSGETXATTRA:
|
case XFS_IOC_FSGETXATTRA:
|
||||||
return xfs_ioc_fsgetxattra(ip, arg);
|
return xfs_ioc_fsgetxattra(ip, arg);
|
||||||
|
case XFS_IOC_GETPARENTS:
|
||||||
|
return xfs_ioc_getparents(filp, arg);
|
||||||
|
case XFS_IOC_GETPARENTS_BY_HANDLE:
|
||||||
|
return xfs_ioc_getparents_by_handle(filp, arg);
|
||||||
case XFS_IOC_GETBMAP:
|
case XFS_IOC_GETBMAP:
|
||||||
case XFS_IOC_GETBMAPA:
|
case XFS_IOC_GETBMAPA:
|
||||||
case XFS_IOC_GETBMAPX:
|
case XFS_IOC_GETBMAPX:
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
#include "xfs_bmap.h"
|
#include "xfs_bmap.h"
|
||||||
#include "xfs_exchmaps.h"
|
#include "xfs_exchmaps.h"
|
||||||
#include "xfs_exchrange.h"
|
#include "xfs_exchrange.h"
|
||||||
|
#include "xfs_parent.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We include this last to have the helpers above available for the trace
|
* We include this last to have the helpers above available for the trace
|
||||||
|
@ -87,6 +87,9 @@ struct xfs_bmap_intent;
|
|||||||
struct xfs_exchmaps_intent;
|
struct xfs_exchmaps_intent;
|
||||||
struct xfs_exchmaps_req;
|
struct xfs_exchmaps_req;
|
||||||
struct xfs_exchrange;
|
struct xfs_exchrange;
|
||||||
|
struct xfs_getparents;
|
||||||
|
struct xfs_parent_irec;
|
||||||
|
struct xfs_attrlist_cursor_kern;
|
||||||
|
|
||||||
#define XFS_ATTR_FILTER_FLAGS \
|
#define XFS_ATTR_FILTER_FLAGS \
|
||||||
{ XFS_ATTR_ROOT, "ROOT" }, \
|
{ XFS_ATTR_ROOT, "ROOT" }, \
|
||||||
@ -5096,6 +5099,95 @@ TRACE_EVENT(xfs_exchmaps_delta_nextents,
|
|||||||
__entry->d_nexts1, __entry->d_nexts2)
|
__entry->d_nexts1, __entry->d_nexts2)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
DECLARE_EVENT_CLASS(xfs_getparents_rec_class,
|
||||||
|
TP_PROTO(struct xfs_inode *ip, const struct xfs_getparents *ppi,
|
||||||
|
const struct xfs_attr_list_context *context,
|
||||||
|
const struct xfs_getparents_rec *pptr),
|
||||||
|
TP_ARGS(ip, ppi, context, pptr),
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field(dev_t, dev)
|
||||||
|
__field(xfs_ino_t, ino)
|
||||||
|
__field(unsigned int, firstu)
|
||||||
|
__field(unsigned short, reclen)
|
||||||
|
__field(unsigned int, bufsize)
|
||||||
|
__field(xfs_ino_t, parent_ino)
|
||||||
|
__field(unsigned int, parent_gen)
|
||||||
|
__string(name, pptr->gpr_name)
|
||||||
|
),
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->dev = ip->i_mount->m_super->s_dev;
|
||||||
|
__entry->ino = ip->i_ino;
|
||||||
|
__entry->firstu = context->firstu;
|
||||||
|
__entry->reclen = pptr->gpr_reclen;
|
||||||
|
__entry->bufsize = ppi->gp_bufsize;
|
||||||
|
__entry->parent_ino = pptr->gpr_parent.ha_fid.fid_ino;
|
||||||
|
__entry->parent_gen = pptr->gpr_parent.ha_fid.fid_gen;
|
||||||
|
__assign_str(name, pptr->gpr_name);
|
||||||
|
),
|
||||||
|
TP_printk("dev %d:%d ino 0x%llx firstu %u reclen %u bufsize %u parent_ino 0x%llx parent_gen 0x%x name '%s'",
|
||||||
|
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||||
|
__entry->ino,
|
||||||
|
__entry->firstu,
|
||||||
|
__entry->reclen,
|
||||||
|
__entry->bufsize,
|
||||||
|
__entry->parent_ino,
|
||||||
|
__entry->parent_gen,
|
||||||
|
__get_str(name))
|
||||||
|
)
|
||||||
|
#define DEFINE_XFS_GETPARENTS_REC_EVENT(name) \
|
||||||
|
DEFINE_EVENT(xfs_getparents_rec_class, name, \
|
||||||
|
TP_PROTO(struct xfs_inode *ip, const struct xfs_getparents *ppi, \
|
||||||
|
const struct xfs_attr_list_context *context, \
|
||||||
|
const struct xfs_getparents_rec *pptr), \
|
||||||
|
TP_ARGS(ip, ppi, context, pptr))
|
||||||
|
DEFINE_XFS_GETPARENTS_REC_EVENT(xfs_getparents_put_listent);
|
||||||
|
DEFINE_XFS_GETPARENTS_REC_EVENT(xfs_getparents_expand_lastrec);
|
||||||
|
|
||||||
|
DECLARE_EVENT_CLASS(xfs_getparents_class,
|
||||||
|
TP_PROTO(struct xfs_inode *ip, const struct xfs_getparents *ppi,
|
||||||
|
const struct xfs_attrlist_cursor_kern *cur),
|
||||||
|
TP_ARGS(ip, ppi, cur),
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field(dev_t, dev)
|
||||||
|
__field(xfs_ino_t, ino)
|
||||||
|
__field(unsigned short, iflags)
|
||||||
|
__field(unsigned short, oflags)
|
||||||
|
__field(unsigned int, bufsize)
|
||||||
|
__field(unsigned int, hashval)
|
||||||
|
__field(unsigned int, blkno)
|
||||||
|
__field(unsigned int, offset)
|
||||||
|
__field(int, initted)
|
||||||
|
),
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->dev = ip->i_mount->m_super->s_dev;
|
||||||
|
__entry->ino = ip->i_ino;
|
||||||
|
__entry->iflags = ppi->gp_iflags;
|
||||||
|
__entry->oflags = ppi->gp_oflags;
|
||||||
|
__entry->bufsize = ppi->gp_bufsize;
|
||||||
|
__entry->hashval = cur->hashval;
|
||||||
|
__entry->blkno = cur->blkno;
|
||||||
|
__entry->offset = cur->offset;
|
||||||
|
__entry->initted = cur->initted;
|
||||||
|
),
|
||||||
|
TP_printk("dev %d:%d ino 0x%llx iflags 0x%x oflags 0x%x bufsize %u cur_init? %d hashval 0x%x blkno %u offset %u",
|
||||||
|
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||||
|
__entry->ino,
|
||||||
|
__entry->iflags,
|
||||||
|
__entry->oflags,
|
||||||
|
__entry->bufsize,
|
||||||
|
__entry->initted,
|
||||||
|
__entry->hashval,
|
||||||
|
__entry->blkno,
|
||||||
|
__entry->offset)
|
||||||
|
)
|
||||||
|
#define DEFINE_XFS_GETPARENTS_EVENT(name) \
|
||||||
|
DEFINE_EVENT(xfs_getparents_class, name, \
|
||||||
|
TP_PROTO(struct xfs_inode *ip, const struct xfs_getparents *ppi, \
|
||||||
|
const struct xfs_attrlist_cursor_kern *cur), \
|
||||||
|
TP_ARGS(ip, ppi, cur))
|
||||||
|
DEFINE_XFS_GETPARENTS_EVENT(xfs_getparents_begin);
|
||||||
|
DEFINE_XFS_GETPARENTS_EVENT(xfs_getparents_end);
|
||||||
|
|
||||||
#endif /* _TRACE_XFS_H */
|
#endif /* _TRACE_XFS_H */
|
||||||
|
|
||||||
#undef TRACE_INCLUDE_PATH
|
#undef TRACE_INCLUDE_PATH
|
||||||
|
Loading…
Reference in New Issue
Block a user