ceph: add getvxattr op
authorMilind Changire <milindchangire@gmail.com>
Mon, 14 Feb 2022 05:01:01 +0000 (05:01 +0000)
committerIlya Dryomov <idryomov@gmail.com>
Tue, 1 Mar 2022 17:26:37 +0000 (18:26 +0100)
Problem:
Some directory vxattrs (e.g. ceph.dir.pin.random) are governed by
information that isn't necessarily shared with the client. Add support
for the new GETVXATTR operation, which allows the client to query the
MDS directly for vxattrs.
When the client is queried for a vxattr that doesn't have a special
handler, have it issue a GETVXATTR to the MDS directly.

Solution:
Adds new getvxattr op to fetch ceph.dir.pin*, ceph.dir.layout* and
ceph.file.layout* vxattrs.
If the entire layout for a dir or a file is being set, then it is
expected that the layout be set in standard JSON format. Individual
field value retrieval is not wrapped in JSON. The JSON format also
applies while setting the vxattr if the entire layout is being set in
one go.
As a temporary measure, setting a vxattr can also be done in the old
format. The old format will be deprecated in the future.

URL: https://tracker.ceph.com/issues/51062
Signed-off-by: Milind Changire <mchangir@redhat.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
fs/ceph/inode.c
fs/ceph/mds_client.c
fs/ceph/mds_client.h
fs/ceph/strings.c
fs/ceph/super.h
fs/ceph/xattr.c
include/linux/ceph/ceph_fs.h

index 22adddb3d051e5a6178dd46cde3be07837e7d248..7b1e93c8a0d2984504ee9351618b1805ea51b2f4 100644 (file)
@@ -2301,6 +2301,57 @@ int __ceph_do_getattr(struct inode *inode, struct page *locked_page,
        return err;
 }
 
+int ceph_do_getvxattr(struct inode *inode, const char *name, void *value,
+                     size_t size)
+{
+       struct ceph_fs_client *fsc = ceph_sb_to_client(inode->i_sb);
+       struct ceph_mds_client *mdsc = fsc->mdsc;
+       struct ceph_mds_request *req;
+       int mode = USE_AUTH_MDS;
+       int err;
+       char *xattr_value;
+       size_t xattr_value_len;
+
+       req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_GETVXATTR, mode);
+       if (IS_ERR(req)) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       req->r_path2 = kstrdup(name, GFP_NOFS);
+       if (!req->r_path2) {
+               err = -ENOMEM;
+               goto put;
+       }
+
+       ihold(inode);
+       req->r_inode = inode;
+       err = ceph_mdsc_do_request(mdsc, NULL, req);
+       if (err < 0)
+               goto put;
+
+       xattr_value = req->r_reply_info.xattr_info.xattr_value;
+       xattr_value_len = req->r_reply_info.xattr_info.xattr_value_len;
+
+       dout("do_getvxattr xattr_value_len:%zu, size:%zu\n", xattr_value_len, size);
+
+       err = (int)xattr_value_len;
+       if (size == 0)
+               goto put;
+
+       if (xattr_value_len > size) {
+               err = -ERANGE;
+               goto put;
+       }
+
+       memcpy(value, xattr_value, xattr_value_len);
+put:
+       ceph_mdsc_put_request(req);
+out:
+       dout("do_getvxattr result=%d\n", err);
+       return err;
+}
+
 
 /*
  * Check inode permissions.  We verify we have a valid value for
index f3abbf83ea5b41e16a3412be7b6aa76f149f8ed7..0b7bde73bf03a81ee3460329d73334d62f93d09c 100644 (file)
@@ -555,6 +555,28 @@ bad:
        return -EIO;
 }
 
+static int parse_reply_info_getvxattr(void **p, void *end,
+                                     struct ceph_mds_reply_info_parsed *info,
+                                     u64 features)
+{
+       u32 value_len;
+
+       ceph_decode_skip_8(p, end, bad); /* skip current version: 1 */
+       ceph_decode_skip_8(p, end, bad); /* skip first version: 1 */
+       ceph_decode_skip_32(p, end, bad); /* skip payload length */
+
+       ceph_decode_32_safe(p, end, value_len, bad);
+
+       if (value_len == end - *p) {
+         info->xattr_info.xattr_value = *p;
+         info->xattr_info.xattr_value_len = value_len;
+         *p = end;
+         return value_len;
+       }
+bad:
+       return -EIO;
+}
+
 /*
  * parse extra results
  */
