]> www.infradead.org Git - users/hch/xfsprogs.git/commitdiff
xfs: Add parent pointer ioctls
authorDarrick J. Wong <djwong@kernel.org>
Thu, 4 Apr 2024 23:35:42 +0000 (16:35 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 10 Apr 2024 00:21:30 +0000 (17:21 -0700)
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 <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: adjust to new ondisk format, split ioctls]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
libxfs/xfs_fs.h
libxfs/xfs_ondisk.h
libxfs/xfs_parent.c
libxfs/xfs_parent.h
man/man2/ioctl_xfs_getparents.2 [new file with mode: 0644]

index 7486dcba8c218407df631e14249c61e9c784c5b9..fa28c18e521bf6c2fdc04147ccf948c7bab11d17 100644 (file)
@@ -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
index 25952ef584eeea1171a7f6119f8782da220b3cf2..34c972113d997ea129639485cd79aa4e8d29a8a8 100644 (file)
@@ -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
index 033740e06112b5ee632f01a7c50fc24b277477b5..2f5fe3e557ec280264ca46cafa127440316cdd44 100644 (file)
@@ -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;
+}
index 768633b313671a32f23fc7a8477e8bed4a1b4c38..3003ab496f8548dbdd0f656afd9746f47bdaed5d 100644 (file)
@@ -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 (file)
index 0000000..5bb9b96
--- /dev/null
@@ -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 <xfs/xfs_fs.h>
+.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 <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <xfs/linux.h>
+#include <xfs/xfs.h>
+#include <xfs/xfs_types.h>
+#include <xfs/xfs_fs.h>
+
+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)