lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <E1JzDzC-0008MT-Hb@pomaz-ex.szeredi.hu>
Date:	Thu, 22 May 2008 18:49:50 +0200
From:	Miklos Szeredi <miklos@...redi.hu>
To:	linux-fsdevel@...r.kernel.org
Cc:	hch@...radead.org, viro@...IV.linux.org.uk,
	trond.myklebust@....uio.no, linux-kernel@...r.kernel.org
Subject: [git patch] vfs: permission API cleanup (v2)

I've committed this series to the vfs-cleanups tree at:

  git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git vfs-cleanups

Changes since yesterday's posting:

   - removed flags passed to ->permission() and path_permission()
   - renamed exec_permission() to check_execute()

Thanks to Al and Trond for the suggestions.

Miklos
---

Miklos Szeredi (12):
      hppfs: remove hppfs_permission
      gfs2: don't call permission()
      hpfs: don't call permission()
      hfsplus: remove hfsplus_permission()
      vfs: pass dentry to permission()
      vfs: don't use nameidata flags in ->permission()
      vfs: cleanup i_op->permission()
      security: don't pass nameidata to security_inode_permission()
      vfs: don't pass nameidata to dentry_permission()
      vfs: move executable checking into ->permission()
      vfs: create path_permission()
      vfs: don't use dentry_permission()

 Documentation/filesystems/Locking |    2 +-
 Documentation/filesystems/vfs.txt |    2 +-
 fs/afs/internal.h                 |    4 +-
 fs/afs/security.c                 |    3 +-
 fs/bad_inode.c                    |    3 +-
 fs/cifs/cifsfs.c                  |    5 +-
 fs/coda/dir.c                     |   10 ++-
 fs/coda/pioctl.c                  |    8 +-
 fs/ecryptfs/inode.c               |   20 +---
 fs/exec.c                         |    4 +-
 fs/ext2/acl.c                     |    4 +-
 fs/ext2/acl.h                     |    2 +-
 fs/ext3/acl.c                     |    4 +-
 fs/ext3/acl.h                     |    2 +-
 fs/ext4/acl.c                     |    4 +-
 fs/ext4/acl.h                     |    2 +-
 fs/fuse/dir.c                     |    8 +-
 fs/gfs2/inode.c                   |    6 +-
 fs/gfs2/inode.h                   |    1 +
 fs/gfs2/ops_file.c                |   11 ++-
 fs/gfs2/ops_inode.c               |   18 +++-
 fs/hfs/inode.c                    |    7 +-
 fs/hfsplus/inode.c                |   13 ---
 fs/hostfs/hostfs_kern.c           |    3 +-
 fs/hpfs/namei.c                   |    2 +-
 fs/hppfs/hppfs.c                  |    7 --
 fs/inotify_user.c                 |    2 +-
 fs/jffs2/acl.c                    |    4 +-
 fs/jffs2/acl.h                    |    2 +-
 fs/jfs/acl.c                      |    4 +-
 fs/jfs/jfs_acl.h                  |    2 +-
 fs/namei.c                        |  203 ++++++++++++++++++++-----------------
 fs/nfs/dir.c                      |   17 ++--
 fs/nfsd/nfsfh.c                   |   11 +-
 fs/nfsd/vfs.c                     |    8 +-
 fs/ocfs2/file.c                   |    3 +-
 fs/ocfs2/file.h                   |    3 +-
 fs/open.c                         |   15 ++--
 fs/proc/base.c                    |    4 +-
 fs/proc/proc_sysctl.c             |   11 ++-
 fs/reiserfs/xattr.c               |    4 +-
 fs/smbfs/file.c                   |   11 +-
 fs/utimes.c                       |    2 +-
 fs/xattr.c                        |   22 +++--
 fs/xfs/linux-2.6/xfs_iops.c       |    7 +-
 include/linux/coda_linux.h        |    2 +-
 include/linux/fs.h                |   16 +++-
 include/linux/namei.h             |    2 -
 include/linux/nfs_fs.h            |    2 +-
 include/linux/reiserfs_xattr.h    |    2 +-
 include/linux/security.h          |    8 +-
 include/linux/shmem_fs.h          |    2 +-
 ipc/mqueue.c                      |   15 ++-
 mm/shmem_acl.c                    |    4 +-
 net/unix/af_unix.c                |    2 +-
 security/dummy.c                  |    2 +-
 security/security.c               |    4 +-
 security/selinux/hooks.c          |    6 +-
 security/smack/smack_lsm.c        |    4 +-
 59 files changed, 296 insertions(+), 265 deletions(-)
---

diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking
index 8b22d7d..9801e87 100644
--- a/Documentation/filesystems/Locking
+++ b/Documentation/filesystems/Locking
@@ -44,7 +44,7 @@ ata *);
 	int (*readlink) (struct dentry *, char __user *,int);
 	int (*follow_link) (struct dentry *, struct nameidata *);
 	void (*truncate) (struct inode *);
-	int (*permission) (struct inode *, int, struct nameidata *);
+	int (*permission) (struct dentry *, 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);
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt
index b7522c6..a597097 100644
--- a/Documentation/filesystems/vfs.txt
+++ b/Documentation/filesystems/vfs.txt
@@ -326,7 +326,7 @@ 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 dentry *, 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);
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 7102824..6aa93b1 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -469,8 +469,6 @@ extern bool afs_cm_incoming_call(struct afs_call *);
 extern const struct inode_operations afs_dir_inode_operations;
 extern const struct file_operations afs_dir_file_operations;
 
-extern int afs_permission(struct inode *, int, struct nameidata *);
-
 /*
  * file.c
  */
@@ -605,7 +603,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, struct nameidata *);
+extern int afs_permission(struct dentry *, int);
 
 /*
  * server.c
diff --git a/fs/afs/security.c b/fs/afs/security.c
index 3bcbece..dd53831 100644
--- a/fs/afs/security.c
+++ b/fs/afs/security.c
@@ -284,8 +284,9 @@ 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, struct nameidata *nd)
+int afs_permission(struct dentry *dentry, int mask)
 {
+	struct inode *inode = dentry->d_inode;
 	struct afs_vnode *vnode = AFS_FS_I(inode);
 	afs_access_t uninitialized_var(access);
 	struct key *key;
diff --git a/fs/bad_inode.c b/fs/bad_inode.c
index f1c2ea8..d1320bd 100644
--- a/fs/bad_inode.c
+++ b/fs/bad_inode.c
@@ -243,8 +243,7 @@ static int bad_inode_readlink(struct dentry *dentry, char __user *buffer,
 	return -EIO;
 }
 
-static int bad_inode_permission(struct inode *inode, int mask,
-			struct nameidata *nd)
+static int bad_inode_permission(struct dentry *dentry, int mask)
 {
 	return -EIO;
 }
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 427a7c6..bd16cc8 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -268,14 +268,15 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
 	return 0;
 }
 
-static int cifs_permission(struct inode *inode, int mask, struct nameidata *nd)
+static int cifs_permission(struct dentry *dentry, int mask)
 {
+	struct inode *inode = dentry->d_inode;
 	struct cifs_sb_info *cifs_sb;
 
 	cifs_sb = CIFS_SB(inode->i_sb);
 
 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
-		return 0;
+		return check_execute(inode, mask);
 	else /* file mode might have been restricted at mount time
 		on the client (above and beyond ACL on servers) for
 		servers which do not support setting and viewing mode bits,
diff --git a/fs/coda/dir.c b/fs/coda/dir.c
index 3d2580e..51d1500 100644
--- a/fs/coda/dir.c
+++ b/fs/coda/dir.c
@@ -137,13 +137,19 @@ exit:
 }
 
 
-int coda_permission(struct inode *inode, int mask, struct nameidata *nd)
+int coda_permission(struct dentry *dentry, int mask)
 {
-        int error = 0;
+	struct inode *inode = dentry->d_inode;
+	int error = 0;
  
+	mask &= PERMISSION_MASK;
 	if (!mask)
 		return 0; 
 
+	error = check_execute(inode, mask);
+	if (error)
+		return error;
+
 	lock_kernel();
 
 	if (coda_cache_check(inode, mask))
diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c
index c21a1f5..afb7836 100644
--- a/fs/coda/pioctl.c
+++ b/fs/coda/pioctl.c
@@ -24,8 +24,7 @@
 #include <linux/coda_psdev.h>
 
 /* pioctl ops */
-static int coda_ioctl_permission(struct inode *inode, int mask,
-				 struct nameidata *nd);
+static int coda_ioctl_permission(struct dentry *dentry, int mask);
 static int coda_pioctl(struct inode * inode, struct file * filp, 
                        unsigned int cmd, unsigned long user_data);
 
@@ -42,10 +41,9 @@ const struct file_operations coda_ioctl_operations = {
 };
 
 /* the coda pioctl inode ops */