@@ -570,6 +592,8 @@ static int parse_reply_info_extra(void **p, void *end,
                return parse_reply_info_readdir(p, end, info, features);
        else if (op == CEPH_MDS_OP_CREATE)
                return parse_reply_info_create(p, end, info, features, s);
+       else if (op == CEPH_MDS_OP_GETVXATTR)
+               return parse_reply_info_getvxattr(p, end, info, features);
        else
                return -EIO;
 }
index ab12f3ce81a3e4ea0859d6a1cc234263718db9f0..33497846e47e81639976856e2a3896c85931f0d4 100644 (file)
@@ -100,6 +100,11 @@ struct ceph_mds_reply_dir_entry {
        loff_t                        offset;
 };
 
+struct ceph_mds_reply_xattr {
+       char *xattr_value;
+       size_t xattr_value_len;
+};
+
 /*
  * parsed info about an mds reply, including information about
  * either: 1) the target inode and/or its parent directory and dentry,
@@ -115,6 +120,7 @@ struct ceph_mds_reply_info_parsed {
        char                          *dname;
        u32                           dname_len;
        struct ceph_mds_reply_lease   *dlease;
+       struct ceph_mds_reply_xattr   xattr_info;
 
        /* extra */
        union {
index 573bb9556fb56cbf16802d7770fcf22305bc5469..e36e8948e7285b66127470ee82fa95c2bfce08e8 100644 (file)
@@ -60,6 +60,7 @@ const char *ceph_mds_op_name(int op)
        case CEPH_MDS_OP_LOOKUPINO:  return "lookupino";
        case CEPH_MDS_OP_LOOKUPNAME:  return "lookupname";
        case CEPH_MDS_OP_GETATTR:  return "getattr";
+       case CEPH_MDS_OP_GETVXATTR:  return "getvxattr";
        case CEPH_MDS_OP_SETXATTR: return "setxattr";
        case CEPH_MDS_OP_SETATTR: return "setattr";
        case CEPH_MDS_OP_RMXATTR: return "rmxattr";
index a35c27489901d9bf9b1f6358fc74351df3242e1a..4569f802ddbb12b482d9533b17c8a9d36b79d6e1 100644 (file)
@@ -1048,6 +1048,7 @@ static inline bool ceph_inode_is_shutdown(struct inode *inode)
 
 /* xattr.c */
 int __ceph_setxattr(struct inode *, const char *, const void *, size_t, int);
+int ceph_do_getvxattr(struct inode *inode, const char *name, void *value, size_t size);
 ssize_t __ceph_getxattr(struct inode *, const char *, void *, size_t);
 extern ssize_t ceph_listxattr(struct dentry *, char *, size_t);
 extern struct ceph_buffer *__ceph_build_xattrs_blob(struct ceph_inode_info *ci);
index fcf7dfdecf96603fe56085460dc68a6b7d34b6fd..afec840884719f0d5702a12b0ff9ce152e3e9a2c 100644 (file)
@@ -923,10 +923,13 @@ ssize_t __ceph_getxattr(struct inode *inode, const char *name, void *value,
 {
        struct ceph_inode_info *ci = ceph_inode(inode);
        struct ceph_inode_xattr *xattr;
-       struct ceph_vxattr *vxattr = NULL;
+       struct ceph_vxattr *vxattr;
        int req_mask;
        ssize_t err;
 
+       if (strncmp(name, XATTR_CEPH_PREFIX, XATTR_CEPH_PREFIX_LEN))
+               goto handle_non_vxattrs;
+
        /* let's see if a virtual xattr was requested */
        vxattr = ceph_match_vxattr(inode, name);
        if (vxattr) {
@@ -945,8 +948,14 @@ ssize_t __ceph_getxattr(struct inode *inode, const char *name, void *value,
                                err = -ERANGE;
                }
                return err;
+       } else {
+               err = ceph_do_getvxattr(inode, name, value, size);
+               /* this would happen with a new client and old server combo */
+               if (err == -EOPNOTSUPP)
+                       err = -ENODATA;
+               return err;
        }
-
+handle_non_vxattrs:
        req_mask = __get_request_mask(inode);
 
        spin_lock(&ci->i_ceph_lock);
index 7ad6c3d0db7da1f2f76e26b540b0bfef75df15be..66db21ac5f0cab7155d3a4a756af90f3f6b67e3d 100644 (file)
@@ -328,6 +328,7 @@ enum {
        CEPH_MDS_OP_LOOKUPPARENT = 0x00103,
        CEPH_MDS_OP_LOOKUPINO  = 0x00104,
        CEPH_MDS_OP_LOOKUPNAME = 0x00105,
+       CEPH_MDS_OP_GETVXATTR  = 0x00106,
 
        CEPH_MDS_OP_SETXATTR   = 0x01105,
        CEPH_MDS_OP_RMXATTR    = 0x01106,