]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] remove_suid() fix
authorAndrew Morton <akpm@osdl.org>
Wed, 4 Feb 2004 02:44:02 +0000 (18:44 -0800)
committerLinus Torvalds <torvalds@home.osdl.org>
Wed, 4 Feb 2004 02:44:02 +0000 (18:44 -0800)
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

index e07154c4c97949a378fbb7b3b4fec621b3295fb9..b57186d5e187110c394f9454f19a499f826f09d6 100644 (file)
@@ -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);
+       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;
 
-       if (!(mode & S_IXGRP))
-               mode &= S_ISUID;
+       /*
+        * 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;
 
-       /* were any of the uid bits set? */
-       if (mode && !capable(CAP_FSETID)) {
-               newattrs.ia_valid = ATTR_KILL_SUID|ATTR_KILL_SGID|ATTR_FORCE;
+       if (unlikely(kill && !capable(CAP_FSETID))) {
+               struct iattr newattrs;
+
+               newattrs.ia_valid = ATTR_FORCE | kill;
                notify_change(dentry, &newattrs);
        }
 }
-
 EXPORT_SYMBOL(remove_suid);
 
 /*