Commit 28cf22d0 authored by Trond Myklebust's avatar Trond Myklebust

NFSv4: Fix exclusive create attributes encoding

When using NFS4_CREATE_EXCLUSIVE4_1 mode, the client will overestimate the
amount of space that it needs for the attributes because it does so
before checking whether or not the server supports a given attribute.

Fix by checking the attribute mask earlier.
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
parent 2e84611b
...@@ -1000,8 +1000,9 @@ static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *ve ...@@ -1000,8 +1000,9 @@ static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *ve
static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
const struct nfs4_label *label, const struct nfs4_label *label,
const umode_t *umask,
const struct nfs_server *server, const struct nfs_server *server,
bool excl_check, const umode_t *umask) const uint32_t attrmask[])
{ {
char owner_name[IDMAP_NAMESZ]; char owner_name[IDMAP_NAMESZ];
char owner_group[IDMAP_NAMESZ]; char owner_group[IDMAP_NAMESZ];
...@@ -1016,22 +1017,20 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, ...@@ -1016,22 +1017,20 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
/* /*
* We reserve enough space to write the entire attribute buffer at once. * We reserve enough space to write the entire attribute buffer at once.
*/ */
if (iap->ia_valid & ATTR_SIZE) { if ((iap->ia_valid & ATTR_SIZE) && (attrmask[0] & FATTR4_WORD0_SIZE)) {
bmval[0] |= FATTR4_WORD0_SIZE; bmval[0] |= FATTR4_WORD0_SIZE;
len += 8; len += 8;
} }
if (!(server->attr_bitmask[2] & FATTR4_WORD2_MODE_UMASK))
umask = NULL;
if (iap->ia_valid & ATTR_MODE) { if (iap->ia_valid & ATTR_MODE) {
if (umask) { if (umask && (attrmask[2] & FATTR4_WORD2_MODE_UMASK)) {
bmval[2] |= FATTR4_WORD2_MODE_UMASK; bmval[2] |= FATTR4_WORD2_MODE_UMASK;
len += 8; len += 8;
} else { } else if (attrmask[1] & FATTR4_WORD1_MODE) {
bmval[1] |= FATTR4_WORD1_MODE; bmval[1] |= FATTR4_WORD1_MODE;
len += 4; len += 4;
} }
} }
if (iap->ia_valid & ATTR_UID) { if ((iap->ia_valid & ATTR_UID) && (attrmask[1] & FATTR4_WORD1_OWNER)) {
owner_namelen = nfs_map_uid_to_name(server, iap->ia_uid, owner_name, IDMAP_NAMESZ); owner_namelen = nfs_map_uid_to_name(server, iap->ia_uid, owner_name, IDMAP_NAMESZ);
if (owner_namelen < 0) { if (owner_namelen < 0) {
dprintk("nfs: couldn't resolve uid %d to string\n", dprintk("nfs: couldn't resolve uid %d to string\n",
...@@ -1044,7 +1043,8 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, ...@@ -1044,7 +1043,8 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
bmval[1] |= FATTR4_WORD1_OWNER; bmval[1] |= FATTR4_WORD1_OWNER;
len += 4 + (XDR_QUADLEN(owner_namelen) << 2); len += 4 + (XDR_QUADLEN(owner_namelen) << 2);
} }
if (iap->ia_valid & ATTR_GID) { if ((iap->ia_valid & ATTR_GID) &&
(attrmask[1] & FATTR4_WORD1_OWNER_GROUP)) {
owner_grouplen = nfs_map_gid_to_group(server, iap->ia_gid, owner_group, IDMAP_NAMESZ); owner_grouplen = nfs_map_gid_to_group(server, iap->ia_gid, owner_group, IDMAP_NAMESZ);
if (owner_grouplen < 0) { if (owner_grouplen < 0) {
dprintk("nfs: couldn't resolve gid %d to string\n", dprintk("nfs: couldn't resolve gid %d to string\n",
...@@ -1056,32 +1056,26 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, ...@@ -1056,32 +1056,26 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
bmval[1] |= FATTR4_WORD1_OWNER_GROUP; bmval[1] |= FATTR4_WORD1_OWNER_GROUP;
len += 4 + (XDR_QUADLEN(owner_grouplen) << 2); len += 4 + (XDR_QUADLEN(owner_grouplen) << 2);
} }
if (iap->ia_valid & ATTR_ATIME_SET) { if (attrmask[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
bmval[1] |= FATTR4_WORD1_TIME_ACCESS_SET; if (iap->ia_valid & ATTR_ATIME_SET) {
len += 16; bmval[1] |= FATTR4_WORD1_TIME_ACCESS_SET;
} else if (iap->ia_valid & ATTR_ATIME) { len += 16;
bmval[1] |= FATTR4_WORD1_TIME_ACCESS_SET; } else if (iap->ia_valid & ATTR_ATIME) {
len += 4; bmval[1] |= FATTR4_WORD1_TIME_ACCESS_SET;
} len += 4;
if (iap->ia_valid & ATTR_MTIME_SET) { }
bmval[1] |= FATTR4_WORD1_TIME_MODIFY_SET;
len += 16;
} else if (iap->ia_valid & ATTR_MTIME) {
bmval[1] |= FATTR4_WORD1_TIME_MODIFY_SET;
len += 4;
} }
if (attrmask[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
if (excl_check) { if (iap->ia_valid & ATTR_MTIME_SET) {
const u32 *excl_bmval = server->exclcreat_bitmask; bmval[1] |= FATTR4_WORD1_TIME_MODIFY_SET;
bmval[0] &= excl_bmval[0]; len += 16;
bmval[1] &= excl_bmval[1]; } else if (iap->ia_valid & ATTR_MTIME) {
bmval[2] &= excl_bmval[2]; bmval[1] |= FATTR4_WORD1_TIME_MODIFY_SET;
len += 4;
if (!(excl_bmval[2] & FATTR4_WORD2_SECURITY_LABEL)) }
label = NULL;
} }
if (label) { if (label && (attrmask[2] & FATTR4_WORD2_SECURITY_LABEL)) {
len += 4 + 4 + 4 + (XDR_QUADLEN(label->len) << 2); len += 4 + 4 + 4 + (XDR_QUADLEN(label->len) << 2);
bmval[2] |= FATTR4_WORD2_SECURITY_LABEL; bmval[2] |= FATTR4_WORD2_SECURITY_LABEL;
} }
...@@ -1188,8 +1182,8 @@ static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg * ...@@ -1188,8 +1182,8 @@ static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *
} }
encode_string(xdr, create->name->len, create->name->name); encode_string(xdr, create->name->len, create->name->name);
encode_attrs(xdr, create->attrs, create->label, create->server, false, encode_attrs(xdr, create->attrs, create->label, &create->umask,
&create->umask); create->server, create->server->attr_bitmask);
} }
static void encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap, struct compound_hdr *hdr) static void encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap, struct compound_hdr *hdr)
...@@ -1409,13 +1403,13 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op ...@@ -1409,13 +1403,13 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op
switch(arg->createmode) { switch(arg->createmode) {
case NFS4_CREATE_UNCHECKED: case NFS4_CREATE_UNCHECKED:
*p = cpu_to_be32(NFS4_CREATE_UNCHECKED); *p = cpu_to_be32(NFS4_CREATE_UNCHECKED);
encode_attrs(xdr, arg->u.attrs, arg->label, arg->server, false, encode_attrs(xdr, arg->u.attrs, arg->label, &arg->umask,
&arg->umask); arg->server, arg->server->attr_bitmask);
break; break;
case NFS4_CREATE_GUARDED: case NFS4_CREATE_GUARDED:
*p = cpu_to_be32(NFS4_CREATE_GUARDED); *p = cpu_to_be32(NFS4_CREATE_GUARDED);
encode_attrs(xdr, arg->u.attrs, arg->label, arg->server, false, encode_attrs(xdr, arg->u.attrs, arg->label, &arg->umask,
&arg->umask); arg->server, arg->server->attr_bitmask);
break; break;
case NFS4_CREATE_EXCLUSIVE: case NFS4_CREATE_EXCLUSIVE:
*p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE); *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE);
...@@ -1424,8 +1418,8 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op ...@@ -1424,8 +1418,8 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op
case NFS4_CREATE_EXCLUSIVE4_1: case NFS4_CREATE_EXCLUSIVE4_1:
*p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE4_1); *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE4_1);
encode_nfs4_verifier(xdr, &arg->u.verifier); encode_nfs4_verifier(xdr, &arg->u.verifier);
encode_attrs(xdr, arg->u.attrs, arg->label, arg->server, true, encode_attrs(xdr, arg->u.attrs, arg->label, &arg->umask,
&arg->umask); arg->server, arg->server->exclcreat_bitmask);
} }
} }
...@@ -1681,7 +1675,8 @@ static void encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs ...@@ -1681,7 +1675,8 @@ static void encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs
{ {
encode_op_hdr(xdr, OP_SETATTR, decode_setattr_maxsz, hdr); encode_op_hdr(xdr, OP_SETATTR, decode_setattr_maxsz, hdr);
encode_nfs4_stateid(xdr, &arg->stateid); encode_nfs4_stateid(xdr, &arg->stateid);
encode_attrs(xdr, arg->iap, arg->label, server, false, NULL); encode_attrs(xdr, arg->iap, arg->label, NULL, server,
server->attr_bitmask);
} }
static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid, struct compound_hdr *hdr) static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid, struct compound_hdr *hdr)
......
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