NFS: Clean up nfs_permission(). Fix a bug whereby we are failing to test for permissions on opendir(). Optimize away permissions checks that request MAY_WRITE on directories. Ensure that VFS sets LOOKUP_CONTINUE before calling permission(). Signed-off-by: Trond Myklebust --- fs/nfs/dir.c | 36 +++++++++++++++++++++++++----------- fs/nfs/inode.c | 1 + include/linux/nfs_fs_sb.h | 1 + 3 files changed, 27 insertions(+), 11 deletions(-) Index: linux-2.6.11/fs/nfs/dir.c =================================================================== --- linux-2.6.11.orig/fs/nfs/dir.c +++ linux-2.6.11/fs/nfs/dir.c @@ -1498,21 +1498,34 @@ out: int nfs_permission(struct inode *inode, int mask, struct nameidata *nd) { struct rpc_cred *cred; - int res; + int res = 0; if (mask == 0) - return 0; - - /* Are we checking permissions on anything other than lookup/execute? */ - if ((mask & MAY_EXEC) == 0) { - /* We only need to check permissions on file open() and access() */ - if (!nd || !(nd->flags & (LOOKUP_OPEN|LOOKUP_ACCESS))) - return 0; - /* NFSv4 has atomic_open... */ - if (NFS_PROTO(inode)->version > 3 && (nd->flags & LOOKUP_OPEN)) - return 0; + goto out; + /* Is this sys_access() ? */ + if (nd != NULL && (nd->flags & LOOKUP_ACCESS)) + goto force_lookup; + + switch (inode->i_mode & S_IFMT) { + case S_IFLNK: + goto out; + case S_IFREG: + /* NFSv4 has atomic_open... */ + if (nfs_server_capable(inode, NFS_CAP_ATOMIC_OPEN) + && nd != NULL + && (nd->flags & LOOKUP_OPEN)) + goto out; + break; + case S_IFDIR: + /* + * Optimize away all write operations, since the server + * will check permissions when we perform the op. + */ + if ((mask & MAY_WRITE) && !(mask & MAY_READ)) + goto out; } +force_lookup: lock_kernel(); if (!NFS_PROTO(inode)->access) @@ -1522,6 +1535,7 @@ int nfs_permission(struct inode *inode, res = nfs_do_access(inode, cred, mask); put_rpccred(cred); unlock_kernel(); +out: return res; out_notsup: nfs_revalidate_inode(NFS_SERVER(inode), inode); Index: linux-2.6.11/include/linux/nfs_fs_sb.h =================================================================== --- linux-2.6.11.orig/include/linux/nfs_fs_sb.h +++ linux-2.6.11/include/linux/nfs_fs_sb.h @@ -53,5 +53,6 @@ struct nfs_server { #define NFS_CAP_HARDLINKS (1U << 1) #define NFS_CAP_SYMLINKS (1U << 2) #define NFS_CAP_ACLS (1U << 3) +#define NFS_CAP_ATOMIC_OPEN (1U << 4) #endif Index: linux-2.6.11/fs/nfs/inode.c =================================================================== --- linux-2.6.11.orig/fs/nfs/inode.c +++ linux-2.6.11/fs/nfs/inode.c @@ -1542,6 +1542,7 @@ static int nfs4_fill_super(struct super_ if (data->wsize != 0) server->wsize = nfs_block_size(data->wsize, NULL); server->flags = data->flags & NFS_MOUNT_FLAGMASK; + server->caps = NFS_CAP_ATOMIC_OPEN; server->acregmin = data->acregmin*HZ; server->acregmax = data->acregmax*HZ;