[Bluetooth] Handle device registration failures
[pandora-kernel.git] / mm / shmem.c
index eda907c..70da7a0 100644 (file)
@@ -26,6 +26,8 @@
 #include <linux/module.h>
 #include <linux/init.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>
@@ -46,6 +48,7 @@
 #include <linux/ctype.h>
 #include <linux/migrate.h>
 #include <linux/highmem.h>
+#include <linux/backing-dev.h>
 
 #include <asm/uaccess.h>
 #include <asm/div64.h>
@@ -174,9 +177,10 @@ static inline void shmem_unacct_blocks(unsigned long flags, long pages)
 
 static struct super_operations shmem_ops;
 static const struct address_space_operations shmem_aops;
-static struct file_operations shmem_file_operations;
+static const 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 = {
@@ -511,7 +515,12 @@ static void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end)
                        size = SHMEM_NR_DIRECT;
                nr_swaps_freed = shmem_free_swp(ptr+idx, ptr+size);
        }
-       if (!topdir)
+
+       /*
+        * If there are no indirect blocks or we are punching a hole
+        * below indirect blocks, nothing to be done.
+        */
+       if (!topdir || (punch_hole && (limit <= SHMEM_NR_DIRECT)))
                goto done2;
 
        BUG_ON(limit <= SHMEM_NR_DIRECT);
@@ -637,7 +646,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
@@ -670,6 +679,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;
@@ -1124,7 +1137,7 @@ repeat:
                        page_cache_release(swappage);
                        if (error == -ENOMEM) {
                                /* let kswapd refresh zone for GFP_ATOMICs */
-                               blk_congestion_wait(WRITE, HZ/50);
+                               congestion_wait(WRITE, HZ/50);
                        }
                        goto repeat;
                }
