vserver 1.9.5.x5
[linux-2.6.git] / fs / nfs / nfs4proc.c
index 7509bd2..7d56ca4 100644 (file)
@@ -477,7 +477,7 @@ static struct nfs4_state *nfs4_open_delegated(struct inode *inode, int flags, st
 /*
  * Returns an nfs4_state + an referenced inode
  */
-static int _nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
+static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
 {
        struct nfs4_state_owner  *sp;
        struct nfs4_state     *state = NULL;
@@ -491,7 +491,7 @@ static int _nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct
        struct nfs_openargs o_arg = {
                .fh             = NFS_FH(dir),
                .open_flags     = flags,
-               .name           = name,
+               .name           = &dentry->d_name,
                .server         = server,
                .bitmask = server->attr_bitmask,
                .claim = NFS4_OPEN_CLAIM_NULL,
@@ -581,14 +581,14 @@ out_err:
 }
 
 
-struct nfs4_state *nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred)
+struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, struct iattr *sattr, struct rpc_cred *cred)
 {
        struct nfs4_exception exception = { };
        struct nfs4_state *res;
        int status;
 
        do {
-               status = _nfs4_do_open(dir, name, flags, sattr, cred, &res);
+               status = _nfs4_do_open(dir, dentry, flags, sattr, cred, &res);
                if (status == 0)
                        break;
                /* NOTE: BAD_SEQID means the server and client disagree about the
@@ -635,6 +635,8 @@ static int _nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
 
         fattr->valid = 0;
 
+       if (state != NULL)
+               msg.rpc_cred = state->owner->so_cred;
        if (sattr->ia_valid & ATTR_SIZE)
                nfs4_copy_stateid(&arg.stateid, state, NULL);
        else
@@ -658,6 +660,61 @@ int nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
        return err;
 }
 
+struct nfs4_closedata {
+       struct inode *inode;
+       struct nfs4_state *state;
+       struct nfs_closeargs arg;
+       struct nfs_closeres res;
+};
+
+static void nfs4_close_done(struct rpc_task *task)
+{
+       struct nfs4_closedata *calldata = (struct nfs4_closedata *)task->tk_calldata;
+       struct nfs4_state *state = calldata->state;
+       struct nfs4_state_owner *sp = state->owner;
+       struct nfs_server *server = NFS_SERVER(calldata->inode);
+
+        /* hmm. we are done with the inode, and in the process of freeing
+        * the state_owner. we keep this around to process errors
+        */
+       nfs4_increment_seqid(task->tk_status, sp);
+       switch (task->tk_status) {
+               case 0:
+                       state->state = calldata->arg.open_flags;
+                       memcpy(&state->stateid, &calldata->res.stateid,
+                                       sizeof(state->stateid));
+                       break;
+               case -NFS4ERR_STALE_STATEID:
+               case -NFS4ERR_EXPIRED:
+                       state->state = calldata->arg.open_flags;
+                       nfs4_schedule_state_recovery(server->nfs4_state);
+                       break;
+               default:
+                       if (nfs4_async_handle_error(task, server) == -EAGAIN) {
+                               rpc_restart_call(task);
+                               return;
+                       }
+       }
+       nfs4_put_open_state(state);
+       up(&sp->so_sema);
+       nfs4_put_state_owner(sp);
+       up_read(&server->nfs4_state->cl_sem);
+       kfree(calldata);
+}
+
+static inline int nfs4_close_call(struct rpc_clnt *clnt, struct nfs4_closedata *calldata)
+{
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE],
+               .rpc_argp = &calldata->arg,
+               .rpc_resp = &calldata->res,
+               .rpc_cred = calldata->state->owner->so_cred,
+       };
+       if (calldata->arg.open_flags != 0)
+               msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
+       return rpc_call_async(clnt, &msg, 0, nfs4_close_done, calldata);
+}
+
 /* 
  * It is possible for data to be read/written from a mem-mapped file 
  * after the sys_close call (which hits the vfs layer as a flush).
@@ -669,102 +726,34 @@ int nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
  *
  * NOTE: Caller must be holding the sp->so_owner semaphore!
  */
-static int _nfs4_do_close(struct inode *inode, struct nfs4_state *state) 
+int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode) 
 {
-       struct nfs4_state_owner *sp = state->owner;
-       int status = 0;
-       struct nfs_closeargs arg = {
-               .fh             = NFS_FH(inode),
-       };
-       struct nfs_closeres res;
-       struct rpc_message msg = {
-               .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_CLOSE],
-               .rpc_argp       = &arg,
-               .rpc_resp       = &res,
-       };
+       struct nfs4_closedata *calldata;
+       int status;
 
-       if (test_bit(NFS_DELEGATED_STATE, &state->flags))
+       /* Tell caller we're done */
+       if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
+               state->state = mode;
                return 0;