-static int coda_ioctl_permission(struct inode *inode, int mask,
-				 struct nameidata *nd)
+static int coda_ioctl_permission(struct dentry *dentry, int mask)
 {
-        return 0;
+	return check_execute(dentry->d_inode, mask);
 }
 
 static int coda_pioctl(struct inode * inode, struct file * filp, 
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 5b0c334..f4074f7 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -808,22 +808,14 @@ out:
 }
 
 static int
-ecryptfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+ecryptfs_permission(struct dentry *dentry, int mask)
 {
-	int rc;
+	struct path lower_path;
+
+	lower_path.mnt = ecryptfs_dentry_to_lower_mnt(dentry);
+	lower_path.dentry = ecryptfs_dentry_to_lower(dentry);
 
-        if (nd) {
-		struct vfsmount *vfsmnt_save = nd->path.mnt;
-		struct dentry *dentry_save = nd->path.dentry;
-
-		nd->path.mnt = ecryptfs_dentry_to_lower_mnt(nd->path.dentry);
-		nd->path.dentry = ecryptfs_dentry_to_lower(nd->path.dentry);
-		rc = permission(ecryptfs_inode_to_lower(inode), mask, nd);
-		nd->path.mnt = vfsmnt_save;
-		nd->path.dentry = dentry_save;
-        } else
-		rc = permission(ecryptfs_inode_to_lower(inode), mask, NULL);
-        return rc;
+	return path_permission(&lower_path, mask);
 }
 
 /**
diff --git a/fs/exec.c b/fs/exec.c
index b68682a..5924a42 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -116,7 +116,7 @@ asmlinkage long sys_uselib(const char __user * library)
 	if (!S_ISREG(nd.path.dentry->d_inode->i_mode))
 		goto exit;
 
-	error = vfs_permission(&nd, MAY_READ | MAY_EXEC);
+	error = path_permission(&nd.path, MAY_READ | MAY_EXEC);
 	if (error)
 		goto exit;
 
@@ -664,7 +664,7 @@ struct file *open_exec(const char *name)
 		struct inode *inode = nd.path.dentry->d_inode;
 		file = ERR_PTR(-EACCES);
 		if (S_ISREG(inode->i_mode)) {
-			int err = vfs_permission(&nd, MAY_EXEC);
+			int err = path_permission(&nd.path, MAY_EXEC);
 			file = ERR_PTR(err);
 			if (!err) {
 				file = nameidata_to_filp(&nd,
diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c
index e58669e..124668a 100644
--- a/fs/ext2/acl.c
+++ b/fs/ext2/acl.c
@@ -294,9 +294,9 @@ ext2_check_acl(struct inode *inode, int mask)
 }
 
 int
-ext2_permission(struct inode *inode, int mask, struct nameidata *nd)
+ext2_permission(struct dentry *dentry, int mask)
 {
-	return generic_permission(inode, mask, ext2_check_acl);
+	return generic_permission(dentry->d_inode, mask, ext2_check_acl);
 }
 
 /*
diff --git a/fs/ext2/acl.h b/fs/ext2/acl.h
index 0bde85b..a8ebf60 100644
--- a/fs/ext2/acl.h
+++ b/fs/ext2/acl.h
@@ -58,7 +58,7 @@ static inline int ext2_acl_count(size_t size)
 #define EXT2_ACL_NOT_CACHED ((void *)-1)
 
 /* acl.c */
-extern int ext2_permission (struct inode *, int, struct nameidata *);
+extern int ext2_permission(struct dentry *, int);
 extern int ext2_acl_chmod (struct inode *);
 extern int ext2_init_acl (struct inode *, struct inode *);
 
diff --git a/fs/ext3/acl.c b/fs/ext3/acl.c
index a754d18..f8d1bea 100644
--- a/fs/ext3/acl.c
+++ b/fs/ext3/acl.c
@@ -299,9 +299,9 @@ ext3_check_acl(struct inode *inode, int mask)
 }
 
 int
-ext3_permission(struct inode *inode, int mask, struct nameidata *nd)
+ext3_permission(struct dentry *dentry, int mask)
 {
-	return generic_permission(inode, mask, ext3_check_acl);
+	return generic_permission(dentry->d_inode, mask, ext3_check_acl);
 }
 
 /*
diff --git a/fs/ext3/acl.h b/fs/ext3/acl.h
index 0d1e627..d6cc255 100644
--- a/fs/ext3/acl.h
+++ b/fs/ext3/acl.h
@@ -58,7 +58,7 @@ static inline int ext3_acl_count(size_t size)
 #define EXT3_ACL_NOT_CACHED ((void *)-1)
 
 /* acl.c */
-extern int ext3_permission (struct inode *, int, struct nameidata *);
+extern int ext3_permission(struct dentry *, int);
 extern int ext3_acl_chmod (struct inode *);
 extern int ext3_init_acl (handle_t *, struct inode *, struct inode *);
 
diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c
index 3c8dab8..097a518 100644
--- a/fs/ext4/acl.c
+++ b/fs/ext4/acl.c
@@ -299,9 +299,9 @@ ext4_check_acl(struct inode *inode, int mask)
 }
 
 int
-ext4_permission(struct inode *inode, int mask, struct nameidata *nd)
+ext4_permission(struct dentry *dentry, int mask)
 {
-	return generic_permission(inode, mask, ext4_check_acl);
+	return generic_permission(dentry->d_inode, mask, ext4_check_acl);
 }
 
 /*
diff --git a/fs/ext4/acl.h b/fs/ext4/acl.h
index 26a5c1a..f5289af 100644
--- a/fs/ext4/acl.h
+++ b/fs/ext4/acl.h
@@ -58,7 +58,7 @@ static inline int ext4_acl_count(size_t size)
 #define EXT4_ACL_NOT_CACHED ((void *)-1)
 
 /* acl.c */
-extern int ext4_permission (struct inode *, int, struct nameidata *);
+extern int ext4_permission(struct dentry *, int);
 extern int ext4_acl_chmod (struct inode *);
 extern int ext4_init_acl (handle_t *, struct inode *, struct inode *);
 
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 2060bf0..5cf0740 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -886,8 +886,9 @@ 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, struct nameidata *nd)
+static int fuse_permission(struct dentry *dentry, int mask)
 {
+	struct inode *inode = dentry->d_inode;
 	struct fuse_conn *fc = get_fuse_conn(inode);
 	bool refreshed = false;
 	int err = 0;
@@ -921,8 +922,9 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd)
 		   exist.  So if permissions are revoked this won't be
 		   noticed immediately, only after the attribute
 		   timeout has expired */
-	} else if (nd && (nd->flags & (LOOKUP_ACCESS | LOOKUP_CHDIR))) {
-		err = fuse_access(inode, mask);
+	} else if ((mask & PERM_OP_MASK) == PERM_OP_ACCESS ||
+		   (mask & PERM_OP_MASK) == PERM_OP_CHDIR) {
+		err = fuse_access(inode, mask & PERMISSION_MASK);
 	} else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) {
 		if (!(inode->i_mode & S_IXUGO)) {
 			if (refreshed)
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 3a9ef52..3666c32 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -504,7 +504,7 @@ struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name,
 	}
 
 	if (!is_root) {
-		error = permission(dir, MAY_EXEC, NULL);
+		error = gfs2_do_permission(dir, MAY_EXEC);
 		if (error)
 			goto out;
 	}
@@ -667,7 +667,7 @@ static int create_ok(struct gfs2_inode *dip, const struct qstr *name,
 {
 	int error;
 
-	error = permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, NULL);
+	error = gfs2_do_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC);
 	if (error)
 		return error;
 
@@ -1134,7 +1134,7 @@ int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name,
 	if (IS_APPEND(&dip->i_inode))
 		return -EPERM;
 
-	error = permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, NULL);
+	error = gfs2_do_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC);
 	if (error)
 		return error;
 
diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h
index 580da45..49c1a3e 100644
--- a/fs/gfs2/inode.h
+++ b/fs/gfs2/inode.h
@@ -91,6 +91,7 @@ int gfs2_rmdiri(struct gfs2_inode *dip, const struct qstr *name,
 		struct gfs2_inode *ip);
 int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name,
 		   const struct gfs2_inode *ip);
+int gfs2_do_permission(struct inode *inode, int mask);
 int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to);
 int gfs2_readlinki(struct gfs2_inode *ip, char **buf, unsigned int *len);
 int gfs2_glock_nq_atime(struct gfs2_holder *gh);
diff --git a/fs/gfs2/ops_file.c b/fs/gfs2/ops_file.c
index e1b7d52..68a3dd7 100644
--- a/fs/gfs2/ops_file.c
+++ b/fs/gfs2/ops_file.c
@@ -15,6 +15,7 @@
 #include <linux/uio.h>
 #include <linux/blkdev.h>
 #include <linux/mm.h>
