Linux 6.9-rc1
[linux-2.6-microblaze.git] / fs / file_table.c
index 99c6796..4f03bee 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/fs.h>
+#include <linux/filelock.h>
 #include <linux/security.h>
 #include <linux/cred.h>
 #include <linux/eventpoll.h>
@@ -25,7 +26,6 @@
 #include <linux/percpu_counter.h>
 #include <linux/percpu.h>
 #include <linux/task_work.h>
-#include <linux/ima.h>
 #include <linux/swap.h>
 #include <linux/kmemleak.h>
 
@@ -39,24 +39,39 @@ static struct files_stat_struct files_stat = {
 };
 
 /* SLAB cache for file structures */
-static struct kmem_cache *filp_cachep __read_mostly;
+static struct kmem_cache *filp_cachep __ro_after_init;
 
 static struct percpu_counter nr_files __cacheline_aligned_in_smp;
 
-static void file_free_rcu(struct rcu_head *head)
+/* Container for backing file with optional user path */
+struct backing_file {
+       struct file file;
+       struct path user_path;
+};
+
+static inline struct backing_file *backing_file(struct file *f)
 {
-       struct file *f = container_of(head, struct file, f_rcuhead);
+       return container_of(f, struct backing_file, file);
+}
 
-       put_cred(f->f_cred);
-       kmem_cache_free(filp_cachep, f);
+struct path *backing_file_user_path(struct file *f)
+{
+       return &backing_file(f)->user_path;
 }
+EXPORT_SYMBOL_GPL(backing_file_user_path);
 
 static inline void file_free(struct file *f)
 {
        security_file_free(f);
-       if (!(f->f_mode & FMODE_NOACCOUNT))
+       if (likely(!(f->f_mode & FMODE_NOACCOUNT)))
                percpu_counter_dec(&nr_files);
-       call_rcu(&f->f_rcuhead, file_free_rcu);
+       put_cred(f->f_cred);
+       if (unlikely(f->f_mode & FMODE_BACKING)) {
+               path_put(backing_file_user_path(f));
+               kfree(backing_file(f));
+       } else {
+               kmem_cache_free(filp_cachep, f);
+       }
 }
 
 /*
@@ -114,7 +129,6 @@ static struct ctl_table fs_stat_sysctls[] = {
                .extra1         = &sysctl_nr_open_min,
                .extra2         = &sysctl_nr_open_max,
        },
-       { }
 };
 
 static int __init init_fs_stat_sysctls(void)
@@ -130,23 +144,17 @@ static int __init init_fs_stat_sysctls(void)
 fs_initcall(init_fs_stat_sysctls);
 #endif
 
-static struct file *__alloc_file(int flags, const struct cred *cred)
+static int init_file(struct file *f, int flags, const struct cred *cred)
 {
-       struct file *f;
        int error;
 
-       f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL);
-       if (unlikely(!f))
-               return ERR_PTR(-ENOMEM);
-
        f->f_cred = get_cred(cred);
        error = security_file_alloc(f);
        if (unlikely(error)) {
-               file_free_rcu(&f->f_rcuhead);
-               return ERR_PTR(error);
+               put_cred(f->f_cred);
+               return error;
        }
 
-       atomic_long_set(&f->f_count, 1);
        rwlock_init(&f->f_owner.lock);
        spin_lock_init(&f->f_lock);
        mutex_init(&f->f_pos_lock);
@@ -154,7 +162,13 @@ static struct file *__alloc_file(int flags, const struct cred *cred)
        f->f_mode = OPEN_FMODE(flags);
        /* f->f_version: 0 */
 
