usb: gadget: f_hid: fix report descriptor allocation
[platform/kernel/linux-starfive.git] / fs / inode.c
index b608528..73ad1b0 100644 (file)
@@ -1104,6 +1104,48 @@ void discard_new_inode(struct inode *inode)
 EXPORT_SYMBOL(discard_new_inode);
 
 /**
+ * lock_two_inodes - lock two inodes (may be regular files but also dirs)
+ *
+ * Lock any non-NULL argument. The caller must make sure that if he is passing
+ * in two directories, one is not ancestor of the other.  Zero, one or two
+ * objects may be locked by this function.
+ *
+ * @inode1: first inode to lock
+ * @inode2: second inode to lock
+ * @subclass1: inode lock subclass for the first lock obtained
+ * @subclass2: inode lock subclass for the second lock obtained
+ */
+void lock_two_inodes(struct inode *inode1, struct inode *inode2,
+                    unsigned subclass1, unsigned subclass2)
+{
+       if (!inode1 || !inode2) {
+               /*
+                * Make sure @subclass1 will be used for the acquired lock.
+                * This is not strictly necessary (no current caller cares) but
+                * let's keep things consistent.
+                */
+               if (!inode1)
+                       swap(inode1, inode2);
+               goto lock;
+       }
+
+       /*
+        * If one object is directory and the other is not, we must make sure
+        * to lock directory first as the other object may be its child.
+        */
+       if (S_ISDIR(inode2->i_mode) == S_ISDIR(inode1->i_mode)) {
+               if (inode1 > inode2)
+                       swap(inode1, inode2);
+       } else if (!S_ISDIR(inode1->i_mode))
+               swap(inode1, inode2);
+lock:
+       if (inode1)
+               inode_lock_nested(inode1, subclass1);
+       if (inode2 && inode2 != inode1)
+               inode_lock_nested(inode2, subclass2);
+}
+
+/**
  * lock_two_nondirectories - take two i_mutexes on non-directory objects
  *
  * Lock any non-NULL argument that is not a directory.
@@ -1949,40 +1991,12 @@ skip_update:
 EXPORT_SYMBOL(touch_atime);
 
 /*
- * The logic we want is
- *
- *     if suid or (sgid and xgrp)
- *             remove privs
- */
-int should_remove_suid(struct dentry *dentry)
-{
-       umode_t mode = d_inode(dentry)->i_mode;
-       int kill = 0;
-
-       /* suid always must be killed */
-       if (unlikely(mode & S_ISUID))
-               kill = ATTR_KILL_SUID;
-
-       /*
-        * sgid without any exec bits is just a mandatory locking mark; leave
-        * it alone.  If some exec bits are set, it's a real sgid; kill it.
-        */
-       if (unlikely((mode & S_ISGID) && (mode & S_IXGRP)))
-               kill |= ATTR_KILL_SGID;
-
-       if (unlikely(kill && !capable(CAP_FSETID) && S_ISREG(mode)))
-               return kill;
-
-       return 0;
-}
-EXPORT_SYMBOL(should_remove_suid);
-
-/*
  * Return mask of changes for notify_change() that need to be done as a
  * response to write or truncate. Return 0 if nothing has to be changed.
  * Negative value on error (change should be denied).
  */
-int dentry_needs_remove_privs(struct dentry *dentry)
+int dentry_needs_remove_privs(struct user_namespace *mnt_userns,
+                             struct dentry *dentry)
 {
        struct inode *inode = d_inode(dentry);
        int mask = 0;
@@ -1991,7 +2005,7 @@ int dentry_needs_remove_privs(struct dentry *dentry)
        if (IS_NOSEC(inode))
                return 0;
 
-       mask = should_remove_suid(dentry);
+       mask = setattr_should_drop_suidgid(mnt_userns, inode);
        ret = security_inode_need_killpriv(dentry);
        if (ret < 0)
                return ret;
@@ -2023,7 +2037,7 @@ static int __file_remove_privs(struct file *file, unsigned int flags)
        if (IS_NOSEC(inode) || !S_ISREG(inode->i_mode))
                return 0;
 
-       kill = dentry_needs_remove_privs(dentry);
+       kill = dentry_needs_remove_privs(file_mnt_user_ns(file), dentry);
        if (kill < 0)
                return kill;
 
@@ -2488,6 +2502,44 @@ struct timespec64 current_time(struct inode *inode)
 EXPORT_SYMBOL(current_time);
 
 /**
+ * inode_set_ctime_current - set the ctime to current_time
+ * @inode: inode
+ *
+ * Set the inode->i_ctime to the current value for the inode. Returns
+ * the current value that was assigned to i_ctime.
+ */
+struct timespec64 inode_set_ctime_current(struct inode *inode)
+{
+       struct timespec64 now = current_time(inode);
+
+       inode_set_ctime(inode, now.tv_sec, now.tv_nsec);
+       return now;
+}
+EXPORT_SYMBOL(inode_set_ctime_current);
+
+/**
+ * in_group_or_capable - check whether caller is CAP_FSETID privileged
+ * @mnt_userns: user namespace of the mount @inode was found from
+ * @inode:     inode to check
+ * @vfsgid:    the new/current vfsgid of @inode
+ *
+ * Check wether @vfsgid is in the caller's group list or if the caller is
+ * privileged with CAP_FSETID over @inode. This can be used to determine
+ * whether the setgid bit can be kept or must be dropped.
+ *
+ * Return: true if the caller is sufficiently privileged, false if not.
+ */
+bool in_group_or_capable(struct user_namespace *mnt_userns,
+                        const struct inode *inode, vfsgid_t vfsgid)
+{
+       if (vfsgid_in_group_p(vfsgid))
+               return true;
+       if (capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))
+               return true;
+       return false;
+}
+
+/**
  * 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
@@ -2508,11 +2560,9 @@ umode_t mode_strip_sgid(struct user_namespace *mnt_userns,
                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))
+       if (in_group_or_capable(mnt_userns, dir,
+                               i_gid_into_vfsgid(mnt_userns, dir)))
                return mode;
-
        return mode & ~S_ISGID;
 }
 EXPORT_SYMBOL(mode_strip_sgid);