+#include <linux/mount.h>
 #include <linux/fs.h>
 #include <linux/gfs2_ondisk.h>
 #include <linux/ext2_fs.h>
@@ -220,10 +221,14 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
 	int error;
 	u32 new_flags, flags;
 
-	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
+	error = mnt_want_write(filp->f_path.mnt);
 	if (error)
 		return error;
 
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
+	if (error)
+		goto out_drop_write;
+
 	flags = ip->i_di.di_flags;
 	new_flags = (flags & ~mask) | (reqflags & mask);
 	if ((new_flags ^ flags) == 0)
@@ -242,7 +247,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 = permission(inode, MAY_WRITE, NULL);
+		error = gfs2_do_permission(inode, MAY_WRITE);
 		if (error)
 			goto out;
 	}
@@ -272,6 +277,8 @@ out_trans_end:
 	gfs2_trans_end(sdp);
 out:
 	gfs2_glock_dq_uninit(&gh);
+out_drop_write:
+	mnt_drop_write(filp->f_path.mnt);
 	return error;
 }
 
diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c
index 2686ad4..a9d6a63 100644
--- a/fs/gfs2/ops_inode.c
+++ b/fs/gfs2/ops_inode.c
@@ -163,7 +163,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
 	if (error)
 		goto out;
 
-	error = permission(dir, MAY_WRITE | MAY_EXEC, NULL);
+	error = gfs2_do_permission(dir, MAY_WRITE | MAY_EXEC);
 	if (error)
 		goto out_gunlock;
 
@@ -669,7 +669,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
 			}
 		}
 	} else {
-		error = permission(ndir, MAY_WRITE | MAY_EXEC, NULL);
+		error = gfs2_do_permission(ndir, MAY_WRITE | MAY_EXEC);
 		if (error)
 			goto out_gunlock;
 
@@ -704,7 +704,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
 	/* Check out the dir to be renamed */
 
 	if (dir_rename) {
-		error = permission(odentry->d_inode, MAY_WRITE, NULL);
+		error = gfs2_do_permission(odentry->d_inode, MAY_WRITE);
 		if (error)
 			goto out_gunlock;
 	}
@@ -891,7 +891,7 @@ static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd)
  * Returns: errno
  */
 
-static int gfs2_permission(struct inode *inode, int mask, struct nameidata *nd)
+int gfs2_do_permission(struct inode *inode, int mask)
 {
 	struct gfs2_inode *ip = GFS2_I(inode);
 	struct gfs2_holder i_gh;
@@ -905,13 +905,21 @@ static int gfs2_permission(struct inode *inode, int mask, struct nameidata *nd)
 		unlock = 1;
 	}
 
-	error = generic_permission(inode, mask, gfs2_check_acl);
+	if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
+		error = -EACCES;
+	else
+		error = generic_permission(inode, mask, gfs2_check_acl);
 	if (unlock)
 		gfs2_glock_dq_uninit(&i_gh);
 
 	return error;
 }
 
+static int gfs2_permission(struct dentry *dentry, int mask)
+{
+	return gfs2_do_permission(dentry->d_inode, mask);
+}
+
 static int setattr_size(struct inode *inode, struct iattr *attr)
 {
 	struct gfs2_inode *ip = GFS2_I(inode);
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index 97f8446..c87eeee 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -511,11 +511,12 @@ void hfs_clear_inode(struct inode *inode)
 	}
 }
 
-static int hfs_permission(struct inode *inode, int mask,
-			  struct nameidata *nd)
+static int hfs_permission(struct dentry *dentry, int mask)
 {
+	struct inode *inode = dentry->d_inode;
+
 	if (S_ISREG(inode->i_mode) && mask & MAY_EXEC)
-		return 0;
+		return check_execute(inode, mask);
 	return generic_permission(inode, mask, NULL);
 }
 
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index 67e1c8b..369624c 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -238,18 +238,6 @@ static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms)
 	perms->dev = cpu_to_be32(HFSPLUS_I(inode).dev);
 }
 
-static int hfsplus_permission(struct inode *inode, int mask, struct nameidata *nd)
-{
-	/* MAY_EXEC is also used for lookup, if no x bit is set allow lookup,
-	 * open_exec has the same test, so it's still not executable, if a x bit
-	 * is set fall back to standard permission check.
-	 */
-	if (S_ISREG(inode->i_mode) && mask & MAY_EXEC && !(inode->i_mode & 0111))
-		return 0;
-	return generic_permission(inode, mask, NULL);
-}
-
-
 static int hfsplus_file_open(struct inode *inode, struct file *file)
 {
 	if (HFSPLUS_IS_RSRC(inode))
@@ -283,7 +271,6 @@ static int hfsplus_file_release(struct inode *inode, struct file *file)
 static const struct inode_operations hfsplus_file_inode_operations = {
 	.lookup		= hfsplus_file_lookup,
 	.truncate	= hfsplus_file_truncate,
-	.permission	= hfsplus_permission,
 	.setxattr	= hfsplus_setxattr,
 	.getxattr	= hfsplus_getxattr,
 	.listxattr	= hfsplus_listxattr,
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index 5222345..1bf36c1 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -822,8 +822,9 @@ int hostfs_rename(struct inode *from_ino, struct dentry *from,
 	return err;
 }
 
-int hostfs_permission(struct inode *ino, int desired, struct nameidata *nd)
+int hostfs_permission(struct dentry *dentry, int desired)
 {
+	struct inode *ino = dentry->d_inode;
 	char *name;
 	int r = 0, w = 0, x = 0, err;
 
diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c
index d5ed786..e1fadd3 100644
--- a/fs/hpfs/namei.c
+++ b/fs/hpfs/namei.c
@@ -415,7 +415,7 @@ again:
 		d_drop(dentry);
 		spin_lock(&dentry->d_lock);
 		if (atomic_read(&dentry->d_count) > 1 ||
-		    permission(inode, MAY_WRITE, NULL) ||
+		    generic_permission(inode, MAY_WRITE, NULL) ||
 		    !S_ISREG(inode->i_mode) ||
 		    get_write_access(inode)) {
 			spin_unlock(&dentry->d_lock);
diff --git a/fs/hppfs/hppfs.c b/fs/hppfs/hppfs.c
index 65077aa..2b3d182 100644
--- a/fs/hppfs/hppfs.c
+++ b/fs/hppfs/hppfs.c
@@ -655,20 +655,13 @@ static void *hppfs_follow_link(struct dentry *dentry, struct nameidata *nd)
 	return proc_dentry->d_inode->i_op->follow_link(proc_dentry, nd);
 }
 
-int hppfs_permission(struct inode *inode, int mask, struct nameidata *nd)
-{
-	return generic_permission(inode, mask, NULL);
-}
-
 static const struct inode_operations hppfs_dir_iops = {
 	.lookup		= hppfs_lookup,
-	.permission	= hppfs_permission,
 };
 
 static const struct inode_operations hppfs_link_iops = {
 	.readlink	= hppfs_readlink,
 	.follow_link	= hppfs_follow_link,
-	.permission	= hppfs_permission,
 };
 
 static struct inode *get_inode(struct super_block *sb, struct dentry *dentry)
diff --git a/fs/inotify_user.c b/fs/inotify_user.c
index 6676c06..254c7fb 100644
--- a/fs/inotify_user.c
+++ b/fs/inotify_user.c
@@ -365,7 +365,7 @@ static int find_inode(const char __user *dirname, struct nameidata *nd,
 	if (error)
 		return error;
 	/* you can only watch an inode if you have read permissions on it */
-	error = vfs_permission(nd, MAY_READ);
+	error = path_permission(&nd->path, MAY_READ);
 	if (error)
 		path_put(&nd->path);
 	return error;
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
index 4c80404..f1ecb8c 100644
--- a/fs/jffs2/acl.c
+++ b/fs/jffs2/acl.c
@@ -314,9 +314,9 @@ static int jffs2_check_acl(struct inode *inode, int mask)
 	return -EAGAIN;
 }
 
-int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd)
+int jffs2_permission(struct dentry *dentry, int mask)
 {
-	return generic_permission(inode, mask, jffs2_check_acl);
+	return generic_permission(dentry->d_inode, mask, jffs2_check_acl);
 }
 
 int jffs2_init_acl_pre(struct inode *dir_i, struct inode *inode, int *i_mode)
diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h
index 0bb7f00..b5da329 100644
--- a/fs/jffs2/acl.h
+++ b/fs/jffs2/acl.h
@@ -28,7 +28,7 @@ struct jffs2_acl_header {
 
 #define JFFS2_ACL_NOT_CACHED ((void *)-1)
 
-extern int jffs2_permission(struct inode *, int, struct nameidata *);
+extern int jffs2_permission(struct dentry *, 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 *);
diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c
index 4d84bdc..0b4c3a1 100644
--- a/fs/jfs/acl.c
+++ b/fs/jfs/acl.c
@@ -140,9 +140,9 @@ static int jfs_check_acl(struct inode *inode, int mask)
 	return -EAGAIN;
 }
 
-int jfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+int jfs_permission(struct dentry *dentry, int mask)
 {
-	return generic_permission(inode, mask, jfs_check_acl);
+	return generic_permission(dentry->d_inode, mask, jfs_check_acl);
 }
 
 int jfs_init_acl(tid_t tid, struct inode *inode, struct inode *dir)
diff --git a/fs/jfs/jfs_acl.h b/fs/jfs/jfs_acl.h
index 455fa42..69f4af5 100644
--- a/fs/jfs/jfs_acl.h
+++ b/fs/jfs/jfs_acl.h
@@ -20,7 +20,7 @@
 
 #ifdef CONFIG_JFS_POSIX_ACL
 
-int jfs_permission(struct inode *, int, struct nameidata *);
+int jfs_permission(struct dentry *, int);
 int jfs_init_acl(tid_t, struct inode *, struct inode *);
 int jfs_setattr(struct dentry *, struct iattr *);
 
diff --git a/fs/namei.c b/fs/namei.c
index 1114bc0..83d0f37 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -185,6 +185,7 @@ int generic_permission(struct inode *inode, int mask,
 {
 	umode_t			mode = inode->i_mode;
 
+	mask &= PERMISSION_MASK;
 	if (current->fsuid == inode->i_uid)
 		mode >>= 6;
 	else {
@@ -226,13 +227,30 @@ int generic_permission(struct inode *inode, int mask,
 	return -EACCES;
 }
 
-int permission(struct inode *inode, int mask, struct nameidata *nd)
+/**
+ * check_execute - check for general execute permission on file
+ * @inode:	inode to check access rights for
+ * @mask:	right to check for
+ *
+ * Exec permission on a regular file is denied if none of the execute
+ * bits are set.
+ *
+ * This needs to be called by filesystems which define a
+ * ->permission() method, and don't call generic_permission().
+ */
+int check_execute(struct inode *inode, int mask)
 {
-	int retval, submask;
-	struct vfsmount *mnt = NULL;
+	if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode) &&
+	    !(inode->i_mode & S_IXUGO))
+		return -EACCES;
 
-	if (nd)
-		mnt = nd->path.mnt;
+	return 0;
+}
+
+static int dentry_permission(struct dentry *dentry, int mask)
+{
+	struct inode *inode = dentry->d_inode;
+	int retval, submask;
 
 	if (mask & MAY_WRITE) {
 		umode_t mode = inode->i_mode;
@@ -251,34 +269,13 @@ int permission(struct inode *inode, int mask, struct nameidata *nd)
 			return -EACCES;
 	}
 
-	if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) {
-		/*
-		 * MAY_EXEC on regular files is denied if the fs is mounted
-		 * with the "noexec" flag.
-		 */
-		if (mnt && (mnt->mnt_flags & MNT_NOEXEC))
-			return -EACCES;
-	}
-
 	/* Ordinary permission routines do not understand MAY_APPEND. */
 	submask = mask & ~MAY_APPEND;
-	if (inode->i_op && inode->i_op->permission) {
-		retval = inode->i_op->permission(inode, submask, nd);
-		if (!retval) {
-			/*
-			 * Exec permission on a regular file is denied if none
-			 * of the execute bits are set.
-			 *
-			 * This check should be done by the ->permission()
-			 * method.
-			 */
-			if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode) &&
-			    !(inode->i_mode & S_IXUGO))
-				return -EACCES;
-		}
-	} else {
+	if (inode->i_op && inode->i_op->permission)
+		retval = inode->i_op->permission(dentry, submask);
+	else
 		retval = generic_permission(inode, submask, NULL);
-	}
+
 	if (retval)
 		return retval;
 
@@ -286,12 +283,12 @@ int permission(struct inode *inode, int mask, struct nameidata *nd)
 	if (retval)
 		return retval;
 
-	return security_inode_permission(inode, mask, nd);
+	return security_inode_permission(inode, mask);
 }
 
 /**
- * vfs_permission  -  check for access rights to a given path
- * @nd:		lookup result that describes the path
+ * path_permission  -  check for access rights to a given path
+ * @path:	lookup result that describes the path
  * @mask:	right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
  *
  * Used to check for read/write/execute permissions on a path.
@@ -299,9 +296,23 @@ int permission(struct inode *inode, int mask, struct nameidata *nd)
  * for filesystem access without changing the "normal" uids which
  * are used for other things.
  */
