|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Copyright (C) 2008 Christoph Hellwig. | 
|  | * Portions Copyright (C) 2000-2008 Silicon Graphics, Inc. | 
|  | */ | 
|  |  | 
|  | #include "xfs.h" | 
|  | #include "xfs_format.h" | 
|  | #include "xfs_log_format.h" | 
|  | #include "xfs_trans_resv.h" | 
|  | #include "xfs_mount.h" | 
|  | #include "xfs_da_format.h" | 
|  | #include "xfs_inode.h" | 
|  | #include "xfs_attr.h" | 
|  | #include "xfs_attr_leaf.h" | 
|  | #include "xfs_acl.h" | 
|  |  | 
|  | #include <linux/posix_acl_xattr.h> | 
|  | #include <linux/xattr.h> | 
|  |  | 
|  |  | 
|  | static int | 
|  | xfs_xattr_get(const struct xattr_handler *handler, struct dentry *unused, | 
|  | struct inode *inode, const char *name, void *value, size_t size) | 
|  | { | 
|  | int xflags = handler->flags; | 
|  | struct xfs_inode *ip = XFS_I(inode); | 
|  | int error, asize = size; | 
|  |  | 
|  | /* Convert Linux syscall to XFS internal ATTR flags */ | 
|  | if (!size) { | 
|  | xflags |= ATTR_KERNOVAL; | 
|  | value = NULL; | 
|  | } | 
|  |  | 
|  | error = xfs_attr_get(ip, (unsigned char *)name, value, &asize, xflags); | 
|  | if (error) | 
|  | return error; | 
|  | return asize; | 
|  | } | 
|  |  | 
|  | void | 
|  | xfs_forget_acl( | 
|  | struct inode		*inode, | 
|  | const char		*name, | 
|  | int			xflags) | 
|  | { | 
|  | /* | 
|  | * Invalidate any cached ACLs if the user has bypassed the ACL | 
|  | * interface. We don't validate the content whatsoever so it is caller | 
|  | * responsibility to provide data in valid format and ensure i_mode is | 
|  | * consistent. | 
|  | */ | 
|  | if (xflags & ATTR_ROOT) { | 
|  | #ifdef CONFIG_XFS_POSIX_ACL | 
|  | if (!strcmp(name, SGI_ACL_FILE)) | 
|  | forget_cached_acl(inode, ACL_TYPE_ACCESS); | 
|  | else if (!strcmp(name, SGI_ACL_DEFAULT)) | 
|  | forget_cached_acl(inode, ACL_TYPE_DEFAULT); | 
|  | #endif | 
|  | } | 
|  | } | 
|  |  | 
|  | static int | 
|  | xfs_xattr_set(const struct xattr_handler *handler, struct dentry *unused, | 
|  | struct inode *inode, const char *name, const void *value, | 
|  | size_t size, int flags) | 
|  | { | 
|  | int			xflags = handler->flags; | 
|  | struct xfs_inode	*ip = XFS_I(inode); | 
|  | int			error; | 
|  |  | 
|  | /* Convert Linux syscall to XFS internal ATTR flags */ | 
|  | if (flags & XATTR_CREATE) | 
|  | xflags |= ATTR_CREATE; | 
|  | if (flags & XATTR_REPLACE) | 
|  | xflags |= ATTR_REPLACE; | 
|  |  | 
|  | if (!value) | 
|  | return xfs_attr_remove(ip, (unsigned char *)name, xflags); | 
|  | error = xfs_attr_set(ip, (unsigned char *)name, | 
|  | (void *)value, size, xflags); | 
|  | if (!error) | 
|  | xfs_forget_acl(inode, name, xflags); | 
|  |  | 
|  | return error; | 
|  | } | 
|  |  | 
|  | static const struct xattr_handler xfs_xattr_user_handler = { | 
|  | .prefix	= XATTR_USER_PREFIX, | 
|  | .flags	= 0, /* no flags implies user namespace */ | 
|  | .get	= xfs_xattr_get, | 
|  | .set	= xfs_xattr_set, | 
|  | }; | 
|  |  | 
|  | static const struct xattr_handler xfs_xattr_trusted_handler = { | 
|  | .prefix	= XATTR_TRUSTED_PREFIX, | 
|  | .flags	= ATTR_ROOT, | 
|  | .get	= xfs_xattr_get, | 
|  | .set	= xfs_xattr_set, | 
|  | }; | 
|  |  | 
|  | static const struct xattr_handler xfs_xattr_security_handler = { | 
|  | .prefix	= XATTR_SECURITY_PREFIX, | 
|  | .flags	= ATTR_SECURE, | 
|  | .get	= xfs_xattr_get, | 
|  | .set	= xfs_xattr_set, | 
|  | }; | 
|  |  | 
|  | const struct xattr_handler *xfs_xattr_handlers[] = { | 
|  | &xfs_xattr_user_handler, | 
|  | &xfs_xattr_trusted_handler, | 
|  | &xfs_xattr_security_handler, | 
|  | #ifdef CONFIG_XFS_POSIX_ACL | 
|  | &posix_acl_access_xattr_handler, | 
|  | &posix_acl_default_xattr_handler, | 
|  | #endif | 
|  | NULL | 
|  | }; | 
|  |  | 
|  | static void | 
|  | __xfs_xattr_put_listent( | 
|  | struct xfs_attr_list_context *context, | 
|  | char *prefix, | 
|  | int prefix_len, | 
|  | unsigned char *name, | 
|  | int namelen) | 
|  | { | 
|  | char *offset; | 
|  | int arraytop; | 
|  |  | 
|  | if (context->count < 0 || context->seen_enough) | 
|  | return; | 
|  |  | 
|  | if (!context->alist) | 
|  | goto compute_size; | 
|  |  | 
|  | arraytop = context->count + prefix_len + namelen + 1; | 
|  | if (arraytop > context->firstu) { | 
|  | context->count = -1;	/* insufficient space */ | 
|  | context->seen_enough = 1; | 
|  | return; | 
|  | } | 
|  | offset = (char *)context->alist + context->count; | 
|  | strncpy(offset, prefix, prefix_len); | 
|  | offset += prefix_len; | 
|  | strncpy(offset, (char *)name, namelen);			/* real name */ | 
|  | offset += namelen; | 
|  | *offset = '\0'; | 
|  |  | 
|  | compute_size: | 
|  | context->count += prefix_len + namelen + 1; | 
|  | return; | 
|  | } | 
|  |  | 
|  | static void | 
|  | xfs_xattr_put_listent( | 
|  | struct xfs_attr_list_context *context, | 
|  | int		flags, | 
|  | unsigned char	*name, | 
|  | int		namelen, | 
|  | int		valuelen) | 
|  | { | 
|  | char *prefix; | 
|  | int prefix_len; | 
|  |  | 
|  | ASSERT(context->count >= 0); | 
|  |  | 
|  | if (flags & XFS_ATTR_ROOT) { | 
|  | #ifdef CONFIG_XFS_POSIX_ACL | 
|  | if (namelen == SGI_ACL_FILE_SIZE && | 
|  | strncmp(name, SGI_ACL_FILE, | 
|  | SGI_ACL_FILE_SIZE) == 0) { | 
|  | __xfs_xattr_put_listent( | 
|  | context, XATTR_SYSTEM_PREFIX, | 
|  | XATTR_SYSTEM_PREFIX_LEN, | 
|  | XATTR_POSIX_ACL_ACCESS, | 
|  | strlen(XATTR_POSIX_ACL_ACCESS)); | 
|  | } else if (namelen == SGI_ACL_DEFAULT_SIZE && | 
|  | strncmp(name, SGI_ACL_DEFAULT, | 
|  | SGI_ACL_DEFAULT_SIZE) == 0) { | 
|  | __xfs_xattr_put_listent( | 
|  | context, XATTR_SYSTEM_PREFIX, | 
|  | XATTR_SYSTEM_PREFIX_LEN, | 
|  | XATTR_POSIX_ACL_DEFAULT, | 
|  | strlen(XATTR_POSIX_ACL_DEFAULT)); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * Only show root namespace entries if we are actually allowed to | 
|  | * see them. | 
|  | */ | 
|  | if (!capable(CAP_SYS_ADMIN)) | 
|  | return; | 
|  |  | 
|  | prefix = XATTR_TRUSTED_PREFIX; | 
|  | prefix_len = XATTR_TRUSTED_PREFIX_LEN; | 
|  | } else if (flags & XFS_ATTR_SECURE) { | 
|  | prefix = XATTR_SECURITY_PREFIX; | 
|  | prefix_len = XATTR_SECURITY_PREFIX_LEN; | 
|  | } else { | 
|  | prefix = XATTR_USER_PREFIX; | 
|  | prefix_len = XATTR_USER_PREFIX_LEN; | 
|  | } | 
|  |  | 
|  | __xfs_xattr_put_listent(context, prefix, prefix_len, name, | 
|  | namelen); | 
|  | return; | 
|  | } | 
|  |  | 
|  | ssize_t | 
|  | xfs_vn_listxattr( | 
|  | struct dentry	*dentry, | 
|  | char		*data, | 
|  | size_t		size) | 
|  | { | 
|  | struct xfs_attr_list_context context; | 
|  | struct attrlist_cursor_kern cursor = { 0 }; | 
|  | struct inode	*inode = d_inode(dentry); | 
|  | int		error; | 
|  |  | 
|  | /* | 
|  | * First read the regular on-disk attributes. | 
|  | */ | 
|  | memset(&context, 0, sizeof(context)); | 
|  | context.dp = XFS_I(inode); | 
|  | context.cursor = &cursor; | 
|  | context.resynch = 1; | 
|  | context.alist = size ? data : NULL; | 
|  | context.bufsize = size; | 
|  | context.firstu = context.bufsize; | 
|  | context.put_listent = xfs_xattr_put_listent; | 
|  |  | 
|  | error = xfs_attr_list_int(&context); | 
|  | if (error) | 
|  | return error; | 
|  | if (context.count < 0) | 
|  | return -ERANGE; | 
|  |  | 
|  | return context.count; | 
|  | } |