-       return f;
+       /*
+        * We're SLAB_TYPESAFE_BY_RCU so initialize f_count last. While
+        * fget-rcu pattern users need to be able to handle spurious
+        * refcount bumps we should reinitialize the reused file first.
+        */
+       atomic_long_set(&f->f_count, 1);
+       return 0;
 }
 
 /* Find an unused file structure and return a pointer to it.
@@ -171,6 +185,7 @@ struct file *alloc_empty_file(int flags, const struct cred *cred)
 {
        static long old_max;
        struct file *f;
+       int error;
 
        /*
         * Privileged users can go above max_files
@@ -184,9 +199,17 @@ struct file *alloc_empty_file(int flags, const struct cred *cred)
                        goto over;
        }
 
-       f = __alloc_file(flags, cred);
-       if (!IS_ERR(f))
-               percpu_counter_inc(&nr_files);
+       f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL);
+       if (unlikely(!f))
+               return ERR_PTR(-ENOMEM);
+
+       error = init_file(f, flags, cred);
+       if (unlikely(error)) {
+               kmem_cache_free(filp_cachep, f);
+               return ERR_PTR(error);
+       }
+
+       percpu_counter_inc(&nr_files);
 
        return f;
 
@@ -202,34 +225,65 @@ over:
 /*
  * Variant of alloc_empty_file() that doesn't check and modify nr_files.
  *
- * Should not be used unless there's a very good reason to do so.
+ * This is only for kernel internal use, and the allocate file must not be
+ * installed into file tables or such.
  */
 struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred)
 {
-       struct file *f = __alloc_file(flags, cred);
+       struct file *f;
+       int error;
 
-       if (!IS_ERR(f))
-               f->f_mode |= FMODE_NOACCOUNT;
+       f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL);
+       if (unlikely(!f))
+               return ERR_PTR(-ENOMEM);
+
+       error = init_file(f, flags, cred);
+       if (unlikely(error)) {
+               kmem_cache_free(filp_cachep, f);
+               return ERR_PTR(error);
+       }
+
+       f->f_mode |= FMODE_NOACCOUNT;
 
        return f;
 }
 
+/*
+ * Variant of alloc_empty_file() that allocates a backing_file container
+ * and doesn't check and modify nr_files.
+ *
+ * This is only for kernel internal use, and the allocate file must not be
+ * installed into file tables or such.
+ */
+struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
+{
+       struct backing_file *ff;
+       int error;
+
+       ff = kzalloc(sizeof(struct backing_file), GFP_KERNEL);
+       if (unlikely(!ff))
+               return ERR_PTR(-ENOMEM);
+
+       error = init_file(&ff->file, flags, cred);
+       if (unlikely(error)) {
+               kfree(ff);
+               return ERR_PTR(error);
+       }
+
+       ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT;
+       return &ff->file;
+}
+
 /**
- * alloc_file - allocate and initialize a 'struct file'
+ * file_init_path - initialize a 'struct file' based on path
  *
+ * @file: the file to set up
  * @path: the (dentry, vfsmount) pair for the new file
- * @flags: O_... flags with which the new file will be opened
  * @fop: the 'struct file_operations' for the new file
  */