-int vfs_permission(struct nameidata *nd, int mask)
+int path_permission(struct path *path, int mask)
 {
-	return permission(nd->path.dentry->d_inode, mask, nd);
+	struct dentry *dentry = path->dentry;
+	struct inode *inode = dentry->d_inode;
+
+	if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) {
+		struct vfsmount *mnt = path->mnt;
+
+		/*
+		 * MAY_EXEC on regular files is denied if the fs is mounted
+		 * with the "noexec" flag.
+		 */
+		if (mnt->mnt_flags & MNT_NOEXEC)
+			return -EACCES;
+	}
+
+	return dentry_permission(dentry, mask);
 }
 
 /**
@@ -309,16 +320,11 @@ int vfs_permission(struct nameidata *nd, int mask)
  * @file:	file to check access rights for
  * @mask:	right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
  *
- * Used to check for read/write/execute permissions on an already opened
- * file.
- *
- * Note:
- *	Do not use this function in new code.  All access checks should
- *	be done using vfs_permission().
+ * This is a helper for path_permission().
  */
 int file_permission(struct file *file, int mask)
 {
-	return permission(file->f_path.dentry->d_inode, mask, NULL);
+	return path_permission(&file->f_path, mask);
 }
 
 /*
@@ -459,8 +465,7 @@ static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name,
  * short-cut DAC fails, then call permission() to do more
  * complete permission check.
  */
