fs: provide rcu-walk aware permission i_ops
authorNick Piggin <npiggin@kernel.dk>
Fri, 7 Jan 2011 06:49:58 +0000 (17:49 +1100)
committerNick Piggin <npiggin@kernel.dk>
Fri, 7 Jan 2011 06:50:29 +0000 (17:50 +1100)
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
60 files changed:
Documentation/filesystems/Locking
Documentation/filesystems/path-lookup.txt
Documentation/filesystems/porting
Documentation/filesystems/vfs.txt
drivers/staging/smbfs/file.c
fs/9p/acl.c
fs/9p/acl.h
fs/afs/internal.h
fs/afs/security.c
fs/bad_inode.c
fs/btrfs/acl.c
fs/btrfs/ctree.h
fs/btrfs/inode.c
fs/ceph/inode.c
fs/ceph/super.h
fs/cifs/cifsfs.c
fs/coda/dir.c
fs/coda/pioctl.c
fs/ecryptfs/inode.c
fs/ext2/acl.c
fs/ext2/acl.h
fs/ext3/acl.c
fs/ext3/acl.h
fs/ext4/acl.c
fs/ext4/acl.h
fs/fuse/dir.c
fs/generic_acl.c
fs/gfs2/acl.c
fs/gfs2/acl.h
fs/gfs2/file.c
fs/gfs2/inode.c
fs/gfs2/inode.h
fs/gfs2/ops_inode.c
fs/hostfs/hostfs_kern.c
fs/hpfs/namei.c
fs/jffs2/acl.c
fs/jffs2/acl.h
fs/jfs/acl.c
fs/jfs/jfs_acl.h
fs/logfs/dir.c
fs/namei.c
fs/nfs/dir.c
fs/nilfs2/inode.c
fs/nilfs2/nilfs.h
fs/ocfs2/acl.c
fs/ocfs2/acl.h
fs/ocfs2/file.c
fs/ocfs2/file.h
fs/proc/base.c
fs/proc/proc_sysctl.c
fs/reiserfs/xattr.c
fs/sysfs/inode.c
fs/sysfs/sysfs.h
fs/xfs/linux-2.6/xfs_acl.c
fs/xfs/xfs_acl.h
include/linux/coda_linux.h
include/linux/fs.h
include/linux/generic_acl.h
include/linux/nfs_fs.h
include/linux/reiserfs_xattr.h

index e90ffe61eb656b733356deacafe64ca98ee57b8d..977d8919cc69c80e9dd3f0e4a4e5327bac608376 100644 (file)
@@ -47,8 +47,8 @@ ata *);
        void * (*follow_link) (struct dentry *, struct nameidata *);
        void (*put_link) (struct dentry *, struct nameidata *, void *);
        void (*truncate) (struct inode *);
-       int (*permission) (struct inode *, int, struct nameidata *);
-       int (*check_acl)(struct inode *, int);
+       int (*permission) (struct inode *, int, unsigned int);
+       int (*check_acl)(struct inode *, int, unsigned int);
        int (*setattr) (struct dentry *, struct iattr *);
        int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *);
        int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
@@ -76,7 +76,7 @@ follow_link:  no
 put_link:      no
 truncate:      yes             (see below)
 setattr:       yes
-permission:    no
+permission:    no (may not block if called in rcu-walk mode)
 check_acl:     no
 getattr:       no
 setxattr:      yes
index 8789d1810bed3d7f73d076431dfda285efd61eae..eb59c8b44be99f3bf033142c90cd565b8f2814d3 100644 (file)
@@ -316,11 +316,9 @@ The detailed design for rcu-walk is like this:
 
 The cases where rcu-walk cannot continue are:
 * NULL dentry (ie. any uncached path element)
-* parent with d_inode->i_op->permission or ACLs
 * Following links
 
-In future patches, permission checks become rcu-walk aware. It may be possible
-eventually to make following links rcu-walk aware.
+It may be possible eventually to make following links rcu-walk aware.
 
 Uncached path elements will always require dropping to ref-walk mode, at the
 very least because i_mutex needs to be grabbed, and objects allocated.
@@ -336,9 +334,49 @@ or stored into. The result is massive improvements in performance and
 scalability of path resolution.
 
 
+Interesting statistics
+======================
+
+The following table gives rcu lookup statistics for a few simple workloads
+(2s12c24t Westmere, debian non-graphical system). Ungraceful are attempts to
+drop rcu that fail due to d_seq failure and requiring the entire path lookup
+again. Other cases are successful rcu-drops that are required before the final
+element, nodentry for missing dentry, revalidate for filesystem revalidate
+routine requiring rcu drop, permission for permission check requiring drop,
+and link for symlink traversal requiring drop.
+
+     rcu-lookups     restart  nodentry          link  revalidate  permission
+bootup     47121           0      4624          1010       10283        7852
+dbench  25386793           0   6778659(26.7%)     55         549        1156
+kbuild   2696672          10     64442(2.3%)  108764(4.0%)     1        1590
+git diff   39605           0        28             2           0         106
+vfstest 24185492        4945    708725(2.9%) 1076136(4.4%)     0        2651
+
+What this shows is that failed rcu-walk lookups, ie. ones that are restarted
+entirely with ref-walk, are quite rare. Even the "vfstest" case which
+specifically has concurrent renames/mkdir/rmdir/ creat/unlink/etc to excercise
+such races is not showing a huge amount of restarts.
+
+Dropping from rcu-walk to ref-walk mean that we have encountered a dentry where
+the reference count needs to be taken for some reason. This is either because
+we have reached the target of the path walk, or because we have encountered a
+condition that can't be resolved in rcu-walk mode.  Ideally, we drop rcu-walk
+only when we have reached the target dentry, so the other statistics show where
+this does not happen.
+
+Note that a graceful drop from rcu-walk mode due to something such as the
+dentry not existing (which can be common) is not necessarily a failure of
+rcu-walk scheme, because some elements of the path may have been walked in
+rcu-walk mode. The further we get from common path elements (such as cwd or
+root), the less contended the dentry is likely to be. The closer we are to
+common path elements, the more likely they will exist in dentry cache.
+
+
 Papers and other documentation on dcache locking
 ================================================
 
 1. Scaling dcache with RCU (http://linuxjournal.com/article.php?sid=7124).
 
 2. http://lse.sourceforge.net/locking/dcache/dcache.html
+
+
index cd9756a2709d0f1643ae398d22d5e01a2feabc06..07a32b42cf9ce364d129a57a9578907817e97727 100644 (file)
@@ -379,4 +379,9 @@ where possible.
 the filesystem provides it), which requires dropping out of rcu-walk mode. This
 may now be called in rcu-walk mode (nd->flags & LOOKUP_RCU). -ECHILD should be
 returned if the filesystem cannot handle rcu-walk. See
