xfs
[Top] [All Lists]

[RFC 12/17] xfs: (parent ptr) add parent pointer support for user space

To: xfs@xxxxxxxxxxx
Subject: [RFC 12/17] xfs: (parent ptr) add parent pointer support for user space
From: Mark Tinguely <tinguely@xxxxxxx>
Date: Wed, 15 Jan 2014 16:00:24 -0600
Delivered-to: xfs@xxxxxxxxxxx
References: <20140115220012.624438534@xxxxxxx>
User-agent: quilt/0.51-1
Add the parent inode / offset support for user space.
This adds two new ioctls to provide the names or paths
for an inode that is pointed to by handle.

---
 fs/xfs/xfs_attr.h   |    2 
 fs/xfs/xfs_fs.h     |   10 +
 fs/xfs/xfs_ioctl.c  |   72 ++++++++
 fs/xfs/xfs_parent.c |  435 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_parent.h |   30 +++
 5 files changed, 548 insertions(+), 1 deletion(-)

Index: b/fs/xfs/xfs_attr.h
===================================================================
--- a/fs/xfs/xfs_attr.h
+++ b/fs/xfs/xfs_attr.h
@@ -131,6 +131,8 @@ typedef struct xfs_attr_list_context {
        int                             put_value;      /* T/F: need value for 
listent */
        put_listent_func_t              put_listent;    /* list output fmt 
function */
        int                             index;          /* index into output 
buffer */
+       char                            *scratch;       /* parent private buf */
+       int                             srtchlen;       /* parent priv buf len 
*/
 } xfs_attr_list_context_t;
 
 
Index: b/fs/xfs/xfs_fs.h
===================================================================
--- a/fs/xfs/xfs_fs.h
+++ b/fs/xfs/xfs_fs.h
@@ -438,6 +438,14 @@ typedef struct xfs_fsop_attrmulti_handle
        struct xfs_attr_multiop         __user *ops; /* attr_multi data */
 } xfs_fsop_attrmulti_handlereq_t;
 
+typedef struct xfs_fsop_parentlist_handlereq {
+       struct xfs_fsop_handlereq       hreq;   /* handle interface structure */
+       u32                             flags;  /* which namespace to use */
+       u32                             buflen; /* length of supplied buffer */
+       __s32                           __user *ocount; /* output count ptr */
+       void                            __user *buffer; /* returned names */
+} xfs_fsop_parentlist_handlereq_t;
+
 /*
  * per machine unique filesystem identifier types.
  */
@@ -553,6 +561,8 @@ typedef struct xfs_swapext
 #define XFS_IOC_ATTRMULTI_BY_HANDLE  _IOW ('X', 123, struct 
xfs_fsop_attrmulti_handlereq)
 #define XFS_IOC_FSGEOMETRY          _IOR ('X', 124, struct xfs_fsop_geom)
 #define XFS_IOC_GOINGDOWN           _IOR ('X', 125, __uint32_t)
+#define XFS_IOC_GETPARENTS_BY_HANDLE _IOWR('X', 126, struct 
xfs_fsop_parentlist_handlereq)
+#define XFS_IOC_GETPARENTPATHS_BY_HANDLE _IOWR('X', 127, struct 
xfs_fsop_parentlist_handlereq)
 /*     XFS_IOC_GETFSUUID ---------- deprecated 140      */
 
 
Index: b/fs/xfs/xfs_ioctl.c
===================================================================
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -42,6 +42,7 @@
 #include "xfs_symlink.h"
 #include "xfs_dinode.h"
 #include "xfs_trans.h"
+#include "xfs_parent.h"
 
 #include <linux/capability.h>
 #include <linux/dcache.h>
@@ -632,6 +633,73 @@ xfs_attrmulti_by_handle(
        return -error;
 }
 
