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);
 
 /*

_