diff options
Diffstat (limited to 'mm/shmem.c')
| -rw-r--r-- | mm/shmem.c | 120 |
1 files changed, 105 insertions, 15 deletions
diff --git a/mm/shmem.c b/mm/shmem.c index 84b5cf9b63c5..b96de69f236b 100644 --- a/mm/shmem.c +++ b/mm/shmem.c | |||
| @@ -23,11 +23,11 @@ | |||
| 23 | * which makes it a completely usable filesystem. | 23 | * which makes it a completely usable filesystem. |
| 24 | */ | 24 | */ |
| 25 | 25 | ||
| 26 | #include <linux/config.h> | ||
| 27 | #include <linux/module.h> | 26 | #include <linux/module.h> |
| 28 | #include <linux/init.h> | 27 | #include <linux/init.h> |
| 29 | #include <linux/devfs_fs_kernel.h> | ||
| 30 | #include <linux/fs.h> | 28 | #include <linux/fs.h> |
| 29 | #include <linux/xattr.h> | ||
| 30 | #include <linux/generic_acl.h> | ||
| 31 | #include <linux/mm.h> | 31 | #include <linux/mm.h> |
| 32 | #include <linux/mman.h> | 32 | #include <linux/mman.h> |
| 33 | #include <linux/file.h> | 33 | #include <linux/file.h> |
| @@ -47,6 +47,7 @@ | |||
| 47 | #include <linux/namei.h> | 47 | #include <linux/namei.h> |
| 48 | #include <linux/ctype.h> | 48 | #include <linux/ctype.h> |
| 49 | #include <linux/migrate.h> | 49 | #include <linux/migrate.h> |
| 50 | #include <linux/highmem.h> | ||
| 50 | 51 | ||
| 51 | #include <asm/uaccess.h> | 52 | #include <asm/uaccess.h> |
| 52 | #include <asm/div64.h> | 53 | #include <asm/div64.h> |
| @@ -174,10 +175,11 @@ static inline void shmem_unacct_blocks(unsigned long flags, long pages) | |||
| 174 | } | 175 | } |
| 175 | 176 | ||
| 176 | static struct super_operations shmem_ops; | 177 | static struct super_operations shmem_ops; |
| 177 | static struct address_space_operations shmem_aops; | 178 | static const struct address_space_operations shmem_aops; |
| 178 | static struct file_operations shmem_file_operations; | 179 | static struct file_operations shmem_file_operations; |
| 179 | static struct inode_operations shmem_inode_operations; | 180 | static struct inode_operations shmem_inode_operations; |
| 180 | static struct inode_operations shmem_dir_inode_operations; | 181 | static struct inode_operations shmem_dir_inode_operations; |
| 182 | static struct inode_operations shmem_special_inode_operations; | ||
| 181 | static struct vm_operations_struct shmem_vm_ops; | 183 | static struct vm_operations_struct shmem_vm_ops; |
| 182 | 184 | ||
| 183 | static struct backing_dev_info shmem_backing_dev_info __read_mostly = { | 185 | static struct backing_dev_info shmem_backing_dev_info __read_mostly = { |
| @@ -638,7 +640,7 @@ static int shmem_notify_change(struct dentry *dentry, struct iattr *attr) | |||
| 638 | struct page *page = NULL; | 640 | struct page *page = NULL; |
| 639 | int error; | 641 | int error; |
| 640 | 642 | ||
| 641 | if (attr->ia_valid & ATTR_SIZE) { | 643 | if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) { |
| 642 | if (attr->ia_size < inode->i_size) { | 644 | if (attr->ia_size < inode->i_size) { |
| 643 | /* | 645 | /* |
| 644 | * If truncating down to a partial page, then | 646 | * If truncating down to a partial page, then |
| @@ -671,6 +673,10 @@ static int shmem_notify_change(struct dentry *dentry, struct iattr *attr) | |||
| 671 | error = inode_change_ok(inode, attr); | 673 | error = inode_change_ok(inode, attr); |
| 672 | if (!error) | 674 | if (!error) |
| 673 | error = inode_setattr(inode, attr); | 675 | error = inode_setattr(inode, attr); |
| 676 | #ifdef CONFIG_TMPFS_POSIX_ACL | ||
| 677 | if (!error && (attr->ia_valid & ATTR_MODE)) | ||
| 678 | error = generic_acl_chmod(inode, &shmem_acl_ops); | ||
| 679 | #endif | ||
| 674 | if (page) | 680 | if (page) |
| 675 | page_cache_release(page); | 681 | page_cache_release(page); |
| 676 | return error; | 682 | return error; |
| @@ -1046,12 +1052,12 @@ repeat: | |||
| 1046 | swappage = lookup_swap_cache(swap); | 1052 | swappage = lookup_swap_cache(swap); |
| 1047 | if (!swappage) { | 1053 | if (!swappage) { |
| 1048 | shmem_swp_unmap(entry); | 1054 | shmem_swp_unmap(entry); |
| 1049 | spin_unlock(&info->lock); | ||
| 1050 | /* here we actually do the io */ | 1055 | /* here we actually do the io */ |
| 1051 | if (type && *type == VM_FAULT_MINOR) { | 1056 | if (type && *type == VM_FAULT_MINOR) { |
| 1052 | inc_page_state(pgmajfault); | 1057 | __count_vm_event(PGMAJFAULT); |
| 1053 | *type = VM_FAULT_MAJOR; | 1058 | *type = VM_FAULT_MAJOR; |
| 1054 | } | 1059 | } |
| 1060 | spin_unlock(&info->lock); | ||
| 1055 | swappage = shmem_swapin(info, swap, idx); | 1061 | swappage = shmem_swapin(info, swap, idx); |
| 1056 | if (!swappage) { | 1062 | if (!swappage) { |
| 1057 | spin_lock(&info->lock); | 1063 | spin_lock(&info->lock); |
| @@ -1352,7 +1358,6 @@ shmem_get_inode(struct super_block *sb, int mode, dev_t dev) | |||
| 1352 | inode->i_mode = mode; | 1358 | inode->i_mode = mode; |
| 1353 | inode->i_uid = current->fsuid; | 1359 | inode->i_uid = current->fsuid; |
| 1354 | inode->i_gid = current->fsgid; | 1360 | inode->i_gid = current->fsgid; |
| 1355 | inode->i_blksize = PAGE_CACHE_SIZE; | ||
| 1356 | inode->i_blocks = 0; | 1361 | inode->i_blocks = 0; |
| 1357 | inode->i_mapping->a_ops = &shmem_aops; | 1362 | inode->i_mapping->a_ops = &shmem_aops; |
| 1358 | inode->i_mapping->backing_dev_info = &shmem_backing_dev_info; | 1363 | inode->i_mapping->backing_dev_info = &shmem_backing_dev_info; |
| @@ -1364,6 +1369,7 @@ shmem_get_inode(struct super_block *sb, int mode, dev_t dev) | |||
| 1364 | 1369 | ||
| 1365 | switch (mode & S_IFMT) { | 1370 | switch (mode & S_IFMT) { |
| 1366 | default: | 1371 | default: |
| 1372 | inode->i_op = &shmem_special_inode_operations; | ||
| 1367 | init_special_inode(inode, mode, dev); | 1373 | init_special_inode(inode, mode, dev); |
| 1368 | break; | 1374 | break; |
| 1369 | case S_IFREG: | 1375 | case S_IFREG: |
| @@ -1684,7 +1690,11 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) | |||
| 1684 | iput(inode); | 1690 | iput(inode); |
| 1685 | return error; | 1691 | return error; |
| 1686 | } | 1692 | } |
| 1687 | error = 0; | 1693 | } |
| 1694 | error = shmem_acl_init(inode, dir); | ||
| 1695 | if (error) { | ||
| 1696 | iput(inode); | ||
| 1697 | return error; | ||
| 1688 | } | 1698 | } |
| 1689 | if (dir->i_mode & S_ISGID) { | 1699 | if (dir->i_mode & S_ISGID) { |
| 1690 | inode->i_gid = dir->i_gid; | 1700 | inode->i_gid = dir->i_gid; |
| @@ -1899,6 +1909,53 @@ static struct inode_operations shmem_symlink_inode_operations = { | |||
| 1899 | .put_link = shmem_put_link, | 1909 | .put_link = shmem_put_link, |
| 1900 | }; | 1910 | }; |
| 1901 | 1911 | ||
| 1912 | #ifdef CONFIG_TMPFS_POSIX_ACL | ||
| 1913 | /** | ||
| 1914 | * Superblocks without xattr inode operations will get security.* xattr | ||
| 1915 | * support from the VFS "for free". As soon as we have any other xattrs | ||
| 1916 | * like ACLs, we also need to implement the security.* handlers at | ||
| 1917 | * filesystem level, though. | ||
| 1918 | */ | ||
| 1919 | |||
| 1920 | static size_t shmem_xattr_security_list(struct inode *inode, char *list, | ||
| 1921 | size_t list_len, const char *name, | ||
| 1922 | size_t name_len) | ||
| 1923 | { | ||
| 1924 | return security_inode_listsecurity(inode, list, list_len); | ||
| 1925 | } | ||
| 1926 | |||
| 1927 | static int shmem_xattr_security_get(struct inode *inode, const char *name, | ||
| 1928 | void *buffer, size_t size) | ||
| 1929 | { | ||
| 1930 | if (strcmp(name, "") == 0) | ||
| 1931 | return -EINVAL; | ||
| 1932 | return security_inode_getsecurity(inode, name, buffer, size, | ||
| 1933 | -EOPNOTSUPP); | ||
| 1934 | } | ||
| 1935 | |||
| 1936 | static int shmem_xattr_security_set(struct inode *inode, const char *name, | ||
| 1937 | const void *value, size_t size, int flags) | ||
| 1938 | { | ||
| 1939 | if (strcmp(name, "") == 0) | ||
| 1940 | return -EINVAL; | ||
| 1941 | return security_inode_setsecurity(inode, name, value, size, flags); | ||
| 1942 | } | ||
| 1943 | |||
| 1944 | struct xattr_handler shmem_xattr_security_handler = { | ||
| 1945 | .prefix = XATTR_SECURITY_PREFIX, | ||
| 1946 | .list = shmem_xattr_security_list, | ||
| 1947 | .get = shmem_xattr_security_get, | ||
| 1948 | .set = shmem_xattr_security_set, | ||
| 1949 | }; | ||
| 1950 | |||
| 1951 | static struct xattr_handler *shmem_xattr_handlers[] = { | ||
| 1952 | &shmem_xattr_acl_access_handler, | ||
| 1953 | &shmem_xattr_acl_default_handler, | ||
| 1954 | &shmem_xattr_security_handler, | ||
| 1955 | NULL | ||
| 1956 | }; | ||
| 1957 | #endif | ||
| 1958 | |||
| 1902 | static int shmem_parse_options(char *options, int *mode, uid_t *uid, | 1959 | static int shmem_parse_options(char *options, int *mode, uid_t *uid, |
| 1903 | gid_t *gid, unsigned long *blocks, unsigned long *inodes, | 1960 | gid_t *gid, unsigned long *blocks, unsigned long *inodes, |
| 1904 | int *policy, nodemask_t *policy_nodes) | 1961 | int *policy, nodemask_t *policy_nodes) |
| @@ -2096,6 +2153,10 @@ static int shmem_fill_super(struct super_block *sb, | |||
| 2096 | sb->s_magic = TMPFS_MAGIC; | 2153 | sb->s_magic = TMPFS_MAGIC; |
| 2097 | sb->s_op = &shmem_ops; | 2154 | sb->s_op = &shmem_ops; |
| 2098 | sb->s_time_gran = 1; | 2155 | sb->s_time_gran = 1; |
| 2156 | #ifdef CONFIG_TMPFS_POSIX_ACL | ||
| 2157 | sb->s_xattr = shmem_xattr_handlers; | ||
| 2158 | sb->s_flags |= MS_POSIXACL; | ||
| 2159 | #endif | ||
| 2099 | 2160 | ||
| 2100 | inode = shmem_get_inode(sb, S_IFDIR | mode, 0); | 2161 | inode = shmem_get_inode(sb, S_IFDIR | mode, 0); |
| 2101 | if (!inode) | 2162 | if (!inode) |
| @@ -2132,6 +2193,7 @@ static void shmem_destroy_inode(struct inode *inode) | |||
| 2132 | /* only struct inode is valid if it's an inline symlink */ | 2193 | /* only struct inode is valid if it's an inline symlink */ |
| 2133 | mpol_free_shared_policy(&SHMEM_I(inode)->policy); | 2194 | mpol_free_shared_policy(&SHMEM_I(inode)->policy); |
| 2134 | } | 2195 | } |
| 2196 | shmem_acl_destroy_inode(inode); | ||
| 2135 | kmem_cache_free(shmem_inode_cachep, SHMEM_I(inode)); | 2197 | kmem_cache_free(shmem_inode_cachep, SHMEM_I(inode)); |
| 2136 | } | 2198 | } |
| 2137 | 2199 | ||
| @@ -2143,6 +2205,10 @@ static void init_once(void *foo, struct kmem_cache *cachep, | |||
| 2143 | if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == | 2205 | if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == |
| 2144 | SLAB_CTOR_CONSTRUCTOR) { | 2206 | SLAB_CTOR_CONSTRUCTOR) { |
| 2145 | inode_init_once(&p->vfs_inode); | 2207 | inode_init_once(&p->vfs_inode); |
| 2208 | #ifdef CONFIG_TMPFS_POSIX_ACL | ||
| 2209 | p->i_acl = NULL; | ||
| 2210 | p->i_default_acl = NULL; | ||
| 2211 | #endif | ||
| 2146 | } | 2212 | } |
| 2147 | } | 2213 | } |
| 2148 | 2214 | ||
| @@ -2158,11 +2224,10 @@ static int init_inodecache(void) | |||
| 2158 | 2224 | ||
| 2159 | static void destroy_inodecache(void) | 2225 | static void destroy_inodecache(void) |
| 2160 | { | 2226 | { |
| 2161 | if (kmem_cache_destroy(shmem_inode_cachep)) | 2227 | kmem_cache_destroy(shmem_inode_cachep); |
| 2162 | printk(KERN_INFO "shmem_inode_cache: not all structures were freed\n"); | ||
| 2163 | } | 2228 | } |
| 2164 | 2229 | ||
| 2165 | static struct address_space_operations shmem_aops = { | 2230 | static const struct address_space_operations shmem_aops = { |
| 2166 | .writepage = shmem_writepage, | 2231 | .writepage = shmem_writepage, |
| 2167 | .set_page_dirty = __set_page_dirty_nobuffers, | 2232 | .set_page_dirty = __set_page_dirty_nobuffers, |
| 2168 | #ifdef CONFIG_TMPFS | 2233 | #ifdef CONFIG_TMPFS |
| @@ -2187,6 +2252,14 @@ static struct inode_operations shmem_inode_operations = { | |||
| 2187 | .truncate = shmem_truncate, | 2252 | .truncate = shmem_truncate, |
| 2188 | .setattr = shmem_notify_change, | 2253 | .setattr = shmem_notify_change, |
| 2189 | .truncate_range = shmem_truncate_range, | 2254 | .truncate_range = shmem_truncate_range, |
| 2255 | #ifdef CONFIG_TMPFS_POSIX_ACL | ||
| 2256 | .setxattr = generic_setxattr, | ||
| 2257 | .getxattr = generic_getxattr, | ||
| 2258 | .listxattr = generic_listxattr, | ||
| 2259 | .removexattr = generic_removexattr, | ||
| 2260 | .permission = shmem_permission, | ||
| 2261 | #endif | ||
| 2262 | |||
| 2190 | }; | 2263 | }; |
| 2191 | 2264 | ||
| 2192 | static struct inode_operations shmem_dir_inode_operations = { | 2265 | static struct inode_operations shmem_dir_inode_operations = { |
| @@ -2201,6 +2274,25 @@ static struct inode_operations shmem_dir_inode_operations = { | |||
| 2201 | .mknod = shmem_mknod, | 2274 | .mknod = shmem_mknod, |
| 2202 | .rename = shmem_rename, | 2275 | .rename = shmem_rename, |
| 2203 | #endif | 2276 | #endif |
| 2277 | #ifdef CONFIG_TMPFS_POSIX_ACL | ||
| 2278 | .setattr = shmem_notify_change, | ||
| 2279 | .setxattr = generic_setxattr, | ||
| 2280 | .getxattr = generic_getxattr, | ||
| 2281 | .listxattr = generic_listxattr, | ||
| 2282 | .removexattr = generic_removexattr, | ||
| 2283 | .permission = shmem_permission, | ||
| 2284 | #endif | ||
| 2285 | }; | ||
| 2286 | |||
| 2287 | static struct inode_operations shmem_special_inode_operations = { | ||
| 2288 | #ifdef CONFIG_TMPFS_POSIX_ACL | ||
| 2289 | .setattr = shmem_notify_change, | ||
| 2290 | .setxattr = generic_setxattr, | ||
| 2291 | .getxattr = generic_getxattr, | ||
| 2292 | .listxattr = generic_listxattr, | ||
| 2293 | .removexattr = generic_removexattr, | ||
| 2294 | .permission = shmem_permission, | ||
| 2295 | #endif | ||
| 2204 | }; | 2296 | }; |
| 2205 | 2297 | ||
| 2206 | static struct super_operations shmem_ops = { | 2298 | static struct super_operations shmem_ops = { |
| @@ -2252,10 +2344,8 @@ static int __init init_tmpfs(void) | |||
| 2252 | printk(KERN_ERR "Could not register tmpfs\n"); | 2344 | printk(KERN_ERR "Could not register tmpfs\n"); |
| 2253 | goto out2; | 2345 | goto out2; |
| 2254 | } | 2346 | } |
| 2255 | #ifdef CONFIG_TMPFS | 2347 | |
| 2256 | devfs_mk_dir("shm"); | 2348 | shm_mnt = vfs_kern_mount(&tmpfs_fs_type, MS_NOUSER, |
| 2257 | #endif | ||
| 2258 | shm_mnt = do_kern_mount(tmpfs_fs_type.name, MS_NOUSER, | ||
| 2259 | tmpfs_fs_type.name, NULL); | 2349 | tmpfs_fs_type.name, NULL); |
| 2260 | if (IS_ERR(shm_mnt)) { | 2350 | if (IS_ERR(shm_mnt)) { |
| 2261 | error = PTR_ERR(shm_mnt); | 2351 | error = PTR_ERR(shm_mnt); |