-       memcpy(&arg.stateid, &state->stateid, sizeof(arg.stateid));
+       }
+       calldata = (struct nfs4_closedata *)kmalloc(sizeof(*calldata), GFP_KERNEL);
+       if (calldata == NULL)
+               return -ENOMEM;
+       calldata->inode = inode;
+       calldata->state = state;
+       calldata->arg.fh = NFS_FH(inode);
        /* Serialization for the sequence id */
-       arg.seqid = sp->so_seqid,
-       status = rpc_call_sync(NFS_SERVER(inode)->client, &msg, RPC_TASK_NOINTR);
-
-        /* hmm. we are done with the inode, and in the process of freeing
-        * the state_owner. we keep this around to process errors
+       calldata->arg.seqid = state->owner->so_seqid;
+       calldata->arg.open_flags = mode;
+       memcpy(&calldata->arg.stateid, &state->stateid,
+                       sizeof(calldata->arg.stateid));
+       status = nfs4_close_call(NFS_SERVER(inode)->client, calldata);
+       /*
+        * Return -EINPROGRESS on success in order to indicate to the
+        * caller that an asynchronous RPC call has been launched, and
+        * that it will release the semaphores on completion.
         */
-       nfs4_increment_seqid(status, sp);
-       if (!status)
-               memcpy(&state->stateid, &res.stateid, sizeof(state->stateid));
-
-       return status;
-}
-
-int nfs4_do_close(struct inode *inode, struct nfs4_state *state) 
-{
-       struct nfs_server *server = NFS_SERVER(state->inode);
-       struct nfs4_exception exception = { };
-       int err;
-       do {
-               err = _nfs4_do_close(inode, state);
-               switch (err) {
-                       case -NFS4ERR_STALE_STATEID:
-                       case -NFS4ERR_EXPIRED:
-                               nfs4_schedule_state_recovery(server->nfs4_state);
-                               err = 0;
-                       default:
-                               state->state = 0;
-               }
-               err = nfs4_handle_exception(server, err, &exception);
-       } while (exception.retry);
-       return err;
-}
-
-static int _nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode) 
-{
-       struct nfs4_state_owner *sp = state->owner;
-       int status = 0;
-       struct nfs_closeargs arg = {
-               .fh             = NFS_FH(inode),
-               .seqid          = sp->so_seqid,
-               .open_flags     = mode,
-       };
-       struct nfs_closeres res;
-       struct rpc_message msg = {
-               .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE],
-               .rpc_argp       = &arg,
-               .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, RPC_TASK_NOINTR);
-       nfs4_increment_seqid(status, sp);
-       if (!status)
-               memcpy(&state->stateid, &res.stateid, sizeof(state->stateid));
-
-       return status;
-}
-
-int nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode) 
-{
-       struct nfs_server *server = NFS_SERVER(state->inode);
-       struct nfs4_exception exception = { };
-       int err;
-       do {
-               err = _nfs4_do_downgrade(inode, state, mode);
-               switch (err) {
-                       case -NFS4ERR_STALE_STATEID:
-                       case -NFS4ERR_EXPIRED:
-                               nfs4_schedule_state_recovery(server->nfs4_state);
-                               err = 0;
-                       default:
-                               state->state = mode;
-               }
-               err = nfs4_handle_exception(server, err, &exception);
-       } while (exception.retry);
-       return err;
+       return (status == 0) ? -EINPROGRESS : status;
 }
 
 struct inode *
@@ -785,7 +774,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
        }
 
        cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
-       state = nfs4_do_open(dir, &dentry->d_name, nd->intent.open.flags, &attr, cred);
+       state = nfs4_do_open(dir, dentry, nd->intent.open.flags, &attr, cred);
        put_rpccred(cred);
        if (IS_ERR(state))
                return (struct inode *)state;
@@ -802,7 +791,7 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags)
        cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
        state = nfs4_open_delegated(dentry->d_inode, openflags, cred);
        if (IS_ERR(state))
-               state = nfs4_do_open(dir, &dentry->d_name, openflags, NULL, cred);
+               state = nfs4_do_open(dir, dentry, openflags, NULL, cred);
        put_rpccred(cred);
        if (state == ERR_PTR(-ENOENT) && dentry->d_inode == 0)
                return 1;
@@ -1026,7 +1015,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
                                        FMODE_WRITE, cred);
                        if (IS_ERR(state))
                                state = nfs4_do_open(dentry->d_parent->d_inode,
-                                               &dentry->d_name, FMODE_WRITE,
+                                               dentry, FMODE_WRITE,
                                                NULL, cred);
                        need_iput = 1;
                }
@@ -1327,7 +1316,7 @@ static int nfs4_proc_commit(struct nfs_write_data *cdata)
  */
 
 static struct inode *
-nfs4_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
+nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
                  int flags)
 {
        struct inode *inode;
@@ -1335,7 +1324,7 @@ nfs4_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
        struct rpc_cred *cred;
 
        cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
-       state = nfs4_do_open(dir, name, flags, sattr, cred);
+       state = nfs4_do_open(dir, dentry, flags, sattr, cred);
        put_rpccred(cred);
        if (!IS_ERR(state)) {
                inode = state->inode;