diff --git a/fs/nfs/nfs3acl.c b/fs/nfs/nfs3acl.c index 89b6468700e770215cb063843b6a983bbfece208..451112ff9aa47cda581abb6f4c028a5ca4379f77 100644 --- a/fs/nfs/nfs3acl.c +++ b/fs/nfs/nfs3acl.c @@ -113,6 +113,69 @@ int nfs3_removexattr(struct dentry *dentry, const char *name) return nfs3_proc_setacl(inode, type, NULL); } +static void __nfs3_forget_cached_acls(struct nfs_inode *nfsi) +{ + if (nfsi->acl_access != ERR_PTR(-EAGAIN)) { + posix_acl_release(nfsi->acl_access); + nfsi->acl_access = ERR_PTR(-EAGAIN); + } + if (nfsi->acl_default != ERR_PTR(-EAGAIN)) { + posix_acl_release(nfsi->acl_default); + nfsi->acl_default = ERR_PTR(-EAGAIN); + } +} + +void nfs3_forget_cached_acls(struct inode *inode) +{ + dprintk("NFS: nfs3_forget_cached_acls(%s/%ld)\n", inode->i_sb->s_id, + inode->i_ino); + spin_lock(&inode->i_lock); + __nfs3_forget_cached_acls(NFS_I(inode)); + spin_unlock(&inode->i_lock); +} + +static struct posix_acl *nfs3_get_cached_acl(struct inode *inode, int type) +{ + struct nfs_inode *nfsi = NFS_I(inode); + struct posix_acl *acl = ERR_PTR(-EAGAIN); + + spin_lock(&inode->i_lock); + switch(type) { + case ACL_TYPE_ACCESS: + acl = nfsi->acl_access; + break; + + case ACL_TYPE_DEFAULT: + acl = nfsi->acl_default; + break; + + default: + return ERR_PTR(-EINVAL); + } + if (acl == ERR_PTR(-EAGAIN)) + acl = ERR_PTR(-EAGAIN); + else + acl = posix_acl_dup(acl); + spin_unlock(&inode->i_lock); + dprintk("NFS: nfs3_get_cached_acl(%s/%ld, %d) = %p\n", inode->i_sb->s_id, + inode->i_ino, type, acl); + return acl; +} + +static void nfs3_cache_acls(struct inode *inode, struct posix_acl *acl, + struct posix_acl *dfacl) +{ + struct nfs_inode *nfsi = NFS_I(inode); + + dprintk("nfs3_cache_acls(%s/%ld, %p, %p)\n", inode->i_sb->s_id, + inode->i_ino, acl, dfacl); + spin_lock(&inode->i_lock); + __nfs3_forget_cached_acls(NFS_I(inode)); + nfsi->acl_access = posix_acl_dup(acl); + nfsi->acl_default = posix_acl_dup(dfacl); + spin_unlock(&inode->i_lock); +} + struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type) { struct nfs_server *server = NFS_SERVER(inode); @@ -126,26 +189,32 @@ struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type) struct nfs3_getaclres res = { .fattr = &fattr, }; - struct posix_acl *acl = NULL; + struct posix_acl *acl; int status, count; if (!nfs_server_capable(inode, NFS_CAP_ACLS)) return ERR_PTR(-EOPNOTSUPP); - switch (type) { - case ACL_TYPE_ACCESS: - args.mask = NFS_ACLCNT|NFS_ACL; - break; - - case ACL_TYPE_DEFAULT: - if (!S_ISDIR(inode->i_mode)) - return NULL; - args.mask = NFS_DFACLCNT|NFS_DFACL; - break; - - default: - return ERR_PTR(-EINVAL); - } + status = nfs_revalidate_inode(server, inode); + if (status < 0) + return ERR_PTR(status); + acl = nfs3_get_cached_acl(inode, type); + if (acl != ERR_PTR(-EAGAIN)) + return acl; + acl = NULL; + + /* + * Only get the access acl when explicitly requested: We don't + * need it for access decisions, and only some applications use + * it. Applications which request the access acl first are not + * penalized from this optimization. + */ + if (type == ACL_TYPE_ACCESS) + args.mask |= NFS_ACLCNT|NFS_ACL; + if (S_ISDIR(inode->i_mode)) + args.mask |= NFS_DFACLCNT|NFS_DFACL; + if (args.mask == 0) + return NULL; dprintk("NFS call getacl\n"); status = rpc_call(server->client_acl, ACLPROC3_GETACL, @@ -180,6 +249,7 @@ struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type) res.acl_access = NULL; } } + nfs3_cache_acls(inode, res.acl_access, res.acl_default); switch(type) { case ACL_TYPE_ACCESS: diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index a9ddc196224d95de237b1119548a0262c9f02755..7851569b31c63f03a5095f503d0b1b18183273d4 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -882,4 +882,5 @@ struct nfs_rpc_ops nfs_v3_clientops = { .file_open = nfs_open, .file_release = nfs_release, .lock = nfs3_proc_lock, + .clear_acl_cache = nfs3_forget_cached_acls, }; diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 7662c5131b47411b95f779fa09094d32f2a26572..4ceac9ddac932904d7dfbb0970b824cfac1175e3 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -91,6 +91,8 @@ struct nfs_open_context { */ struct nfs_delegation; +struct posix_acl; + /* * nfs fs inode data in memory */ @@ -144,6 +146,10 @@ struct nfs_inode { atomic_t data_updates; struct nfs_access_entry cache_access; +#ifdef CONFIG_NFS_V3_ACL + struct posix_acl *acl_access; + struct posix_acl *acl_default; +#endif /* * This is the cookie verifier used for NFSv3 readdir @@ -480,6 +486,7 @@ extern int nfs3_proc_setacl(struct inode *inode, int type, struct posix_acl *acl); extern int nfs3_proc_set_default_acl(struct inode *dir, struct inode *inode, mode_t mode); +extern void nfs3_forget_cached_acls(struct inode *inode); #else static inline int nfs3_proc_set_default_acl(struct inode *dir, struct inode *inode, @@ -487,6 +494,10 @@ static inline int nfs3_proc_set_default_acl(struct inode *dir, { return 0; } + +static inline void nfs3_forget_cached_acls(struct inode *inode) +{ +} #endif /* CONFIG_NFS_V3_ACL */ /*