xref: /openbmc/linux/fs/xfs/xfs_xattr.c (revision 34389616)
10b61f8a4SDave Chinner // SPDX-License-Identifier: GPL-2.0
2c59d87c4SChristoph Hellwig /*
3c59d87c4SChristoph Hellwig  * Copyright (C) 2008 Christoph Hellwig.
4c59d87c4SChristoph Hellwig  * Portions Copyright (C) 2000-2008 Silicon Graphics, Inc.
5c59d87c4SChristoph Hellwig  */
6c59d87c4SChristoph Hellwig 
7c59d87c4SChristoph Hellwig #include "xfs.h"
85467b34bSDarrick J. Wong #include "xfs_shared.h"
9a4fbe6abSDave Chinner #include "xfs_format.h"
1069432832SDave Chinner #include "xfs_log_format.h"
1157062787SDave Chinner #include "xfs_da_format.h"
12383e32b0SDarrick J. Wong #include "xfs_trans_resv.h"
13383e32b0SDarrick J. Wong #include "xfs_mount.h"
14c59d87c4SChristoph Hellwig #include "xfs_inode.h"
15e0c41089SDave Chinner #include "xfs_da_btree.h"
16c59d87c4SChristoph Hellwig #include "xfs_attr.h"
175f213ddbSDarrick J. Wong #include "xfs_acl.h"
18d9c61ccbSDarrick J. Wong #include "xfs_log.h"
19d9c61ccbSDarrick J. Wong #include "xfs_xattr.h"
20c59d87c4SChristoph Hellwig 
21c59d87c4SChristoph Hellwig #include <linux/posix_acl_xattr.h>
22c59d87c4SChristoph Hellwig 
23d9c61ccbSDarrick J. Wong /*
24d9c61ccbSDarrick J. Wong  * Get permission to use log-assisted atomic exchange of file extents.
25d9c61ccbSDarrick J. Wong  *
26d9c61ccbSDarrick J. Wong  * Callers must not be running any transactions or hold any inode locks, and
27d9c61ccbSDarrick J. Wong  * they must release the permission by calling xlog_drop_incompat_feat
28d9c61ccbSDarrick J. Wong  * when they're done.
29d9c61ccbSDarrick J. Wong  */
30efc2efebSDarrick J. Wong static inline int
xfs_attr_grab_log_assist(struct xfs_mount * mp)31d9c61ccbSDarrick J. Wong xfs_attr_grab_log_assist(
32d9c61ccbSDarrick J. Wong 	struct xfs_mount	*mp)
33d9c61ccbSDarrick J. Wong {
34d9c61ccbSDarrick J. Wong 	int			error = 0;
35d9c61ccbSDarrick J. Wong 
36d9c61ccbSDarrick J. Wong 	/*
37d9c61ccbSDarrick J. Wong 	 * Protect ourselves from an idle log clearing the logged xattrs log
38d9c61ccbSDarrick J. Wong 	 * incompat feature bit.
39d9c61ccbSDarrick J. Wong 	 */
40d9c61ccbSDarrick J. Wong 	xlog_use_incompat_feat(mp->m_log);
41d9c61ccbSDarrick J. Wong 
42d9c61ccbSDarrick J. Wong 	/*
43d9c61ccbSDarrick J. Wong 	 * If log-assisted xattrs are already enabled, the caller can use the
44d9c61ccbSDarrick J. Wong 	 * log assisted swap functions with the log-incompat reference we got.
45d9c61ccbSDarrick J. Wong 	 */
46d9c61ccbSDarrick J. Wong 	if (xfs_sb_version_haslogxattrs(&mp->m_sb))
47d9c61ccbSDarrick J. Wong 		return 0;
48d9c61ccbSDarrick J. Wong 
49*34389616SDarrick J. Wong 	/*
50*34389616SDarrick J. Wong 	 * Check if the filesystem featureset is new enough to set this log
51*34389616SDarrick J. Wong 	 * incompat feature bit.  Strictly speaking, the minimum requirement is
52*34389616SDarrick J. Wong 	 * a V5 filesystem for the superblock field, but we'll require rmap
53*34389616SDarrick J. Wong 	 * or reflink to avoid having to deal with really old kernels.
54*34389616SDarrick J. Wong 	 */
55*34389616SDarrick J. Wong 	if (!xfs_has_reflink(mp) && !xfs_has_rmapbt(mp)) {
56*34389616SDarrick J. Wong 		error = -EOPNOTSUPP;
57*34389616SDarrick J. Wong 		goto drop_incompat;
58*34389616SDarrick J. Wong 	}
59*34389616SDarrick J. Wong 
60d9c61ccbSDarrick J. Wong 	/* Enable log-assisted xattrs. */
61d9c61ccbSDarrick J. Wong 	error = xfs_add_incompat_log_feature(mp,
62d9c61ccbSDarrick J. Wong 			XFS_SB_FEAT_INCOMPAT_LOG_XATTRS);
63d9c61ccbSDarrick J. Wong 	if (error)
64d9c61ccbSDarrick J. Wong 		goto drop_incompat;
65d9c61ccbSDarrick J. Wong 
66d9c61ccbSDarrick J. Wong 	xfs_warn_mount(mp, XFS_OPSTATE_WARNED_LARP,
67d9c61ccbSDarrick J. Wong  "EXPERIMENTAL logged extended attributes feature in use. Use at your own risk!");
68d9c61ccbSDarrick J. Wong 
69d9c61ccbSDarrick J. Wong 	return 0;
70d9c61ccbSDarrick J. Wong drop_incompat:
71d9c61ccbSDarrick J. Wong 	xlog_drop_incompat_feat(mp->m_log);
72d9c61ccbSDarrick J. Wong 	return error;
73d9c61ccbSDarrick J. Wong }
74d9c61ccbSDarrick J. Wong 
75efc2efebSDarrick J. Wong static inline void
xfs_attr_rele_log_assist(struct xfs_mount * mp)76d9c61ccbSDarrick J. Wong xfs_attr_rele_log_assist(
77d9c61ccbSDarrick J. Wong 	struct xfs_mount	*mp)
78d9c61ccbSDarrick J. Wong {
79d9c61ccbSDarrick J. Wong 	xlog_drop_incompat_feat(mp->m_log);
80d9c61ccbSDarrick J. Wong }
81c59d87c4SChristoph Hellwig 
82f4288f01SDarrick J. Wong static inline bool
xfs_attr_want_log_assist(struct xfs_mount * mp)83f4288f01SDarrick J. Wong xfs_attr_want_log_assist(
84f4288f01SDarrick J. Wong 	struct xfs_mount	*mp)
85f4288f01SDarrick J. Wong {
86f4288f01SDarrick J. Wong #ifdef DEBUG
87f4288f01SDarrick J. Wong 	/* Logged xattrs require a V5 super for log_incompat */
88f4288f01SDarrick J. Wong 	return xfs_has_crc(mp) && xfs_globals.larp;
89f4288f01SDarrick J. Wong #else
90f4288f01SDarrick J. Wong 	return false;
91f4288f01SDarrick J. Wong #endif
92f4288f01SDarrick J. Wong }
93f4288f01SDarrick J. Wong 
94efc2efebSDarrick J. Wong /*
95efc2efebSDarrick J. Wong  * Set or remove an xattr, having grabbed the appropriate logging resources
96efc2efebSDarrick J. Wong  * prior to calling libxfs.
97efc2efebSDarrick J. Wong  */
98efc2efebSDarrick J. Wong int
xfs_attr_change(struct xfs_da_args * args)99efc2efebSDarrick J. Wong xfs_attr_change(
100efc2efebSDarrick J. Wong 	struct xfs_da_args	*args)
101efc2efebSDarrick J. Wong {
102efc2efebSDarrick J. Wong 	struct xfs_mount	*mp = args->dp->i_mount;
103efc2efebSDarrick J. Wong 	bool			use_logging = false;
104efc2efebSDarrick J. Wong 	int			error;
105efc2efebSDarrick J. Wong 
106f4288f01SDarrick J. Wong 	ASSERT(!(args->op_flags & XFS_DA_OP_LOGGED));
107f4288f01SDarrick J. Wong 
108f4288f01SDarrick J. Wong 	if (xfs_attr_want_log_assist(mp)) {
109efc2efebSDarrick J. Wong 		error = xfs_attr_grab_log_assist(mp);
110efc2efebSDarrick J. Wong 		if (error)
111efc2efebSDarrick J. Wong 			return error;
112efc2efebSDarrick J. Wong 
113f4288f01SDarrick J. Wong 		args->op_flags |= XFS_DA_OP_LOGGED;
114efc2efebSDarrick J. Wong 		use_logging = true;
115efc2efebSDarrick J. Wong 	}
116efc2efebSDarrick J. Wong 
117efc2efebSDarrick J. Wong 	error = xfs_attr_set(args);
118efc2efebSDarrick J. Wong 
119efc2efebSDarrick J. Wong 	if (use_logging)
120efc2efebSDarrick J. Wong 		xfs_attr_rele_log_assist(mp);
121efc2efebSDarrick J. Wong 	return error;
122efc2efebSDarrick J. Wong }
123efc2efebSDarrick J. Wong 
124efc2efebSDarrick J. Wong 
125c59d87c4SChristoph Hellwig static int
xfs_xattr_get(const struct xattr_handler * handler,struct dentry * unused,struct inode * inode,const char * name,void * value,size_t size)126b296821aSAl Viro xfs_xattr_get(const struct xattr_handler *handler, struct dentry *unused,
127b296821aSAl Viro 		struct inode *inode, const char *name, void *value, size_t size)
128c59d87c4SChristoph Hellwig {
129e5171d7eSChristoph Hellwig 	struct xfs_da_args	args = {
130e5171d7eSChristoph Hellwig 		.dp		= XFS_I(inode),
131d5f0f49aSChristoph Hellwig 		.attr_filter	= handler->flags,
132e5171d7eSChristoph Hellwig 		.name		= name,
133e5171d7eSChristoph Hellwig 		.namelen	= strlen(name),
134e5171d7eSChristoph Hellwig 		.value		= value,
135e5171d7eSChristoph Hellwig 		.valuelen	= size,
136e5171d7eSChristoph Hellwig 	};
137e5171d7eSChristoph Hellwig 	int			error;
138c59d87c4SChristoph Hellwig 
139e5171d7eSChristoph Hellwig 	error = xfs_attr_get(&args);
140c59d87c4SChristoph Hellwig 	if (error)
141c59d87c4SChristoph Hellwig 		return error;
142e5171d7eSChristoph Hellwig 	return args.valuelen;
143c59d87c4SChristoph Hellwig }
144c59d87c4SChristoph Hellwig 
145c59d87c4SChristoph Hellwig static int
xfs_xattr_set(const struct xattr_handler * handler,struct mnt_idmap * idmap,struct dentry * unused,struct inode * inode,const char * name,const void * value,size_t size,int flags)146e65ce2a5SChristian Brauner xfs_xattr_set(const struct xattr_handler *handler,
14739f60c1cSChristian Brauner 	      struct mnt_idmap *idmap, struct dentry *unused,
14859301226SAl Viro 	      struct inode *inode, const char *name, const void *value,
14959301226SAl Viro 	      size_t size, int flags)
150c59d87c4SChristoph Hellwig {
151a2544622SChristoph Hellwig 	struct xfs_da_args	args = {
152a2544622SChristoph Hellwig 		.dp		= XFS_I(inode),
153d5f0f49aSChristoph Hellwig 		.attr_filter	= handler->flags,
154d5f0f49aSChristoph Hellwig 		.attr_flags	= flags,
155a2544622SChristoph Hellwig 		.name		= name,
156a2544622SChristoph Hellwig 		.namelen	= strlen(name),
157a2544622SChristoph Hellwig 		.value		= (void *)value,
158a2544622SChristoph Hellwig 		.valuelen	= size,
159a2544622SChristoph Hellwig 	};
16067d8e04eSBrian Foster 	int			error;
161c59d87c4SChristoph Hellwig 
162efc2efebSDarrick J. Wong 	error = xfs_attr_change(&args);
163d5f0f49aSChristoph Hellwig 	if (!error && (handler->flags & XFS_ATTR_ROOT))
1645a3930e2SChristoph Hellwig 		xfs_forget_acl(inode, name);
16567d8e04eSBrian Foster 	return error;
166c59d87c4SChristoph Hellwig }
167c59d87c4SChristoph Hellwig 
168c59d87c4SChristoph Hellwig static const struct xattr_handler xfs_xattr_user_handler = {
169c59d87c4SChristoph Hellwig 	.prefix	= XATTR_USER_PREFIX,
170c59d87c4SChristoph Hellwig 	.flags	= 0, /* no flags implies user namespace */
171c59d87c4SChristoph Hellwig 	.get	= xfs_xattr_get,
172c59d87c4SChristoph Hellwig 	.set	= xfs_xattr_set,
173c59d87c4SChristoph Hellwig };
174c59d87c4SChristoph Hellwig 
175c59d87c4SChristoph Hellwig static const struct xattr_handler xfs_xattr_trusted_handler = {
176c59d87c4SChristoph Hellwig 	.prefix	= XATTR_TRUSTED_PREFIX,
177d5f0f49aSChristoph Hellwig 	.flags	= XFS_ATTR_ROOT,
178c59d87c4SChristoph Hellwig 	.get	= xfs_xattr_get,
179c59d87c4SChristoph Hellwig 	.set	= xfs_xattr_set,
180c59d87c4SChristoph Hellwig };
181c59d87c4SChristoph Hellwig 
182c59d87c4SChristoph Hellwig static const struct xattr_handler xfs_xattr_security_handler = {
183c59d87c4SChristoph Hellwig 	.prefix	= XATTR_SECURITY_PREFIX,
184d5f0f49aSChristoph Hellwig 	.flags	= XFS_ATTR_SECURE,
185c59d87c4SChristoph Hellwig 	.get	= xfs_xattr_get,
186c59d87c4SChristoph Hellwig 	.set	= xfs_xattr_set,
187c59d87c4SChristoph Hellwig };
188c59d87c4SChristoph Hellwig 
189c59d87c4SChristoph Hellwig const struct xattr_handler *xfs_xattr_handlers[] = {
190c59d87c4SChristoph Hellwig 	&xfs_xattr_user_handler,
191c59d87c4SChristoph Hellwig 	&xfs_xattr_trusted_handler,
192c59d87c4SChristoph Hellwig 	&xfs_xattr_security_handler,
193c59d87c4SChristoph Hellwig 	NULL
194c59d87c4SChristoph Hellwig };
195c59d87c4SChristoph Hellwig 
196f7a136aeSEric Sandeen static void
__xfs_xattr_put_listent(struct xfs_attr_list_context * context,char * prefix,int prefix_len,unsigned char * name,int namelen)1975d92b75cSAndreas Gruenbacher __xfs_xattr_put_listent(
1985d92b75cSAndreas Gruenbacher 	struct xfs_attr_list_context *context,
1995d92b75cSAndreas Gruenbacher 	char *prefix,
2005d92b75cSAndreas Gruenbacher 	int prefix_len,
2015d92b75cSAndreas Gruenbacher 	unsigned char *name,
2025d92b75cSAndreas Gruenbacher 	int namelen)
203c59d87c4SChristoph Hellwig {
2045d92b75cSAndreas Gruenbacher 	char *offset;
2055d92b75cSAndreas Gruenbacher 	int arraytop;
206c59d87c4SChristoph Hellwig 
2073b50086fSDarrick J. Wong 	if (context->count < 0 || context->seen_enough)
2083b50086fSDarrick J. Wong 		return;
2093b50086fSDarrick J. Wong 
210a9c8c69bSChristoph Hellwig 	if (!context->buffer)
2115d92b75cSAndreas Gruenbacher 		goto compute_size;
2125d92b75cSAndreas Gruenbacher 
2135d92b75cSAndreas Gruenbacher 	arraytop = context->count + prefix_len + namelen + 1;
2145d92b75cSAndreas Gruenbacher 	if (arraytop > context->firstu) {
2155d92b75cSAndreas Gruenbacher 		context->count = -1;	/* insufficient space */
216791cc43bSArtem Savkov 		context->seen_enough = 1;
217f7a136aeSEric Sandeen 		return;
2185d92b75cSAndreas Gruenbacher 	}
219a9c8c69bSChristoph Hellwig 	offset = context->buffer + context->count;
220fd5beaffSDarrick J. Wong 	memcpy(offset, prefix, prefix_len);
2215d92b75cSAndreas Gruenbacher 	offset += prefix_len;
2225d92b75cSAndreas Gruenbacher 	strncpy(offset, (char *)name, namelen);			/* real name */
2235d92b75cSAndreas Gruenbacher 	offset += namelen;
2245d92b75cSAndreas Gruenbacher 	*offset = '\0';
2255d92b75cSAndreas Gruenbacher 
2265d92b75cSAndreas Gruenbacher compute_size:
2275d92b75cSAndreas Gruenbacher 	context->count += prefix_len + namelen + 1;
228f7a136aeSEric Sandeen 	return;
229c59d87c4SChristoph Hellwig }
230c59d87c4SChristoph Hellwig 
231f7a136aeSEric Sandeen static void
xfs_xattr_put_listent(struct xfs_attr_list_context * context,int flags,unsigned char * name,int namelen,int valuelen)232c59d87c4SChristoph Hellwig xfs_xattr_put_listent(
233c59d87c4SChristoph Hellwig 	struct xfs_attr_list_context *context,
234c59d87c4SChristoph Hellwig 	int		flags,
235c59d87c4SChristoph Hellwig 	unsigned char	*name,
236c59d87c4SChristoph Hellwig 	int		namelen,
237e5bd12bfSEric Sandeen 	int		valuelen)
238c59d87c4SChristoph Hellwig {
2395d92b75cSAndreas Gruenbacher 	char *prefix;
2405d92b75cSAndreas Gruenbacher 	int prefix_len;
241c59d87c4SChristoph Hellwig 
242c59d87c4SChristoph Hellwig 	ASSERT(context->count >= 0);
243c59d87c4SChristoph Hellwig 
2445d92b75cSAndreas Gruenbacher 	if (flags & XFS_ATTR_ROOT) {
2455d92b75cSAndreas Gruenbacher #ifdef CONFIG_XFS_POSIX_ACL
2465d92b75cSAndreas Gruenbacher 		if (namelen == SGI_ACL_FILE_SIZE &&
2475d92b75cSAndreas Gruenbacher 		    strncmp(name, SGI_ACL_FILE,
2485d92b75cSAndreas Gruenbacher 			    SGI_ACL_FILE_SIZE) == 0) {
249f7a136aeSEric Sandeen 			__xfs_xattr_put_listent(
2505d92b75cSAndreas Gruenbacher 					context, XATTR_SYSTEM_PREFIX,
2515d92b75cSAndreas Gruenbacher 					XATTR_SYSTEM_PREFIX_LEN,
2525d92b75cSAndreas Gruenbacher 					XATTR_POSIX_ACL_ACCESS,
2535d92b75cSAndreas Gruenbacher 					strlen(XATTR_POSIX_ACL_ACCESS));
2545d92b75cSAndreas Gruenbacher 		} else if (namelen == SGI_ACL_DEFAULT_SIZE &&
2555d92b75cSAndreas Gruenbacher 			 strncmp(name, SGI_ACL_DEFAULT,
2565d92b75cSAndreas Gruenbacher 				 SGI_ACL_DEFAULT_SIZE) == 0) {
257f7a136aeSEric Sandeen 			__xfs_xattr_put_listent(
2585d92b75cSAndreas Gruenbacher 					context, XATTR_SYSTEM_PREFIX,
2595d92b75cSAndreas Gruenbacher 					XATTR_SYSTEM_PREFIX_LEN,
2605d92b75cSAndreas Gruenbacher 					XATTR_POSIX_ACL_DEFAULT,
2615d92b75cSAndreas Gruenbacher 					strlen(XATTR_POSIX_ACL_DEFAULT));
2625d92b75cSAndreas Gruenbacher 		}
2635d92b75cSAndreas Gruenbacher #endif
2645d92b75cSAndreas Gruenbacher 
265c59d87c4SChristoph Hellwig 		/*
266c59d87c4SChristoph Hellwig 		 * Only show root namespace entries if we are actually allowed to
267c59d87c4SChristoph Hellwig 		 * see them.
268c59d87c4SChristoph Hellwig 		 */
2695d92b75cSAndreas Gruenbacher 		if (!capable(CAP_SYS_ADMIN))
270f7a136aeSEric Sandeen 			return;
271c59d87c4SChristoph Hellwig 
2725d92b75cSAndreas Gruenbacher 		prefix = XATTR_TRUSTED_PREFIX;
2735d92b75cSAndreas Gruenbacher 		prefix_len = XATTR_TRUSTED_PREFIX_LEN;
2745d92b75cSAndreas Gruenbacher 	} else if (flags & XFS_ATTR_SECURE) {
2755d92b75cSAndreas Gruenbacher 		prefix = XATTR_SECURITY_PREFIX;
2765d92b75cSAndreas Gruenbacher 		prefix_len = XATTR_SECURITY_PREFIX_LEN;
2775d92b75cSAndreas Gruenbacher 	} else {
2785d92b75cSAndreas Gruenbacher 		prefix = XATTR_USER_PREFIX;
2795d92b75cSAndreas Gruenbacher 		prefix_len = XATTR_USER_PREFIX_LEN;
280c59d87c4SChristoph Hellwig 	}
281c59d87c4SChristoph Hellwig 
282f7a136aeSEric Sandeen 	__xfs_xattr_put_listent(context, prefix, prefix_len, name,
2835d92b75cSAndreas Gruenbacher 				namelen);
284f7a136aeSEric Sandeen 	return;
285c59d87c4SChristoph Hellwig }
286c59d87c4SChristoph Hellwig 
287c59d87c4SChristoph Hellwig ssize_t
xfs_vn_listxattr(struct dentry * dentry,char * data,size_t size)2882a6fba6dSEric Sandeen xfs_vn_listxattr(
2892a6fba6dSEric Sandeen 	struct dentry	*dentry,
2902a6fba6dSEric Sandeen 	char		*data,
2912a6fba6dSEric Sandeen 	size_t		size)
292c59d87c4SChristoph Hellwig {
293c59d87c4SChristoph Hellwig 	struct xfs_attr_list_context context;
2942b0143b5SDavid Howells 	struct inode	*inode = d_inode(dentry);
2952a6fba6dSEric Sandeen 	int		error;
296c59d87c4SChristoph Hellwig 
297c59d87c4SChristoph Hellwig 	/*
298c59d87c4SChristoph Hellwig 	 * First read the regular on-disk attributes.
299c59d87c4SChristoph Hellwig 	 */
300c59d87c4SChristoph Hellwig 	memset(&context, 0, sizeof(context));
301c59d87c4SChristoph Hellwig 	context.dp = XFS_I(inode);
302c59d87c4SChristoph Hellwig 	context.resynch = 1;
303a9c8c69bSChristoph Hellwig 	context.buffer = size ? data : NULL;
304c59d87c4SChristoph Hellwig 	context.bufsize = size;
305c59d87c4SChristoph Hellwig 	context.firstu = context.bufsize;
306c59d87c4SChristoph Hellwig 	context.put_listent = xfs_xattr_put_listent;
307c59d87c4SChristoph Hellwig 
30817e1dd83SChristoph Hellwig 	error = xfs_attr_list(&context);
3092a6fba6dSEric Sandeen 	if (error)
3102a6fba6dSEric Sandeen 		return error;
311c59d87c4SChristoph Hellwig 	if (context.count < 0)
312c59d87c4SChristoph Hellwig 		return -ERANGE;
313c59d87c4SChristoph Hellwig 
314c59d87c4SChristoph Hellwig 	return context.count;
315c59d87c4SChristoph Hellwig }
316