From: Felix Fietkau Date: Tue, 19 Jul 2011 09:00:55 +0000 (+0000) Subject: kernel: backport and enable tmpfs xattr support for 2.6.39 X-Git-Tag: reboot~16223 X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=fee32761f33dbd165a114d2e83eb1b101f4cb4cc;p=openwrt%2Fstaging%2Fxback.git kernel: backport and enable tmpfs xattr support for 2.6.39 SVN-Revision: 27691 --- diff --git a/target/linux/generic/config-2.6.39 b/target/linux/generic/config-2.6.39 index a322f4763e..b98814ab4b 100644 --- a/target/linux/generic/config-2.6.39 +++ b/target/linux/generic/config-2.6.39 @@ -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 index 0000000000..e5d8d43ca1 --- /dev/null +++ b/target/linux/generic/patches-2.6.39/040-backport_tmpfs_xattr.patch @@ -0,0 +1,477 @@ +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -121,9 +121,25 @@ config TMPFS + + See 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 ++ 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 + }; diff --git a/target/linux/generic/patches-2.6.39/950-vm_exports.patch b/target/linux/generic/patches-2.6.39/950-vm_exports.patch index 26b2da93ac..33adb23e69 100644 --- a/target/linux/generic/patches-2.6.39/950-vm_exports.patch +++ b/target/linux/generic/patches-2.6.39/950-vm_exports.patch @@ -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//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);