[PATCH] Add lockless helpers for remove_suid()
Jens Axboe [Tue, 17 Oct 2006 17:50:36 +0000 (19:50 +0200)]
Right now users have to grab i_mutex before calling remove_suid(), in the
unlikely event that a call to ->setattr() may be needed. Split up the
function in two parts:

- One to check if we need to remove suid
- One to actually remove it

The first we can call lockless.

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>

include/linux/fs.h
mm/filemap.c

index d695ba2..2fe6e3f 100644 (file)
@@ -1712,6 +1712,8 @@ extern void __iget(struct inode * inode);
 extern void clear_inode(struct inode *);
 extern void destroy_inode(struct inode *);
 extern struct inode *new_inode(struct super_block *);
+extern int __remove_suid(struct dentry *, int);
+extern int should_remove_suid(struct dentry *);
 extern int remove_suid(struct dentry *);
 extern void remove_dquot_ref(struct super_block *, int, struct list_head *);
 
index 3464b68..7c7addb 100644 (file)
@@ -1884,11 +1884,10 @@ repeat:
  *     if suid or (sgid and xgrp)
  *             remove privs
  */
-int remove_suid(struct dentry *dentry)
+int should_remove_suid(struct dentry *dentry)
 {
        mode_t mode = dentry->d_inode->i_mode;
        int kill = 0;
-       int result = 0;
 
        /* suid always must be killed */
        if (unlikely(mode & S_ISUID))
@@ -1901,13 +1900,28 @@ int remove_suid(struct dentry *dentry)
        if (unlikely((mode & S_ISGID) && (mode & S_IXGRP)))
                kill |= ATTR_KILL_SGID;
 
-       if (unlikely(kill && !capable(CAP_FSETID))) {
-               struct iattr newattrs;
+       if (unlikely(kill && !capable(CAP_FSETID)))
+               return kill;
 
-               newattrs.ia_valid = ATTR_FORCE | kill;
-               result = notify_change(dentry, &newattrs);
-       }
-       return result;
+       return 0;
+}
+
+int __remove_suid(struct dentry *dentry, int kill)
+{
+       struct iattr newattrs;
+
+       newattrs.ia_valid = ATTR_FORCE | kill;
+       return notify_change(dentry, &newattrs);
+}
+
+int remove_suid(struct dentry *dentry)
+{
+       int kill = should_remove_suid(dentry);
+
+       if (unlikely(kill))
+               return __remove_suid(dentry, kill);
+
+       return 0;
 }
 EXPORT_SYMBOL(remove_suid);