diff options
Diffstat (limited to 'tmpfs-implement-generic-xattr-support.patch')
| -rw-r--r-- | tmpfs-implement-generic-xattr-support.patch | 557 |
1 files changed, 557 insertions, 0 deletions
diff --git a/tmpfs-implement-generic-xattr-support.patch b/tmpfs-implement-generic-xattr-support.patch new file mode 100644 index 00000000..9ca97d1c --- /dev/null +++ b/tmpfs-implement-generic-xattr-support.patch @@ -0,0 +1,557 @@ +From linux-fsdevel-owner@vger.kernel.org Fri May 13 07:16:45 2011 +From: Miklos Szeredi <miklos@szeredi.hu> +To: Andrew Morton <akpm@linux-foundation.org>, + Hugh Dickins <hughd@google.com> +Subject: [PATCH v2] tmpfs: implement generic xattr support +CC: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, + Michal Suchanek <hramrach@centrum.cz>, + Serge Hallyn <serge.hallyn@ubuntu.com>, + Eric Paris <eparis@redhat.com>, + James Morris <jmorris@namei.org>, + Christoph Hellwig <hch@infradead.org> +Date: Fri, 13 May 2011 13:16:35 +0200 +Message-ID: <871v02lujw.fsf@tucsk.pomaz.szeredi.hu> +X-Mailing-List: linux-fsdevel@vger.kernel.org + +[This update addresses comments from Hugh and Andrew] + + +From: Eric Paris <eparis@redhat.com> + +This patch implements generic xattrs for tmpfs filesystems. The feodra +project, while trying to replace suid apps with file capabilities, +realized that tmpfs, which is used on the build systems, does not +support file capabilities and thus cannot be used to build packages +which use file capabilities. Xattrs are also needed for overlayfs. + +The xattr interface is a bit, odd. If a filesystem does not implement any +{get,set,list}xattr functions the VFS will call into some random LSM hooks and +the running LSM can then implement some method for handling xattrs. SELinux +for example provides a method to support security.selinux but no other +security.* xattrs. + +As it stands today when one enables CONFIG_TMPFS_POSIX_ACL tmpfs will have +xattr handler routines specifically to handle acls. Because of this tmpfs +would loose the VFS/LSM helpers to support the running LSM. To make up for +that tmpfs had stub functions that did nothing but call into the LSM hooks +which implement the helpers. + +This new patch does not use the LSM fallback functions and instead +just implements a native get/set/list xattr feature for the full +security.* and trusted.* namespace like a normal filesystem. This +means that tmpfs can now support both security.selinux and +security.capability, which was not previously possible. + +The basic implementation is that I attach a: + +struct shmem_xattr { + struct list_head list; /* anchored by shmem_inode_info->xattr_list */ + char *name; + size_t size; + char value[0]; +}; + +Into the struct shmem_inode_info for each xattr that is set. This +implementation could easily support the user.* namespace as well, +except some care needs to be taken to prevent large amounts of +unswappable memory being allocated for unprivileged users. + +[miklos: new config option, suport trusted.*, support symlinks] + +Signed-off-by: Eric Paris <eparis@redhat.com> +Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> +Acked-by: Serge Hallyn <serge.hallyn@ubuntu.com> +--- + fs/Kconfig | 18 ++ + include/linux/shmem_fs.h | 8 + + mm/shmem.c | 320 +++++++++++++++++++++++++++++++++++++++-------- + 3 files changed, 290 insertions(+), 56 deletions(-) + +Index: linux-2.6/fs/Kconfig +=================================================================== +--- linux-2.6.orig/fs/Kconfig 2011-05-13 12:26:31.000000000 +0200 ++++ linux-2.6/fs/Kconfig 2011-05-13 12:29:34.000000000 +0200 +@@ -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 +Index: linux-2.6/include/linux/shmem_fs.h +=================================================================== +--- linux-2.6.orig/include/linux/shmem_fs.h 2011-05-13 12:26:31.000000000 +0200 ++++ linux-2.6/include/linux/shmem_fs.h 2011-05-13 12:35:36.000000000 +0200 +@@ -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; + }; + +Index: linux-2.6/mm/shmem.c +=================================================================== +--- linux-2.6.orig/mm/shmem.c 2011-05-13 12:26:31.000000000 +0200 ++++ linux-2.6/mm/shmem.c 2011-05-13 12:36:08.000000000 +0200 +@@ -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); +@@ -1616,6 +1629,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) { +@@ -2015,9 +2029,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); +@@ -2043,7 +2057,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; + } + +@@ -2067,63 +2081,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) + { +@@ -2403,8 +2607,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 + +@@ -2502,11 +2708,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 + +@@ -2524,23 +2732,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 + }; +-- +To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html + |