-static int exec_permission_lite(struct inode *inode,
-				       struct nameidata *nd)
+static int exec_permission_lite(struct inode *inode)
 {
 	umode_t	mode = inode->i_mode;
 
@@ -486,7 +491,7 @@ static int exec_permission_lite(struct inode *inode,
 
 	return -EACCES;
 ok:
-	return security_inode_permission(inode, MAY_EXEC, nd);
+	return security_inode_permission(inode, MAY_EXEC);
 }
 
 /*
@@ -891,9 +896,9 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
 		unsigned int c;
 
 		nd->flags |= LOOKUP_CONTINUE;
-		err = exec_permission_lite(inode, nd);
+		err = exec_permission_lite(inode);
 		if (err == -EAGAIN)
-			err = vfs_permission(nd, MAY_EXEC);
+			err = path_permission(&nd->path, MAY_EXEC);
  		if (err)
 			break;
 
@@ -1341,7 +1346,7 @@ static struct dentry *lookup_hash(struct nameidata *nd)
 {
 	int err;
 
-	err = permission(nd->path.dentry->d_inode, MAY_EXEC, nd);
+	err = path_permission(&nd->path, MAY_EXEC);
 	if (err)
 		return ERR_PTR(err);
 	return __lookup_hash(&nd->last, nd->path.dentry, nd);
@@ -1389,7 +1394,7 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
 	if (err)
 		return ERR_PTR(err);
 
-	err = permission(base->d_inode, MAY_EXEC, NULL);
+	err = dentry_permission(base, MAY_EXEC);
 	if (err)
 		return ERR_PTR(err);
 	return __lookup_hash(&this, base, NULL);
@@ -1469,8 +1474,10 @@ static inline int check_sticky(struct inode *dir, struct inode *inode)
  * 10. We don't allow removal of NFS sillyrenamed files; it's handled by
  *     nfs_async_unlink().
  */
-static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
+static int may_delete(struct dentry *dir_dentry, struct dentry *victim,
+		      int isdir)
 {
+	struct inode *dir = dir_dentry->d_inode;
 	int error;
 
 	if (!victim->d_inode)
@@ -1479,7 +1486,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
 	BUG_ON(victim->d_parent->d_inode != dir);
 	audit_inode_child(victim->d_name.name, victim, dir);
 
-	error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
+	error = dentry_permission(dir_dentry, MAY_WRITE | MAY_EXEC);
 	if (error)
 		return error;
 	if (IS_APPEND(dir))
@@ -1509,14 +1516,13 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
  *  3. We should have write and exec permissions on dir
  *  4. We can't do it if dir is immutable (done in permission())
  */
-static inline int may_create(struct inode *dir, struct dentry *child,
-			     struct nameidata *nd)
+static inline int may_create(struct dentry *dir_dentry, struct dentry *child)
 {
 	if (child->d_inode)
 		return -EEXIST;
-	if (IS_DEADDIR(dir))
+	if (IS_DEADDIR(dir_dentry->d_inode))
 		return -ENOENT;
-	return permission(dir,MAY_WRITE | MAY_EXEC, nd);
+	return dentry_permission(dir_dentry, MAY_WRITE | MAY_EXEC);
 }
 
 /* 
@@ -1579,10 +1585,11 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
 	}
 }
 
-static int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
-		struct nameidata *nd)
+static int vfs_create(struct dentry *dir_dentry, struct dentry *dentry,
+		      int mode,	struct nameidata *nd)
 {
-	int error = may_create(dir, dentry, nd);
+	struct inode *dir = dir_dentry->d_inode;
+	int error = may_create(dir_dentry, dentry);
 
 	if (error)
 		return error;
@@ -1607,7 +1614,7 @@ int path_create(struct path *dir_path, struct dentry *dentry, int mode,
 	int error = mnt_want_write(dir_path->mnt);
 
 	if (!error) {
-		error = vfs_create(dir_path->dentry->d_inode, dentry, mode, nd);
+		error = vfs_create(dir_path->dentry, dentry, mode, nd);
 		mnt_drop_write(dir_path->mnt);
 	}
 
@@ -1617,6 +1624,7 @@ EXPORT_SYMBOL(path_create);
 
 int may_open(struct nameidata *nd, int acc_mode, int flag)
 {
+	int op;
 	struct dentry *dentry = nd->path.dentry;
 	struct inode *inode = dentry->d_inode;
 	int error;
@@ -1644,7 +1652,8 @@ int may_open(struct nameidata *nd, int acc_mode, int flag)
 		flag &= ~O_TRUNC;
 	}
 
-	error = vfs_permission(nd, acc_mode);
+	op = (nd->flags & LOOKUP_OPEN) ? PERM_OP_OPEN : 0;
+	error = path_permission(&nd->path, acc_mode | op);
 	if (error)
 		return error;
 	/*
@@ -1708,7 +1717,7 @@ static int __open_namei_create(struct nameidata *nd, struct path *path,
 
 	if (!IS_POSIXACL(dir->d_inode))
 		mode &= ~current->fs->umask;
-	error = vfs_create(dir->d_inode, path->dentry, mode, nd);
+	error = vfs_create(dir, path->dentry, mode, nd);
 	mutex_unlock(&dir->d_inode->i_mutex);
 	dput(nd->path.dentry);
 	nd->path.dentry = path->dentry;
@@ -2034,9 +2043,11 @@ fail:
 }
 EXPORT_SYMBOL_GPL(lookup_create);
 
-static int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+static int vfs_mknod(struct dentry *dir_dentry, struct dentry *dentry,
+		     int mode, dev_t dev)
 {
-	int error = may_create(dir, dentry, NULL);
+	struct inode *dir = dir_dentry->d_inode;
+	int error = may_create(dir_dentry, dentry);
 
 	if (error)
 		return error;
@@ -2068,7 +2079,7 @@ int path_mknod(struct path *dir_path, struct dentry *dentry, int mode,
 	int error = mnt_want_write(dir_path->mnt);
 
 	if (!error) {
-		error = vfs_mknod(dir_path->dentry->d_inode, dentry, mode, dev);
+		error = vfs_mknod(dir_path->dentry, dentry, mode, dev);
 		mnt_drop_write(dir_path->mnt);
 	}
 
@@ -2131,9 +2142,10 @@ asmlinkage long sys_mknod(const char __user *filename, int mode, unsigned dev)
 	return sys_mknodat(AT_FDCWD, filename, mode, dev);
 }
 
-static int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+static int vfs_mkdir(struct dentry *dir_dentry, struct dentry *dentry, int mode)
 {
-	int error = may_create(dir, dentry, NULL);
+	struct inode *dir = dir_dentry->d_inode;
+	int error = may_create(dir_dentry, dentry);
 
 	if (error)
 		return error;
@@ -2158,7 +2170,7 @@ int path_mkdir(struct path *dir_path, struct dentry *dentry, int mode)
 	int error = mnt_want_write(dir_path->mnt);
 
 	if (!error) {
-		error = vfs_mkdir(dir_path->dentry->d_inode, dentry, mode);
+		error = vfs_mkdir(dir_path->dentry, dentry, mode);
 		mnt_drop_write(dir_path->mnt);
 	}
 
@@ -2231,9 +2243,10 @@ void dentry_unhash(struct dentry *dentry)
 	spin_unlock(&dcache_lock);
 }
 
-static int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+static int vfs_rmdir(struct dentry *dir_dentry, struct dentry *dentry)
 {
-	int error = may_delete(dir, dentry, 1);
+	struct inode *dir = dir_dentry->d_inode;
+	int error = may_delete(dir_dentry, dentry, 1);
 
 	if (error)
 		return error;
@@ -2269,7 +2282,7 @@ int path_rmdir(struct path *dir_path, struct dentry *dentry)
 	int error = mnt_want_write(dir_path->mnt);
 
 	if (!error) {
-		error = vfs_rmdir(dir_path->dentry->d_inode, dentry);
+		error = vfs_rmdir(dir_path->dentry, dentry);
 		mnt_drop_write(dir_path->mnt);
 	}
 
@@ -2324,9 +2337,10 @@ asmlinkage long sys_rmdir(const char __user *pathname)
 	return do_rmdir(AT_FDCWD, pathname);
 }
 
-static int vfs_unlink(struct inode *dir, struct dentry *dentry)
+static int vfs_unlink(struct dentry *dir_dentry, struct dentry *dentry)
 {
-	int error = may_delete(dir, dentry, 0);
+	struct inode *dir = dir_dentry->d_inode;
+	int error = may_delete(dir_dentry, dentry, 0);
 
 	if (error)
 		return error;
@@ -2360,7 +2374,7 @@ int path_unlink(struct path *dir_path, struct dentry *dentry)
 	int error = mnt_want_write(dir_path->mnt);
 
 	if (!error) {
-		error = vfs_unlink(dir_path->dentry->d_inode, dentry);
+		error = vfs_unlink(dir_path->dentry, dentry);
 		mnt_drop_write(dir_path->mnt);
 	}
 
@@ -2437,9 +2451,11 @@ asmlinkage long sys_unlink(const char __user *pathname)
 	return do_unlinkat(AT_FDCWD, pathname);
 }
 
-static int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
+static int vfs_symlink(struct dentry *dir_dentry, struct dentry *dentry,
+		       const char *oldname)
 {
-	int error = may_create(dir, dentry, NULL);
+	struct inode *dir = dir_dentry->d_inode;
+	int error = may_create(dir_dentry, dentry);
 
 	if (error)
 		return error;
@@ -2464,9 +2480,7 @@ int path_symlink(struct path *dir_path, struct dentry *dentry,
 	int error = mnt_want_write(dir_path->mnt);
 
 	if (!error) {
-		struct inode *dir = dir_path->dentry->d_inode;
-
-		error = vfs_symlink(dir, dentry, oldname);
+		error = vfs_symlink(dir_path->dentry, dentry, oldname);
 		mnt_drop_write(dir_path->mnt);
 	}
 
@@ -2516,15 +2530,17 @@ asmlinkage long sys_symlink(const char __user *oldname, const char __user *newna
 	return sys_symlinkat(oldname, AT_FDCWD, newname);
 }
 
-static int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
+static int vfs_link(struct dentry *old_dentry, struct dentry *new_dir_dentry,
+		    struct dentry *new_dentry)
 {
+	struct inode *dir = new_dir_dentry->d_inode;
 	struct inode *inode = old_dentry->d_inode;
 	int error;
 
 	if (!inode)
 		return -ENOENT;
 
-	error = may_create(dir, new_dentry, NULL);
+	error = may_create(new_dir_dentry, new_dentry);
 	if (error)
 		return error;
 
@@ -2560,9 +2576,7 @@ int path_link(struct dentry *old_dentry, struct path *dir_path,
 	int error = mnt_want_write(dir_path->mnt);
 
 	if (!error) {
-		struct inode *dir = dir_path->dentry->d_inode;
-
-		error = vfs_link(old_dentry, dir, new_dentry);
+		error = vfs_link(old_dentry, dir_path->dentry, new_dentry);
 		mnt_drop_write(dir_path->mnt);
 	}
 
@@ -2672,7 +2686,7 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
 	 * we'll need to flip '..'.
 	 */
 	if (new_dir != old_dir) {
-		error = permission(old_dentry->d_inode, MAY_WRITE, NULL);
+		error = dentry_permission(old_dentry, MAY_WRITE);
 		if (error)
 			return error;
 	}
@@ -2732,9 +2746,11 @@ static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
 	return error;
 }
 
-static int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
-		      struct inode *new_dir, struct dentry *new_dentry)
+static int vfs_rename(struct dentry *old_dir_dentry, struct dentry *old_dentry,
+		      struct dentry *new_dir_dentry, struct dentry *new_dentry)
 {
+	struct inode *old_dir = old_dir_dentry->d_inode;
+	struct inode *new_dir = new_dir_dentry->d_inode;
 	int error;
 	int is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
 	const char *old_name;
@@ -2742,14 +2758,14 @@ static int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 	if (old_dentry->d_inode == new_dentry->d_inode)
  		return 0;
  
-	error = may_delete(old_dir, old_dentry, is_dir);
+	error = may_delete(old_dir_dentry, old_dentry, is_dir);
 	if (error)
 		return error;
 
 	if (!new_dentry->d_inode)
-		error = may_create(new_dir, new_dentry, NULL);
+		error = may_create(new_dir_dentry, new_dentry);
 	else
-		error = may_delete(new_dir, new_dentry, is_dir);
+		error = may_delete(new_dir_dentry, new_dentry, is_dir);
 	if (error)
 		return error;
 
@@ -2785,10 +2801,8 @@ int path_rename(struct path *old_dir_path, struct dentry *old_dentry,
 
 	error = mnt_want_write(mnt);
 	if (!error) {
-		struct inode *old_dir = old_dir_path->dentry->d_inode;
-		struct inode *new_dir = new_dir_path->dentry->d_inode;
-
-		error = vfs_rename(old_dir, old_dentry, new_dir, new_dentry);
+		error = vfs_rename(old_dir_path->dentry, old_dentry,
+				   new_dir_path->dentry, new_dentry);
 		mnt_drop_write(mnt);
 	}
 
@@ -3041,8 +3055,7 @@ EXPORT_SYMBOL(page_symlink);
 EXPORT_SYMBOL(page_symlink_inode_operations);
 EXPORT_SYMBOL(path_lookup);
 EXPORT_SYMBOL(vfs_path_lookup);
-EXPORT_SYMBOL(permission);
-EXPORT_SYMBOL(vfs_permission);
+EXPORT_SYMBOL(path_permission);
 EXPORT_SYMBOL(file_permission);
 EXPORT_SYMBOL(unlock_rename);
 EXPORT_SYMBOL(vfs_follow_link);
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index f288b3e..060bd4d 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -1929,17 +1929,18 @@ 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, struct nameidata *nd)
+int nfs_permission(struct dentry *dentry, int mask)
 {
+	struct inode *inode = dentry->d_inode;
 	struct rpc_cred *cred;
 	int res = 0;
 
 	nfs_inc_stats(inode, NFSIOS_VFSACCESS);
 
-	if (mask == 0)
+	if ((mask & PERMISSION_MASK) == 0)
 		goto out;
 	/* Is this sys_access() ? */
-	if (nd != NULL && (nd->flags & LOOKUP_ACCESS))
+	if ((mask & PERM_OP_MASK) == PERM_OP_ACCESS)
 		goto force_lookup;
 
 	switch (inode->i_mode & S_IFMT) {
@@ -1947,9 +1948,8 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
 			goto out;
 		case S_IFREG:
 			/* NFSv4 has atomic_open... */
-			if (nfs_server_capable(inode, NFS_CAP_ATOMIC_OPEN)
-					&& nd != NULL
-					&& (nd->flags & LOOKUP_OPEN))
+			if (nfs_server_capable(inode, NFS_CAP_ATOMIC_OPEN) &&
+					(mask & PERM_OP_MASK) == PERM_OP_OPEN)
 				goto out;
 			break;
 		case S_IFDIR:
@@ -1969,12 +1969,15 @@ force_lookup:
 
 	cred = rpc_lookup_cred();
 	if (!IS_ERR(cred)) {
-		res = nfs_do_access(inode, cred, mask);
+		res = nfs_do_access(inode, cred, mask & PERMISSION_MASK);
 		put_rpccred(cred);
 	} else
 		res = PTR_ERR(cred);
 	unlock_kernel();
 out:
+	if (res == 0)
+		res = check_execute(inode, mask);
+
 	dfprintk(VFS, "NFS: permission(%s/%ld), mask=0x%x, res=%d\n",
 		inode->i_sb->s_id, inode->i_ino, mask, res);
 	return res;
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index 100ae56..f8240ea 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -41,7 +41,7 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry)
 	struct svc_export *exp = expv;
 	int rv;
 	struct dentry *tdentry;
-	struct dentry *parent;
+	struct path parent = { .mnt = exp->ex_path.mnt };
 
 	if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
 		return 1;
@@ -50,14 +50,15 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry)
 	while (tdentry != exp->ex_path.dentry && !IS_ROOT(tdentry)) {
 		/* make sure parents give x permission to user */
 		int err;
-		parent = dget_parent(tdentry);
-		err = permission(parent->d_inode, MAY_EXEC, NULL);
+
+		parent.dentry = dget_parent(tdentry);
+		err = path_permission(&parent, MAY_EXEC);
 		if (err < 0) {
-			dput(parent);
+			dput(parent.dentry);
 			break;
 		}
 		dput(tdentry);
-		tdentry = parent;
+		tdentry = parent.dentry;
 	}
 	if (tdentry != exp->ex_path.dentry)
 		dprintk("nfsd_acceptable failed at %p %s\n", tdentry, tdentry->d_name.name);
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index d361354..ccda8ce 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1878,6 +1878,10 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
 {
 	struct inode	*inode = dentry->d_inode;
 	int		err;
+	struct path	path = {
+		.mnt = exp->ex_path.mnt,
+		.dentry = dentry,
+	};
 
 	if (acc == MAY_NOP)
 		return 0;
@@ -1942,12 +1946,12 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
 	    inode->i_uid == current->fsuid)
 		return 0;
 
-	err = permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC), NULL);
+	err = path_permission(&path, acc & (MAY_READ|MAY_WRITE|MAY_EXEC));
 
 	/* Allow read access to binaries even when mode 111 */
 	if (err == -EACCES && S_ISREG(inode->i_mode) &&
 	    acc == (MAY_READ | MAY_OWNER_OVERRIDE))