@@ -1217,7 +1230,7 @@ failed:
 
 struct page *shmem_nopage(struct vm_area_struct *vma, unsigned long address, int *type)
 {
-       struct inode *inode = vma->vm_file->f_dentry->d_inode;
+       struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
        struct page *page = NULL;
        unsigned long idx;
        int error;
@@ -1240,7 +1253,7 @@ static int shmem_populate(struct vm_area_struct *vma,
        unsigned long addr, unsigned long len,
        pgprot_t prot, unsigned long pgoff, int nonblock)
 {
-       struct inode *inode = vma->vm_file->f_dentry->d_inode;
+       struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
        struct mm_struct *mm = vma->vm_mm;
        enum sgp_type sgp = nonblock? SGP_QUICK: SGP_CACHE;
        unsigned long size;
@@ -1285,14 +1298,14 @@ static int shmem_populate(struct vm_area_struct *vma,
 #ifdef CONFIG_NUMA
 int shmem_set_policy(struct vm_area_struct *vma, struct mempolicy *new)
 {
-       struct inode *i = vma->vm_file->f_dentry->d_inode;
+       struct inode *i = vma->vm_file->f_path.dentry->d_inode;
        return mpol_set_shared_policy(&SHMEM_I(i)->policy, vma, new);
 }
 
 struct mempolicy *
 shmem_get_policy(struct vm_area_struct *vma, unsigned long addr)
 {
-       struct inode *i = vma->vm_file->f_dentry->d_inode;
+       struct inode *i = vma->vm_file->f_path.dentry->d_inode;
        unsigned long idx;
 
        idx = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
@@ -1302,7 +1315,7 @@ shmem_get_policy(struct vm_area_struct *vma, unsigned long addr)
 
 int shmem_lock(struct file *file, int lock, struct user_struct *user)
 {
-       struct inode *inode = file->f_dentry->d_inode;
+       struct inode *inode = file->f_path.dentry->d_inode;
        struct shmem_inode_info *info = SHMEM_I(inode);
        int retval = -ENOMEM;
 
@@ -1355,6 +1368,7 @@ shmem_get_inode(struct super_block *sb, int mode, dev_t dev)
                inode->i_mapping->a_ops = &shmem_aops;
                inode->i_mapping->backing_dev_info = &shmem_backing_dev_info;
                inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+               inode->i_generation = get_seconds();
                info = SHMEM_I(inode);
                memset(info, 0, (char *)inode - (char *)info);
                spin_lock_init(&info->lock);
@@ -1362,6 +1376,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:
@@ -1371,7 +1386,7 @@ shmem_get_inode(struct super_block *sb, int mode, dev_t dev)
                                                        &sbinfo->policy_nodes);
                        break;
                case S_IFDIR:
-                       inode->i_nlink++;
+                       inc_nlink(inode);
                        /* Some things misbehave if size == 0 on a directory */
                        inode->i_size = 2 * BOGO_DIRENT_SIZE;
                        inode->i_op = &shmem_dir_inode_operations;
@@ -1412,7 +1427,7 @@ shmem_prepare_write(struct file *file, struct page *page, unsigned offset, unsig
 static ssize_t
 shmem_file_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
 {
-       struct inode    *inode = file->f_dentry->d_inode;
+       struct inode    *inode = file->f_path.dentry->d_inode;
        loff_t          pos;
        unsigned long   written;
        ssize_t         err;
@@ -1432,7 +1447,7 @@ shmem_file_write(struct file *file, const char __user *buf, size_t count, loff_t
        if (err || !count)
                goto out;
 
-       err = remove_suid(file->f_dentry);
+       err = remove_suid(file->f_path.dentry);
        if (err)
                goto out;
 
@@ -1514,7 +1529,7 @@ out:
 
 static void do_shmem_file_read(struct file *filp, loff_t *ppos, read_descriptor_t *desc, read_actor_t actor)
 {
-       struct inode *inode = filp->f_dentry->d_inode;
+       struct inode *inode = filp->f_path.dentry->d_inode;
        struct address_space *mapping = inode->i_mapping;
        unsigned long index, offset;
 
@@ -1682,7 +1697,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;
@@ -1703,7 +1722,7 @@ static int shmem_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 
        if ((error = shmem_mknod(dir, dentry, mode | S_IFDIR, 0)))
                return error;
-       dir->i_nlink++;
+       inc_nlink(dir);
        return 0;
 }
 
@@ -1738,7 +1757,7 @@ static int shmem_link(struct dentry *old_dentry, struct inode *dir, struct dentr
 
        dir->i_size += BOGO_DIRENT_SIZE;
        inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-       inode->i_nlink++;
+       inc_nlink(inode);
        atomic_inc(&inode->i_count);    /* New dentry reference */
        dget(dentry);           /* Extra pinning count for the created dentry */
        d_instantiate(dentry, inode);
@@ -1760,7 +1779,7 @@ static int shmem_unlink(struct inode *dir, struct dentry *dentry)
 
        dir->i_size -= BOGO_DIRENT_SIZE;
        inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-       inode->i_nlink--;
+       drop_nlink(inode);
        dput(dentry);   /* Undo the count from "create" - this does all the work */
        return 0;
 }
@@ -1770,8 +1789,8 @@ static int shmem_rmdir(struct inode *dir, struct dentry *dentry)
        if (!simple_empty(dentry))
                return -ENOTEMPTY;
 
-       dentry->d_inode->i_nlink--;
-       dir->i_nlink--;
+       drop_nlink(dentry->d_inode);
+       drop_nlink(dir);
        return shmem_unlink(dir, dentry);
 }
 
@@ -1792,10 +1811,10 @@ static int shmem_rename(struct inode *old_dir, struct dentry *old_dentry, struct
        if (new_dentry->d_inode) {
                (void) shmem_unlink(new_dir, new_dentry);
                if (they_are_dirs)
-                       old_dir->i_nlink--;
+                       drop_nlink(old_dir);
        } else if (they_are_dirs) {
-               old_dir->i_nlink--;
-               new_dir->i_nlink++;
+               drop_nlink(old_dir);
+               inc_nlink(new_dir);
        }
 
        old_dir->i_size -= BOGO_DIRENT_SIZE;
@@ -1897,6 +1916,132 @@ 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);
+}
+
+static 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 struct dentry *shmem_get_parent(struct dentry *child)
+{
+       return ERR_PTR(-ESTALE);
+}
+
+static int shmem_match(struct inode *ino, void *vfh)
+{
+       __u32 *fh = vfh;
+       __u64 inum = fh[2];
+       inum = (inum << 32) | fh[1];
+       return ino->i_ino == inum && fh[0] == ino->i_generation;
+}
+
+static struct dentry *shmem_get_dentry(struct super_block *sb, void *vfh)
+{
+       struct dentry *de = NULL;
+       struct inode *inode;
+       __u32 *fh = vfh;
+       __u64 inum = fh[2];
+       inum = (inum << 32) | fh[1];
+
+       inode = ilookup5(sb, (unsigned long)(inum+fh[0]), shmem_match, vfh);
+       if (inode) {
+               de = d_find_alias(inode);
+               iput(inode);
+       }
+
+       return de? de: ERR_PTR(-ESTALE);
+}
+
+static struct dentry *shmem_decode_fh(struct super_block *sb, __u32 *fh,
+               int len, int type,
+               int (*acceptable)(void *context, struct dentry *de),
+               void *context)
+{
+       if (len < 3)
+               return ERR_PTR(-ESTALE);
+
+       return sb->s_export_op->find_exported_dentry(sb, fh, NULL, acceptable,
+                                                       context);
+}
+
+static int shmem_encode_fh(struct dentry *dentry, __u32 *fh, int *len,
+                               int connectable)
+{
+       struct inode *inode = dentry->d_inode;
+
+       if (*len < 3)
+               return 255;
+
+       if (hlist_unhashed(&inode->i_hash)) {
+               /* Unfortunately insert_inode_hash is not idempotent,
+                * so as we hash inodes here rather than at creation
+                * time, we need a lock to ensure we only try
+                * to do it once
+                */
+               static DEFINE_SPINLOCK(lock);
+               spin_lock(&lock);
+               if (hlist_unhashed(&inode->i_hash))
+                       __insert_inode_hash(inode,
+                                           inode->i_ino + inode->i_generation);
+               spin_unlock(&lock);
+       }
+
+       fh[0] = inode->i_generation;
+       fh[1] = inode->i_ino;
+       fh[2] = ((__u64)inode->i_ino) >> 32;
+
+       *len = 3;
+       return 1;
+}
+
+static struct export_operations shmem_export_ops = {
+       .get_parent     = shmem_get_parent,
+       .get_dentry     = shmem_get_dentry,
+       .encode_fh      = shmem_encode_fh,
+       .decode_fh      = shmem_decode_fh,
+};
+
 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)
@@ -2069,6 +2214,7 @@ static int shmem_fill_super(struct super_block *sb,
                                        &inodes, &policy, &policy_nodes))
                        return -EINVAL;
        }
+       sb->s_export_op = &shmem_export_ops;
 #else
        sb->s_flags |= MS_NOUSER;
 #endif
@@ -2094,6 +2240,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)
@@ -2118,7 +2268,7 @@ static struct kmem_cache *shmem_inode_cachep;
 static struct inode *shmem_alloc_inode(struct super_block *sb)
 {
        struct shmem_inode_info *p;
-       p = (struct shmem_inode_info *)kmem_cache_alloc(shmem_inode_cachep, SLAB_KERNEL);
+       p = (struct shmem_inode_info *)kmem_cache_alloc(shmem_inode_cachep, GFP_KERNEL);
        if (!p)
                return NULL;
        return &p->vfs_inode;
@@ -2130,6 +2280,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));
 }
 
@@ -2141,6 +2292,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
        }
 }
 
@@ -2169,7 +2324,7 @@ static const struct address_space_operations shmem_aops = {
        .migratepage    = migrate_page,
 };
 
-static struct file_operations shmem_file_operations = {
+static const struct file_operations shmem_file_operations = {
        .mmap           = shmem_mmap,
 #ifdef CONFIG_TMPFS
        .llseek         = generic_file_llseek,
@@ -2184,6 +2339,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 = {
@@ -2198,6 +2361,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 = {
@@ -2316,8 +2498,8 @@ struct file *shmem_file_setup(char *name, loff_t size, unsigned long flags)
        d_instantiate(dentry, inode);
        inode->i_size = size;
        inode->i_nlink = 0;     /* It is unlinked */
-       file->f_vfsmnt = mntget(shm_mnt);
-       file->f_dentry = dentry;
+       file->f_path.mnt = mntget(shm_mnt);
+       file->f_path.dentry = dentry;
        file->f_mapping = inode->i_mapping;
        file->f_op = &shmem_file_operations;
        file->f_mode = FMODE_WRITE | FMODE_READ;