-static struct file *alloc_file(const struct path *path, int flags,
-               const struct file_operations *fop)
+static void file_init_path(struct file *file, const struct path *path,
+                          const struct file_operations *fop)
 {
-       struct file *file;
-
-       file = alloc_empty_file(flags, current_cred());
-       if (IS_ERR(file))
-               return file;
-
        file->f_path = *path;
        file->f_inode = path->dentry->d_inode;
        file->f_mapping = path->dentry->d_inode->i_mapping;
@@ -248,27 +302,51 @@ static struct file *alloc_file(const struct path *path, int flags,
        file->f_op = fop;
        if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
                i_readcount_inc(path->dentry->d_inode);
+}
+
+/**
+ * alloc_file - allocate and initialize a 'struct file'
+ *
+ * @path: the (dentry, vfsmount) pair for the new file
+ * @flags: O_... flags with which the new file will be opened
+ * @fop: the 'struct file_operations' for the new file
+ */
+static struct file *alloc_file(const struct path *path, int flags,
+               const struct file_operations *fop)
+{
+       struct file *file;
+
+       file = alloc_empty_file(flags, current_cred());
+       if (!IS_ERR(file))
+               file_init_path(file, path, fop);
        return file;
 }
 
-struct file *alloc_file_pseudo(struct inode *inode, struct vfsmount *mnt,
-                               const char *name, int flags,
-                               const struct file_operations *fops)
+static inline int alloc_path_pseudo(const char *name, struct inode *inode,
+                                   struct vfsmount *mnt, struct path *path)
 {
-       static const struct dentry_operations anon_ops = {
-               .d_dname = simple_dname
-       };
        struct qstr this = QSTR_INIT(name, strlen(name));
+
+       path->dentry = d_alloc_pseudo(mnt->mnt_sb, &this);
+       if (!path->dentry)
+               return -ENOMEM;
+       path->mnt = mntget(mnt);
+       d_instantiate(path->dentry, inode);
+       return 0;
+}
+
+struct file *alloc_file_pseudo(struct inode *inode, struct vfsmount *mnt,
+                              const char *name, int flags,
+                              const struct file_operations *fops)
+{
+       int ret;
        struct path path;
        struct file *file;
 
-       path.dentry = d_alloc_pseudo(mnt->mnt_sb, &this);
-       if (!path.dentry)
-               return ERR_PTR(-ENOMEM);
-       if (!mnt->mnt_sb->s_d_op)
-               d_set_d_op(path.dentry, &anon_ops);
-       path.mnt = mntget(mnt);
-       d_instantiate(path.dentry, inode);
+       ret = alloc_path_pseudo(name, inode, mnt, &path);
+       if (ret)
+               return ERR_PTR(ret);
+
        file = alloc_file(&path, flags, fops);
        if (IS_ERR(file)) {
                ihold(inode);
@@ -278,6 +356,30 @@ struct file *alloc_file_pseudo(struct inode *inode, struct vfsmount *mnt,
 }
 EXPORT_SYMBOL(alloc_file_pseudo);
 
+struct file *alloc_file_pseudo_noaccount(struct inode *inode,
+                                        struct vfsmount *mnt, const char *name,
+                                        int flags,
+                                        const struct file_operations *fops)
+{
+       int ret;
+       struct path path;
+       struct file *file;
+
+       ret = alloc_path_pseudo(name, inode, mnt, &path);
+       if (ret)
+               return ERR_PTR(ret);
+
+       file = alloc_empty_file_noaccount(flags, current_cred());
+       if (IS_ERR(file)) {
+               ihold(inode);
+               path_put(&path);
+               return file;
+       }
+       file_init_path(file, &path, fops);
+       return file;
+}
+EXPORT_SYMBOL_GPL(alloc_file_pseudo_noaccount);
+
 struct file *alloc_file_clone(struct file *base, int flags,
                                const struct file_operations *fops)
 {
@@ -311,7 +413,7 @@ static void __fput(struct file *file)
        eventpoll_release(file);
        locks_remove_file(file);
 
-       ima_file_free(file);
+       security_file_release(file);
        if (unlikely(file->f_flags & FASYNC)) {
                if (file->f_op->fasync)
                        file->f_op->fasync(-1, file, 0);
@@ -324,12 +426,7 @@ static void __fput(struct file *file)
        }
        fops_put(file->f_op);
        put_pid(file->f_owner.pid);
-       if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
-               i_readcount_dec(inode);
-       if (mode & FMODE_WRITER) {
-               put_write_access(inode);
-               __mnt_drop_write(mnt);
-       }
+       put_file_access(file);
        dput(dentry);
        if (unlikely(mode & FMODE_NEED_UNMOUNT))
                dissolve_on_fput(mnt);
@@ -350,7 +447,7 @@ static void delayed_fput(struct work_struct *unused)
 
 static void ____fput(struct callback_head *work)
 {
-       __fput(container_of(work, struct file, f_rcuhead));
+       __fput(container_of(work, struct file, f_task_work));
 }
 
 /*
@@ -376,9 +473,13 @@ void fput(struct file *file)
        if (atomic_long_dec_and_test(&file->f_count)) {
                struct task_struct *task = current;
 
+               if (unlikely(!(file->f_mode & (FMODE_BACKING | FMODE_OPENED)))) {
+                       file_free(file);
+                       return;
+               }
                if (likely(!in_interrupt() && !(task->flags & PF_KTHREAD))) {
-                       init_task_work(&file->f_rcuhead, ____fput);
-                       if (!task_work_add(task, &file->f_rcuhead, TWA_RESUME))
+                       init_task_work(&file->f_task_work, ____fput);
+                       if (!task_work_add(task, &file->f_task_work, TWA_RESUME))
                                return;
                        /*
                         * After this task has run exit_task_work(),
@@ -402,11 +503,8 @@ void fput(struct file *file)
  */
 void __fput_sync(struct file *file)
 {
-       if (atomic_long_dec_and_test(&file->f_count)) {
-               struct task_struct *task = current;
-               BUG_ON(!(task->flags & PF_KTHREAD));
+       if (atomic_long_dec_and_test(&file->f_count))
                __fput(file);
-       }
 }
 
 EXPORT_SYMBOL(fput);
@@ -415,7 +513,8 @@ EXPORT_SYMBOL(__fput_sync);
 void __init files_init(void)
 {
        filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0,
-                       SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT, NULL);
+                               SLAB_TYPESAFE_BY_RCU | SLAB_HWCACHE_ALIGN |
+                               SLAB_PANIC | SLAB_ACCOUNT, NULL);
        percpu_counter_init(&nr_files, 0, GFP_KERNEL);
 }