]> git.hungrycats.org Git - linux/commitdiff
NFSv4: Delegated open.
authorTrond Myklebust <trond.myklebust@fys.uio.no>
Mon, 23 Aug 2004 15:59:17 +0000 (11:59 -0400)
committerTrond Myklebust <trond.myklebust@fys.uio.no>
Mon, 23 Aug 2004 15:59:17 +0000 (11:59 -0400)
Signed-off-by: Trond Myklebust <trond.myklebust@fys.uio.no>
fs/nfs/dir.c
fs/nfs/nfs4proc.c
include/linux/nfs_fs.h

index a9d54bea05c9c346f0ca2c22b7314b0288e0aafc..840af1a237006e918aea15dc1d48339e5783c734 100644 (file)
@@ -1489,7 +1489,7 @@ out:
        return error;
 }
 
-static int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res)
+int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res)
 {
        struct nfs_access_entry *cache = &NFS_I(inode)->cache_access;
 
@@ -1501,7 +1501,7 @@ static int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, str
        return 0;
 }
 
-static void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
+void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
 {
        struct nfs_access_entry *cache = &NFS_I(inode)->cache_access;
 
index d1142a2304f98ef690484baaca6d799dc7b3785e..f63d3d60ea86ec5e35944a343384d43374e28aa3 100644 (file)
@@ -56,6 +56,7 @@
 
 static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
 static int nfs4_async_handle_error(struct rpc_task *, struct nfs_server *);
+static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry);
 extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus);
 extern struct rpc_procinfo nfs4_procedures[];
 
@@ -354,6 +355,125 @@ static int _nfs4_proc_open_confirm(struct rpc_clnt *clnt, const struct nfs_fh *f
        return status;
 }
 
+static int _nfs4_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
+{
+       struct nfs_access_entry cache;
+       int status;
+
+       status = nfs_access_get_cached(inode, cred, &cache);
+       if (status == 0)
+               goto out;
+
+       /* Be clever: ask server to check for all possible rights */
+       cache.mask = MAY_EXEC | MAY_WRITE | MAY_READ;
+       cache.cred = cred;
+       cache.jiffies = jiffies;
+       status = _nfs4_proc_access(inode, &cache);
+       if (status != 0)
+               return status;
+       nfs_access_add_cache(inode, &cache);
+out:
+       if ((cache.mask & mask) == mask)
+               return 0;
+       return -EACCES;
+}
+
+/*
+ * Returns an nfs4_state + an extra reference to the inode
+ */
+int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred, struct nfs4_state **res)
+{
+       struct nfs_delegation *delegation;
+       struct nfs_server *server = NFS_SERVER(inode);
+       struct nfs4_client *clp = server->nfs4_state;
+       struct nfs_inode *nfsi = NFS_I(inode);
+       struct nfs4_state_owner *sp = NULL;
+       struct nfs4_state *state = NULL;
+       int open_flags = flags & (FMODE_READ|FMODE_WRITE);
+       int mask = 0;
+       int err;
+
+       /* Protect against reboot recovery - NOTE ORDER! */
+       down_read(&clp->cl_sem);
+       /* Protect against delegation recall */
+       down_read(&nfsi->rwsem);
+       delegation = NFS_I(inode)->delegation;
+       err = -ENOENT;
+       if (delegation == NULL || (delegation->type & open_flags) != open_flags)
+               goto out_err;
+       err = -ENOMEM;
+       if (!(sp = nfs4_get_state_owner(server, cred))) {
+               dprintk("%s: nfs4_get_state_owner failed!\n", __FUNCTION__);
+               goto out_err;
+       }
+       down(&sp->so_sema);
+       state = nfs4_get_open_state(inode, sp);
+       if (state == NULL)
+               goto out_err;
+
+       err = -ENOENT;
+       if ((state->state & open_flags) == open_flags) {
+               spin_lock(&inode->i_lock);
+               if (open_flags & FMODE_READ)
+                       state->nreaders++;
+               if (open_flags & FMODE_WRITE)
+                       state->nwriters++;
+               spin_unlock(&inode->i_lock);
+               goto out_ok;
+       } else if (state->state != 0)
+               goto out_err;
+
+       lock_kernel();
+       err = _nfs4_do_access(inode, cred, mask);
+       unlock_kernel();
+       if (err != 0)
+               goto out_err;
+       spin_lock(&inode->i_lock);
+       memcpy(state->stateid.data, delegation->stateid.data,
+                       sizeof(state->stateid.data));
+       state->state |= open_flags;
+       if (open_flags & FMODE_READ)
+               state->nreaders++;
+       if (open_flags & FMODE_WRITE)
+               state->nwriters++;
+       set_bit(NFS_DELEGATED_STATE, &state->flags);
+       spin_unlock(&inode->i_lock);
+out_ok:
+       up(&sp->so_sema);
+       nfs4_put_state_owner(sp);
+       up_read(&nfsi->rwsem);
+       up_read(&clp->cl_sem);
+       igrab(inode);
+       *res = state;
+       return 0; 
+out_err:
+       if (sp != NULL) {
+               if (state != NULL)
+                       nfs4_put_open_state(state);
+               up(&sp->so_sema);
+               nfs4_put_state_owner(sp);
+       }
+       up_read(&nfsi->rwsem);
+       up_read(&clp->cl_sem);
+       return err;
+}
+
+static struct nfs4_state *nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred)
+{
+       struct nfs4_exception exception = { };
+       struct nfs4_state *res;
+       int err;
+
+       do {
+               err = _nfs4_open_delegated(inode, flags, cred, &res);
+               if (err == 0)
+                       break;
+               res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(inode),
+                                       err, &exception));
+       } while (exception.retry);
+       return res;
+}
+
 /*
  * Returns an nfs4_state + an referenced inode
  */
