From: viro@parcelfarce.linux.theplanet.co.uk bernhard_heibler@gmx.de has discovered that NFS is very slow when writing to a file which has execute permissions. See http://bugme.osdl.org/show_bug.cgi?id=1936 This patch fixes remove_suid() to not try to modify the inode mode on every write to such a file. --- mm/filemap.c | 35 ++++++++++++++++++++++++----------- 1 files changed, 24 insertions(+), 11 deletions(-) diff -puN mm/filemap.c~remove_suid-fix mm/filemap.c --- 25/mm/filemap.c~remove_suid-fix 2004-01-24 00:51:56.000000000 -0800 +++ 25-akpm/mm/filemap.c 2004-01-24 00:53:54.000000000 -0800 @@ -1495,22 +1495,35 @@ repeat: return page; } +/* + * The logic we want is + * + * if suid or (sgid and xgrp) + * remove privs + */ void remove_suid(struct dentry *dentry) { - struct iattr newattrs; - struct inode *inode = dentry->d_inode; - unsigned int mode = inode->i_mode & (S_ISUID|S_ISGID|S_IXGRP); - - if (!(mode & S_IXGRP)) - mode &= S_ISUID; - - /* were any of the uid bits set? */ - if (mode && !capable(CAP_FSETID)) { - newattrs.ia_valid = ATTR_KILL_SUID|ATTR_KILL_SGID|ATTR_FORCE; + mode_t mode = dentry->d_inode->i_mode; + int kill = 0; + + /* suid always must be killed */ + if (unlikely(mode & S_ISUID)) + kill = ATTR_KILL_SUID; + + /* + * sgid without any exec bits is just a mandatory locking mark; leave + * it alone. If some exec bits are set, it's a real sgid; kill it. + */ + if (unlikely((mode & S_ISGID) && (mode & S_IXGRP))) + kill |= ATTR_KILL_SGID; + + if (unlikely(kill && !capable(CAP_FSETID))) { + struct iattr newattrs; + + newattrs.ia_valid = ATTR_FORCE | kill; notify_change(dentry, &newattrs); } } - EXPORT_SYMBOL(remove_suid); /* _