xfs: use memdup_user()
[linux-2.6] / fs / xfs / linux-2.6 / xfs_xattr.c
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_da_btree.h"
21 #include "xfs_bmap_btree.h"
22 #include "xfs_inode.h"
23 #include "xfs_attr.h"
24 #include "xfs_attr_leaf.h"
25 #include "xfs_acl.h"
26 #include "xfs_vnodeops.h"
27
28 #include <linux/posix_acl_xattr.h>
29 #include <linux/xattr.h>
30
31
32 /*
33  * ACL handling.  Should eventually be moved into xfs_acl.c
34  */
35
36 static int
37 xfs_decode_acl(const char *name)
38 {
39         if (strcmp(name, "posix_acl_access") == 0)
40                 return _ACL_TYPE_ACCESS;
41         else if (strcmp(name, "posix_acl_default") == 0)
42                 return _ACL_TYPE_DEFAULT;
43         return -EINVAL;
44 }
45
46 /*
47  * Get system extended attributes which at the moment only
48  * includes Posix ACLs.
49  */
50 static int
51 xfs_xattr_system_get(struct inode *inode, const char *name,
52                 void *buffer, size_t size)
53 {
54         int acl;
55
56         acl = xfs_decode_acl(name);
57         if (acl < 0)
58                 return acl;
59
60         return xfs_acl_vget(inode, buffer, size, acl);
61 }
62
63 static int
64 xfs_xattr_system_set(struct inode *inode, const char *name,
65                 const void *value, size_t size, int flags)
66 {
67         int acl;
68
69         acl = xfs_decode_acl(name);
70         if (acl < 0)
71                 return acl;
72         if (flags & XATTR_CREATE)
73                 return -EINVAL;
74
75         if (!value)
76                 return xfs_acl_vremove(inode, acl);
77
78         return xfs_acl_vset(inode, (void *)value, size, acl);
79 }
80
81 static struct xattr_handler xfs_xattr_system_handler = {
82         .prefix = XATTR_SYSTEM_PREFIX,
83         .get    = xfs_xattr_system_get,
84         .set    = xfs_xattr_system_set,
85 };
86
87
88 /*
89  * Real xattr handling.  The only difference between the namespaces is
90  * a flag passed to the low-level attr code.
91  */
92
93 static int
94 __xfs_xattr_get(struct inode *inode, const char *name,
95                 void *value, size_t size, int xflags)
96 {
97         struct xfs_inode *ip = XFS_I(inode);
98         int error, asize = size;
99
100         if (strcmp(name, "") == 0)
101                 return -EINVAL;
102
103         /* Convert Linux syscall to XFS internal ATTR flags */
104         if (!size) {
105                 xflags |= ATTR_KERNOVAL;
106                 value = NULL;
107         }
108
109         error = -xfs_attr_get(ip, name, value, &asize, xflags);
110         if (error)
111                 return error;
112         return asize;
113 }
114
115 static int
116 __xfs_xattr_set(struct inode *inode, const char *name, const void *value,
117                 size_t size, int flags, int xflags)
118 {
119         struct xfs_inode *ip = XFS_I(inode);
120
121         if (strcmp(name, "") == 0)
122                 return -EINVAL;
123
124         /* Convert Linux syscall to XFS internal ATTR flags */
125         if (flags & XATTR_CREATE)
126                 xflags |= ATTR_CREATE;
127         if (flags & XATTR_REPLACE)
128                 xflags |= ATTR_REPLACE;
129
130         if (!value)
131                 return -xfs_attr_remove(ip, name, xflags);
132         return -xfs_attr_set(ip, name, (void *)value, size, xflags);
133 }
134
135 static int
136 xfs_xattr_user_get(struct inode *inode, const char *name,
137                 void *value, size_t size)
138 {
139         return __xfs_xattr_get(inode, name, value, size, 0);
140 }
141
142 static int
143 xfs_xattr_user_set(struct inode *inode, const char *name,
144                 const void *value, size_t size, int flags)
145 {
146         return __xfs_xattr_set(inode, name, value, size, flags, 0);
147 }
148
149 static struct xattr_handler xfs_xattr_user_handler = {
150         .prefix = XATTR_USER_PREFIX,
151         .get    = xfs_xattr_user_get,
152         .set    = xfs_xattr_user_set,
153 };
154
155
156 static int
157 xfs_xattr_trusted_get(struct inode *inode, const char *name,
158                 void *value, size_t size)
159 {
160         return __xfs_xattr_get(inode, name, value, size, ATTR_ROOT);
161 }
162
163 static int
164 xfs_xattr_trusted_set(struct inode *inode, const char *name,
165                 const void *value, size_t size, int flags)
166 {
167         return __xfs_xattr_set(inode, name, value, size, flags, ATTR_ROOT);
168 }
169
170 static struct xattr_handler xfs_xattr_trusted_handler = {
171         .prefix = XATTR_TRUSTED_PREFIX,
172         .get    = xfs_xattr_trusted_get,
173         .set    = xfs_xattr_trusted_set,
174 };
175
176
177 static int
178 xfs_xattr_secure_get(struct inode *inode, const char *name,
179                 void *value, size_t size)
180 {
181         return __xfs_xattr_get(inode, name, value, size, ATTR_SECURE);
182 }
183
184 static int
185 xfs_xattr_secure_set(struct inode *inode, const char *name,
186                 const void *value, size_t size, int flags)
187 {
188         return __xfs_xattr_set(inode, name, value, size, flags, ATTR_SECURE);
189 }
190
191 static struct xattr_handler xfs_xattr_security_handler = {
192         .prefix = XATTR_SECURITY_PREFIX,
193         .get    = xfs_xattr_secure_get,
194         .set    = xfs_xattr_secure_set,
195 };
196
197
198 struct xattr_handler *xfs_xattr_handlers[] = {
199         &xfs_xattr_user_handler,
200         &xfs_xattr_trusted_handler,
201         &xfs_xattr_security_handler,
202         &xfs_xattr_system_handler,
203         NULL
204 };
205
206 static unsigned int xfs_xattr_prefix_len(int flags)
207 {
208         if (flags & XFS_ATTR_SECURE)
209                 return sizeof("security");
210         else if (flags & XFS_ATTR_ROOT)
211                 return sizeof("trusted");
212         else
213                 return sizeof("user");
214 }
215
216 static const char *xfs_xattr_prefix(int flags)
217 {
218         if (flags & XFS_ATTR_SECURE)
219                 return xfs_xattr_security_handler.prefix;
220         else if (flags & XFS_ATTR_ROOT)
221                 return xfs_xattr_trusted_handler.prefix;
222         else
223                 return xfs_xattr_user_handler.prefix;
224 }
225
226 static int
227 xfs_xattr_put_listent(struct xfs_attr_list_context *context, int flags,
228                 char *name, int namelen, int valuelen, char *value)
229 {
230         unsigned int prefix_len = xfs_xattr_prefix_len(flags);
231         char *offset;
232         int arraytop;
233
234         ASSERT(context->count >= 0);
235
236         /*
237          * Only show root namespace entries if we are actually allowed to
238          * see them.
239          */
240         if ((flags & XFS_ATTR_ROOT) && !capable(CAP_SYS_ADMIN))
241                 return 0;
242
243         arraytop = context->count + prefix_len + namelen + 1;
244         if (arraytop > context->firstu) {
245                 context->count = -1;    /* insufficient space */
246                 return 1;
247         }
248         offset = (char *)context->alist + context->count;
249         strncpy(offset, xfs_xattr_prefix(flags), prefix_len);
250         offset += prefix_len;
251         strncpy(offset, name, namelen);                 /* real name */
252         offset += namelen;
253         *offset = '\0';
254         context->count += prefix_len + namelen + 1;
255         return 0;
256 }
257
258 static int
259 xfs_xattr_put_listent_sizes(struct xfs_attr_list_context *context, int flags,
260                 char *name, int namelen, int valuelen, char *value)
261 {
262         context->count += xfs_xattr_prefix_len(flags) + namelen + 1;
263         return 0;
264 }
265
266 static int
267 list_one_attr(const char *name, const size_t len, void *data,
268                 size_t size, ssize_t *result)
269 {
270         char *p = data + *result;
271
272         *result += len;
273         if (!size)
274                 return 0;
275         if (*result > size)
276                 return -ERANGE;
277
278         strcpy(p, name);
279         return 0;
280 }
281
282 ssize_t
283 xfs_vn_listxattr(struct dentry *dentry, char *data, size_t size)
284 {
285         struct xfs_attr_list_context context;
286         struct attrlist_cursor_kern cursor = { 0 };
287         struct inode            *inode = dentry->d_inode;
288         int                     error;
289
290         /*
291          * First read the regular on-disk attributes.
292          */
293         memset(&context, 0, sizeof(context));
294         context.dp = XFS_I(inode);
295         context.cursor = &cursor;
296         context.resynch = 1;
297         context.alist = data;
298         context.bufsize = size;
299         context.firstu = context.bufsize;
300
301         if (size)
302                 context.put_listent = xfs_xattr_put_listent;
303         else
304                 context.put_listent = xfs_xattr_put_listent_sizes;
305
306         xfs_attr_list_int(&context);
307         if (context.count < 0)
308                 return -ERANGE;
309
310         /*
311          * Then add the two synthetic ACL attributes.
312          */
313         if (xfs_acl_vhasacl_access(inode)) {
314                 error = list_one_attr(POSIX_ACL_XATTR_ACCESS,
315                                 strlen(POSIX_ACL_XATTR_ACCESS) + 1,
316                                 data, size, &context.count);
317                 if (error)
318                         return error;
319         }
320
321         if (xfs_acl_vhasacl_default(inode)) {
322                 error = list_one_attr(POSIX_ACL_XATTR_DEFAULT,
323                                 strlen(POSIX_ACL_XATTR_DEFAULT) + 1,
324                                 data, size, &context.count);
325                 if (error)
326                         return error;
327         }
328
329         return context.count;
330 }