+Documentation/filesystems/vfs.txt for more details.
+
+       permission and check_acl are inode permission checks that are called
+on many or all directory inodes on the way down a path walk (to check for
+exec permission). These must now be rcu-walk aware (flags & IPERM_RCU). See
 Documentation/filesystems/vfs.txt for more details.
index c936b4912383d466608189c60781fd0bb0f12210..fbb324e2bd43e6d1926a13f344ea5d55f6ce9360 100644 (file)
@@ -325,7 +325,8 @@ struct inode_operations {
         void * (*follow_link) (struct dentry *, struct nameidata *);
         void (*put_link) (struct dentry *, struct nameidata *, void *);
        void (*truncate) (struct inode *);
-       int (*permission) (struct inode *, int, struct nameidata *);
+       int (*permission) (struct inode *, int, unsigned int);
+       int (*check_acl)(struct inode *, int, unsigned int);
        int (*setattr) (struct dentry *, struct iattr *);
        int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
        int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
@@ -414,6 +415,13 @@ otherwise noted.
   permission: called by the VFS to check for access rights on a POSIX-like
        filesystem.
 
+       May be called in rcu-walk mode (flags & IPERM_RCU). If in rcu-walk
+       mode, the filesystem must check the permission without blocking or
+       storing to the inode.
+
+       If a situation is encountered that rcu-walk cannot handle, return
+       -ECHILD and it will be called again in ref-walk mode.
+
   setattr: called by the VFS to set attributes for a file. This method
        is called by chmod(2) and related system calls.
 
index 5dcd19c60eb95cb7c60e8e44fbe39aeccdc7bb17..31372e7b12de7ed1a2cddc39ecf4584531eaeca1 100644 (file)
@@ -407,11 +407,14 @@ smb_file_release(struct inode *inode, struct file * file)
  * privileges, so we need our own check for this.
  */
 static int
-smb_file_permission(struct inode *inode, int mask)
+smb_file_permission(struct inode *inode, int mask, unsigned int flags)
 {
        int mode = inode->i_mode;
        int error = 0;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        VERBOSE("mode=%x, mask=%x\n", mode, mask);
 
        /* Look at user permissions */
index 12d602351dbe585cdb3b29d46306d92ab049590a..6e58c4ca1e6e15c06d3a04b88f7a6e589dd5132d 100644 (file)
@@ -91,11 +91,14 @@ static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type)
        return acl;
 }
 
-int v9fs_check_acl(struct inode *inode, int mask)
+int v9fs_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
        struct posix_acl *acl;
        struct v9fs_session_info *v9ses;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        v9ses = v9fs_inode2v9ses(inode);
        if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) {
                /*
index 59e18c2e8c7e6ba3b7e1fd4386155bad519adc9e..7ef3ac9f6d95ba8b2111929c5340ae63512151a9 100644 (file)
@@ -16,7 +16,7 @@
 
 #ifdef CONFIG_9P_FS_POSIX_ACL
 extern int v9fs_get_acl(struct inode *, struct p9_fid *);
-extern int v9fs_check_acl(struct inode *inode, int mask);
+extern int v9fs_check_acl(struct inode *inode, int mask, unsigned int flags);
 extern int v9fs_acl_chmod(struct dentry *);
 extern int v9fs_set_create_acl(struct dentry *,
                               struct posix_acl *, struct posix_acl *);
index cca8eef736fcf2b6f9a9516e96c4fc06c8d228be..6d4bc1c8ff60c8573a67b4dff81997683822ed66 100644 (file)
@@ -624,7 +624,7 @@ extern void afs_clear_permits(struct afs_vnode *);
 extern void afs_cache_permit(struct afs_vnode *, struct key *, long);
 extern void afs_zap_permits(struct rcu_head *);
 extern struct key *afs_request_key(struct afs_cell *);
-extern int afs_permission(struct inode *, int);
+extern int afs_permission(struct inode *, int, unsigned int);
 
 /*
  * server.c
index bb4ed144d0e446c627fb8b4e391664058d094b87..f44b9d35537713c4bba6013e74be36013f2cca6e 100644 (file)
@@ -285,13 +285,16 @@ static int afs_check_permit(struct afs_vnode *vnode, struct key *key,
  * - AFS ACLs are attached to directories only, and a file is controlled by its
  *   parent directory's ACL
  */
-int afs_permission(struct inode *inode, int mask)
+int afs_permission(struct inode *inode, int mask, unsigned int flags)
 {
        struct afs_vnode *vnode = AFS_FS_I(inode);
        afs_access_t uninitialized_var(access);
        struct key *key;
        int ret;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        _enter("{{%x:%u},%lx},%x,",
               vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask);
 
@@ -347,7 +350,7 @@ int afs_permission(struct inode *inode, int mask)
        }
 
        key_put(key);
-       ret = generic_permission(inode, mask, NULL);
+       ret = generic_permission(inode, mask, flags, NULL);
        _leave(" = %d", ret);
        return ret;
 
index f024d8aaddefff929ec7d557fdfc6449ab7825b8..9ad2369d9e35e0651885a78dc3648ef9dbe8c412 100644 (file)
@@ -229,8 +229,11 @@ static int bad_inode_readlink(struct dentry *dentry, char __user *buffer,
        return -EIO;
 }
 
-static int bad_inode_permission(struct inode *inode, int mask)
+static int bad_inode_permission(struct inode *inode, int mask, unsigned int flags)
 {
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        return -EIO;
 }
 
index 2222d161c7b68111431031e007b019d7de629d3e..cb518a4b917c8ce078d89e6261f62bdeb42a4c95 100644 (file)
@@ -185,13 +185,15 @@ static int btrfs_xattr_acl_set(struct dentry *dentry, const char *name,
        return ret;
 }
 
-int btrfs_check_acl(struct inode *inode, int mask)
+int btrfs_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
        struct posix_acl *acl;
        int error = -EAGAIN;
 
-       acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS);
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
 
+       acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS);
        if (IS_ERR(acl))
                return PTR_ERR(acl);
        if (acl) {
index af52f6d7a4d8ed8269f0cf835ab09c14d9a5c8a1..a142d204b5260b7b89e2dd7154e8bb5992a97002 100644 (file)
@@ -2544,7 +2544,7 @@ int btrfs_sync_fs(struct super_block *sb, int wait);
 
 /* acl.c */
 #ifdef CONFIG_BTRFS_FS_POSIX_ACL
-int btrfs_check_acl(struct inode *inode, int mask);
+int btrfs_check_acl(struct inode *inode, int mask, unsigned int flags);
 #else
 #define btrfs_check_acl NULL
 #endif
index 63e4546b478acd144ad0c73fc886163be876f810..5cf0db0081f9f0da661451a6b7fe958dd042557e 100644 (file)
@@ -7211,11 +7211,14 @@ static int btrfs_set_page_dirty(struct page *page)
        return __set_page_dirty_nobuffers(page);
 }
 
