fs/nfs/dir.c | 80 +++++++++++++++++++++++++++++++---------------- fs/nfs/inode.c | 16 ++++++++- fs/nfs/nfs3proc.c | 8 ++-- include/linux/nfs_fs_i.h | 12 +++++++ include/linux/nfs_xdr.h | 2 - 5 files changed, 86 insertions(+), 32 deletions(-) diff -u --recursive --new-file --show-c-function linux-2.4.26-02-seekdir/fs/nfs/dir.c linux-2.4.26-03-access/fs/nfs/dir.c --- linux-2.4.26-02-seekdir/fs/nfs/dir.c 2004-04-16 09:35:24.000000000 -0700 +++ linux-2.4.26-03-access/fs/nfs/dir.c 2004-04-16 09:35:26.000000000 -0700 @@ -1115,34 +1115,62 @@ out: int nfs_permission(struct inode *inode, int mask) { - int error = vfs_permission(inode, mask); + struct nfs_access_cache *cache = &NFS_I(inode)->cache_access; + struct rpc_cred *cred; + int mode = inode->i_mode; + int error; + + if (mask & MAY_WRITE) { + /* + * + * Nobody gets write access to a read-only fs. + * + */ + if (IS_RDONLY(inode) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) + return -EROFS; + + /* + * + * Nobody gets write access to an immutable file. + * + */ + if (IS_IMMUTABLE(inode)) + return -EACCES; + } if (!NFS_PROTO(inode)->access) - goto out; - - if (error == -EROFS) - goto out; - - /* - * Trust UNIX mode bits except: - * - * 1) When override capabilities may have been invoked - * 2) When root squashing may be involved - * 3) When ACLs may overturn a negative answer */ - if (!capable(CAP_DAC_OVERRIDE) && !capable(CAP_DAC_READ_SEARCH) - && (current->fsuid != 0) && (current->fsgid != 0) - && error != -EACCES) - goto out; - - error = NFS_PROTO(inode)->access(inode, mask, 0); - - if (error == -EACCES && NFS_CLIENT(inode)->cl_droppriv && - current->uid != 0 && current->gid != 0 && - (current->fsuid != current->uid || current->fsgid != current->gid)) - error = NFS_PROTO(inode)->access(inode, mask, 1); - - out: - return error; + goto out_notsup; + cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); + if (cache->cred == cred + && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))) { + if (!cache->err) { + /* Is the mask a subset of an accepted mask? */ + if ((cache->mask & mask) == mask) + goto out_cached; + } else { + /* ...or is it a superset of a rejected mask? */ + if ((cache->mask & mask) == cache->mask) + goto out_cached; + } + } + error = NFS_PROTO(inode)->access(inode, cred, mask); + if (!error || error == -EACCES) { + cache->jiffies = jiffies; + if (cache->cred) + put_rpccred(cache->cred); + cache->cred = cred; + cache->mask = mask; + cache->err = error; + return error; + } + put_rpccred(cred); +out_notsup: + nfs_revalidate_inode(NFS_SERVER(inode), inode); + return vfs_permission(inode, mask); +out_cached: + put_rpccred(cred); + return cache->err; } /* diff -u --recursive --new-file --show-c-function linux-2.4.26-02-seekdir/fs/nfs/inode.c linux-2.4.26-03-access/fs/nfs/inode.c --- linux-2.4.26-02-seekdir/fs/nfs/inode.c 2004-04-14 11:28:36.000000000 -0700 +++ linux-2.4.26-03-access/fs/nfs/inode.c 2004-04-16 09:35:26.000000000 -0700 @@ -146,10 +146,14 @@ nfs_delete_inode(struct inode * inode) static void nfs_clear_inode(struct inode *inode) { - struct rpc_cred *cred = NFS_I(inode)->mm_cred; + struct nfs_inode_info *nfsi = NFS_I(inode); + struct rpc_cred *cred = nfsi->mm_cred; if (cred) put_rpccred(cred); + cred = nfsi->cache_access.cred; + if (cred) + put_rpccred(cred); } void @@ -1068,6 +1072,16 @@ __nfs_refresh_inode(struct inode *inode, NFS_CACHE_ISIZE(inode) = new_size; inode->i_size = new_isize; + if (inode->i_mode != fattr->mode || + inode->i_uid != fattr->uid || + inode->i_gid != fattr->gid) { + struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred; + if (*cred) { + put_rpccred(*cred); + *cred = NULL; + } + } + inode->i_mode = fattr->mode; inode->i_nlink = fattr->nlink; inode->i_uid = fattr->uid; diff -u --recursive --new-file --show-c-function linux-2.4.26-02-seekdir/fs/nfs/nfs3proc.c linux-2.4.26-03-access/fs/nfs/nfs3proc.c --- linux-2.4.26-02-seekdir/fs/nfs/nfs3proc.c 2004-04-14 11:28:23.000000000 -0700 +++ linux-2.4.26-03-access/fs/nfs/nfs3proc.c 2004-04-16 09:35:26.000000000 -0700 @@ -117,12 +117,13 @@ nfs3_proc_lookup(struct inode *dir, stru } static int -nfs3_proc_access(struct inode *inode, int mode, int ruid) +nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode) { struct nfs_fattr fattr; struct nfs3_accessargs arg = { NFS_FH(inode), 0 }; struct nfs3_accessres res = { &fattr, 0 }; - int status, flags; + struct rpc_message msg = { NFS3PROC_ACCESS, &arg, &res, cred }; + int status; dprintk("NFS call access\n"); fattr.valid = 0; @@ -140,8 +141,7 @@ nfs3_proc_access(struct inode *inode, in if (mode & MAY_EXEC) arg.access |= NFS3_ACCESS_EXECUTE; } - flags = (ruid) ? RPC_CALL_REALUID : 0; - status = rpc_call(NFS_CLIENT(inode), NFS3PROC_ACCESS, &arg, &res, flags); + status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); nfs_refresh_inode(inode, &fattr); dprintk("NFS reply access\n"); diff -u --recursive --new-file --show-c-function linux-2.4.26-02-seekdir/include/linux/nfs_fs_i.h linux-2.4.26-03-access/include/linux/nfs_fs_i.h --- linux-2.4.26-02-seekdir/include/linux/nfs_fs_i.h 2004-04-14 11:28:18.000000000 -0700 +++ linux-2.4.26-03-access/include/linux/nfs_fs_i.h 2004-04-16 09:35:26.000000000 -0700 @@ -6,6 +6,16 @@ #include /* + * NFSv3 Access mode cache + */ +struct nfs_access_cache { + unsigned long jiffies; + struct rpc_cred * cred; + int mask; + int err; +}; + +/* * nfs fs inode data in memory */ struct nfs_inode_info { @@ -54,6 +64,8 @@ struct nfs_inode_info { */ unsigned long cache_mtime_jiffies; + struct nfs_access_cache cache_access; + /* * This is the cookie verifier used for NFSv3 readdir * operations diff -u --recursive --new-file --show-c-function linux-2.4.26-02-seekdir/include/linux/nfs_xdr.h linux-2.4.26-03-access/include/linux/nfs_xdr.h --- linux-2.4.26-02-seekdir/include/linux/nfs_xdr.h 2004-04-14 11:28:46.000000000 -0700 +++ linux-2.4.26-03-access/include/linux/nfs_xdr.h 2004-04-16 09:35:26.000000000 -0700 @@ -298,7 +298,7 @@ struct nfs_rpc_ops { struct iattr *); int (*lookup) (struct inode *, struct qstr *, struct nfs_fh *, struct nfs_fattr *); - int (*access) (struct inode *, int , int); + int (*access) (struct inode *, struct rpc_cred *, int); int (*readlink)(struct inode *, struct page *); int (*read) (struct inode *, struct rpc_cred *, struct nfs_fattr *,