-		err = permission(inode, MAY_EXEC, NULL);
+		err = path_permission(&path, MAY_EXEC);
 
 	return err? nfserrno(err) : 0;
 }
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 57e0d30..0a53a18 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -1176,8 +1176,9 @@ bail:
 	return err;
 }
 
-int ocfs2_permission(struct inode *inode, int mask, struct nameidata *nd)
+int ocfs2_permission(struct dentry *dentry, int mask)
 {
+	struct inode *inode = dentry->d_inode;
 	int ret;
 
 	mlog_entry_void();
diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h
index 048ddca..ef74fa7 100644
--- a/fs/ocfs2/file.h
+++ b/fs/ocfs2/file.h
@@ -62,8 +62,7 @@ int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di,
 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,
-		     struct nameidata *nd);
+int ocfs2_permission(struct dentry *dentry, int mask);
 
 int ocfs2_should_update_atime(struct inode *inode,
 			      struct vfsmount *vfsmnt);
diff --git a/fs/open.c b/fs/open.c
index ceb18bc..6f9cbcc 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -267,7 +267,7 @@ static long do_sys_truncate(const char __user * path, loff_t length)
 	if (error)
 		goto dput_and_out;
 
-	error = vfs_permission(&nd, MAY_WRITE);
+	error = path_permission(&nd.path, MAY_WRITE);
 	if (error)
 		goto mnt_drop_write_and_out;
 
@@ -467,11 +467,11 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
 	else
 		current->cap_effective = current->cap_permitted;
 
-	res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd);
+	res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd);
 	if (res)
 		goto out;
 
-	res = vfs_permission(&nd, mode);
+	res = path_permission(&nd.path, mode | PERM_OP_ACCESS);
 	/* SuS v2 requires we report a read only fs too */
 	if(res || !(mode & S_IWOTH) ||
 	   special_file(nd.path.dentry->d_inode->i_mode))
@@ -509,12 +509,11 @@ asmlinkage long sys_chdir(const char __user * filename)
 	struct nameidata nd;
 	int error;
 
-	error = __user_walk(filename,
-			    LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_CHDIR, &nd);
+	error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &nd);
 	if (error)
 		goto out;
 
-	error = vfs_permission(&nd, MAY_EXEC);
+	error = path_permission(&nd.path, MAY_EXEC | PERM_OP_CHDIR);
 	if (error)
 		goto dput_and_out;
 
@@ -543,7 +542,7 @@ asmlinkage long sys_fchdir(unsigned int fd)
 	if (!S_ISDIR(inode->i_mode))
 		goto out_putf;
 
-	error = file_permission(file, MAY_EXEC);
+	error = file_permission(file, MAY_EXEC | PERM_OP_CHDIR);
 	if (!error)
 		set_fs_pwd(current->fs, &file->f_path);
 out_putf:
@@ -561,7 +560,7 @@ asmlinkage long sys_chroot(const char __user * filename)
 	if (error)
 		goto out;
 
