It's theoretically possible for a single SETATTR call to come in that sets the mode and the uid/gid. In that case, assume the mode is correct and don't set the ATTR_KILL_S*ID bits. Doing so would trip the BUG() in notify_change. Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx> --- fs/nfsd/vfs.c | 17 +++++++++++------ 1 files changed, 11 insertions(+), 6 deletions(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 70f2c86..3b5b8cf 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -369,14 +369,19 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, if (iap->ia_valid & ATTR_MODE) { iap->ia_mode &= S_IALLUGO; imode = iap->ia_mode |= (imode & ~S_IALLUGO); + if ((iap->ia_valid & ATTR_UID) && iap->ia_uid != inode->i_uid) + iap->ia_valid |= ATTR_KILL_PRIV; + } else { + /* + * Revoke setuid/setgid bit on chown/chgrp, unless the mode + * is being explicitly set + */ + if ((iap->ia_valid & ATTR_UID) && iap->ia_uid != inode->i_uid) + iap->ia_valid |= ATTR_KILL_SUID | ATTR_KILL_PRIV; + if ((iap->ia_valid & ATTR_GID) && iap->ia_gid != inode->i_gid) + iap->ia_valid |= ATTR_KILL_SGID; } - /* Revoke setuid/setgid bit on chown/chgrp */ - if ((iap->ia_valid & ATTR_UID) && iap->ia_uid != inode->i_uid) - iap->ia_valid |= ATTR_KILL_SUID | ATTR_KILL_PRIV; - if ((iap->ia_valid & ATTR_GID) && iap->ia_gid != inode->i_gid) - iap->ia_valid |= ATTR_KILL_SGID; - /* Change the attributes. */ iap->ia_valid |= ATTR_CTIME; -- 1.5.2.1 - To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html