123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * (C) 2001 Clemson University and The University of Chicago
- *
- * See COPYING in top-level directory.
- */
- /*
- * Linux VFS extended attribute operations.
- */
- #include "protocol.h"
- #include "orangefs-kernel.h"
- #include "orangefs-bufmap.h"
- #include <linux/posix_acl_xattr.h>
- #include <linux/xattr.h>
- #define SYSTEM_ORANGEFS_KEY "system.pvfs2."
- #define SYSTEM_ORANGEFS_KEY_LEN 13
- /*
- * this function returns
- * 0 if the key corresponding to name is not meant to be printed as part
- * of a listxattr.
- * 1 if the key corresponding to name is meant to be returned as part of
- * a listxattr.
- * The ones that start SYSTEM_ORANGEFS_KEY are the ones to avoid printing.
- */
- static int is_reserved_key(const char *key, size_t size)
- {
- if (size < SYSTEM_ORANGEFS_KEY_LEN)
- return 1;
- return strncmp(key, SYSTEM_ORANGEFS_KEY, SYSTEM_ORANGEFS_KEY_LEN) ? 1 : 0;
- }
- static inline int convert_to_internal_xattr_flags(int setxattr_flags)
- {
- int internal_flag = 0;
- if (setxattr_flags & XATTR_REPLACE) {
- /* Attribute must exist! */
- internal_flag = ORANGEFS_XATTR_REPLACE;
- } else if (setxattr_flags & XATTR_CREATE) {
- /* Attribute must not exist */
- internal_flag = ORANGEFS_XATTR_CREATE;
- }
- return internal_flag;
- }
- /*
- * Tries to get a specified key's attributes of a given
- * file into a user-specified buffer. Note that the getxattr
- * interface allows for the users to probe the size of an
- * extended attribute by passing in a value of 0 to size.
- * Thus our return value is always the size of the attribute
- * unless the key does not exist for the file and/or if
- * there were errors in fetching the attribute value.
- */
- ssize_t orangefs_inode_getxattr(struct inode *inode, const char *name,
- void *buffer, size_t size)
- {
- struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
- struct orangefs_kernel_op_s *new_op = NULL;
- ssize_t ret = -ENOMEM;
- ssize_t length = 0;
- int fsuid;
- int fsgid;
- gossip_debug(GOSSIP_XATTR_DEBUG,
- "%s: name %s, buffer_size %zd\n",
- __func__, name, size);
- if (S_ISLNK(inode->i_mode))
- return -EOPNOTSUPP;
- if (strlen(name) >= ORANGEFS_MAX_XATTR_NAMELEN)
- return -EINVAL;
- fsuid = from_kuid(&init_user_ns, current_fsuid());
- fsgid = from_kgid(&init_user_ns, current_fsgid());
- gossip_debug(GOSSIP_XATTR_DEBUG,
- "getxattr on inode %pU, name %s "
- "(uid %o, gid %o)\n",
- get_khandle_from_ino(inode),
- name,
- fsuid,
- fsgid);
- down_read(&orangefs_inode->xattr_sem);
- new_op = op_alloc(ORANGEFS_VFS_OP_GETXATTR);
- if (!new_op)
- goto out_unlock;
- new_op->upcall.req.getxattr.refn = orangefs_inode->refn;
- strcpy(new_op->upcall.req.getxattr.key, name);
- /*
- * NOTE: Although keys are meant to be NULL terminated textual
- * strings, I am going to explicitly pass the length just in case
- * we change this later on...
- */
- new_op->upcall.req.getxattr.key_sz = strlen(name) + 1;
- ret = service_operation(new_op, "orangefs_inode_getxattr",
- get_interruptible_flag(inode));
- if (ret != 0) {
- if (ret == -ENOENT) {
- ret = -ENODATA;
- gossip_debug(GOSSIP_XATTR_DEBUG,
- "orangefs_inode_getxattr: inode %pU key %s"
- " does not exist!\n",
- get_khandle_from_ino(inode),
- (char *)new_op->upcall.req.getxattr.key);
- }
- goto out_release_op;
- }
- /*
- * Length returned includes null terminator.
- */
- length = new_op->downcall.resp.getxattr.val_sz;
- /*
- * Just return the length of the queried attribute.
- */
- if (size == 0) {
- ret = length;
- goto out_release_op;
- }
- /*
- * Check to see if key length is > provided buffer size.
- */
- if (length > size) {
- ret = -ERANGE;
- goto out_release_op;
- }
- memcpy(buffer, new_op->downcall.resp.getxattr.val, length);
- memset(buffer + length, 0, size - length);
- gossip_debug(GOSSIP_XATTR_DEBUG,
- "orangefs_inode_getxattr: inode %pU "
- "key %s key_sz %d, val_len %d\n",
- get_khandle_from_ino(inode),
- (char *)new_op->
- upcall.req.getxattr.key,
- (int)new_op->
- upcall.req.getxattr.key_sz,
- (int)ret);
- ret = length;
- out_release_op:
- op_release(new_op);
- out_unlock:
- up_read(&orangefs_inode->xattr_sem);
- return ret;
- }
- static int orangefs_inode_removexattr(struct inode *inode, const char *name,
- int flags)
- {
- struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
- struct orangefs_kernel_op_s *new_op = NULL;
- int ret = -ENOMEM;
- if (strlen(name) >= ORANGEFS_MAX_XATTR_NAMELEN)
- return -EINVAL;
- down_write(&orangefs_inode->xattr_sem);
- new_op = op_alloc(ORANGEFS_VFS_OP_REMOVEXATTR);
- if (!new_op)
- goto out_unlock;
- new_op->upcall.req.removexattr.refn = orangefs_inode->refn;
- /*
- * NOTE: Although keys are meant to be NULL terminated
- * textual strings, I am going to explicitly pass the
- * length just in case we change this later on...
- */
- strcpy(new_op->upcall.req.removexattr.key, name);
- new_op->upcall.req.removexattr.key_sz = strlen(name) + 1;
- gossip_debug(GOSSIP_XATTR_DEBUG,
- "orangefs_inode_removexattr: key %s, key_sz %d\n",
- (char *)new_op->upcall.req.removexattr.key,
- (int)new_op->upcall.req.removexattr.key_sz);
- ret = service_operation(new_op,
- "orangefs_inode_removexattr",
- get_interruptible_flag(inode));
- if (ret == -ENOENT) {
- /*
- * Request to replace a non-existent attribute is an error.
- */
- if (flags & XATTR_REPLACE)
- ret = -ENODATA;
- else
- ret = 0;
- }
- gossip_debug(GOSSIP_XATTR_DEBUG,
- "orangefs_inode_removexattr: returning %d\n", ret);
- op_release(new_op);
- out_unlock:
- up_write(&orangefs_inode->xattr_sem);
- return ret;
- }
- /*
- * Tries to set an attribute for a given key on a file.
- *
- * Returns a -ve number on error and 0 on success. Key is text, but value
- * can be binary!
- */
- int orangefs_inode_setxattr(struct inode *inode, const char *name,
- const void *value, size_t size, int flags)
- {
- struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
- struct orangefs_kernel_op_s *new_op;
- int internal_flag = 0;
- int ret = -ENOMEM;
- gossip_debug(GOSSIP_XATTR_DEBUG,
- "%s: name %s, buffer_size %zd\n",
- __func__, name, size);
- if (size > ORANGEFS_MAX_XATTR_VALUELEN)
- return -EINVAL;
- if (strlen(name) >= ORANGEFS_MAX_XATTR_NAMELEN)
- return -EINVAL;
- internal_flag = convert_to_internal_xattr_flags(flags);
- /* This is equivalent to a removexattr */
- if (size == 0 && !value) {
- gossip_debug(GOSSIP_XATTR_DEBUG,
- "removing xattr (%s)\n",
- name);
- return orangefs_inode_removexattr(inode, name, flags);
- }
- gossip_debug(GOSSIP_XATTR_DEBUG,
- "setxattr on inode %pU, name %s\n",
- get_khandle_from_ino(inode),
- name);
- down_write(&orangefs_inode->xattr_sem);
- new_op = op_alloc(ORANGEFS_VFS_OP_SETXATTR);
- if (!new_op)
- goto out_unlock;
- new_op->upcall.req.setxattr.refn = orangefs_inode->refn;
- new_op->upcall.req.setxattr.flags = internal_flag;
- /*
- * NOTE: Although keys are meant to be NULL terminated textual
- * strings, I am going to explicitly pass the length just in
- * case we change this later on...
- */
- strcpy(new_op->upcall.req.setxattr.keyval.key, name);
- new_op->upcall.req.setxattr.keyval.key_sz = strlen(name) + 1;
- memcpy(new_op->upcall.req.setxattr.keyval.val, value, size);
- new_op->upcall.req.setxattr.keyval.val_sz = size;
- gossip_debug(GOSSIP_XATTR_DEBUG,
- "orangefs_inode_setxattr: key %s, key_sz %d "
- " value size %zd\n",
- (char *)new_op->upcall.req.setxattr.keyval.key,
- (int)new_op->upcall.req.setxattr.keyval.key_sz,
- size);
- ret = service_operation(new_op,
- "orangefs_inode_setxattr",
- get_interruptible_flag(inode));
- gossip_debug(GOSSIP_XATTR_DEBUG,
- "orangefs_inode_setxattr: returning %d\n",
- ret);
- /* when request is serviced properly, free req op struct */
- op_release(new_op);
- out_unlock:
- up_write(&orangefs_inode->xattr_sem);
- return ret;
- }
- /*
- * Tries to get a specified object's keys into a user-specified buffer of a
- * given size. Note that like the previous instances of xattr routines, this
- * also allows you to pass in a NULL pointer and 0 size to probe the size for
- * subsequent memory allocations. Thus our return value is always the size of
- * all the keys unless there were errors in fetching the keys!
- */
- ssize_t orangefs_listxattr(struct dentry *dentry, char *buffer, size_t size)
- {
- struct inode *inode = dentry->d_inode;
- struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
- struct orangefs_kernel_op_s *new_op;
- __u64 token = ORANGEFS_ITERATE_START;
- ssize_t ret = -ENOMEM;
- ssize_t total = 0;
- int count_keys = 0;
- int key_size;
- int i = 0;
- int returned_count = 0;
- if (size > 0 && !buffer) {
- gossip_err("%s: bogus NULL pointers\n", __func__);
- return -EINVAL;
- }
- down_read(&orangefs_inode->xattr_sem);
- new_op = op_alloc(ORANGEFS_VFS_OP_LISTXATTR);
- if (!new_op)
- goto out_unlock;
- if (buffer && size > 0)
- memset(buffer, 0, size);
- try_again:
- key_size = 0;
- new_op->upcall.req.listxattr.refn = orangefs_inode->refn;
- new_op->upcall.req.listxattr.token = token;
- new_op->upcall.req.listxattr.requested_count =
- (size == 0) ? 0 : ORANGEFS_MAX_XATTR_LISTLEN;
- ret = service_operation(new_op, __func__,
- get_interruptible_flag(inode));
- if (ret != 0)
- goto done;
- if (size == 0) {
- /*
- * This is a bit of a big upper limit, but I did not want to
- * spend too much time getting this correct, since users end
- * up allocating memory rather than us...
- */
- total = new_op->downcall.resp.listxattr.returned_count *
- ORANGEFS_MAX_XATTR_NAMELEN;
- goto done;
- }
- returned_count = new_op->downcall.resp.listxattr.returned_count;
- if (returned_count < 0 ||
- returned_count > ORANGEFS_MAX_XATTR_LISTLEN) {
- gossip_err("%s: impossible value for returned_count:%d:\n",
- __func__,
- returned_count);
- ret = -EIO;
- goto done;
- }
- /*
- * Check to see how much can be fit in the buffer. Fit only whole keys.
- */
- for (i = 0; i < returned_count; i++) {
- if (new_op->downcall.resp.listxattr.lengths[i] < 0 ||
- new_op->downcall.resp.listxattr.lengths[i] >
- ORANGEFS_MAX_XATTR_NAMELEN) {
- gossip_err("%s: impossible value for lengths[%d]\n",
- __func__,
- new_op->downcall.resp.listxattr.lengths[i]);
- ret = -EIO;
- goto done;
- }
- if (total + new_op->downcall.resp.listxattr.lengths[i] > size)
- goto done;
- /*
- * Since many dumb programs try to setxattr() on our reserved
- * xattrs this is a feeble attempt at defeating those by not
- * listing them in the output of listxattr.. sigh
- */
- if (is_reserved_key(new_op->downcall.resp.listxattr.key +
- key_size,
- new_op->downcall.resp.
- listxattr.lengths[i])) {
- gossip_debug(GOSSIP_XATTR_DEBUG, "Copying key %d -> %s\n",
- i, new_op->downcall.resp.listxattr.key +
- key_size);
- memcpy(buffer + total,
- new_op->downcall.resp.listxattr.key + key_size,
- new_op->downcall.resp.listxattr.lengths[i]);
- total += new_op->downcall.resp.listxattr.lengths[i];
- count_keys++;
- } else {
- gossip_debug(GOSSIP_XATTR_DEBUG, "[RESERVED] key %d -> %s\n",
- i, new_op->downcall.resp.listxattr.key +
- key_size);
- }
- key_size += new_op->downcall.resp.listxattr.lengths[i];
- }
- /*
- * Since the buffer was large enough, we might have to continue
- * fetching more keys!
- */
- token = new_op->downcall.resp.listxattr.token;
- if (token != ORANGEFS_ITERATE_END)
- goto try_again;
- done:
- gossip_debug(GOSSIP_XATTR_DEBUG, "%s: returning %d"
- " [size of buffer %ld] (filled in %d keys)\n",
- __func__,
- ret ? (int)ret : (int)total,
- (long)size,
- count_keys);
- op_release(new_op);
- if (ret == 0)
- ret = total;
- out_unlock:
- up_read(&orangefs_inode->xattr_sem);
- return ret;
- }
- static int orangefs_xattr_set_default(const struct xattr_handler *handler,
- struct dentry *unused,
- struct inode *inode,
- const char *name,
- const void *buffer,
- size_t size,
- int flags)
- {
- return orangefs_inode_setxattr(inode, name, buffer, size, flags);
- }
- static int orangefs_xattr_get_default(const struct xattr_handler *handler,
- struct dentry *unused,
- struct inode *inode,
- const char *name,
- void *buffer,
- size_t size)
- {
- return orangefs_inode_getxattr(inode, name, buffer, size);
- }
- static const struct xattr_handler orangefs_xattr_default_handler = {
- .prefix = "", /* match any name => handlers called with full name */
- .get = orangefs_xattr_get_default,
- .set = orangefs_xattr_set_default,
- };
- const struct xattr_handler *orangefs_xattr_handlers[] = {
- &posix_acl_access_xattr_handler,
- &posix_acl_default_xattr_handler,
- &orangefs_xattr_default_handler,
- NULL
- };
|