-static int btrfs_permission(struct inode *inode, int mask)
+static int btrfs_permission(struct inode *inode, int mask, unsigned int flags)
 {
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        if ((BTRFS_I(inode)->flags & BTRFS_INODE_READONLY) && (mask & MAY_WRITE))
                return -EACCES;
-       return generic_permission(inode, mask, btrfs_check_acl);
+       return generic_permission(inode, mask, flags, btrfs_check_acl);
 }
 
 static const struct inode_operations btrfs_dir_inode_operations = {
index 47f8c8baf3b5f500b7bf90b1ce7fd5efc6cfad72..e61de4f7b99d0c737023bb4e2d926c103fdf570f 100644 (file)
@@ -1781,12 +1781,17 @@ int ceph_do_getattr(struct inode *inode, int mask)
  * Check inode permissions.  We verify we have a valid value for
  * the AUTH cap, then call the generic handler.
  */
-int ceph_permission(struct inode *inode, int mask)
+int ceph_permission(struct inode *inode, int mask, unsigned int flags)
 {
-       int err = ceph_do_getattr(inode, CEPH_CAP_AUTH_SHARED);
+       int err;
+
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
+       err = ceph_do_getattr(inode, CEPH_CAP_AUTH_SHARED);
 
        if (!err)
-               err = generic_permission(inode, mask, NULL);
+               err = generic_permission(inode, mask, flags, NULL);
        return err;
 }
 
index 7f01728a46571f2fa22a47fbd062f6eadf4cba83..4553d8829edb100e515a6f10b6e097359bdf3060 100644 (file)
@@ -665,7 +665,7 @@ extern void ceph_queue_invalidate(struct inode *inode);
 extern void ceph_queue_writeback(struct inode *inode);
 
 extern int ceph_do_getattr(struct inode *inode, int mask);
-extern int ceph_permission(struct inode *inode, int mask);
+extern int ceph_permission(struct inode *inode, int mask, unsigned int flags);
 extern int ceph_setattr(struct dentry *dentry, struct iattr *attr);
 extern int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry,
                        struct kstat *stat);
index 223717dcc401937323ee479fc40b9bf5e4816f71..8e21e0fe65d5d663209ea114f57978b880104e49 100644 (file)
@@ -283,10 +283,13 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
        return 0;
 }
 
-static int cifs_permission(struct inode *inode, int mask)
+static int cifs_permission(struct inode *inode, int mask, unsigned int flags)
 {
        struct cifs_sb_info *cifs_sb;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        cifs_sb = CIFS_SB(inode->i_sb);
 
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) {
@@ -298,7 +301,7 @@ static int cifs_permission(struct inode *inode, int mask)
                on the client (above and beyond ACL on servers) for
                servers which do not support setting and viewing mode bits,
                so allowing client to check permissions is useful */
-               return generic_permission(inode, mask, NULL);
+               return generic_permission(inode, mask, flags, NULL);
 }
 
 static struct kmem_cache *cifs_inode_cachep;
index 619a8303766e36ace280a9a4ffb25b96755aeb2b..29badd91360fdca54dc40c1b2a4d8f7af12b88c8 100644 (file)
@@ -135,10 +135,13 @@ exit:
 }
 
 
-int coda_permission(struct inode *inode, int mask)
+int coda_permission(struct inode *inode, int mask, unsigned int flags)
 {
        int error;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
  
        if (!mask)
index 2fd89b5c5c7b06eab16db7a6b644520ef8476462..741f0bd03918e5eb6190c52da211b4dc0eb6ea02 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/coda_psdev.h>
 
 /* pioctl ops */
-static int coda_ioctl_permission(struct inode *inode, int mask);
+static int coda_ioctl_permission(struct inode *inode, int mask, unsigned int flags);
 static long coda_pioctl(struct file *filp, unsigned int cmd,
                        unsigned long user_data);
 
@@ -41,8 +41,10 @@ const struct file_operations coda_ioctl_operations = {
 };
 
 /* the coda pioctl inode ops */
-static int coda_ioctl_permission(struct inode *inode, int mask)
+static int coda_ioctl_permission(struct inode *inode, int mask, unsigned int flags)
 {
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
        return (mask & MAY_EXEC) ? -EACCES : 0;
 }
 
index f91b35db4c6e987e3ba7bfc2196045989f874ec7..337352a947511f92c584e7ad0d589eb6106010f4 100644 (file)
@@ -980,8 +980,10 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
 }
 
 static int
-ecryptfs_permission(struct inode *inode, int mask)
+ecryptfs_permission(struct inode *inode, int mask, unsigned int flags)
 {
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
        return inode_permission(ecryptfs_inode_to_lower(inode), mask);
 }
 
index 2bcc0431badac87d6a8e9cdb2b0adb737836ebf4..dd9bb3f0c8d7d15a89d806fcca1272d295edfbc0 100644 (file)
@@ -232,10 +232,14 @@ ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
 }
 
 int
-ext2_check_acl(struct inode *inode, int mask)
+ext2_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
-       struct posix_acl *acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
+       struct posix_acl *acl;
+
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
 
