From: Darrick J. Wong Date: Thu, 4 Apr 2024 23:35:42 +0000 (-0700) Subject: xfs: Add parent pointer ioctls X-Git-Tag: xfs-zoned-old-2024-06-10~367 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=bbf91851deba8aaf721fb8c6a3fd1010be54b84a;p=users%2Fhch%2Fxfsprogs.git 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: Allison Henderson Reviewed-by: Darrick J. Wong [djwong: adjust to new ondisk format, split ioctls] Signed-off-by: Darrick J. Wong --- diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h index 7486dcba8..fa28c18e5 100644 --- a/libxfs/xfs_fs.h +++ b/libxfs/xfs_fs.h @@ -633,7 +633,9 @@ typedef struct xfs_fsop_attrmulti_handlereq { /* * per machine unique filesystem identifier types. */ -typedef struct { __u32 val[2]; } xfs_fsid_t; /* file system id type */ +typedef struct xfs_fsid { + __u32 val[2]; /* file system id type */ +} xfs_fsid_t; typedef struct xfs_fid { __u16 fid_len; /* length of remainder */ @@ -838,6 +840,77 @@ struct xfs_commit_range { XFS_EXCHANGE_RANGE_DRY_RUN | \ 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 */ + __u16 gpr_reclen; /* Length of entire record */ + char gpr_name[]; /* Null-terminated filename */ +} __packed; + +/* 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 __pad; + + /* 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) +{ + char *next = ((char *)gpr + gpr->gpr_reclen); + char *end = (char *)(uintptr_t)(gp->gp_buffer + gp->gp_bufsize); + + if (next >= end) + return NULL; + + return (struct xfs_getparents_rec *)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 */ @@ -873,6 +946,8 @@ struct xfs_commit_range { /* 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_getparents) +#define XFS_IOC_GETPARENTS_BY_HANDLE _IOWR('X', 63, struct xfs_getparents_by_handle) /* * ioctl commands that replace IRIX syssgi()'s diff --git a/libxfs/xfs_ondisk.h b/libxfs/xfs_ondisk.h index 25952ef58..34c972113 100644 --- a/libxfs/xfs_ondisk.h +++ b/libxfs/xfs_ondisk.h @@ -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_64, efi_extents, 16); + /* parent pointer ioctls */ + XFS_CHECK_STRUCT_SIZE(struct xfs_getparents_rec, 26); + 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 * additional data. While new fields are only accessible on v5 diff --git a/libxfs/xfs_parent.c b/libxfs/xfs_parent.c index 033740e06..2f5fe3e55 100644 --- a/libxfs/xfs_parent.c +++ b/libxfs/xfs_parent.c @@ -261,3 +261,38 @@ xfs_parent_replacename( xfs_attr_defer_parent(&ppargs->args, XFS_ATTR_DEFER_REPLACE); return 0; } + +/* + * Extract parent pointer information from any xattr into @parent_ino/gen. + * The last two parameters can be NULL pointers. + * + * Returns 1 if this is a valid parent pointer; 0 if this is not a parent + * pointer xattr at all; or -EFSCORRUPTED for garbage. + */ +int +xfs_parent_from_xattr( + 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; + + if (!(attr_flags & XFS_ATTR_PARENT)) + return 0; + + 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 1; +} diff --git a/libxfs/xfs_parent.h b/libxfs/xfs_parent.h index 768633b31..3003ab496 100644 --- a/libxfs/xfs_parent.h +++ b/libxfs/xfs_parent.h @@ -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 *child); +int xfs_parent_from_xattr(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__ */ diff --git a/man/man2/ioctl_xfs_getparents.2 b/man/man2/ioctl_xfs_getparents.2 new file mode 100644 index 000000000..5bb9b96a0 --- /dev/null +++ b/man/man2/ioctl_xfs_getparents.2 @@ -0,0 +1,212 @@ +.\" Copyright (c) 2019-2024 Oracle. All rights reserved. +.\" +.\" %%%LICENSE_START(GPLv2+_DOC_FULL) +.\" SPDX-License-Identifier: GPL-2.0-or-later +.\" %%%LICENSE_END +.TH IOCTL-XFS-GETPARENTS 2 2024-04-09 "XFS" +.SH NAME +ioctl_xfs_getparents \- query XFS directory parent information +.SH SYNOPSIS +.br +.B #include +.PP +.BI "int ioctl(int " fd ", XFS_IOC_GETPARENTS, struct xfs_getparents *" arg ); +.PP +.BI "int ioctl(int " fd ", XFS_IOC_GETPARENTS_BY_HANDLE, struct xfs_getparents_by_handle *" arg ); +.SH DESCRIPTION +This command is used to retrieve the directory parent pointers of either the +currently opened file or a file handle. +Parent pointers point upwards in the directory tree from a child file towards a +parent directories. +Each entry in a parent directory must have a corresponding parent pointer in +the child. + +Calling programs should allocate a large memory buffer and initialize a header +of the following form: +.PP +.in +4n +.nf +struct xfs_getparents { + struct xfs_attrlist_cursor gp_cursor; + __u16 gp_iflags; + __u16 gp_oflags; + __u32 gp_bufsize; + __u64 __pad; + __u64 gp_buffer; +}; + +struct xfs_getparents { + struct xfs_handle gph_handle; + struct xfs_getparents gph_request; +}; +.fi +.in + +.PP +The field +.I gp_cursor +tracks the progress of iterating through the parent pointers. +Calling programs must initialize this to zero before the first system call +and must not touch it after that. + +.PP +The field +.I gp_iflags +control the behavior of the query operation and provide more information +about the outcome of the operation. +There are no input flags currently defined; this field must be zero. + +.PP +The field +.I gp_oflags +contains information about the query itself. +Possibly output flags are: +.RS 0.4i +.TP +.B XFS_GETPARENTS_OFLAG_ROOT +The file queried was the root directory. +.TP +.B XFS_GETPARENTS_OFLAG_DONE +There are no more parent pointers to query. +.RE + +.PP +The field +.I __pad +must be zero. + +.PP +The field +.I gp_bufsize +should be set to the size of the buffer, in bytes. + +.PP +The field +.I gp_buffer +should point to an output buffer for the parent pointer records. + +Parent pointer records are returned in the following form: +.PP +.in +4n +.nf + +struct xfs_getparents_rec { + struct xfs_handle gpr_parent; + __u16 gpr_reclen; + char gpr_name[]; +}; +.fi +.in + +.PP +The field +.I gpr_parent +is a file handle that can be used to open the parent directory. + +.PP +The field +.I gpr_reclen +will be set to the number of bytes used by this parent record. + +.PP +The array +.I gpr_name +will be set to a NULL-terminated byte sequence representing the filename +stored in the parent pointer. +If the name is a zero-length string, the file queried has no parents. + +.SH SAMPLE PROGRAM +Calling programs should allocate a large memory buffer, initialize the head +structure to zeroes, set gp_bufsize to the size of the buffer, and call the +ioctl. +The XFS_GETPARENTS_OFLAG_DONE flag will be set in gp_flags when there are no +more parent pointers to be read. +The below code is an example of XFS_IOC_GETPARENTS usage: + +.nf +#include +#include +#include +#include +#include +#include +#include + +int main() { + struct xfs_getparents gp = { }; + struct xfs_getparents_rec *gpr; + int error, fd; + + gp.gp_buffer = (uintptr_t)malloc(65536); + if (!gp.gp_buffer) { + perror("malloc"); + return 1; + } + gp->gp_bufsize = 65536; + + fd = open("/mnt/test/foo.txt", O_RDONLY | O_CREAT); + if (fd == -1) + return errno; + + do { + error = ioctl(fd, XFS_IOC_GETPARENTS, gp); + if (error) + return error; + + for (gpr = xfs_getparents_first_rec(&gp); + gpr != NULL; + gpr = xfs_getparents_next_rec(&gp, gpr)) { + if (gpr->gpr_name[0] == 0) + break; + + printf("inode = %llu\\n", + gpr->gpr_parent.ha_fid.fid_ino); + printf("generation = %u\\n", + gpr->gpr_parent.ha_fid.fid_gen); + printf("name = \\"%s\\"\\n\\n", + gpr->gpr_name); + } + } while (!(gp.gp_flags & XFS_GETPARENTS_OFLAG_DONE)); + + return 0; +} +.fi + +.SH RETURN VALUE +On error, \-1 is returned, and +.I errno +is set to indicate the error. +.PP +.SH ERRORS +Error codes can be one of, but are not limited to, the following: +.TP +.B EFSBADCRC +Metadata checksum validation failed while performing the query. +.TP +.B EFSCORRUPTED +Metadata corruption was encountered while performing the query. +.TP +.B EINVAL +One or more of the arguments specified is invalid. +.TP +.B EMSGSIZE +The record buffer was not large enough to store even a single record. +.TP +.B ENOMEM +Not enough memory to retrieve parent pointers. +.TP +.B EOPNOTSUPP +Repairs of the requested metadata object are not supported. +.TP +.B EROFS +Filesystem is read-only and a repair was requested. +.TP +.B ESHUTDOWN +Filesystem is shut down due to previous errors. +.TP +.B EIO +An I/O error was encountered while performing the query. +.SH CONFORMING TO +This API is specific to XFS filesystem on the Linux kernel. +.SH SEE ALSO +.BR ioctl (2)