[PATCH] Access Control Lists for tmpfs
[linux-2.6.git] / mm / shmem.c
index 1e43c8a..b96de69 100644 (file)
  * which makes it a completely usable filesystem.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
-#include <linux/devfs_fs_kernel.h>
 #include <linux/fs.h>
+#include <linux/xattr.h>
+#include <linux/generic_acl.h>
 #include <linux/mm.h>
 #include <linux/mman.h>
 #include <linux/file.h>
@@ -47,6 +47,7 @@
 #include <linux/namei.h>
 #include <linux/ctype.h>
 #include <linux/migrate.h>
+#include <linux/highmem.h>
 
 #include <asm/uaccess.h>
 #include <asm/div64.h>
@@ -174,10 +175,11 @@ static inline void shmem_unacct_blocks(unsigned long flags, long pages)
 }
 
 static struct super_operations shmem_ops;
-static struct address_space_operations shmem_aops;
+static const struct address_space_operations shmem_aops;
 static struct file_operations shmem_file_operations;
 static struct inode_operations shmem_inode_operations;
 static struct inode_operations shmem_dir_inode_operations;
+static struct inode_operations shmem_special_inode_operations;
 static struct vm_operations_struct shmem_vm_ops;
 
 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)
        struct page *page = NULL;
        int error;
 
-       if (attr->ia_valid & ATTR_SIZE) {
+       if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
                if (attr->ia_size < inode->i_size) {
                        /*
                         * If truncating down to a partial page, then
@@ -671,6 +673,10 @@ static int shmem_notify_change(struct dentry *dentry, struct iattr *attr)
        error = inode_change_ok(inode, attr);
        if (!error)
                error = inode_setattr(inode, attr);
+#ifdef CONFIG_TMPFS_POSIX_ACL
+       if (!error && (attr->ia_valid & ATTR_MODE))
+               error = generic_acl_chmod(inode, &shmem_acl_ops);
+#endif
        if (page)
                page_cache_release(page);
        return error;
@@ -1046,12 +1052,12 @@ repeat:
                swappage = lookup_swap_cache(swap);
                if (!swappage) {
                        shmem_swp_unmap(entry);
-                       spin_unlock(&info->lock);
                        /* here we actually do the io */
                        if (type && *type == VM_FAULT_MINOR) {
-                               inc_page_state(pgmajfault);
+                               __count_vm_event(PGMAJFAULT);
                                *type = VM_FAULT_MAJOR;
                        }
+                       spin_unlock(&info->lock);
                        swappage = shmem_swapin(info, swap, idx);
                        if (!swappage) {
                                spin_lock(&info->lock);
@@ -1081,14 +1087,6 @@ repeat:
                        page_cache_release(swappage);
                        goto repeat;
                }
-               if (!PageSwapCache(swappage)) {
-                       /* Page migration has occured */
-                       shmem_swp_unmap(entry);
-                       spin_unlock(&info->lock);
-                       unlock_page(swappage);
-                       page_cache_release(swappage);
-                       goto repeat;
-               }
                if (PageWriteback(swappage)) {
                        shmem_swp_unmap(entry);
                        spin_unlock(&info->lock);
@@ -1360,7 +1358,6 @@ shmem_get_inode(struct super_block *sb, int mode, dev_t dev)
                inode->i_mode = mode;
                inode->i_uid = current->fsuid;
                inode->i_gid = current->fsgid;
-               inode->i_blksize = PAGE_CACHE_SIZE;
                inode->i_blocks = 0;
                inode->i_mapping->a_ops = &shmem_aops;
                inode->i_mapping->backing_dev_info = &shmem_backing_dev_info;
@@ -1372,6 +1369,7 @@ shmem_get_inode(struct super_block *sb, int mode, dev_t dev)
 
                switch (mode & S_IFMT) {
                default:
+                       inode->i_op = &shmem_special_inode_operations;
                        init_special_inode(inode, mode, dev);
                        break;
                case S_IFREG:
@@ -1654,9 +1652,9 @@ static ssize_t shmem_file_sendfile(struct file *in_file, loff_t *ppos,
        return desc.error;
 }
 
-static int shmem_statfs(struct super_block *sb, struct kstatfs *buf)
+static int shmem_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
-       struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
+       struct shmem_sb_info *sbinfo = SHMEM_SB(dentry->d_sb);
 
        buf->f_type = TMPFS_MAGIC;
        buf->f_bsize = PAGE_CACHE_SIZE;
@@ -1692,7 +1690,11 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
                                iput(inode);
                                return error;
                        }
-                       error = 0;
+               }
+               error = shmem_acl_init(inode, dir);
+               if (error) {
+                       iput(inode);
+                       return error;
                }
                if (dir->i_mode & S_ISGID) {
                        inode->i_gid = dir->i_gid;
@@ -1907,6 +1909,53 @@ static struct inode_operations shmem_symlink_inode_operations = {
        .put_link       = shmem_put_link,
 };
 
+#ifdef CONFIG_TMPFS_POSIX_ACL
+/**
+ * Superblocks without xattr inode operations will get security.* xattr
+ * support from the VFS "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 inode *inode, char *list,
+                                       size_t list_len, const char *name,
+                                       size_t name_len)
+{
+       return security_inode_listsecurity(inode, list, list_len);
+}
+
+static int shmem_xattr_security_get(struct inode *inode, const char *name,
+                                   void *buffer, size_t size)
+{
+       if (strcmp(name, "") == 0)
+               return -EINVAL;
+       return security_inode_getsecurity(inode, name, buffer, size,
+                                         -EOPNOTSUPP);
+}
+
+static int shmem_xattr_security_set(struct inode *inode, const char *name,
+                                   const void *value, size_t size, int flags)
+{
+       if (strcmp(name, "") == 0)
+               return -EINVAL;
+       return security_inode_setsecurity(inode, name, value, size, flags);
+}
+
+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 struct xattr_handler *shmem_xattr_handlers[] = {
+       &shmem_xattr_acl_access_handler,
+       &shmem_xattr_acl_default_handler,
+       &shmem_xattr_security_handler,
+       NULL
+};
+#endif
+
 static int shmem_parse_options(char *options, int *mode, uid_t *uid,
        gid_t *gid, unsigned long *blocks, unsigned long *inodes,
        int *policy, nodemask_t *policy_nodes)
@@ -2104,6 +2153,10 @@ static int shmem_fill_super(struct super_block *sb,
        sb->s_magic = TMPFS_MAGIC;
        sb->s_op = &shmem_ops;
        sb->s_time_gran = 1;
+#ifdef CONFIG_TMPFS_POSIX_ACL
+       sb->s_xattr = shmem_xattr_handlers;
+       sb->s_flags |= MS_POSIXACL;
+#endif
 
        inode = shmem_get_inode(sb, S_IFDIR | mode, 0);
        if (!inode)
@@ -2140,6 +2193,7 @@ static void shmem_destroy_inode(struct inode *inode)
                /* only struct inode is valid if it's an inline symlink */
                mpol_free_shared_policy(&SHMEM_I(inode)->policy);
        }
+       shmem_acl_destroy_inode(inode);
        kmem_cache_free(shmem_inode_cachep, SHMEM_I(inode));
 }
 
@@ -2151,6 +2205,10 @@ static void init_once(void *foo, struct kmem_cache *cachep,
        if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
            SLAB_CTOR_CONSTRUCTOR) {
                inode_init_once(&p->vfs_inode);
+#ifdef CONFIG_TMPFS_POSIX_ACL
+               p->i_acl = NULL;
+               p->i_default_acl = NULL;
+#endif
        }
 }
 