+       acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
        if (IS_ERR(acl))
                return PTR_ERR(acl);
        if (acl) {
index 3ff6cbb9ac4486e2c6bc4e7b0ca83485ab01fd1f..c939b7b1209905cba3aa6622e04ad48600ede984 100644 (file)
@@ -54,7 +54,7 @@ static inline int ext2_acl_count(size_t size)
 #ifdef CONFIG_EXT2_FS_POSIX_ACL
 
 /* acl.c */
-extern int ext2_check_acl (struct inode *, int);
+extern int ext2_check_acl (struct inode *, int, unsigned int);
 extern int ext2_acl_chmod (struct inode *);
 extern int ext2_init_acl (struct inode *, struct inode *);
 
index 8a11fe212183529e225dee8a812264de5053b9d0..9e49da8302d395c98ed3296b1f5c001c7a44c393 100644 (file)
@@ -240,10 +240,14 @@ ext3_set_acl(handle_t *handle, struct inode *inode, int type,
 }
 
 int
-ext3_check_acl(struct inode *inode, int mask)
+ext3_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
-       struct posix_acl *acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
+       struct posix_acl *acl;
+
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
 
+       acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
        if (IS_ERR(acl))
                return PTR_ERR(acl);
        if (acl) {
index 597334626de93aee68631305d98a823e815058c4..5faf8048e906403bb9065c006d3513e438f4d2e7 100644 (file)
@@ -54,7 +54,7 @@ static inline int ext3_acl_count(size_t size)
 #ifdef CONFIG_EXT3_FS_POSIX_ACL
 
 /* acl.c */
-extern int ext3_check_acl (struct inode *, int);
+extern int ext3_check_acl (struct inode *, int, unsigned int);
 extern int ext3_acl_chmod (struct inode *);
 extern int ext3_init_acl (handle_t *, struct inode *, struct inode *);
 
index 5e2ed4504eadd85401d9f86d5e3784ecd2e2e7c1..373dcaeedba921e5c5a02eef301f7e99f5b4d9e4 100644 (file)
@@ -238,10 +238,14 @@ ext4_set_acl(handle_t *handle, struct inode *inode, int type,
 }
 
 int
-ext4_check_acl(struct inode *inode, int mask)
+ext4_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
-       struct posix_acl *acl = ext4_get_acl(inode, ACL_TYPE_ACCESS);
+       struct posix_acl *acl;
+
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
 
+       acl = ext4_get_acl(inode, ACL_TYPE_ACCESS);
        if (IS_ERR(acl))
                return PTR_ERR(acl);
        if (acl) {
index 9d843d5deac402593b8b557e27c6ed25aafe401d..dec821168fd49a86ecf7097e508b5df8dac12a46 100644 (file)
@@ -54,7 +54,7 @@ static inline int ext4_acl_count(size_t size)
 #ifdef CONFIG_EXT4_FS_POSIX_ACL
 
 /* acl.c */
-extern int ext4_check_acl(struct inode *, int);
+extern int ext4_check_acl(struct inode *, int, unsigned int);
 extern int ext4_acl_chmod(struct inode *);
 extern int ext4_init_acl(handle_t *, struct inode *, struct inode *);
 
index 07f4b5e675fcdb3f47c7886e42a5d3588e5f0046..f738599fd8cd0b18e52d298513f5230c0359503c 100644 (file)
@@ -985,12 +985,15 @@ static int fuse_access(struct inode *inode, int mask)
  * access request is sent.  Execute permission is still checked
  * locally based on file mode.
  */
-static int fuse_permission(struct inode *inode, int mask)
+static int fuse_permission(struct inode *inode, int mask, unsigned int flags)
 {
        struct fuse_conn *fc = get_fuse_conn(inode);
        bool refreshed = false;
        int err = 0;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        if (!fuse_allow_task(fc, current))
                return -EACCES;
 
@@ -1005,7 +1008,7 @@ static int fuse_permission(struct inode *inode, int mask)
        }
 
        if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
-               err = generic_permission(inode, mask, NULL);
+               err = generic_permission(inode, mask, flags, NULL);
 
                /* If permission is denied, try to refresh file
                   attributes.  This is also needed, because the root
@@ -1013,7 +1016,8 @@ static int fuse_permission(struct inode *inode, int mask)
                if (err == -EACCES && !refreshed) {
                        err = fuse_do_getattr(inode, NULL, NULL);
                        if (!err)
-                               err = generic_permission(inode, mask, NULL);
+                               err = generic_permission(inode, mask,
+                                                       flags, NULL);
                }
 
                /* Note: the opposite of the above test does not
index 6bc9e3a5a693b0fa2d5d8f09349f42b9d44ea72f..628004282130029d3d9b7bf06707a48161abe599 100644 (file)
@@ -190,10 +190,14 @@ generic_acl_chmod(struct inode *inode)
 }
 
 int
-generic_check_acl(struct inode *inode, int mask)
+generic_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
-       struct posix_acl *acl = get_cached_acl(inode, ACL_TYPE_ACCESS);
+       struct posix_acl *acl;
+
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
 
+       acl = get_cached_acl(inode, ACL_TYPE_ACCESS);
        if (acl) {
                int error = posix_acl_permission(inode, acl, mask);
                posix_acl_release(acl);
index 48171f4c943dbb2523a22011a25aca402fe3e6e5..7118f1a780a9592ac67114f25ca7e9e040f937d4 100644 (file)
@@ -75,11 +75,14 @@ static struct posix_acl *gfs2_acl_get(struct gfs2_inode *ip, int type)
  * Returns: errno
  */
 
-int gfs2_check_acl(struct inode *inode, int mask)
+int gfs2_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
        struct posix_acl *acl;
        int error;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        acl = gfs2_acl_get(GFS2_I(inode), ACL_TYPE_ACCESS);
        if (IS_ERR(acl))
                return PTR_ERR(acl);
index b522b0cb39eaa12798649c2a8bd2b1e952a746f9..a93907c8159b78d8eaafa09232574c367e02c124 100644 (file)
@@ -16,7 +16,7 @@
 #define GFS2_POSIX_ACL_DEFAULT         "posix_acl_default"
 #define GFS2_ACL_MAX_ENTRIES           25
 
-extern int gfs2_check_acl(struct inode *inode, int mask);
+extern int gfs2_check_acl(struct inode *inode, int mask, unsigned int);
 extern int gfs2_acl_create(struct gfs2_inode *dip, struct inode *inode);
 extern int gfs2_acl_chmod(struct gfs2_inode *ip, struct iattr *attr);
 extern const struct xattr_handler gfs2_xattr_system_handler;
index aa996471ec5c961d7d94c62c10a20d29ac8e1bbb..fca6689e12e6d2e02a5139415f86fbaf1e0f3bae 100644 (file)
@@ -241,7 +241,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
            !capable(CAP_LINUX_IMMUTABLE))
                goto out;
        if (!IS_IMMUTABLE(inode)) {
-               error = gfs2_permission(inode, MAY_WRITE);
+               error = gfs2_permission(inode, MAY_WRITE, 0);
                if (error)
                        goto out;
        }
index e1213f7f92179aa2472304ff0db4294be66040d8..6163be9686be55318a1a84bdd5e8e4fc29501882 100644 (file)
@@ -509,7 +509,7 @@ struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name,
        }
 
        if (!is_root) {
-               error = gfs2_permission(dir, MAY_EXEC);
+               error = gfs2_permission(dir, MAY_EXEC, 0);
                if (error)
                        goto out;
        }
@@ -539,7 +539,7 @@ static int create_ok(struct gfs2_inode *dip, const struct qstr *name,
 {
        int error;
 
-       error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC);
+       error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, 0);
        if (error)
                return error;
 
index d8499fadcc53915837a8ae7e7bdcc77213f825cc..732a183efdb3d183dd43fc05a70ddbdc80df05e6 100644 (file)
@@ -113,7 +113,7 @@ extern struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name,
 extern struct inode *gfs2_createi(struct gfs2_holder *ghs,
                                  const struct qstr *name,
                                  unsigned int mode, dev_t dev);
-extern int gfs2_permission(struct inode *inode, int mask);
+extern int gfs2_permission(struct inode *inode, int mask, unsigned int flags);
 extern int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr);
 extern struct inode *gfs2_lookup_simple(struct inode *dip, const char *name);
 extern void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf);
index f28f89796f4dca6835f16bcb01de48121ce38d6b..5c63a06fcd7c9c7455f54051081ec6d66f0a133f 100644 (file)
@@ -166,7 +166,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
        if (error)
                goto out_child;
 
