1/*
2 * Copyright (C) 2008 Christoph Hellwig.
3 * Portions Copyright (C) 2000-2008 Silicon Graphics, Inc.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19#include "xfs.h"
20#include "xfs_format.h"
21#include "xfs_log_format.h"
22#include "xfs_trans_resv.h"
23#include "xfs_mount.h"
24#include "xfs_da_format.h"
25#include "xfs_inode.h"
26#include "xfs_attr.h"
27#include "xfs_attr_leaf.h"
28#include "xfs_acl.h"
29
30#include <linux/posix_acl_xattr.h>
31#include <linux/xattr.h>
32
33
34static int
35xfs_xattr_get(struct dentry *dentry, const char *name,
36		void *value, size_t size, int xflags)
37{
38	struct xfs_inode *ip = XFS_I(d_inode(dentry));
39	int error, asize = size;
40
41	if (strcmp(name, "") == 0)
42		return -EINVAL;
43
44	/* Convert Linux syscall to XFS internal ATTR flags */
45	if (!size) {
46		xflags |= ATTR_KERNOVAL;
47		value = NULL;
48	}
49
50	error = xfs_attr_get(ip, (unsigned char *)name, value, &asize, xflags);
51	if (error)
52		return error;
53	return asize;
54}
55
56static int
57xfs_xattr_set(struct dentry *dentry, const char *name, const void *value,
58		size_t size, int flags, int xflags)
59{
60	struct xfs_inode *ip = XFS_I(d_inode(dentry));
61
62	if (strcmp(name, "") == 0)
63		return -EINVAL;
64
65	/* Convert Linux syscall to XFS internal ATTR flags */
66	if (flags & XATTR_CREATE)
67		xflags |= ATTR_CREATE;
68	if (flags & XATTR_REPLACE)
69		xflags |= ATTR_REPLACE;
70
71	if (!value)
72		return xfs_attr_remove(ip, (unsigned char *)name, xflags);
73	return xfs_attr_set(ip, (unsigned char *)name,
74				(void *)value, size, xflags);
75}
76
77static const struct xattr_handler xfs_xattr_user_handler = {
78	.prefix	= XATTR_USER_PREFIX,
79	.flags	= 0, /* no flags implies user namespace */
80	.get	= xfs_xattr_get,
81	.set	= xfs_xattr_set,
82};
83
84static const struct xattr_handler xfs_xattr_trusted_handler = {
85	.prefix	= XATTR_TRUSTED_PREFIX,
86	.flags	= ATTR_ROOT,
87	.get	= xfs_xattr_get,
88	.set	= xfs_xattr_set,
89};
90
91static const struct xattr_handler xfs_xattr_security_handler = {
92	.prefix	= XATTR_SECURITY_PREFIX,
93	.flags	= ATTR_SECURE,
94	.get	= xfs_xattr_get,
95	.set	= xfs_xattr_set,
96};
97
98const struct xattr_handler *xfs_xattr_handlers[] = {
99	&xfs_xattr_user_handler,
100	&xfs_xattr_trusted_handler,
101	&xfs_xattr_security_handler,
102#ifdef CONFIG_XFS_POSIX_ACL
103	&posix_acl_access_xattr_handler,
104	&posix_acl_default_xattr_handler,
105#endif
106	NULL
107};
108
109static unsigned int xfs_xattr_prefix_len(int flags)
110{
111	if (flags & XFS_ATTR_SECURE)
112		return sizeof("security");
113	else if (flags & XFS_ATTR_ROOT)
114		return sizeof("trusted");
115	else
116		return sizeof("user");
117}
118
119static const char *xfs_xattr_prefix(int flags)
120{
121	if (flags & XFS_ATTR_SECURE)
122		return xfs_xattr_security_handler.prefix;
123	else if (flags & XFS_ATTR_ROOT)
124		return xfs_xattr_trusted_handler.prefix;
125	else
126		return xfs_xattr_user_handler.prefix;
127}
128
129static int
130xfs_xattr_put_listent(
131	struct xfs_attr_list_context *context,
132	int		flags,
133	unsigned char	*name,
134	int		namelen,
135	int		valuelen,
136	unsigned char	*value)
137{
138	unsigned int prefix_len = xfs_xattr_prefix_len(flags);
139	char *offset;
140	int arraytop;
141
142	ASSERT(context->count >= 0);
143
144	/*
145	 * Only show root namespace entries if we are actually allowed to
146	 * see them.
147	 */
148	if ((flags & XFS_ATTR_ROOT) && !capable(CAP_SYS_ADMIN))
149		return 0;
150
151	arraytop = context->count + prefix_len + namelen + 1;
152	if (arraytop > context->firstu) {
153		context->count = -1;	/* insufficient space */
154		return 1;
155	}
156	offset = (char *)context->alist + context->count;
157	strncpy(offset, xfs_xattr_prefix(flags), prefix_len);
158	offset += prefix_len;
159	strncpy(offset, (char *)name, namelen);			/* real name */
160	offset += namelen;
161	*offset = '\0';
162	context->count += prefix_len + namelen + 1;
163	return 0;
164}
165
166static int
167xfs_xattr_put_listent_sizes(
168	struct xfs_attr_list_context *context,
169	int		flags,
170	unsigned char	*name,
171	int		namelen,
172	int		valuelen,
173	unsigned char	*value)
174{
175	context->count += xfs_xattr_prefix_len(flags) + namelen + 1;
176	return 0;
177}
178
179static int
180list_one_attr(const char *name, const size_t len, void *data,
181		size_t size, ssize_t *result)
182{
183	char *p = data + *result;
184
185	*result += len;
186	if (!size)
187		return 0;
188	if (*result > size)
189		return -ERANGE;
190
191	strcpy(p, name);
192	return 0;
193}
194
195ssize_t
196xfs_vn_listxattr(struct dentry *dentry, char *data, size_t size)
197{
198	struct xfs_attr_list_context context;
199	struct attrlist_cursor_kern cursor = { 0 };
200	struct inode		*inode = d_inode(dentry);
201	int			error;
202
203	/*
204	 * First read the regular on-disk attributes.
205	 */
206	memset(&context, 0, sizeof(context));
207	context.dp = XFS_I(inode);
208	context.cursor = &cursor;
209	context.resynch = 1;
210	context.alist = data;
211	context.bufsize = size;
212	context.firstu = context.bufsize;
213
214	if (size)
215		context.put_listent = xfs_xattr_put_listent;
216	else
217		context.put_listent = xfs_xattr_put_listent_sizes;
218
219	xfs_attr_list_int(&context);
220	if (context.count < 0)
221		return -ERANGE;
222
223	/*
224	 * Then add the two synthetic ACL attributes.
225	 */
226	if (posix_acl_access_exists(inode)) {
227		error = list_one_attr(POSIX_ACL_XATTR_ACCESS,
228				strlen(POSIX_ACL_XATTR_ACCESS) + 1,
229				data, size, &context.count);
230		if (error)
231			return error;
232	}
233
234	if (posix_acl_default_exists(inode)) {
235		error = list_one_attr(POSIX_ACL_XATTR_DEFAULT,
236				strlen(POSIX_ACL_XATTR_DEFAULT) + 1,
237				data, size, &context.count);
238		if (error)
239			return error;
240	}
241
242	return context.count;
243}
244