Merge branch '100GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/net
[platform/kernel/linux-starfive.git] / fs / inode.c
index 524ee91..6462276 100644 (file)
@@ -422,6 +422,7 @@ void inode_init_once(struct inode *inode)
        INIT_LIST_HEAD(&inode->i_io_list);
        INIT_LIST_HEAD(&inode->i_wb_list);
        INIT_LIST_HEAD(&inode->i_lru);
+       INIT_LIST_HEAD(&inode->i_sb_list);
        __address_space_init_once(&inode->i_data);
        i_size_ordered_init(inode);
 }
@@ -1021,7 +1022,6 @@ struct inode *new_inode_pseudo(struct super_block *sb)
                spin_lock(&inode->i_lock);
                inode->i_state = 0;
                spin_unlock(&inode->i_lock);
-               INIT_LIST_HEAD(&inode->i_sb_list);
        }
        return inode;
 }
@@ -1165,7 +1165,6 @@ struct inode *inode_insert5(struct inode *inode, unsigned long hashval,
 {
        struct hlist_head *head = inode_hashtable + hash(inode->i_sb, hashval);
        struct inode *old;
-       bool creating = inode->i_state & I_CREATING;
 
 again:
        spin_lock(&inode_hash_lock);
@@ -1199,7 +1198,12 @@ again:
        inode->i_state |= I_NEW;
        hlist_add_head_rcu(&inode->i_hash, head);
        spin_unlock(&inode->i_lock);
-       if (!creating)
+
+       /*
+        * Add inode to the sb list if it's not already. It has I_NEW at this
+        * point, so it should be safe to test i_sb_list locklessly.
+        */
+       if (list_empty(&inode->i_sb_list))
                inode_sb_list_add(inode);
 unlock:
        spin_unlock(&inode_hash_lock);
@@ -2326,10 +2330,6 @@ void inode_init_owner(struct user_namespace *mnt_userns, struct inode *inode,
                /* Directories are special, and always inherit S_ISGID */
                if (S_ISDIR(mode))
                        mode |= S_ISGID;
-               else if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP) &&
-                        !in_group_p(i_gid_into_mnt(mnt_userns, dir)) &&
-                        !capable_wrt_inode_uidgid(mnt_userns, dir, CAP_FSETID))
-                       mode &= ~S_ISGID;
        } else
                inode_fsgid_set(inode, mnt_userns);
        inode->i_mode = mode;
@@ -2485,3 +2485,33 @@ struct timespec64 current_time(struct inode *inode)
        return timestamp_truncate(now, inode);
 }
 EXPORT_SYMBOL(current_time);
+
+/**
+ * mode_strip_sgid - handle the sgid bit for non-directories
+ * @mnt_userns: User namespace of the mount the inode was created from
+ * @dir: parent directory inode
+ * @mode: mode of the file to be created in @dir
+ *
+ * If the @mode of the new file has both the S_ISGID and S_IXGRP bit
+ * raised and @dir has the S_ISGID bit raised ensure that the caller is
+ * either in the group of the parent directory or they have CAP_FSETID
+ * in their user namespace and are privileged over the parent directory.
+ * In all other cases, strip the S_ISGID bit from @mode.
+ *
+ * Return: the new mode to use for the file
+ */
+umode_t mode_strip_sgid(struct user_namespace *mnt_userns,
+                       const struct inode *dir, umode_t mode)
+{
+       if ((mode & (S_ISGID | S_IXGRP)) != (S_ISGID | S_IXGRP))
+               return mode;
+       if (S_ISDIR(mode) || !dir || !(dir->i_mode & S_ISGID))
+               return mode;
+       if (in_group_p(i_gid_into_mnt(mnt_userns, dir)))
+               return mode;
+       if (capable_wrt_inode_uidgid(mnt_userns, dir, CAP_FSETID))
+               return mode;
+
+       return mode & ~S_ISGID;
+}
+EXPORT_SYMBOL(mode_strip_sgid);