kernel: backport and enable tmpfs xattr support for 2.6.39
authorFelix Fietkau <nbd@openwrt.org>
Tue, 19 Jul 2011 09:00:55 +0000 (09:00 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Tue, 19 Jul 2011 09:00:55 +0000 (09:00 +0000)
SVN-Revision: 27691

target/linux/generic/config-2.6.39
target/linux/generic/patches-2.6.39/040-backport_tmpfs_xattr.patch [new file with mode: 0644]
target/linux/generic/patches-2.6.39/950-vm_exports.patch

index a322f4763ebcbcde68c4c6dcbab4d60ba8624ab0..b98814ab4bacb5d05e2704df927e5b8d5a6485b4 100644 (file)
@@ -2631,6 +2631,7 @@ CONFIG_TINY_RCU=y
 # CONFIG_TLAN is not set
 # CONFIG_TMD_HERMES is not set
 CONFIG_TMPFS=y
+CONFIG_TMPFS_XATTR=y
 # CONFIG_TMPFS_POSIX_ACL is not set
 # CONFIG_TOUCHSCREEN_AD7877 is not set
 # CONFIG_TOUCHSCREEN_AD7879 is not set
diff --git a/target/linux/generic/patches-2.6.39/040-backport_tmpfs_xattr.patch b/target/linux/generic/patches-2.6.39/040-backport_tmpfs_xattr.patch
new file mode 100644 (file)
index 0000000..e5d8d43
--- /dev/null
@@ -0,0 +1,477 @@
+--- a/fs/Kconfig
++++ b/fs/Kconfig
+@@ -121,9 +121,25 @@ config TMPFS
+         See <file:Documentation/filesystems/tmpfs.txt> for details.
++config TMPFS_XATTR
++      bool "Tmpfs extended attributes"
++      depends on TMPFS
++      default n
++      help
++        Extended attributes are name:value pairs associated with inodes by
++        the kernel or by users (see the attr(5) manual page, or visit
++        <http://acl.bestbits.at/> for details).
++
++        Currently this enables support for the trusted.* and
++        security.* namespaces.
++
++        If unsure, say N.
++
++        You need this for POSIX ACL support on tmpfs.
++
+ config TMPFS_POSIX_ACL
+       bool "Tmpfs POSIX Access Control Lists"
+-      depends on TMPFS
++      depends on TMPFS_XATTR
+       select GENERIC_ACL
+       help
+         POSIX Access Control Lists (ACLs) support permissions for users and
+--- a/include/linux/shmem_fs.h
++++ b/include/linux/shmem_fs.h
+@@ -9,6 +9,8 @@
+ #define SHMEM_NR_DIRECT 16
++#define SHMEM_SYMLINK_INLINE_LEN (SHMEM_NR_DIRECT * sizeof(swp_entry_t))
++
+ struct shmem_inode_info {
+       spinlock_t              lock;
+       unsigned long           flags;
+@@ -17,8 +19,12 @@ struct shmem_inode_info {
+       unsigned long           next_index;     /* highest alloced index + 1 */
+       struct shared_policy    policy;         /* NUMA memory alloc policy */
+       struct page             *i_indirect;    /* top indirect blocks page */
+-      swp_entry_t             i_direct[SHMEM_NR_DIRECT]; /* first blocks */
++      union {
++              swp_entry_t     i_direct[SHMEM_NR_DIRECT]; /* first blocks */
++              char            inline_symlink[SHMEM_SYMLINK_INLINE_LEN];
++      };
+       struct list_head        swaplist;       /* chain of maybes on swap */
++      struct list_head        xattr_list;     /* list of shmem_xattr */
+       struct inode            vfs_inode;
+ };
+--- a/mm/shmem.c
++++ b/mm/shmem.c
+@@ -99,6 +99,13 @@ static struct vfsmount *shm_mnt;
+ /* Pretend that each entry is of this size in directory's i_size */
+ #define BOGO_DIRENT_SIZE 20
++struct shmem_xattr {
++      struct list_head list;  /* anchored by shmem_inode_info->xattr_list */
++      char *name;             /* xattr name */
++      size_t size;
++      char value[0];
++};
++
+ /* Flag allocation requirements to shmem_getpage and shmem_swp_alloc */
+ enum sgp_type {
+       SGP_READ,       /* don't exceed i_size, don't allocate page */
+@@ -822,6 +829,7 @@ static int shmem_notify_change(struct de
+ static void shmem_evict_inode(struct inode *inode)
+ {
+       struct shmem_inode_info *info = SHMEM_I(inode);
++      struct shmem_xattr *xattr, *nxattr;
+       if (inode->i_mapping->a_ops == &shmem_aops) {
+               truncate_inode_pages(inode->i_mapping, 0);
+@@ -834,6 +842,11 @@ static void shmem_evict_inode(struct ino
+                       mutex_unlock(&shmem_swaplist_mutex);
+               }
+       }
++
++      list_for_each_entry_safe(xattr, nxattr, &info->xattr_list, list) {
++              kfree(xattr->name);
++              kfree(xattr);
++      }
+       BUG_ON(inode->i_blocks);
+       shmem_free_inode(inode->i_sb);
+       end_writeback(inode);
+@@ -1615,6 +1628,7 @@ static struct inode *shmem_get_inode(str
+               spin_lock_init(&info->lock);
+               info->flags = flags & VM_NORESERVE;
+               INIT_LIST_HEAD(&info->swaplist);
++              INIT_LIST_HEAD(&info->xattr_list);
+               cache_no_acl(inode);
+               switch (mode & S_IFMT) {
+@@ -2014,9 +2028,9 @@ static int shmem_symlink(struct inode *d
+       info = SHMEM_I(inode);
+       inode->i_size = len-1;
+-      if (len <= (char *)inode - (char *)info) {
++      if (len <= SHMEM_SYMLINK_INLINE_LEN) {
+               /* do it inline */
+-              memcpy(info, symname, len);
++              memcpy(info->inline_symlink, symname, len);
+               inode->i_op = &shmem_symlink_inline_operations;
+       } else {
+               error = shmem_getpage(inode, 0, &page, SGP_WRITE, NULL);
+@@ -2042,7 +2056,7 @@ static int shmem_symlink(struct inode *d
+ static void *shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd)
+ {
+-      nd_set_link(nd, (char *)SHMEM_I(dentry->d_inode));
++      nd_set_link(nd, SHMEM_I(dentry->d_inode)->inline_symlink);
+       return NULL;
+ }
+@@ -2066,63 +2080,253 @@ static void shmem_put_link(struct dentry
+       }
+ }
+-static const struct inode_operations shmem_symlink_inline_operations = {
+-      .readlink       = generic_readlink,
+-      .follow_link    = shmem_follow_link_inline,
+-};
+-
+-static const struct inode_operations shmem_symlink_inode_operations = {
+-      .readlink       = generic_readlink,
+-      .follow_link    = shmem_follow_link,
+-      .put_link       = shmem_put_link,
+-};
+-
+-#ifdef CONFIG_TMPFS_POSIX_ACL
++#ifdef CONFIG_TMPFS_XATTR
+ /*
+- * Superblocks without xattr inode operations will get security.* xattr
+- * support from the VFS "for free". As soon as we have any other xattrs
++ * Superblocks without xattr inode operations may get some security.* xattr
++ * support from the LSM "for free". As soon as we have any other xattrs
+  * like ACLs, we also need to implement the security.* handlers at
+  * filesystem level, though.
+  */
+-static size_t shmem_xattr_security_list(struct dentry *dentry, char *list,
+-                                      size_t list_len, const char *name,
+-                                      size_t name_len, int handler_flags)
++static int shmem_xattr_get(struct dentry *dentry, const char *name,
++                         void *buffer, size_t size)
+ {
+-      return security_inode_listsecurity(dentry->d_inode, list, list_len);
+-}
++      struct shmem_inode_info *info;
++      struct shmem_xattr *xattr;
++      int ret = -ENODATA;
+-static int shmem_xattr_security_get(struct dentry *dentry, const char *name,
+-              void *buffer, size_t size, int handler_flags)
+-{
+-      if (strcmp(name, "") == 0)
+-              return -EINVAL;
+-      return xattr_getsecurity(dentry->d_inode, name, buffer, size);
++      info = SHMEM_I(dentry->d_inode);
++
++      spin_lock(&info->lock);
++      list_for_each_entry(xattr, &info->xattr_list, list) {
++              if (strcmp(name, xattr->name))
++                      continue;
++
++              ret = xattr->size;
++              if (buffer) {
++                      if (size < xattr->size)
++                              ret = -ERANGE;
++                      else
++                              memcpy(buffer, xattr->value, xattr->size);
++              }
++              break;
++      }
++      spin_unlock(&info->lock);
++      return ret;
+ }
+-static int shmem_xattr_security_set(struct dentry *dentry, const char *name,
+-              const void *value, size_t size, int flags, int handler_flags)
++static int shmem_xattr_set(struct dentry *dentry, const char *name,
++                         const void *value, size_t size, int flags)
+ {
+-      if (strcmp(name, "") == 0)
+-              return -EINVAL;
+-      return security_inode_setsecurity(dentry->d_inode, name, value,
+-                                        size, flags);
++      struct inode *inode = dentry->d_inode;
++      struct shmem_inode_info *info = SHMEM_I(inode);
++      struct shmem_xattr *xattr;
++      struct shmem_xattr *new_xattr = NULL;
++      size_t len;
++      int err = 0;
++
++      /* value == NULL means remove */
++      if (value) {
++              /* wrap around? */
++              len = sizeof(*new_xattr) + size;
++              if (len <= sizeof(*new_xattr))
++                      return -ENOMEM;
++
++              new_xattr = kmalloc(len, GFP_KERNEL);
++              if (!new_xattr)
++                      return -ENOMEM;
++
++              new_xattr->name = kstrdup(name, GFP_KERNEL);
++              if (!new_xattr->name) {
++                      kfree(new_xattr);
++                      return -ENOMEM;
++              }
++
++              new_xattr->size = size;
++              memcpy(new_xattr->value, value, size);
++      }
++
++      spin_lock(&info->lock);
++      list_for_each_entry(xattr, &info->xattr_list, list) {
++              if (!strcmp(name, xattr->name)) {
++                      if (flags & XATTR_CREATE) {
++                              xattr = new_xattr;
++                              err = -EEXIST;
++                      } else if (new_xattr) {
++                              list_replace(&xattr->list, &new_xattr->list);
++                      } else {
++                              list_del(&xattr->list);
++                      }
++                      goto out;
++              }
++      }
++      if (flags & XATTR_REPLACE) {
++              xattr = new_xattr;
++              err = -ENODATA;
++      } else {
++              list_add(&new_xattr->list, &info->xattr_list);
++              xattr = NULL;
++      }
++out:
++      spin_unlock(&info->lock);
++      if (xattr)
++              kfree(xattr->name);
++      kfree(xattr);
++      return err;
+ }
+-static const struct xattr_handler shmem_xattr_security_handler = {
+-      .prefix = XATTR_SECURITY_PREFIX,
+-      .list   = shmem_xattr_security_list,
+-      .get    = shmem_xattr_security_get,
+-      .set    = shmem_xattr_security_set,
+-};
+ static const struct xattr_handler *shmem_xattr_handlers[] = {
++#ifdef CONFIG_TMPFS_POSIX_ACL
+       &generic_acl_access_handler,
+       &generic_acl_default_handler,
+-      &shmem_xattr_security_handler,
++#endif
+       NULL
+ };
++
++static int shmem_xattr_validate(const char *name)
++{
++      struct { const char *prefix; size_t len; } arr[] = {
++              { XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN },
++              { XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN }
++      };
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(arr); i++) {
++              size_t preflen = arr[i].len;
++              if (strncmp(name, arr[i].prefix, preflen) == 0) {
++                      if (!name[preflen])
++                              return -EINVAL;
++                      return 0;
++              }
++      }
++      return -EOPNOTSUPP;
++}
++
++static ssize_t shmem_getxattr(struct dentry *dentry, const char *name,
++                            void *buffer, size_t size)
++{
++      int err;
++
++      /*
++       * If this is a request for a synthetic attribute in the system.*
++       * namespace use the generic infrastructure to resolve a handler
++       * for it via sb->s_xattr.
++       */
++      if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
++              return generic_getxattr(dentry, name, buffer, size);
++
++      err = shmem_xattr_validate(name);
++      if (err)
++              return err;
++
++      return shmem_xattr_get(dentry, name, buffer, size);
++}
++
++static int shmem_setxattr(struct dentry *dentry, const char *name,
++                        const void *value, size_t size, int flags)
++{
++      int err;
++
++      /*
++       * If this is a request for a synthetic attribute in the system.*
++       * namespace use the generic infrastructure to resolve a handler
++       * for it via sb->s_xattr.
++       */
++      if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
++              return generic_setxattr(dentry, name, value, size, flags);
++
++      err = shmem_xattr_validate(name);
++      if (err)
++              return err;
++
++      if (size == 0)
++              value = "";  /* empty EA, do not remove */
++
++      return shmem_xattr_set(dentry, name, value, size, flags);
++
++}
++
++static int shmem_removexattr(struct dentry *dentry, const char *name)
++{
++      int err;
++
++      /*
++       * If this is a request for a synthetic attribute in the system.*
++       * namespace use the generic infrastructure to resolve a handler
++       * for it via sb->s_xattr.
++       */
++      if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
++              return generic_removexattr(dentry, name);
++
++      err = shmem_xattr_validate(name);
++      if (err)
++              return err;
++
++      return shmem_xattr_set(dentry, name, NULL, 0, XATTR_REPLACE);
++}
++
++static bool xattr_is_trusted(const char *name)
++{
++      return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN);
++}
++
++static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size)
++{
++      bool trusted = capable(CAP_SYS_ADMIN);
++      struct shmem_xattr *xattr;
++      struct shmem_inode_info *info;
++      size_t used = 0;
++
++      info = SHMEM_I(dentry->d_inode);
++
++      spin_lock(&info->lock);
++      list_for_each_entry(xattr, &info->xattr_list, list) {
++              size_t len;
++
++              /* skip "trusted." attributes for unprivileged callers */
++              if (!trusted && xattr_is_trusted(xattr->name))
++                      continue;
++
++              len = strlen(xattr->name) + 1;
++              used += len;
++              if (buffer) {
++                      if (size < used) {
++                              used = -ERANGE;
++                              break;
++                      }
++                      memcpy(buffer, xattr->name, len);
++                      buffer += len;
++              }
++      }
++      spin_unlock(&info->lock);
++
++      return used;
++}
++#endif /* CONFIG_TMPFS_XATTR */
++
++static const struct inode_operations shmem_symlink_inline_operations = {
++      .readlink       = generic_readlink,
++      .follow_link    = shmem_follow_link_inline,
++#ifdef CONFIG_TMPFS_XATTR
++      .setxattr       = shmem_setxattr,
++      .getxattr       = shmem_getxattr,
++      .listxattr      = shmem_listxattr,
++      .removexattr    = shmem_removexattr,
++#endif
++};
++
++static const struct inode_operations shmem_symlink_inode_operations = {
++      .readlink       = generic_readlink,
++      .follow_link    = shmem_follow_link,
++      .put_link       = shmem_put_link,
++#ifdef CONFIG_TMPFS_XATTR
++      .setxattr       = shmem_setxattr,
++      .getxattr       = shmem_getxattr,
++      .listxattr      = shmem_listxattr,
++      .removexattr    = shmem_removexattr,
+ #endif
++};
+ static struct dentry *shmem_get_parent(struct dentry *child)
+ {
+@@ -2402,8 +2606,10 @@ int shmem_fill_super(struct super_block 
+       sb->s_magic = TMPFS_MAGIC;
+       sb->s_op = &shmem_ops;
+       sb->s_time_gran = 1;
+-#ifdef CONFIG_TMPFS_POSIX_ACL
++#ifdef CONFIG_TMPFS_XATTR
+       sb->s_xattr = shmem_xattr_handlers;
++#endif
++#ifdef CONFIG_TMPFS_POSIX_ACL
+       sb->s_flags |= MS_POSIXACL;
+ #endif
+@@ -2501,11 +2707,13 @@ static const struct file_operations shme
+ static const struct inode_operations shmem_inode_operations = {
+       .setattr        = shmem_notify_change,
+       .truncate_range = shmem_truncate_range,
++#ifdef CONFIG_TMPFS_XATTR
++      .setxattr       = shmem_setxattr,
++      .getxattr       = shmem_getxattr,
++      .listxattr      = shmem_listxattr,
++      .removexattr    = shmem_removexattr,
++#endif
+ #ifdef CONFIG_TMPFS_POSIX_ACL
+-      .setxattr       = generic_setxattr,
+-      .getxattr       = generic_getxattr,
+-      .listxattr      = generic_listxattr,
+-      .removexattr    = generic_removexattr,
+       .check_acl      = generic_check_acl,
+ #endif
+@@ -2523,23 +2731,27 @@ static const struct inode_operations shm
+       .mknod          = shmem_mknod,
+       .rename         = shmem_rename,
+ #endif
++#ifdef CONFIG_TMPFS_XATTR
++      .setxattr       = shmem_setxattr,
++      .getxattr       = shmem_getxattr,
++      .listxattr      = shmem_listxattr,
++      .removexattr    = shmem_removexattr,
++#endif
+ #ifdef CONFIG_TMPFS_POSIX_ACL
+       .setattr        = shmem_notify_change,
+-      .setxattr       = generic_setxattr,
+-      .getxattr       = generic_getxattr,
+-      .listxattr      = generic_listxattr,
+-      .removexattr    = generic_removexattr,
+       .check_acl      = generic_check_acl,
+ #endif
+ };
+ static const struct inode_operations shmem_special_inode_operations = {
++#ifdef CONFIG_TMPFS_XATTR
++      .setxattr       = shmem_setxattr,
++      .getxattr       = shmem_getxattr,
++      .listxattr      = shmem_listxattr,
++      .removexattr    = shmem_removexattr,
++#endif
+ #ifdef CONFIG_TMPFS_POSIX_ACL
+       .setattr        = shmem_notify_change,
+-      .setxattr       = generic_setxattr,
+-      .getxattr       = generic_getxattr,
+-      .listxattr      = generic_listxattr,
+-      .removexattr    = generic_removexattr,
+       .check_acl      = generic_check_acl,
+ #endif
+ };
index 26b2da93acc34fe5113a8aa3658344090b74d144..33adb23e692bda6bf647200f6de8595c32d6d8bf 100644 (file)
@@ -1,6 +1,6 @@
 --- a/mm/shmem.c
 +++ b/mm/shmem.c
-@@ -2731,6 +2731,16 @@ out:
+@@ -2943,6 +2943,16 @@ out:
  
  /* common code */
  
@@ -17,7 +17,7 @@
  /**
   * shmem_file_setup - get an unlinked file living in tmpfs
   * @name: name for dentry (to be seen in /proc/<pid>/maps
-@@ -2808,10 +2818,7 @@ int shmem_zero_setup(struct vm_area_stru
+@@ -3020,10 +3030,7 @@ int shmem_zero_setup(struct vm_area_stru
        if (IS_ERR(file))
                return PTR_ERR(file);