-	error = vfs_permission(&nd, MAY_EXEC);
+	error = path_permission(&nd.path, MAY_EXEC);
 	if (error)
 		goto dput_and_out;
 
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 808cbdc..2d74dbb 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1814,9 +1814,9 @@ 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,
-				struct nameidata *nd)
+static int proc_fd_permission(struct dentry *dentry, int mask)
 {
+	struct inode *inode = dentry->d_inode;
 	int rv;
 
 	rv = generic_permission(inode, mask, NULL);
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 5acc001..f72fae4 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -343,7 +343,7 @@ out:
 	return ret;
 }
 
-static int proc_sys_permission(struct inode *inode, int mask, struct nameidata *nd)
+static int proc_sys_permission(struct dentry *dentry, int mask)
 {
 	/*
 	 * sysctl entries that are not writeable,
@@ -351,7 +351,7 @@ static int proc_sys_permission(struct inode *inode, int mask, struct nameidata *
 	 */
 	struct ctl_table_header *head;
 	struct ctl_table *table;
-	struct dentry *dentry;
+	struct inode *inode = dentry->d_inode;
 	int mode;
 	int depth;
 	int error;
@@ -359,6 +359,7 @@ static int proc_sys_permission(struct inode *inode, int mask, struct nameidata *
 	head = NULL;
 	depth = PROC_I(inode)->fd;
 
+	mask &= PERMISSION_MASK;
 	/* First check the cached permissions, in case we don't have
 	 * enough information to lookup the sysctl table entry.
 	 */
@@ -376,10 +377,9 @@ static int proc_sys_permission(struct inode *inode, int mask, struct nameidata *
 	/* If we can't get a sysctl table entry the permission
 	 * checks on the cached mode will have to be enough.
 	 */
-	if (!nd || !depth)
+	if (!depth)
 		goto out;
 
-	dentry = nd->path.dentry;
 	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
 
 	/* If the entry does not exist deny permission */
@@ -391,6 +391,9 @@ static int proc_sys_permission(struct inode *inode, int mask, struct nameidata *
 	error = sysctl_perm(head->root, table, mask);
 out:
 	sysctl_head_finish(head);
+	if (!error)
+		error = check_execute(inode, mask);
+
 	return error;
 }
 
diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c
index 4125468..6d710e3 100644
--- a/fs/reiserfs/xattr.c
+++ b/fs/reiserfs/xattr.c
@@ -1270,8 +1270,10 @@ static int reiserfs_check_acl(struct inode *inode, int mask)
 	return error;
 }
 
-int reiserfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+int reiserfs_permission(struct dentry *dentry, int mask)
 {
+	struct inode *inode = dentry->d_inode;
+
 	/*
 	 * We don't do permission checks on the internal objects.
 	 * Permissions are determined by the "owning" object.
diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c
index efbe29a..7000dc2 100644
--- a/fs/smbfs/file.c
+++ b/fs/smbfs/file.c
@@ -408,18 +408,19 @@ 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, struct nameidata *nd)
+smb_file_permission(struct dentry *dentry, int mask)
 {
-	int mode = inode->i_mode;
-	int error = 0;
+	int mode = dentry->d_inode->i_mode;
 
 	VERBOSE("mode=%x, mask=%x\n", mode, mask);
 
 	/* Look at user permissions */
+	mask &= PERMISSION_MASK;
 	mode >>= 6;
 	if ((mode & 7 & mask) != mask)
-		error = -EACCES;
-	return error;
+		return -EACCES;
+
+	return check_execute(dentry->d_inode, mask);
 }
 
 const struct file_operations smb_file_operations =
diff --git a/fs/utimes.c b/fs/utimes.c
index 2390e3a..3b85b0b 100644
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -141,7 +141,7 @@ static int do_utimes_name(int dfd, char __user *filename,
 			goto out_path_put;
 
 		if (!is_owner_or_cap(inode)) {
-			error = vfs_permission(&nd, MAY_WRITE);
+			error = path_permission(&nd.path, MAY_WRITE);
 			if (error)
 				goto out_path_put;
 		}
diff --git a/fs/xattr.c b/fs/xattr.c
index 7eb9cd9..b403da3 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -26,8 +26,10 @@
  * because different namespaces have very different rules.
  */
 static int
-xattr_permission(struct inode *inode, const char *name, int mask)
+xattr_permission(struct path *path, const char *name, int mask)
 {
+	struct inode *inode = path->dentry->d_inode;
+
 	/*
 	 * We can never set or remove an extended attribute on a read-only
 	 * filesystem  or on an immutable / append-only inode.
@@ -63,17 +65,18 @@ xattr_permission(struct inode *inode, const char *name, int mask)
 			return -EPERM;
 	}
 
-	return permission(inode, mask, NULL);
+	return path_permission(path, mask);
 }
 
 static int
-vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
+vfs_setxattr(struct path *path, const char *name, const void *value,
 		size_t size, int flags)
 {
+	struct dentry *dentry = path->dentry;
 	struct inode *inode = dentry->d_inode;
 	int error;
 
-	error = xattr_permission(inode, name, MAY_WRITE);
+	error = xattr_permission(path, name, MAY_WRITE);
 	if (error)
 		return error;
 
@@ -108,7 +111,7 @@ int path_setxattr(struct path *path, const char *name, const void *value,
 	int error = mnt_want_write(path->mnt);
 
 	if (!error) {
-		error = vfs_setxattr(path->dentry, name, value, size, flags);
+		error = vfs_setxattr(path, name, value, size, flags);
 		mnt_drop_write(path->mnt);
 	}
 
@@ -150,7 +153,7 @@ path_getxattr(struct path *path, const char *name, void *value, size_t size)
 	struct inode *inode = dentry->d_inode;
 	int error;
 
-	error = xattr_permission(inode, name, MAY_READ);
+	error = xattr_permission(path, name, MAY_READ);
 	if (error)
 		return error;
 
@@ -202,15 +205,16 @@ path_listxattr(struct path *path, char *list, size_t size)
 EXPORT_SYMBOL_GPL(path_listxattr);
 
 static int
-vfs_removexattr(struct dentry *dentry, const char *name)
+vfs_removexattr(struct path *path, const char *name)
 {
+	struct dentry *dentry = path->dentry;
 	struct inode *inode = dentry->d_inode;
 	int error;
 
 	if (!inode->i_op->removexattr)
 		return -EOPNOTSUPP;
 
-	error = xattr_permission(inode, name, MAY_WRITE);
+	error = xattr_permission(path, name, MAY_WRITE);
 	if (error)
 		return error;
 
@@ -232,7 +236,7 @@ int path_removexattr(struct path *path, const char *name)
 	int error = mnt_want_write(path->mnt);
 
 	if (!error) {
-		error = vfs_removexattr(path->dentry, name);
+		error = vfs_removexattr(path, name);
 		mnt_drop_write(path->mnt);
 	}
 
diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c
index 2bf287e..5b31e4a 100644
--- a/fs/xfs/linux-2.6/xfs_iops.c
+++ b/fs/xfs/linux-2.6/xfs_iops.c
@@ -588,11 +588,10 @@ xfs_check_acl(
 
 STATIC int
 xfs_vn_permission(
-	struct inode		*inode,
-	int			mask,
-	struct nameidata	*nd)
+	struct dentry		*dentry,
+	int			mask)
 {
-	return generic_permission(inode, mask, xfs_check_acl);
+	return generic_permission(dentry->d_inode, mask, xfs_check_acl);
 }
 #else
 #define xfs_vn_permission NULL
diff --git a/include/linux/coda_linux.h b/include/linux/coda_linux.h
index 31b7531..663559c 100644
--- a/include/linux/coda_linux.h
+++ b/include/linux/coda_linux.h
@@ -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, struct nameidata *nd);
+int coda_permission(struct dentry *dentry, int mask);
 int coda_revalidate_inode(struct dentry *);
 int coda_getattr(struct vfsmount *, struct dentry *, struct kstat *);
 int coda_setattr(struct dentry *, struct iattr *);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f7f273b..fb26e0f 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -60,6 +60,16 @@ extern int dir_notify_enable;
 #define MAY_WRITE 2
 #define MAY_READ 4
 #define MAY_APPEND 8
+#define PERMISSION_MASK (MAY_EXEC | MAY_WRITE | MAY_READ | MAY_APPEND)
+
+/*
+ * Special "MAY_" flags for i_op->permission(), indicating the filesystem
+ * operation for which we are performing the permission check.
+ */
+#define PERM_OP_MASK	(0xf << 28)
+#define PERM_OP_OPEN	(0x1 << 28)
+#define PERM_OP_ACCESS	(0x2 << 28)
+#define PERM_OP_CHDIR	(0x3 << 28)
 
 #define FMODE_READ 1
 #define FMODE_WRITE 2
@@ -1123,7 +1133,7 @@ extern void unlock_super(struct super_block *);
 /*
  * VFS helper functions..
  */
-extern int vfs_permission(struct nameidata *, int);
+extern int path_permission(struct path *, int);
 extern int path_create(struct path *, struct dentry *, int, struct nameidata *);
 extern int path_mkdir(struct path *, struct dentry *, int);
 extern int path_mknod(struct path *, struct dentry *, int, dev_t);
@@ -1264,7 +1274,7 @@ 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 dentry *, 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);
@@ -1758,9 +1768,9 @@ extern sector_t bmap(struct inode *, sector_t);
 #endif
 extern int notify_change(struct dentry *, struct iattr *);
 extern int path_setattr(struct path *, struct iattr *);
-extern int permission(struct inode *, int, struct nameidata *);
 extern int generic_permission(struct inode *, int,
 		int (*check_acl)(struct inode *, int));
+extern int check_execute(struct inode *, int);
 
 extern int get_write_access(struct inode *);
 extern int deny_write_access(struct file *);
diff --git a/include/linux/namei.h b/include/linux/namei.h
index 24d88e9..75e8270 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -54,8 +54,6 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
  */
 #define LOOKUP_OPEN		(0x0100)
 #define LOOKUP_CREATE		(0x0200)
-#define LOOKUP_ACCESS		(0x0400)
-#define LOOKUP_CHDIR		(0x0800)
 
 extern int __user_walk(const char __user *, unsigned, struct nameidata *);
 extern int __user_walk_fd(int dfd, const char __user *, unsigned, struct nameidata *);
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 27d6a8d..07ef8a8 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -322,7 +322,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, struct nameidata *);
+extern int nfs_permission(struct dentry *, 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);
diff --git a/include/linux/reiserfs_xattr.h b/include/linux/reiserfs_xattr.h
index 66a9681..91f9dbb 100644
--- a/include/linux/reiserfs_xattr.h
+++ b/include/linux/reiserfs_xattr.h
@@ -55,7 +55,7 @@ int reiserfs_removexattr(struct dentry *dentry, const char *name);
 int reiserfs_delete_xattrs(struct inode *inode);
 int reiserfs_chown_xattrs(struct inode *inode, struct iattr *attrs);
 int reiserfs_xattr_init(struct super_block *sb, int mount_flags);
-int reiserfs_permission(struct inode *inode, int mask, struct nameidata *nd);
+int reiserfs_permission(struct dentry *dentry, int mask);
 
 int reiserfs_xattr_del(struct inode *, const char *);
 int reiserfs_xattr_get(const struct inode *, const char *, void *, size_t);
diff --git a/include/linux/security.h b/include/linux/security.h
index 50737c7..030b137 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -407,7 +407,6 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *	called when the actual read/write operations are performed.
  *	@inode contains the inode structure to check.
  *	@mask contains the permission mask.
- *	@nd contains the nameidata (may be NULL).
  *	Return 0 if permission is granted.
  * @inode_setattr:
  *	Check permission before setting file attributes.  Note that the kernel
@@ -1370,7 +1369,7 @@ struct security_operations {
 			     struct inode *new_dir, struct dentry *new_dentry);
 	int (*inode_readlink) (struct dentry *dentry);
 	int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd);
-	int (*inode_permission) (struct inode *inode, int mask, struct nameidata *nd);
+	int (*inode_permission) (struct inode *inode, int mask);
 	int (*inode_setattr)	(struct dentry *dentry, struct iattr *attr);
 	int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry);
 	void (*inode_delete) (struct inode *inode);