@@ -563,6 +683,8 @@ static int _nfs4_do_close(struct inode *inode, struct nfs4_state *state)
                .rpc_resp       = &res,
        };
 
+       if (test_bit(NFS_DELEGATED_STATE, &state->flags))
+               return 0;
        memcpy(&arg.stateid, &state->stateid, sizeof(arg.stateid));
        /* Serialization for the sequence id */
        arg.seqid = sp->so_seqid,
@@ -614,6 +736,8 @@ static int _nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mod
                .rpc_resp       = &res,
        };
 
+       if (test_bit(NFS_DELEGATED_STATE, &state->flags))
+               return 0;
        memcpy(&arg.stateid, &state->stateid, sizeof(arg.stateid));
        status = rpc_call_sync(NFS_SERVER(inode)->client, &msg, 0);
        nfs4_increment_seqid(status, sp);
@@ -676,7 +800,9 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags)
        struct inode *inode;
 
        cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
-       state = nfs4_do_open(dir, &dentry->d_name, openflags, NULL, cred);
+       state = nfs4_open_delegated(dentry->d_inode, openflags, cred);
+       if (IS_ERR(state))
+               state = nfs4_do_open(dir, &dentry->d_name, openflags, NULL, cred);
        put_rpccred(cred);
        if (state == ERR_PTR(-ENOENT) && dentry->d_inode == 0)
                return 1;
@@ -895,9 +1021,13 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
        if (size_change) {
                struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
                state = nfs4_find_state(inode, cred, FMODE_WRITE);
-               if (!state) {
-                       state = nfs4_do_open(dentry->d_parent->d_inode, 
-                               &dentry->d_name, FMODE_WRITE, NULL, cred);
+               if (state == NULL) {
+                       state = nfs4_open_delegated(dentry->d_inode,
+                                       FMODE_WRITE, cred);
+                       if (IS_ERR(state))
+                               state = nfs4_do_open(dentry->d_parent->d_inode,
+                                               &dentry->d_name, FMODE_WRITE,
+                                               NULL, cred);
                        need_iput = 1;
                }
                put_rpccred(cred);
index 0d8bc087589f17aa168780cca51170fd6a9898e0..c186bdae70762a2b2e0e357f0bbe6c7ad329757a 100644 (file)
@@ -290,6 +290,8 @@ extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *,
 extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
 extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
 extern int nfs_permission(struct inode *, int, struct nameidata *);
+extern int nfs_access_get_cached(struct inode *, struct rpc_cred *, struct nfs_access_entry *);
+extern void nfs_access_add_cache(struct inode *, struct nfs_access_entry *);
 extern int nfs_open(struct inode *, struct file *);
 extern int nfs_release(struct inode *, struct file *);
 extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);