c7ae1964e46db56a62db98a03fcce37c30851030
[openwrt.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  };