fuse: make fuse_permission() RCU aware
Miklos Szeredi [Mon, 21 Mar 2011 12:58:06 +0000 (13:58 +0100)]
Only bail out of fuse_permission() on IPERM_FLAG_RCU when blocking is
actually necessary.

CC: Nick Piggin <npiggin@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>

fs/fuse/dir.c

index 8bd0ef9..3b84b91 100644 (file)
@@ -970,6 +970,14 @@ static int fuse_access(struct inode *inode, int mask)
        return err;
 }
 
+static int fuse_perm_getattr(struct inode *inode, int flags)
+{
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
+       return fuse_do_getattr(inode, NULL, NULL);
+}
+
 /*
  * Check permission.  The two basic access models of FUSE are:
  *
@@ -989,9 +997,6 @@ static int fuse_permission(struct inode *inode, int mask, unsigned int flags)
        bool refreshed = false;
        int err = 0;
 
-       if (flags & IPERM_FLAG_RCU)
-               return -ECHILD;
-
        if (!fuse_allow_task(fc, current))
                return -EACCES;
 
@@ -1000,9 +1005,15 @@ static int fuse_permission(struct inode *inode, int mask, unsigned int flags)
         */
        if ((fc->flags & FUSE_DEFAULT_PERMISSIONS) ||
            ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))) {
-               err = fuse_update_attributes(inode, NULL, NULL, &refreshed);
-               if (err)
-                       return err;
+               struct fuse_inode *fi = get_fuse_inode(inode);
+
+               if (fi->i_time < get_jiffies_64()) {
+                       refreshed = true;
+
+                       err = fuse_perm_getattr(inode, flags);
+                       if (err)
+                               return err;
+               }
        }
 
        if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
@@ -1012,7 +1023,7 @@ static int fuse_permission(struct inode *inode, int mask, unsigned int flags)
                   attributes.  This is also needed, because the root
                   node will at first have no permissions */
                if (err == -EACCES && !refreshed) {
-                       err = fuse_do_getattr(inode, NULL, NULL);
+                       err = fuse_perm_getattr(inode, flags);
                        if (!err)
                                err = generic_permission(inode, mask,
                                                        flags, NULL);
@@ -1023,13 +1034,16 @@ static int fuse_permission(struct inode *inode, int mask, unsigned int flags)
                   noticed immediately, only after the attribute
                   timeout has expired */
        } else if (mask & (MAY_ACCESS | MAY_CHDIR)) {
+               if (flags & IPERM_FLAG_RCU)
+                       return -ECHILD;
+
                err = fuse_access(inode, mask);
        } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) {
                if (!(inode->i_mode & S_IXUGO)) {
                        if (refreshed)
                                return -EACCES;
 
-                       err = fuse_do_getattr(inode, NULL, NULL);
+                       err = fuse_perm_getattr(inode, flags);
                        if (!err && !(inode->i_mode & S_IXUGO))
                                return -EACCES;
                }