162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2008 Christoph Hellwig. 462306a36Sopenharmony_ci * Portions Copyright (C) 2000-2008 Silicon Graphics, Inc. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "xfs.h" 862306a36Sopenharmony_ci#include "xfs_shared.h" 962306a36Sopenharmony_ci#include "xfs_format.h" 1062306a36Sopenharmony_ci#include "xfs_log_format.h" 1162306a36Sopenharmony_ci#include "xfs_da_format.h" 1262306a36Sopenharmony_ci#include "xfs_trans_resv.h" 1362306a36Sopenharmony_ci#include "xfs_mount.h" 1462306a36Sopenharmony_ci#include "xfs_inode.h" 1562306a36Sopenharmony_ci#include "xfs_da_btree.h" 1662306a36Sopenharmony_ci#include "xfs_attr.h" 1762306a36Sopenharmony_ci#include "xfs_acl.h" 1862306a36Sopenharmony_ci#include "xfs_log.h" 1962306a36Sopenharmony_ci#include "xfs_xattr.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/posix_acl_xattr.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * Get permission to use log-assisted atomic exchange of file extents. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * Callers must not be running any transactions or hold any inode locks, and 2762306a36Sopenharmony_ci * they must release the permission by calling xlog_drop_incompat_feat 2862306a36Sopenharmony_ci * when they're done. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_cistatic inline int 3162306a36Sopenharmony_cixfs_attr_grab_log_assist( 3262306a36Sopenharmony_ci struct xfs_mount *mp) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci int error = 0; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci /* 3762306a36Sopenharmony_ci * Protect ourselves from an idle log clearing the logged xattrs log 3862306a36Sopenharmony_ci * incompat feature bit. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci xlog_use_incompat_feat(mp->m_log); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* 4362306a36Sopenharmony_ci * If log-assisted xattrs are already enabled, the caller can use the 4462306a36Sopenharmony_ci * log assisted swap functions with the log-incompat reference we got. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci if (xfs_sb_version_haslogxattrs(&mp->m_sb)) 4762306a36Sopenharmony_ci return 0; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* 5062306a36Sopenharmony_ci * Check if the filesystem featureset is new enough to set this log 5162306a36Sopenharmony_ci * incompat feature bit. Strictly speaking, the minimum requirement is 5262306a36Sopenharmony_ci * a V5 filesystem for the superblock field, but we'll require rmap 5362306a36Sopenharmony_ci * or reflink to avoid having to deal with really old kernels. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci if (!xfs_has_reflink(mp) && !xfs_has_rmapbt(mp)) { 5662306a36Sopenharmony_ci error = -EOPNOTSUPP; 5762306a36Sopenharmony_ci goto drop_incompat; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* Enable log-assisted xattrs. */ 6162306a36Sopenharmony_ci error = xfs_add_incompat_log_feature(mp, 6262306a36Sopenharmony_ci XFS_SB_FEAT_INCOMPAT_LOG_XATTRS); 6362306a36Sopenharmony_ci if (error) 6462306a36Sopenharmony_ci goto drop_incompat; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci xfs_warn_mount(mp, XFS_OPSTATE_WARNED_LARP, 6762306a36Sopenharmony_ci "EXPERIMENTAL logged extended attributes feature in use. Use at your own risk!"); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return 0; 7062306a36Sopenharmony_cidrop_incompat: 7162306a36Sopenharmony_ci xlog_drop_incompat_feat(mp->m_log); 7262306a36Sopenharmony_ci return error; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic inline void 7662306a36Sopenharmony_cixfs_attr_rele_log_assist( 7762306a36Sopenharmony_ci struct xfs_mount *mp) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci xlog_drop_incompat_feat(mp->m_log); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic inline bool 8362306a36Sopenharmony_cixfs_attr_want_log_assist( 8462306a36Sopenharmony_ci struct xfs_mount *mp) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci#ifdef DEBUG 8762306a36Sopenharmony_ci /* Logged xattrs require a V5 super for log_incompat */ 8862306a36Sopenharmony_ci return xfs_has_crc(mp) && xfs_globals.larp; 8962306a36Sopenharmony_ci#else 9062306a36Sopenharmony_ci return false; 9162306a36Sopenharmony_ci#endif 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* 9562306a36Sopenharmony_ci * Set or remove an xattr, having grabbed the appropriate logging resources 9662306a36Sopenharmony_ci * prior to calling libxfs. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ciint 9962306a36Sopenharmony_cixfs_attr_change( 10062306a36Sopenharmony_ci struct xfs_da_args *args) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct xfs_mount *mp = args->dp->i_mount; 10362306a36Sopenharmony_ci bool use_logging = false; 10462306a36Sopenharmony_ci int error; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci ASSERT(!(args->op_flags & XFS_DA_OP_LOGGED)); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (xfs_attr_want_log_assist(mp)) { 10962306a36Sopenharmony_ci error = xfs_attr_grab_log_assist(mp); 11062306a36Sopenharmony_ci if (error) 11162306a36Sopenharmony_ci return error; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci args->op_flags |= XFS_DA_OP_LOGGED; 11462306a36Sopenharmony_ci use_logging = true; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci error = xfs_attr_set(args); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (use_logging) 12062306a36Sopenharmony_ci xfs_attr_rele_log_assist(mp); 12162306a36Sopenharmony_ci return error; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int 12662306a36Sopenharmony_cixfs_xattr_get(const struct xattr_handler *handler, struct dentry *unused, 12762306a36Sopenharmony_ci struct inode *inode, const char *name, void *value, size_t size) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct xfs_da_args args = { 13062306a36Sopenharmony_ci .dp = XFS_I(inode), 13162306a36Sopenharmony_ci .attr_filter = handler->flags, 13262306a36Sopenharmony_ci .name = name, 13362306a36Sopenharmony_ci .namelen = strlen(name), 13462306a36Sopenharmony_ci .value = value, 13562306a36Sopenharmony_ci .valuelen = size, 13662306a36Sopenharmony_ci }; 13762306a36Sopenharmony_ci int error; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci error = xfs_attr_get(&args); 14062306a36Sopenharmony_ci if (error) 14162306a36Sopenharmony_ci return error; 14262306a36Sopenharmony_ci return args.valuelen; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int 14662306a36Sopenharmony_cixfs_xattr_set(const struct xattr_handler *handler, 14762306a36Sopenharmony_ci struct mnt_idmap *idmap, struct dentry *unused, 14862306a36Sopenharmony_ci struct inode *inode, const char *name, const void *value, 14962306a36Sopenharmony_ci size_t size, int flags) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct xfs_da_args args = { 15262306a36Sopenharmony_ci .dp = XFS_I(inode), 15362306a36Sopenharmony_ci .attr_filter = handler->flags, 15462306a36Sopenharmony_ci .attr_flags = flags, 15562306a36Sopenharmony_ci .name = name, 15662306a36Sopenharmony_ci .namelen = strlen(name), 15762306a36Sopenharmony_ci .value = (void *)value, 15862306a36Sopenharmony_ci .valuelen = size, 15962306a36Sopenharmony_ci }; 16062306a36Sopenharmony_ci int error; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci error = xfs_attr_change(&args); 16362306a36Sopenharmony_ci if (!error && (handler->flags & XFS_ATTR_ROOT)) 16462306a36Sopenharmony_ci xfs_forget_acl(inode, name); 16562306a36Sopenharmony_ci return error; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic const struct xattr_handler xfs_xattr_user_handler = { 16962306a36Sopenharmony_ci .prefix = XATTR_USER_PREFIX, 17062306a36Sopenharmony_ci .flags = 0, /* no flags implies user namespace */ 17162306a36Sopenharmony_ci .get = xfs_xattr_get, 17262306a36Sopenharmony_ci .set = xfs_xattr_set, 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic const struct xattr_handler xfs_xattr_trusted_handler = { 17662306a36Sopenharmony_ci .prefix = XATTR_TRUSTED_PREFIX, 17762306a36Sopenharmony_ci .flags = XFS_ATTR_ROOT, 17862306a36Sopenharmony_ci .get = xfs_xattr_get, 17962306a36Sopenharmony_ci .set = xfs_xattr_set, 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic const struct xattr_handler xfs_xattr_security_handler = { 18362306a36Sopenharmony_ci .prefix = XATTR_SECURITY_PREFIX, 18462306a36Sopenharmony_ci .flags = XFS_ATTR_SECURE, 18562306a36Sopenharmony_ci .get = xfs_xattr_get, 18662306a36Sopenharmony_ci .set = xfs_xattr_set, 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ciconst struct xattr_handler *xfs_xattr_handlers[] = { 19062306a36Sopenharmony_ci &xfs_xattr_user_handler, 19162306a36Sopenharmony_ci &xfs_xattr_trusted_handler, 19262306a36Sopenharmony_ci &xfs_xattr_security_handler, 19362306a36Sopenharmony_ci NULL 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic void 19762306a36Sopenharmony_ci__xfs_xattr_put_listent( 19862306a36Sopenharmony_ci struct xfs_attr_list_context *context, 19962306a36Sopenharmony_ci char *prefix, 20062306a36Sopenharmony_ci int prefix_len, 20162306a36Sopenharmony_ci unsigned char *name, 20262306a36Sopenharmony_ci int namelen) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci char *offset; 20562306a36Sopenharmony_ci int arraytop; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (context->count < 0 || context->seen_enough) 20862306a36Sopenharmony_ci return; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (!context->buffer) 21162306a36Sopenharmony_ci goto compute_size; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci arraytop = context->count + prefix_len + namelen + 1; 21462306a36Sopenharmony_ci if (arraytop > context->firstu) { 21562306a36Sopenharmony_ci context->count = -1; /* insufficient space */ 21662306a36Sopenharmony_ci context->seen_enough = 1; 21762306a36Sopenharmony_ci return; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci offset = context->buffer + context->count; 22062306a36Sopenharmony_ci memcpy(offset, prefix, prefix_len); 22162306a36Sopenharmony_ci offset += prefix_len; 22262306a36Sopenharmony_ci strncpy(offset, (char *)name, namelen); /* real name */ 22362306a36Sopenharmony_ci offset += namelen; 22462306a36Sopenharmony_ci *offset = '\0'; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cicompute_size: 22762306a36Sopenharmony_ci context->count += prefix_len + namelen + 1; 22862306a36Sopenharmony_ci return; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic void 23262306a36Sopenharmony_cixfs_xattr_put_listent( 23362306a36Sopenharmony_ci struct xfs_attr_list_context *context, 23462306a36Sopenharmony_ci int flags, 23562306a36Sopenharmony_ci unsigned char *name, 23662306a36Sopenharmony_ci int namelen, 23762306a36Sopenharmony_ci int valuelen) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci char *prefix; 24062306a36Sopenharmony_ci int prefix_len; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci ASSERT(context->count >= 0); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (flags & XFS_ATTR_ROOT) { 24562306a36Sopenharmony_ci#ifdef CONFIG_XFS_POSIX_ACL 24662306a36Sopenharmony_ci if (namelen == SGI_ACL_FILE_SIZE && 24762306a36Sopenharmony_ci strncmp(name, SGI_ACL_FILE, 24862306a36Sopenharmony_ci SGI_ACL_FILE_SIZE) == 0) { 24962306a36Sopenharmony_ci __xfs_xattr_put_listent( 25062306a36Sopenharmony_ci context, XATTR_SYSTEM_PREFIX, 25162306a36Sopenharmony_ci XATTR_SYSTEM_PREFIX_LEN, 25262306a36Sopenharmony_ci XATTR_POSIX_ACL_ACCESS, 25362306a36Sopenharmony_ci strlen(XATTR_POSIX_ACL_ACCESS)); 25462306a36Sopenharmony_ci } else if (namelen == SGI_ACL_DEFAULT_SIZE && 25562306a36Sopenharmony_ci strncmp(name, SGI_ACL_DEFAULT, 25662306a36Sopenharmony_ci SGI_ACL_DEFAULT_SIZE) == 0) { 25762306a36Sopenharmony_ci __xfs_xattr_put_listent( 25862306a36Sopenharmony_ci context, XATTR_SYSTEM_PREFIX, 25962306a36Sopenharmony_ci XATTR_SYSTEM_PREFIX_LEN, 26062306a36Sopenharmony_ci XATTR_POSIX_ACL_DEFAULT, 26162306a36Sopenharmony_ci strlen(XATTR_POSIX_ACL_DEFAULT)); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci#endif 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* 26662306a36Sopenharmony_ci * Only show root namespace entries if we are actually allowed to 26762306a36Sopenharmony_ci * see them. 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 27062306a36Sopenharmony_ci return; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci prefix = XATTR_TRUSTED_PREFIX; 27362306a36Sopenharmony_ci prefix_len = XATTR_TRUSTED_PREFIX_LEN; 27462306a36Sopenharmony_ci } else if (flags & XFS_ATTR_SECURE) { 27562306a36Sopenharmony_ci prefix = XATTR_SECURITY_PREFIX; 27662306a36Sopenharmony_ci prefix_len = XATTR_SECURITY_PREFIX_LEN; 27762306a36Sopenharmony_ci } else { 27862306a36Sopenharmony_ci prefix = XATTR_USER_PREFIX; 27962306a36Sopenharmony_ci prefix_len = XATTR_USER_PREFIX_LEN; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci __xfs_xattr_put_listent(context, prefix, prefix_len, name, 28362306a36Sopenharmony_ci namelen); 28462306a36Sopenharmony_ci return; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cissize_t 28862306a36Sopenharmony_cixfs_vn_listxattr( 28962306a36Sopenharmony_ci struct dentry *dentry, 29062306a36Sopenharmony_ci char *data, 29162306a36Sopenharmony_ci size_t size) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci struct xfs_attr_list_context context; 29462306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 29562306a36Sopenharmony_ci int error; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* 29862306a36Sopenharmony_ci * First read the regular on-disk attributes. 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_ci memset(&context, 0, sizeof(context)); 30162306a36Sopenharmony_ci context.dp = XFS_I(inode); 30262306a36Sopenharmony_ci context.resynch = 1; 30362306a36Sopenharmony_ci context.buffer = size ? data : NULL; 30462306a36Sopenharmony_ci context.bufsize = size; 30562306a36Sopenharmony_ci context.firstu = context.bufsize; 30662306a36Sopenharmony_ci context.put_listent = xfs_xattr_put_listent; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci error = xfs_attr_list(&context); 30962306a36Sopenharmony_ci if (error) 31062306a36Sopenharmony_ci return error; 31162306a36Sopenharmony_ci if (context.count < 0) 31262306a36Sopenharmony_ci return -ERANGE; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return context.count; 31562306a36Sopenharmony_ci} 316