Linux 6.13-rc1
[linux-2.6-microblaze.git] / fs / attr.c
index 49d23b5..9caf63d 100644 (file)
--- a/fs/attr.c
+++ b/fs/attr.c
 #include <linux/fcntl.h>
 #include <linux/filelock.h>
 #include <linux/security.h>
-#include <linux/evm.h>
-#include <linux/ima.h>
-
-#include "internal.h"
 
 /**
  * setattr_should_drop_sgid - determine whether the setgid bit needs to be
@@ -275,6 +271,47 @@ out_big:
 }
 EXPORT_SYMBOL(inode_newsize_ok);
 
+/**
+ * setattr_copy_mgtime - update timestamps for mgtime inodes
+ * @inode: inode timestamps to be updated
+ * @attr: attrs for the update
+ *
+ * With multigrain timestamps, take more care to prevent races when
+ * updating the ctime. Always update the ctime to the very latest using
+ * the standard mechanism, and use that to populate the atime and mtime
+ * appropriately (unless those are being set to specific values).
+ */
+static void setattr_copy_mgtime(struct inode *inode, const struct iattr *attr)
+{
+       unsigned int ia_valid = attr->ia_valid;
+       struct timespec64 now;
+
+       if (ia_valid & ATTR_CTIME) {
+               /*
+                * In the case of an update for a write delegation, we must respect
+                * the value in ia_ctime and not use the current time.
+                */
+               if (ia_valid & ATTR_DELEG)
+                       now = inode_set_ctime_deleg(inode, attr->ia_ctime);
+               else
+                       now = inode_set_ctime_current(inode);
+       } else {
+               /* If ATTR_CTIME isn't set, then ATTR_MTIME shouldn't be either. */
+               WARN_ON_ONCE(ia_valid & ATTR_MTIME);
+               now = current_time(inode);
+       }
+
+       if (ia_valid & ATTR_ATIME_SET)
+               inode_set_atime_to_ts(inode, attr->ia_atime);
+       else if (ia_valid & ATTR_ATIME)
+               inode_set_atime_to_ts(inode, now);
+
+       if (ia_valid & ATTR_MTIME_SET)
+               inode_set_mtime_to_ts(inode, attr->ia_mtime);
+       else if (ia_valid & ATTR_MTIME)
+               inode_set_mtime_to_ts(inode, now);
+}
+
 /**
  * setattr_copy - copy simple metadata updates into the generic inode
  * @idmap:     idmap of the mount the inode was found from
@@ -307,12 +344,6 @@ void setattr_copy(struct mnt_idmap *idmap, struct inode *inode,
 
        i_uid_update(idmap, attr, inode);
        i_gid_update(idmap, attr, inode);
-       if (ia_valid & ATTR_ATIME)
-               inode_set_atime_to_ts(inode, attr->ia_atime);
-       if (ia_valid & ATTR_MTIME)
-               inode_set_mtime_to_ts(inode, attr->ia_mtime);
-       if (ia_valid & ATTR_CTIME)
-               inode_set_ctime_to_ts(inode, attr->ia_ctime);
        if (ia_valid & ATTR_MODE) {
                umode_t mode = attr->ia_mode;
                if (!in_group_or_capable(idmap, inode,
@@ -320,6 +351,20 @@ void setattr_copy(struct mnt_idmap *idmap, struct inode *inode,
                        mode &= ~S_ISGID;
                inode->i_mode = mode;
        }
+
+       if (is_mgtime(inode))
+               return setattr_copy_mgtime(inode, attr);
+
+       if (ia_valid & ATTR_ATIME)
+               inode_set_atime_to_ts(inode, attr->ia_atime);
+       if (ia_valid & ATTR_MTIME)
+               inode_set_mtime_to_ts(inode, attr->ia_mtime);
+       if (ia_valid & ATTR_CTIME) {
+               if (ia_valid & ATTR_DELEG)
+                       inode_set_ctime_deleg(inode, attr->ia_ctime);
+               else
+                       inode_set_ctime_to_ts(inode, attr->ia_ctime);
+       }
 }
 EXPORT_SYMBOL(setattr_copy);
 
@@ -491,9 +536,17 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
        error = security_inode_setattr(idmap, dentry, attr);
        if (error)
                return error;
-       error = try_break_deleg(inode, delegated_inode);
-       if (error)
-               return error;
+
+       /*
+        * If ATTR_DELEG is set, then these attributes are being set on
+        * behalf of the holder of a write delegation. We want to avoid
+        * breaking the delegation in this case.
+        */
+       if (!(ia_valid & ATTR_DELEG)) {
+               error = try_break_deleg(inode, delegated_inode);
+               if (error)
+                       return error;
+       }
 
        if (inode->i_op->setattr)
                error = inode->i_op->setattr(idmap, dentry, attr);
@@ -502,8 +555,7 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
 
        if (!error) {
                fsnotify_change(dentry, ia_valid);
-               ima_inode_post_setattr(idmap, dentry);
-               evm_inode_post_setattr(dentry, ia_valid);
+               security_inode_post_setattr(idmap, dentry, ia_valid);
        }
 
        return error;