162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * (C) 2001 Clemson University and The University of Chicago
462306a36Sopenharmony_ci * Copyright 2018 Omnibond Systems, L.L.C.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * See COPYING in top-level directory.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci/*
1062306a36Sopenharmony_ci *  Linux VFS extended attribute operations.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "protocol.h"
1462306a36Sopenharmony_ci#include "orangefs-kernel.h"
1562306a36Sopenharmony_ci#include "orangefs-bufmap.h"
1662306a36Sopenharmony_ci#include <linux/posix_acl_xattr.h>
1762306a36Sopenharmony_ci#include <linux/xattr.h>
1862306a36Sopenharmony_ci#include <linux/hashtable.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define SYSTEM_ORANGEFS_KEY "system.pvfs2."
2162306a36Sopenharmony_ci#define SYSTEM_ORANGEFS_KEY_LEN 13
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/*
2462306a36Sopenharmony_ci * this function returns
2562306a36Sopenharmony_ci *   0 if the key corresponding to name is not meant to be printed as part
2662306a36Sopenharmony_ci *     of a listxattr.
2762306a36Sopenharmony_ci *   1 if the key corresponding to name is meant to be returned as part of
2862306a36Sopenharmony_ci *     a listxattr.
2962306a36Sopenharmony_ci * The ones that start SYSTEM_ORANGEFS_KEY are the ones to avoid printing.
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_cistatic int is_reserved_key(const char *key, size_t size)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	if (size < SYSTEM_ORANGEFS_KEY_LEN)
3562306a36Sopenharmony_ci		return 1;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	return strncmp(key, SYSTEM_ORANGEFS_KEY, SYSTEM_ORANGEFS_KEY_LEN) ?  1 : 0;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic inline int convert_to_internal_xattr_flags(int setxattr_flags)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	int internal_flag = 0;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (setxattr_flags & XATTR_REPLACE) {
4562306a36Sopenharmony_ci		/* Attribute must exist! */
4662306a36Sopenharmony_ci		internal_flag = ORANGEFS_XATTR_REPLACE;
4762306a36Sopenharmony_ci	} else if (setxattr_flags & XATTR_CREATE) {
4862306a36Sopenharmony_ci		/* Attribute must not exist */
4962306a36Sopenharmony_ci		internal_flag = ORANGEFS_XATTR_CREATE;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci	return internal_flag;
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic unsigned int xattr_key(const char *key)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	unsigned int i = 0;
5762306a36Sopenharmony_ci	while (key)
5862306a36Sopenharmony_ci		i += *key++;
5962306a36Sopenharmony_ci	return i % 16;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic struct orangefs_cached_xattr *find_cached_xattr(struct inode *inode,
6362306a36Sopenharmony_ci    const char *key)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
6662306a36Sopenharmony_ci	struct orangefs_cached_xattr *cx;
6762306a36Sopenharmony_ci	struct hlist_head *h;
6862306a36Sopenharmony_ci	struct hlist_node *tmp;
6962306a36Sopenharmony_ci	h = &orangefs_inode->xattr_cache[xattr_key(key)];
7062306a36Sopenharmony_ci	if (hlist_empty(h))
7162306a36Sopenharmony_ci		return NULL;
7262306a36Sopenharmony_ci	hlist_for_each_entry_safe(cx, tmp, h, node) {
7362306a36Sopenharmony_ci/*		if (!time_before(jiffies, cx->timeout)) {
7462306a36Sopenharmony_ci			hlist_del(&cx->node);
7562306a36Sopenharmony_ci			kfree(cx);
7662306a36Sopenharmony_ci			continue;
7762306a36Sopenharmony_ci		}*/
7862306a36Sopenharmony_ci		if (!strcmp(cx->key, key))
7962306a36Sopenharmony_ci			return cx;
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci	return NULL;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/*
8562306a36Sopenharmony_ci * Tries to get a specified key's attributes of a given
8662306a36Sopenharmony_ci * file into a user-specified buffer. Note that the getxattr
8762306a36Sopenharmony_ci * interface allows for the users to probe the size of an
8862306a36Sopenharmony_ci * extended attribute by passing in a value of 0 to size.
8962306a36Sopenharmony_ci * Thus our return value is always the size of the attribute
9062306a36Sopenharmony_ci * unless the key does not exist for the file and/or if
9162306a36Sopenharmony_ci * there were errors in fetching the attribute value.
9262306a36Sopenharmony_ci */
9362306a36Sopenharmony_cissize_t orangefs_inode_getxattr(struct inode *inode, const char *name,
9462306a36Sopenharmony_ci				void *buffer, size_t size)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
9762306a36Sopenharmony_ci	struct orangefs_kernel_op_s *new_op = NULL;
9862306a36Sopenharmony_ci	struct orangefs_cached_xattr *cx;
9962306a36Sopenharmony_ci	ssize_t ret = -ENOMEM;
10062306a36Sopenharmony_ci	ssize_t length = 0;
10162306a36Sopenharmony_ci	int fsuid;
10262306a36Sopenharmony_ci	int fsgid;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
10562306a36Sopenharmony_ci		     "%s: name %s, buffer_size %zd\n",
10662306a36Sopenharmony_ci		     __func__, name, size);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (S_ISLNK(inode->i_mode))
10962306a36Sopenharmony_ci		return -EOPNOTSUPP;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (strlen(name) >= ORANGEFS_MAX_XATTR_NAMELEN)
11262306a36Sopenharmony_ci		return -EINVAL;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	fsuid = from_kuid(&init_user_ns, current_fsuid());
11562306a36Sopenharmony_ci	fsgid = from_kgid(&init_user_ns, current_fsgid());
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
11862306a36Sopenharmony_ci		     "getxattr on inode %pU, name %s "
11962306a36Sopenharmony_ci		     "(uid %o, gid %o)\n",
12062306a36Sopenharmony_ci		     get_khandle_from_ino(inode),
12162306a36Sopenharmony_ci		     name,
12262306a36Sopenharmony_ci		     fsuid,
12362306a36Sopenharmony_ci		     fsgid);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	down_read(&orangefs_inode->xattr_sem);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	cx = find_cached_xattr(inode, name);
12862306a36Sopenharmony_ci	if (cx && time_before(jiffies, cx->timeout)) {
12962306a36Sopenharmony_ci		if (cx->length == -1) {
13062306a36Sopenharmony_ci			ret = -ENODATA;
13162306a36Sopenharmony_ci			goto out_unlock;
13262306a36Sopenharmony_ci		} else {
13362306a36Sopenharmony_ci			if (size == 0) {
13462306a36Sopenharmony_ci				ret = cx->length;
13562306a36Sopenharmony_ci				goto out_unlock;
13662306a36Sopenharmony_ci			}
13762306a36Sopenharmony_ci			if (cx->length > size) {
13862306a36Sopenharmony_ci				ret = -ERANGE;
13962306a36Sopenharmony_ci				goto out_unlock;
14062306a36Sopenharmony_ci			}
14162306a36Sopenharmony_ci			memcpy(buffer, cx->val, cx->length);
14262306a36Sopenharmony_ci			memset(buffer + cx->length, 0, size - cx->length);
14362306a36Sopenharmony_ci			ret = cx->length;
14462306a36Sopenharmony_ci			goto out_unlock;
14562306a36Sopenharmony_ci		}
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	new_op = op_alloc(ORANGEFS_VFS_OP_GETXATTR);
14962306a36Sopenharmony_ci	if (!new_op)
15062306a36Sopenharmony_ci		goto out_unlock;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	new_op->upcall.req.getxattr.refn = orangefs_inode->refn;
15362306a36Sopenharmony_ci	strcpy(new_op->upcall.req.getxattr.key, name);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/*
15662306a36Sopenharmony_ci	 * NOTE: Although keys are meant to be NULL terminated textual
15762306a36Sopenharmony_ci	 * strings, I am going to explicitly pass the length just in case
15862306a36Sopenharmony_ci	 * we change this later on...
15962306a36Sopenharmony_ci	 */
16062306a36Sopenharmony_ci	new_op->upcall.req.getxattr.key_sz = strlen(name) + 1;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	ret = service_operation(new_op, "orangefs_inode_getxattr",
16362306a36Sopenharmony_ci				get_interruptible_flag(inode));
16462306a36Sopenharmony_ci	if (ret != 0) {
16562306a36Sopenharmony_ci		if (ret == -ENOENT) {
16662306a36Sopenharmony_ci			ret = -ENODATA;
16762306a36Sopenharmony_ci			gossip_debug(GOSSIP_XATTR_DEBUG,
16862306a36Sopenharmony_ci				     "orangefs_inode_getxattr: inode %pU key %s"
16962306a36Sopenharmony_ci				     " does not exist!\n",
17062306a36Sopenharmony_ci				     get_khandle_from_ino(inode),
17162306a36Sopenharmony_ci				     (char *)new_op->upcall.req.getxattr.key);
17262306a36Sopenharmony_ci			cx = kmalloc(sizeof *cx, GFP_KERNEL);
17362306a36Sopenharmony_ci			if (cx) {
17462306a36Sopenharmony_ci				strcpy(cx->key, name);
17562306a36Sopenharmony_ci				cx->length = -1;
17662306a36Sopenharmony_ci				cx->timeout = jiffies +
17762306a36Sopenharmony_ci				    orangefs_getattr_timeout_msecs*HZ/1000;
17862306a36Sopenharmony_ci				hash_add(orangefs_inode->xattr_cache, &cx->node,
17962306a36Sopenharmony_ci				    xattr_key(cx->key));
18062306a36Sopenharmony_ci			}
18162306a36Sopenharmony_ci		}
18262306a36Sopenharmony_ci		goto out_release_op;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/*
18662306a36Sopenharmony_ci	 * Length returned includes null terminator.
18762306a36Sopenharmony_ci	 */
18862306a36Sopenharmony_ci	length = new_op->downcall.resp.getxattr.val_sz;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	/*
19162306a36Sopenharmony_ci	 * Just return the length of the queried attribute.
19262306a36Sopenharmony_ci	 */
19362306a36Sopenharmony_ci	if (size == 0) {
19462306a36Sopenharmony_ci		ret = length;
19562306a36Sopenharmony_ci		goto out_release_op;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/*
19962306a36Sopenharmony_ci	 * Check to see if key length is > provided buffer size.
20062306a36Sopenharmony_ci	 */
20162306a36Sopenharmony_ci	if (length > size) {
20262306a36Sopenharmony_ci		ret = -ERANGE;
20362306a36Sopenharmony_ci		goto out_release_op;
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	memcpy(buffer, new_op->downcall.resp.getxattr.val, length);
20762306a36Sopenharmony_ci	memset(buffer + length, 0, size - length);
20862306a36Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
20962306a36Sopenharmony_ci	     "orangefs_inode_getxattr: inode %pU "
21062306a36Sopenharmony_ci	     "key %s key_sz %d, val_len %d\n",
21162306a36Sopenharmony_ci	     get_khandle_from_ino(inode),
21262306a36Sopenharmony_ci	     (char *)new_op->
21362306a36Sopenharmony_ci		upcall.req.getxattr.key,
21462306a36Sopenharmony_ci		     (int)new_op->
21562306a36Sopenharmony_ci		upcall.req.getxattr.key_sz,
21662306a36Sopenharmony_ci	     (int)ret);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	ret = length;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (cx) {
22162306a36Sopenharmony_ci		strcpy(cx->key, name);
22262306a36Sopenharmony_ci		memcpy(cx->val, buffer, length);
22362306a36Sopenharmony_ci		cx->length = length;
22462306a36Sopenharmony_ci		cx->timeout = jiffies + HZ;
22562306a36Sopenharmony_ci	} else {
22662306a36Sopenharmony_ci		cx = kmalloc(sizeof *cx, GFP_KERNEL);
22762306a36Sopenharmony_ci		if (cx) {
22862306a36Sopenharmony_ci			strcpy(cx->key, name);
22962306a36Sopenharmony_ci			memcpy(cx->val, buffer, length);
23062306a36Sopenharmony_ci			cx->length = length;
23162306a36Sopenharmony_ci			cx->timeout = jiffies + HZ;
23262306a36Sopenharmony_ci			hash_add(orangefs_inode->xattr_cache, &cx->node,
23362306a36Sopenharmony_ci			    xattr_key(cx->key));
23462306a36Sopenharmony_ci		}
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ciout_release_op:
23862306a36Sopenharmony_ci	op_release(new_op);
23962306a36Sopenharmony_ciout_unlock:
24062306a36Sopenharmony_ci	up_read(&orangefs_inode->xattr_sem);
24162306a36Sopenharmony_ci	return ret;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic int orangefs_inode_removexattr(struct inode *inode, const char *name,
24562306a36Sopenharmony_ci				      int flags)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
24862306a36Sopenharmony_ci	struct orangefs_kernel_op_s *new_op = NULL;
24962306a36Sopenharmony_ci	struct orangefs_cached_xattr *cx;
25062306a36Sopenharmony_ci	struct hlist_head *h;
25162306a36Sopenharmony_ci	struct hlist_node *tmp;
25262306a36Sopenharmony_ci	int ret = -ENOMEM;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (strlen(name) >= ORANGEFS_MAX_XATTR_NAMELEN)
25562306a36Sopenharmony_ci		return -EINVAL;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	down_write(&orangefs_inode->xattr_sem);
25862306a36Sopenharmony_ci	new_op = op_alloc(ORANGEFS_VFS_OP_REMOVEXATTR);
25962306a36Sopenharmony_ci	if (!new_op)
26062306a36Sopenharmony_ci		goto out_unlock;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	new_op->upcall.req.removexattr.refn = orangefs_inode->refn;
26362306a36Sopenharmony_ci	/*
26462306a36Sopenharmony_ci	 * NOTE: Although keys are meant to be NULL terminated
26562306a36Sopenharmony_ci	 * textual strings, I am going to explicitly pass the
26662306a36Sopenharmony_ci	 * length just in case we change this later on...
26762306a36Sopenharmony_ci	 */
26862306a36Sopenharmony_ci	strcpy(new_op->upcall.req.removexattr.key, name);
26962306a36Sopenharmony_ci	new_op->upcall.req.removexattr.key_sz = strlen(name) + 1;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
27262306a36Sopenharmony_ci		     "orangefs_inode_removexattr: key %s, key_sz %d\n",
27362306a36Sopenharmony_ci		     (char *)new_op->upcall.req.removexattr.key,
27462306a36Sopenharmony_ci		     (int)new_op->upcall.req.removexattr.key_sz);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	ret = service_operation(new_op,
27762306a36Sopenharmony_ci				"orangefs_inode_removexattr",
27862306a36Sopenharmony_ci				get_interruptible_flag(inode));
27962306a36Sopenharmony_ci	if (ret == -ENOENT) {
28062306a36Sopenharmony_ci		/*
28162306a36Sopenharmony_ci		 * Request to replace a non-existent attribute is an error.
28262306a36Sopenharmony_ci		 */
28362306a36Sopenharmony_ci		if (flags & XATTR_REPLACE)
28462306a36Sopenharmony_ci			ret = -ENODATA;
28562306a36Sopenharmony_ci		else
28662306a36Sopenharmony_ci			ret = 0;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
29062306a36Sopenharmony_ci		     "orangefs_inode_removexattr: returning %d\n", ret);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	op_release(new_op);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	h = &orangefs_inode->xattr_cache[xattr_key(name)];
29562306a36Sopenharmony_ci	hlist_for_each_entry_safe(cx, tmp, h, node) {
29662306a36Sopenharmony_ci		if (!strcmp(cx->key, name)) {
29762306a36Sopenharmony_ci			hlist_del(&cx->node);
29862306a36Sopenharmony_ci			kfree(cx);
29962306a36Sopenharmony_ci			break;
30062306a36Sopenharmony_ci		}
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ciout_unlock:
30462306a36Sopenharmony_ci	up_write(&orangefs_inode->xattr_sem);
30562306a36Sopenharmony_ci	return ret;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci/*
30962306a36Sopenharmony_ci * Tries to set an attribute for a given key on a file.
31062306a36Sopenharmony_ci *
31162306a36Sopenharmony_ci * Returns a -ve number on error and 0 on success.  Key is text, but value
31262306a36Sopenharmony_ci * can be binary!
31362306a36Sopenharmony_ci */
31462306a36Sopenharmony_ciint orangefs_inode_setxattr(struct inode *inode, const char *name,
31562306a36Sopenharmony_ci			    const void *value, size_t size, int flags)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
31862306a36Sopenharmony_ci	struct orangefs_kernel_op_s *new_op;
31962306a36Sopenharmony_ci	int internal_flag = 0;
32062306a36Sopenharmony_ci	struct orangefs_cached_xattr *cx;
32162306a36Sopenharmony_ci	struct hlist_head *h;
32262306a36Sopenharmony_ci	struct hlist_node *tmp;
32362306a36Sopenharmony_ci	int ret = -ENOMEM;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
32662306a36Sopenharmony_ci		     "%s: name %s, buffer_size %zd\n",
32762306a36Sopenharmony_ci		     __func__, name, size);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (size > ORANGEFS_MAX_XATTR_VALUELEN)
33062306a36Sopenharmony_ci		return -EINVAL;
33162306a36Sopenharmony_ci	if (strlen(name) >= ORANGEFS_MAX_XATTR_NAMELEN)
33262306a36Sopenharmony_ci		return -EINVAL;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	internal_flag = convert_to_internal_xattr_flags(flags);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/* This is equivalent to a removexattr */
33762306a36Sopenharmony_ci	if (size == 0 && !value) {
33862306a36Sopenharmony_ci		gossip_debug(GOSSIP_XATTR_DEBUG,
33962306a36Sopenharmony_ci			     "removing xattr (%s)\n",
34062306a36Sopenharmony_ci			     name);
34162306a36Sopenharmony_ci		return orangefs_inode_removexattr(inode, name, flags);
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
34562306a36Sopenharmony_ci		     "setxattr on inode %pU, name %s\n",
34662306a36Sopenharmony_ci		     get_khandle_from_ino(inode),
34762306a36Sopenharmony_ci		     name);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	down_write(&orangefs_inode->xattr_sem);
35062306a36Sopenharmony_ci	new_op = op_alloc(ORANGEFS_VFS_OP_SETXATTR);
35162306a36Sopenharmony_ci	if (!new_op)
35262306a36Sopenharmony_ci		goto out_unlock;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	new_op->upcall.req.setxattr.refn = orangefs_inode->refn;
35662306a36Sopenharmony_ci	new_op->upcall.req.setxattr.flags = internal_flag;
35762306a36Sopenharmony_ci	/*
35862306a36Sopenharmony_ci	 * NOTE: Although keys are meant to be NULL terminated textual
35962306a36Sopenharmony_ci	 * strings, I am going to explicitly pass the length just in
36062306a36Sopenharmony_ci	 * case we change this later on...
36162306a36Sopenharmony_ci	 */
36262306a36Sopenharmony_ci	strcpy(new_op->upcall.req.setxattr.keyval.key, name);
36362306a36Sopenharmony_ci	new_op->upcall.req.setxattr.keyval.key_sz = strlen(name) + 1;
36462306a36Sopenharmony_ci	memcpy(new_op->upcall.req.setxattr.keyval.val, value, size);
36562306a36Sopenharmony_ci	new_op->upcall.req.setxattr.keyval.val_sz = size;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
36862306a36Sopenharmony_ci		     "orangefs_inode_setxattr: key %s, key_sz %d "
36962306a36Sopenharmony_ci		     " value size %zd\n",
37062306a36Sopenharmony_ci		     (char *)new_op->upcall.req.setxattr.keyval.key,
37162306a36Sopenharmony_ci		     (int)new_op->upcall.req.setxattr.keyval.key_sz,
37262306a36Sopenharmony_ci		     size);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	ret = service_operation(new_op,
37562306a36Sopenharmony_ci				"orangefs_inode_setxattr",
37662306a36Sopenharmony_ci				get_interruptible_flag(inode));
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
37962306a36Sopenharmony_ci		     "orangefs_inode_setxattr: returning %d\n",
38062306a36Sopenharmony_ci		     ret);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	/* when request is serviced properly, free req op struct */
38362306a36Sopenharmony_ci	op_release(new_op);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	h = &orangefs_inode->xattr_cache[xattr_key(name)];
38662306a36Sopenharmony_ci	hlist_for_each_entry_safe(cx, tmp, h, node) {
38762306a36Sopenharmony_ci		if (!strcmp(cx->key, name)) {
38862306a36Sopenharmony_ci			hlist_del(&cx->node);
38962306a36Sopenharmony_ci			kfree(cx);
39062306a36Sopenharmony_ci			break;
39162306a36Sopenharmony_ci		}
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ciout_unlock:
39562306a36Sopenharmony_ci	up_write(&orangefs_inode->xattr_sem);
39662306a36Sopenharmony_ci	return ret;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci/*
40062306a36Sopenharmony_ci * Tries to get a specified object's keys into a user-specified buffer of a
40162306a36Sopenharmony_ci * given size.  Note that like the previous instances of xattr routines, this
40262306a36Sopenharmony_ci * also allows you to pass in a NULL pointer and 0 size to probe the size for
40362306a36Sopenharmony_ci * subsequent memory allocations. Thus our return value is always the size of
40462306a36Sopenharmony_ci * all the keys unless there were errors in fetching the keys!
40562306a36Sopenharmony_ci */
40662306a36Sopenharmony_cissize_t orangefs_listxattr(struct dentry *dentry, char *buffer, size_t size)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct inode *inode = dentry->d_inode;
40962306a36Sopenharmony_ci	struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
41062306a36Sopenharmony_ci	struct orangefs_kernel_op_s *new_op;
41162306a36Sopenharmony_ci	__u64 token = ORANGEFS_ITERATE_START;
41262306a36Sopenharmony_ci	ssize_t ret = -ENOMEM;
41362306a36Sopenharmony_ci	ssize_t total = 0;
41462306a36Sopenharmony_ci	int count_keys = 0;
41562306a36Sopenharmony_ci	int key_size;
41662306a36Sopenharmony_ci	int i = 0;
41762306a36Sopenharmony_ci	int returned_count = 0;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (size > 0 && !buffer) {
42062306a36Sopenharmony_ci		gossip_err("%s: bogus NULL pointers\n", __func__);
42162306a36Sopenharmony_ci		return -EINVAL;
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	down_read(&orangefs_inode->xattr_sem);
42562306a36Sopenharmony_ci	new_op = op_alloc(ORANGEFS_VFS_OP_LISTXATTR);
42662306a36Sopenharmony_ci	if (!new_op)
42762306a36Sopenharmony_ci		goto out_unlock;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (buffer && size > 0)
43062306a36Sopenharmony_ci		memset(buffer, 0, size);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_citry_again:
43362306a36Sopenharmony_ci	key_size = 0;
43462306a36Sopenharmony_ci	new_op->upcall.req.listxattr.refn = orangefs_inode->refn;
43562306a36Sopenharmony_ci	new_op->upcall.req.listxattr.token = token;
43662306a36Sopenharmony_ci	new_op->upcall.req.listxattr.requested_count =
43762306a36Sopenharmony_ci	    (size == 0) ? 0 : ORANGEFS_MAX_XATTR_LISTLEN;
43862306a36Sopenharmony_ci	ret = service_operation(new_op, __func__,
43962306a36Sopenharmony_ci				get_interruptible_flag(inode));
44062306a36Sopenharmony_ci	if (ret != 0)
44162306a36Sopenharmony_ci		goto done;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (size == 0) {
44462306a36Sopenharmony_ci		/*
44562306a36Sopenharmony_ci		 * This is a bit of a big upper limit, but I did not want to
44662306a36Sopenharmony_ci		 * spend too much time getting this correct, since users end
44762306a36Sopenharmony_ci		 * up allocating memory rather than us...
44862306a36Sopenharmony_ci		 */
44962306a36Sopenharmony_ci		total = new_op->downcall.resp.listxattr.returned_count *
45062306a36Sopenharmony_ci			ORANGEFS_MAX_XATTR_NAMELEN;
45162306a36Sopenharmony_ci		goto done;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	returned_count = new_op->downcall.resp.listxattr.returned_count;
45562306a36Sopenharmony_ci	if (returned_count < 0 ||
45662306a36Sopenharmony_ci	    returned_count > ORANGEFS_MAX_XATTR_LISTLEN) {
45762306a36Sopenharmony_ci		gossip_err("%s: impossible value for returned_count:%d:\n",
45862306a36Sopenharmony_ci		__func__,
45962306a36Sopenharmony_ci		returned_count);
46062306a36Sopenharmony_ci		ret = -EIO;
46162306a36Sopenharmony_ci		goto done;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	/*
46562306a36Sopenharmony_ci	 * Check to see how much can be fit in the buffer. Fit only whole keys.
46662306a36Sopenharmony_ci	 */
46762306a36Sopenharmony_ci	for (i = 0; i < returned_count; i++) {
46862306a36Sopenharmony_ci		if (new_op->downcall.resp.listxattr.lengths[i] < 0 ||
46962306a36Sopenharmony_ci		    new_op->downcall.resp.listxattr.lengths[i] >
47062306a36Sopenharmony_ci		    ORANGEFS_MAX_XATTR_NAMELEN) {
47162306a36Sopenharmony_ci			gossip_err("%s: impossible value for lengths[%d]\n",
47262306a36Sopenharmony_ci			    __func__,
47362306a36Sopenharmony_ci			    new_op->downcall.resp.listxattr.lengths[i]);
47462306a36Sopenharmony_ci			ret = -EIO;
47562306a36Sopenharmony_ci			goto done;
47662306a36Sopenharmony_ci		}
47762306a36Sopenharmony_ci		if (total + new_op->downcall.resp.listxattr.lengths[i] > size)
47862306a36Sopenharmony_ci			goto done;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci		/*
48162306a36Sopenharmony_ci		 * Since many dumb programs try to setxattr() on our reserved
48262306a36Sopenharmony_ci		 * xattrs this is a feeble attempt at defeating those by not
48362306a36Sopenharmony_ci		 * listing them in the output of listxattr.. sigh
48462306a36Sopenharmony_ci		 */
48562306a36Sopenharmony_ci		if (is_reserved_key(new_op->downcall.resp.listxattr.key +
48662306a36Sopenharmony_ci				    key_size,
48762306a36Sopenharmony_ci				    new_op->downcall.resp.
48862306a36Sopenharmony_ci					listxattr.lengths[i])) {
48962306a36Sopenharmony_ci			gossip_debug(GOSSIP_XATTR_DEBUG, "Copying key %d -> %s\n",
49062306a36Sopenharmony_ci					i, new_op->downcall.resp.listxattr.key +
49162306a36Sopenharmony_ci						key_size);
49262306a36Sopenharmony_ci			memcpy(buffer + total,
49362306a36Sopenharmony_ci				new_op->downcall.resp.listxattr.key + key_size,
49462306a36Sopenharmony_ci				new_op->downcall.resp.listxattr.lengths[i]);
49562306a36Sopenharmony_ci			total += new_op->downcall.resp.listxattr.lengths[i];
49662306a36Sopenharmony_ci			count_keys++;
49762306a36Sopenharmony_ci		} else {
49862306a36Sopenharmony_ci			gossip_debug(GOSSIP_XATTR_DEBUG, "[RESERVED] key %d -> %s\n",
49962306a36Sopenharmony_ci					i, new_op->downcall.resp.listxattr.key +
50062306a36Sopenharmony_ci						key_size);
50162306a36Sopenharmony_ci		}
50262306a36Sopenharmony_ci		key_size += new_op->downcall.resp.listxattr.lengths[i];
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	/*
50662306a36Sopenharmony_ci	 * Since the buffer was large enough, we might have to continue
50762306a36Sopenharmony_ci	 * fetching more keys!
50862306a36Sopenharmony_ci	 */
50962306a36Sopenharmony_ci	token = new_op->downcall.resp.listxattr.token;
51062306a36Sopenharmony_ci	if (token != ORANGEFS_ITERATE_END)
51162306a36Sopenharmony_ci		goto try_again;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cidone:
51462306a36Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG, "%s: returning %d"
51562306a36Sopenharmony_ci		     " [size of buffer %ld] (filled in %d keys)\n",
51662306a36Sopenharmony_ci		     __func__,
51762306a36Sopenharmony_ci		     ret ? (int)ret : (int)total,
51862306a36Sopenharmony_ci		     (long)size,
51962306a36Sopenharmony_ci		     count_keys);
52062306a36Sopenharmony_ci	op_release(new_op);
52162306a36Sopenharmony_ci	if (ret == 0)
52262306a36Sopenharmony_ci		ret = total;
52362306a36Sopenharmony_ciout_unlock:
52462306a36Sopenharmony_ci	up_read(&orangefs_inode->xattr_sem);
52562306a36Sopenharmony_ci	return ret;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistatic int orangefs_xattr_set_default(const struct xattr_handler *handler,
52962306a36Sopenharmony_ci				      struct mnt_idmap *idmap,
53062306a36Sopenharmony_ci				      struct dentry *unused,
53162306a36Sopenharmony_ci				      struct inode *inode,
53262306a36Sopenharmony_ci				      const char *name,
53362306a36Sopenharmony_ci				      const void *buffer,
53462306a36Sopenharmony_ci				      size_t size,
53562306a36Sopenharmony_ci				      int flags)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	return orangefs_inode_setxattr(inode, name, buffer, size, flags);
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic int orangefs_xattr_get_default(const struct xattr_handler *handler,
54162306a36Sopenharmony_ci				      struct dentry *unused,
54262306a36Sopenharmony_ci				      struct inode *inode,
54362306a36Sopenharmony_ci				      const char *name,
54462306a36Sopenharmony_ci				      void *buffer,
54562306a36Sopenharmony_ci				      size_t size)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	return orangefs_inode_getxattr(inode, name, buffer, size);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic const struct xattr_handler orangefs_xattr_default_handler = {
55262306a36Sopenharmony_ci	.prefix = "",  /* match any name => handlers called with full name */
55362306a36Sopenharmony_ci	.get = orangefs_xattr_get_default,
55462306a36Sopenharmony_ci	.set = orangefs_xattr_set_default,
55562306a36Sopenharmony_ci};
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ciconst struct xattr_handler *orangefs_xattr_handlers[] = {
55862306a36Sopenharmony_ci	&orangefs_xattr_default_handler,
55962306a36Sopenharmony_ci	NULL
56062306a36Sopenharmony_ci};
561