xref: /linux/fs/xfs/xfs_xattr.c (revision 5d2eb548b309be34ecf3b91f0b7300a2b9d09b8c)
1f9e09f09SLachlan McIlroy /*
2f9e09f09SLachlan McIlroy  * Copyright (C) 2008 Christoph Hellwig.
3f9e09f09SLachlan McIlroy  * Portions Copyright (C) 2000-2008 Silicon Graphics, Inc.
4f9e09f09SLachlan McIlroy  *
5f9e09f09SLachlan McIlroy  * This program is free software; you can redistribute it and/or
6f9e09f09SLachlan McIlroy  * modify it under the terms of the GNU General Public License as
7f9e09f09SLachlan McIlroy  * published by the Free Software Foundation.
8f9e09f09SLachlan McIlroy  *
9f9e09f09SLachlan McIlroy  * This program is distributed in the hope that it would be useful,
10f9e09f09SLachlan McIlroy  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11f9e09f09SLachlan McIlroy  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12f9e09f09SLachlan McIlroy  * GNU General Public License for more details.
13f9e09f09SLachlan McIlroy  *
14f9e09f09SLachlan McIlroy  * You should have received a copy of the GNU General Public License
15f9e09f09SLachlan McIlroy  * along with this program; if not, write the Free Software Foundation,
16f9e09f09SLachlan McIlroy  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17f9e09f09SLachlan McIlroy  */
18f9e09f09SLachlan McIlroy 
19f9e09f09SLachlan McIlroy #include "xfs.h"
20a4fbe6abSDave Chinner #include "xfs_format.h"
2169432832SDave Chinner #include "xfs_log_format.h"
22239880efSDave Chinner #include "xfs_trans_resv.h"
2357062787SDave Chinner #include "xfs_mount.h"
2457062787SDave Chinner #include "xfs_da_format.h"
25f9e09f09SLachlan McIlroy #include "xfs_inode.h"
26f9e09f09SLachlan McIlroy #include "xfs_attr.h"
27f9e09f09SLachlan McIlroy #include "xfs_attr_leaf.h"
28f9e09f09SLachlan McIlroy #include "xfs_acl.h"
29f9e09f09SLachlan McIlroy 
30f9e09f09SLachlan McIlroy #include <linux/posix_acl_xattr.h>
31f9e09f09SLachlan McIlroy #include <linux/xattr.h>
32f9e09f09SLachlan McIlroy 
33f9e09f09SLachlan McIlroy 
34f9e09f09SLachlan McIlroy static int
35d9a82a04SAndreas Gruenbacher xfs_xattr_get(const struct xattr_handler *handler, struct dentry *dentry,
36d9a82a04SAndreas Gruenbacher 		const char *name, void *value, size_t size)
37f9e09f09SLachlan McIlroy {
38d9a82a04SAndreas Gruenbacher 	int xflags = handler->flags;
392b0143b5SDavid Howells 	struct xfs_inode *ip = XFS_I(d_inode(dentry));
40f9e09f09SLachlan McIlroy 	int error, asize = size;
41f9e09f09SLachlan McIlroy 
42f9e09f09SLachlan McIlroy 	if (strcmp(name, "") == 0)
43f9e09f09SLachlan McIlroy 		return -EINVAL;
44f9e09f09SLachlan McIlroy 
45f9e09f09SLachlan McIlroy 	/* Convert Linux syscall to XFS internal ATTR flags */
46f9e09f09SLachlan McIlroy 	if (!size) {
47f9e09f09SLachlan McIlroy 		xflags |= ATTR_KERNOVAL;
48f9e09f09SLachlan McIlroy 		value = NULL;
49f9e09f09SLachlan McIlroy 	}
50f9e09f09SLachlan McIlroy 
512451337dSDave Chinner 	error = xfs_attr_get(ip, (unsigned char *)name, value, &asize, xflags);
52f9e09f09SLachlan McIlroy 	if (error)
53f9e09f09SLachlan McIlroy 		return error;
54f9e09f09SLachlan McIlroy 	return asize;
55f9e09f09SLachlan McIlroy }
56f9e09f09SLachlan McIlroy 
5747e1bf64SAndreas Gruenbacher void
5847e1bf64SAndreas Gruenbacher xfs_forget_acl(
5947e1bf64SAndreas Gruenbacher 	struct inode		*inode,
6047e1bf64SAndreas Gruenbacher 	const char		*name,
6147e1bf64SAndreas Gruenbacher 	int			xflags)
6247e1bf64SAndreas Gruenbacher {
6347e1bf64SAndreas Gruenbacher 	/*
6447e1bf64SAndreas Gruenbacher 	 * Invalidate any cached ACLs if the user has bypassed the ACL
6547e1bf64SAndreas Gruenbacher 	 * interface. We don't validate the content whatsoever so it is caller
6647e1bf64SAndreas Gruenbacher 	 * responsibility to provide data in valid format and ensure i_mode is
6747e1bf64SAndreas Gruenbacher 	 * consistent.
6847e1bf64SAndreas Gruenbacher 	 */
6947e1bf64SAndreas Gruenbacher 	if (xflags & ATTR_ROOT) {
7047e1bf64SAndreas Gruenbacher #ifdef CONFIG_XFS_POSIX_ACL
7147e1bf64SAndreas Gruenbacher 		if (!strcmp(name, SGI_ACL_FILE))
7247e1bf64SAndreas Gruenbacher 			forget_cached_acl(inode, ACL_TYPE_ACCESS);
7347e1bf64SAndreas Gruenbacher 		else if (!strcmp(name, SGI_ACL_DEFAULT))
7447e1bf64SAndreas Gruenbacher 			forget_cached_acl(inode, ACL_TYPE_DEFAULT);
7547e1bf64SAndreas Gruenbacher #endif
7647e1bf64SAndreas Gruenbacher 	}
7747e1bf64SAndreas Gruenbacher }
7847e1bf64SAndreas Gruenbacher 
79f9e09f09SLachlan McIlroy static int
80d9a82a04SAndreas Gruenbacher xfs_xattr_set(const struct xattr_handler *handler, struct dentry *dentry,
81d9a82a04SAndreas Gruenbacher 		const char *name, const void *value, size_t size, int flags)
82f9e09f09SLachlan McIlroy {
83d9a82a04SAndreas Gruenbacher 	int			xflags = handler->flags;
842b0143b5SDavid Howells 	struct xfs_inode	*ip = XFS_I(d_inode(dentry));
8567d8e04eSBrian Foster 	int			error;
86f9e09f09SLachlan McIlroy 
87f9e09f09SLachlan McIlroy 	if (strcmp(name, "") == 0)
88f9e09f09SLachlan McIlroy 		return -EINVAL;
89f9e09f09SLachlan McIlroy 
90f9e09f09SLachlan McIlroy 	/* Convert Linux syscall to XFS internal ATTR flags */
91f9e09f09SLachlan McIlroy 	if (flags & XATTR_CREATE)
92f9e09f09SLachlan McIlroy 		xflags |= ATTR_CREATE;
93f9e09f09SLachlan McIlroy 	if (flags & XATTR_REPLACE)
94f9e09f09SLachlan McIlroy 		xflags |= ATTR_REPLACE;
95f9e09f09SLachlan McIlroy 
96f9e09f09SLachlan McIlroy 	if (!value)
972451337dSDave Chinner 		return xfs_attr_remove(ip, (unsigned char *)name, xflags);
9867d8e04eSBrian Foster 	error = xfs_attr_set(ip, (unsigned char *)name,
99a9273ca5SDave Chinner 				(void *)value, size, xflags);
10047e1bf64SAndreas Gruenbacher 	if (!error)
10147e1bf64SAndreas Gruenbacher 		xfs_forget_acl(d_inode(dentry), name, xflags);
10267d8e04eSBrian Foster 
10367d8e04eSBrian Foster 	return error;
104f9e09f09SLachlan McIlroy }
105f9e09f09SLachlan McIlroy 
10646e58764SStephen Hemminger static const struct xattr_handler xfs_xattr_user_handler = {
107f9e09f09SLachlan McIlroy 	.prefix	= XATTR_USER_PREFIX,
108431547b3SChristoph Hellwig 	.flags	= 0, /* no flags implies user namespace */
109431547b3SChristoph Hellwig 	.get	= xfs_xattr_get,
110431547b3SChristoph Hellwig 	.set	= xfs_xattr_set,
111f9e09f09SLachlan McIlroy };
112f9e09f09SLachlan McIlroy 
11346e58764SStephen Hemminger static const struct xattr_handler xfs_xattr_trusted_handler = {
114f9e09f09SLachlan McIlroy 	.prefix	= XATTR_TRUSTED_PREFIX,
115431547b3SChristoph Hellwig 	.flags	= ATTR_ROOT,
116431547b3SChristoph Hellwig 	.get	= xfs_xattr_get,
117431547b3SChristoph Hellwig 	.set	= xfs_xattr_set,
118f9e09f09SLachlan McIlroy };
119f9e09f09SLachlan McIlroy 
12046e58764SStephen Hemminger static const struct xattr_handler xfs_xattr_security_handler = {
121f9e09f09SLachlan McIlroy 	.prefix	= XATTR_SECURITY_PREFIX,
122431547b3SChristoph Hellwig 	.flags	= ATTR_SECURE,
123431547b3SChristoph Hellwig 	.get	= xfs_xattr_get,
124431547b3SChristoph Hellwig 	.set	= xfs_xattr_set,
125f9e09f09SLachlan McIlroy };
126f9e09f09SLachlan McIlroy 
12746e58764SStephen Hemminger const struct xattr_handler *xfs_xattr_handlers[] = {
128f9e09f09SLachlan McIlroy 	&xfs_xattr_user_handler,
129f9e09f09SLachlan McIlroy 	&xfs_xattr_trusted_handler,
130f9e09f09SLachlan McIlroy 	&xfs_xattr_security_handler,
131ef14f0c1SChristoph Hellwig #ifdef CONFIG_XFS_POSIX_ACL
1322401dc29SChristoph Hellwig 	&posix_acl_access_xattr_handler,
1332401dc29SChristoph Hellwig 	&posix_acl_default_xattr_handler,
134ef14f0c1SChristoph Hellwig #endif
135f9e09f09SLachlan McIlroy 	NULL
136f9e09f09SLachlan McIlroy };
137f9e09f09SLachlan McIlroy 
138f9e09f09SLachlan McIlroy static unsigned int xfs_xattr_prefix_len(int flags)
139f9e09f09SLachlan McIlroy {
140f9e09f09SLachlan McIlroy 	if (flags & XFS_ATTR_SECURE)
141f9e09f09SLachlan McIlroy 		return sizeof("security");
142f9e09f09SLachlan McIlroy 	else if (flags & XFS_ATTR_ROOT)
143f9e09f09SLachlan McIlroy 		return sizeof("trusted");
144f9e09f09SLachlan McIlroy 	else
145f9e09f09SLachlan McIlroy 		return sizeof("user");
146f9e09f09SLachlan McIlroy }
147f9e09f09SLachlan McIlroy 
148f9e09f09SLachlan McIlroy static const char *xfs_xattr_prefix(int flags)
149f9e09f09SLachlan McIlroy {
150f9e09f09SLachlan McIlroy 	if (flags & XFS_ATTR_SECURE)
151f9e09f09SLachlan McIlroy 		return xfs_xattr_security_handler.prefix;
152f9e09f09SLachlan McIlroy 	else if (flags & XFS_ATTR_ROOT)
153f9e09f09SLachlan McIlroy 		return xfs_xattr_trusted_handler.prefix;
154f9e09f09SLachlan McIlroy 	else
155f9e09f09SLachlan McIlroy 		return xfs_xattr_user_handler.prefix;
156f9e09f09SLachlan McIlroy }
157f9e09f09SLachlan McIlroy 
158f9e09f09SLachlan McIlroy static int
159a9273ca5SDave Chinner xfs_xattr_put_listent(
160a9273ca5SDave Chinner 	struct xfs_attr_list_context *context,
161a9273ca5SDave Chinner 	int		flags,
162a9273ca5SDave Chinner 	unsigned char	*name,
163a9273ca5SDave Chinner 	int		namelen,
164a9273ca5SDave Chinner 	int		valuelen,
165a9273ca5SDave Chinner 	unsigned char	*value)
166f9e09f09SLachlan McIlroy {
167f9e09f09SLachlan McIlroy 	unsigned int prefix_len = xfs_xattr_prefix_len(flags);
168f9e09f09SLachlan McIlroy 	char *offset;
169f9e09f09SLachlan McIlroy 	int arraytop;
170f9e09f09SLachlan McIlroy 
171f9e09f09SLachlan McIlroy 	ASSERT(context->count >= 0);
172f9e09f09SLachlan McIlroy 
173f9e09f09SLachlan McIlroy 	/*
174f9e09f09SLachlan McIlroy 	 * Only show root namespace entries if we are actually allowed to
175f9e09f09SLachlan McIlroy 	 * see them.
176f9e09f09SLachlan McIlroy 	 */
177f9e09f09SLachlan McIlroy 	if ((flags & XFS_ATTR_ROOT) && !capable(CAP_SYS_ADMIN))
178f9e09f09SLachlan McIlroy 		return 0;
179f9e09f09SLachlan McIlroy 
180f9e09f09SLachlan McIlroy 	arraytop = context->count + prefix_len + namelen + 1;
181f9e09f09SLachlan McIlroy 	if (arraytop > context->firstu) {
182f9e09f09SLachlan McIlroy 		context->count = -1;	/* insufficient space */
183f9e09f09SLachlan McIlroy 		return 1;
184f9e09f09SLachlan McIlroy 	}
185f9e09f09SLachlan McIlroy 	offset = (char *)context->alist + context->count;
186f9e09f09SLachlan McIlroy 	strncpy(offset, xfs_xattr_prefix(flags), prefix_len);
187f9e09f09SLachlan McIlroy 	offset += prefix_len;
188a9273ca5SDave Chinner 	strncpy(offset, (char *)name, namelen);			/* real name */
189f9e09f09SLachlan McIlroy 	offset += namelen;
190f9e09f09SLachlan McIlroy 	*offset = '\0';
191f9e09f09SLachlan McIlroy 	context->count += prefix_len + namelen + 1;
192f9e09f09SLachlan McIlroy 	return 0;
193f9e09f09SLachlan McIlroy }
194f9e09f09SLachlan McIlroy 
195f9e09f09SLachlan McIlroy static int
196a9273ca5SDave Chinner xfs_xattr_put_listent_sizes(
197a9273ca5SDave Chinner 	struct xfs_attr_list_context *context,
198a9273ca5SDave Chinner 	int		flags,
199a9273ca5SDave Chinner 	unsigned char	*name,
200a9273ca5SDave Chinner 	int		namelen,
201a9273ca5SDave Chinner 	int		valuelen,
202a9273ca5SDave Chinner 	unsigned char	*value)
203f9e09f09SLachlan McIlroy {
204f9e09f09SLachlan McIlroy 	context->count += xfs_xattr_prefix_len(flags) + namelen + 1;
205f9e09f09SLachlan McIlroy 	return 0;
206f9e09f09SLachlan McIlroy }
207f9e09f09SLachlan McIlroy 
208f9e09f09SLachlan McIlroy static int
209f9e09f09SLachlan McIlroy list_one_attr(const char *name, const size_t len, void *data,
210f9e09f09SLachlan McIlroy 		size_t size, ssize_t *result)
211f9e09f09SLachlan McIlroy {
212f9e09f09SLachlan McIlroy 	char *p = data + *result;
213f9e09f09SLachlan McIlroy 
214f9e09f09SLachlan McIlroy 	*result += len;
215f9e09f09SLachlan McIlroy 	if (!size)
216f9e09f09SLachlan McIlroy 		return 0;
217f9e09f09SLachlan McIlroy 	if (*result > size)
218f9e09f09SLachlan McIlroy 		return -ERANGE;
219f9e09f09SLachlan McIlroy 
220f9e09f09SLachlan McIlroy 	strcpy(p, name);
221f9e09f09SLachlan McIlroy 	return 0;
222f9e09f09SLachlan McIlroy }
223f9e09f09SLachlan McIlroy 
224f9e09f09SLachlan McIlroy ssize_t
225f9e09f09SLachlan McIlroy xfs_vn_listxattr(struct dentry *dentry, char *data, size_t size)
226f9e09f09SLachlan McIlroy {
227f9e09f09SLachlan McIlroy 	struct xfs_attr_list_context context;
228f9e09f09SLachlan McIlroy 	struct attrlist_cursor_kern cursor = { 0 };
2292b0143b5SDavid Howells 	struct inode		*inode = d_inode(dentry);
230f9e09f09SLachlan McIlroy 	int			error;
231f9e09f09SLachlan McIlroy 
232f9e09f09SLachlan McIlroy 	/*
233f9e09f09SLachlan McIlroy 	 * First read the regular on-disk attributes.
234f9e09f09SLachlan McIlroy 	 */
235f9e09f09SLachlan McIlroy 	memset(&context, 0, sizeof(context));
236f9e09f09SLachlan McIlroy 	context.dp = XFS_I(inode);
237f9e09f09SLachlan McIlroy 	context.cursor = &cursor;
238f9e09f09SLachlan McIlroy 	context.resynch = 1;
239f9e09f09SLachlan McIlroy 	context.alist = data;
240f9e09f09SLachlan McIlroy 	context.bufsize = size;
241f9e09f09SLachlan McIlroy 	context.firstu = context.bufsize;
242f9e09f09SLachlan McIlroy 
243f9e09f09SLachlan McIlroy 	if (size)
244f9e09f09SLachlan McIlroy 		context.put_listent = xfs_xattr_put_listent;
245f9e09f09SLachlan McIlroy 	else
246f9e09f09SLachlan McIlroy 		context.put_listent = xfs_xattr_put_listent_sizes;
247f9e09f09SLachlan McIlroy 
248f9e09f09SLachlan McIlroy 	xfs_attr_list_int(&context);
249f9e09f09SLachlan McIlroy 	if (context.count < 0)
250f9e09f09SLachlan McIlroy 		return -ERANGE;
251f9e09f09SLachlan McIlroy 
252f9e09f09SLachlan McIlroy 	/*
253f9e09f09SLachlan McIlroy 	 * Then add the two synthetic ACL attributes.
254f9e09f09SLachlan McIlroy 	 */
255ef14f0c1SChristoph Hellwig 	if (posix_acl_access_exists(inode)) {
256f9e09f09SLachlan McIlroy 		error = list_one_attr(POSIX_ACL_XATTR_ACCESS,
257f9e09f09SLachlan McIlroy 				strlen(POSIX_ACL_XATTR_ACCESS) + 1,
258f9e09f09SLachlan McIlroy 				data, size, &context.count);
259f9e09f09SLachlan McIlroy 		if (error)
260f9e09f09SLachlan McIlroy 			return error;
261f9e09f09SLachlan McIlroy 	}
262f9e09f09SLachlan McIlroy 
263ef14f0c1SChristoph Hellwig 	if (posix_acl_default_exists(inode)) {
264f9e09f09SLachlan McIlroy 		error = list_one_attr(POSIX_ACL_XATTR_DEFAULT,
265f9e09f09SLachlan McIlroy 				strlen(POSIX_ACL_XATTR_DEFAULT) + 1,
266f9e09f09SLachlan McIlroy 				data, size, &context.count);
267f9e09f09SLachlan McIlroy 		if (error)
268f9e09f09SLachlan McIlroy 			return error;
269f9e09f09SLachlan McIlroy 	}
270f9e09f09SLachlan McIlroy 
271f9e09f09SLachlan McIlroy 	return context.count;
272f9e09f09SLachlan McIlroy }
273