@@ -1641,7 +1640,7 @@ int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
 			  struct inode *new_dir, struct dentry *new_dentry);
 int security_inode_readlink(struct dentry *dentry);
 int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd);
-int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd);
+int security_inode_permission(struct inode *inode, int mask);
 int security_inode_setattr(struct dentry *dentry, struct iattr *attr);
 int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry);
 void security_inode_delete(struct inode *inode);
@@ -2032,8 +2031,7 @@ static inline int security_inode_follow_link(struct dentry *dentry,
 	return 0;
 }
 
-static inline int security_inode_permission(struct inode *inode, int mask,
-					     struct nameidata *nd)
+static inline int security_inode_permission(struct inode *inode, int mask)
 {
 	return 0;
 }
diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
index f2d12d5..be2ae57 100644
--- a/include/linux/shmem_fs.h
+++ b/include/linux/shmem_fs.h
@@ -43,7 +43,7 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode)
 }
 
 #ifdef CONFIG_TMPFS_POSIX_ACL
-int shmem_permission(struct inode *, int, struct nameidata *);
+int shmem_permission(struct dentry *, int);
 int shmem_acl_init(struct inode *, struct inode *);
 void shmem_acl_destroy_inode(struct inode *);
 
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index c229691..265a27d 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -647,19 +647,22 @@ static struct file *do_open(struct dentry *dentry, int oflag)
 static int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE,
 					MAY_READ | MAY_WRITE };
 
+	struct path path = {
+		.mnt = mqueue_mnt,
+		.dentry = dentry,
+	};
+
 	if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) {
-		dput(dentry);
-		mntput(mqueue_mnt);
+		path_put(&path);
 		return ERR_PTR(-EINVAL);
 	}
 
-	if (permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE], NULL)) {
-		dput(dentry);
-		mntput(mqueue_mnt);
+	if (path_permission(&path, oflag2acc[oflag & O_ACCMODE])) {
+		path_put(&path);
 		return ERR_PTR(-EACCES);
 	}
 
-	return dentry_open(dentry, mqueue_mnt, oflag);
+	return dentry_open(path.dentry, path.mnt, oflag);
 }
 
 asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode,
diff --git a/mm/shmem_acl.c b/mm/shmem_acl.c
index f5664c5..042f244 100644
--- a/mm/shmem_acl.c
+++ b/mm/shmem_acl.c
@@ -191,7 +191,7 @@ shmem_check_acl(struct inode *inode, int mask)
  * shmem_permission  -  permission() inode operation
  */
 int
-shmem_permission(struct inode *inode, int mask, struct nameidata *nd)
+shmem_permission(struct dentry *dentry, int mask)
 {
-	return generic_permission(inode, mask, shmem_check_acl);
+	return generic_permission(dentry->d_inode, mask, shmem_check_acl);
 }
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index abd4614..5401cb8 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -713,7 +713,7 @@ static struct sock *unix_find_other(struct net *net,
 		err = path_lookup(sunname->sun_path, LOOKUP_FOLLOW, &nd);
 		if (err)
 			goto fail;
-		err = vfs_permission(&nd, MAY_WRITE);
+		err = path_permission(&nd.path, MAY_WRITE);
 		if (err)
 			goto put_fail;
 
diff --git a/security/dummy.c b/security/dummy.c
index f50c6c3..e5ad811 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -345,7 +345,7 @@ static int dummy_inode_follow_link (struct dentry *dentry,
 	return 0;
 }
 
-static int dummy_inode_permission (struct inode *inode, int mask, struct nameidata *nd)
+static int dummy_inode_permission (struct inode *inode, int mask)
 {
 	return 0;
 }
diff --git a/security/security.c b/security/security.c
index 01f0acd..5fd5249 100644
--- a/security/security.c
+++ b/security/security.c
@@ -463,11 +463,11 @@ int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd)
 	return security_ops->inode_follow_link(dentry, nd);
 }
 
-int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd)
+int security_inode_permission(struct inode *inode, int mask)
 {
 	if (unlikely(IS_PRIVATE(inode)))
 		return 0;
-	return security_ops->inode_permission(inode, mask, nd);
+	return security_ops->inode_permission(inode, mask);
 }
 
 int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 1c864c0..491bf88 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2579,15 +2579,15 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na
 	return dentry_has_perm(current, NULL, dentry, FILE__READ);
 }
 
-static int selinux_inode_permission(struct inode *inode, int mask,
-				    struct nameidata *nd)
+static int selinux_inode_permission(struct inode *inode, int mask)
 {
 	int rc;
 
-	rc = secondary_ops->inode_permission(inode, mask, nd);
+	rc = secondary_ops->inode_permission(inode, mask);
 	if (rc)
 		return rc;
 
+	mask &= PERMISSION_MASK;
 	if (!mask) {
 		/* No permission to check.  Existence test. */
 		return 0;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index b5c8f92..cee792f 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -515,14 +515,12 @@ static int smack_inode_rename(struct inode *old_inode,
  * smack_inode_permission - Smack version of permission()
  * @inode: the inode in question
  * @mask: the access requested
- * @nd: unused
  *
  * This is the important Smack hook.
  *
  * Returns 0 if access is permitted, -EACCES otherwise
  */
-static int smack_inode_permission(struct inode *inode, int mask,
-				  struct nameidata *nd)
+static int smack_inode_permission(struct inode *inode, int mask)
 {
 	/*
 	 * No permission to check. Existence test. Yup, it's there.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