Commit be6c1a46 authored by Trond Myklebust's avatar Trond Myklebust

Make NFSv4 'setattr()' method use the cached stateid if the file is

already open.
parent 45f3fae6
...@@ -461,18 +461,6 @@ nfs4_setup_savefh(struct nfs4_compound *cp) ...@@ -461,18 +461,6 @@ nfs4_setup_savefh(struct nfs4_compound *cp)
cp->req_nops++; cp->req_nops++;
} }
static void
nfs4_setup_setattr(struct nfs4_compound *cp, char *stateid, struct iattr *iap)
{
struct nfs4_setattr *setattr = GET_OP(cp, setattr);
setattr->st_stateid = stateid;
setattr->st_iap = iap;
OPNUM(cp) = OP_SETATTR;
cp->req_nops++;
}
static void static void
nfs4_setup_setclientid(struct nfs4_compound *cp, u32 program, unsigned short port) nfs4_setup_setclientid(struct nfs4_compound *cp, u32 program, unsigned short port)
{ {
...@@ -681,20 +669,39 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, ...@@ -681,20 +669,39 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags,
return status; return status;
} }
static int int
do_setattr(struct nfs_server *server, struct nfs_fattr *fattr, nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
struct nfs_fh *fhandle, struct iattr *sattr, char *stateid) struct nfs_fh *fhandle, struct iattr *sattr,
struct nfs4_shareowner *sp)
{ {
struct nfs4_compound compound; u32 g_bmres[2];
struct nfs4_op ops[3]; struct nfs4_getattr getattr = {
u32 bmres[2]; .gt_bmval = nfs4_fattr_bitmap,
.gt_attrs = fattr,
.gt_bmres = g_bmres,
};
struct nfs_setattrargs arg = {
.fh = fhandle,
.iap = sattr,
.attr = &getattr,
};
struct nfs_setattrres res = {
.attr = &getattr,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
.rpc_argp = &arg,
.rpc_resp = &res,
};
fattr->valid = 0; fattr->valid = 0;
nfs4_setup_compound(&compound, ops, server, "setattr");
nfs4_setup_putfh(&compound, fhandle); if (sp)
nfs4_setup_setattr(&compound, stateid, sattr); memcpy(arg.stateid, sp->so_stateid, sizeof(nfs4_stateid));
nfs4_setup_getattr(&compound, fattr, bmres); else
return nfs4_call_compound(&compound, NULL, 0); memcpy(arg.stateid, zero_stateid, sizeof(nfs4_stateid));
return(rpc_call_sync(server->client, &msg, 0));
} }
/* /*
...@@ -839,7 +846,18 @@ nfs4_proc_getattr(struct inode *inode, struct nfs_fattr *fattr) ...@@ -839,7 +846,18 @@ nfs4_proc_getattr(struct inode *inode, struct nfs_fattr *fattr)
* The file is not closed if it is opened due to the a request to change * The file is not closed if it is opened due to the a request to change
* the size of the file. The open call will not be needed once the * the size of the file. The open call will not be needed once the
* VFS layer lookup-intents are implemented. * VFS layer lookup-intents are implemented.
*
* Close is called when the inode is destroyed. * Close is called when the inode is destroyed.
* If we haven't opened the file for O_WRONLY, we
* need to in the size_change case to obtain a stateid.
*
* Got race?
* Because OPEN is always done by name in nfsv4, it is
* possible that we opened a different file by the same
* name. We can recognize this race condition, but we
* can't do anything about it besides returning an error.
*
* This will be fixed with VFS changes (lookup-intent).
*/ */
static int static int
nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
...@@ -847,37 +865,31 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, ...@@ -847,37 +865,31 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
{ {
struct inode * inode = dentry->d_inode; struct inode * inode = dentry->d_inode;
int size_change = sattr->ia_valid & ATTR_SIZE; int size_change = sattr->ia_valid & ATTR_SIZE;
struct nfs_fh throwaway_fh;
struct nfs4_shareowner *sp = NULL; struct nfs4_shareowner *sp = NULL;
int status, fake = 1; int status;
fattr->valid = 0; fattr->valid = 0;
if (size_change) { if (size_change) {
if (NFS_I(inode)->wo_owner) {
/* file is already open for O_WRONLY */
sp = NFS_I(inode)->wo_owner;
goto no_open;
}
status = nfs4_do_open(dentry->d_parent->d_inode, status = nfs4_do_open(dentry->d_parent->d_inode,
&dentry->d_name, &dentry->d_name, O_WRONLY, NULL, fattr,
O_WRONLY, NULL, fattr, NULL, &sp);
&throwaway_fh,&sp);
if (status) if (status)
return status; return status;
fake = 0;
/*
* Because OPEN is always done by name in nfsv4, it is
* possible that we opened a different file by the same
* name. We can recognize this race condition, but we
* can't do anything about it besides returning an error.
*
* XXX: Should we compare filehandles too, as in
* nfs_find_actor()?
*/
if (fattr->fileid != NFS_FILEID(inode)) { if (fattr->fileid != NFS_FILEID(inode)) {
printk(KERN_WARNING "nfs: raced in setattr, returning -EIO\n"); printk(KERN_WARNING "nfs: raced in setattr, returning -EIO\n");
return -EIO; return -EIO;
} }
} }
no_open:
status = do_setattr(NFS_SERVER(inode), fattr, NFS_FH(inode), sattr, status = nfs4_do_setattr(NFS_SERVER(inode), fattr,
fake == 1? zero_stateid: sp->so_stateid); NFS_FH(inode), sattr, sp);
return status; return status;
} }
...@@ -1097,8 +1109,8 @@ nfs4_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, ...@@ -1097,8 +1109,8 @@ nfs4_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
status = nfs4_do_open(dir, name, oflags, sattr, fattr, fhandle, &sp); status = nfs4_do_open(dir, name, oflags, sattr, fattr, fhandle, &sp);
if (!status) { if (!status) {
if (flags & O_EXCL) { if (flags & O_EXCL) {
status = do_setattr(NFS_SERVER(dir), fattr, status = nfs4_do_setattr(NFS_SERVER(dir), fattr,
fhandle, sattr, sp->so_stateid); fhandle, sattr, sp);
/* XXX should i bother closing the file? */ /* XXX should i bother closing the file? */
} }
} }
......
...@@ -150,8 +150,14 @@ extern int nfs_stat_to_errno(int); ...@@ -150,8 +150,14 @@ extern int nfs_stat_to_errno(int);
#define NFS4_dec_close_sz compound_decode_hdr_maxsz + \ #define NFS4_dec_close_sz compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
op_decode_hdr_maxsz + 4 op_decode_hdr_maxsz + 4
#define NFS4_enc_setattr_sz compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
op_encode_hdr_maxsz + 4 + \
nfs4_fattr_bitmap_maxsz + \
encode_getattr_maxsz
#define NFS4_dec_setattr_sz compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
op_decode_hdr_maxsz + 3
static struct { static struct {
...@@ -771,16 +777,16 @@ encode_savefh(struct xdr_stream *xdr) ...@@ -771,16 +777,16 @@ encode_savefh(struct xdr_stream *xdr)
} }
static int static int
encode_setattr(struct xdr_stream *xdr, struct nfs4_setattr *setattr) encode_setattr(struct xdr_stream *xdr, struct nfs_setattrargs *arg)
{ {
int status; int status;
uint32_t *p; uint32_t *p;
RESERVE_SPACE(4+sizeof(nfs4_stateid)); RESERVE_SPACE(4+sizeof(nfs4_stateid));
WRITE32(OP_SETATTR); WRITE32(OP_SETATTR);
WRITEMEM(setattr->st_stateid, sizeof(nfs4_stateid)); WRITEMEM(arg->stateid, sizeof(nfs4_stateid));
if ((status = encode_attrs(xdr, setattr->st_iap))) if ((status = encode_attrs(xdr, arg->iap)))
return status; return status;
return 0; return 0;
...@@ -907,9 +913,6 @@ encode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs ...@@ -907,9 +913,6 @@ encode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs
case OP_SAVEFH: case OP_SAVEFH:
status = encode_savefh(xdr); status = encode_savefh(xdr);
break; break;
case OP_SETATTR:
status = encode_setattr(xdr, &cp->ops[i].u.setattr);
break;
case OP_SETCLIENTID: case OP_SETCLIENTID:
status = encode_setclientid(xdr, &cp->ops[i].u.setclientid); status = encode_setclientid(xdr, &cp->ops[i].u.setclientid);
break; break;
...@@ -1061,6 +1064,31 @@ nfs4_xdr_enc_read(struct rpc_rqst *req, uint32_t *p, struct nfs_readargs *args) ...@@ -1061,6 +1064,31 @@ nfs4_xdr_enc_read(struct rpc_rqst *req, uint32_t *p, struct nfs_readargs *args)
return status; return status;
} }
/*
* Encode an SETATTR request
*/
static int
nfs4_xdr_enc_setattr(struct rpc_rqst *req, uint32_t *p, struct nfs_setattrargs *args)
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
.nops = 3,
};
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh);
if(status)
goto out;
status = encode_setattr(&xdr, args);
if(status)
goto out;
status = encode_getattr(&xdr, args->attr);
out:
return status;
}
/* /*
* Encode a WRITE request * Encode a WRITE request
*/ */
...@@ -1991,7 +2019,7 @@ decode_savefh(struct xdr_stream *xdr) ...@@ -1991,7 +2019,7 @@ decode_savefh(struct xdr_stream *xdr)
} }
static int static int
decode_setattr(struct xdr_stream *xdr) decode_setattr(struct xdr_stream *xdr, struct nfs_setattrres *res)
{ {
uint32_t *p; uint32_t *p;
uint32_t bmlen; uint32_t bmlen;
...@@ -2144,9 +2172,6 @@ decode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs ...@@ -2144,9 +2172,6 @@ decode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs
case OP_SAVEFH: case OP_SAVEFH:
status = decode_savefh(xdr); status = decode_savefh(xdr);
break; break;
case OP_SETATTR:
status = decode_setattr(xdr);
break;
case OP_SETCLIENTID: case OP_SETCLIENTID:
status = decode_setclientid(xdr, &op->u.setclientid); status = decode_setclientid(xdr, &op->u.setclientid);
break; break;
...@@ -2274,6 +2299,31 @@ nfs4_xdr_dec_open_confirm(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_open_c ...@@ -2274,6 +2299,31 @@ nfs4_xdr_dec_open_confirm(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_open_c
return status; return status;
} }
/*
* Decode SETATTR response
*/
static int
nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_setattrres *res)
{
struct xdr_stream xdr;
struct compound_hdr hdr;
int status;
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
status = decode_compound_hdr(&xdr, &hdr);
if (status)
goto out;
status = decode_putfh(&xdr);
if (status)
goto out;
status = decode_setattr(&xdr, res);
if (status)
goto out;
status = decode_getattr(&xdr, res->attr);
out:
return status;
}
/* /*
* Decode Read response * Decode Read response
...@@ -2419,6 +2469,7 @@ struct rpc_procinfo nfs4_procedures[] = { ...@@ -2419,6 +2469,7 @@ struct rpc_procinfo nfs4_procedures[] = {
PROC(OPEN, enc_open, dec_open), PROC(OPEN, enc_open, dec_open),
PROC(OPEN_CONFIRM, enc_open_confirm, dec_open_confirm), PROC(OPEN_CONFIRM, enc_open_confirm, dec_open_confirm),
PROC(CLOSE, enc_close, dec_close), PROC(CLOSE, enc_close, dec_close),
PROC(SETATTR, enc_setattr, dec_setattr),
}; };
struct rpc_version nfs_version4 = { struct rpc_version nfs_version4 = {
......
...@@ -209,6 +209,7 @@ enum { ...@@ -209,6 +209,7 @@ enum {
NFSPROC4_CLNT_OPEN, NFSPROC4_CLNT_OPEN,
NFSPROC4_CLNT_OPEN_CONFIRM, NFSPROC4_CLNT_OPEN_CONFIRM,
NFSPROC4_CLNT_CLOSE, NFSPROC4_CLNT_CLOSE,
NFSPROC4_CLNT_SETATTR,
}; };
#endif #endif
......
...@@ -241,6 +241,17 @@ struct nfs_renameargs { ...@@ -241,6 +241,17 @@ struct nfs_renameargs {
unsigned int tolen; unsigned int tolen;
}; };
struct nfs_setattrargs {
struct nfs_fh * fh;
nfs4_stateid stateid;
struct iattr * iap;
struct nfs4_getattr * attr;
};
struct nfs_setattrres {
struct nfs4_getattr * attr;
};
struct nfs_linkargs { struct nfs_linkargs {
struct nfs_fh * fromfh; struct nfs_fh * fromfh;
struct nfs_fh * tofh; struct nfs_fh * tofh;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment