1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2008 Christoph Hellwig.
4 * Portions Copyright (C) 2000-2008 Silicon Graphics, Inc.
5 */
6
7#include "xfs.h"
8#include "xfs_shared.h"
9#include "xfs_format.h"
10#include "xfs_log_format.h"
11#include "xfs_da_format.h"
12#include "xfs_trans_resv.h"
13#include "xfs_mount.h"
14#include "xfs_inode.h"
15#include "xfs_da_btree.h"
16#include "xfs_attr.h"
17#include "xfs_acl.h"
18#include "xfs_log.h"
19#include "xfs_xattr.h"
20#include "xfs_quota.h"
21
22#include <linux/posix_acl_xattr.h>
23
24/*
25 * Get permission to use log-assisted atomic exchange of file extents.
26 * Callers must not be running any transactions or hold any ILOCKs.
27 */
28static inline int
29xfs_attr_grab_log_assist(
30	struct xfs_mount	*mp)
31{
32	int			error = 0;
33
34	/* xattr update log intent items are already enabled */
35	if (xfs_is_using_logged_xattrs(mp))
36		return 0;
37
38	/*
39	 * Check if the filesystem featureset is new enough to set this log
40	 * incompat feature bit.  Strictly speaking, the minimum requirement is
41	 * a V5 filesystem for the superblock field, but we'll require rmap
42	 * or reflink to avoid having to deal with really old kernels.
43	 */
44	if (!xfs_has_reflink(mp) && !xfs_has_rmapbt(mp))
45		return -EOPNOTSUPP;
46
47	/* Enable log-assisted xattrs. */
48	error = xfs_add_incompat_log_feature(mp,
49			XFS_SB_FEAT_INCOMPAT_LOG_XATTRS);
50	if (error)
51		return error;
52	xfs_set_using_logged_xattrs(mp);
53
54	xfs_warn_mount(mp, XFS_OPSTATE_WARNED_LARP,
55 "EXPERIMENTAL logged extended attributes feature in use. Use at your own risk!");
56
57	return 0;
58}
59
60static inline bool
61xfs_attr_want_log_assist(
62	struct xfs_mount	*mp)
63{
64#ifdef DEBUG
65	/* Logged xattrs require a V5 super for log_incompat */
66	return xfs_has_crc(mp) && xfs_globals.larp;
67#else
68	return false;
69#endif
70}
71
72/*
73 * Set or remove an xattr, having grabbed the appropriate logging resources
74 * prior to calling libxfs.  Callers of this function are only required to
75 * initialize the inode, attr_filter, name, namelen, value, and valuelen fields
76 * of @args.
77 */
78int
79xfs_attr_change(
80	struct xfs_da_args	*args,
81	enum xfs_attr_update	op)
82{
83	struct xfs_mount	*mp = args->dp->i_mount;
84	int			error;
85
86	if (xfs_is_shutdown(mp))
87		return -EIO;
88
89	error = xfs_qm_dqattach(args->dp);
90	if (error)
91		return error;
92
93	/*
94	 * We have no control over the attribute names that userspace passes us
95	 * to remove, so we have to allow the name lookup prior to attribute
96	 * removal to fail as well.
97	 */
98	args->op_flags = XFS_DA_OP_OKNOENT;
99
100	if (xfs_attr_want_log_assist(mp)) {
101		error = xfs_attr_grab_log_assist(mp);
102		if (error)
103			return error;
104
105		args->op_flags |= XFS_DA_OP_LOGGED;
106	}
107
108	args->owner = args->dp->i_ino;
109	args->geo = mp->m_attr_geo;
110	args->whichfork = XFS_ATTR_FORK;
111	xfs_attr_sethash(args);
112
113	return xfs_attr_set(args, op, args->attr_filter & XFS_ATTR_ROOT);
114}
115
116
117static int
118xfs_xattr_get(const struct xattr_handler *handler, struct dentry *unused,
119		struct inode *inode, const char *name, void *value, size_t size)
120{
121	struct xfs_da_args	args = {
122		.dp		= XFS_I(inode),
123		.attr_filter	= handler->flags,
124		.name		= name,
125		.namelen	= strlen(name),
126		.value		= value,
127		.valuelen	= size,
128	};
129	int			error;
130
131	if (xfs_ifork_zapped(XFS_I(inode), XFS_ATTR_FORK))
132		return -EIO;
133
134	error = xfs_attr_get(&args);
135	if (error)
136		return error;
137	return args.valuelen;
138}
139
140static inline enum xfs_attr_update
141xfs_xattr_flags_to_op(
142	int		flags,
143	const void	*value)
144{
145	if (!value)
146		return XFS_ATTRUPDATE_REMOVE;
147	if (flags & XATTR_CREATE)
148		return XFS_ATTRUPDATE_CREATE;
149	if (flags & XATTR_REPLACE)
150		return XFS_ATTRUPDATE_REPLACE;
151	return XFS_ATTRUPDATE_UPSERT;
152}
153
154static int
155xfs_xattr_set(const struct xattr_handler *handler,
156	      struct mnt_idmap *idmap, struct dentry *unused,
157	      struct inode *inode, const char *name, const void *value,
158	      size_t size, int flags)
159{
160	struct xfs_da_args	args = {
161		.dp		= XFS_I(inode),
162		.attr_filter	= handler->flags,
163		.name		= name,
164		.namelen	= strlen(name),
165		.value		= (void *)value,
166		.valuelen	= size,
167	};
168	int			error;
169
170	error = xfs_attr_change(&args, xfs_xattr_flags_to_op(flags, value));
171	if (!error && (handler->flags & XFS_ATTR_ROOT))
172		xfs_forget_acl(inode, name);
173	return error;
174}
175
176static const struct xattr_handler xfs_xattr_user_handler = {
177	.prefix	= XATTR_USER_PREFIX,
178	.flags	= 0, /* no flags implies user namespace */
179	.get	= xfs_xattr_get,
180	.set	= xfs_xattr_set,
181};
182
183static const struct xattr_handler xfs_xattr_trusted_handler = {
184	.prefix	= XATTR_TRUSTED_PREFIX,
185	.flags	= XFS_ATTR_ROOT,
186	.get	= xfs_xattr_get,
187	.set	= xfs_xattr_set,
188};
189
190static const struct xattr_handler xfs_xattr_security_handler = {
191	.prefix	= XATTR_SECURITY_PREFIX,
192	.flags	= XFS_ATTR_SECURE,
193	.get	= xfs_xattr_get,
194	.set	= xfs_xattr_set,
195};
196
197const struct xattr_handler * const xfs_xattr_handlers[] = {
198	&xfs_xattr_user_handler,
199	&xfs_xattr_trusted_handler,
200	&xfs_xattr_security_handler,
201	NULL
202};
203
204static void
205__xfs_xattr_put_listent(
206	struct xfs_attr_list_context *context,
207	char *prefix,
208	int prefix_len,
209	unsigned char *name,
210	int namelen)
211{
212	char *offset;
213	int arraytop;
214
215	if (context->count < 0 || context->seen_enough)
216		return;
217
218	if (!context->buffer)
219		goto compute_size;
220
221	arraytop = context->count + prefix_len + namelen + 1;
222	if (arraytop > context->firstu) {
223		context->count = -1;	/* insufficient space */
224		context->seen_enough = 1;
225		return;
226	}
227	offset = context->buffer + context->count;
228	memcpy(offset, prefix, prefix_len);
229	offset += prefix_len;
230	strncpy(offset, (char *)name, namelen);			/* real name */
231	offset += namelen;
232	*offset = '\0';
233
234compute_size:
235	context->count += prefix_len + namelen + 1;
236	return;
237}
238
239static void
240xfs_xattr_put_listent(
241	struct xfs_attr_list_context *context,
242	int		flags,
243	unsigned char	*name,
244	int		namelen,
245	void		*value,
246	int		valuelen)
247{
248	char *prefix;
249	int prefix_len;
250
251	ASSERT(context->count >= 0);
252
253	/* Don't expose private xattr namespaces. */
254	if (flags & XFS_ATTR_PRIVATE_NSP_MASK)
255		return;
256
257	if (flags & XFS_ATTR_ROOT) {
258#ifdef CONFIG_XFS_POSIX_ACL
259		if (namelen == SGI_ACL_FILE_SIZE &&
260		    strncmp(name, SGI_ACL_FILE,
261			    SGI_ACL_FILE_SIZE) == 0) {
262			__xfs_xattr_put_listent(
263					context, XATTR_SYSTEM_PREFIX,
264					XATTR_SYSTEM_PREFIX_LEN,
265					XATTR_POSIX_ACL_ACCESS,
266					strlen(XATTR_POSIX_ACL_ACCESS));
267		} else if (namelen == SGI_ACL_DEFAULT_SIZE &&
268			 strncmp(name, SGI_ACL_DEFAULT,
269				 SGI_ACL_DEFAULT_SIZE) == 0) {
270			__xfs_xattr_put_listent(
271					context, XATTR_SYSTEM_PREFIX,
272					XATTR_SYSTEM_PREFIX_LEN,
273					XATTR_POSIX_ACL_DEFAULT,
274					strlen(XATTR_POSIX_ACL_DEFAULT));
275		}
276#endif
277
278		/*
279		 * Only show root namespace entries if we are actually allowed to
280		 * see them.
281		 */
282		if (!capable(CAP_SYS_ADMIN))
283			return;
284
285		prefix = XATTR_TRUSTED_PREFIX;
286		prefix_len = XATTR_TRUSTED_PREFIX_LEN;
287	} else if (flags & XFS_ATTR_SECURE) {
288		prefix = XATTR_SECURITY_PREFIX;
289		prefix_len = XATTR_SECURITY_PREFIX_LEN;
290	} else {
291		prefix = XATTR_USER_PREFIX;
292		prefix_len = XATTR_USER_PREFIX_LEN;
293	}
294
295	__xfs_xattr_put_listent(context, prefix, prefix_len, name,
296				namelen);
297	return;
298}
299
300ssize_t
301xfs_vn_listxattr(
302	struct dentry	*dentry,
303	char		*data,
304	size_t		size)
305{
306	struct xfs_attr_list_context context;
307	struct inode	*inode = d_inode(dentry);
308	int		error;
309
310	if (xfs_ifork_zapped(XFS_I(inode), XFS_ATTR_FORK))
311		return -EIO;
312
313	/*
314	 * First read the regular on-disk attributes.
315	 */
316	memset(&context, 0, sizeof(context));
317	context.dp = XFS_I(inode);
318	context.resynch = 1;
319	context.buffer = size ? data : NULL;
320	context.bufsize = size;
321	context.firstu = context.bufsize;
322	context.put_listent = xfs_xattr_put_listent;
323
324	error = xfs_attr_list(&context);
325	if (error)
326		return error;
327	if (context.count < 0)
328		return -ERANGE;
329
330	return context.count;
331}
332