xref: /openbmc/linux/fs/xfs/xfs_xattr.c (revision c494a447)
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 
21 #include <linux/posix_acl_xattr.h>
22 
23 /*
24  * Get permission to use log-assisted atomic exchange of file extents.
25  *
26  * Callers must not be running any transactions or hold any inode locks, and
27  * they must release the permission by calling xlog_drop_incompat_feat
28  * when they're done.
29  */
30 static inline int
31 xfs_attr_grab_log_assist(
32 	struct xfs_mount	*mp)
33 {
34 	int			error = 0;
35 
36 	/*
37 	 * Protect ourselves from an idle log clearing the logged xattrs log
38 	 * incompat feature bit.
39 	 */
40 	xlog_use_incompat_feat(mp->m_log);
41 
42 	/*
43 	 * If log-assisted xattrs are already enabled, the caller can use the
44 	 * log assisted swap functions with the log-incompat reference we got.
45 	 */
46 	if (xfs_sb_version_haslogxattrs(&mp->m_sb))
47 		return 0;
48 
49 	/* Enable log-assisted xattrs. */
50 	error = xfs_add_incompat_log_feature(mp,
51 			XFS_SB_FEAT_INCOMPAT_LOG_XATTRS);
52 	if (error)
53 		goto drop_incompat;
54 
55 	xfs_warn_mount(mp, XFS_OPSTATE_WARNED_LARP,
56  "EXPERIMENTAL logged extended attributes feature in use. Use at your own risk!");
57 
58 	return 0;
59 drop_incompat:
60 	xlog_drop_incompat_feat(mp->m_log);
61 	return error;
62 }
63 
64 static inline void
65 xfs_attr_rele_log_assist(
66 	struct xfs_mount	*mp)
67 {
68 	xlog_drop_incompat_feat(mp->m_log);
69 }
70 
71 /*
72  * Set or remove an xattr, having grabbed the appropriate logging resources
73  * prior to calling libxfs.
74  */
75 int
76 xfs_attr_change(
77 	struct xfs_da_args	*args)
78 {
79 	struct xfs_mount	*mp = args->dp->i_mount;
80 	bool			use_logging = false;
81 	int			error;
82 
83 	if (xfs_has_larp(mp)) {
84 		error = xfs_attr_grab_log_assist(mp);
85 		if (error)
86 			return error;
87 
88 		use_logging = true;
89 	}
90 
91 	error = xfs_attr_set(args);
92 
93 	if (use_logging)
94 		xfs_attr_rele_log_assist(mp);
95 	return error;
96 }
97 
98 
99 static int
100 xfs_xattr_get(const struct xattr_handler *handler, struct dentry *unused,
101 		struct inode *inode, const char *name, void *value, size_t size)
102 {
103 	struct xfs_da_args	args = {
104 		.dp		= XFS_I(inode),
105 		.attr_filter	= handler->flags,
106 		.name		= name,
107 		.namelen	= strlen(name),
108 		.value		= value,
109 		.valuelen	= size,
110 	};
111 	int			error;
112 
113 	error = xfs_attr_get(&args);
114 	if (error)
115 		return error;
116 	return args.valuelen;
117 }
118 
119 static int
120 xfs_xattr_set(const struct xattr_handler *handler,
121 	      struct user_namespace *mnt_userns, struct dentry *unused,
122 	      struct inode *inode, const char *name, const void *value,
123 	      size_t size, int flags)
124 {
125 	struct xfs_da_args	args = {
126 		.dp		= XFS_I(inode),
127 		.attr_filter	= handler->flags,
128 		.attr_flags	= flags,
129 		.name		= name,
130 		.namelen	= strlen(name),
131 		.value		= (void *)value,
132 		.valuelen	= size,
133 	};
134 	int			error;
135 
136 	error = xfs_attr_change(&args);
137 	if (!error && (handler->flags & XFS_ATTR_ROOT))
138 		xfs_forget_acl(inode, name);
139 	return error;
140 }
141 
142 static const struct xattr_handler xfs_xattr_user_handler = {
143 	.prefix	= XATTR_USER_PREFIX,
144 	.flags	= 0, /* no flags implies user namespace */
145 	.get	= xfs_xattr_get,
146 	.set	= xfs_xattr_set,
147 };
148 
149 static const struct xattr_handler xfs_xattr_trusted_handler = {
150 	.prefix	= XATTR_TRUSTED_PREFIX,
151 	.flags	= XFS_ATTR_ROOT,
152 	.get	= xfs_xattr_get,
153 	.set	= xfs_xattr_set,
154 };
155 
156 static const struct xattr_handler xfs_xattr_security_handler = {
157 	.prefix	= XATTR_SECURITY_PREFIX,
158 	.flags	= XFS_ATTR_SECURE,
159 	.get	= xfs_xattr_get,
160 	.set	= xfs_xattr_set,
161 };
162 
163 const struct xattr_handler *xfs_xattr_handlers[] = {
164 	&xfs_xattr_user_handler,
165 	&xfs_xattr_trusted_handler,
166 	&xfs_xattr_security_handler,
167 #ifdef CONFIG_XFS_POSIX_ACL
168 	&posix_acl_access_xattr_handler,
169 	&posix_acl_default_xattr_handler,
170 #endif
171 	NULL
172 };
173 
174 static void
175 __xfs_xattr_put_listent(
176 	struct xfs_attr_list_context *context,
177 	char *prefix,
178 	int prefix_len,
179 	unsigned char *name,
180 	int namelen)
181 {
182 	char *offset;
183 	int arraytop;
184 
185 	if (context->count < 0 || context->seen_enough)
186 		return;
187 
188 	if (!context->buffer)
189 		goto compute_size;
190 
191 	arraytop = context->count + prefix_len + namelen + 1;
192 	if (arraytop > context->firstu) {
193 		context->count = -1;	/* insufficient space */
194 		context->seen_enough = 1;
195 		return;
196 	}
197 	offset = context->buffer + context->count;
198 	strncpy(offset, prefix, prefix_len);
199 	offset += prefix_len;
200 	strncpy(offset, (char *)name, namelen);			/* real name */
201 	offset += namelen;
202 	*offset = '\0';
203 
204 compute_size:
205 	context->count += prefix_len + namelen + 1;
206 	return;
207 }
208 
209 static void
210 xfs_xattr_put_listent(
211 	struct xfs_attr_list_context *context,
212 	int		flags,
213 	unsigned char	*name,
214 	int		namelen,
215 	int		valuelen)
216 {
217 	char *prefix;
218 	int prefix_len;
219 
220 	ASSERT(context->count >= 0);
221 
222 	if (flags & XFS_ATTR_ROOT) {
223 #ifdef CONFIG_XFS_POSIX_ACL
224 		if (namelen == SGI_ACL_FILE_SIZE &&
225 		    strncmp(name, SGI_ACL_FILE,
226 			    SGI_ACL_FILE_SIZE) == 0) {
227 			__xfs_xattr_put_listent(
228 					context, XATTR_SYSTEM_PREFIX,
229 					XATTR_SYSTEM_PREFIX_LEN,
230 					XATTR_POSIX_ACL_ACCESS,
231 					strlen(XATTR_POSIX_ACL_ACCESS));
232 		} else if (namelen == SGI_ACL_DEFAULT_SIZE &&
233 			 strncmp(name, SGI_ACL_DEFAULT,
234 				 SGI_ACL_DEFAULT_SIZE) == 0) {
235 			__xfs_xattr_put_listent(
236 					context, XATTR_SYSTEM_PREFIX,
237 					XATTR_SYSTEM_PREFIX_LEN,
238 					XATTR_POSIX_ACL_DEFAULT,
239 					strlen(XATTR_POSIX_ACL_DEFAULT));
240 		}
241 #endif
242 
243 		/*
244 		 * Only show root namespace entries if we are actually allowed to
245 		 * see them.
246 		 */
247 		if (!capable(CAP_SYS_ADMIN))
248 			return;
249 
250 		prefix = XATTR_TRUSTED_PREFIX;
251 		prefix_len = XATTR_TRUSTED_PREFIX_LEN;
252 	} else if (flags & XFS_ATTR_SECURE) {
253 		prefix = XATTR_SECURITY_PREFIX;
254 		prefix_len = XATTR_SECURITY_PREFIX_LEN;
255 	} else {
256 		prefix = XATTR_USER_PREFIX;
257 		prefix_len = XATTR_USER_PREFIX_LEN;
258 	}
259 
260 	__xfs_xattr_put_listent(context, prefix, prefix_len, name,
261 				namelen);
262 	return;
263 }
264 
265 ssize_t
266 xfs_vn_listxattr(
267 	struct dentry	*dentry,
268 	char		*data,
269 	size_t		size)
270 {
271 	struct xfs_attr_list_context context;
272 	struct inode	*inode = d_inode(dentry);
273 	int		error;
274 
275 	/*
276 	 * First read the regular on-disk attributes.
277 	 */
278 	memset(&context, 0, sizeof(context));
279 	context.dp = XFS_I(inode);
280 	context.resynch = 1;
281 	context.buffer = size ? data : NULL;
282 	context.bufsize = size;
283 	context.firstu = context.bufsize;
284 	context.put_listent = xfs_xattr_put_listent;
285 
286 	error = xfs_attr_list(&context);
287 	if (error)
288 		return error;
289 	if (context.count < 0)
290 		return -ERANGE;
291 
292 	return context.count;
293 }
294