diff options
Diffstat (limited to 'mm/shmem.c')
-rw-r--r-- | mm/shmem.c | 320 |
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 | ||
102 | struct 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 */ |
103 | enum sgp_type { | 110 | enum 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) | |||
822 | static void shmem_evict_inode(struct inode *inode) | 829 | static 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 | ||
2043 | static void *shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd) | 2057 | static 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 | ||
2069 | static 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 | |||
2074 | static 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 | ||
2088 | static size_t shmem_xattr_security_list(struct dentry *dentry, char *list, | 2091 | static 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 | ||
2095 | static 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 | ||
2103 | static int shmem_xattr_security_set(struct dentry *dentry, const char *name, | 2118 | static 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 | } | ||
2170 | out: | ||
2171 | spin_unlock(&info->lock); | ||
2172 | if (xattr) | ||
2173 | kfree(xattr->name); | ||
2174 | kfree(xattr); | ||
2175 | return err; | ||
2110 | } | 2176 | } |
2111 | 2177 | ||
2112 | static 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 | ||
2119 | static const struct xattr_handler *shmem_xattr_handlers[] = { | 2179 | static 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 | |||
2187 | static 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 | |||
2206 | static 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 | |||
2226 | static 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 | |||
2250 | static 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 | |||
2269 | static bool xattr_is_trusted(const char *name) | ||
2270 | { | ||
2271 | return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN); | ||
2272 | } | ||
2273 | |||
2274 | static 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 | |||
2308 | static 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 | |||
2319 | static 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 | ||
2127 | static struct dentry *shmem_get_parent(struct dentry *child) | 2331 | static 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 = { | |||
2501 | static const struct inode_operations shmem_inode_operations = { | 2707 | static 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 | ||
2536 | static const struct inode_operations shmem_special_inode_operations = { | 2746 | static 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 | }; |