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)
committerJaegeuk Kim <jaegeuk@kernel.org>
Wed, 27 Jan 2021 23:20:00 +0000 (15:20 -0800)
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>
fs/f2fs/acl.c
fs/f2fs/file.c
fs/f2fs/xattr.c

index 1e5e9b1136ee1c37c460578e364eac49e8c8f02d..732ec10e78909787a23b6e0f567349e997bfa759 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 eced14882fc14ff9701a4900f0f4427d778c7f57..2ddc4baaf173c2fe9647519dd1e8324e8894a550 100644 (file)
@@ -850,7 +850,6 @@ static void __setattr_copy(struct inode *inode, const struct iattr *attr)
 
                if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
                        mode &= ~S_ISGID;
-               inode->i_mode = (inode->i_mode & S_IRWXUGO) | (mode & ~S_IRWXUGO);
                set_acl_inode(inode, mode);
        }
 }
@@ -950,8 +949,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 65afcc3cc68a0e225912da649bfc90847fec0bf8..2086bef6c15471047c5bbf8d2637d09d3d8fd5e4 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;
@@ -738,17 +738,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;