+STATIC int
+xfs_getparent_by_handle(
+       struct file             *parfilp,
+       void                    __user *arg,
+       unsigned int            cmd)
+{
+       int                     error = -ENOMEM;
+       int                     count = 0;
+       struct xfs_fsop_parentlist_handlereq    pl_hreq;
+       struct dentry           *dentry;
+       char                    *kbuf;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -XFS_ERROR(EPERM);
+
+       if (copy_from_user(&pl_hreq, arg, sizeof(pl_hreq)))
+               return -XFS_ERROR(EFAULT);
+
+       if (pl_hreq.buflen > XATTR_LIST_MAX)
+               return -XFS_ERROR(EINVAL);
+
+       /*
+        * Reject flags, only allow namespaces.
+        */
+       if (pl_hreq.flags & ~(ATTR_PARENT))
+               return -XFS_ERROR(EINVAL);
+
+       dentry = xfs_handlereq_to_dentry(parfilp, &pl_hreq.hreq);
+       if (IS_ERR(dentry))
+               return PTR_ERR(dentry);
+
+       kbuf = kmem_zalloc_large(pl_hreq.buflen, KM_SLEEP);
+       if (!kbuf)
+               goto out_dput;
+
+       switch (cmd) {
+       case XFS_IOC_GETPARENTS_BY_HANDLE:
+               error = -xfs_parents_attr_list(XFS_I(dentry->d_inode), kbuf,
+                                       pl_hreq.buflen, &count);
+               break;
+       case XFS_IOC_GETPARENTPATHS_BY_HANDLE:
+               error = -xfs_parentpaths_attr_list(XFS_I(dentry->d_inode),
+                                       kbuf, pl_hreq.buflen, &count);
+               break;
+       default:
+               error = -ENOTTY;
+       }
+       if (error)
+               goto out_kfree;
+
+       if (copy_to_user(pl_hreq.buffer, kbuf, pl_hreq.buflen)) {
+               error = -XFS_ERROR(EFAULT);
+               goto out_kfree;
+       }
+
+       if (pl_hreq.ocount != NULL) {
+               if (copy_to_user(pl_hreq.ocount, &count, sizeof(count)))
+                       error = -XFS_ERROR(EFAULT);
+       }
+
+ out_kfree:
+       kmem_free(kbuf);
+ out_dput:
+       dput(dentry);
+       return error;
+}
+
 int
 xfs_ioc_space(
        struct xfs_inode        *ip,
@@ -1675,6 +1743,10 @@ xfs_file_ioctl(
        case XFS_IOC_ATTRMULTI_BY_HANDLE:
                return xfs_attrmulti_by_handle(filp, arg);
 
+       case XFS_IOC_GETPARENTS_BY_HANDLE:
+       case XFS_IOC_GETPARENTPATHS_BY_HANDLE:
+               return xfs_getparent_by_handle(filp, arg, cmd);
+
        case XFS_IOC_SWAPEXT: {
                struct xfs_swapext      sxp;
 
Index: b/fs/xfs/xfs_parent.c
===================================================================
--- a/fs/xfs/xfs_parent.c
+++ b/fs/xfs/xfs_parent.c
@@ -156,3 +156,438 @@ xfs_pptr_rename(
         */
        return XFSREMOVESRC;
 }
+
+/* the fill function for xfs_readdir */
+static int
+xfs_gpfill(
+       void            *gfill,
+       const char      *name,
+       int             namlen,
+       loff_t          offset,
+       u64             ino,
+       unsigned int    type)
+{
+       struct xfs_pfillinfo *gfp = (struct xfs_pfillinfo *) gfill;
+
+       if (offset != gfp->gp_off)      /*XXX mft remove */
+               return 0;
+
+       namlen = MIN(namlen, gfp->gp_len);
+       strncpy(gfp->gp_path + gfp->gp_len - namlen, name, namlen);
+       gfp->gp_len -= namlen;
+       return 1;       /* return a 1 to stop */
+}
+
+/* save name of entry at directory pino/offset to the buffer (path) */
+int
+xfs_getpname(
+       struct xfs_mount        *mp,            /* IN mount point */
+       xfs_ino_t               pino,           /* IN starting parent ino */
+       xfs_off_t               offset,         /* IN starting dir offset */
+       char                    *path,          /* OUT buffer to hold path */
+       int                     *plen)          /* IN/OUT pref MAXPATHLEN+1 */
+{
+       int                     error = 0;
+       struct xfs_inode        *dp = NULL;
+       struct xfs_pfillinfo    gfill = {
+               .ctx.actor = xfs_gpfill
+        };
+
+       ASSERT(path != NULL);
+
+       gfill.ctx.pos = offset;
+       gfill.gp_off = offset;
+       gfill.gp_path = path;
+       gfill.gp_path[*plen-1] = '\0';
+       memset(path, 0, *plen);
+       gfill.gp_len = *plen-1;
+
+       error = xfs_iget(mp, NULL, pino, 0, XFS_ILOCK_SHARED, &dp);
+       if (error)
+               return error;
+
+       error = xfs_readdir(dp, &(gfill.ctx), *plen);
+       xfs_iunlock(dp, XFS_ILOCK_SHARED);
+       IRELE(dp);
+       *plen = *plen - 1 - gfill.gp_len;
+       return error;
+}
+
+STATIC int
+xfs_attr_paths_put_listent(
+       struct xfs_attr_list_context *context,
+       int             flags,
+       unsigned char   *name,
+       int             namelen,
+       int             valuelen,
+       unsigned char   *value)
+{
+       struct xfs_pattr        *p_attr;
+       struct xfs_parent       *p_entry;
+
+       ASSERT(!(context->flags & ATTR_KERNOVAL));
+       ASSERT(context->count >= 0);
+       ASSERT(context->count < (ATTR_MAX_VALUELEN/8));
+       ASSERT(namelen == sizeof(struct xfs_pattr));
+
+       /*
+        * Only list entries in the right namespace.
+        */
+       if ((context->flags & ATTR_PARENT) == 0)
+               return 0;
+
+       if (namelen != sizeof(struct xfs_pattr))
+               return 0;
+
+       p_attr = (struct xfs_pattr *) name;
+
+       p_entry = (struct xfs_parent *)context->alist;
+       p_entry->p_ino = be64_to_cpu(p_attr->p_ino);
+       p_entry->p_offset = be32_to_cpu(p_attr->p_offset);
+       context->seen_enough = 1;
+       return 0;
+}
+
+/* save path of entry at directory pino/offset to the buffer (path) */
+int
+xfs_getpath(
+       struct xfs_mount        *mp,            /* IN mount point */
+       xfs_ino_t               pino,           /* IN starting parent ino */
+       xfs_off_t               offset,         /* IN starting dir offset */
+       char                    *path,          /* OUT buffer to hold path */
+       int                     *plen)          /* IN/OUT pref MAXPATHLEN+1 */
+{
+       int                     error = 0;
+       uint32_t                dotdot;
+       struct xfs_inode        *dp = NULL;
+       struct xfs_pfillinfo    gfill = {
+               .ctx.actor = xfs_gpfill
+        };
+
+       ASSERT(path != NULL);
+
+       gfill.ctx.pos = offset;
+       gfill.gp_off = offset;
+       gfill.gp_path = path;
+       gfill.gp_path[*plen-1] = '\0';
+       memset(path, 0, *plen);
+       gfill.gp_len = *plen-1;
+
+
+       error = xfs_iget(mp, NULL, pino, 0, XFS_ILOCK_SHARED, &dp);
+       if (error)
+               return error;
+
+       dotdot = dp->d_ops->data_dotdot_offset >> XFS_DIR2_DATA_ALIGN_LOG;
+
+       /*
+        * start with the specified parent inode/offset. save the current
+        * name and then loop through the parent directories filling in
+        * the current directory name until the mount point is reached or
+        * the buffer is full.
+        */
+
+       while (gfill.gp_len > 1 ) {
+               gfill.gp_off = offset; /*XXX mft remove */
+               gfill.ctx.pos = offset;
+               error = xfs_readdir(dp, &(gfill.ctx), *plen);
+               if (error)
+                       goto out_irelse;
+
+               /* look for the parent attribute in the current directory */
+               pino = be64_to_cpu(dp->i_d.di_parent);
+               offset = be32_to_cpu(dp->i_d.di_poffset);
+
+               /* stop at root directory */
+               if (pino == mp->m_sb.sb_rootino && offset <= dotdot)
+                       break;
+
+               xfs_iunlock(dp, XFS_ILOCK_SHARED);
+               IRELE(dp);
+
+               error = xfs_iget(mp, NULL, pino, 0, XFS_ILOCK_SHARED, &dp);
+               if (error)
+                       goto out_irelse;
+
+               if (gfill.gp_len < 1)
+                       break;
+
+               /* there is another parent directory - add dir slash */
+               gfill.gp_len--;
+               *(gfill.gp_path + gfill.gp_len) = '/';
+       }
+
+       *plen = *plen - 1 - gfill.gp_len;
+
+ out_irelse:
+       xfs_iunlock(dp, XFS_ILOCK_SHARED);
+       IRELE(dp);
+       return error;
+}
+
+/*
+ * Format an attribute and copy it out to the user's buffer.
+ * Take care to check values and protect against them changing later,
+ * we may be reading them directly out of a user buffer.
+ */
+/*ARGSUSED*/
+STATIC int
+xfs_attr_parents_put_listent(
+       struct xfs_attr_list_context *context,
+       int             flags,
+       unsigned char   *name,
+       int             namelen,
+       int             valuelen,
+       unsigned char   *value)
+{
+       int error;
+       int len;
+       int reclen;
+       struct xfs_pattr *p_attr;
+       struct xfs_parent *p_entry;
+
+       ASSERT(!(context->flags & ATTR_KERNOVAL));
+       ASSERT(context->count >= 0);
+       ASSERT(context->count < (ATTR_MAX_VALUELEN/8));
+       ASSERT(namelen == sizeof(struct xfs_pattr));
+
+       /*
+        * Only list entries in the right namespace.
+        */
+       if ((context->flags & ATTR_PARENT) == 0)
+               return 0;
+
+       if (namelen != sizeof(struct xfs_pattr))
+               return 0;
+
+       len = context->srtchlen;
+       p_attr = (struct xfs_pattr *) name;
+       error = xfs_getpname(context->dp->i_mount, be64_to_cpu(p_attr->p_ino),
+                       be32_to_cpu(p_attr->p_offset), context->scratch, &len);
+       if (error)
+               return 0;
+
+       /* round up to the nearest 8 byte alignment */
+       reclen = PARENT_ENTSIZE(namelen);
+       if ((context->index + reclen) >= context->bufsize) {
+               context->seen_enough = 1;
+               return 0;
+       }
+
+       p_entry = (struct xfs_parent *)(context->alist + context->index);
+       p_entry->p_ino = be64_to_cpu(p_attr->p_ino);
+       p_entry->p_offset = be32_to_cpu(p_attr->p_offset);
+
+       /*
+        * xfs_getpname() will write the name at the end of the buffer
+        * and null terminated. Copy the path and null to xfs_parent_t
+        */
+       memcpy(p_entry->p_name, context->scratch + context->srtchlen -1 - len,
+               len + 1);
+       p_entry->p_reclen = reclen;
+
+       context->index += reclen;
+       context->count++;
+       return 0;
+}
+
+/*
+ * Format an attribute and copy it out to the user's buffer.
+ * Take care to check values and protect against them changing later,
+ * we may be reading them directly out of a user buffer.
+ */
+/*ARGSUSED*/
+STATIC int
+xfs_attr_parentpaths_put_listent(
+       struct xfs_attr_list_context *context,
+       int             flags,
+       unsigned char   *name,
+       int             namelen,
+       int             valuelen,
+       unsigned char   *value)
+{
+       int error;
+       int len;
+       int reclen;
+       struct xfs_pattr *p_attr;
+       struct xfs_parent *p_entry;
+
+       ASSERT(!(context->flags & ATTR_KERNOVAL));
+       ASSERT(context->count >= 0);
+       ASSERT(context->count < (ATTR_MAX_VALUELEN/8));
+       ASSERT(namelen == sizeof(struct xfs_pattr));
+
+       /*
+        * Only list entries in the right namespace.
+        */
+       if ((context->flags & ATTR_PARENT) == 0)
+               return 0;
+
+       if (namelen != sizeof(struct xfs_pattr))
+               return 0;
+
+       len = context->srtchlen;
+       p_attr = (struct xfs_pattr *) name;
+       error = xfs_getpath(context->dp->i_mount, be64_to_cpu(p_attr->p_ino),
+                       be32_to_cpu(p_attr->p_offset), context->scratch, &len);
+       if (error)
+               return 0;
+
+       /* round up to the nearest 8 byte alignment */
+       reclen = PARENT_ENTSIZE(len);
+       if ((context->index + reclen) >= context->bufsize) {
+               context->seen_enough = 1;
+               return 0;
+       }
+
+       p_entry = (struct xfs_parent *)(context->alist + context->index);
+       p_entry->p_ino = be64_to_cpu(p_attr->p_ino);
+       p_entry->p_offset = be32_to_cpu(p_attr->p_offset);
+
+       /*
+        * xfs_getpath() will write the path at the end of the buffer
+        * and null terminated. Copy the path and null to xfs_parent_t
+        */
+       memcpy(p_entry->p_name, context->scratch + context->srtchlen - 1 - len,
+               len + 1);
+       p_entry->p_reclen = reclen;
+
+       context->index += reclen;
+       context->count++;
+       return 0;
+}
+
+/*
+ * Generate a list of extended attribute parent directory path names */
+int
+xfs_parents_attr_list(
+       struct xfs_inode        *dp,
+       char                    *buffer,
+       int                     bufsize,
+       int                     *count)
+{
+       struct attrlist_cursor_kern     cursor;
+       struct xfs_attr_list_context    context;
+       int error;
+
+       /*
+        * Check for a properly aligned buffer.
+        */
+       if (((long)buffer) & (sizeof(int)-1))
+               return XFS_ERROR(EFAULT);
+
+       /*
+        * Initialize the output buffer.
+        */
+       memset(&cursor, 0, sizeof(cursor));
+       memset(&context, 0, sizeof(context));
+       context.dp = dp;
+       context.cursor = &cursor;
+       context.resynch = 1;
+       context.flags = ATTR_PARENT;
+       context.alist = buffer;
+       context.bufsize = (bufsize & ~(sizeof(int)-1));         /* align */
+       context.firstu = context.bufsize;
+       context.put_listent = xfs_attr_parents_put_listent;
+       context.scratch = kmalloc(MAXPATHLEN+1, GFP_KERNEL);
+       context.srtchlen = MAXPATHLEN+1;
+       *(context.scratch+MAXPATHLEN) = '\0';
+
+       if (dp->i_d.di_parent != NULLFSINO) {
+               struct xfs_pattr        p_attr;
+
+               /* copy the incore entry */
+               p_attr.p_ino = dp->i_d.di_parent;
+               p_attr.p_offset = dp->i_d.di_poffset;
+               xfs_attr_parents_put_listent(&context, ATTR_PARENT,
+                               (char *)&p_attr, sizeof(p_attr), 0, NULL);
+               if (context.seen_enough) {
+                       *count = context.count;
+                       error = ERANGE;
+                       goto freeout;
+               }
+       }
+
+       /* copy the parent extended attribute entries */
+       error = xfs_attr_list_int(&context);
+
+       *count = context.count;
+       if (context.seen_enough) {
+               /*
+               * The buffer is full and we return error.
+               * User would need to recall us with a bigger buffer.
+               */
+               error = ERANGE;
+       }
+ freeout:
+       kfree(context.scratch);
+       return XFS_ERROR(error);
+}
+
+/*
+ * Generate a list of extended attribute parent directory path names
+ */
+int
+xfs_parentpaths_attr_list(
+       struct xfs_inode        *dp,
+       char                    *buffer,
+       int                     bufsize,
+       int                     *count)
+{
+       struct attrlist_cursor_kern     cursor;
+       struct xfs_attr_list_context    context;
+       int error;
+
+       /*
+        * Check for a properly aligned buffer.
+        */
+       if (((long)buffer) & (sizeof(int) - 1))
+               return XFS_ERROR(EFAULT);
+
+       /*
+        * Initialize the output buffer.
+        */
+       memset(&cursor, 0, sizeof(cursor));
+       memset(&context, 0, sizeof(context));
+       context.dp = dp;
+       context.cursor = &cursor;
+       context.resynch = 1;
+       context.flags = ATTR_PARENT;
+       context.alist = buffer;
+       context.bufsize = (bufsize & ~(sizeof(int) - 1));       /* align */
+       context.firstu = context.bufsize;
+       context.put_listent = xfs_attr_parentpaths_put_listent;
+       context.scratch = kmalloc(MAXPATHLEN+1, GFP_KERNEL);
+       context.srtchlen = MAXPATHLEN+1;
+       *(context.scratch+MAXPATHLEN) = '\0';
+
+       if (dp->i_d.di_parent != NULLFSINO) {
+               struct xfs_pattr        p_attr;
+
+               /* copy the incore entry */
+               p_attr.p_ino = dp->i_d.di_parent;
+               p_attr.p_offset = dp->i_d.di_poffset;
+               xfs_attr_parentpaths_put_listent(&context, ATTR_PARENT,
+                               (char *)&p_attr, sizeof(p_attr), 0, NULL);
+               if (context.seen_enough) {
+                       *count = context.count;
+                       error = ERANGE;
+                       goto freeout;
+               }
+       }
+
+       /* copy the parent extended attribute entries */
+       error = xfs_attr_list_int(&context);
+
+       *count = context.count;
+       if (context.seen_enough) {
+               /*
+               * The buffer is full and we return error.
+               * User would need to recall us with a bigger buffer.
+               */
+               error = ERANGE;
+       }
+ freeout:
+       kfree(context.scratch);
+       return XFS_ERROR(error);
+}
Index: b/fs/xfs/xfs_parent.h
===================================================================
--- a/fs/xfs/xfs_parent.h
+++ b/fs/xfs/xfs_parent.h
@@ -28,6 +28,22 @@ struct xfs_pattr {
        __be32  p_offset;
 } __attribute__((packed));
 
+/* xfs_readdir fill program private data */
+struct xfs_pfillinfo {
+       struct dir_context      ctx;            /* must be first entry */
+       int                     gp_len;
+       int                     gp_off;
+       char                    *gp_path;
+};
+
+typedef struct xfs_parent {
+       uint64_t        p_ino;          /* parent inode number */
+       uint32_t        p_offset;       /* entry offset in parent inode */
+       uint16_t        p_reclen;       /* name length */
+       uint16_t        p_pad;          /* padding for future */
+       char            p_name[1];      /* variable length name */
+} xfs_parent_t;
+
 static inline void xfs_parent_pname(xfs_ino_t ino, __uint32_t off, struct 
xfs_pattr *pe, struct xfs_name *pn)
 {
        pe->p_ino = cpu_to_be64(ino);
@@ -36,9 +52,21 @@ static inline void xfs_parent_pname(xfs_
        pn->len = sizeof(struct xfs_pattr);
 }
 
+/* actual bytes used by parent entry */
+#define PARENT_ENTSIZE(namelen)                                        \
+       ((offsetof(struct xfs_parent, p_name) + (namelen) + 1 + \
+       sizeof(uint64_t)-1) & ~(sizeof(uint64_t)-1))
+
+int xfs_getpath(struct xfs_mount *mp, xfs_ino_t pino, xfs_off_t offset,
+               char *path, int *plen);
+int xfs_getpname(struct xfs_mount *mp, xfs_ino_t pino, xfs_off_t offset,
+               char *path, int *plen);
+int xfs_parents_attr_list(struct xfs_inode *dp, char *buffer, int bufsize,
+               int *count);
+int xfs_parentpaths_attr_list(struct xfs_inode *dp, char *buffer, int bufsize,
+               int *count);
 int xfs_pptr_rename(struct xfs_mount *mp, struct xfs_trans *tp,
        struct xfs_inode *src_dp, struct xfs_inode *target_dp,
        struct xfs_inode *src_ip, struct xfs_inode *target_ip,
        uint src_offset, uint tar_offset);
 #endif /* __XFS_PARENT_H__ */
-


<Prev in Thread] Current Thread [Next in Thread>