提交 b74c79e9 编写于 作者: N Nick Piggin

fs: provide rcu-walk aware permission i_ops

Signed-off-by: NNick Piggin <npiggin@kernel.dk>
上级 34286d66
...@@ -47,8 +47,8 @@ ata *); ...@@ -47,8 +47,8 @@ ata *);
void * (*follow_link) (struct dentry *, struct nameidata *); void * (*follow_link) (struct dentry *, struct nameidata *);
void (*put_link) (struct dentry *, struct nameidata *, void *); void (*put_link) (struct dentry *, struct nameidata *, void *);
void (*truncate) (struct inode *); void (*truncate) (struct inode *);
int (*permission) (struct inode *, int, struct nameidata *); int (*permission) (struct inode *, int, unsigned int);
int (*check_acl)(struct inode *, int); int (*check_acl)(struct inode *, int, unsigned int);
int (*setattr) (struct dentry *, struct iattr *); int (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *); int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *);
int (*setxattr) (struct dentry *, const char *,const void *,size_t,int); int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
...@@ -76,7 +76,7 @@ follow_link: no ...@@ -76,7 +76,7 @@ follow_link: no
put_link: no put_link: no
truncate: yes (see below) truncate: yes (see below)
setattr: yes setattr: yes
permission: no permission: no (may not block if called in rcu-walk mode)
check_acl: no check_acl: no
getattr: no getattr: no
setxattr: yes setxattr: yes
......
...@@ -316,11 +316,9 @@ The detailed design for rcu-walk is like this: ...@@ -316,11 +316,9 @@ The detailed design for rcu-walk is like this:
The cases where rcu-walk cannot continue are: The cases where rcu-walk cannot continue are:
* NULL dentry (ie. any uncached path element) * NULL dentry (ie. any uncached path element)
* parent with d_inode->i_op->permission or ACLs
* Following links * Following links
In future patches, permission checks become rcu-walk aware. It may be possible It may be possible eventually to make following links rcu-walk aware.
eventually to make following links rcu-walk aware.
Uncached path elements will always require dropping to ref-walk mode, at the 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. 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 ...@@ -336,9 +334,49 @@ or stored into. The result is massive improvements in performance and
scalability of path resolution. 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 Papers and other documentation on dcache locking
================================================ ================================================
1. Scaling dcache with RCU (http://linuxjournal.com/article.php?sid=7124). 1. Scaling dcache with RCU (http://linuxjournal.com/article.php?sid=7124).
2. http://lse.sourceforge.net/locking/dcache/dcache.html 2. http://lse.sourceforge.net/locking/dcache/dcache.html
...@@ -379,4 +379,9 @@ where possible. ...@@ -379,4 +379,9 @@ where possible.
the filesystem provides it), which requires dropping out of rcu-walk mode. This 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 may now be called in rcu-walk mode (nd->flags & LOOKUP_RCU). -ECHILD should be
returned if the filesystem cannot handle rcu-walk. See 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. Documentation/filesystems/vfs.txt for more details.
...@@ -325,7 +325,8 @@ struct inode_operations { ...@@ -325,7 +325,8 @@ struct inode_operations {
void * (*follow_link) (struct dentry *, struct nameidata *); void * (*follow_link) (struct dentry *, struct nameidata *);
void (*put_link) (struct dentry *, struct nameidata *, void *); void (*put_link) (struct dentry *, struct nameidata *, void *);
void (*truncate) (struct inode *); 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 (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *); int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
int (*setxattr) (struct dentry *, const char *,const void *,size_t,int); int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
...@@ -414,6 +415,13 @@ otherwise noted. ...@@ -414,6 +415,13 @@ otherwise noted.
permission: called by the VFS to check for access rights on a POSIX-like permission: called by the VFS to check for access rights on a POSIX-like
filesystem. 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 setattr: called by the VFS to set attributes for a file. This method
is called by chmod(2) and related system calls. is called by chmod(2) and related system calls.
......
...@@ -407,11 +407,14 @@ smb_file_release(struct inode *inode, struct file * file) ...@@ -407,11 +407,14 @@ smb_file_release(struct inode *inode, struct file * file)
* privileges, so we need our own check for this. * privileges, so we need our own check for this.
*/ */
static int 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 mode = inode->i_mode;
int error = 0; int error = 0;
if (flags & IPERM_FLAG_RCU)
return -ECHILD;
VERBOSE("mode=%x, mask=%x\n", mode, mask); VERBOSE("mode=%x, mask=%x\n", mode, mask);
/* Look at user permissions */ /* Look at user permissions */
......
...@@ -91,11 +91,14 @@ static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type) ...@@ -91,11 +91,14 @@ static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type)
return acl; 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 posix_acl *acl;
struct v9fs_session_info *v9ses; struct v9fs_session_info *v9ses;
if (flags & IPERM_FLAG_RCU)
return -ECHILD;
v9ses = v9fs_inode2v9ses(inode); v9ses = v9fs_inode2v9ses(inode);
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) { if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) {
/* /*
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#ifdef CONFIG_9P_FS_POSIX_ACL #ifdef CONFIG_9P_FS_POSIX_ACL
extern int v9fs_get_acl(struct inode *, struct p9_fid *); 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_acl_chmod(struct dentry *);
extern int v9fs_set_create_acl(struct dentry *, extern int v9fs_set_create_acl(struct dentry *,
struct posix_acl *, struct posix_acl *); struct posix_acl *, struct posix_acl *);
......
...@@ -624,7 +624,7 @@ extern void afs_clear_permits(struct afs_vnode *); ...@@ -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_cache_permit(struct afs_vnode *, struct key *, long);
extern void afs_zap_permits(struct rcu_head *); extern void afs_zap_permits(struct rcu_head *);
extern struct key *afs_request_key(struct afs_cell *); 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 * server.c
......
...@@ -285,13 +285,16 @@ static int afs_check_permit(struct afs_vnode *vnode, struct key *key, ...@@ -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 * - AFS ACLs are attached to directories only, and a file is controlled by its
* parent directory's ACL * 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); struct afs_vnode *vnode = AFS_FS_I(inode);
afs_access_t uninitialized_var(access); afs_access_t uninitialized_var(access);
struct key *key; struct key *key;
int ret; int ret;
if (flags & IPERM_FLAG_RCU)
return -ECHILD;
_enter("{{%x:%u},%lx},%x,", _enter("{{%x:%u},%lx},%x,",
vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask); vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask);
...@@ -347,7 +350,7 @@ int afs_permission(struct inode *inode, int mask) ...@@ -347,7 +350,7 @@ int afs_permission(struct inode *inode, int mask)
} }
key_put(key); key_put(key);
ret = generic_permission(inode, mask, NULL); ret = generic_permission(inode, mask, flags, NULL);
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
......
...@@ -229,8 +229,11 @@ static int bad_inode_readlink(struct dentry *dentry, char __user *buffer, ...@@ -229,8 +229,11 @@ static int bad_inode_readlink(struct dentry *dentry, char __user *buffer,
return -EIO; 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; return -EIO;
} }
......
...@@ -185,13 +185,15 @@ static int btrfs_xattr_acl_set(struct dentry *dentry, const char *name, ...@@ -185,13 +185,15 @@ static int btrfs_xattr_acl_set(struct dentry *dentry, const char *name,
return ret; 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; struct posix_acl *acl;
int error = -EAGAIN; 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)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
if (acl) { if (acl) {
......
...@@ -2544,7 +2544,7 @@ int btrfs_sync_fs(struct super_block *sb, int wait); ...@@ -2544,7 +2544,7 @@ int btrfs_sync_fs(struct super_block *sb, int wait);
/* acl.c */ /* acl.c */
#ifdef CONFIG_BTRFS_FS_POSIX_ACL #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 #else
#define btrfs_check_acl NULL #define btrfs_check_acl NULL
#endif #endif
......
...@@ -7211,11 +7211,14 @@ static int btrfs_set_page_dirty(struct page *page) ...@@ -7211,11 +7211,14 @@ static int btrfs_set_page_dirty(struct page *page)
return __set_page_dirty_nobuffers(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)) if ((BTRFS_I(inode)->flags & BTRFS_INODE_READONLY) && (mask & MAY_WRITE))
return -EACCES; 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 = { static const struct inode_operations btrfs_dir_inode_operations = {
......
...@@ -1781,12 +1781,17 @@ int ceph_do_getattr(struct inode *inode, int mask) ...@@ -1781,12 +1781,17 @@ int ceph_do_getattr(struct inode *inode, int mask)
* Check inode permissions. We verify we have a valid value for * Check inode permissions. We verify we have a valid value for
* the AUTH cap, then call the generic handler. * 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) if (!err)
err = generic_permission(inode, mask, NULL); err = generic_permission(inode, mask, flags, NULL);
return err; return err;
} }
......
...@@ -665,7 +665,7 @@ extern void ceph_queue_invalidate(struct inode *inode); ...@@ -665,7 +665,7 @@ extern void ceph_queue_invalidate(struct inode *inode);
extern void ceph_queue_writeback(struct inode *inode); extern void ceph_queue_writeback(struct inode *inode);
extern int ceph_do_getattr(struct inode *inode, int mask); 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_setattr(struct dentry *dentry, struct iattr *attr);
extern int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry, extern int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat); struct kstat *stat);
......
...@@ -283,10 +283,13 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf) ...@@ -283,10 +283,13 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
return 0; 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; struct cifs_sb_info *cifs_sb;
if (flags & IPERM_FLAG_RCU)
return -ECHILD;
cifs_sb = CIFS_SB(inode->i_sb); cifs_sb = CIFS_SB(inode->i_sb);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) {
...@@ -298,7 +301,7 @@ static int cifs_permission(struct inode *inode, int mask) ...@@ -298,7 +301,7 @@ static int cifs_permission(struct inode *inode, int mask)
on the client (above and beyond ACL on servers) for on the client (above and beyond ACL on servers) for
servers which do not support setting and viewing mode bits, servers which do not support setting and viewing mode bits,
so allowing client to check permissions is useful */ 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; static struct kmem_cache *cifs_inode_cachep;
......
...@@ -135,10 +135,13 @@ static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, struc ...@@ -135,10 +135,13 @@ static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, struc
} }
int coda_permission(struct inode *inode, int mask) int coda_permission(struct inode *inode, int mask, unsigned int flags)
{ {
int error; int error;
if (flags & IPERM_FLAG_RCU)
return -ECHILD;
mask &= MAY_READ | MAY_WRITE | MAY_EXEC; mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
if (!mask) if (!mask)
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
#include <linux/coda_psdev.h> #include <linux/coda_psdev.h>
/* pioctl ops */ /* 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, static long coda_pioctl(struct file *filp, unsigned int cmd,
unsigned long user_data); unsigned long user_data);
...@@ -41,8 +41,10 @@ const struct file_operations coda_ioctl_operations = { ...@@ -41,8 +41,10 @@ const struct file_operations coda_ioctl_operations = {
}; };
/* the coda pioctl inode ops */ /* 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; return (mask & MAY_EXEC) ? -EACCES : 0;
} }
......
...@@ -980,8 +980,10 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length) ...@@ -980,8 +980,10 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
} }
static int 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); return inode_permission(ecryptfs_inode_to_lower(inode), mask);
} }
......
...@@ -232,10 +232,14 @@ ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl) ...@@ -232,10 +232,14 @@ ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
} }
int 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)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
if (acl) { if (acl) {
......
...@@ -54,7 +54,7 @@ static inline int ext2_acl_count(size_t size) ...@@ -54,7 +54,7 @@ static inline int ext2_acl_count(size_t size)
#ifdef CONFIG_EXT2_FS_POSIX_ACL #ifdef CONFIG_EXT2_FS_POSIX_ACL
/* acl.c */ /* 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_acl_chmod (struct inode *);
extern int ext2_init_acl (struct inode *, struct inode *); extern int ext2_init_acl (struct inode *, struct inode *);
......
...@@ -240,10 +240,14 @@ ext3_set_acl(handle_t *handle, struct inode *inode, int type, ...@@ -240,10 +240,14 @@ ext3_set_acl(handle_t *handle, struct inode *inode, int type,
} }
int 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)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
if (acl) { if (acl) {
......
...@@ -54,7 +54,7 @@ static inline int ext3_acl_count(size_t size) ...@@ -54,7 +54,7 @@ static inline int ext3_acl_count(size_t size)
#ifdef CONFIG_EXT3_FS_POSIX_ACL #ifdef CONFIG_EXT3_FS_POSIX_ACL
/* acl.c */ /* 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_acl_chmod (struct inode *);
extern int ext3_init_acl (handle_t *, struct inode *, struct inode *); extern int ext3_init_acl (handle_t *, struct inode *, struct inode *);
......
...@@ -238,10 +238,14 @@ ext4_set_acl(handle_t *handle, struct inode *inode, int type, ...@@ -238,10 +238,14 @@ ext4_set_acl(handle_t *handle, struct inode *inode, int type,
} }
int 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)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
if (acl) { if (acl) {
......
...@@ -54,7 +54,7 @@ static inline int ext4_acl_count(size_t size) ...@@ -54,7 +54,7 @@ static inline int ext4_acl_count(size_t size)
#ifdef CONFIG_EXT4_FS_POSIX_ACL #ifdef CONFIG_EXT4_FS_POSIX_ACL
/* acl.c */ /* 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_acl_chmod(struct inode *);
extern int ext4_init_acl(handle_t *, struct inode *, struct inode *); extern int ext4_init_acl(handle_t *, struct inode *, struct inode *);
......
...@@ -985,12 +985,15 @@ static int fuse_access(struct inode *inode, int mask) ...@@ -985,12 +985,15 @@ static int fuse_access(struct inode *inode, int mask)
* access request is sent. Execute permission is still checked * access request is sent. Execute permission is still checked
* locally based on file mode. * 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); struct fuse_conn *fc = get_fuse_conn(inode);
bool refreshed = false; bool refreshed = false;
int err = 0; int err = 0;
if (flags & IPERM_FLAG_RCU)
return -ECHILD;
if (!fuse_allow_task(fc, current)) if (!fuse_allow_task(fc, current))
return -EACCES; return -EACCES;
...@@ -1005,7 +1008,7 @@ static int fuse_permission(struct inode *inode, int mask) ...@@ -1005,7 +1008,7 @@ static int fuse_permission(struct inode *inode, int mask)
} }
if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { 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 /* If permission is denied, try to refresh file
attributes. This is also needed, because the root attributes. This is also needed, because the root
...@@ -1013,7 +1016,8 @@ static int fuse_permission(struct inode *inode, int mask) ...@@ -1013,7 +1016,8 @@ static int fuse_permission(struct inode *inode, int mask)
if (err == -EACCES && !refreshed) { if (err == -EACCES && !refreshed) {
err = fuse_do_getattr(inode, NULL, NULL); err = fuse_do_getattr(inode, NULL, NULL);
if (!err) if (!err)
err = generic_permission(inode, mask, NULL); err = generic_permission(inode, mask,
flags, NULL);
} }
/* Note: the opposite of the above test does not /* Note: the opposite of the above test does not
......
...@@ -190,10 +190,14 @@ generic_acl_chmod(struct inode *inode) ...@@ -190,10 +190,14 @@ generic_acl_chmod(struct inode *inode)
} }
int 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) { if (acl) {
int error = posix_acl_permission(inode, acl, mask); int error = posix_acl_permission(inode, acl, mask);
posix_acl_release(acl); posix_acl_release(acl);
......
...@@ -75,11 +75,14 @@ static struct posix_acl *gfs2_acl_get(struct gfs2_inode *ip, int type) ...@@ -75,11 +75,14 @@ static struct posix_acl *gfs2_acl_get(struct gfs2_inode *ip, int type)
* Returns: errno * 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; struct posix_acl *acl;
int error; int error;
if (flags & IPERM_FLAG_RCU)
return -ECHILD;
acl = gfs2_acl_get(GFS2_I(inode), ACL_TYPE_ACCESS); acl = gfs2_acl_get(GFS2_I(inode), ACL_TYPE_ACCESS);
if (IS_ERR(acl)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#define GFS2_POSIX_ACL_DEFAULT "posix_acl_default" #define GFS2_POSIX_ACL_DEFAULT "posix_acl_default"
#define GFS2_ACL_MAX_ENTRIES 25 #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_create(struct gfs2_inode *dip, struct inode *inode);
extern int gfs2_acl_chmod(struct gfs2_inode *ip, struct iattr *attr); extern int gfs2_acl_chmod(struct gfs2_inode *ip, struct iattr *attr);
extern const struct xattr_handler gfs2_xattr_system_handler; extern const struct xattr_handler gfs2_xattr_system_handler;
......
...@@ -241,7 +241,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask) ...@@ -241,7 +241,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
!capable(CAP_LINUX_IMMUTABLE)) !capable(CAP_LINUX_IMMUTABLE))
goto out; goto out;
if (!IS_IMMUTABLE(inode)) { if (!IS_IMMUTABLE(inode)) {
error = gfs2_permission(inode, MAY_WRITE); error = gfs2_permission(inode, MAY_WRITE, 0);
if (error) if (error)
goto out; goto out;
} }
......
...@@ -509,7 +509,7 @@ struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name, ...@@ -509,7 +509,7 @@ struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name,
} }
if (!is_root) { if (!is_root) {
error = gfs2_permission(dir, MAY_EXEC); error = gfs2_permission(dir, MAY_EXEC, 0);
if (error) if (error)
goto out; goto out;
} }
...@@ -539,7 +539,7 @@ static int create_ok(struct gfs2_inode *dip, const struct qstr *name, ...@@ -539,7 +539,7 @@ static int create_ok(struct gfs2_inode *dip, const struct qstr *name,
{ {
int error; 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) if (error)
return error; return error;
......
...@@ -113,7 +113,7 @@ extern struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name, ...@@ -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, extern struct inode *gfs2_createi(struct gfs2_holder *ghs,
const struct qstr *name, const struct qstr *name,
unsigned int mode, dev_t dev); 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 int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr);
extern struct inode *gfs2_lookup_simple(struct inode *dip, const char *name); extern struct inode *gfs2_lookup_simple(struct inode *dip, const char *name);
extern void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf); extern void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf);
......
...@@ -166,7 +166,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir, ...@@ -166,7 +166,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
if (error) if (error)
goto out_child; goto out_child;
error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC); error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC, 0);
if (error) if (error)
goto out_gunlock; goto out_gunlock;
...@@ -289,7 +289,7 @@ static int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name, ...@@ -289,7 +289,7 @@ static int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name,
if (IS_APPEND(&dip->i_inode)) if (IS_APPEND(&dip->i_inode))
return -EPERM; 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) if (error)
return error; return error;
...@@ -822,7 +822,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, ...@@ -822,7 +822,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
} }
} }
} else { } else {
error = gfs2_permission(ndir, MAY_WRITE | MAY_EXEC); error = gfs2_permission(ndir, MAY_WRITE | MAY_EXEC, 0);
if (error) if (error)
goto out_gunlock; goto out_gunlock;
...@@ -857,7 +857,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, ...@@ -857,7 +857,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
/* Check out the dir to be renamed */ /* Check out the dir to be renamed */
if (dir_rename) { if (dir_rename) {
error = gfs2_permission(odentry->d_inode, MAY_WRITE); error = gfs2_permission(odentry->d_inode, MAY_WRITE, 0);
if (error) if (error)
goto out_gunlock; goto out_gunlock;
} }
...@@ -1041,13 +1041,17 @@ static void gfs2_put_link(struct dentry *dentry, struct nameidata *nd, void *p) ...@@ -1041,13 +1041,17 @@ static void gfs2_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
* Returns: errno * 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; struct gfs2_holder i_gh;
int error; int error;
int unlock = 0; 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) { 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); error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
if (error) if (error)
...@@ -1058,7 +1062,7 @@ int gfs2_permission(struct inode *inode, int mask) ...@@ -1058,7 +1062,7 @@ int gfs2_permission(struct inode *inode, int mask)
if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode)) if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
error = -EACCES; error = -EACCES;
else else
error = generic_permission(inode, mask, gfs2_check_acl); error = generic_permission(inode, mask, flags, gfs2_check_acl);
if (unlock) if (unlock)
gfs2_glock_dq_uninit(&i_gh); gfs2_glock_dq_uninit(&i_gh);
......
...@@ -749,11 +749,14 @@ int hostfs_rename(struct inode *from_ino, struct dentry *from, ...@@ -749,11 +749,14 @@ int hostfs_rename(struct inode *from_ino, struct dentry *from,
return err; return err;
} }
int hostfs_permission(struct inode *ino, int desired) int hostfs_permission(struct inode *ino, int desired, unsigned int flags)
{ {
char *name; char *name;
int r = 0, w = 0, x = 0, err; int r = 0, w = 0, x = 0, err;
if (flags & IPERM_FLAG_RCU)
return -ECHILD;
if (desired & MAY_READ) r = 1; if (desired & MAY_READ) r = 1;
if (desired & MAY_WRITE) w = 1; if (desired & MAY_WRITE) w = 1;
if (desired & MAY_EXEC) x = 1; if (desired & MAY_EXEC) x = 1;
...@@ -768,7 +771,7 @@ int hostfs_permission(struct inode *ino, int desired) ...@@ -768,7 +771,7 @@ int hostfs_permission(struct inode *ino, int desired)
err = access_file(name, r, w, x); err = access_file(name, r, w, x);
__putname(name); __putname(name);
if (!err) if (!err)
err = generic_permission(ino, desired, NULL); err = generic_permission(ino, desired, flags, NULL);
return err; return err;
} }
......
...@@ -419,7 +419,7 @@ static int hpfs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -419,7 +419,7 @@ static int hpfs_unlink(struct inode *dir, struct dentry *dentry)
unlock_kernel(); unlock_kernel();
return -ENOSPC; return -ENOSPC;
} }
if (generic_permission(inode, MAY_WRITE, NULL) || if (generic_permission(inode, MAY_WRITE, 0, NULL) ||
!S_ISREG(inode->i_mode) || !S_ISREG(inode->i_mode) ||
get_write_access(inode)) { get_write_access(inode)) {
d_rehash(dentry); d_rehash(dentry);
......
...@@ -259,11 +259,14 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl) ...@@ -259,11 +259,14 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
return rc; 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; struct posix_acl *acl;
int rc; int rc;
if (flags & IPERM_FLAG_RCU)
return -ECHILD;
acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS); acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
......
...@@ -26,7 +26,7 @@ struct jffs2_acl_header { ...@@ -26,7 +26,7 @@ struct jffs2_acl_header {
#ifdef CONFIG_JFFS2_FS_POSIX_ACL #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_acl_chmod(struct inode *);
extern int jffs2_init_acl_pre(struct inode *, struct inode *, int *); extern int jffs2_init_acl_pre(struct inode *, struct inode *, int *);
extern int jffs2_init_acl_post(struct inode *); extern int jffs2_init_acl_post(struct inode *);
......
...@@ -114,10 +114,14 @@ static int jfs_set_acl(tid_t tid, struct inode *inode, int type, ...@@ -114,10 +114,14 @@ static int jfs_set_acl(tid_t tid, struct inode *inode, int type,
return rc; 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)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
if (acl) { if (acl) {
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#ifdef CONFIG_JFS_POSIX_ACL #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_init_acl(tid_t, struct inode *, struct inode *);
int jfs_acl_chmod(struct inode *inode); int jfs_acl_chmod(struct inode *inode);
......
...@@ -555,9 +555,11 @@ static int logfs_symlink(struct inode *dir, struct dentry *dentry, ...@@ -555,9 +555,11 @@ static int logfs_symlink(struct inode *dir, struct dentry *dentry,
return __logfs_create(dir, dentry, inode, target, destlen); 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, static int logfs_link(struct dentry *old_dentry, struct inode *dir,
......
...@@ -169,8 +169,8 @@ EXPORT_SYMBOL(putname); ...@@ -169,8 +169,8 @@ EXPORT_SYMBOL(putname);
/* /*
* This does basic POSIX ACL permission checking * This does basic POSIX ACL permission checking
*/ */
static inline int __acl_permission_check(struct inode *inode, int mask, static int acl_permission_check(struct inode *inode, int mask, unsigned int flags,
int (*check_acl)(struct inode *inode, int mask), int rcu) int (*check_acl)(struct inode *inode, int mask, unsigned int flags))
{ {
umode_t mode = inode->i_mode; umode_t mode = inode->i_mode;
...@@ -180,13 +180,9 @@ static inline int __acl_permission_check(struct inode *inode, int mask, ...@@ -180,13 +180,9 @@ static inline int __acl_permission_check(struct inode *inode, int mask,
mode >>= 6; mode >>= 6;
else { else {
if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) { if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
if (rcu) { int error = check_acl(inode, mask, flags);
return -ECHILD; if (error != -EAGAIN)
} else { return error;
int error = check_acl(inode, mask);
if (error != -EAGAIN)
return error;
}
} }
if (in_group_p(inode->i_gid)) if (in_group_p(inode->i_gid))
...@@ -201,32 +197,31 @@ static inline int __acl_permission_check(struct inode *inode, int mask, ...@@ -201,32 +197,31 @@ static inline int __acl_permission_check(struct inode *inode, int mask,
return -EACCES; 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 * @inode: inode to check access rights for
* @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC) * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
* @check_acl: optional callback to check for Posix ACLs * @check_acl: optional callback to check for Posix ACLs
* @flags IPERM_FLAG_ flags.
* *
* Used to check for read/write/execute permissions on a file. * Used to check for read/write/execute permissions on a file.
* We use "fsuid" for this, letting us set arbitrary permissions * We use "fsuid" for this, letting us set arbitrary permissions
* for filesystem access without changing the "normal" uids which * 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 generic_permission(struct inode *inode, int mask, unsigned int flags,
int (*check_acl)(struct inode *inode, int mask)) int (*check_acl)(struct inode *inode, int mask, unsigned int flags))
{ {
int ret; int ret;
/* /*
* Do the basic POSIX ACL permission checks. * 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) if (ret != -EACCES)
return ret; return ret;
...@@ -281,9 +276,10 @@ int inode_permission(struct inode *inode, int mask) ...@@ -281,9 +276,10 @@ int inode_permission(struct inode *inode, int mask)
} }
if (inode->i_op->permission) if (inode->i_op->permission)
retval = inode->i_op->permission(inode, mask); retval = inode->i_op->permission(inode, mask, 0);
else else
retval = generic_permission(inode, mask, inode->i_op->check_acl); retval = generic_permission(inode, mask, 0,
inode->i_op->check_acl);
if (retval) if (retval)
return retval; return retval;
...@@ -668,22 +664,19 @@ force_reval_path(struct path *path, struct nameidata *nd) ...@@ -668,22 +664,19 @@ force_reval_path(struct path *path, struct nameidata *nd)
* short-cut DAC fails, then call ->permission() to do more * short-cut DAC fails, then call ->permission() to do more
* complete permission check. * 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; int ret;
if (inode->i_op->permission) { if (inode->i_op->permission) {
if (rcu) ret = inode->i_op->permission(inode, MAY_EXEC, flags);
return -ECHILD; } else {
ret = inode->i_op->permission(inode, MAY_EXEC); ret = acl_permission_check(inode, MAY_EXEC, flags,
if (!ret) inode->i_op->check_acl);
goto ok;
return ret;
} }
ret = __acl_permission_check(inode, MAY_EXEC, inode->i_op->check_acl, rcu); if (likely(!ret))
if (!ret)
goto ok; goto ok;
if (rcu && ret == -ECHILD) if (ret == -ECHILD)
return ret; return ret;
if (capable(CAP_DAC_OVERRIDE) || capable(CAP_DAC_READ_SEARCH)) if (capable(CAP_DAC_OVERRIDE) || capable(CAP_DAC_READ_SEARCH))
...@@ -691,17 +684,7 @@ static inline int __exec_permission(struct inode *inode, int rcu) ...@@ -691,17 +684,7 @@ static inline int __exec_permission(struct inode *inode, int rcu)
return ret; return ret;
ok: ok:
return security_inode_exec_permission(inode, rcu); return security_inode_exec_permission(inode, flags);
}
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);
} }
static __always_inline void set_root(struct nameidata *nd) static __always_inline void set_root(struct nameidata *nd)
...@@ -1165,7 +1148,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) ...@@ -1165,7 +1148,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
nd->flags |= LOOKUP_CONTINUE; nd->flags |= LOOKUP_CONTINUE;
if (nd->flags & LOOKUP_RCU) { if (nd->flags & LOOKUP_RCU) {
err = exec_permission_rcu(nd->inode); err = exec_permission(nd->inode, IPERM_FLAG_RCU);
if (err == -ECHILD) { if (err == -ECHILD) {
if (nameidata_drop_rcu(nd)) if (nameidata_drop_rcu(nd))
return -ECHILD; return -ECHILD;
...@@ -1173,7 +1156,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) ...@@ -1173,7 +1156,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
} }
} else { } else {
exec_again: exec_again:
err = exec_permission(nd->inode); err = exec_permission(nd->inode, 0);
} }
if (err) if (err)
break; break;
...@@ -1620,7 +1603,7 @@ static struct dentry *__lookup_hash(struct qstr *name, ...@@ -1620,7 +1603,7 @@ static struct dentry *__lookup_hash(struct qstr *name,
struct dentry *dentry; struct dentry *dentry;
int err; int err;
err = exec_permission(inode); err = exec_permission(inode, 0);
if (err) if (err)
return ERR_PTR(err); return ERR_PTR(err);
......
...@@ -2189,11 +2189,14 @@ int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags) ...@@ -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)); 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; struct rpc_cred *cred;
int res = 0; int res = 0;
if (flags & IPERM_FLAG_RCU)
return -ECHILD;
nfs_inc_stats(inode, NFSIOS_VFSACCESS); nfs_inc_stats(inode, NFSIOS_VFSACCESS);
if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0) if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
...@@ -2241,7 +2244,7 @@ int nfs_permission(struct inode *inode, int mask) ...@@ -2241,7 +2244,7 @@ int nfs_permission(struct inode *inode, int mask)
out_notsup: out_notsup:
res = nfs_revalidate_inode(NFS_SERVER(inode), inode); res = nfs_revalidate_inode(NFS_SERVER(inode), inode);
if (res == 0) if (res == 0)
res = generic_permission(inode, mask, NULL); res = generic_permission(inode, mask, flags, NULL);
goto out; goto out;
} }
......
...@@ -785,15 +785,19 @@ int nilfs_setattr(struct dentry *dentry, struct iattr *iattr) ...@@ -785,15 +785,19 @@ int nilfs_setattr(struct dentry *dentry, struct iattr *iattr)
return 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 && if ((mask & MAY_WRITE) && root &&
root->cno != NILFS_CPTREE_CURRENT_CNO) root->cno != NILFS_CPTREE_CURRENT_CNO)
return -EROFS; /* snapshot is not writable */ 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, int nilfs_load_inode_block(struct nilfs_sb_info *sbi, struct inode *inode,
......
...@@ -256,7 +256,7 @@ extern void nilfs_update_inode(struct inode *, struct buffer_head *); ...@@ -256,7 +256,7 @@ extern void nilfs_update_inode(struct inode *, struct buffer_head *);
extern void nilfs_truncate(struct inode *); extern void nilfs_truncate(struct inode *);
extern void nilfs_evict_inode(struct inode *); extern void nilfs_evict_inode(struct inode *);
extern int nilfs_setattr(struct dentry *, struct iattr *); 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 *, extern int nilfs_load_inode_block(struct nilfs_sb_info *, struct inode *,
struct buffer_head **); struct buffer_head **);
extern int nilfs_inode_dirty(struct inode *); extern int nilfs_inode_dirty(struct inode *);
......
...@@ -291,13 +291,17 @@ static int ocfs2_set_acl(handle_t *handle, ...@@ -291,13 +291,17 @@ static int ocfs2_set_acl(handle_t *handle,
return ret; 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 buffer_head *di_bh = NULL;
struct posix_acl *acl; struct posix_acl *acl;
int ret = -EAGAIN; 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)) if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
return ret; return ret;
......
...@@ -26,7 +26,7 @@ struct ocfs2_acl_entry { ...@@ -26,7 +26,7 @@ struct ocfs2_acl_entry {
__le32 e_id; __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_acl_chmod(struct inode *);
extern int ocfs2_init_acl(handle_t *, struct inode *, struct inode *, extern int ocfs2_init_acl(handle_t *, struct inode *, struct inode *,
struct buffer_head *, struct buffer_head *, struct buffer_head *, struct buffer_head *,
......
...@@ -1307,10 +1307,13 @@ int ocfs2_getattr(struct vfsmount *mnt, ...@@ -1307,10 +1307,13 @@ int ocfs2_getattr(struct vfsmount *mnt,
return err; return err;
} }
int ocfs2_permission(struct inode *inode, int mask) int ocfs2_permission(struct inode *inode, int mask, unsigned int flags)
{ {
int ret; int ret;
if (flags & IPERM_FLAG_RCU)
return -ECHILD;
mlog_entry_void(); mlog_entry_void();
ret = ocfs2_inode_lock(inode, NULL, 0); ret = ocfs2_inode_lock(inode, NULL, 0);
...@@ -1320,7 +1323,7 @@ int ocfs2_permission(struct inode *inode, int mask) ...@@ -1320,7 +1323,7 @@ int ocfs2_permission(struct inode *inode, int mask)
goto out; goto out;
} }
ret = generic_permission(inode, mask, ocfs2_check_acl); ret = generic_permission(inode, mask, flags, ocfs2_check_acl);
ocfs2_inode_unlock(inode, 0); ocfs2_inode_unlock(inode, 0);
out: out:
......
...@@ -61,7 +61,7 @@ int ocfs2_zero_extend(struct inode *inode, struct buffer_head *di_bh, ...@@ -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_setattr(struct dentry *dentry, struct iattr *attr);
int ocfs2_getattr(struct vfsmount *mnt, struct dentry *dentry, int ocfs2_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat); 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, int ocfs2_should_update_atime(struct inode *inode,
struct vfsmount *vfsmnt); struct vfsmount *vfsmnt);
......
...@@ -2114,11 +2114,13 @@ static const struct file_operations proc_fd_operations = { ...@@ -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 * /proc/pid/fd needs a special permission handler so that a process can still
* access /proc/self/fd after it has executed a setuid(). * 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; 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) if (rv == 0)
return 0; return 0;
if (task_pid(current) == proc_pid(inode)) if (task_pid(current) == proc_pid(inode))
......
...@@ -295,7 +295,7 @@ static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -295,7 +295,7 @@ static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir)
return ret; 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, * sysctl entries that are not writeable,
...@@ -305,6 +305,9 @@ static int proc_sys_permission(struct inode *inode, int mask) ...@@ -305,6 +305,9 @@ static int proc_sys_permission(struct inode *inode, int mask)
struct ctl_table *table; struct ctl_table *table;
int error; int error;
if (flags & IPERM_FLAG_RCU)
return -ECHILD;
/* Executable files are not allowed under /proc/sys/ */ /* Executable files are not allowed under /proc/sys/ */
if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))
return -EACCES; return -EACCES;
......
...@@ -870,11 +870,14 @@ ssize_t reiserfs_listxattr(struct dentry * dentry, char *buffer, size_t size) ...@@ -870,11 +870,14 @@ ssize_t reiserfs_listxattr(struct dentry * dentry, char *buffer, size_t size)
return err; 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; struct posix_acl *acl;
int error = -EAGAIN; /* do regular unix permission checks by default */ 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); acl = reiserfs_get_acl(inode, ACL_TYPE_ACCESS);
if (acl) { if (acl) {
...@@ -951,8 +954,10 @@ static int xattr_mount_check(struct super_block *s) ...@@ -951,8 +954,10 @@ static int xattr_mount_check(struct super_block *s)
return 0; 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. * We don't do permission checks on the internal objects.
* Permissions are determined by the "owning" object. * Permissions are determined by the "owning" object.
...@@ -965,9 +970,10 @@ int reiserfs_permission(struct inode *inode, int mask) ...@@ -965,9 +970,10 @@ int reiserfs_permission(struct inode *inode, int mask)
* Stat data v1 doesn't support ACLs. * Stat data v1 doesn't support ACLs.
*/ */
if (get_inode_sd_version(inode) != STAT_DATA_V1) 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 #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) static int xattr_hide_revalidate(struct dentry *dentry, struct nameidata *nd)
......
...@@ -348,13 +348,18 @@ int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const void *ns, const cha ...@@ -348,13 +348,18 @@ int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const void *ns, const cha
return -ENOENT; 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); mutex_lock(&sysfs_mutex);
sysfs_refresh_inode(sd, inode); sysfs_refresh_inode(sd, inode);
mutex_unlock(&sysfs_mutex); mutex_unlock(&sysfs_mutex);
return generic_permission(inode, mask, NULL); return generic_permission(inode, mask, flags, NULL);
} }
...@@ -200,7 +200,7 @@ static inline void __sysfs_put(struct sysfs_dirent *sd) ...@@ -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); struct inode *sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd);
void sysfs_evict_inode(struct inode *inode); void sysfs_evict_inode(struct inode *inode);
int sysfs_sd_setattr(struct sysfs_dirent *sd, struct iattr *iattr); 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_setattr(struct dentry *dentry, struct iattr *iattr);
int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat);
int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value,
......
...@@ -219,12 +219,16 @@ xfs_set_acl(struct inode *inode, int type, struct posix_acl *acl) ...@@ -219,12 +219,16 @@ xfs_set_acl(struct inode *inode, int type, struct posix_acl *acl)
} }
int 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; struct posix_acl *acl;
int error = -EAGAIN; int error = -EAGAIN;
if (flags & IPERM_FLAG_RCU)
return -ECHILD;
ip = XFS_I(inode);
trace_xfs_check_acl(ip); trace_xfs_check_acl(ip);
/* /*
......
...@@ -42,7 +42,7 @@ struct xfs_acl { ...@@ -42,7 +42,7 @@ struct xfs_acl {
#define SGI_ACL_DEFAULT_SIZE (sizeof(SGI_ACL_DEFAULT)-1) #define SGI_ACL_DEFAULT_SIZE (sizeof(SGI_ACL_DEFAULT)-1)
#ifdef CONFIG_XFS_POSIX_ACL #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 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_inherit_acl(struct inode *inode, struct posix_acl *default_acl);
extern int xfs_acl_chmod(struct inode *inode); extern int xfs_acl_chmod(struct inode *inode);
......
...@@ -37,7 +37,7 @@ extern const struct file_operations coda_ioctl_operations; ...@@ -37,7 +37,7 @@ extern const struct file_operations coda_ioctl_operations;
/* operations shared over more than one file */ /* operations shared over more than one file */
int coda_open(struct inode *i, struct file *f); int coda_open(struct inode *i, struct file *f);
int coda_release(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_revalidate_inode(struct dentry *);
int coda_getattr(struct vfsmount *, struct dentry *, struct kstat *); int coda_getattr(struct vfsmount *, struct dentry *, struct kstat *);
int coda_setattr(struct dentry *, struct iattr *); int coda_setattr(struct dentry *, struct iattr *);
......
...@@ -1550,11 +1550,13 @@ struct file_operations { ...@@ -1550,11 +1550,13 @@ struct file_operations {
int (*setlease)(struct file *, long, struct file_lock **); int (*setlease)(struct file *, long, struct file_lock **);
}; };
#define IPERM_FLAG_RCU 0x0001
struct inode_operations { struct inode_operations {
struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *); struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
void * (*follow_link) (struct dentry *, struct nameidata *); void * (*follow_link) (struct dentry *, struct nameidata *);
int (*permission) (struct inode *, int); int (*permission) (struct inode *, int, unsigned int);
int (*check_acl)(struct inode *, int); int (*check_acl)(struct inode *, int, unsigned int);
int (*readlink) (struct dentry *, char __user *,int); int (*readlink) (struct dentry *, char __user *,int);
void (*put_link) (struct dentry *, struct nameidata *, void *); void (*put_link) (struct dentry *, struct nameidata *, void *);
...@@ -2165,8 +2167,8 @@ extern sector_t bmap(struct inode *, sector_t); ...@@ -2165,8 +2167,8 @@ extern sector_t bmap(struct inode *, sector_t);
#endif #endif
extern int notify_change(struct dentry *, struct iattr *); extern int notify_change(struct dentry *, struct iattr *);
extern int inode_permission(struct inode *, int); extern int inode_permission(struct inode *, int);
extern int generic_permission(struct inode *, int, extern int generic_permission(struct inode *, int, unsigned int,
int (*check_acl)(struct inode *, int)); int (*check_acl)(struct inode *, int, unsigned int));
static inline bool execute_ok(struct inode *inode) static inline bool execute_ok(struct inode *inode)
{ {
......
...@@ -10,6 +10,6 @@ extern const struct xattr_handler generic_acl_default_handler; ...@@ -10,6 +10,6 @@ extern const struct xattr_handler generic_acl_default_handler;
int generic_acl_init(struct inode *, struct inode *); int generic_acl_init(struct inode *, struct inode *);
int generic_acl_chmod(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 */ #endif /* LINUX_GENERIC_ACL_H */
...@@ -351,7 +351,7 @@ extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *); ...@@ -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(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_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_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_open(struct inode *, struct file *);
extern int nfs_release(struct inode *, struct file *); extern int nfs_release(struct inode *, struct file *);
extern int nfs_attribute_timeout(struct inode *inode); extern int nfs_attribute_timeout(struct inode *inode);
......
...@@ -41,7 +41,7 @@ int reiserfs_xattr_init(struct super_block *sb, int mount_flags); ...@@ -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_lookup_privroot(struct super_block *sb);
int reiserfs_delete_xattrs(struct inode *inode); int reiserfs_delete_xattrs(struct inode *inode);
int reiserfs_chown_xattrs(struct inode *inode, struct iattr *attrs); 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 #ifdef CONFIG_REISERFS_FS_XATTR
#define has_xattr_dir(inode) (REISERFS_I(inode)->i_flags & i_has_xattr_dir) #define has_xattr_dir(inode) (REISERFS_I(inode)->i_flags & i_has_xattr_dir)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册