| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- --- 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
- };
|