]> git.ipfire.org Git - thirdparty/linux.git/blobdiff - fs/tracefs/inode.c
Merge tag 'vfs-6.10.mount' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[thirdparty/linux.git] / fs / tracefs / inode.c
index abd0b477e287595683e306e06c6ddc34dae44fd6..a827f6a716c44ec840357a3afee7a51c04296c86 100644 (file)
@@ -30,20 +30,47 @@ static struct vfsmount *tracefs_mount;
 static int tracefs_mount_count;
 static bool tracefs_registered;
 
+/*
+ * Keep track of all tracefs_inodes in order to update their
+ * flags if necessary on a remount.
+ */
+static DEFINE_SPINLOCK(tracefs_inode_lock);
+static LIST_HEAD(tracefs_inodes);
+
 static struct inode *tracefs_alloc_inode(struct super_block *sb)
 {
        struct tracefs_inode *ti;
+       unsigned long flags;
 
        ti = kmem_cache_alloc(tracefs_inode_cachep, GFP_KERNEL);
        if (!ti)
                return NULL;
 
+       spin_lock_irqsave(&tracefs_inode_lock, flags);
+       list_add_rcu(&ti->list, &tracefs_inodes);
+       spin_unlock_irqrestore(&tracefs_inode_lock, flags);
+
        return &ti->vfs_inode;
 }
 
+static void tracefs_free_inode_rcu(struct rcu_head *rcu)
+{
+       struct tracefs_inode *ti;
+
+       ti = container_of(rcu, struct tracefs_inode, rcu);
+       kmem_cache_free(tracefs_inode_cachep, ti);
+}
+
 static void tracefs_free_inode(struct inode *inode)
 {
-       kmem_cache_free(tracefs_inode_cachep, get_tracefs(inode));
+       struct tracefs_inode *ti = get_tracefs(inode);
+       unsigned long flags;
+
+       spin_lock_irqsave(&tracefs_inode_lock, flags);
+       list_del_rcu(&ti->list);
+       spin_unlock_irqrestore(&tracefs_inode_lock, flags);
+
+       call_rcu(&ti->rcu, tracefs_free_inode_rcu);
 }
 
 static ssize_t default_read_file(struct file *file, char __user *buf,
@@ -153,16 +180,39 @@ static void set_tracefs_inode_owner(struct inode *inode)
 {
        struct tracefs_inode *ti = get_tracefs(inode);
        struct inode *root_inode = ti->private;
+       kuid_t uid;
+       kgid_t gid;
+
+       uid = root_inode->i_uid;
+       gid = root_inode->i_gid;
+
+       /*
+        * If the root is not the mount point, then check the root's
+        * permissions. If it was never set, then default to the
+        * mount point.
+        */
+       if (root_inode != d_inode(root_inode->i_sb->s_root)) {
+               struct tracefs_inode *rti;
+
+               rti = get_tracefs(root_inode);
+               root_inode = d_inode(root_inode->i_sb->s_root);
+
+               if (!(rti->flags & TRACEFS_UID_PERM_SET))
+                       uid = root_inode->i_uid;
+
+               if (!(rti->flags & TRACEFS_GID_PERM_SET))
+                       gid = root_inode->i_gid;
+       }
 
        /*
         * If this inode has never been referenced, then update
         * the permissions to the superblock.
         */
        if (!(ti->flags & TRACEFS_UID_PERM_SET))
-               inode->i_uid = root_inode->i_uid;
+               inode->i_uid = uid;
 
        if (!(ti->flags & TRACEFS_GID_PERM_SET))
-               inode->i_gid = root_inode->i_gid;
+               inode->i_gid = gid;
 }
 
 static int tracefs_permission(struct mnt_idmap *idmap,
@@ -295,6 +345,8 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
 {
        struct tracefs_fs_info *fsi = sb->s_fs_info;
        struct inode *inode = d_inode(sb->s_root);
+       struct tracefs_inode *ti;
+       bool update_uid, update_gid;
        umode_t tmp_mode;
 
        /*
@@ -314,6 +366,25 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
        if (!remount || fsi->opts & BIT(Opt_gid))
                inode->i_gid = fsi->gid;
 
+       if (remount && (fsi->opts & BIT(Opt_uid) || fsi->opts & BIT(Opt_gid))) {
+
+               update_uid = fsi->opts & BIT(Opt_uid);
+               update_gid = fsi->opts & BIT(Opt_gid);
+
+               rcu_read_lock();
+               list_for_each_entry_rcu(ti, &tracefs_inodes, list) {
+                       if (update_uid)
+                               ti->flags &= ~TRACEFS_UID_PERM_SET;
+
+                       if (update_gid)
+                               ti->flags &= ~TRACEFS_GID_PERM_SET;
+
+                       if (ti->flags & TRACEFS_EVENT_INODE)
+                               eventfs_remount(ti, update_uid, update_gid);
+               }
+               rcu_read_unlock();
+       }
+
        return 0;
 }
 
@@ -375,7 +446,22 @@ static int tracefs_d_revalidate(struct dentry *dentry, unsigned int flags)
        return !(ei && ei->is_freed);
 }
 
+static void tracefs_d_iput(struct dentry *dentry, struct inode *inode)
+{
+       struct tracefs_inode *ti = get_tracefs(inode);
+
+       /*
+        * This inode is being freed and cannot be used for
+        * eventfs. Clear the flag so that it doesn't call into
+        * eventfs during the remount flag updates. The eventfs_inode
+        * gets freed after an RCU cycle, so the content will still
+        * be safe if the iteration is going on now.
+        */
+       ti->flags &= ~TRACEFS_EVENT_INODE;
+}
+
 static const struct dentry_operations tracefs_dentry_operations = {
+       .d_iput = tracefs_d_iput,
        .d_revalidate = tracefs_d_revalidate,
        .d_release = tracefs_d_release,
 };