cns3xxx: sync kernel config
[openwrt/svn-archive/archive.git] / target / linux / generic / patches-2.6.39 / 040-backport_tmpfs_xattr.patch
1 --- a/fs/Kconfig
2 +++ b/fs/Kconfig
3 @@ -121,9 +121,25 @@ config TMPFS
4
5 See <file:Documentation/filesystems/tmpfs.txt> for details.
6
7 +config TMPFS_XATTR
8 + bool "Tmpfs extended attributes"
9 + depends on TMPFS
10 + default n
11 + help
12 + Extended attributes are name:value pairs associated with inodes by
13 + the kernel or by users (see the attr(5) manual page, or visit
14 + <http://acl.bestbits.at/> for details).
15 +
16 + Currently this enables support for the trusted.* and
17 + security.* namespaces.
18 +
19 + If unsure, say N.
20 +
21 + You need this for POSIX ACL support on tmpfs.
22 +
23 config TMPFS_POSIX_ACL
24 bool "Tmpfs POSIX Access Control Lists"
25 - depends on TMPFS
26 + depends on TMPFS_XATTR
27 select GENERIC_ACL
28 help
29 POSIX Access Control Lists (ACLs) support permissions for users and
30 --- a/include/linux/shmem_fs.h
31 +++ b/include/linux/shmem_fs.h
32 @@ -9,6 +9,8 @@
33
34 #define SHMEM_NR_DIRECT 16
35
36 +#define SHMEM_SYMLINK_INLINE_LEN (SHMEM_NR_DIRECT * sizeof(swp_entry_t))
37 +
38 struct shmem_inode_info {
39 spinlock_t lock;
40 unsigned long flags;
41 @@ -17,8 +19,12 @@ struct shmem_inode_info {
42 unsigned long next_index; /* highest alloced index + 1 */
43 struct shared_policy policy; /* NUMA memory alloc policy */
44 struct page *i_indirect; /* top indirect blocks page */
45 - swp_entry_t i_direct[SHMEM_NR_DIRECT]; /* first blocks */
46 + union {
47 + swp_entry_t i_direct[SHMEM_NR_DIRECT]; /* first blocks */
48 + char inline_symlink[SHMEM_SYMLINK_INLINE_LEN];
49 + };
50 struct list_head swaplist; /* chain of maybes on swap */
51 + struct list_head xattr_list; /* list of shmem_xattr */
52 struct inode vfs_inode;
53 };
54
55 --- a/mm/shmem.c
56 +++ b/mm/shmem.c
57 @@ -99,6 +99,13 @@ static struct vfsmount *shm_mnt;
58 /* Pretend that each entry is of this size in directory's i_size */
59 #define BOGO_DIRENT_SIZE 20
60
61 +struct shmem_xattr {
62 + struct list_head list; /* anchored by shmem_inode_info->xattr_list */
63 + char *name; /* xattr name */
64 + size_t size;
65 + char value[0];
66 +};
67 +
68 /* Flag allocation requirements to shmem_getpage and shmem_swp_alloc */
69 enum sgp_type {
70 SGP_READ, /* don't exceed i_size, don't allocate page */
71 @@ -822,6 +829,7 @@ static int shmem_notify_change(struct de
72 static void shmem_evict_inode(struct inode *inode)
73 {
74 struct shmem_inode_info *info = SHMEM_I(inode);
75 + struct shmem_xattr *xattr, *nxattr;
76
77 if (inode->i_mapping->a_ops == &shmem_aops) {
78 truncate_inode_pages(inode->i_mapping, 0);
79 @@ -834,6 +842,11 @@ static void shmem_evict_inode(struct ino
80 mutex_unlock(&shmem_swaplist_mutex);
81 }
82 }
83 +
84 + list_for_each_entry_safe(xattr, nxattr, &info->xattr_list, list) {
85 + kfree(xattr->name);
86 + kfree(xattr);
87 + }
88 BUG_ON(inode->i_blocks);
89 shmem_free_inode(inode->i_sb);
90 end_writeback(inode);
91 @@ -1615,6 +1628,7 @@ static struct inode *shmem_get_inode(str
92 spin_lock_init(&info->lock);
93 info->flags = flags & VM_NORESERVE;
94 INIT_LIST_HEAD(&info->swaplist);
95 + INIT_LIST_HEAD(&info->xattr_list);
96 cache_no_acl(inode);
97
98 switch (mode & S_IFMT) {
99 @@ -2014,9 +2028,9 @@ static int shmem_symlink(struct inode *d
100
101 info = SHMEM_I(inode);
102 inode->i_size = len-1;
103 - if (len <= (char *)inode - (char *)info) {
104 + if (len <= SHMEM_SYMLINK_INLINE_LEN) {
105 /* do it inline */
106 - memcpy(info, symname, len);
107 + memcpy(info->inline_symlink, symname, len);
108 inode->i_op = &shmem_symlink_inline_operations;
109 } else {
110 error = shmem_getpage(inode, 0, &page, SGP_WRITE, NULL);
111 @@ -2042,7 +2056,7 @@ static int shmem_symlink(struct inode *d
112
113 static void *shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd)
114 {
115 - nd_set_link(nd, (char *)SHMEM_I(dentry->d_inode));
116 + nd_set_link(nd, SHMEM_I(dentry->d_inode)->inline_symlink);
117 return NULL;
118 }
119
120 @@ -2066,63 +2080,253 @@ static void shmem_put_link(struct dentry
121 }
122 }
123
124 -static const struct inode_operations shmem_symlink_inline_operations = {
125 - .readlink = generic_readlink,
126 - .follow_link = shmem_follow_link_inline,
127 -};
128 -
129 -static const struct inode_operations shmem_symlink_inode_operations = {
130 - .readlink = generic_readlink,
131 - .follow_link = shmem_follow_link,
132 - .put_link = shmem_put_link,
133 -};
134 -
135 -#ifdef CONFIG_TMPFS_POSIX_ACL
136 +#ifdef CONFIG_TMPFS_XATTR
137 /*
138 - * Superblocks without xattr inode operations will get security.* xattr
139 - * support from the VFS "for free". As soon as we have any other xattrs
140 + * Superblocks without xattr inode operations may get some security.* xattr
141 + * support from the LSM "for free". As soon as we have any other xattrs
142 * like ACLs, we also need to implement the security.* handlers at
143 * filesystem level, though.
144 */
145
146 -static size_t shmem_xattr_security_list(struct dentry *dentry, char *list,
147 - size_t list_len, const char *name,
148 - size_t name_len, int handler_flags)
149 +static int shmem_xattr_get(struct dentry *dentry, const char *name,
150 + void *buffer, size_t size)
151 {
152 - return security_inode_listsecurity(dentry->d_inode, list, list_len);
153 -}
154 + struct shmem_inode_info *info;
155 + struct shmem_xattr *xattr;
156 + int ret = -ENODATA;
157
158 -static int shmem_xattr_security_get(struct dentry *dentry, const char *name,
159 - void *buffer, size_t size, int handler_flags)
160 -{
161 - if (strcmp(name, "") == 0)
162 - return -EINVAL;
163 - return xattr_getsecurity(dentry->d_inode, name, buffer, size);
164 + info = SHMEM_I(dentry->d_inode);
165 +
166 + spin_lock(&info->lock);
167 + list_for_each_entry(xattr, &info->xattr_list, list) {
168 + if (strcmp(name, xattr->name))
169 + continue;
170 +
171 + ret = xattr->size;
172 + if (buffer) {
173 + if (size < xattr->size)
174 + ret = -ERANGE;
175 + else
176 + memcpy(buffer, xattr->value, xattr->size);
177 + }
178 + break;
179 + }
180 + spin_unlock(&info->lock);
181 + return ret;
182 }
183
184 -static int shmem_xattr_security_set(struct dentry *dentry, const char *name,
185 - const void *value, size_t size, int flags, int handler_flags)
186 +static int shmem_xattr_set(struct dentry *dentry, const char *name,
187 + const void *value, size_t size, int flags)
188 {
189 - if (strcmp(name, "") == 0)
190 - return -EINVAL;
191 - return security_inode_setsecurity(dentry->d_inode, name, value,
192 - size, flags);
193 + struct inode *inode = dentry->d_inode;
194 + struct shmem_inode_info *info = SHMEM_I(inode);
195 + struct shmem_xattr *xattr;
196 + struct shmem_xattr *new_xattr = NULL;
197 + size_t len;
198 + int err = 0;
199 +
200 + /* value == NULL means remove */
201 + if (value) {
202 + /* wrap around? */
203 + len = sizeof(*new_xattr) + size;
204 + if (len <= sizeof(*new_xattr))
205 + return -ENOMEM;
206 +
207 + new_xattr = kmalloc(len, GFP_KERNEL);
208 + if (!new_xattr)
209 + return -ENOMEM;
210 +
211 + new_xattr->name = kstrdup(name, GFP_KERNEL);
212 + if (!new_xattr->name) {
213 + kfree(new_xattr);
214 + return -ENOMEM;
215 + }
216 +
217 + new_xattr->size = size;
218 + memcpy(new_xattr->value, value, size);
219 + }
220 +
221 + spin_lock(&info->lock);
222 + list_for_each_entry(xattr, &info->xattr_list, list) {
223 + if (!strcmp(name, xattr->name)) {
224 + if (flags & XATTR_CREATE) {
225 + xattr = new_xattr;
226 + err = -EEXIST;
227 + } else if (new_xattr) {
228 + list_replace(&xattr->list, &new_xattr->list);
229 + } else {
230 + list_del(&xattr->list);
231 + }
232 + goto out;
233 + }
234 + }
235 + if (flags & XATTR_REPLACE) {
236 + xattr = new_xattr;
237 + err = -ENODATA;
238 + } else {
239 + list_add(&new_xattr->list, &info->xattr_list);
240 + xattr = NULL;
241 + }
242 +out:
243 + spin_unlock(&info->lock);
244 + if (xattr)
245 + kfree(xattr->name);
246 + kfree(xattr);
247 + return err;
248 }
249
250 -static const struct xattr_handler shmem_xattr_security_handler = {
251 - .prefix = XATTR_SECURITY_PREFIX,
252 - .list = shmem_xattr_security_list,
253 - .get = shmem_xattr_security_get,
254 - .set = shmem_xattr_security_set,
255 -};
256
257 static const struct xattr_handler *shmem_xattr_handlers[] = {
258 +#ifdef CONFIG_TMPFS_POSIX_ACL
259 &generic_acl_access_handler,
260 &generic_acl_default_handler,
261 - &shmem_xattr_security_handler,
262 +#endif
263 NULL
264 };
265 +
266 +static int shmem_xattr_validate(const char *name)
267 +{
268 + struct { const char *prefix; size_t len; } arr[] = {
269 + { XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN },
270 + { XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN }
271 + };
272 + int i;
273 +
274 + for (i = 0; i < ARRAY_SIZE(arr); i++) {
275 + size_t preflen = arr[i].len;
276 + if (strncmp(name, arr[i].prefix, preflen) == 0) {
277 + if (!name[preflen])
278 + return -EINVAL;
279 + return 0;
280 + }
281 + }
282 + return -EOPNOTSUPP;
283 +}
284 +
285 +static ssize_t shmem_getxattr(struct dentry *dentry, const char *name,
286 + void *buffer, size_t size)
287 +{
288 + int err;
289 +
290 + /*
291 + * If this is a request for a synthetic attribute in the system.*
292 + * namespace use the generic infrastructure to resolve a handler
293 + * for it via sb->s_xattr.
294 + */
295 + if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
296 + return generic_getxattr(dentry, name, buffer, size);
297 +
298 + err = shmem_xattr_validate(name);
299 + if (err)
300 + return err;
301 +
302 + return shmem_xattr_get(dentry, name, buffer, size);
303 +}
304 +
305 +static int shmem_setxattr(struct dentry *dentry, const char *name,
306 + const void *value, size_t size, int flags)
307 +{
308 + int err;
309 +
310 + /*
311 + * If this is a request for a synthetic attribute in the system.*
312 + * namespace use the generic infrastructure to resolve a handler
313 + * for it via sb->s_xattr.
314 + */
315 + if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
316 + return generic_setxattr(dentry, name, value, size, flags);
317 +
318 + err = shmem_xattr_validate(name);
319 + if (err)
320 + return err;
321 +
322 + if (size == 0)
323 + value = ""; /* empty EA, do not remove */
324 +
325 + return shmem_xattr_set(dentry, name, value, size, flags);
326 +
327 +}
328 +
329 +static int shmem_removexattr(struct dentry *dentry, const char *name)
330 +{
331 + int err;
332 +
333 + /*
334 + * If this is a request for a synthetic attribute in the system.*
335 + * namespace use the generic infrastructure to resolve a handler
336 + * for it via sb->s_xattr.
337 + */
338 + if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
339 + return generic_removexattr(dentry, name);
340 +
341 + err = shmem_xattr_validate(name);
342 + if (err)
343 + return err;
344 +
345 + return shmem_xattr_set(dentry, name, NULL, 0, XATTR_REPLACE);
346 +}
347 +
348 +static bool xattr_is_trusted(const char *name)
349 +{
350 + return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN);
351 +}
352 +
353 +static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size)
354 +{
355 + bool trusted = capable(CAP_SYS_ADMIN);
356 + struct shmem_xattr *xattr;
357 + struct shmem_inode_info *info;
358 + size_t used = 0;
359 +
360 + info = SHMEM_I(dentry->d_inode);
361 +
362 + spin_lock(&info->lock);
363 + list_for_each_entry(xattr, &info->xattr_list, list) {
364 + size_t len;
365 +
366 + /* skip "trusted." attributes for unprivileged callers */
367 + if (!trusted && xattr_is_trusted(xattr->name))
368 + continue;
369 +
370 + len = strlen(xattr->name) + 1;
371 + used += len;
372 + if (buffer) {
373 + if (size < used) {
374 + used = -ERANGE;
375 + break;
376 + }
377 + memcpy(buffer, xattr->name, len);
378 + buffer += len;
379 + }
380 + }
381 + spin_unlock(&info->lock);
382 +
383 + return used;
384 +}
385 +#endif /* CONFIG_TMPFS_XATTR */
386 +
387 +static const struct inode_operations shmem_symlink_inline_operations = {
388 + .readlink = generic_readlink,
389 + .follow_link = shmem_follow_link_inline,
390 +#ifdef CONFIG_TMPFS_XATTR
391 + .setxattr = shmem_setxattr,
392 + .getxattr = shmem_getxattr,
393 + .listxattr = shmem_listxattr,
394 + .removexattr = shmem_removexattr,
395 +#endif
396 +};
397 +
398 +static const struct inode_operations shmem_symlink_inode_operations = {
399 + .readlink = generic_readlink,
400 + .follow_link = shmem_follow_link,
401 + .put_link = shmem_put_link,
402 +#ifdef CONFIG_TMPFS_XATTR
403 + .setxattr = shmem_setxattr,
404 + .getxattr = shmem_getxattr,
405 + .listxattr = shmem_listxattr,
406 + .removexattr = shmem_removexattr,
407 #endif
408 +};
409
410 static struct dentry *shmem_get_parent(struct dentry *child)
411 {
412 @@ -2402,8 +2606,10 @@ int shmem_fill_super(struct super_block
413 sb->s_magic = TMPFS_MAGIC;
414 sb->s_op = &shmem_ops;
415 sb->s_time_gran = 1;
416 -#ifdef CONFIG_TMPFS_POSIX_ACL
417 +#ifdef CONFIG_TMPFS_XATTR
418 sb->s_xattr = shmem_xattr_handlers;
419 +#endif
420 +#ifdef CONFIG_TMPFS_POSIX_ACL
421 sb->s_flags |= MS_POSIXACL;
422 #endif
423
424 @@ -2501,11 +2707,13 @@ static const struct file_operations shme
425 static const struct inode_operations shmem_inode_operations = {
426 .setattr = shmem_notify_change,
427 .truncate_range = shmem_truncate_range,
428 +#ifdef CONFIG_TMPFS_XATTR
429 + .setxattr = shmem_setxattr,
430 + .getxattr = shmem_getxattr,
431 + .listxattr = shmem_listxattr,
432 + .removexattr = shmem_removexattr,
433 +#endif
434 #ifdef CONFIG_TMPFS_POSIX_ACL
435 - .setxattr = generic_setxattr,
436 - .getxattr = generic_getxattr,
437 - .listxattr = generic_listxattr,
438 - .removexattr = generic_removexattr,
439 .check_acl = generic_check_acl,
440 #endif
441
442 @@ -2523,23 +2731,27 @@ static const struct inode_operations shm
443 .mknod = shmem_mknod,
444 .rename = shmem_rename,
445 #endif
446 +#ifdef CONFIG_TMPFS_XATTR
447 + .setxattr = shmem_setxattr,
448 + .getxattr = shmem_getxattr,
449 + .listxattr = shmem_listxattr,
450 + .removexattr = shmem_removexattr,
451 +#endif
452 #ifdef CONFIG_TMPFS_POSIX_ACL
453 .setattr = shmem_notify_change,
454 - .setxattr = generic_setxattr,
455 - .getxattr = generic_getxattr,
456 - .listxattr = generic_listxattr,
457 - .removexattr = generic_removexattr,
458 .check_acl = generic_check_acl,
459 #endif
460 };
461
462 static const struct inode_operations shmem_special_inode_operations = {
463 +#ifdef CONFIG_TMPFS_XATTR
464 + .setxattr = shmem_setxattr,
465 + .getxattr = shmem_getxattr,
466 + .listxattr = shmem_listxattr,
467 + .removexattr = shmem_removexattr,
468 +#endif
469 #ifdef CONFIG_TMPFS_POSIX_ACL
470 .setattr = shmem_notify_change,
471 - .setxattr = generic_setxattr,
472 - .getxattr = generic_getxattr,
473 - .listxattr = generic_listxattr,
474 - .removexattr = generic_removexattr,
475 .check_acl = generic_check_acl,
476 #endif
477 };