]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
f2fs: enhance to update i_mode and acl atomically in f2fs_setattr()
authorChao Yu <yuchao0@huawei.com>
Fri, 25 Dec 2020 08:52:27 +0000 (16:52 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 17 Oct 2024 13:07:55 +0000 (15:07 +0200)
[ Upstream commit 17232e830afb800acdcc22ae8980bf9d330393ef ]

Previously, in f2fs_setattr(), we don't update S_ISUID|S_ISGID|S_ISVTX
bits with S_IRWXUGO bits and acl entries atomically, so in error path,
chmod() may partially success, this patch enhances to make chmod() flow
being atomical.

Signed-off-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Stable-dep-of: aaf8c0b9ae04 ("f2fs: reduce expensive checkpoint trigger frequency")
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/f2fs/acl.c
fs/f2fs/file.c
fs/f2fs/xattr.c

index 3064135898276a99414c55f6c3bca9efeca84f8b..a89b2b1390e86080e23c7a43c4ab293216a4373c 100644 (file)
@@ -200,6 +200,27 @@ struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
        return __f2fs_get_acl(inode, type, NULL);
 }
 
+static int f2fs_acl_update_mode(struct inode *inode, umode_t *mode_p,
+                         struct posix_acl **acl)
+{
+       umode_t mode = inode->i_mode;
+       int error;
+
+       if (is_inode_flag_set(inode, FI_ACL_MODE))
+               mode = F2FS_I(inode)->i_acl_mode;
+
+       error = posix_acl_equiv_mode(*acl, &mode);
+       if (error < 0)
+               return error;
+       if (error == 0)
+               *acl = NULL;
+       if (!in_group_p(inode->i_gid) &&
+           !capable_wrt_inode_uidgid(inode, CAP_FSETID))
+               mode &= ~S_ISGID;
+       *mode_p = mode;
+       return 0;
+}
+
 static int __f2fs_set_acl(struct inode *inode, int type,
                        struct posix_acl *acl, struct page *ipage)
 {
@@ -213,7 +234,7 @@ static int __f2fs_set_acl(struct inode *inode, int type,
        case ACL_TYPE_ACCESS:
                name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
                if (acl && !ipage) {
-                       error = posix_acl_update_mode(inode, &mode, &acl);
+                       error = f2fs_acl_update_mode(inode, &mode, &acl);
                        if (error)
                                return error;
                        set_acl_inode(inode, mode);
index 50514962771a106523ee0a5b4d392d97c9e9d5bc..8f7aa4010bb900936c97ef70b617c24fdb386406 100644 (file)
@@ -980,8 +980,10 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
 
        if (attr->ia_valid & ATTR_MODE) {
                err = posix_acl_chmod(inode, f2fs_get_inode_mode(inode));
-               if (err || is_inode_flag_set(inode, FI_ACL_MODE)) {
-                       inode->i_mode = F2FS_I(inode)->i_acl_mode;
+
+               if (is_inode_flag_set(inode, FI_ACL_MODE)) {
+                       if (!err)
+                               inode->i_mode = F2FS_I(inode)->i_acl_mode;
                        clear_inode_flag(inode, FI_ACL_MODE);
                }
        }
index dd50b747b671ee7ada4969e7305eec0ea0afd284..4271bcc2738d14b14d90fdf32fa1bf7b86c5533f 100644 (file)
@@ -673,7 +673,7 @@ static int __f2fs_setxattr(struct inode *inode, int index,
                }
 
                if (value && f2fs_xattr_value_same(here, value, size))
-                       goto exit;
+                       goto same;
        } else if ((flags & XATTR_REPLACE)) {
                error = -ENODATA;
                goto exit;
@@ -753,17 +753,20 @@ static int __f2fs_setxattr(struct inode *inode, int index,
        if (error)
                goto exit;
 
-       if (is_inode_flag_set(inode, FI_ACL_MODE)) {
-               inode->i_mode = F2FS_I(inode)->i_acl_mode;
-               inode->i_ctime = current_time(inode);
-               clear_inode_flag(inode, FI_ACL_MODE);
-       }
        if (index == F2FS_XATTR_INDEX_ENCRYPTION &&
                        !strcmp(name, F2FS_XATTR_NAME_ENCRYPTION_CONTEXT))
                f2fs_set_encrypted_inode(inode);
        f2fs_mark_inode_dirty_sync(inode, true);
        if (!error && S_ISDIR(inode->i_mode))
                set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_CP);
+
+same:
+       if (is_inode_flag_set(inode, FI_ACL_MODE)) {
+               inode->i_mode = F2FS_I(inode)->i_acl_mode;
+               inode->i_ctime = current_time(inode);
+               clear_inode_flag(inode, FI_ACL_MODE);
+       }
+
 exit:
        kfree(base_addr);
        return error;