xref: /kernel/linux/linux-5.10/fs/orangefs/xattr.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * (C) 2001 Clemson University and The University of Chicago
48c2ecf20Sopenharmony_ci * Copyright 2018 Omnibond Systems, L.L.C.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * See COPYING in top-level directory.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/*
108c2ecf20Sopenharmony_ci *  Linux VFS extended attribute operations.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "protocol.h"
148c2ecf20Sopenharmony_ci#include "orangefs-kernel.h"
158c2ecf20Sopenharmony_ci#include "orangefs-bufmap.h"
168c2ecf20Sopenharmony_ci#include <linux/posix_acl_xattr.h>
178c2ecf20Sopenharmony_ci#include <linux/xattr.h>
188c2ecf20Sopenharmony_ci#include <linux/hashtable.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define SYSTEM_ORANGEFS_KEY "system.pvfs2."
218c2ecf20Sopenharmony_ci#define SYSTEM_ORANGEFS_KEY_LEN 13
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*
248c2ecf20Sopenharmony_ci * this function returns
258c2ecf20Sopenharmony_ci *   0 if the key corresponding to name is not meant to be printed as part
268c2ecf20Sopenharmony_ci *     of a listxattr.
278c2ecf20Sopenharmony_ci *   1 if the key corresponding to name is meant to be returned as part of
288c2ecf20Sopenharmony_ci *     a listxattr.
298c2ecf20Sopenharmony_ci * The ones that start SYSTEM_ORANGEFS_KEY are the ones to avoid printing.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_cistatic int is_reserved_key(const char *key, size_t size)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	if (size < SYSTEM_ORANGEFS_KEY_LEN)
358c2ecf20Sopenharmony_ci		return 1;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	return strncmp(key, SYSTEM_ORANGEFS_KEY, SYSTEM_ORANGEFS_KEY_LEN) ?  1 : 0;
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic inline int convert_to_internal_xattr_flags(int setxattr_flags)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	int internal_flag = 0;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	if (setxattr_flags & XATTR_REPLACE) {
458c2ecf20Sopenharmony_ci		/* Attribute must exist! */
468c2ecf20Sopenharmony_ci		internal_flag = ORANGEFS_XATTR_REPLACE;
478c2ecf20Sopenharmony_ci	} else if (setxattr_flags & XATTR_CREATE) {
488c2ecf20Sopenharmony_ci		/* Attribute must not exist */
498c2ecf20Sopenharmony_ci		internal_flag = ORANGEFS_XATTR_CREATE;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci	return internal_flag;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic unsigned int xattr_key(const char *key)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	unsigned int i = 0;
578c2ecf20Sopenharmony_ci	while (key)
588c2ecf20Sopenharmony_ci		i += *key++;
598c2ecf20Sopenharmony_ci	return i % 16;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic struct orangefs_cached_xattr *find_cached_xattr(struct inode *inode,
638c2ecf20Sopenharmony_ci    const char *key)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
668c2ecf20Sopenharmony_ci	struct orangefs_cached_xattr *cx;
678c2ecf20Sopenharmony_ci	struct hlist_head *h;
688c2ecf20Sopenharmony_ci	struct hlist_node *tmp;
698c2ecf20Sopenharmony_ci	h = &orangefs_inode->xattr_cache[xattr_key(key)];
708c2ecf20Sopenharmony_ci	if (hlist_empty(h))
718c2ecf20Sopenharmony_ci		return NULL;
728c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(cx, tmp, h, node) {
738c2ecf20Sopenharmony_ci/*		if (!time_before(jiffies, cx->timeout)) {
748c2ecf20Sopenharmony_ci			hlist_del(&cx->node);
758c2ecf20Sopenharmony_ci			kfree(cx);
768c2ecf20Sopenharmony_ci			continue;
778c2ecf20Sopenharmony_ci		}*/
788c2ecf20Sopenharmony_ci		if (!strcmp(cx->key, key))
798c2ecf20Sopenharmony_ci			return cx;
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci	return NULL;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/*
858c2ecf20Sopenharmony_ci * Tries to get a specified key's attributes of a given
868c2ecf20Sopenharmony_ci * file into a user-specified buffer. Note that the getxattr
878c2ecf20Sopenharmony_ci * interface allows for the users to probe the size of an
888c2ecf20Sopenharmony_ci * extended attribute by passing in a value of 0 to size.
898c2ecf20Sopenharmony_ci * Thus our return value is always the size of the attribute
908c2ecf20Sopenharmony_ci * unless the key does not exist for the file and/or if
918c2ecf20Sopenharmony_ci * there were errors in fetching the attribute value.
928c2ecf20Sopenharmony_ci */
938c2ecf20Sopenharmony_cissize_t orangefs_inode_getxattr(struct inode *inode, const char *name,
948c2ecf20Sopenharmony_ci				void *buffer, size_t size)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
978c2ecf20Sopenharmony_ci	struct orangefs_kernel_op_s *new_op = NULL;
988c2ecf20Sopenharmony_ci	struct orangefs_cached_xattr *cx;
998c2ecf20Sopenharmony_ci	ssize_t ret = -ENOMEM;
1008c2ecf20Sopenharmony_ci	ssize_t length = 0;
1018c2ecf20Sopenharmony_ci	int fsuid;
1028c2ecf20Sopenharmony_ci	int fsgid;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
1058c2ecf20Sopenharmony_ci		     "%s: name %s, buffer_size %zd\n",
1068c2ecf20Sopenharmony_ci		     __func__, name, size);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (S_ISLNK(inode->i_mode))
1098c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (strlen(name) >= ORANGEFS_MAX_XATTR_NAMELEN)
1128c2ecf20Sopenharmony_ci		return -EINVAL;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	fsuid = from_kuid(&init_user_ns, current_fsuid());
1158c2ecf20Sopenharmony_ci	fsgid = from_kgid(&init_user_ns, current_fsgid());
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
1188c2ecf20Sopenharmony_ci		     "getxattr on inode %pU, name %s "
1198c2ecf20Sopenharmony_ci		     "(uid %o, gid %o)\n",
1208c2ecf20Sopenharmony_ci		     get_khandle_from_ino(inode),
1218c2ecf20Sopenharmony_ci		     name,
1228c2ecf20Sopenharmony_ci		     fsuid,
1238c2ecf20Sopenharmony_ci		     fsgid);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	down_read(&orangefs_inode->xattr_sem);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	cx = find_cached_xattr(inode, name);
1288c2ecf20Sopenharmony_ci	if (cx && time_before(jiffies, cx->timeout)) {
1298c2ecf20Sopenharmony_ci		if (cx->length == -1) {
1308c2ecf20Sopenharmony_ci			ret = -ENODATA;
1318c2ecf20Sopenharmony_ci			goto out_unlock;
1328c2ecf20Sopenharmony_ci		} else {
1338c2ecf20Sopenharmony_ci			if (size == 0) {
1348c2ecf20Sopenharmony_ci				ret = cx->length;
1358c2ecf20Sopenharmony_ci				goto out_unlock;
1368c2ecf20Sopenharmony_ci			}
1378c2ecf20Sopenharmony_ci			if (cx->length > size) {
1388c2ecf20Sopenharmony_ci				ret = -ERANGE;
1398c2ecf20Sopenharmony_ci				goto out_unlock;
1408c2ecf20Sopenharmony_ci			}
1418c2ecf20Sopenharmony_ci			memcpy(buffer, cx->val, cx->length);
1428c2ecf20Sopenharmony_ci			memset(buffer + cx->length, 0, size - cx->length);
1438c2ecf20Sopenharmony_ci			ret = cx->length;
1448c2ecf20Sopenharmony_ci			goto out_unlock;
1458c2ecf20Sopenharmony_ci		}
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	new_op = op_alloc(ORANGEFS_VFS_OP_GETXATTR);
1498c2ecf20Sopenharmony_ci	if (!new_op)
1508c2ecf20Sopenharmony_ci		goto out_unlock;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	new_op->upcall.req.getxattr.refn = orangefs_inode->refn;
1538c2ecf20Sopenharmony_ci	strcpy(new_op->upcall.req.getxattr.key, name);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/*
1568c2ecf20Sopenharmony_ci	 * NOTE: Although keys are meant to be NULL terminated textual
1578c2ecf20Sopenharmony_ci	 * strings, I am going to explicitly pass the length just in case
1588c2ecf20Sopenharmony_ci	 * we change this later on...
1598c2ecf20Sopenharmony_ci	 */
1608c2ecf20Sopenharmony_ci	new_op->upcall.req.getxattr.key_sz = strlen(name) + 1;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	ret = service_operation(new_op, "orangefs_inode_getxattr",
1638c2ecf20Sopenharmony_ci				get_interruptible_flag(inode));
1648c2ecf20Sopenharmony_ci	if (ret != 0) {
1658c2ecf20Sopenharmony_ci		if (ret == -ENOENT) {
1668c2ecf20Sopenharmony_ci			ret = -ENODATA;
1678c2ecf20Sopenharmony_ci			gossip_debug(GOSSIP_XATTR_DEBUG,
1688c2ecf20Sopenharmony_ci				     "orangefs_inode_getxattr: inode %pU key %s"
1698c2ecf20Sopenharmony_ci				     " does not exist!\n",
1708c2ecf20Sopenharmony_ci				     get_khandle_from_ino(inode),
1718c2ecf20Sopenharmony_ci				     (char *)new_op->upcall.req.getxattr.key);
1728c2ecf20Sopenharmony_ci			cx = kmalloc(sizeof *cx, GFP_KERNEL);
1738c2ecf20Sopenharmony_ci			if (cx) {
1748c2ecf20Sopenharmony_ci				strcpy(cx->key, name);
1758c2ecf20Sopenharmony_ci				cx->length = -1;
1768c2ecf20Sopenharmony_ci				cx->timeout = jiffies +
1778c2ecf20Sopenharmony_ci				    orangefs_getattr_timeout_msecs*HZ/1000;
1788c2ecf20Sopenharmony_ci				hash_add(orangefs_inode->xattr_cache, &cx->node,
1798c2ecf20Sopenharmony_ci				    xattr_key(cx->key));
1808c2ecf20Sopenharmony_ci			}
1818c2ecf20Sopenharmony_ci		}
1828c2ecf20Sopenharmony_ci		goto out_release_op;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	/*
1868c2ecf20Sopenharmony_ci	 * Length returned includes null terminator.
1878c2ecf20Sopenharmony_ci	 */
1888c2ecf20Sopenharmony_ci	length = new_op->downcall.resp.getxattr.val_sz;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	/*
1918c2ecf20Sopenharmony_ci	 * Just return the length of the queried attribute.
1928c2ecf20Sopenharmony_ci	 */
1938c2ecf20Sopenharmony_ci	if (size == 0) {
1948c2ecf20Sopenharmony_ci		ret = length;
1958c2ecf20Sopenharmony_ci		goto out_release_op;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	/*
1998c2ecf20Sopenharmony_ci	 * Check to see if key length is > provided buffer size.
2008c2ecf20Sopenharmony_ci	 */
2018c2ecf20Sopenharmony_ci	if (length > size) {
2028c2ecf20Sopenharmony_ci		ret = -ERANGE;
2038c2ecf20Sopenharmony_ci		goto out_release_op;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	memcpy(buffer, new_op->downcall.resp.getxattr.val, length);
2078c2ecf20Sopenharmony_ci	memset(buffer + length, 0, size - length);
2088c2ecf20Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
2098c2ecf20Sopenharmony_ci	     "orangefs_inode_getxattr: inode %pU "
2108c2ecf20Sopenharmony_ci	     "key %s key_sz %d, val_len %d\n",
2118c2ecf20Sopenharmony_ci	     get_khandle_from_ino(inode),
2128c2ecf20Sopenharmony_ci	     (char *)new_op->
2138c2ecf20Sopenharmony_ci		upcall.req.getxattr.key,
2148c2ecf20Sopenharmony_ci		     (int)new_op->
2158c2ecf20Sopenharmony_ci		upcall.req.getxattr.key_sz,
2168c2ecf20Sopenharmony_ci	     (int)ret);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	ret = length;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if (cx) {
2218c2ecf20Sopenharmony_ci		strcpy(cx->key, name);
2228c2ecf20Sopenharmony_ci		memcpy(cx->val, buffer, length);
2238c2ecf20Sopenharmony_ci		cx->length = length;
2248c2ecf20Sopenharmony_ci		cx->timeout = jiffies + HZ;
2258c2ecf20Sopenharmony_ci	} else {
2268c2ecf20Sopenharmony_ci		cx = kmalloc(sizeof *cx, GFP_KERNEL);
2278c2ecf20Sopenharmony_ci		if (cx) {
2288c2ecf20Sopenharmony_ci			strcpy(cx->key, name);
2298c2ecf20Sopenharmony_ci			memcpy(cx->val, buffer, length);
2308c2ecf20Sopenharmony_ci			cx->length = length;
2318c2ecf20Sopenharmony_ci			cx->timeout = jiffies + HZ;
2328c2ecf20Sopenharmony_ci			hash_add(orangefs_inode->xattr_cache, &cx->node,
2338c2ecf20Sopenharmony_ci			    xattr_key(cx->key));
2348c2ecf20Sopenharmony_ci		}
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ciout_release_op:
2388c2ecf20Sopenharmony_ci	op_release(new_op);
2398c2ecf20Sopenharmony_ciout_unlock:
2408c2ecf20Sopenharmony_ci	up_read(&orangefs_inode->xattr_sem);
2418c2ecf20Sopenharmony_ci	return ret;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic int orangefs_inode_removexattr(struct inode *inode, const char *name,
2458c2ecf20Sopenharmony_ci				      int flags)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
2488c2ecf20Sopenharmony_ci	struct orangefs_kernel_op_s *new_op = NULL;
2498c2ecf20Sopenharmony_ci	struct orangefs_cached_xattr *cx;
2508c2ecf20Sopenharmony_ci	struct hlist_head *h;
2518c2ecf20Sopenharmony_ci	struct hlist_node *tmp;
2528c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (strlen(name) >= ORANGEFS_MAX_XATTR_NAMELEN)
2558c2ecf20Sopenharmony_ci		return -EINVAL;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	down_write(&orangefs_inode->xattr_sem);
2588c2ecf20Sopenharmony_ci	new_op = op_alloc(ORANGEFS_VFS_OP_REMOVEXATTR);
2598c2ecf20Sopenharmony_ci	if (!new_op)
2608c2ecf20Sopenharmony_ci		goto out_unlock;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	new_op->upcall.req.removexattr.refn = orangefs_inode->refn;
2638c2ecf20Sopenharmony_ci	/*
2648c2ecf20Sopenharmony_ci	 * NOTE: Although keys are meant to be NULL terminated
2658c2ecf20Sopenharmony_ci	 * textual strings, I am going to explicitly pass the
2668c2ecf20Sopenharmony_ci	 * length just in case we change this later on...
2678c2ecf20Sopenharmony_ci	 */
2688c2ecf20Sopenharmony_ci	strcpy(new_op->upcall.req.removexattr.key, name);
2698c2ecf20Sopenharmony_ci	new_op->upcall.req.removexattr.key_sz = strlen(name) + 1;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
2728c2ecf20Sopenharmony_ci		     "orangefs_inode_removexattr: key %s, key_sz %d\n",
2738c2ecf20Sopenharmony_ci		     (char *)new_op->upcall.req.removexattr.key,
2748c2ecf20Sopenharmony_ci		     (int)new_op->upcall.req.removexattr.key_sz);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	ret = service_operation(new_op,
2778c2ecf20Sopenharmony_ci				"orangefs_inode_removexattr",
2788c2ecf20Sopenharmony_ci				get_interruptible_flag(inode));
2798c2ecf20Sopenharmony_ci	if (ret == -ENOENT) {
2808c2ecf20Sopenharmony_ci		/*
2818c2ecf20Sopenharmony_ci		 * Request to replace a non-existent attribute is an error.
2828c2ecf20Sopenharmony_ci		 */
2838c2ecf20Sopenharmony_ci		if (flags & XATTR_REPLACE)
2848c2ecf20Sopenharmony_ci			ret = -ENODATA;
2858c2ecf20Sopenharmony_ci		else
2868c2ecf20Sopenharmony_ci			ret = 0;
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
2908c2ecf20Sopenharmony_ci		     "orangefs_inode_removexattr: returning %d\n", ret);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	op_release(new_op);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	h = &orangefs_inode->xattr_cache[xattr_key(name)];
2958c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(cx, tmp, h, node) {
2968c2ecf20Sopenharmony_ci		if (!strcmp(cx->key, name)) {
2978c2ecf20Sopenharmony_ci			hlist_del(&cx->node);
2988c2ecf20Sopenharmony_ci			kfree(cx);
2998c2ecf20Sopenharmony_ci			break;
3008c2ecf20Sopenharmony_ci		}
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ciout_unlock:
3048c2ecf20Sopenharmony_ci	up_write(&orangefs_inode->xattr_sem);
3058c2ecf20Sopenharmony_ci	return ret;
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci/*
3098c2ecf20Sopenharmony_ci * Tries to set an attribute for a given key on a file.
3108c2ecf20Sopenharmony_ci *
3118c2ecf20Sopenharmony_ci * Returns a -ve number on error and 0 on success.  Key is text, but value
3128c2ecf20Sopenharmony_ci * can be binary!
3138c2ecf20Sopenharmony_ci */
3148c2ecf20Sopenharmony_ciint orangefs_inode_setxattr(struct inode *inode, const char *name,
3158c2ecf20Sopenharmony_ci			    const void *value, size_t size, int flags)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
3188c2ecf20Sopenharmony_ci	struct orangefs_kernel_op_s *new_op;
3198c2ecf20Sopenharmony_ci	int internal_flag = 0;
3208c2ecf20Sopenharmony_ci	struct orangefs_cached_xattr *cx;
3218c2ecf20Sopenharmony_ci	struct hlist_head *h;
3228c2ecf20Sopenharmony_ci	struct hlist_node *tmp;
3238c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
3268c2ecf20Sopenharmony_ci		     "%s: name %s, buffer_size %zd\n",
3278c2ecf20Sopenharmony_ci		     __func__, name, size);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (size > ORANGEFS_MAX_XATTR_VALUELEN)
3308c2ecf20Sopenharmony_ci		return -EINVAL;
3318c2ecf20Sopenharmony_ci	if (strlen(name) >= ORANGEFS_MAX_XATTR_NAMELEN)
3328c2ecf20Sopenharmony_ci		return -EINVAL;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	internal_flag = convert_to_internal_xattr_flags(flags);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* This is equivalent to a removexattr */
3378c2ecf20Sopenharmony_ci	if (size == 0 && !value) {
3388c2ecf20Sopenharmony_ci		gossip_debug(GOSSIP_XATTR_DEBUG,
3398c2ecf20Sopenharmony_ci			     "removing xattr (%s)\n",
3408c2ecf20Sopenharmony_ci			     name);
3418c2ecf20Sopenharmony_ci		return orangefs_inode_removexattr(inode, name, flags);
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
3458c2ecf20Sopenharmony_ci		     "setxattr on inode %pU, name %s\n",
3468c2ecf20Sopenharmony_ci		     get_khandle_from_ino(inode),
3478c2ecf20Sopenharmony_ci		     name);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	down_write(&orangefs_inode->xattr_sem);
3508c2ecf20Sopenharmony_ci	new_op = op_alloc(ORANGEFS_VFS_OP_SETXATTR);
3518c2ecf20Sopenharmony_ci	if (!new_op)
3528c2ecf20Sopenharmony_ci		goto out_unlock;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	new_op->upcall.req.setxattr.refn = orangefs_inode->refn;
3568c2ecf20Sopenharmony_ci	new_op->upcall.req.setxattr.flags = internal_flag;
3578c2ecf20Sopenharmony_ci	/*
3588c2ecf20Sopenharmony_ci	 * NOTE: Although keys are meant to be NULL terminated textual
3598c2ecf20Sopenharmony_ci	 * strings, I am going to explicitly pass the length just in
3608c2ecf20Sopenharmony_ci	 * case we change this later on...
3618c2ecf20Sopenharmony_ci	 */
3628c2ecf20Sopenharmony_ci	strcpy(new_op->upcall.req.setxattr.keyval.key, name);
3638c2ecf20Sopenharmony_ci	new_op->upcall.req.setxattr.keyval.key_sz = strlen(name) + 1;
3648c2ecf20Sopenharmony_ci	memcpy(new_op->upcall.req.setxattr.keyval.val, value, size);
3658c2ecf20Sopenharmony_ci	new_op->upcall.req.setxattr.keyval.val_sz = size;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
3688c2ecf20Sopenharmony_ci		     "orangefs_inode_setxattr: key %s, key_sz %d "
3698c2ecf20Sopenharmony_ci		     " value size %zd\n",
3708c2ecf20Sopenharmony_ci		     (char *)new_op->upcall.req.setxattr.keyval.key,
3718c2ecf20Sopenharmony_ci		     (int)new_op->upcall.req.setxattr.keyval.key_sz,
3728c2ecf20Sopenharmony_ci		     size);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	ret = service_operation(new_op,
3758c2ecf20Sopenharmony_ci				"orangefs_inode_setxattr",
3768c2ecf20Sopenharmony_ci				get_interruptible_flag(inode));
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG,
3798c2ecf20Sopenharmony_ci		     "orangefs_inode_setxattr: returning %d\n",
3808c2ecf20Sopenharmony_ci		     ret);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	/* when request is serviced properly, free req op struct */
3838c2ecf20Sopenharmony_ci	op_release(new_op);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	h = &orangefs_inode->xattr_cache[xattr_key(name)];
3868c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(cx, tmp, h, node) {
3878c2ecf20Sopenharmony_ci		if (!strcmp(cx->key, name)) {
3888c2ecf20Sopenharmony_ci			hlist_del(&cx->node);
3898c2ecf20Sopenharmony_ci			kfree(cx);
3908c2ecf20Sopenharmony_ci			break;
3918c2ecf20Sopenharmony_ci		}
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ciout_unlock:
3958c2ecf20Sopenharmony_ci	up_write(&orangefs_inode->xattr_sem);
3968c2ecf20Sopenharmony_ci	return ret;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci/*
4008c2ecf20Sopenharmony_ci * Tries to get a specified object's keys into a user-specified buffer of a
4018c2ecf20Sopenharmony_ci * given size.  Note that like the previous instances of xattr routines, this
4028c2ecf20Sopenharmony_ci * also allows you to pass in a NULL pointer and 0 size to probe the size for
4038c2ecf20Sopenharmony_ci * subsequent memory allocations. Thus our return value is always the size of
4048c2ecf20Sopenharmony_ci * all the keys unless there were errors in fetching the keys!
4058c2ecf20Sopenharmony_ci */
4068c2ecf20Sopenharmony_cissize_t orangefs_listxattr(struct dentry *dentry, char *buffer, size_t size)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	struct inode *inode = dentry->d_inode;
4098c2ecf20Sopenharmony_ci	struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
4108c2ecf20Sopenharmony_ci	struct orangefs_kernel_op_s *new_op;
4118c2ecf20Sopenharmony_ci	__u64 token = ORANGEFS_ITERATE_START;
4128c2ecf20Sopenharmony_ci	ssize_t ret = -ENOMEM;
4138c2ecf20Sopenharmony_ci	ssize_t total = 0;
4148c2ecf20Sopenharmony_ci	int count_keys = 0;
4158c2ecf20Sopenharmony_ci	int key_size;
4168c2ecf20Sopenharmony_ci	int i = 0;
4178c2ecf20Sopenharmony_ci	int returned_count = 0;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	if (size > 0 && !buffer) {
4208c2ecf20Sopenharmony_ci		gossip_err("%s: bogus NULL pointers\n", __func__);
4218c2ecf20Sopenharmony_ci		return -EINVAL;
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	down_read(&orangefs_inode->xattr_sem);
4258c2ecf20Sopenharmony_ci	new_op = op_alloc(ORANGEFS_VFS_OP_LISTXATTR);
4268c2ecf20Sopenharmony_ci	if (!new_op)
4278c2ecf20Sopenharmony_ci		goto out_unlock;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	if (buffer && size > 0)
4308c2ecf20Sopenharmony_ci		memset(buffer, 0, size);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_citry_again:
4338c2ecf20Sopenharmony_ci	key_size = 0;
4348c2ecf20Sopenharmony_ci	new_op->upcall.req.listxattr.refn = orangefs_inode->refn;
4358c2ecf20Sopenharmony_ci	new_op->upcall.req.listxattr.token = token;
4368c2ecf20Sopenharmony_ci	new_op->upcall.req.listxattr.requested_count =
4378c2ecf20Sopenharmony_ci	    (size == 0) ? 0 : ORANGEFS_MAX_XATTR_LISTLEN;
4388c2ecf20Sopenharmony_ci	ret = service_operation(new_op, __func__,
4398c2ecf20Sopenharmony_ci				get_interruptible_flag(inode));
4408c2ecf20Sopenharmony_ci	if (ret != 0)
4418c2ecf20Sopenharmony_ci		goto done;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	if (size == 0) {
4448c2ecf20Sopenharmony_ci		/*
4458c2ecf20Sopenharmony_ci		 * This is a bit of a big upper limit, but I did not want to
4468c2ecf20Sopenharmony_ci		 * spend too much time getting this correct, since users end
4478c2ecf20Sopenharmony_ci		 * up allocating memory rather than us...
4488c2ecf20Sopenharmony_ci		 */
4498c2ecf20Sopenharmony_ci		total = new_op->downcall.resp.listxattr.returned_count *
4508c2ecf20Sopenharmony_ci			ORANGEFS_MAX_XATTR_NAMELEN;
4518c2ecf20Sopenharmony_ci		goto done;
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	returned_count = new_op->downcall.resp.listxattr.returned_count;
4558c2ecf20Sopenharmony_ci	if (returned_count < 0 ||
4568c2ecf20Sopenharmony_ci	    returned_count > ORANGEFS_MAX_XATTR_LISTLEN) {
4578c2ecf20Sopenharmony_ci		gossip_err("%s: impossible value for returned_count:%d:\n",
4588c2ecf20Sopenharmony_ci		__func__,
4598c2ecf20Sopenharmony_ci		returned_count);
4608c2ecf20Sopenharmony_ci		ret = -EIO;
4618c2ecf20Sopenharmony_ci		goto done;
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	/*
4658c2ecf20Sopenharmony_ci	 * Check to see how much can be fit in the buffer. Fit only whole keys.
4668c2ecf20Sopenharmony_ci	 */
4678c2ecf20Sopenharmony_ci	for (i = 0; i < returned_count; i++) {
4688c2ecf20Sopenharmony_ci		if (new_op->downcall.resp.listxattr.lengths[i] < 0 ||
4698c2ecf20Sopenharmony_ci		    new_op->downcall.resp.listxattr.lengths[i] >
4708c2ecf20Sopenharmony_ci		    ORANGEFS_MAX_XATTR_NAMELEN) {
4718c2ecf20Sopenharmony_ci			gossip_err("%s: impossible value for lengths[%d]\n",
4728c2ecf20Sopenharmony_ci			    __func__,
4738c2ecf20Sopenharmony_ci			    new_op->downcall.resp.listxattr.lengths[i]);
4748c2ecf20Sopenharmony_ci			ret = -EIO;
4758c2ecf20Sopenharmony_ci			goto done;
4768c2ecf20Sopenharmony_ci		}
4778c2ecf20Sopenharmony_ci		if (total + new_op->downcall.resp.listxattr.lengths[i] > size)
4788c2ecf20Sopenharmony_ci			goto done;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		/*
4818c2ecf20Sopenharmony_ci		 * Since many dumb programs try to setxattr() on our reserved
4828c2ecf20Sopenharmony_ci		 * xattrs this is a feeble attempt at defeating those by not
4838c2ecf20Sopenharmony_ci		 * listing them in the output of listxattr.. sigh
4848c2ecf20Sopenharmony_ci		 */
4858c2ecf20Sopenharmony_ci		if (is_reserved_key(new_op->downcall.resp.listxattr.key +
4868c2ecf20Sopenharmony_ci				    key_size,
4878c2ecf20Sopenharmony_ci				    new_op->downcall.resp.
4888c2ecf20Sopenharmony_ci					listxattr.lengths[i])) {
4898c2ecf20Sopenharmony_ci			gossip_debug(GOSSIP_XATTR_DEBUG, "Copying key %d -> %s\n",
4908c2ecf20Sopenharmony_ci					i, new_op->downcall.resp.listxattr.key +
4918c2ecf20Sopenharmony_ci						key_size);
4928c2ecf20Sopenharmony_ci			memcpy(buffer + total,
4938c2ecf20Sopenharmony_ci				new_op->downcall.resp.listxattr.key + key_size,
4948c2ecf20Sopenharmony_ci				new_op->downcall.resp.listxattr.lengths[i]);
4958c2ecf20Sopenharmony_ci			total += new_op->downcall.resp.listxattr.lengths[i];
4968c2ecf20Sopenharmony_ci			count_keys++;
4978c2ecf20Sopenharmony_ci		} else {
4988c2ecf20Sopenharmony_ci			gossip_debug(GOSSIP_XATTR_DEBUG, "[RESERVED] key %d -> %s\n",
4998c2ecf20Sopenharmony_ci					i, new_op->downcall.resp.listxattr.key +
5008c2ecf20Sopenharmony_ci						key_size);
5018c2ecf20Sopenharmony_ci		}
5028c2ecf20Sopenharmony_ci		key_size += new_op->downcall.resp.listxattr.lengths[i];
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	/*
5068c2ecf20Sopenharmony_ci	 * Since the buffer was large enough, we might have to continue
5078c2ecf20Sopenharmony_ci	 * fetching more keys!
5088c2ecf20Sopenharmony_ci	 */
5098c2ecf20Sopenharmony_ci	token = new_op->downcall.resp.listxattr.token;
5108c2ecf20Sopenharmony_ci	if (token != ORANGEFS_ITERATE_END)
5118c2ecf20Sopenharmony_ci		goto try_again;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cidone:
5148c2ecf20Sopenharmony_ci	gossip_debug(GOSSIP_XATTR_DEBUG, "%s: returning %d"
5158c2ecf20Sopenharmony_ci		     " [size of buffer %ld] (filled in %d keys)\n",
5168c2ecf20Sopenharmony_ci		     __func__,
5178c2ecf20Sopenharmony_ci		     ret ? (int)ret : (int)total,
5188c2ecf20Sopenharmony_ci		     (long)size,
5198c2ecf20Sopenharmony_ci		     count_keys);
5208c2ecf20Sopenharmony_ci	op_release(new_op);
5218c2ecf20Sopenharmony_ci	if (ret == 0)
5228c2ecf20Sopenharmony_ci		ret = total;
5238c2ecf20Sopenharmony_ciout_unlock:
5248c2ecf20Sopenharmony_ci	up_read(&orangefs_inode->xattr_sem);
5258c2ecf20Sopenharmony_ci	return ret;
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_cistatic int orangefs_xattr_set_default(const struct xattr_handler *handler,
5298c2ecf20Sopenharmony_ci				      struct dentry *unused,
5308c2ecf20Sopenharmony_ci				      struct inode *inode,
5318c2ecf20Sopenharmony_ci				      const char *name,
5328c2ecf20Sopenharmony_ci				      const void *buffer,
5338c2ecf20Sopenharmony_ci				      size_t size,
5348c2ecf20Sopenharmony_ci				      int flags)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	return orangefs_inode_setxattr(inode, name, buffer, size, flags);
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic int orangefs_xattr_get_default(const struct xattr_handler *handler,
5408c2ecf20Sopenharmony_ci				      struct dentry *unused,
5418c2ecf20Sopenharmony_ci				      struct inode *inode,
5428c2ecf20Sopenharmony_ci				      const char *name,
5438c2ecf20Sopenharmony_ci				      void *buffer,
5448c2ecf20Sopenharmony_ci				      size_t size)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	return orangefs_inode_getxattr(inode, name, buffer, size);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic const struct xattr_handler orangefs_xattr_default_handler = {
5518c2ecf20Sopenharmony_ci	.prefix = "",  /* match any name => handlers called with full name */
5528c2ecf20Sopenharmony_ci	.get = orangefs_xattr_get_default,
5538c2ecf20Sopenharmony_ci	.set = orangefs_xattr_set_default,
5548c2ecf20Sopenharmony_ci};
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ciconst struct xattr_handler *orangefs_xattr_handlers[] = {
5578c2ecf20Sopenharmony_ci	&posix_acl_access_xattr_handler,
5588c2ecf20Sopenharmony_ci	&posix_acl_default_xattr_handler,
5598c2ecf20Sopenharmony_ci	&orangefs_xattr_default_handler,
5608c2ecf20Sopenharmony_ci	NULL
5618c2ecf20Sopenharmony_ci};
562