aboutsummaryrefslogtreecommitdiffstats
path: root/mm/shmem.c
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2011-05-24 20:12:39 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-05-25 11:39:31 -0400
commitb09e0fa4b4ea66266058eead43350bd7d55fec67 (patch)
tree35eed2e1bc39bbbca1c041b69e1e12e826241ccf /mm/shmem.c
parent4eb317072be81bd93906f768679f745bc574e6b7 (diff)
tmpfs: implement generic xattr support
Implement 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. [mszeredi@suse.cz: 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> Tested-by: Serge Hallyn <serge.hallyn@ubuntu.com> Cc: Kyle McMartin <kyle@mcmartin.ca> Acked-by: Hugh Dickins <hughd@google.com> Tested-by: Jordi Pujol <jordipujolp@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/shmem.c')
-rw-r--r--mm/shmem.c320
1 files changed, 266 insertions, 54 deletions
diff --git a/mm/shmem.c b/mm/shmem.c
index ba4ad28b7db6..69edb45a9f28 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -99,6 +99,13 @@ static struct vfsmount *shm_mnt;
99/* Pretend that each entry is of this size in directory's i_size */ 99/* Pretend that each entry is of this size in directory's i_size */
100#define BOGO_DIRENT_SIZE 20 100#define BOGO_DIRENT_SIZE 20
101 101
102struct shmem_xattr {
103 struct list_head list; /* anchored by shmem_inode_info->xattr_list */
104 char *name; /* xattr name */
105 size_t size;
106 char value[0];
107};
108
102/* Flag allocation requirements to shmem_getpage and shmem_swp_alloc */ 109/* Flag allocation requirements to shmem_getpage and shmem_swp_alloc */
103enum sgp_type { 110enum sgp_type {
104 SGP_READ, /* don't exceed i_size, don't allocate page */ 111 SGP_READ, /* don't exceed i_size, don't allocate page */
@@ -822,6 +829,7 @@ static int shmem_notify_change(struct dentry *dentry, struct iattr *attr)
822static void shmem_evict_inode(struct inode *inode) 829static void shmem_evict_inode(struct inode *inode)
823{ 830{
824 struct shmem_inode_info *info = SHMEM_I(inode); 831 struct shmem_inode_info *info = SHMEM_I(inode);
832 struct shmem_xattr *xattr, *nxattr;
825 833
826 if (inode->i_mapping->a_ops == &shmem_aops) { 834 if (inode->i_mapping->a_ops == &shmem_aops) {
827 truncate_inode_pages(inode->i_mapping, 0); 835 truncate_inode_pages(inode->i_mapping, 0);
@@ -834,6 +842,11 @@ static void shmem_evict_inode(struct inode *inode)
834 mutex_unlock(&shmem_swaplist_mutex); 842 mutex_unlock(&shmem_swaplist_mutex);
835 } 843 }
836 } 844 }
845
846 list_for_each_entry_safe(xattr, nxattr, &info->xattr_list, list) {
847 kfree(xattr->name);
848 kfree(xattr);
849 }
837 BUG_ON(inode->i_blocks); 850 BUG_ON(inode->i_blocks);
838 shmem_free_inode(inode->i_sb); 851 shmem_free_inode(inode->i_sb);
839 end_writeback(inode); 852 end_writeback(inode);
@@ -1615,6 +1628,7 @@ static struct inode *shmem_get_inode(struct super_block *sb, const struct inode
1615 spin_lock_init(&info->lock); 1628 spin_lock_init(&info->lock);
1616 info->flags = flags & VM_NORESERVE; 1629 info->flags = flags & VM_NORESERVE;
1617 INIT_LIST_HEAD(&info->swaplist); 1630 INIT_LIST_HEAD(&info->swaplist);
1631 INIT_LIST_HEAD(&info->xattr_list);
1618 cache_no_acl(inode); 1632 cache_no_acl(inode);
1619 1633
1620 switch (mode & S_IFMT) { 1634 switch (mode & S_IFMT) {
@@ -2014,9 +2028,9 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
2014 2028
2015 info = SHMEM_I(inode); 2029 info = SHMEM_I(inode);
2016 inode->i_size = len-1; 2030 inode->i_size = len-1;
2017 if (len <= (char *)inode - (char *)info) { 2031 if (len <= SHMEM_SYMLINK_INLINE_LEN) {
2018 /* do it inline */ 2032 /* do it inline */
2019 memcpy(info, symname, len); 2033 memcpy(info->inline_symlink, symname, len);
2020 inode->i_op = &shmem_symlink_inline_operations; 2034 inode->i_op = &shmem_symlink_inline_operations;
2021 } else { 2035 } else {
2022 error = shmem_getpage(inode, 0, &page, SGP_WRITE, NULL); 2036 error = shmem_getpage(inode, 0, &page, SGP_WRITE, NULL);
@@ -2042,7 +2056,7 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
2042 2056
2043static void *shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd) 2057static void *shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd)
2044{ 2058{
2045 nd_set_link(nd, (char *)SHMEM_I(dentry->d_inode)); 2059 nd_set_link(nd, SHMEM_I(dentry->d_inode)->inline_symlink);
2046 return NULL; 2060 return NULL;
2047} 2061}
2048 2062
@@ -2066,63 +2080,253 @@ static void shmem_put_link(struct dentry *dentry, struct nameidata *nd, void *co
2066 } 2080 }
2067} 2081}
2068 2082
2069static const struct inode_operations shmem_symlink_inline_operations = { 2083#ifdef CONFIG_TMPFS_XATTR
2070 .readlink = generic_readlink,
2071 .follow_link = shmem_follow_link_inline,
2072};
2073
2074static const struct inode_operations shmem_symlink_inode_operations = {
2075 .readlink = generic_readlink,
2076 .follow_link = shmem_follow_link,
2077 .put_link = shmem_put_link,
2078};
2079
2080#ifdef CONFIG_TMPFS_POSIX_ACL
2081/* 2084/*
2082 * Superblocks without xattr inode operations will get security.* xattr 2085 * Superblocks without xattr inode operations may get some security.* xattr
2083 * support from the VFS "for free". As soon as we have any other xattrs 2086 * support from the LSM "for free". As soon as we have any other xattrs
2084 * like ACLs, we also need to implement the security.* handlers at 2087 * like ACLs, we also need to implement the security.* handlers at
2085 * filesystem level, though. 2088 * filesystem level, though.
2086 */ 2089 */
2087 2090
2088static size_t shmem_xattr_security_list(struct dentry *dentry, char *list, 2091static int shmem_xattr_get(struct dentry *dentry, const char *name,
2089 size_t list_len, const char *name, 2092 void *buffer, size_t size)
2090 size_t name_len, int handler_flags)
2091{ 2093{
2092 return security_inode_listsecurity(dentry->d_inode, list, list_len); 2094 struct shmem_inode_info *info;
2093} 2095 struct shmem_xattr *xattr;
2096 int ret = -ENODATA;
2094 2097
2095static int shmem_xattr_security_get(struct dentry *dentry, const char *name, 2098 info = SHMEM_I(dentry->d_inode);
2096 void *buffer, size_t size, int handler_flags) 2099
2097{ 2100 spin_lock(&info->lock);
2098 if (strcmp(name, "") == 0) 2101 list_for_each_entry(xattr, &info->xattr_list, list) {
2099 return -EINVAL; 2102 if (strcmp(name, xattr->name))
2100 return xattr_getsecurity(dentry->d_inode, name, buffer, size); 2103 continue;
2104
2105 ret = xattr->size;
2106 if (buffer) {
2107 if (size < xattr->size)
2108 ret = -ERANGE;
2109 else
2110 memcpy(buffer, xattr->value, xattr->size);
2111 }
2112 break;
2113 }
2114 spin_unlock(&info->lock);
2115 return ret;
2101} 2116}
2102 2117
2103static int shmem_xattr_security_set(struct dentry *dentry, const char *name, 2118static int shmem_xattr_set(struct dentry *dentry, const char *name,
2104 const void *value, size_t size, int flags, int handler_flags) 2119 const void *value, size_t size, int flags)
2105{ 2120{
2106 if (strcmp(name, "") == 0) 2121 struct inode *inode = dentry->d_inode;
2107 return -EINVAL; 2122 struct shmem_inode_info *info = SHMEM_I(inode);
2108 return security_inode_setsecurity(dentry->d_inode, name, value, 2123 struct shmem_xattr *xattr;
2109 size, flags); 2124 struct shmem_xattr *new_xattr = NULL;
2125 size_t len;
2126 int err = 0;
2127
2128 /* value == NULL means remove */
2129 if (value) {
2130 /* wrap around? */
2131 len = sizeof(*new_xattr) + size;
2132 if (len <= sizeof(*new_xattr))
2133 return -ENOMEM;
2134
2135 new_xattr = kmalloc(len, GFP_KERNEL);
2136 if (!new_xattr)
2137 return -ENOMEM;
2138
2139 new_xattr->name = kstrdup(name, GFP_KERNEL);
2140 if (!new_xattr->name) {
2141 kfree(new_xattr);
2142 return -ENOMEM;
2143 }
2144
2145 new_xattr->size = size;
2146 memcpy(new_xattr->value, value, size);
2147 }
2148
2149 spin_lock(&info->lock);
2150 list_for_each_entry(xattr, &info->xattr_list, list) {
2151 if (!strcmp(name, xattr->name)) {
2152 if (flags & XATTR_CREATE) {
2153 xattr = new_xattr;
2154 err = -EEXIST;
2155 } else if (new_xattr) {
2156 list_replace(&xattr->list, &new_xattr->list);
2157 } else {
2158 list_del(&xattr->list);
2159 }
2160 goto out;
2161 }
2162 }
2163 if (flags & XATTR_REPLACE) {
2164 xattr = new_xattr;
2165 err = -ENODATA;
2166 } else {
2167 list_add(&new_xattr->list, &info->xattr_list);
2168 xattr = NULL;
2169 }
2170out:
2171 spin_unlock(&info->lock);
2172 if (xattr)
2173 kfree(xattr->name);
2174 kfree(xattr);
2175 return err;
2110} 2176}
2111 2177
2112static const struct xattr_handler shmem_xattr_security_handler = {
2113 .prefix = XATTR_SECURITY_PREFIX,
2114 .list = shmem_xattr_security_list,
2115 .get = shmem_xattr_security_get,
2116 .set = shmem_xattr_security_set,
2117};
2118 2178
2119static const struct xattr_handler *shmem_xattr_handlers[] = { 2179static const struct xattr_handler *shmem_xattr_handlers[] = {
2180#ifdef CONFIG_TMPFS_POSIX_ACL
2120 &generic_acl_access_handler, 2181 &generic_acl_access_handler,
2121 &generic_acl_default_handler, 2182 &generic_acl_default_handler,
2122 &shmem_xattr_security_handler, 2183#endif
2123 NULL 2184 NULL
2124}; 2185};
2186
2187static int shmem_xattr_validate(const char *name)
2188{
2189 struct { const char *prefix; size_t len; } arr[] = {
2190 { XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN },
2191 { XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN }
2192 };
2193 int i;
2194
2195 for (i = 0; i < ARRAY_SIZE(arr); i++) {
2196 size_t preflen = arr[i].len;
2197 if (strncmp(name, arr[i].prefix, preflen) == 0) {
2198 if (!name[preflen])
2199 return -EINVAL;
2200 return 0;
2201 }
2202 }
2203 return -EOPNOTSUPP;
2204}
2205
2206static ssize_t shmem_getxattr(struct dentry *dentry, const char *name,
2207 void *buffer, size_t size)
2208{
2209 int err;
2210
2211 /*
2212 * If this is a request for a synthetic attribute in the system.*
2213 * namespace use the generic infrastructure to resolve a handler
2214 * for it via sb->s_xattr.
2215 */
2216 if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
2217 return generic_getxattr(dentry, name, buffer, size);
2218
2219 err = shmem_xattr_validate(name);
2220 if (err)
2221 return err;
2222
2223 return shmem_xattr_get(dentry, name, buffer, size);
2224}
2225
2226static int shmem_setxattr(struct dentry *dentry, const char *name,
2227 const void *value, size_t size, int flags)
2228{
2229 int err;
2230
2231 /*
2232 * If this is a request for a synthetic attribute in the system.*
2233 * namespace use the generic infrastructure to resolve a handler
2234 * for it via sb->s_xattr.
2235 */
2236 if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
2237 return generic_setxattr(dentry, name, value, size, flags);
2238
2239 err = shmem_xattr_validate(name);
2240 if (err)
2241 return err;
2242
2243 if (size == 0)
2244 value = ""; /* empty EA, do not remove */
2245
2246 return shmem_xattr_set(dentry, name, value, size, flags);
2247
2248}
2249
2250static int shmem_removexattr(struct dentry *dentry, const char *name)
2251{
2252 int err;
2253
2254 /*
2255 * If this is a request for a synthetic attribute in the system.*
2256 * namespace use the generic infrastructure to resolve a handler
2257 * for it via sb->s_xattr.
2258 */
2259 if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
2260 return generic_removexattr(dentry, name);
2261
2262 err = shmem_xattr_validate(name);
2263 if (err)
2264 return err;
2265
2266 return shmem_xattr_set(dentry, name, NULL, 0, XATTR_REPLACE);
2267}
2268
2269static bool xattr_is_trusted(const char *name)
2270{
2271 return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN);
2272}
2273
2274static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size)
2275{
2276 bool trusted = capable(CAP_SYS_ADMIN);
2277 struct shmem_xattr *xattr;
2278 struct shmem_inode_info *info;
2279 size_t used = 0;
2280
2281 info = SHMEM_I(dentry->d_inode);
2282
2283 spin_lock(&info->lock);
2284 list_for_each_entry(xattr, &info->xattr_list, list) {
2285 size_t len;
2286
2287 /* skip "trusted." attributes for unprivileged callers */
2288 if (!trusted && xattr_is_trusted(xattr->name))
2289 continue;
2290
2291 len = strlen(xattr->name) + 1;
2292 used += len;
2293 if (buffer) {
2294 if (size < used) {
2295 used = -ERANGE;
2296 break;
2297 }
2298 memcpy(buffer, xattr->name, len);
2299 buffer += len;
2300 }
2301 }
2302 spin_unlock(&info->lock);
2303
2304 return used;
2305}
2306#endif /* CONFIG_TMPFS_XATTR */
2307
2308static const struct inode_operations shmem_symlink_inline_operations = {
2309 .readlink = generic_readlink,
2310 .follow_link = shmem_follow_link_inline,
2311#ifdef CONFIG_TMPFS_XATTR
2312 .setxattr = shmem_setxattr,
2313 .getxattr = shmem_getxattr,
2314 .listxattr = shmem_listxattr,
2315 .removexattr = shmem_removexattr,
2316#endif
2317};
2318
2319static const struct inode_operations shmem_symlink_inode_operations = {
2320 .readlink = generic_readlink,
2321 .follow_link = shmem_follow_link,
2322 .put_link = shmem_put_link,
2323#ifdef CONFIG_TMPFS_XATTR
2324 .setxattr = shmem_setxattr,
2325 .getxattr = shmem_getxattr,
2326 .listxattr = shmem_listxattr,
2327 .removexattr = shmem_removexattr,
2125#endif 2328#endif
2329};
2126 2330
2127static struct dentry *shmem_get_parent(struct dentry *child) 2331static struct dentry *shmem_get_parent(struct dentry *child)
2128{ 2332{
@@ -2402,8 +2606,10 @@ int shmem_fill_super(struct super_block *sb, void *data, int silent)
2402 sb->s_magic = TMPFS_MAGIC; 2606 sb->s_magic = TMPFS_MAGIC;
2403 sb->s_op = &shmem_ops; 2607 sb->s_op = &shmem_ops;
2404 sb->s_time_gran = 1; 2608 sb->s_time_gran = 1;
2405#ifdef CONFIG_TMPFS_POSIX_ACL 2609#ifdef CONFIG_TMPFS_XATTR
2406 sb->s_xattr = shmem_xattr_handlers; 2610 sb->s_xattr = shmem_xattr_handlers;
2611#endif
2612#ifdef CONFIG_TMPFS_POSIX_ACL
2407 sb->s_flags |= MS_POSIXACL; 2613 sb->s_flags |= MS_POSIXACL;
2408#endif 2614#endif
2409 2615
@@ -2501,11 +2707,13 @@ static const struct file_operations shmem_file_operations = {
2501static const struct inode_operations shmem_inode_operations = { 2707static const struct inode_operations shmem_inode_operations = {
2502 .setattr = shmem_notify_change, 2708 .setattr = shmem_notify_change,
2503 .truncate_range = shmem_truncate_range, 2709 .truncate_range = shmem_truncate_range,
2710#ifdef CONFIG_TMPFS_XATTR
2711 .setxattr = shmem_setxattr,
2712 .getxattr = shmem_getxattr,
2713 .listxattr = shmem_listxattr,
2714 .removexattr = shmem_removexattr,
2715#endif
2504#ifdef CONFIG_TMPFS_POSIX_ACL 2716#ifdef CONFIG_TMPFS_POSIX_ACL
2505 .setxattr = generic_setxattr,
2506 .getxattr = generic_getxattr,
2507 .listxattr = generic_listxattr,
2508 .removexattr = generic_removexattr,
2509 .check_acl = generic_check_acl, 2717 .check_acl = generic_check_acl,
2510#endif 2718#endif
2511 2719
@@ -2523,23 +2731,27 @@ static const struct inode_operations shmem_dir_inode_operations = {
2523 .mknod = shmem_mknod, 2731 .mknod = shmem_mknod,
2524 .rename = shmem_rename, 2732 .rename = shmem_rename,
2525#endif 2733#endif
2734#ifdef CONFIG_TMPFS_XATTR
2735 .setxattr = shmem_setxattr,
2736 .getxattr = shmem_getxattr,
2737 .listxattr = shmem_listxattr,
2738 .removexattr = shmem_removexattr,
2739#endif
2526#ifdef CONFIG_TMPFS_POSIX_ACL 2740#ifdef CONFIG_TMPFS_POSIX_ACL
2527 .setattr = shmem_notify_change, 2741 .setattr = shmem_notify_change,
2528 .setxattr = generic_setxattr,
2529 .getxattr = generic_getxattr,
2530 .listxattr = generic_listxattr,
2531 .removexattr = generic_removexattr,
2532 .check_acl = generic_check_acl, 2742 .check_acl = generic_check_acl,
2533#endif 2743#endif
2534}; 2744};
2535 2745
2536static const struct inode_operations shmem_special_inode_operations = { 2746static const struct inode_operations shmem_special_inode_operations = {
2747#ifdef CONFIG_TMPFS_XATTR
2748 .setxattr = shmem_setxattr,
2749 .getxattr = shmem_getxattr,
2750 .listxattr = shmem_listxattr,
2751 .removexattr = shmem_removexattr,
2752#endif
2537#ifdef CONFIG_TMPFS_POSIX_ACL 2753#ifdef CONFIG_TMPFS_POSIX_ACL
2538 .setattr = shmem_notify_change, 2754 .setattr = shmem_notify_change,
2539 .setxattr = generic_setxattr,
2540 .getxattr = generic_getxattr,
2541 .listxattr = generic_listxattr,
2542 .removexattr = generic_removexattr,
2543 .check_acl = generic_check_acl, 2755 .check_acl = generic_check_acl,
2544#endif 2756#endif
2545}; 2757};