-       error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC);
+       error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC, 0);
        if (error)
                goto out_gunlock;
 
@@ -289,7 +289,7 @@ static int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name,
        if (IS_APPEND(&dip->i_inode))
                return -EPERM;
 
-       error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC);
+       error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, 0);
        if (error)
                return error;
 
@@ -822,7 +822,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
                        }
                }
        } else {
-               error = gfs2_permission(ndir, MAY_WRITE | MAY_EXEC);
+               error = gfs2_permission(ndir, MAY_WRITE | MAY_EXEC, 0);
                if (error)
                        goto out_gunlock;
 
@@ -857,7 +857,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
        /* Check out the dir to be renamed */
 
        if (dir_rename) {
-               error = gfs2_permission(odentry->d_inode, MAY_WRITE);
+               error = gfs2_permission(odentry->d_inode, MAY_WRITE, 0);
                if (error)
                        goto out_gunlock;
        }
@@ -1041,13 +1041,17 @@ static void gfs2_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
  * Returns: errno
  */
 
-int gfs2_permission(struct inode *inode, int mask)
+int gfs2_permission(struct inode *inode, int mask, unsigned int flags)
 {
-       struct gfs2_inode *ip = GFS2_I(inode);
+       struct gfs2_inode *ip;
        struct gfs2_holder i_gh;
        int error;
        int unlock = 0;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
+       ip = GFS2_I(inode);
        if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
                error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
                if (error)
@@ -1058,7 +1062,7 @@ int gfs2_permission(struct inode *inode, int mask)
        if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
                error = -EACCES;
        else
-               error = generic_permission(inode, mask, gfs2_check_acl);
+               error = generic_permission(inode, mask, flags, gfs2_check_acl);
        if (unlock)
                gfs2_glock_dq_uninit(&i_gh);
 
index 0bc81cf256b8320c325bf64eafd32e1aede575d6..d3244d949a4ea140a2a838b8f683a59d029028eb 100644 (file)
@@ -749,11 +749,14 @@ int hostfs_rename(struct inode *from_ino, struct dentry *from,
        return err;
 }
 
-int hostfs_permission(struct inode *ino, int desired)
+int hostfs_permission(struct inode *ino, int desired, unsigned int flags)
 {
        char *name;
        int r = 0, w = 0, x = 0, err;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        if (desired & MAY_READ) r = 1;
        if (desired & MAY_WRITE) w = 1;
        if (desired & MAY_EXEC) x = 1;
@@ -768,7 +771,7 @@ int hostfs_permission(struct inode *ino, int desired)
                err = access_file(name, r, w, x);
        __putname(name);
        if (!err)
-               err = generic_permission(ino, desired, NULL);
+               err = generic_permission(ino, desired, flags, NULL);
        return err;
 }
 
index 11c2b4080f65d335d373e599bc374c9122d543a0..f4ad9e31ddc438a38896b19b68e051ab811336c2 100644 (file)
@@ -419,7 +419,7 @@ again:
                        unlock_kernel();
                        return -ENOSPC;
                }
-               if (generic_permission(inode, MAY_WRITE, NULL) ||
+               if (generic_permission(inode, MAY_WRITE, 0, NULL) ||
                    !S_ISREG(inode->i_mode) ||
                    get_write_access(inode)) {
                        d_rehash(dentry);
index 54a92fd02bbd63b2d75543f59bd816b4d738e2e4..95b79672150ab7b6159b00e3ca4083b41eaca9c7 100644 (file)
@@ -259,11 +259,14 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
        return rc;
 }
 
-int jffs2_check_acl(struct inode *inode, int mask)
+int jffs2_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
        struct posix_acl *acl;
        int rc;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
        if (IS_ERR(acl))
                return PTR_ERR(acl);
index 5e42de8d954126a46664125678cdd037a76470a7..3119f59253d3c178b85ee7b21bb90f7ae7e9836b 100644 (file)
@@ -26,7 +26,7 @@ struct jffs2_acl_header {
 
 #ifdef CONFIG_JFFS2_FS_POSIX_ACL
 
-extern int jffs2_check_acl(struct inode *, int);
+extern int jffs2_check_acl(struct inode *, int, unsigned int);
 extern int jffs2_acl_chmod(struct inode *);
 extern int jffs2_init_acl_pre(struct inode *, struct inode *, int *);
 extern int jffs2_init_acl_post(struct inode *);
index 1057a4998e4e793f7a5706dbd4026916c7f3e9b6..e5de9422fa325538e5aa3f1ca9e830e9f2b5652e 100644 (file)
@@ -114,10 +114,14 @@ out:
        return rc;
 }
 
-int jfs_check_acl(struct inode *inode, int mask)
+int jfs_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
-       struct posix_acl *acl = jfs_get_acl(inode, ACL_TYPE_ACCESS);
+       struct posix_acl *acl;
+
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
 
+       acl = jfs_get_acl(inode, ACL_TYPE_ACCESS);
        if (IS_ERR(acl))
                return PTR_ERR(acl);
        if (acl) {
index 54e07559878d12e2fe77c7b39e4f2114c7a3abdb..f9285c4900fa5410f82af317360e1028f4a05e6f 100644 (file)
@@ -20,7 +20,7 @@
 
 #ifdef CONFIG_JFS_POSIX_ACL
 
-int jfs_check_acl(struct inode *, int);
+int jfs_check_acl(struct inode *, int, unsigned int flags);
 int jfs_init_acl(tid_t, struct inode *, struct inode *);
 int jfs_acl_chmod(struct inode *inode);
 
index 409dfd65e9a1764029edf55071069e61f447d29d..f9ddf0c388c8b83c50faf148b3b5b1dcc1c7194b 100644 (file)
@@ -555,9 +555,11 @@ static int logfs_symlink(struct inode *dir, struct dentry *dentry,
        return __logfs_create(dir, dentry, inode, target, destlen);
 }
 
-static int logfs_permission(struct inode *inode, int mask)
+static int logfs_permission(struct inode *inode, int mask, unsigned int flags)
 {
-       return generic_permission(inode, mask, NULL);
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+       return generic_permission(inode, mask, flags, NULL);
 }
 
 static int logfs_link(struct dentry *old_dentry, struct inode *dir,
index 6e275363e89d30e9512fb81423ba4905965a1a26..4e957bf744ae11443092cf11f27afaa43d11a4c8 100644 (file)
@@ -169,8 +169,8 @@ EXPORT_SYMBOL(putname);
 /*
  * This does basic POSIX ACL permission checking
  */
-static inline int __acl_permission_check(struct inode *inode, int mask,
-               int (*check_acl)(struct inode *inode, int mask), int rcu)
+static int acl_permission_check(struct inode *inode, int mask, unsigned int flags,
+               int (*check_acl)(struct inode *inode, int mask, unsigned int flags))
 {
        umode_t                 mode = inode->i_mode;
 
@@ -180,13 +180,9 @@ static inline int __acl_permission_check(struct inode *inode, int mask,
                mode >>= 6;
        else {
                if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
-                       if (rcu) {
-                               return -ECHILD;
-                       } else {
-                               int error = check_acl(inode, mask);
-                               if (error != -EAGAIN)
-                                       return error;
-                       }
+                       int error = check_acl(inode, mask, flags);
+                       if (error != -EAGAIN)
+                               return error;
                }
 
                if (in_group_p(inode->i_gid))
@@ -201,32 +197,31 @@ static inline int __acl_permission_check(struct inode *inode, int mask,
        return -EACCES;
 }
 
-static inline int acl_permission_check(struct inode *inode, int mask,
-               int (*check_acl)(struct inode *inode, int mask))
-{
-       return __acl_permission_check(inode, mask, check_acl, 0);
-}
-
 /**
- * generic_permission  -  check for access rights on a Posix-like filesystem
+ * generic_permission -  check for access rights on a Posix-like filesystem
  * @inode:     inode to check access rights for
  * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
  * @check_acl: optional callback to check for Posix ACLs
+ * @flags      IPERM_FLAG_ flags.
  *
  * Used to check for read/write/execute permissions on a file.
  * We use "fsuid" for this, letting us set arbitrary permissions
  * for filesystem access without changing the "normal" uids which
- * are used for other things..
+ * are used for other things.
+ *
+ * generic_permission is rcu-walk aware. It returns -ECHILD in case an rcu-walk
+ * request cannot be satisfied (eg. requires blocking or too much complexity).
+ * It would then be called again in ref-walk mode.
  */
-int generic_permission(struct inode *inode, int mask,
-               int (*check_acl)(struct inode *inode, int mask))
+int generic_permission(struct inode *inode, int mask, unsigned int flags,
+       int (*check_acl)(struct inode *inode, int mask, unsigned int flags))
 {
        int ret;
 
        /*
         * Do the basic POSIX ACL permission checks.
         */
-       ret = acl_permission_check(inode, mask, check_acl);
+       ret = acl_permission_check(inode, mask, flags, check_acl);
        if (ret != -EACCES)
                return ret;
 
@@ -281,9 +276,10 @@ int inode_permission(struct inode *inode, int mask)
        }
 
        if (inode->i_op->permission)
-               retval = inode->i_op->permission(inode, mask);
+               retval = inode->i_op->permission(inode, mask, 0);
        else
-               retval = generic_permission(inode, mask, inode->i_op->check_acl);
+               retval = generic_permission(inode, mask, 0,
+                               inode->i_op->check_acl);
 
        if (retval)
                return retval;
@@ -668,22 +664,19 @@ force_reval_path(struct path *path, struct nameidata *nd)
  * short-cut DAC fails, then call ->permission() to do more
  * complete permission check.
  */
-static inline int __exec_permission(struct inode *inode, int rcu)
+static inline int exec_permission(struct inode *inode, unsigned int flags)
 {
        int ret;
 
        if (inode->i_op->permission) {
-               if (rcu)
-                       return -ECHILD;
-               ret = inode->i_op->permission(inode, MAY_EXEC);
-               if (!ret)
-                       goto ok;
-               return ret;
+               ret = inode->i_op->permission(inode, MAY_EXEC, flags);
+       } else {
+               ret = acl_permission_check(inode, MAY_EXEC, flags,
+                               inode->i_op->check_acl);
        }
-       ret = __acl_permission_check(inode, MAY_EXEC, inode->i_op->check_acl, rcu);
-       if (!ret)
+       if (likely(!ret))
                goto ok;
-       if (rcu && ret == -ECHILD)
+       if (ret == -ECHILD)
                return ret;
 
        if (capable(CAP_DAC_OVERRIDE) || capable(CAP_DAC_READ_SEARCH))
@@ -691,17 +684,7 @@ static inline int __exec_permission(struct inode *inode, int rcu)
 
        return ret;
 ok:
-       return security_inode_exec_permission(inode, rcu);
-}
-
-static int exec_permission(struct inode *inode)
-{
-       return __exec_permission(inode, 0);
-}
-
-static int exec_permission_rcu(struct inode *inode)
-{
-       return __exec_permission(inode, 1);
+       return security_inode_exec_permission(inode, flags);
 }
 
 static __always_inline void set_root(struct nameidata *nd)
@@ -1165,7 +1148,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
 
                nd->flags |= LOOKUP_CONTINUE;
                if (nd->flags & LOOKUP_RCU) {
-                       err = exec_permission_rcu(nd->inode);
+                       err = exec_permission(nd->inode, IPERM_FLAG_RCU);
                        if (err == -ECHILD) {
                                if (nameidata_drop_rcu(nd))
                                        return -ECHILD;
@@ -1173,7 +1156,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
                        }
                } else {
 exec_again:
-                       err = exec_permission(nd->inode);
+                       err = exec_permission(nd->inode, 0);
                }
                if (err)
                        break;
@@ -1620,7 +1603,7 @@ static struct dentry *__lookup_hash(struct qstr *name,
        struct dentry *dentry;
        int err;
 
-       err = exec_permission(inode);
+       err = exec_permission(inode, 0);
        if (err)
                return ERR_PTR(err);
 
index 58beace14b1981275d6ac1e0ea8eb1677e2dbcbe..d33da530097acd350493bcbab2f315311f474b30 100644 (file)
@@ -2189,11 +2189,14 @@ int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags)
        return nfs_do_access(inode, cred, nfs_open_permission_mask(openflags));
 }
 
-int nfs_permission(struct inode *inode, int mask)
+int nfs_permission(struct inode *inode, int mask, unsigned int flags)
 {
        struct rpc_cred *cred;
        int res = 0;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        nfs_inc_stats(inode, NFSIOS_VFSACCESS);
 
        if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
@@ -2241,7 +2244,7 @@ out:
 out_notsup:
        res = nfs_revalidate_inode(NFS_SERVER(inode), inode);
        if (res == 0)
-               res = generic_permission(inode, mask, NULL);
+               res = generic_permission(inode, mask, flags, NULL);
        goto out;
 }
 
index 71d4bc8464e09b793b6c4eb23b97ff09ee093002..77b48c8fab17c31015cf7a5433ef794f524b3ee8 100644 (file)
@@ -785,15 +785,19 @@ out_err:
        return err;
 }
 
-int nilfs_permission(struct inode *inode, int mask)
+int nilfs_permission(struct inode *inode, int mask, unsigned int flags)
 {
-       struct nilfs_root *root = NILFS_I(inode)->i_root;
+       struct nilfs_root *root;
+
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
 
+       root = NILFS_I(inode)->i_root;
        if ((mask & MAY_WRITE) && root &&
            root->cno != NILFS_CPTREE_CURRENT_CNO)
                return -EROFS; /* snapshot is not writable */
 
-       return generic_permission(inode, mask, NULL);
+       return generic_permission(inode, mask, flags, NULL);
 }
 
 int nilfs_load_inode_block(struct nilfs_sb_info *sbi, struct inode *inode,
index f7560da5a5678b2e026264141036c12cdf588cec..0ca98823db5934400f5671dabd367ececc91cdc2 100644 (file)
@@ -256,7 +256,7 @@ extern void nilfs_update_inode(struct inode *, struct buffer_head *);
 extern void nilfs_truncate(struct inode *);
 extern void nilfs_evict_inode(struct inode *);
 extern int nilfs_setattr(struct dentry *, struct iattr *);
-int nilfs_permission(struct inode *inode, int mask);
+int nilfs_permission(struct inode *inode, int mask, unsigned int flags);
 extern int nilfs_load_inode_block(struct nilfs_sb_info *, struct inode *,
                                  struct buffer_head **);
 extern int nilfs_inode_dirty(struct inode *);
index 391915093fe1c494a58ec8feae54cd576fd2464a..704f6b1742f3204ddf9eae9bb0ebbdfd0e1fcace 100644 (file)
@@ -291,13 +291,17 @@ static int ocfs2_set_acl(handle_t *handle,
        return ret;
 }
 
-int ocfs2_check_acl(struct inode *inode, int mask)
+int ocfs2_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
-       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       struct ocfs2_super *osb;
        struct buffer_head *di_bh = NULL;
        struct posix_acl *acl;
        int ret = -EAGAIN;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
+       osb = OCFS2_SB(inode->i_sb);
        if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
                return ret;
 
index 5c5d31f05853175476cc0c9ed4c2460c67f40da9..4fe7c9cf4bfb82fcd4b1d829068cd2d9ef2c88bc 100644 (file)
@@ -26,7 +26,7 @@ struct ocfs2_acl_entry {
        __le32 e_id;
 };
 
-extern int ocfs2_check_acl(struct inode *, int);
+extern int ocfs2_check_acl(struct inode *, int, unsigned int);
 extern int ocfs2_acl_chmod(struct inode *);
 extern int ocfs2_init_acl(handle_t *, struct inode *, struct inode *,
                          struct buffer_head *, struct buffer_head *,
index f6cba566429d314fe82f9f433889d1e22111462a..bdadbae0909404a531d85e7cbe724429c784d5fc 100644 (file)
@@ -1307,10 +1307,13 @@ bail:
        return err;
 }
 
-int ocfs2_permission(struct inode *inode, int mask)
+int ocfs2_permission(struct inode *inode, int mask, unsigned int flags)
 {
        int ret;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        mlog_entry_void();
 
        ret = ocfs2_inode_lock(inode, NULL, 0);
@@ -1320,7 +1323,7 @@ int ocfs2_permission(struct inode *inode, int mask)
                goto out;
        }
 
-       ret = generic_permission(inode, mask, ocfs2_check_acl);
+       ret = generic_permission(inode, mask, flags, ocfs2_check_acl);
 
        ocfs2_inode_unlock(inode, 0);
 out:
index 97bf761c9e7c7b8f1c1744bbada1d778095eb3d7..f5afbbef67035d23405da17cf6836604c9494d94 100644 (file)
@@ -61,7 +61,7 @@ int ocfs2_zero_extend(struct inode *inode, struct buffer_head *di_bh,
 int ocfs2_setattr(struct dentry *dentry, struct iattr *attr);
 int ocfs2_getattr(struct vfsmount *mnt, struct dentry *dentry,
                  struct kstat *stat);
-int ocfs2_permission(struct inode *inode, int mask);
+int ocfs2_permission(struct inode *inode, int mask, unsigned int flags);
 
 int ocfs2_should_update_atime(struct inode *inode,
                              struct vfsmount *vfsmnt);
index dc5b2fcadc3b63c0e409eb0b054e3065569cbb00..b953d41d9abf6da6d48dc2cd984669b385f7ff27 100644 (file)
@@ -2114,11 +2114,13 @@ static const struct file_operations proc_fd_operations = {
  * /proc/pid/fd needs a special permission handler so that a process can still
  * access /proc/self/fd after it has executed a setuid().
  */
-static int proc_fd_permission(struct inode *inode, int mask)
+static int proc_fd_permission(struct inode *inode, int mask, unsigned int flags)
 {
        int rv;
 
-       rv = generic_permission(inode, mask, NULL);
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+       rv = generic_permission(inode, mask, flags, NULL);
        if (rv == 0)
                return 0;
        if (task_pid(current) == proc_pid(inode))
index c9097f43b425f6df9cbec5df0cc4f639b3747dcc..09a1f92a34ef2fce52b07cf394d040708e86a22b 100644 (file)
@@ -295,7 +295,7 @@ out:
        return ret;
 }
 
-static int proc_sys_permission(struct inode *inode, int mask)
+static int proc_sys_permission(struct inode *inode, int mask,unsigned int flags)
 {
        /*
         * sysctl entries that are not writeable,
@@ -305,6 +305,9 @@ static int proc_sys_permission(struct inode *inode, int mask)
        struct ctl_table *table;
        int error;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        /* Executable files are not allowed under /proc/sys/ */
        if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))
                return -EACCES;
index 9ea22a56cdf1cab58349ea0ed675da1469f4e6e4..3cfb2e93364424886f94807a15031dc90362142d 100644 (file)
@@ -870,11 +870,14 @@ out:
        return err;
 }
 
-static int reiserfs_check_acl(struct inode *inode, int mask)
+static int reiserfs_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
        struct posix_acl *acl;
        int error = -EAGAIN; /* do regular unix permission checks by default */
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        acl = reiserfs_get_acl(inode, ACL_TYPE_ACCESS);
 
        if (acl) {
@@ -951,8 +954,10 @@ static int xattr_mount_check(struct super_block *s)
        return 0;
 }
 
-int reiserfs_permission(struct inode *inode, int mask)
+int reiserfs_permission(struct inode *inode, int mask, unsigned int flags)
 {
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
        /*
         * We don't do permission checks on the internal objects.
         * Permissions are determined by the "owning" object.
@@ -965,9 +970,10 @@ int reiserfs_permission(struct inode *inode, int mask)
         * Stat data v1 doesn't support ACLs.
         */
        if (get_inode_sd_version(inode) != STAT_DATA_V1)
-               return generic_permission(inode, mask, reiserfs_check_acl);
+               return generic_permission(inode, mask, flags,
+                                       reiserfs_check_acl);
 #endif
-       return generic_permission(inode, mask, NULL);
+       return generic_permission(inode, mask, flags, NULL);
 }
 
 static int xattr_hide_revalidate(struct dentry *dentry, struct nameidata *nd)
index cffb1fd8ba33fdb66aab0c076cf24e3f4f2e6387..30ac27345586ea7b943a0042f0f2d52df7acc2f8 100644 (file)
@@ -348,13 +348,18 @@ int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const void *ns, const cha
                return -ENOENT;
 }
 
-int sysfs_permission(struct inode *inode, int mask)
+int sysfs_permission(struct inode *inode, int mask, unsigned int flags)
 {
-       struct sysfs_dirent *sd = inode->i_private;
+       struct sysfs_dirent *sd;
+
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
+       sd = inode->i_private;
 
        mutex_lock(&sysfs_mutex);
        sysfs_refresh_inode(sd, inode);
        mutex_unlock(&sysfs_mutex);
 
-       return generic_permission(inode, mask, NULL);
+       return generic_permission(inode, mask, flags, NULL);
 }
index d9be60a2e9561159ced24d2cc545125e26c6ee74..ffaaa816bfba4c3c1c845e772845749675210e30 100644 (file)
@@ -200,7 +200,7 @@ static inline void __sysfs_put(struct sysfs_dirent *sd)
 struct inode *sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd);
 void sysfs_evict_inode(struct inode *inode);
 int sysfs_sd_setattr(struct sysfs_dirent *sd, struct iattr *iattr);
-int sysfs_permission(struct inode *inode, int mask);
+int sysfs_permission(struct inode *inode, int mask, unsigned int flags);
 int sysfs_setattr(struct dentry *dentry, struct iattr *iattr);
 int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat);
 int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value,
index b2771862fd3df79fbb721b3c5c93865680a8e145..4b11eaf6a580926befe9d9433f93f68bae5b2f82 100644 (file)
@@ -219,12 +219,16 @@ xfs_set_acl(struct inode *inode, int type, struct posix_acl *acl)
 }
 
 int
-xfs_check_acl(struct inode *inode, int mask)
+xfs_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
-       struct xfs_inode *ip = XFS_I(inode);
+       struct xfs_inode *ip;
        struct posix_acl *acl;
        int error = -EAGAIN;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
+       ip = XFS_I(inode);
        trace_xfs_check_acl(ip);
 
        /*
index 0135e2a669d78924c2e4261450ad5d17676371b8..11dd72070cbb12ab0457bea528f70db969a8b1d5 100644 (file)
@@ -42,7 +42,7 @@ struct xfs_acl {
 #define SGI_ACL_DEFAULT_SIZE   (sizeof(SGI_ACL_DEFAULT)-1)
 
 #ifdef CONFIG_XFS_POSIX_ACL
-extern int xfs_check_acl(struct inode *inode, int mask);
+extern int xfs_check_acl(struct inode *inode, int mask, unsigned int flags);
 extern struct posix_acl *xfs_get_acl(struct inode *inode, int type);
 extern int xfs_inherit_acl(struct inode *inode, struct posix_acl *default_acl);
 extern int xfs_acl_chmod(struct inode *inode);
index 2e914d0771b9022ccf42bf50afd7d0e4445510d9..4ccc59c1ea82bf8488ebe8fbbb5cc286764b6f1e 100644 (file)
@@ -37,7 +37,7 @@ extern const struct file_operations coda_ioctl_operations;
 /* operations shared over more than one file */
 int coda_open(struct inode *i, struct file *f);
 int coda_release(struct inode *i, struct file *f);
-int coda_permission(struct inode *inode, int mask);
+int coda_permission(struct inode *inode, int mask, unsigned int flags);
 int coda_revalidate_inode(struct dentry *);
 int coda_getattr(struct vfsmount *, struct dentry *, struct kstat *);
 int coda_setattr(struct dentry *, struct iattr *);
index a04aa96acb71f173733346dbaf19f213d89f1b4d..d5a4d42f655bbfb39b1f70e8f51fb5e50b68e27c 100644 (file)
@@ -1550,11 +1550,13 @@ struct file_operations {
        int (*setlease)(struct file *, long, struct file_lock **);
 };
 
+#define IPERM_FLAG_RCU 0x0001
+
 struct inode_operations {
        struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
        void * (*follow_link) (struct dentry *, struct nameidata *);
-       int (*permission) (struct inode *, int);
-       int (*check_acl)(struct inode *, int);
+       int (*permission) (struct inode *, int, unsigned int);
+       int (*check_acl)(struct inode *, int, unsigned int);
 
        int (*readlink) (struct dentry *, char __user *,int);
        void (*put_link) (struct dentry *, struct nameidata *, void *);
@@ -2165,8 +2167,8 @@ extern sector_t bmap(struct inode *, sector_t);
 #endif
 extern int notify_change(struct dentry *, struct iattr *);
 extern int inode_permission(struct inode *, int);
-extern int generic_permission(struct inode *, int,
-               int (*check_acl)(struct inode *, int));
+extern int generic_permission(struct inode *, int, unsigned int,
+               int (*check_acl)(struct inode *, int, unsigned int));
 
 static inline bool execute_ok(struct inode *inode)
 {
index 574bea4013b618d0816d2280ced3670fa4e75bd4..0437e377b555ca0c4f26d4d3c244f189e289929b 100644 (file)
@@ -10,6 +10,6 @@ extern const struct xattr_handler generic_acl_default_handler;
 
 int generic_acl_init(struct inode *, struct inode *);
 int generic_acl_chmod(struct inode *);
-int generic_check_acl(struct inode *inode, int mask);
+int generic_check_acl(struct inode *inode, int mask, unsigned int flags);
 
 #endif /* LINUX_GENERIC_ACL_H */
index 29d504d5d1c31f5698433657dd534c1500f317b2..0779bb8f95beb9ce6a6ca3ba4b103496a3ddacf4 100644 (file)
@@ -351,7 +351,7 @@ extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
 extern int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr);
 extern int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr);
 extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
-extern int nfs_permission(struct inode *, int);
+extern int nfs_permission(struct inode *, int, unsigned int);
 extern int nfs_open(struct inode *, struct file *);
 extern int nfs_release(struct inode *, struct file *);
 extern int nfs_attribute_timeout(struct inode *inode);
index b2cf2089769b5a699d28910af6c92c177e8ecef0..3b94c91f20a6dfa6dc6c4553c44350042a560f23 100644 (file)
@@ -41,7 +41,7 @@ int reiserfs_xattr_init(struct super_block *sb, int mount_flags);
 int reiserfs_lookup_privroot(struct super_block *sb);
 int reiserfs_delete_xattrs(struct inode *inode);
 int reiserfs_chown_xattrs(struct inode *inode, struct iattr *attrs);
-int reiserfs_permission(struct inode *inode, int mask);
+int reiserfs_permission(struct inode *inode, int mask, unsigned int flags);
 
 #ifdef CONFIG_REISERFS_FS_XATTR
 #define has_xattr_dir(inode) (REISERFS_I(inode)->i_flags & i_has_xattr_dir)