@@ -2166,11 +2224,10 @@ static int init_inodecache(void)
 
 static void destroy_inodecache(void)
 {
-       if (kmem_cache_destroy(shmem_inode_cachep))
-               printk(KERN_INFO "shmem_inode_cache: not all structures were freed\n");
+       kmem_cache_destroy(shmem_inode_cachep);
 }
 
-static struct address_space_operations shmem_aops = {
+static const struct address_space_operations shmem_aops = {
        .writepage      = shmem_writepage,
        .set_page_dirty = __set_page_dirty_nobuffers,
 #ifdef CONFIG_TMPFS
@@ -2195,6 +2252,14 @@ static struct inode_operations shmem_inode_operations = {
        .truncate       = shmem_truncate,
        .setattr        = shmem_notify_change,
        .truncate_range = shmem_truncate_range,
+#ifdef CONFIG_TMPFS_POSIX_ACL
+       .setxattr       = generic_setxattr,
+       .getxattr       = generic_getxattr,
+       .listxattr      = generic_listxattr,
+       .removexattr    = generic_removexattr,
+       .permission     = shmem_permission,
+#endif
+
 };
 
 static struct inode_operations shmem_dir_inode_operations = {
@@ -2209,6 +2274,25 @@ static struct inode_operations shmem_dir_inode_operations = {
        .mknod          = shmem_mknod,
        .rename         = shmem_rename,
 #endif
+#ifdef CONFIG_TMPFS_POSIX_ACL
+       .setattr        = shmem_notify_change,
+       .setxattr       = generic_setxattr,
+       .getxattr       = generic_getxattr,
+       .listxattr      = generic_listxattr,
+       .removexattr    = generic_removexattr,
+       .permission     = shmem_permission,
+#endif
+};
+
+static struct inode_operations shmem_special_inode_operations = {
+#ifdef CONFIG_TMPFS_POSIX_ACL
+       .setattr        = shmem_notify_change,
+       .setxattr       = generic_setxattr,
+       .getxattr       = generic_getxattr,
+       .listxattr      = generic_listxattr,
+       .removexattr    = generic_removexattr,
+       .permission     = shmem_permission,
+#endif
 };
 
 static struct super_operations shmem_ops = {
@@ -2233,10 +2317,10 @@ static struct vm_operations_struct shmem_vm_ops = {
 };
 
 
-static struct super_block *shmem_get_sb(struct file_system_type *fs_type,
-       int flags, const char *dev_name, void *data)
+static int shmem_get_sb(struct file_system_type *fs_type,
+       int flags, const char *dev_name, void *data, struct vfsmount *mnt)
 {
-       return get_sb_nodev(fs_type, flags, data, shmem_fill_super);
+       return get_sb_nodev(fs_type, flags, data, shmem_fill_super, mnt);
 }
 
 static struct file_system_type tmpfs_fs_type = {
@@ -2260,10 +2344,8 @@ static int __init init_tmpfs(void)
                printk(KERN_ERR "Could not register tmpfs\n");
                goto out2;
        }
-#ifdef CONFIG_TMPFS
-       devfs_mk_dir("shm");
-#endif
-       shm_mnt = do_kern_mount(tmpfs_fs_type.name, MS_NOUSER,
+
+       shm_mnt = vfs_kern_mount(&tmpfs_fs_type, MS_NOUSER,
                                tmpfs_fs_type.name, NULL);
        if (IS_ERR(shm_mnt)) {
                error = PTR_ERR(shm_mnt);