From f7a1c358e51c21b80336413fb824e1a82df648b9 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 9 Aug 2016 10:07:52 +0200 Subject: [PATCH 01/11] ext2: Check return value from ext2_get_group_desc() ext2_get_group_desc() can return NULL if there is some error. This usually means there is some programming error in the ext2 driver itself but let's be defensive and handle that case. Coverity-id: 115628 Signed-off-by: Jan Kara --- fs/ext2/ialloc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index efe5fb21c5332..04e73a99902b7 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -465,6 +465,11 @@ struct inode *ext2_new_inode(struct inode *dir, umode_t mode, for (i = 0; i < sbi->s_groups_count; i++) { gdp = ext2_get_group_desc(sb, group, &bh2); + if (!gdp) { + if (++group == sbi->s_groups_count) + group = 0; + continue; + } brelse(bitmap_bh); bitmap_bh = read_inode_bitmap(sb, group); if (!bitmap_bh) { From 3cd0126dca82ecba8b2a6bf5aca91454da0a0776 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Fri, 12 Aug 2016 17:40:09 -0500 Subject: [PATCH 02/11] quota: fill in Q_XGETQSTAT inode information for inactive quotas The manpage for quotactl says that the Q_XGETQSTAT command is "useful in finding out how much space is spent to store quota information," but the current implementation does not report this info if the inode is allocated, but its quota type is not enabled. This is a change from the earlier XFS implementation, which reported information about allocated quota inodes even if their quota type was not currently active. Change quota_getstate() and quota_getstatev() to copy out the inode information if the filesystem has provided it, even if the quota type for that inode is not currently active. Signed-off-by: Eric Sandeen Reviewed-by: Bill O'Donnell Signed-off-by: Jan Kara --- fs/quota/quota.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/fs/quota/quota.c b/fs/quota/quota.c index 35df08ee9c97d..2d445425aad71 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -341,6 +341,7 @@ static int quota_getstate(struct super_block *sb, struct fs_quota_stat *fqs) struct qc_state state; int ret; + memset(&state, 0, sizeof (struct qc_state)); ret = sb->s_qcop->get_state(sb, &state); if (ret < 0) return ret; @@ -365,17 +366,19 @@ static int quota_getstate(struct super_block *sb, struct fs_quota_stat *fqs) fqs->qs_rtbtimelimit = state.s_state[type].rt_spc_timelimit; fqs->qs_bwarnlimit = state.s_state[type].spc_warnlimit; fqs->qs_iwarnlimit = state.s_state[type].ino_warnlimit; - if (state.s_state[USRQUOTA].flags & QCI_ACCT_ENABLED) { + + /* Inodes may be allocated even if inactive; copy out if present */ + if (state.s_state[USRQUOTA].ino) { fqs->qs_uquota.qfs_ino = state.s_state[USRQUOTA].ino; fqs->qs_uquota.qfs_nblks = state.s_state[USRQUOTA].blocks; fqs->qs_uquota.qfs_nextents = state.s_state[USRQUOTA].nextents; } - if (state.s_state[GRPQUOTA].flags & QCI_ACCT_ENABLED) { + if (state.s_state[GRPQUOTA].ino) { fqs->qs_gquota.qfs_ino = state.s_state[GRPQUOTA].ino; fqs->qs_gquota.qfs_nblks = state.s_state[GRPQUOTA].blocks; fqs->qs_gquota.qfs_nextents = state.s_state[GRPQUOTA].nextents; } - if (state.s_state[PRJQUOTA].flags & QCI_ACCT_ENABLED) { + if (state.s_state[PRJQUOTA].ino) { /* * Q_XGETQSTAT doesn't have room for both group and project * quotas. So, allow the project quota values to be copied out @@ -411,6 +414,7 @@ static int quota_getstatev(struct super_block *sb, struct fs_quota_statv *fqs) struct qc_state state; int ret; + memset(&state, 0, sizeof (struct qc_state)); ret = sb->s_qcop->get_state(sb, &state); if (ret < 0) return ret; @@ -435,17 +439,19 @@ static int quota_getstatev(struct super_block *sb, struct fs_quota_statv *fqs) fqs->qs_rtbtimelimit = state.s_state[type].rt_spc_timelimit; fqs->qs_bwarnlimit = state.s_state[type].spc_warnlimit; fqs->qs_iwarnlimit = state.s_state[type].ino_warnlimit; - if (state.s_state[USRQUOTA].flags & QCI_ACCT_ENABLED) { + + /* Inodes may be allocated even if inactive; copy out if present */ + if (state.s_state[USRQUOTA].ino) { fqs->qs_uquota.qfs_ino = state.s_state[USRQUOTA].ino; fqs->qs_uquota.qfs_nblks = state.s_state[USRQUOTA].blocks; fqs->qs_uquota.qfs_nextents = state.s_state[USRQUOTA].nextents; } - if (state.s_state[GRPQUOTA].flags & QCI_ACCT_ENABLED) { + if (state.s_state[GRPQUOTA].ino) { fqs->qs_gquota.qfs_ino = state.s_state[GRPQUOTA].ino; fqs->qs_gquota.qfs_nblks = state.s_state[GRPQUOTA].blocks; fqs->qs_gquota.qfs_nextents = state.s_state[GRPQUOTA].nextents; } - if (state.s_state[PRJQUOTA].flags & QCI_ACCT_ENABLED) { + if (state.s_state[PRJQUOTA].ino) { fqs->qs_pquota.qfs_ino = state.s_state[PRJQUOTA].ino; fqs->qs_pquota.qfs_nblks = state.s_state[PRJQUOTA].blocks; fqs->qs_pquota.qfs_nextents = state.s_state[PRJQUOTA].nextents; From f27792f5b7e0ba8407fcdeabb5db778fe929ab78 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 6 Sep 2016 18:04:40 +0200 Subject: [PATCH 03/11] udf: Remove useless check in udf_adinicb_write_begin() As Al properly points out, len is guaranteed to be smaller than PAGE_SIZE when we reach udf_adinicb_write_begin() as otherwise we would have converted the file to the normal format. Reported-by: Al Viro Signed-off-by: Jan Kara --- fs/udf/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/udf/file.c b/fs/udf/file.c index 6325706173275..254bc02d7e2c3 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -94,7 +94,7 @@ static int udf_adinicb_write_begin(struct file *file, return -ENOMEM; *pagep = page; - if (!PageUptodate(page) && len != PAGE_SIZE) + if (!PageUptodate(page)) __udf_adinicb_readpage(page); return 0; } From 420902c9d086848a7548c83e0a49021514bd71b7 Mon Sep 17 00:00:00 2001 From: Mike Galbraith Date: Mon, 13 Aug 2012 15:21:23 +0200 Subject: [PATCH 04/11] reiserfs: Unlock superblock before calling reiserfs_quota_on_mount() If we hold the superblock lock while calling reiserfs_quota_on_mount(), we can deadlock our own worker - mount blocks kworker/3:2, sleeps forever more. crash> ps|grep UN 715 2 3 ffff880220734d30 UN 0.0 0 0 [kworker/3:2] 9369 9341 2 ffff88021ffb7560 UN 1.3 493404 123184 Xorg 9665 9664 3 ffff880225b92ab0 UN 0.0 47368 812 udisks-daemon 10635 10403 3 ffff880222f22c70 UN 0.0 14904 936 mount crash> bt ffff880220734d30 PID: 715 TASK: ffff880220734d30 CPU: 3 COMMAND: "kworker/3:2" #0 [ffff8802244c3c20] schedule at ffffffff8144584b #1 [ffff8802244c3cc8] __rt_mutex_slowlock at ffffffff814472b3 #2 [ffff8802244c3d28] rt_mutex_slowlock at ffffffff814473f5 #3 [ffff8802244c3dc8] reiserfs_write_lock at ffffffffa05f28fd [reiserfs] #4 [ffff8802244c3de8] flush_async_commits at ffffffffa05ec91d [reiserfs] #5 [ffff8802244c3e08] process_one_work at ffffffff81073726 #6 [ffff8802244c3e68] worker_thread at ffffffff81073eba #7 [ffff8802244c3ec8] kthread at ffffffff810782e0 #8 [ffff8802244c3f48] kernel_thread_helper at ffffffff81450064 crash> rd ffff8802244c3cc8 10 ffff8802244c3cc8: ffffffff814472b3 ffff880222f23250 .rD.....P2.".... ffff8802244c3cd8: 0000000000000000 0000000000000286 ................ ffff8802244c3ce8: ffff8802244c3d30 ffff880220734d80 0=L$.....Ms .... ffff8802244c3cf8: ffff880222e8f628 0000000000000000 (.."............ ffff8802244c3d08: 0000000000000000 0000000000000002 ................ crash> struct rt_mutex ffff880222e8f628 struct rt_mutex { wait_lock = { raw_lock = { slock = 65537 } }, wait_list = { node_list = { next = 0xffff8802244c3d48, prev = 0xffff8802244c3d48 } }, owner = 0xffff880222f22c71, save_state = 0 } crash> bt 0xffff880222f22c70 PID: 10635 TASK: ffff880222f22c70 CPU: 3 COMMAND: "mount" #0 [ffff8802216a9868] schedule at ffffffff8144584b #1 [ffff8802216a9910] schedule_timeout at ffffffff81446865 #2 [ffff8802216a99a0] wait_for_common at ffffffff81445f74 #3 [ffff8802216a9a30] flush_work at ffffffff810712d3 #4 [ffff8802216a9ab0] schedule_on_each_cpu at ffffffff81074463 #5 [ffff8802216a9ae0] invalidate_bdev at ffffffff81178aba #6 [ffff8802216a9af0] vfs_load_quota_inode at ffffffff811a3632 #7 [ffff8802216a9b50] dquot_quota_on_mount at ffffffff811a375c #8 [ffff8802216a9b80] finish_unfinished at ffffffffa05dd8b0 [reiserfs] #9 [ffff8802216a9cc0] reiserfs_fill_super at ffffffffa05de825 [reiserfs] RIP: 00007f7b9303997a RSP: 00007ffff443c7a8 RFLAGS: 00010202 RAX: 00000000000000a5 RBX: ffffffff8144ef12 RCX: 00007f7b932e9ee0 RDX: 00007f7b93d9a400 RSI: 00007f7b93d9a3e0 RDI: 00007f7b93d9a3c0 RBP: 00007f7b93d9a2c0 R8: 00007f7b93d9a550 R9: 0000000000000001 R10: ffffffffc0ed040e R11: 0000000000000202 R12: 000000000000040e R13: 0000000000000000 R14: 00000000c0ed040e R15: 00007ffff443ca20 ORIG_RAX: 00000000000000a5 CS: 0033 SS: 002b Signed-off-by: Mike Galbraith Acked-by: Frederic Weisbecker Acked-by: Mike Galbraith Cc: Signed-off-by: Jan Kara --- fs/reiserfs/super.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index 7a4a85a6821e7..74d5ddd262961 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -190,7 +190,15 @@ static int remove_save_link_only(struct super_block *s, static int reiserfs_quota_on_mount(struct super_block *, int); #endif -/* look for uncompleted unlinks and truncates and complete them */ +/* + * Look for uncompleted unlinks and truncates and complete them + * + * Called with superblock write locked. If quotas are enabled, we have to + * release/retake lest we call dquot_quota_on_mount(), proceed to + * schedule_on_each_cpu() in invalidate_bdev() and deadlock waiting for the per + * cpu worklets to complete flush_async_commits() that in turn wait for the + * superblock write lock. + */ static int finish_unfinished(struct super_block *s) { INITIALIZE_PATH(path); @@ -237,7 +245,9 @@ static int finish_unfinished(struct super_block *s) quota_enabled[i] = 0; continue; } + reiserfs_write_unlock(s); ret = reiserfs_quota_on_mount(s, i); + reiserfs_write_lock(s); if (ret < 0) reiserfs_warning(s, "reiserfs-2500", "cannot turn on journaled " From 5d3ddd84eaefffd23c028bce5610dac8726f71c1 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 16 Sep 2016 00:17:21 +0100 Subject: [PATCH 05/11] udf: don't bother with full-page write optimisations in adinicb case ... it would get converted to regular if such had been attempted Signed-off-by: Al Viro Signed-off-by: Jan Kara --- fs/udf/file.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/fs/udf/file.c b/fs/udf/file.c index 254bc02d7e2c3..e855bf8d74b4b 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -105,11 +105,25 @@ static ssize_t udf_adinicb_direct_IO(struct kiocb *iocb, struct iov_iter *iter) return 0; } +static int udf_adinicb_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) +{ + struct inode *inode = page->mapping->host; + loff_t last_pos = pos + copied; + if (last_pos > inode->i_size) + i_size_write(inode, last_pos); + set_page_dirty(page); + unlock_page(page); + put_page(page); + return copied; +} + const struct address_space_operations udf_adinicb_aops = { .readpage = udf_adinicb_readpage, .writepage = udf_adinicb_writepage, .write_begin = udf_adinicb_write_begin, - .write_end = simple_write_end, + .write_end = udf_adinicb_write_end, .direct_IO = udf_adinicb_direct_IO, }; From 073931017b49d9458aa351605b43a7e34598caef Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 19 Sep 2016 17:39:09 +0200 Subject: [PATCH 06/11] posix_acl: Clear SGID bit when setting file permissions When file permissions are modified via chmod(2) and the user is not in the owning group or capable of CAP_FSETID, the setgid bit is cleared in inode_change_ok(). Setting a POSIX ACL via setxattr(2) sets the file permissions as well as the new ACL, but doesn't clear the setgid bit in a similar way; this allows to bypass the check in chmod(2). Fix that. References: CVE-2016-7097 Reviewed-by: Christoph Hellwig Reviewed-by: Jeff Layton Signed-off-by: Jan Kara Signed-off-by: Andreas Gruenbacher --- fs/9p/acl.c | 40 +++++++++++++++++---------------------- fs/btrfs/acl.c | 6 ++---- fs/ceph/acl.c | 6 ++---- fs/ext2/acl.c | 12 ++++-------- fs/ext4/acl.c | 12 ++++-------- fs/f2fs/acl.c | 6 ++---- fs/gfs2/acl.c | 12 +++--------- fs/hfsplus/posix_acl.c | 4 ++-- fs/jffs2/acl.c | 9 ++++----- fs/jfs/acl.c | 6 ++---- fs/ocfs2/acl.c | 10 ++++------ fs/orangefs/acl.c | 15 +++++---------- fs/posix_acl.c | 31 ++++++++++++++++++++++++++++++ fs/reiserfs/xattr_acl.c | 8 ++------ fs/xfs/xfs_acl.c | 13 ++++--------- include/linux/posix_acl.h | 1 + 16 files changed, 89 insertions(+), 102 deletions(-) diff --git a/fs/9p/acl.c b/fs/9p/acl.c index 5b6a1743ea17b..b3c2cc79c20d2 100644 --- a/fs/9p/acl.c +++ b/fs/9p/acl.c @@ -276,32 +276,26 @@ static int v9fs_xattr_set_acl(const struct xattr_handler *handler, switch (handler->flags) { case ACL_TYPE_ACCESS: if (acl) { - umode_t mode = inode->i_mode; - retval = posix_acl_equiv_mode(acl, &mode); - if (retval < 0) + struct iattr iattr; + + retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl); + if (retval) goto err_out; - else { - struct iattr iattr; - if (retval == 0) { - /* - * ACL can be represented - * by the mode bits. So don't - * update ACL. - */ - acl = NULL; - value = NULL; - size = 0; - } - /* Updte the mode bits */ - iattr.ia_mode = ((mode & S_IALLUGO) | - (inode->i_mode & ~S_IALLUGO)); - iattr.ia_valid = ATTR_MODE; - /* FIXME should we update ctime ? - * What is the following setxattr update the - * mode ? + if (!acl) { + /* + * ACL can be represented + * by the mode bits. So don't + * update ACL. */ - v9fs_vfs_setattr_dotl(dentry, &iattr); + value = NULL; + size = 0; } + iattr.ia_valid = ATTR_MODE; + /* FIXME should we update ctime ? + * What is the following setxattr update the + * mode ? + */ + v9fs_vfs_setattr_dotl(dentry, &iattr); } break; case ACL_TYPE_DEFAULT: diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c index 53bb7af4e5f06..247b8dfaf6e5e 100644 --- a/fs/btrfs/acl.c +++ b/fs/btrfs/acl.c @@ -79,11 +79,9 @@ static int __btrfs_set_acl(struct btrfs_trans_handle *trans, case ACL_TYPE_ACCESS: name = XATTR_NAME_POSIX_ACL_ACCESS; if (acl) { - ret = posix_acl_equiv_mode(acl, &inode->i_mode); - if (ret < 0) + ret = posix_acl_update_mode(inode, &inode->i_mode, &acl); + if (ret) return ret; - if (ret == 0) - acl = NULL; } ret = 0; break; diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c index 4f67227f69a5a..d0b6b342dff98 100644 --- a/fs/ceph/acl.c +++ b/fs/ceph/acl.c @@ -95,11 +95,9 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type) case ACL_TYPE_ACCESS: name = XATTR_NAME_POSIX_ACL_ACCESS; if (acl) { - ret = posix_acl_equiv_mode(acl, &new_mode); - if (ret < 0) + ret = posix_acl_update_mode(inode, &new_mode, &acl); + if (ret) goto out; - if (ret == 0) - acl = NULL; } break; case ACL_TYPE_DEFAULT: diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c index 42f1d1814083c..e725aa0890e00 100644 --- a/fs/ext2/acl.c +++ b/fs/ext2/acl.c @@ -190,15 +190,11 @@ ext2_set_acl(struct inode *inode, struct posix_acl *acl, int type) case ACL_TYPE_ACCESS: name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS; if (acl) { - error = posix_acl_equiv_mode(acl, &inode->i_mode); - if (error < 0) + error = posix_acl_update_mode(inode, &inode->i_mode, &acl); + if (error) return error; - else { - inode->i_ctime = CURRENT_TIME_SEC; - mark_inode_dirty(inode); - if (error == 0) - acl = NULL; - } + inode->i_ctime = CURRENT_TIME_SEC; + mark_inode_dirty(inode); } break; diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c index c6601a476c021..dfa519979038b 100644 --- a/fs/ext4/acl.c +++ b/fs/ext4/acl.c @@ -193,15 +193,11 @@ __ext4_set_acl(handle_t *handle, struct inode *inode, int type, case ACL_TYPE_ACCESS: name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS; if (acl) { - error = posix_acl_equiv_mode(acl, &inode->i_mode); - if (error < 0) + error = posix_acl_update_mode(inode, &inode->i_mode, &acl); + if (error) return error; - else { - inode->i_ctime = ext4_current_time(inode); - ext4_mark_inode_dirty(handle, inode); - if (error == 0) - acl = NULL; - } + inode->i_ctime = ext4_current_time(inode); + ext4_mark_inode_dirty(handle, inode); } break; diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index 4dcc9e28dc5c7..31344247ce891 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -210,12 +210,10 @@ static int __f2fs_set_acl(struct inode *inode, int type, case ACL_TYPE_ACCESS: name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; if (acl) { - error = posix_acl_equiv_mode(acl, &inode->i_mode); - if (error < 0) + error = posix_acl_update_mode(inode, &inode->i_mode, &acl); + if (error) return error; set_acl_inode(inode, inode->i_mode); - if (error == 0) - acl = NULL; } break; diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c index 363ba9e9d8d0a..2524807ee0703 100644 --- a/fs/gfs2/acl.c +++ b/fs/gfs2/acl.c @@ -92,17 +92,11 @@ int __gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type) if (type == ACL_TYPE_ACCESS) { umode_t mode = inode->i_mode; - error = posix_acl_equiv_mode(acl, &mode); - if (error < 0) + error = posix_acl_update_mode(inode, &inode->i_mode, &acl); + if (error) return error; - - if (error == 0) - acl = NULL; - - if (mode != inode->i_mode) { - inode->i_mode = mode; + if (mode != inode->i_mode) mark_inode_dirty(inode); - } } if (acl) { diff --git a/fs/hfsplus/posix_acl.c b/fs/hfsplus/posix_acl.c index ab7ea2506b4de..9b92058a12409 100644 --- a/fs/hfsplus/posix_acl.c +++ b/fs/hfsplus/posix_acl.c @@ -65,8 +65,8 @@ int hfsplus_set_posix_acl(struct inode *inode, struct posix_acl *acl, case ACL_TYPE_ACCESS: xattr_name = XATTR_NAME_POSIX_ACL_ACCESS; if (acl) { - err = posix_acl_equiv_mode(acl, &inode->i_mode); - if (err < 0) + err = posix_acl_update_mode(inode, &inode->i_mode, &acl); + if (err) return err; } err = 0; diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c index bc2693d562987..2a0f2a1044c16 100644 --- a/fs/jffs2/acl.c +++ b/fs/jffs2/acl.c @@ -233,9 +233,10 @@ int jffs2_set_acl(struct inode *inode, struct posix_acl *acl, int type) case ACL_TYPE_ACCESS: xprefix = JFFS2_XPREFIX_ACL_ACCESS; if (acl) { - umode_t mode = inode->i_mode; - rc = posix_acl_equiv_mode(acl, &mode); - if (rc < 0) + umode_t mode; + + rc = posix_acl_update_mode(inode, &mode, &acl); + if (rc) return rc; if (inode->i_mode != mode) { struct iattr attr; @@ -247,8 +248,6 @@ int jffs2_set_acl(struct inode *inode, struct posix_acl *acl, int type) if (rc < 0) return rc; } - if (rc == 0) - acl = NULL; } break; case ACL_TYPE_DEFAULT: diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c index 21fa92ba2c191..3a1e1554a4e35 100644 --- a/fs/jfs/acl.c +++ b/fs/jfs/acl.c @@ -78,13 +78,11 @@ static int __jfs_set_acl(tid_t tid, struct inode *inode, int type, case ACL_TYPE_ACCESS: ea_name = XATTR_NAME_POSIX_ACL_ACCESS; if (acl) { - rc = posix_acl_equiv_mode(acl, &inode->i_mode); - if (rc < 0) + rc = posix_acl_update_mode(inode, &inode->i_mode, &acl); + if (rc) return rc; inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); - if (rc == 0) - acl = NULL; } break; case ACL_TYPE_DEFAULT: diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c index 2162434728c02..164307b994052 100644 --- a/fs/ocfs2/acl.c +++ b/fs/ocfs2/acl.c @@ -241,13 +241,11 @@ int ocfs2_set_acl(handle_t *handle, case ACL_TYPE_ACCESS: name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS; if (acl) { - umode_t mode = inode->i_mode; - ret = posix_acl_equiv_mode(acl, &mode); - if (ret < 0) - return ret; + umode_t mode; - if (ret == 0) - acl = NULL; + ret = posix_acl_update_mode(inode, &mode, &acl); + if (ret) + return ret; ret = ocfs2_acl_set_mode(inode, di_bh, handle, mode); diff --git a/fs/orangefs/acl.c b/fs/orangefs/acl.c index 28f2195cd7986..7a3754488312c 100644 --- a/fs/orangefs/acl.c +++ b/fs/orangefs/acl.c @@ -73,14 +73,11 @@ int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type) case ACL_TYPE_ACCESS: name = XATTR_NAME_POSIX_ACL_ACCESS; if (acl) { - umode_t mode = inode->i_mode; - /* - * can we represent this with the traditional file - * mode permission bits? - */ - error = posix_acl_equiv_mode(acl, &mode); - if (error < 0) { - gossip_err("%s: posix_acl_equiv_mode err: %d\n", + umode_t mode; + + error = posix_acl_update_mode(inode, &mode, &acl); + if (error) { + gossip_err("%s: posix_acl_update_mode err: %d\n", __func__, error); return error; @@ -90,8 +87,6 @@ int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type) SetModeFlag(orangefs_inode); inode->i_mode = mode; mark_inode_dirty_sync(inode); - if (error == 0) - acl = NULL; } break; case ACL_TYPE_DEFAULT: diff --git a/fs/posix_acl.c b/fs/posix_acl.c index 59d47ab0791af..bfc3ec3883228 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -626,6 +626,37 @@ posix_acl_create(struct inode *dir, umode_t *mode, } EXPORT_SYMBOL_GPL(posix_acl_create); +/** + * posix_acl_update_mode - update mode in set_acl + * + * Update the file mode when setting an ACL: compute the new file permission + * bits based on the ACL. In addition, if the ACL is equivalent to the new + * file mode, set *acl to NULL to indicate that no ACL should be set. + * + * As with chmod, clear the setgit bit if the caller is not in the owning group + * or capable of CAP_FSETID (see inode_change_ok). + * + * Called from set_acl inode operations. + */ +int posix_acl_update_mode(struct inode *inode, umode_t *mode_p, + struct posix_acl **acl) +{ + umode_t mode = inode->i_mode; + int error; + + 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; +} +EXPORT_SYMBOL(posix_acl_update_mode); + /* * Fix up the uids and gids in posix acl extended attributes in place. */ diff --git a/fs/reiserfs/xattr_acl.c b/fs/reiserfs/xattr_acl.c index dbed42f755e01..27376681c6405 100644 --- a/fs/reiserfs/xattr_acl.c +++ b/fs/reiserfs/xattr_acl.c @@ -242,13 +242,9 @@ __reiserfs_set_acl(struct reiserfs_transaction_handle *th, struct inode *inode, case ACL_TYPE_ACCESS: name = XATTR_NAME_POSIX_ACL_ACCESS; if (acl) { - error = posix_acl_equiv_mode(acl, &inode->i_mode); - if (error < 0) + error = posix_acl_update_mode(inode, &inode->i_mode, &acl); + if (error) return error; - else { - if (error == 0) - acl = NULL; - } } break; case ACL_TYPE_DEFAULT: diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c index b6e527b8eccb6..8a0dec89ca560 100644 --- a/fs/xfs/xfs_acl.c +++ b/fs/xfs/xfs_acl.c @@ -257,16 +257,11 @@ xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type) return error; if (type == ACL_TYPE_ACCESS) { - umode_t mode = inode->i_mode; - error = posix_acl_equiv_mode(acl, &mode); - - if (error <= 0) { - acl = NULL; - - if (error < 0) - return error; - } + umode_t mode; + error = posix_acl_update_mode(inode, &mode, &acl); + if (error) + return error; error = xfs_set_mode(inode, mode); if (error) return error; diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h index d5d3d741f0286..bf1046d0397bb 100644 --- a/include/linux/posix_acl.h +++ b/include/linux/posix_acl.h @@ -93,6 +93,7 @@ extern int set_posix_acl(struct inode *, int, struct posix_acl *); extern int posix_acl_chmod(struct inode *, umode_t); extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **, struct posix_acl **); +extern int posix_acl_update_mode(struct inode *, umode_t *, struct posix_acl **); extern int simple_set_acl(struct inode *, struct posix_acl *, int); extern int simple_acl_create(struct inode *, struct inode *); From 69bca80744eef58fa155e8042996b968fec17b26 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 26 May 2016 14:46:43 +0200 Subject: [PATCH 07/11] xfs: Propagate dentry down to inode_change_ok() To avoid clearing of capabilities or security related extended attributes too early, inode_change_ok() will need to take dentry instead of inode. Propagate dentry down to functions calling inode_change_ok(). This is rather straightforward except for xfs_set_mode() function which does not have dentry easily available. Luckily that function does not call inode_change_ok() anyway so we just have to do a little dance with function prototypes. Acked-by: Dave Chinner Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara --- fs/xfs/xfs_file.c | 2 +- fs/xfs/xfs_inode.c | 2 +- fs/xfs/xfs_ioctl.c | 2 +- fs/xfs/xfs_iops.c | 94 +++++++++++++++++++++++++++++++--------------- fs/xfs/xfs_iops.h | 3 +- 5 files changed, 68 insertions(+), 35 deletions(-) diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index ed95e5bb04e69..79205202a29a2 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -973,7 +973,7 @@ xfs_file_fallocate( iattr.ia_valid = ATTR_SIZE; iattr.ia_size = new_size; - error = xfs_setattr_size(ip, &iattr); + error = xfs_vn_setattr_size(file_dentry(file), &iattr); if (error) goto out_unlock; } diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index e08eaea6327b5..df22e1254c98d 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1710,7 +1710,7 @@ xfs_inactive_truncate( /* * Log the inode size first to prevent stale data exposure in the event * of a system crash before the truncate completes. See the related - * comment in xfs_setattr_size() for details. + * comment in xfs_vn_setattr_size() for details. */ ip->i_d.di_size = 0; xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index 96a70fd1f5d67..0d9021f0551e4 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c @@ -720,7 +720,7 @@ xfs_ioc_space( iattr.ia_valid = ATTR_SIZE; iattr.ia_size = bf->l_start; - error = xfs_setattr_size(ip, &iattr); + error = xfs_vn_setattr_size(file_dentry(filp), &iattr); break; default: ASSERT(0); diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index ab820f84ed507..f5db392e7d1ef 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -542,6 +542,30 @@ xfs_setattr_time( inode->i_mtime = iattr->ia_mtime; } +static int +xfs_vn_change_ok( + struct dentry *dentry, + struct iattr *iattr) +{ + struct inode *inode = d_inode(dentry); + struct xfs_inode *ip = XFS_I(inode); + struct xfs_mount *mp = ip->i_mount; + + if (mp->m_flags & XFS_MOUNT_RDONLY) + return -EROFS; + + if (XFS_FORCED_SHUTDOWN(mp)) + return -EIO; + + return inode_change_ok(inode, iattr); +} + +/* + * Set non-size attributes of an inode. + * + * Caution: The caller of this function is responsible for calling + * inode_change_ok() or otherwise verifying the change is fine. + */ int xfs_setattr_nonsize( struct xfs_inode *ip, @@ -558,21 +582,6 @@ xfs_setattr_nonsize( struct xfs_dquot *udqp = NULL, *gdqp = NULL; struct xfs_dquot *olddquot1 = NULL, *olddquot2 = NULL; - trace_xfs_setattr(ip); - - /* If acls are being inherited, we already have this checked */ - if (!(flags & XFS_ATTR_NOACL)) { - if (mp->m_flags & XFS_MOUNT_RDONLY) - return -EROFS; - - if (XFS_FORCED_SHUTDOWN(mp)) - return -EIO; - - error = inode_change_ok(inode, iattr); - if (error) - return error; - } - ASSERT((mask & ATTR_SIZE) == 0); /* @@ -743,8 +752,27 @@ xfs_setattr_nonsize( return error; } +int +xfs_vn_setattr_nonsize( + struct dentry *dentry, + struct iattr *iattr) +{ + struct xfs_inode *ip = XFS_I(d_inode(dentry)); + int error; + + trace_xfs_setattr(ip); + + error = xfs_vn_change_ok(dentry, iattr); + if (error) + return error; + return xfs_setattr_nonsize(ip, iattr, 0); +} + /* * Truncate file. Must have write permission and not be a directory. + * + * Caution: The caller of this function is responsible for calling + * inode_change_ok() or otherwise verifying the change is fine. */ int xfs_setattr_size( @@ -759,18 +787,6 @@ xfs_setattr_size( uint lock_flags = 0; bool did_zeroing = false; - trace_xfs_setattr(ip); - - if (mp->m_flags & XFS_MOUNT_RDONLY) - return -EROFS; - - if (XFS_FORCED_SHUTDOWN(mp)) - return -EIO; - - error = inode_change_ok(inode, iattr); - if (error) - return error; - ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL)); ASSERT(xfs_isilocked(ip, XFS_MMAPLOCK_EXCL)); ASSERT(S_ISREG(inode->i_mode)); @@ -942,16 +958,32 @@ xfs_setattr_size( goto out_unlock; } +int +xfs_vn_setattr_size( + struct dentry *dentry, + struct iattr *iattr) +{ + struct xfs_inode *ip = XFS_I(d_inode(dentry)); + int error; + + trace_xfs_setattr(ip); + + error = xfs_vn_change_ok(dentry, iattr); + if (error) + return error; + return xfs_setattr_size(ip, iattr); +} + STATIC int xfs_vn_setattr( struct dentry *dentry, struct iattr *iattr) { - struct xfs_inode *ip = XFS_I(d_inode(dentry)); int error; if (iattr->ia_valid & ATTR_SIZE) { - uint iolock = XFS_IOLOCK_EXCL; + struct xfs_inode *ip = XFS_I(d_inode(dentry)); + uint iolock = XFS_IOLOCK_EXCL; xfs_ilock(ip, iolock); error = xfs_break_layouts(d_inode(dentry), &iolock, true); @@ -959,11 +991,11 @@ xfs_vn_setattr( xfs_ilock(ip, XFS_MMAPLOCK_EXCL); iolock |= XFS_MMAPLOCK_EXCL; - error = xfs_setattr_size(ip, iattr); + error = xfs_vn_setattr_size(dentry, iattr); } xfs_iunlock(ip, iolock); } else { - error = xfs_setattr_nonsize(ip, iattr, 0); + error = xfs_vn_setattr_nonsize(dentry, iattr); } return error; diff --git a/fs/xfs/xfs_iops.h b/fs/xfs/xfs_iops.h index a0f84abb0d09d..0259a383721a5 100644 --- a/fs/xfs/xfs_iops.h +++ b/fs/xfs/xfs_iops.h @@ -33,6 +33,7 @@ extern ssize_t xfs_vn_listxattr(struct dentry *, char *data, size_t size); extern void xfs_setattr_time(struct xfs_inode *ip, struct iattr *iattr); extern int xfs_setattr_nonsize(struct xfs_inode *ip, struct iattr *vap, int flags); -extern int xfs_setattr_size(struct xfs_inode *ip, struct iattr *vap); +extern int xfs_vn_setattr_nonsize(struct dentry *dentry, struct iattr *vap); +extern int xfs_vn_setattr_size(struct dentry *dentry, struct iattr *vap); #endif /* __XFS_IOPS_H__ */ From fd5472ed44683cf593322a2ef54b9a7675dc780a Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 26 May 2016 16:10:38 +0200 Subject: [PATCH 08/11] ceph: Propagate dentry down to inode_change_ok() To avoid clearing of capabilities or security related extended attributes too early, inode_change_ok() will need to take dentry instead of inode. ceph_setattr() has the dentry easily available but __ceph_setattr() is also called from ceph_set_acl() where dentry is not easily available. Luckily that call path does not need inode_change_ok() to be called anyway. So reorganize functions a bit so that inode_change_ok() is called only from paths where dentry is available. Reviewed-by: Christoph Hellwig Acked-by: Jeff Layton Signed-off-by: Jan Kara --- fs/ceph/acl.c | 5 +++++ fs/ceph/inode.c | 19 +++++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c index d0b6b342dff98..987044bca1c27 100644 --- a/fs/ceph/acl.c +++ b/fs/ceph/acl.c @@ -125,6 +125,11 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type) goto out_free; } + if (ceph_snap(inode) != CEPH_NOSNAP) { + ret = -EROFS; + goto out_free; + } + if (new_mode != old_mode) { newattrs.ia_mode = new_mode; newattrs.ia_valid = ATTR_MODE; diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index dd3a6dbf71ebc..2aa3c0bcf3a52 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -1905,13 +1905,6 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr) int inode_dirty_flags = 0; bool lock_snap_rwsem = false; - if (ceph_snap(inode) != CEPH_NOSNAP) - return -EROFS; - - err = inode_change_ok(inode, attr); - if (err != 0) - return err; - prealloc_cf = ceph_alloc_cap_flush(); if (!prealloc_cf) return -ENOMEM; @@ -2124,7 +2117,17 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr) */ int ceph_setattr(struct dentry *dentry, struct iattr *attr) { - return __ceph_setattr(d_inode(dentry), attr); + struct inode *inode = d_inode(dentry); + int err; + + if (ceph_snap(inode) != CEPH_NOSNAP) + return -EROFS; + + err = inode_change_ok(inode, attr); + if (err != 0) + return err; + + return __ceph_setattr(inode, attr); } /* From 62490330769c1ce5dcba3f1f3e8f4005e9b797e6 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 26 May 2016 17:12:41 +0200 Subject: [PATCH 09/11] fuse: Propagate dentry down to inode_change_ok() To avoid clearing of capabilities or security related extended attributes too early, inode_change_ok() will need to take dentry instead of inode. Propagate it down to fuse_do_setattr(). Acked-by: Miklos Szeredi Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara --- fs/fuse/dir.c | 7 ++++--- fs/fuse/file.c | 2 +- fs/fuse/fuse_i.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index c47b7780ce37b..6911bfad7834e 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1591,9 +1591,10 @@ int fuse_flush_times(struct inode *inode, struct fuse_file *ff) * vmtruncate() doesn't allow for this case, so do the rlimit checking * and the actual truncation by hand. */ -int fuse_do_setattr(struct inode *inode, struct iattr *attr, +int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, struct file *file) { + struct inode *inode = d_inode(dentry); struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); FUSE_ARGS(args); @@ -1707,9 +1708,9 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr) return -EACCES; if (attr->ia_valid & ATTR_FILE) - return fuse_do_setattr(inode, attr, attr->ia_file); + return fuse_do_setattr(entry, attr, attr->ia_file); else - return fuse_do_setattr(inode, attr, NULL); + return fuse_do_setattr(entry, attr, NULL); } static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, diff --git a/fs/fuse/file.c b/fs/fuse/file.c index f394aff59c363..4b9201b7c80b3 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -2841,7 +2841,7 @@ static void fuse_do_truncate(struct file *file) attr.ia_file = file; attr.ia_valid |= ATTR_FILE; - fuse_do_setattr(inode, &attr, file); + fuse_do_setattr(file_dentry(file), &attr, file); } static inline loff_t fuse_round_up(loff_t off) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index d98d8cc84defb..cace5526c6d65 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -958,7 +958,7 @@ bool fuse_write_update_size(struct inode *inode, loff_t pos); int fuse_flush_times(struct inode *inode, struct fuse_file *ff); int fuse_write_inode(struct inode *inode, struct writeback_control *wbc); -int fuse_do_setattr(struct inode *inode, struct iattr *attr, +int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, struct file *file); void fuse_set_initialized(struct fuse_conn *fc); From 31051c85b5e2aaaf6315f74c72a732673632a905 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 26 May 2016 16:55:18 +0200 Subject: [PATCH 10/11] fs: Give dentry to inode_change_ok() instead of inode inode_change_ok() will be resposible for clearing capabilities and IMA extended attributes and as such will need dentry. Give it as an argument to inode_change_ok() instead of an inode. Also rename inode_change_ok() to setattr_prepare() to better relect that it does also some modifications in addition to checks. Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara --- Documentation/filesystems/porting | 4 ++-- drivers/staging/lustre/lustre/llite/llite_lib.c | 2 +- fs/9p/vfs_inode.c | 2 +- fs/9p/vfs_inode_dotl.c | 2 +- fs/adfs/inode.c | 2 +- fs/affs/inode.c | 2 +- fs/attr.c | 15 +++++++++------ fs/btrfs/inode.c | 2 +- fs/ceph/inode.c | 2 +- fs/cifs/inode.c | 4 ++-- fs/ecryptfs/inode.c | 2 +- fs/exofs/inode.c | 2 +- fs/ext2/inode.c | 2 +- fs/ext4/inode.c | 2 +- fs/f2fs/file.c | 2 +- fs/fat/file.c | 2 +- fs/fuse/dir.c | 2 +- fs/gfs2/inode.c | 2 +- fs/hfs/inode.c | 2 +- fs/hfsplus/inode.c | 2 +- fs/hostfs/hostfs_kern.c | 2 +- fs/hpfs/inode.c | 2 +- fs/hugetlbfs/inode.c | 2 +- fs/jffs2/fs.c | 2 +- fs/jfs/file.c | 2 +- fs/kernfs/inode.c | 2 +- fs/libfs.c | 2 +- fs/logfs/file.c | 2 +- fs/minix/file.c | 2 +- fs/ncpfs/inode.c | 2 +- fs/nfsd/nfsproc.c | 8 +++----- fs/nilfs2/inode.c | 2 +- fs/ntfs/inode.c | 2 +- fs/ocfs2/dlmfs/dlmfs.c | 2 +- fs/ocfs2/file.c | 2 +- fs/omfs/file.c | 2 +- fs/orangefs/inode.c | 2 +- fs/overlayfs/inode.c | 2 +- fs/proc/base.c | 2 +- fs/proc/generic.c | 2 +- fs/proc/proc_sysctl.c | 2 +- fs/ramfs/file-nommu.c | 2 +- fs/reiserfs/inode.c | 2 +- fs/sysv/file.c | 2 +- fs/ubifs/file.c | 2 +- fs/udf/file.c | 2 +- fs/ufs/inode.c | 2 +- fs/utimes.c | 4 ++-- fs/xfs/xfs_iops.c | 10 ++++------ include/linux/fs.h | 2 +- mm/shmem.c | 2 +- 51 files changed, 67 insertions(+), 68 deletions(-) diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index b1bd05ea66b2d..ab320c77147a0 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting @@ -287,8 +287,8 @@ implementing on-disk size changes. Start with a copy of the old inode_setattr and vmtruncate, and the reorder the vmtruncate + foofs_vmtruncate sequence to be in order of zeroing blocks using block_truncate_page or similar helpers, size update and on finally on-disk truncation which should not fail. -inode_change_ok now includes the size checks for ATTR_SIZE and must be called -in the beginning of ->setattr unconditionally. +setattr_prepare (which used to be inode_change_ok) now includes the size checks +for ATTR_SIZE and must be called in the beginning of ->setattr unconditionally. [mandatory] diff --git a/drivers/staging/lustre/lustre/llite/llite_lib.c b/drivers/staging/lustre/lustre/llite/llite_lib.c index 546063e728db1..fb7b54eff0274 100644 --- a/drivers/staging/lustre/lustre/llite/llite_lib.c +++ b/drivers/staging/lustre/lustre/llite/llite_lib.c @@ -1192,7 +1192,7 @@ int ll_setattr_raw(struct dentry *dentry, struct iattr *attr, bool hsm_import) attr->ia_valid |= ATTR_MTIME | ATTR_CTIME; } - /* POSIX: check before ATTR_*TIME_SET set (from inode_change_ok) */ + /* POSIX: check before ATTR_*TIME_SET set (from setattr_prepare) */ if (attr->ia_valid & TIMES_SET_FLAGS) { if ((!uid_eq(current_fsuid(), inode->i_uid)) && !capable(CFS_CAP_FOWNER)) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 8b1999b528e9b..aa639bb1f2896 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -1094,7 +1094,7 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) struct p9_wstat wstat; p9_debug(P9_DEBUG_VFS, "\n"); - retval = inode_change_ok(d_inode(dentry), iattr); + retval = setattr_prepare(dentry, iattr); if (retval) return retval; diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index eeabcb0bad125..0e6ad30197110 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -558,7 +558,7 @@ int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr) p9_debug(P9_DEBUG_VFS, "\n"); - retval = inode_change_ok(inode, iattr); + retval = setattr_prepare(dentry, iattr); if (retval) return retval; diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c index 335055d828e45..f57baaa511aa5 100644 --- a/fs/adfs/inode.c +++ b/fs/adfs/inode.c @@ -303,7 +303,7 @@ adfs_notify_change(struct dentry *dentry, struct iattr *attr) unsigned int ia_valid = attr->ia_valid; int error; - error = inode_change_ok(inode, attr); + error = setattr_prepare(dentry, attr); /* * we can't change the UID or GID of any file - diff --git a/fs/affs/inode.c b/fs/affs/inode.c index 0fdb0f5b2239d..1aa243502c7ff 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -219,7 +219,7 @@ affs_notify_change(struct dentry *dentry, struct iattr *attr) pr_debug("notify_change(%lu,0x%x)\n", inode->i_ino, attr->ia_valid); - error = inode_change_ok(inode,attr); + error = setattr_prepare(dentry, attr); if (error) goto out; diff --git a/fs/attr.c b/fs/attr.c index 42bb42bb3c72c..5c45909ea2042 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -17,19 +17,22 @@ #include /** - * inode_change_ok - check if attribute changes to an inode are allowed - * @inode: inode to check + * setattr_prepare - check if attribute changes to a dentry are allowed + * @dentry: dentry to check * @attr: attributes to change * * Check if we are allowed to change the attributes contained in @attr - * in the given inode. This includes the normal unix access permission - * checks, as well as checks for rlimits and others. + * in the given dentry. This includes the normal unix access permission + * checks, as well as checks for rlimits and others. The function also clears + * SGID bit from mode if user is not allowed to set it. Also file capabilities + * and IMA extended attributes are cleared if ATTR_KILL_PRIV is set. * * Should be called as the first thing in ->setattr implementations, * possibly after taking additional locks. */ -int inode_change_ok(const struct inode *inode, struct iattr *attr) +int setattr_prepare(struct dentry *dentry, struct iattr *attr) { + struct inode *inode = d_inode(dentry); unsigned int ia_valid = attr->ia_valid; /* @@ -79,7 +82,7 @@ int inode_change_ok(const struct inode *inode, struct iattr *attr) return 0; } -EXPORT_SYMBOL(inode_change_ok); +EXPORT_SYMBOL(setattr_prepare); /** * inode_newsize_ok - may this inode be truncated to a given size diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 2f5975954ccf1..bfa0b07160d5b 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5042,7 +5042,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr) if (btrfs_root_readonly(root)) return -EROFS; - err = inode_change_ok(inode, attr); + err = setattr_prepare(dentry, attr); if (err) return err; diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 2aa3c0bcf3a52..082e82dcbaa41 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -2123,7 +2123,7 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) if (ceph_snap(inode) != CEPH_NOSNAP) return -EROFS; - err = inode_change_ok(inode, attr); + err = setattr_prepare(dentry, attr); if (err != 0) return err; diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index b87efd0c92d60..13cf507d14239 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -2154,7 +2154,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) attrs->ia_valid |= ATTR_FORCE; - rc = inode_change_ok(inode, attrs); + rc = setattr_prepare(direntry, attrs); if (rc < 0) goto out; @@ -2294,7 +2294,7 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) attrs->ia_valid |= ATTR_FORCE; - rc = inode_change_ok(inode, attrs); + rc = setattr_prepare(direntry, attrs); if (rc < 0) { free_xid(xid); return rc; diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 9d153b6a1d723..5ffba186f3521 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -927,7 +927,7 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) } mutex_unlock(&crypt_stat->cs_mutex); - rc = inode_change_ok(inode, ia); + rc = setattr_prepare(dentry, ia); if (rc) goto out; if (ia->ia_valid & ATTR_SIZE) { diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c index 9dc4c6dbf3c99..5e68daee5fe47 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -1034,7 +1034,7 @@ int exofs_setattr(struct dentry *dentry, struct iattr *iattr) if (unlikely(error)) return error; - error = inode_change_ok(inode, iattr); + error = setattr_prepare(dentry, iattr); if (unlikely(error)) return error; diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index d5c7d09919f31..65c077d9a5e93 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1580,7 +1580,7 @@ int ext2_setattr(struct dentry *dentry, struct iattr *iattr) struct inode *inode = d_inode(dentry); int error; - error = inode_change_ok(inode, iattr); + error = setattr_prepare(dentry, iattr); if (error) return error; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 3131747199e16..fdf9bee67a128 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5073,7 +5073,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) int orphan = 0; const unsigned int ia_valid = attr->ia_valid; - error = inode_change_ok(inode, attr); + error = setattr_prepare(dentry, attr); if (error) return error; diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 0e493f63ea414..26ebda971d537 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -680,7 +680,7 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) struct inode *inode = d_inode(dentry); int err; - err = inode_change_ok(inode, attr); + err = setattr_prepare(dentry, attr); if (err) return err; diff --git a/fs/fat/file.c b/fs/fat/file.c index f701856688324..c09ab4e108e5f 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -450,7 +450,7 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr) attr->ia_valid &= ~TIMES_SET_FLAGS; } - error = inode_change_ok(inode, attr); + error = setattr_prepare(dentry, attr); attr->ia_valid = ia_valid; if (error) { if (sbi->options.quiet) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 6911bfad7834e..b23502174682d 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1609,7 +1609,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) attr->ia_valid |= ATTR_FORCE; - err = inode_change_ok(inode, attr); + err = setattr_prepare(dentry, attr); if (err) return err; diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index e4da0ecd32852..d693af5ca5bcc 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1932,7 +1932,7 @@ static int gfs2_setattr(struct dentry *dentry, struct iattr *attr) if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto out; - error = inode_change_ok(inode, attr); + error = setattr_prepare(dentry, attr); if (error) goto out; diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index c6a32415735bc..09cce23864dae 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -605,7 +605,7 @@ int hfs_inode_setattr(struct dentry *dentry, struct iattr * attr) struct hfs_sb_info *hsb = HFS_SB(inode->i_sb); int error; - error = inode_change_ok(inode, attr); /* basic permission checks */ + error = setattr_prepare(dentry, attr); /* basic permission checks */ if (error) return error; diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 19462d773fe24..c43ef397a3aab 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -245,7 +245,7 @@ static int hfsplus_setattr(struct dentry *dentry, struct iattr *attr) struct inode *inode = d_inode(dentry); int error; - error = inode_change_ok(inode, attr); + error = setattr_prepare(dentry, attr); if (error) return error; diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 90e46cd752fe7..44aa96ba1df87 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -812,7 +812,7 @@ static int hostfs_setattr(struct dentry *dentry, struct iattr *attr) int fd = HOSTFS_I(inode)->fd; - err = inode_change_ok(inode, attr); + err = setattr_prepare(dentry, attr); if (err) return err; diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c index 1f3c6d76200be..b9c724ed1e7e3 100644 --- a/fs/hpfs/inode.c +++ b/fs/hpfs/inode.c @@ -273,7 +273,7 @@ int hpfs_setattr(struct dentry *dentry, struct iattr *attr) if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size > inode->i_size) goto out_unlock; - error = inode_change_ok(inode, attr); + error = setattr_prepare(dentry, attr); if (error) goto out_unlock; diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 4ea71eba40a57..fb3312f2c861c 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -672,7 +672,7 @@ static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr) BUG_ON(!inode); - error = inode_change_ok(inode, attr); + error = setattr_prepare(dentry, attr); if (error) return error; diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index ae2ebb26b4468..3773b24b4db0d 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -193,7 +193,7 @@ int jffs2_setattr(struct dentry *dentry, struct iattr *iattr) struct inode *inode = d_inode(dentry); int rc; - rc = inode_change_ok(inode, iattr); + rc = setattr_prepare(dentry, iattr); if (rc) return rc; diff --git a/fs/jfs/file.c b/fs/jfs/file.c index 7f1a585a0a947..cf62037b8a042 100644 --- a/fs/jfs/file.c +++ b/fs/jfs/file.c @@ -103,7 +103,7 @@ int jfs_setattr(struct dentry *dentry, struct iattr *iattr) struct inode *inode = d_inode(dentry); int rc; - rc = inode_change_ok(inode, iattr); + rc = setattr_prepare(dentry, iattr); if (rc) return rc; diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index 63b925d5ba1e4..df21f5b755499 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -122,7 +122,7 @@ int kernfs_iop_setattr(struct dentry *dentry, struct iattr *iattr) return -EINVAL; mutex_lock(&kernfs_mutex); - error = inode_change_ok(inode, iattr); + error = setattr_prepare(dentry, iattr); if (error) goto out; diff --git a/fs/libfs.c b/fs/libfs.c index 74dc8b9e7f53a..2b3c3ae701533 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -394,7 +394,7 @@ int simple_setattr(struct dentry *dentry, struct iattr *iattr) struct inode *inode = d_inode(dentry); int error; - error = inode_change_ok(inode, iattr); + error = setattr_prepare(dentry, iattr); if (error) return error; diff --git a/fs/logfs/file.c b/fs/logfs/file.c index f01ddfb1a03b6..5d9fe466bbc94 100644 --- a/fs/logfs/file.c +++ b/fs/logfs/file.c @@ -244,7 +244,7 @@ static int logfs_setattr(struct dentry *dentry, struct iattr *attr) struct inode *inode = d_inode(dentry); int err = 0; - err = inode_change_ok(inode, attr); + err = setattr_prepare(dentry, attr); if (err) return err; diff --git a/fs/minix/file.c b/fs/minix/file.c index 94f0eb9a6e2c1..a6a4797aa0d40 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -26,7 +26,7 @@ static int minix_setattr(struct dentry *dentry, struct iattr *attr) struct inode *inode = d_inode(dentry); int error; - error = inode_change_ok(inode, attr); + error = setattr_prepare(dentry, attr); if (error) return error; diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 1af15fcbe57b7..f6cf4c7e92b1b 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -884,7 +884,7 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) /* ageing the dentry to force validation */ ncp_age_dentry(server, dentry); - result = inode_change_ok(inode, attr); + result = setattr_prepare(dentry, attr); if (result < 0) goto out; diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index e9214768cde90..08188743db537 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -74,10 +74,10 @@ nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp, * which only requires access, and "set-[ac]time-to-X" which * requires ownership. * So if it looks like it might be "set both to the same time which - * is close to now", and if inode_change_ok fails, then we + * is close to now", and if setattr_prepare fails, then we * convert to "set to now" instead of "set to explicit time" * - * We only call inode_change_ok as the last test as technically + * We only call setattr_prepare as the last test as technically * it is not an interface that we should be using. */ #define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET) @@ -92,17 +92,15 @@ nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp, * request is. We require it be within 30 minutes of now. */ time_t delta = iap->ia_atime.tv_sec - get_seconds(); - struct inode *inode; nfserr = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP); if (nfserr) goto done; - inode = d_inode(fhp->fh_dentry); if (delta < 0) delta = -delta; if (delta < MAX_TOUCH_TIME_ERROR && - inode_change_ok(inode, iap) != 0) { + setattr_prepare(fhp->fh_dentry, iap) != 0) { /* * Turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME. * This will cause notify_change to set these times diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index af04f553d7c9d..402c325e0467f 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -829,7 +829,7 @@ int nilfs_setattr(struct dentry *dentry, struct iattr *iattr) struct super_block *sb = inode->i_sb; int err; - err = inode_change_ok(inode, iattr); + err = setattr_prepare(dentry, iattr); if (err) return err; diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index e01287c964a88..9d7a44872df5d 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -2893,7 +2893,7 @@ int ntfs_setattr(struct dentry *dentry, struct iattr *attr) int err; unsigned int ia_valid = attr->ia_valid; - err = inode_change_ok(vi, attr); + err = setattr_prepare(dentry, attr); if (err) goto out; /* We do not support NTFS ACLs yet. */ diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c index ef474cdd64047..6a4b21654037d 100644 --- a/fs/ocfs2/dlmfs/dlmfs.c +++ b/fs/ocfs2/dlmfs/dlmfs.c @@ -211,7 +211,7 @@ static int dlmfs_file_setattr(struct dentry *dentry, struct iattr *attr) struct inode *inode = d_inode(dentry); attr->ia_valid &= ~ATTR_SIZE; - error = inode_change_ok(inode, attr); + error = setattr_prepare(dentry, attr); if (error) return error; diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 4e7b0dc224505..1ab3657242e82 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -1155,7 +1155,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) if (!(attr->ia_valid & OCFS2_VALID_ATTRS)) return 0; - status = inode_change_ok(inode, attr); + status = setattr_prepare(dentry, attr); if (status) return status; diff --git a/fs/omfs/file.c b/fs/omfs/file.c index d9e26cfbb7931..bf83e6644333f 100644 --- a/fs/omfs/file.c +++ b/fs/omfs/file.c @@ -349,7 +349,7 @@ static int omfs_setattr(struct dentry *dentry, struct iattr *attr) struct inode *inode = d_inode(dentry); int error; - error = inode_change_ok(inode, attr); + error = setattr_prepare(dentry, attr); if (error) return error; diff --git a/fs/orangefs/inode.c b/fs/orangefs/inode.c index 28a0557a69be8..cff00ebac03aa 100644 --- a/fs/orangefs/inode.c +++ b/fs/orangefs/inode.c @@ -219,7 +219,7 @@ int orangefs_setattr(struct dentry *dentry, struct iattr *iattr) "orangefs_setattr: called on %s\n", dentry->d_name.name); - ret = inode_change_ok(inode, iattr); + ret = setattr_prepare(dentry, iattr); if (ret) goto out; diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 1b885c156028d..990388dba9b8a 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -52,7 +52,7 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr) * inode_newsize_ok() will always check against MAX_LFS_FILESIZE and not * check for a swapfile (which this won't be anyway). */ - err = inode_change_ok(dentry->d_inode, attr); + err = setattr_prepare(dentry, attr); if (err) return err; diff --git a/fs/proc/base.c b/fs/proc/base.c index 54e270262979b..207f36039f634 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -709,7 +709,7 @@ int proc_setattr(struct dentry *dentry, struct iattr *attr) if (attr->ia_valid & ATTR_MODE) return -EPERM; - error = inode_change_ok(inode, attr); + error = setattr_prepare(dentry, attr); if (error) return error; diff --git a/fs/proc/generic.c b/fs/proc/generic.c index c633476616e0f..23ff30e3c1e77 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -105,7 +105,7 @@ static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) struct proc_dir_entry *de = PDE(inode); int error; - error = inode_change_ok(inode, iattr); + error = setattr_prepare(dentry, iattr); if (error) return error; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 1b93650dda2fc..268fcf592b0ef 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -754,7 +754,7 @@ static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr) if (attr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) return -EPERM; - error = inode_change_ok(inode, attr); + error = setattr_prepare(dentry, attr); if (error) return error; diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c index be3ddd189cd43..2bcbf4e77982a 100644 --- a/fs/ramfs/file-nommu.c +++ b/fs/ramfs/file-nommu.c @@ -169,7 +169,7 @@ static int ramfs_nommu_setattr(struct dentry *dentry, struct iattr *ia) int ret = 0; /* POSIX UID/GID verification for setting inode attributes */ - ret = inode_change_ok(inode, ia); + ret = setattr_prepare(dentry, ia); if (ret) return ret; diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index c2c59f9ff04be..cb7f518d37ae9 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -3312,7 +3312,7 @@ int reiserfs_setattr(struct dentry *dentry, struct iattr *attr) unsigned int ia_valid; int error; - error = inode_change_ok(inode, attr); + error = setattr_prepare(dentry, attr); if (error) return error; diff --git a/fs/sysv/file.c b/fs/sysv/file.c index 82ddc09061e28..7ba997e31aeb9 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -33,7 +33,7 @@ static int sysv_setattr(struct dentry *dentry, struct iattr *attr) struct inode *inode = d_inode(dentry); int error; - error = inode_change_ok(inode, attr); + error = setattr_prepare(dentry, attr); if (error) return error; diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 7bbf420d12898..b0a6a53263f37 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -1262,7 +1262,7 @@ int ubifs_setattr(struct dentry *dentry, struct iattr *attr) dbg_gen("ino %lu, mode %#x, ia_valid %#x", inode->i_ino, inode->i_mode, attr->ia_valid); - err = inode_change_ok(inode, attr); + err = setattr_prepare(dentry, attr); if (err) return err; diff --git a/fs/udf/file.c b/fs/udf/file.c index e855bf8d74b4b..dbcb3a4a0cb99 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -261,7 +261,7 @@ static int udf_setattr(struct dentry *dentry, struct iattr *attr) struct inode *inode = d_inode(dentry); int error; - error = inode_change_ok(inode, attr); + error = setattr_prepare(dentry, attr); if (error) return error; diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 9f49431e798d6..e4a4d248a0f53 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -1208,7 +1208,7 @@ int ufs_setattr(struct dentry *dentry, struct iattr *attr) unsigned int ia_valid = attr->ia_valid; int error; - error = inode_change_ok(inode, attr); + error = setattr_prepare(dentry, attr); if (error) return error; diff --git a/fs/utimes.c b/fs/utimes.c index 794f5f5b1fb5c..c84b26e46bc5e 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -81,7 +81,7 @@ static int utimes_common(struct path *path, struct timespec *times) newattrs.ia_valid |= ATTR_MTIME_SET; } /* - * Tell inode_change_ok(), that this is an explicit time + * Tell setattr_prepare(), that this is an explicit time * update, even if neither ATTR_ATIME_SET nor ATTR_MTIME_SET * were used. */ @@ -90,7 +90,7 @@ static int utimes_common(struct path *path, struct timespec *times) /* * If times is NULL (or both times are UTIME_NOW), * then we need to check permissions, because - * inode_change_ok() won't do it. + * setattr_prepare() won't do it. */ error = -EPERM; if (IS_IMMUTABLE(inode)) diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index f5db392e7d1ef..6d0d5d413fad2 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -547,9 +547,7 @@ xfs_vn_change_ok( struct dentry *dentry, struct iattr *iattr) { - struct inode *inode = d_inode(dentry); - struct xfs_inode *ip = XFS_I(inode); - struct xfs_mount *mp = ip->i_mount; + struct xfs_mount *mp = XFS_I(d_inode(dentry))->i_mount; if (mp->m_flags & XFS_MOUNT_RDONLY) return -EROFS; @@ -557,14 +555,14 @@ xfs_vn_change_ok( if (XFS_FORCED_SHUTDOWN(mp)) return -EIO; - return inode_change_ok(inode, iattr); + return setattr_prepare(dentry, iattr); } /* * Set non-size attributes of an inode. * * Caution: The caller of this function is responsible for calling - * inode_change_ok() or otherwise verifying the change is fine. + * setattr_prepare() or otherwise verifying the change is fine. */ int xfs_setattr_nonsize( @@ -772,7 +770,7 @@ xfs_vn_setattr_nonsize( * Truncate file. Must have write permission and not be a directory. * * Caution: The caller of this function is responsible for calling - * inode_change_ok() or otherwise verifying the change is fine. + * setattr_prepare() or otherwise verifying the change is fine. */ int xfs_setattr_size( diff --git a/include/linux/fs.h b/include/linux/fs.h index 3523bf62f3286..943a21cbb469e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2994,7 +2994,7 @@ extern int buffer_migrate_page(struct address_space *, #define buffer_migrate_page NULL #endif -extern int inode_change_ok(const struct inode *, struct iattr *); +extern int setattr_prepare(struct dentry *, struct iattr *); extern int inode_newsize_ok(const struct inode *, loff_t offset); extern void setattr_copy(struct inode *inode, const struct iattr *attr); diff --git a/mm/shmem.c b/mm/shmem.c index 7f7748a0f9e1f..886b1236e68a5 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -959,7 +959,7 @@ static int shmem_setattr(struct dentry *dentry, struct iattr *attr) struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); int error; - error = inode_change_ok(inode, attr); + error = setattr_prepare(dentry, attr); if (error) return error; From 030b533c4fd4d2ec3402363323de4bb2983c9cee Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 26 May 2016 17:21:32 +0200 Subject: [PATCH 11/11] fs: Avoid premature clearing of capabilities Currently, notify_change() clears capabilities or IMA attributes by calling security_inode_killpriv() before calling into ->setattr. Thus it happens before any other permission checks in inode_change_ok() and user is thus allowed to trigger clearing of capabilities or IMA attributes for any file he can look up e.g. by calling chown for that file. This is unexpected and can lead to user DoSing a system. Fix the problem by calling security_inode_killpriv() at the end of inode_change_ok() instead of from notify_change(). At that moment we are sure user has permissions to do the requested change. References: CVE-2015-1350 Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara --- fs/attr.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/fs/attr.c b/fs/attr.c index 5c45909ea2042..83c8430d908fc 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -47,7 +47,7 @@ int setattr_prepare(struct dentry *dentry, struct iattr *attr) /* If force is set do it anyway. */ if (ia_valid & ATTR_FORCE) - return 0; + goto kill_priv; /* Make sure a caller can chown. */ if ((ia_valid & ATTR_UID) && @@ -80,6 +80,16 @@ int setattr_prepare(struct dentry *dentry, struct iattr *attr) return -EPERM; } +kill_priv: + /* User has permission for the change */ + if (ia_valid & ATTR_KILL_PRIV) { + int error; + + error = security_inode_killpriv(dentry); + if (error) + return error; + } + return 0; } EXPORT_SYMBOL(setattr_prepare); @@ -220,13 +230,11 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de if (!(ia_valid & ATTR_MTIME_SET)) attr->ia_mtime = now; if (ia_valid & ATTR_KILL_PRIV) { - attr->ia_valid &= ~ATTR_KILL_PRIV; - ia_valid &= ~ATTR_KILL_PRIV; error = security_inode_need_killpriv(dentry); - if (error > 0) - error = security_inode_killpriv(dentry); - if (error) + if (error < 0) return error; + if (error == 0) + ia_valid = attr->ia_valid &= ~ATTR_KILL_PRIV; } /*