/*
* 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;
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,
}
-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
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
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).
*
* 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 *
}
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;
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;
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;
}
*/
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;
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;