[patch resend] vfs: move executable checking into ->permission()

From: Miklos Szeredi
Date: Wed Jul 30 2008 - 09:02:23 EST


A ->permission() API cleanup patch that I missed resending...

Please apply to 2.6.27 or .28 at your discretion.

Thanks,
Miklos

----
From: Miklos Szeredi <mszeredi@xxxxxxx>

For execute permission on a regular files we need to check if file has
any execute bits at all, regardless of capabilites.

This check is normally performed by generic_permission() but was also
added to the case when the filesystem defines its own ->permission()
method. In the latter case the filesystem should be responsible for
performing this check.

So create a helper function check_execute() that returns -EACCESS if
MAY_EXEC is present, the inode is a regular file and no execute bits
are present in inode->i_mode.

Call this function from filesystems which don't call
generic_permission() from their ->permission() methods and which
aren't already performing this check. Also remove the check from
dentry_permission().

The new code should be equivalent to the old.

[Tetsuo Handa <penguin-kernel@xxxxxxxxxxxxxxxxxxx>: export
check_execute() to modules]

Signed-off-by: Miklos Szeredi <mszeredi@xxxxxxx>
---
fs/cifs/cifsfs.c | 2 +-
fs/coda/dir.c | 4 ++++
fs/coda/pioctl.c | 2 +-
fs/hfs/inode.c | 2 +-
fs/namei.c | 39 ++++++++++++++++++++++++---------------
fs/nfs/dir.c | 3 +++
fs/proc/proc_sysctl.c | 3 +++
fs/smbfs/file.c | 6 +++---
include/linux/fs.h | 1 +
9 files changed, 41 insertions(+), 21 deletions(-)

Index: linux-2.6/fs/cifs/cifsfs.c
===================================================================
--- linux-2.6.orig/fs/cifs/cifsfs.c 2008-07-30 14:38:22.000000000 +0200
+++ linux-2.6/fs/cifs/cifsfs.c 2008-07-30 14:39:31.000000000 +0200
@@ -274,7 +274,7 @@ static int cifs_permission(struct inode
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,
Index: linux-2.6/fs/coda/dir.c
===================================================================
--- linux-2.6.orig/fs/coda/dir.c 2008-07-30 14:38:22.000000000 +0200
+++ linux-2.6/fs/coda/dir.c 2008-07-30 14:39:31.000000000 +0200
@@ -146,6 +146,10 @@ int coda_permission(struct inode *inode,
if (!mask)
return 0;

+ error = check_execute(inode, mask);
+ if (error)
+ return error;
+
lock_kernel();

if (coda_cache_check(inode, mask))
Index: linux-2.6/fs/coda/pioctl.c
===================================================================
--- linux-2.6.orig/fs/coda/pioctl.c 2008-07-30 14:38:22.000000000 +0200
+++ linux-2.6/fs/coda/pioctl.c 2008-07-30 14:39:31.000000000 +0200
@@ -43,7 +43,7 @@ const struct file_operations coda_ioctl_
/* the coda pioctl inode ops */
static int coda_ioctl_permission(struct inode *inode, int mask)
{
- return 0;
+ return check_execute(dentry->d_inode, mask);
}

static int coda_pioctl(struct inode * inode, struct file * filp,
Index: linux-2.6/fs/hfs/inode.c
===================================================================
--- linux-2.6.orig/fs/hfs/inode.c 2008-07-30 14:38:22.000000000 +0200
+++ linux-2.6/fs/hfs/inode.c 2008-07-30 14:39:31.000000000 +0200
@@ -514,7 +514,7 @@ void hfs_clear_inode(struct inode *inode
static int hfs_permission(struct inode *inode, int mask)
{
if (S_ISREG(inode->i_mode) && mask & MAY_EXEC)
- return 0;
+ return check_execute(inode, mask);
return generic_permission(inode, mask, NULL);
}

Index: linux-2.6/fs/namei.c
===================================================================
--- linux-2.6.orig/fs/namei.c 2008-07-30 14:39:31.000000000 +0200
+++ linux-2.6/fs/namei.c 2008-07-30 14:39:31.000000000 +0200
@@ -227,6 +227,27 @@ int generic_permission(struct inode *ino
return -EACCES;
}

+/**
+ * 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)
+{
+ if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode) &&
+ !(inode->i_mode & S_IXUGO))
+ return -EACCES;
+
+ return 0;
+}
+EXPORT_SYMBOL(check_execute);
+
int inode_permission(struct inode *inode, int mask)
{
int retval;
@@ -249,23 +270,11 @@ int inode_permission(struct inode *inode
}

/* Ordinary permission routines do not understand MAY_APPEND. */
- if (inode->i_op && inode->i_op->permission) {
+ if (inode->i_op && inode->i_op->permission)
retval = inode->i_op->permission(inode, mask);
- 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 {
+ else
retval = generic_permission(inode, mask, NULL);
- }
+
if (retval)
return retval;

Index: linux-2.6/fs/nfs/dir.c
===================================================================
--- linux-2.6.orig/fs/nfs/dir.c 2008-07-30 14:38:22.000000000 +0200
+++ linux-2.6/fs/nfs/dir.c 2008-07-30 14:39:31.000000000 +0200
@@ -1949,6 +1949,9 @@ force_lookup:
} else
res = PTR_ERR(cred);
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;
Index: linux-2.6/fs/proc/proc_sysctl.c
===================================================================
--- linux-2.6.orig/fs/proc/proc_sysctl.c 2008-07-30 14:38:22.000000000 +0200
+++ linux-2.6/fs/proc/proc_sysctl.c 2008-07-30 14:39:31.000000000 +0200
@@ -311,6 +311,9 @@ static int proc_sys_permission(struct in
error = sysctl_perm(head->root, table, mask);

sysctl_head_finish(head);
+ if (!error)
+ error = check_execute(inode, mask);
+
return error;
}

Index: linux-2.6/include/linux/fs.h
===================================================================
--- linux-2.6.orig/include/linux/fs.h 2008-07-30 14:39:31.000000000 +0200
+++ linux-2.6/include/linux/fs.h 2008-07-30 14:39:31.000000000 +0200
@@ -1777,6 +1777,7 @@ extern int notify_change(struct dentry *
extern int inode_permission(struct inode *, int);
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 *);
Index: linux-2.6/fs/smbfs/file.c
===================================================================
--- linux-2.6.orig/fs/smbfs/file.c 2008-07-30 14:38:22.000000000 +0200
+++ linux-2.6/fs/smbfs/file.c 2008-07-30 14:39:31.000000000 +0200
@@ -411,15 +411,15 @@ static int
smb_file_permission(struct inode *inode, int mask)
{
int mode = inode->i_mode;
- int error = 0;

VERBOSE("mode=%x, mask=%x\n", mode, mask);

/* Look at user permissions */
mode >>= 6;
if (mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC))
- error = -EACCES;
- return error;
+ return -EACCES;
+
+ return check_execute(inode, mask);
}

static loff_t smb_remote_llseek(struct file *file, loff_t offset, int origin)
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/