2018-03-20 21:03:41

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 0/9] Misc cleanups

The following patches are a set of cleanups for the NFSv4 XDR encoding
both on the forward and back channels.
Also a couple of delegation related cleanups and fixes.

Trond Myklebust (9):
SUNRPC: Add helpers for decoding opaque and string types
SUNRPC: Add a helper for encoding opaque data inline
NFSv4: Allow GFP_NOIO sleeps in decode_attr_owner/decode_attr_group
NFSv4; Clean up XDR encoding of type bitmap4
NFSv4: Clean up encode_attrs
NFSv4: Add a helper to encode/decode struct timespec
NFSv4: Don't ask for attributes when ACCESS is protected by a
delegation
NFSv4: Clean up CB_GETATTR encoding
NFSv4: Fix the nfs_inode_set_delegation() arguments

fs/nfs/callback_xdr.c | 37 ++-----
fs/nfs/delegation.c | 35 ++++---
fs/nfs/delegation.h | 6 +-
fs/nfs/nfs4proc.c | 25 +++--
fs/nfs/nfs4xdr.c | 238 +++++++++++++++++++++------------------------
include/linux/sunrpc/xdr.h | 94 ++++++++++++++++++
net/sunrpc/xdr.c | 82 ++++++++++++++++
7 files changed, 336 insertions(+), 181 deletions(-)

--
2.14.3



2018-03-20 21:03:47

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 6/9] NFSv4: Add a helper to encode/decode struct timespec

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/nfs4xdr.c | 45 ++++++++++++++++++++++++++++++---------------
1 file changed, 30 insertions(+), 15 deletions(-)

diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 3d088230c975..79f1774b9d68 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -99,6 +99,7 @@ static int nfs4_stat_to_errno(int);
((3+NFS4_FHSIZE) >> 2))
#define nfs4_fattr_bitmap_maxsz 4
#define encode_getattr_maxsz (op_encode_hdr_maxsz + nfs4_fattr_bitmap_maxsz)
+#define nfstime4_maxsz (3)
#define nfs4_name_maxsz (1 + ((3 + NFS4_MAXNAMLEN) >> 2))
#define nfs4_path_maxsz (1 + ((3 + NFS4_MAXPATHLEN) >> 2))
#define nfs4_owner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ))
@@ -113,7 +114,8 @@ static int nfs4_stat_to_errno(int);
#define decode_mdsthreshold_maxsz (1 + 1 + nfs4_fattr_bitmap_maxsz + 1 + 8)
/* This is based on getfattr, which uses the most attributes: */
#define nfs4_fattr_value_maxsz (1 + (1 + 2 + 2 + 4 + 2 + 1 + 1 + 2 + 2 + \
- 3 + 3 + 3 + nfs4_owner_maxsz + \
+ 3*nfstime4_maxsz + \
+ nfs4_owner_maxsz + \
nfs4_group_maxsz + nfs4_label_maxsz + \
decode_mdsthreshold_maxsz))
#define nfs4_fattr_maxsz (nfs4_fattr_bitmap_maxsz + \
@@ -124,7 +126,8 @@ static int nfs4_stat_to_errno(int);
nfs4_owner_maxsz + \
nfs4_group_maxsz + \
nfs4_label_maxsz + \
- 4 + 4)
+ 1 + nfstime4_maxsz + \
+ 1 + nfstime4_maxsz)
#define encode_savefh_maxsz (op_encode_hdr_maxsz)
#define decode_savefh_maxsz (op_decode_hdr_maxsz)
#define encode_restorefh_maxsz (op_encode_hdr_maxsz)
@@ -1041,6 +1044,14 @@ static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *ve
encode_opaque_fixed(xdr, verf->data, NFS4_VERIFIER_SIZE);
}

+static __be32 *
+xdr_encode_nfstime4(__be32 *p, const struct timespec *t)
+{
+ p = xdr_encode_hyper(p, (__s64)t->tv_sec);
+ *p++ = cpu_to_be32(t->tv_nsec);
+ return p;
+}
+
static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
const struct nfs4_label *label,
const umode_t *umask,
@@ -1100,7 +1111,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
if (attrmask[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
if (iap->ia_valid & ATTR_ATIME_SET) {
bmval[1] |= FATTR4_WORD1_TIME_ACCESS_SET;
- len += 16;
+ len += 4 + (nfstime4_maxsz << 2);
} else if (iap->ia_valid & ATTR_ATIME) {
bmval[1] |= FATTR4_WORD1_TIME_ACCESS_SET;
len += 4;
@@ -1109,7 +1120,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
if (attrmask[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
if (iap->ia_valid & ATTR_MTIME_SET) {
bmval[1] |= FATTR4_WORD1_TIME_MODIFY_SET;
- len += 16;
+ len += 4 + (nfstime4_maxsz << 2);
} else if (iap->ia_valid & ATTR_MTIME) {
bmval[1] |= FATTR4_WORD1_TIME_MODIFY_SET;
len += 4;
@@ -1135,16 +1146,14 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
if (iap->ia_valid & ATTR_ATIME_SET) {
*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
- p = xdr_encode_hyper(p, (s64)iap->ia_atime.tv_sec);
- *p++ = cpu_to_be32(iap->ia_atime.tv_nsec);
+ p = xdr_encode_nfstime4(p, &iap->ia_atime);
} else
*p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
}
if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
if (iap->ia_valid & ATTR_MTIME_SET) {
*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
- p = xdr_encode_hyper(p, (s64)iap->ia_mtime.tv_sec);
- *p++ = cpu_to_be32(iap->ia_mtime.tv_nsec);
+ p = xdr_encode_nfstime4(p, &iap->ia_mtime);
} else
*p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
}
@@ -4129,19 +4138,25 @@ static int decode_attr_space_used(struct xdr_stream *xdr, uint32_t *bitmap, uint
return -EIO;
}

+static __be32 *
+xdr_decode_nfstime4(__be32 *p, struct timespec *t)
+{
+ __u64 sec;
+
+ p = xdr_decode_hyper(p, &sec);
+ t-> tv_sec = (time_t)sec;
+ t->tv_nsec = be32_to_cpup(p++);
+ return p;
+}
+
static int decode_attr_time(struct xdr_stream *xdr, struct timespec *time)
{
__be32 *p;
- uint64_t sec;
- uint32_t nsec;

- p = xdr_inline_decode(xdr, 12);
+ p = xdr_inline_decode(xdr, nfstime4_maxsz << 2);
if (unlikely(!p))
goto out_overflow;
- p = xdr_decode_hyper(p, &sec);
- nsec = be32_to_cpup(p);
- time->tv_sec = (time_t)sec;
- time->tv_nsec = (long)nsec;
+ xdr_decode_nfstime4(p, time);
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
--
2.14.3


2018-03-20 21:03:42

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 1/9] SUNRPC: Add helpers for decoding opaque and string types

Signed-off-by: Trond Myklebust <[email protected]>
---
include/linux/sunrpc/xdr.h | 6 ++++
net/sunrpc/xdr.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 88 insertions(+)

diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index d950223c64b1..7e609de34d85 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -253,6 +253,12 @@ xdr_stream_remaining(const struct xdr_stream *xdr)
return xdr->nwords << 2;
}

+ssize_t xdr_stream_decode_opaque(struct xdr_stream *xdr, void *ptr,
+ size_t size);
+ssize_t xdr_stream_decode_opaque_dup(struct xdr_stream *xdr, void **ptr,
+ size_t maxlen, gfp_t gfp_flags);
+ssize_t xdr_stream_decode_string(struct xdr_stream *xdr, char *str,
+ size_t size);
ssize_t xdr_stream_decode_string_dup(struct xdr_stream *xdr, char **str,
size_t maxlen, gfp_t gfp_flags);
/**
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index e34f4ee7f2b6..30afbd236656 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -1518,6 +1518,88 @@ xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len,
}
EXPORT_SYMBOL_GPL(xdr_process_buf);

+/**
+ * xdr_stream_decode_opaque - Decode variable length opaque
+ * @xdr: pointer to xdr_stream
+ * @ptr: location to store opaque data
+ * @size: size of storage buffer @ptr
+ *
+ * Return values:
+ * On success, returns size of object stored in *@ptr
+ * %-EBADMSG on XDR buffer overflow
+ * %-EMSGSIZE on overflow of storage buffer @ptr
+ */
+ssize_t xdr_stream_decode_opaque(struct xdr_stream *xdr, void *ptr, size_t size)
+{
+ ssize_t ret;
+ void *p;
+
+ ret = xdr_stream_decode_opaque_inline(xdr, &p, size);
+ if (ret <= 0)
+ return ret;
+ memcpy(ptr, p, ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(xdr_stream_decode_opaque);
+
+/**
+ * xdr_stream_decode_opaque_dup - Decode and duplicate variable length opaque
+ * @xdr: pointer to xdr_stream
+ * @ptr: location to store pointer to opaque data
+ * @maxlen: maximum acceptable object size
+ * @gfp_flags: GFP mask to use
+ *
+ * Return values:
+ * On success, returns size of object stored in *@ptr
+ * %-EBADMSG on XDR buffer overflow
+ * %-EMSGSIZE if the size of the object would exceed @maxlen
+ * %-ENOMEM on memory allocation failure
+ */
+ssize_t xdr_stream_decode_opaque_dup(struct xdr_stream *xdr, void **ptr,
+ size_t maxlen, gfp_t gfp_flags)
+{
+ ssize_t ret;
+ void *p;
+
+ ret = xdr_stream_decode_opaque_inline(xdr, &p, maxlen);
+ if (ret > 0) {
+ *ptr = kmemdup(p, ret, gfp_flags);
+ if (*ptr != NULL)
+ return ret;
+ ret = -ENOMEM;
+ }
+ *ptr = NULL;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(xdr_stream_decode_opaque_dup);
+
+/**
+ * xdr_stream_decode_string - Decode variable length string
+ * @xdr: pointer to xdr_stream
+ * @str: location to store string
+ * @size: size of storage buffer @str
+ *
+ * Return values:
+ * On success, returns length of NUL-terminated string stored in *@str
+ * %-EBADMSG on XDR buffer overflow
+ * %-EMSGSIZE on overflow of storage buffer @str
+ */
+ssize_t xdr_stream_decode_string(struct xdr_stream *xdr, char *str, size_t size)
+{
+ ssize_t ret;
+ void *p;
+
+ ret = xdr_stream_decode_opaque_inline(xdr, &p, size);
+ if (ret > 0) {
+ memcpy(str, p, ret);
+ str[ret] = '\0';
+ return strlen(str);
+ }
+ *str = '\0';
+ return ret;
+}
+EXPORT_SYMBOL_GPL(xdr_stream_decode_string);
+
/**
* xdr_stream_decode_string_dup - Decode and duplicate variable length string
* @xdr: pointer to xdr_stream
--
2.14.3


2018-03-20 21:03:45

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 4/9] NFSv4; Clean up XDR encoding of type bitmap4

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/nfs4xdr.c | 166 ++++++++++++++++++++-------------------------
include/linux/sunrpc/xdr.h | 63 +++++++++++++++++
2 files changed, 135 insertions(+), 94 deletions(-)

diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 6b08f6b1addf..80c5b519fd6a 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -958,6 +958,35 @@ static void encode_uint64(struct xdr_stream *xdr, u64 n)
WARN_ON_ONCE(xdr_stream_encode_u64(xdr, n) < 0);
}

+static ssize_t xdr_encode_bitmap4(struct xdr_stream *xdr,
+ const __u32 *bitmap, size_t len)
+{
+ ssize_t ret;
+
+ /* Trim empty words */
+ while (len > 0 && bitmap[len-1] == 0)
+ len--;
+ ret = xdr_stream_encode_uint32_array(xdr, bitmap, len);
+ if (WARN_ON_ONCE(ret < 0))
+ return ret;
+ return len;
+}
+
+static size_t mask_bitmap4(const __u32 *bitmap, const __u32 *mask,
+ __u32 *res, size_t len)
+{
+ size_t i;
+ __u32 tmp;
+
+ while (len > 0 && (bitmap[len-1] == 0 || mask[len-1] == 0))
+ len--;
+ for (i = len; i-- > 0;) {
+ tmp = bitmap[i] & mask[i];
+ res[i] = tmp;
+ }
+ return len;
+}
+
static void encode_nfs4_seqid(struct xdr_stream *xdr,
const struct nfs_seqid *seqid)
{
@@ -1200,85 +1229,45 @@ static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *
create->server, create->server->attr_bitmask);
}

-static void encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap, struct compound_hdr *hdr)
-{
- __be32 *p;
-
- encode_op_hdr(xdr, OP_GETATTR, decode_getattr_maxsz, hdr);
- p = reserve_space(xdr, 8);
- *p++ = cpu_to_be32(1);
- *p = cpu_to_be32(bitmap);
-}
-
-static void encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm1, struct compound_hdr *hdr)
-{
- __be32 *p;
-
- encode_op_hdr(xdr, OP_GETATTR, decode_getattr_maxsz, hdr);
- p = reserve_space(xdr, 12);
- *p++ = cpu_to_be32(2);
- *p++ = cpu_to_be32(bm0);
- *p = cpu_to_be32(bm1);
-}
-
-static void
-encode_getattr_three(struct xdr_stream *xdr,
- uint32_t bm0, uint32_t bm1, uint32_t bm2,
- struct compound_hdr *hdr)
+static void encode_getattr(struct xdr_stream *xdr,
+ const __u32 *bitmap, const __u32 *mask, size_t len,
+ struct compound_hdr *hdr)
{
- __be32 *p;
+ __u32 masked_bitmap[nfs4_fattr_bitmap_maxsz];

encode_op_hdr(xdr, OP_GETATTR, decode_getattr_maxsz, hdr);
- if (bm2) {
- p = reserve_space(xdr, 16);
- *p++ = cpu_to_be32(3);
- *p++ = cpu_to_be32(bm0);
- *p++ = cpu_to_be32(bm1);
- *p = cpu_to_be32(bm2);
- } else if (bm1) {
- p = reserve_space(xdr, 12);
- *p++ = cpu_to_be32(2);
- *p++ = cpu_to_be32(bm0);
- *p = cpu_to_be32(bm1);
- } else {
- p = reserve_space(xdr, 8);
- *p++ = cpu_to_be32(1);
- *p = cpu_to_be32(bm0);
+ if (mask) {
+ if (WARN_ON_ONCE(len > ARRAY_SIZE(masked_bitmap)))
+ len = ARRAY_SIZE(masked_bitmap);
+ len = mask_bitmap4(bitmap, mask, masked_bitmap, len);
+ bitmap = masked_bitmap;
}
+ xdr_encode_bitmap4(xdr, bitmap, len);
}

static void encode_getfattr(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
{
- encode_getattr_three(xdr, bitmask[0] & nfs4_fattr_bitmap[0],
- bitmask[1] & nfs4_fattr_bitmap[1],
- bitmask[2] & nfs4_fattr_bitmap[2],
- hdr);
+ encode_getattr(xdr, nfs4_fattr_bitmap, bitmask,
+ ARRAY_SIZE(nfs4_fattr_bitmap), hdr);
}

static void encode_getfattr_open(struct xdr_stream *xdr, const u32 *bitmask,
const u32 *open_bitmap,
struct compound_hdr *hdr)
{
- encode_getattr_three(xdr,
- bitmask[0] & open_bitmap[0],
- bitmask[1] & open_bitmap[1],
- bitmask[2] & open_bitmap[2],
- hdr);
+ encode_getattr(xdr, open_bitmap, bitmask, 3, hdr);
}

static void encode_fsinfo(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
{
- encode_getattr_three(xdr,
- bitmask[0] & nfs4_fsinfo_bitmap[0],
- bitmask[1] & nfs4_fsinfo_bitmap[1],
- bitmask[2] & nfs4_fsinfo_bitmap[2],
- hdr);
+ encode_getattr(xdr, nfs4_fsinfo_bitmap, bitmask,
+ ARRAY_SIZE(nfs4_fsinfo_bitmap), hdr);
}

static void encode_fs_locations(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
{
- encode_getattr_two(xdr, bitmask[0] & nfs4_fs_locations_bitmap[0],
- bitmask[1] & nfs4_fs_locations_bitmap[1], hdr);
+ encode_getattr(xdr, nfs4_fs_locations_bitmap, bitmask,
+ ARRAY_SIZE(nfs4_fs_locations_bitmap), hdr);
}

static void encode_getfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
@@ -2559,13 +2548,17 @@ static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr,
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
+ const __u32 nfs4_acl_bitmap[1] = {
+ [0] = FATTR4_WORD0_ACL,
+ };
uint32_t replen;

encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr);
replen = hdr.replen + op_decode_hdr_maxsz;
- encode_getattr_two(xdr, FATTR4_WORD0_ACL, 0, &hdr);
+ encode_getattr(xdr, nfs4_acl_bitmap, NULL,
+ ARRAY_SIZE(nfs4_acl_bitmap), &hdr);

xdr_inline_pages(&req->rq_rcv_buf, replen << 2,
args->acl_pages, 0, args->acl_len);
@@ -2644,8 +2637,8 @@ static void nfs4_xdr_enc_pathconf(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr);
- encode_getattr_one(xdr, args->bitmask[0] & nfs4_pathconf_bitmap[0],
- &hdr);
+ encode_getattr(xdr, nfs4_pathconf_bitmap, args->bitmask,
+ ARRAY_SIZE(nfs4_pathconf_bitmap), &hdr);
encode_nops(&hdr);
}

@@ -2663,8 +2656,8 @@ static void nfs4_xdr_enc_statfs(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr);
- encode_getattr_two(xdr, args->bitmask[0] & nfs4_statfs_bitmap[0],
- args->bitmask[1] & nfs4_statfs_bitmap[1], &hdr);
+ encode_getattr(xdr, nfs4_statfs_bitmap, args->bitmask,
+ ARRAY_SIZE(nfs4_statfs_bitmap), &hdr);
encode_nops(&hdr);
}

@@ -2684,7 +2677,7 @@ static void nfs4_xdr_enc_server_caps(struct rpc_rqst *req,
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fhandle, &hdr);
- encode_getattr_three(xdr, bitmask[0], bitmask[1], bitmask[2], &hdr);
+ encode_getattr(xdr, bitmask, NULL, 3, &hdr);
encode_nops(&hdr);
}

@@ -3218,34 +3211,27 @@ static int decode_ace(struct xdr_stream *xdr, void *ace)
return -EIO;
}

-static int decode_attr_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
+static ssize_t
+decode_bitmap4(struct xdr_stream *xdr, uint32_t *bitmap, size_t sz)
{
- uint32_t bmlen;
- __be32 *p;
-
- p = xdr_inline_decode(xdr, 4);
- if (unlikely(!p))
- goto out_overflow;
- bmlen = be32_to_cpup(p);
+ ssize_t ret;

- bitmap[0] = bitmap[1] = bitmap[2] = 0;
- p = xdr_inline_decode(xdr, (bmlen << 2));
- if (unlikely(!p))
- goto out_overflow;
- if (bmlen > 0) {
- bitmap[0] = be32_to_cpup(p++);
- if (bmlen > 1) {
- bitmap[1] = be32_to_cpup(p++);
- if (bmlen > 2)
- bitmap[2] = be32_to_cpup(p);
- }
- }
- return 0;
-out_overflow:
+ ret = xdr_stream_decode_uint32_array(xdr, bitmap, sz);
+ if (likely(ret >= 0))
+ return ret;
+ if (ret == -EMSGSIZE)
+ return sz;
print_overflow_msg(__func__, xdr);
return -EIO;
}

+static int decode_attr_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
+{
+ ssize_t ret;
+ ret = decode_bitmap4(xdr, bitmap, 3);
+ return ret < 0 ? ret : 0;
+}
+
static int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen, unsigned int *savep)
{
__be32 *p;
@@ -5471,21 +5457,13 @@ decode_savefh(struct xdr_stream *xdr)

static int decode_setattr(struct xdr_stream *xdr)
{
- __be32 *p;
- uint32_t bmlen;
int status;

status = decode_op_hdr(xdr, OP_SETATTR);
if (status)
return status;
- p = xdr_inline_decode(xdr, 4);
- if (unlikely(!p))
- goto out_overflow;
- bmlen = be32_to_cpup(p);
- p = xdr_inline_decode(xdr, bmlen << 2);
- if (likely(p))
+ if (decode_bitmap4(xdr, NULL, 0) >= 0)
return 0;
-out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index a43c3b6455b6..2bd68177a442 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -386,6 +386,31 @@ xdr_stream_encode_opaque(struct xdr_stream *xdr, const void *ptr, size_t len)
return count;
}

+/**
+ * xdr_stream_encode_uint32_array - Encode variable length array of integers
+ * @xdr: pointer to xdr_stream
+ * @array: array of integers
+ * @array_size: number of elements in @array
+ *
+ * Return values:
+ * On success, returns length in bytes of XDR buffer consumed
+ * %-EMSGSIZE on XDR buffer overflow
+ */
+static inline ssize_t
+xdr_stream_encode_uint32_array(struct xdr_stream *xdr,
+ const __u32 *array, size_t array_size)
+{
+ ssize_t ret = (array_size+1) * sizeof(__u32);
+ __be32 *p = xdr_reserve_space(xdr, ret);
+
+ if (unlikely(!p))
+ return -EMSGSIZE;
+ *p++ = cpu_to_be32(array_size);
+ for (; array_size > 0; p++, array++, array_size--)
+ *p = cpu_to_be32p(array);
+ return ret;
+}
+
/**
* xdr_stream_decode_u32 - Decode a 32-bit integer
* @xdr: pointer to xdr_stream
@@ -463,6 +488,44 @@ xdr_stream_decode_opaque_inline(struct xdr_stream *xdr, void **ptr, size_t maxle
}
return len;
}
+
+/**
+ * xdr_stream_decode_uint32_array - Decode variable length array of integers
+ * @xdr: pointer to xdr_stream
+ * @array: location to store the integer array or NULL
+ * @array_size: number of elements to store
+ *
+ * Return values:
+ * On success, returns number of elements stored in @array
+ * %-EBADMSG on XDR buffer overflow
+ * %-EMSGSIZE if the size of the array exceeds @array_size
+ */
+static inline ssize_t
+xdr_stream_decode_uint32_array(struct xdr_stream *xdr,
+ __u32 *array, size_t array_size)
+{
+ __be32 *p;
+ __u32 len;
+ ssize_t retval;
+
+ if (unlikely(xdr_stream_decode_u32(xdr, &len) < 0))
+ return -EBADMSG;
+ p = xdr_inline_decode(xdr, len * sizeof(*p));
+ if (unlikely(!p))
+ return -EBADMSG;
+ if (array == NULL)
+ return len;
+ if (len <= array_size) {
+ if (len < array_size)
+ memset(array+len, 0, (array_size-len)*sizeof(*array));
+ array_size = len;
+ retval = len;
+ } else
+ retval = -EMSGSIZE;
+ for (; array_size > 0; p++, array++, array_size--)
+ *array = be32_to_cpup(p);
+ return retval;
+}
#endif /* __KERNEL__ */

#endif /* _SUNRPC_XDR_H_ */
--
2.14.3


2018-03-20 21:03:47

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 5/9] NFSv4: Clean up encode_attrs

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/nfs4xdr.c | 17 ++---------------
1 file changed, 2 insertions(+), 15 deletions(-)

diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 80c5b519fd6a..3d088230c975 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -1052,9 +1052,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
int owner_namelen = 0;
int owner_grouplen = 0;
__be32 *p;
- unsigned i;
uint32_t len = 0;
- uint32_t bmval_len;
uint32_t bmval[3] = { 0 };

/*
@@ -1123,19 +1121,8 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
bmval[2] |= FATTR4_WORD2_SECURITY_LABEL;
}

- if (bmval[2] != 0)
- bmval_len = 3;
- else if (bmval[1] != 0)
- bmval_len = 2;
- else
- bmval_len = 1;
-
- p = reserve_space(xdr, 4 + (bmval_len << 2) + 4 + len);
-
- *p++ = cpu_to_be32(bmval_len);
- for (i = 0; i < bmval_len; i++)
- *p++ = cpu_to_be32(bmval[i]);
- *p++ = cpu_to_be32(len);
+ xdr_encode_bitmap4(xdr, bmval, ARRAY_SIZE(bmval));
+ xdr_stream_encode_opaque_inline(xdr, (void **)&p, len);

if (bmval[0] & FATTR4_WORD0_SIZE)
p = xdr_encode_hyper(p, iap->ia_size);
--
2.14.3


2018-03-20 21:03:43

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 2/9] SUNRPC: Add a helper for encoding opaque data inline

Signed-off-by: Trond Myklebust <[email protected]>
---
include/linux/sunrpc/xdr.h | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)

diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index 7e609de34d85..a43c3b6455b6 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -318,6 +318,31 @@ xdr_stream_encode_u64(struct xdr_stream *xdr, __u64 n)
return len;
}

+/**
+ * xdr_stream_encode_opaque_inline - Encode opaque xdr data
+ * @xdr: pointer to xdr_stream
+ * @ptr: pointer to void pointer
+ * @len: size of object
+ *
+ * Return values:
+ * On success, returns length in bytes of XDR buffer consumed
+ * %-EMSGSIZE on XDR buffer overflow
+ */
+static inline ssize_t
+xdr_stream_encode_opaque_inline(struct xdr_stream *xdr, void **ptr, size_t len)
+{
+ size_t count = sizeof(__u32) + xdr_align_size(len);
+ __be32 *p = xdr_reserve_space(xdr, count);
+
+ if (unlikely(!p)) {
+ *ptr = NULL;
+ return -EMSGSIZE;
+ }
+ xdr_encode_opaque(p, NULL, len);
+ *ptr = ++p;
+ return count;
+}
+
/**
* xdr_stream_encode_opaque_fixed - Encode fixed length opaque xdr data
* @xdr: pointer to xdr_stream
--
2.14.3


2018-03-20 21:03:44

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 3/9] NFSv4: Allow GFP_NOIO sleeps in decode_attr_owner/decode_attr_group

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/nfs4xdr.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 65c9c4175145..6b08f6b1addf 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -3981,7 +3981,7 @@ static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap,
bitmap[1] &= ~FATTR4_WORD1_OWNER;

if (owner_name != NULL) {
- len = decode_nfs4_string(xdr, owner_name, GFP_NOWAIT);
+ len = decode_nfs4_string(xdr, owner_name, GFP_NOIO);
if (len <= 0)
goto out;
dprintk("%s: name=%s\n", __func__, owner_name->data);
@@ -4016,7 +4016,7 @@ static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap,
bitmap[1] &= ~FATTR4_WORD1_OWNER_GROUP;

if (group_name != NULL) {
- len = decode_nfs4_string(xdr, group_name, GFP_NOWAIT);
+ len = decode_nfs4_string(xdr, group_name, GFP_NOIO);
if (len <= 0)
goto out;
dprintk("%s: name=%s\n", __func__, group_name->data);
--
2.14.3


2018-03-20 21:03:49

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 8/9] NFSv4: Clean up CB_GETATTR encoding

Replace the open coded bitmap implementation with a generic one.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/callback_xdr.c | 37 ++++++++-----------------------------
1 file changed, 8 insertions(+), 29 deletions(-)

diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c
index 123c069429a7..a813979b5be0 100644
--- a/fs/nfs/callback_xdr.c
+++ b/fs/nfs/callback_xdr.c
@@ -535,35 +535,10 @@ static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char
return 0;
}

-#define CB_SUPPORTED_ATTR0 (FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE)
-#define CB_SUPPORTED_ATTR1 (FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY)
-static __be32 encode_attr_bitmap(struct xdr_stream *xdr, const uint32_t *bitmap, __be32 **savep)
+static __be32 encode_attr_bitmap(struct xdr_stream *xdr, const uint32_t *bitmap, size_t sz)
{
- __be32 bm[2];
- __be32 *p;
-
- bm[0] = htonl(bitmap[0] & CB_SUPPORTED_ATTR0);
- bm[1] = htonl(bitmap[1] & CB_SUPPORTED_ATTR1);
- if (bm[1] != 0) {
- p = xdr_reserve_space(xdr, 16);
- if (unlikely(p == NULL))
- return htonl(NFS4ERR_RESOURCE);
- *p++ = htonl(2);
- *p++ = bm[0];
- *p++ = bm[1];
- } else if (bm[0] != 0) {
- p = xdr_reserve_space(xdr, 12);
- if (unlikely(p == NULL))
- return htonl(NFS4ERR_RESOURCE);
- *p++ = htonl(1);
- *p++ = bm[0];
- } else {
- p = xdr_reserve_space(xdr, 8);
- if (unlikely(p == NULL))
- return htonl(NFS4ERR_RESOURCE);
- *p++ = htonl(0);
- }
- *savep = p;
+ if (xdr_stream_encode_uint32_array(xdr, bitmap, sz) < 0)
+ return cpu_to_be32(NFS4ERR_RESOURCE);
return 0;
}

@@ -656,9 +631,13 @@ static __be32 encode_getattr_res(struct svc_rqst *rqstp, struct xdr_stream *xdr,

if (unlikely(status != 0))
goto out;
- status = encode_attr_bitmap(xdr, res->bitmap, &savep);
+ status = encode_attr_bitmap(xdr, res->bitmap, ARRAY_SIZE(res->bitmap));
if (unlikely(status != 0))
goto out;
+ status = cpu_to_be32(NFS4ERR_RESOURCE);
+ savep = xdr_reserve_space(xdr, sizeof(*savep));
+ if (unlikely(!savep))
+ goto out;
status = encode_attr_change(xdr, res->bitmap, res->change_attr);
if (unlikely(status != 0))
goto out;
--
2.14.3


2018-03-20 21:03:51

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 9/9] NFSv4: Fix the nfs_inode_set_delegation() arguments

Neither nfs_inode_set_delegation() nor nfs_inode_reclaim_delegation() are
generic code. They have no business delving into NFSv4 OPEN xdr structures,
so let's replace the "struct nfs_openres" parameter.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/delegation.c | 35 ++++++++++++++++++++++-------------
fs/nfs/delegation.h | 6 ++++--
fs/nfs/nfs4proc.c | 12 ++++++++----
3 files changed, 34 insertions(+), 19 deletions(-)

diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index a5cb44375100..1819d0d0ba4b 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -172,11 +172,15 @@ static int nfs_delegation_claim_opens(struct inode *inode,
* nfs_inode_reclaim_delegation - process a delegation reclaim request
* @inode: inode to process
* @cred: credential to use for request
- * @res: new delegation state from server
+ * @type: delegation type
+ * @stateid: delegation stateid
+ * @pagemod_limit: write delegation "space_limit"
*
*/
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
- struct nfs_openres *res)
+ fmode_t type,
+ const nfs4_stateid *stateid,
+ unsigned long pagemod_limit)
{
struct nfs_delegation *delegation;
struct rpc_cred *oldcred = NULL;
@@ -186,9 +190,9 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
if (delegation != NULL) {
spin_lock(&delegation->lock);
if (delegation->inode != NULL) {
- nfs4_stateid_copy(&delegation->stateid, &res->delegation);
- delegation->type = res->delegation_type;
- delegation->pagemod_limit = res->pagemod_limit;
+ nfs4_stateid_copy(&delegation->stateid, stateid);
+ delegation->type = type;
+ delegation->pagemod_limit = pagemod_limit;
oldcred = delegation->cred;
delegation->cred = get_rpccred(cred);
clear_bit(NFS_DELEGATION_NEED_RECLAIM,
@@ -196,14 +200,14 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
spin_unlock(&delegation->lock);
rcu_read_unlock();
put_rpccred(oldcred);
- trace_nfs4_reclaim_delegation(inode, res->delegation_type);
+ trace_nfs4_reclaim_delegation(inode, type);
return;
}
/* We appear to have raced with a delegation return. */
spin_unlock(&delegation->lock);
}
rcu_read_unlock();
- nfs_inode_set_delegation(inode, cred, res);
+ nfs_inode_set_delegation(inode, cred, type, stateid, pagemod_limit);
}

static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
@@ -330,11 +334,16 @@ nfs_update_inplace_delegation(struct nfs_delegation *delegation,
* nfs_inode_set_delegation - set up a delegation on an inode
* @inode: inode to which delegation applies
* @cred: cred to use for subsequent delegation processing
- * @res: new delegation state from server
+ * @type: delegation type
+ * @stateid: delegation stateid
+ * @pagemod_limit: write delegation "space_limit"
*
* Returns zero on success, or a negative errno value.
*/
-int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
+int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred,
+ fmode_t type,
+ const nfs4_stateid *stateid,
+ unsigned long pagemod_limit)
{
struct nfs_server *server = NFS_SERVER(inode);
struct nfs_client *clp = server->nfs_client;
@@ -346,9 +355,9 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
delegation = kmalloc(sizeof(*delegation), GFP_NOFS);
if (delegation == NULL)
return -ENOMEM;
- nfs4_stateid_copy(&delegation->stateid, &res->delegation);
- delegation->type = res->delegation_type;
- delegation->pagemod_limit = res->pagemod_limit;
+ nfs4_stateid_copy(&delegation->stateid, stateid);
+ delegation->type = type;
+ delegation->pagemod_limit = pagemod_limit;
delegation->change_attr = inode_peek_iversion_raw(inode);
delegation->cred = get_rpccred(cred);
delegation->inode = inode;
@@ -393,7 +402,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
rcu_assign_pointer(nfsi->delegation, delegation);
delegation = NULL;

- trace_nfs4_set_delegation(inode, res->delegation_type);
+ trace_nfs4_set_delegation(inode, type);

out:
spin_unlock(&clp->cl_lock);
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
index dcc8a783a6e1..bb1ef8c37af4 100644
--- a/fs/nfs/delegation.h
+++ b/fs/nfs/delegation.h
@@ -36,8 +36,10 @@ enum {
NFS_DELEGATION_TEST_EXPIRED,
};

-int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
-void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
+int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred,
+ fmode_t type, const nfs4_stateid *stateid, unsigned long pagemod_limit);
+void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
+ fmode_t type, const nfs4_stateid *stateid, unsigned long pagemod_limit);
int nfs4_inode_return_delegation(struct inode *inode);
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid);
void nfs_inode_return_delegation_noreclaim(struct inode *inode);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 64dd0fa7afaa..6933109fa30f 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -1754,12 +1754,16 @@ nfs4_opendata_check_deleg(struct nfs4_opendata *data, struct nfs4_state *state)
}
if ((delegation_flags & 1UL<<NFS_DELEGATION_NEED_RECLAIM) == 0)
nfs_inode_set_delegation(state->inode,
- data->owner->so_cred,
- &data->o_res);
+ data->owner->so_cred,
+ data->o_res.delegation_type,
+ &data->o_res.delegation,
+ data->o_res.pagemod_limit);
else
nfs_inode_reclaim_delegation(state->inode,
- data->owner->so_cred,
- &data->o_res);
+ data->owner->so_cred,
+ data->o_res.delegation_type,
+ &data->o_res.delegation,
+ data->o_res.pagemod_limit);
}

/*
--
2.14.3


2018-03-20 21:03:48

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 7/9] NFSv4: Don't ask for attributes when ACCESS is protected by a delegation

If we hold a delegation, then the results of the ACCESS call are protected
anyway.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/nfs4proc.c | 13 ++++++++-----
fs/nfs/nfs4xdr.c | 6 ++++--
2 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index acb3350c7571..64dd0fa7afaa 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -4055,7 +4055,6 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_accessargs args = {
.fh = NFS_FH(inode),
- .bitmask = server->cache_consistency_bitmask,
.access = entry->mask,
};
struct nfs4_accessres res = {
@@ -4069,14 +4068,18 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
};
int status = 0;

- res.fattr = nfs_alloc_fattr();
- if (res.fattr == NULL)
- return -ENOMEM;
+ if (!nfs_have_delegated_attributes(inode)) {
+ res.fattr = nfs_alloc_fattr();
+ if (res.fattr == NULL)
+ return -ENOMEM;
+ args.bitmask = server->cache_consistency_bitmask;
+ }

status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
if (!status) {
nfs_access_set_mask(entry, res.access);
- nfs_refresh_inode(inode, res.fattr);
+ if (res.fattr)
+ nfs_refresh_inode(inode, res.fattr);
}
nfs_free_fattr(res.fattr);
return status;
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 79f1774b9d68..51264f5d9d2a 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -2102,7 +2102,8 @@ static void nfs4_xdr_enc_access(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr);
encode_access(xdr, args->access, &hdr);
- encode_getfattr(xdr, args->bitmask, &hdr);
+ if (args->bitmask)
+ encode_getfattr(xdr, args->bitmask, &hdr);
encode_nops(&hdr);
}

@@ -6236,7 +6237,8 @@ static int nfs4_xdr_dec_access(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
status = decode_access(xdr, &res->supported, &res->access);
if (status != 0)
goto out;
- decode_getfattr(xdr, res->fattr, res->server);
+ if (res->fattr)
+ decode_getfattr(xdr, res->fattr, res->server);
out:
return status;
}
--
2.14.3


2022-05-17 19:17:31

by Dan Aloni

[permalink] [raw]
Subject: Re: [PATCH 5/9] NFSv4: Clean up encode_attrs

Hi Trond,

Wireshark claims that this commit broke encoding of SETATTR (see below).

Is Wireshark correct in reference to the RFC?

Frame 56: 274 bytes on wire (2192 bits), 274 bytes captured (2192 bits)
Ethernet II, Src: RealtekU_d0:d8:ff (52:54:00:d0:d8:ff), Dst: RealtekU_7a:80:63 (52:54:00:7a:80:63)
Internet Protocol Version 4, Src: 192.168.40.1, Dst: 192.168.40.11
Transmission Control Protocol, Src Port: 999, Dst Port: 2049, Seq: 4325, Ack: 4489, Len: 208
Remote Procedure Call, Type:Call XID:0x3725a760
Network File System, Ops(4): SEQUENCE, PUTFH, SETATTR, GETATTR
[Program Version: 4]
[V4 Procedure: COMPOUND (1)]
Tag: <EMPTY>
minorversion: 1
Operations (count: 4): SEQUENCE, PUTFH, SETATTR, GETATTR
Opcode: SEQUENCE (53)
Opcode: PUTFH (22)
Opcode: SETATTR (34)
StateID
[StateID Hash: 0xafa9]
StateID seqid: 0
StateID Other: 000000000000000000000000
[StateID Other hash: 0x60de]
[Expert Info (Warning/Protocol): Per RFCs 3530 and 5661 an attribute mask is required but was not provided.]
[Per RFCs 3530 and 5661 an attribute mask is required but was not provided.]
[Severity level: Warning]
[Group: Protocol]
Opcode: GETATTR (9)
[Main Opcode: SETATTR (34)]

0000 52 54 00 7a 80 63 52 54 00 d0 d8 ff 08 00 45 00 RT.z.cRT......E.
0010 01 04 a6 16 40 00 40 06 c2 80 c0 a8 28 01 c0 a8 ....@.@.....(...
0020 28 0b 03 e7 08 01 14 fe a6 02 de f4 09 10 80 18 (...............
0030 01 7b d2 53 00 00 01 01 08 0a 68 ba 83 03 e9 ee .{.S......h.....
0040 7c 0b 80 00 00 cc 37 25 a7 60 00 00 00 00 00 00 |.....7%.`......
0050 00 02 00 01 86 a3 00 00 00 04 00 00 00 01 00 00 ................
0060 00 01 00 00 00 30 00 41 88 3d 00 00 00 16 63 6c .....0.A.=....cl
0070 69 65 6e 74 2e 6e 66 73 2d 74 65 73 74 69 6e 67 ient.nfs-testing
0080 2e 63 6f 6d 00 00 00 00 00 00 00 00 00 00 00 00 .com............
0090 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00a0 00 00 00 00 00 01 00 00 00 04 00 00 00 35 37 00 .............57.
00b0 00 00 00 a0 00 00 36 00 00 00 00 a0 00 00 00 00 ......6.........
00c0 00 14 00 00 00 00 00 00 00 00 00 00 00 01 00 00 ................
00d0 00 16 00 00 00 10 00 00 69 8d 44 d8 0e 34 0f 7d ........i.D..4.}
00e0 00 00 00 00 00 00 00 00 00 22 00 00 00 00 00 00 ........."......
00f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0100 00 00 00 00 00 09 00 00 00 02 00 10 01 1a 00 30 ...............0
0110 a2 3a .:

On 2018-03-20 17:03:09, Trond Myklebust wrote:
> Signed-off-by: Trond Myklebust <[email protected]>
> ---
> fs/nfs/nfs4xdr.c | 17 ++---------------
> 1 file changed, 2 insertions(+), 15 deletions(-)
>
> diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
> index 80c5b519fd6a..3d088230c975 100644
> --- a/fs/nfs/nfs4xdr.c
> +++ b/fs/nfs/nfs4xdr.c
> @@ -1052,9 +1052,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
> int owner_namelen = 0;
> int owner_grouplen = 0;
> __be32 *p;
> - unsigned i;
> uint32_t len = 0;
> - uint32_t bmval_len;
> uint32_t bmval[3] = { 0 };
>
> /*
> @@ -1123,19 +1121,8 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
> bmval[2] |= FATTR4_WORD2_SECURITY_LABEL;
> }
>
> - if (bmval[2] != 0)
> - bmval_len = 3;
> - else if (bmval[1] != 0)
> - bmval_len = 2;
> - else
> - bmval_len = 1;
> -
> - p = reserve_space(xdr, 4 + (bmval_len << 2) + 4 + len);
> -
> - *p++ = cpu_to_be32(bmval_len);
> - for (i = 0; i < bmval_len; i++)
> - *p++ = cpu_to_be32(bmval[i]);
> - *p++ = cpu_to_be32(len);
> + xdr_encode_bitmap4(xdr, bmval, ARRAY_SIZE(bmval));
> + xdr_stream_encode_opaque_inline(xdr, (void **)&p, len);
>
> if (bmval[0] & FATTR4_WORD0_SIZE)
> p = xdr_encode_hyper(p, iap->ia_size);
> --
> 2.14.3
>

--
Dan Aloni

2022-05-18 04:30:43

by Trond Myklebust

[permalink] [raw]
Subject: Re: [PATCH 5/9] NFSv4: Clean up encode_attrs

On Tue, 2022-05-17 at 20:00 +0300, Dan Aloni wrote:
> Hi Trond,
>
> Wireshark claims that this commit broke encoding of SETATTR (see
> below).
>
> Is Wireshark correct in reference to the RFC?
>
> Frame 56: 274 bytes on wire (2192 bits), 274 bytes captured (2192
> bits)
> Ethernet II, Src: RealtekU_d0:d8:ff (52:54:00:d0:d8:ff), Dst:
> RealtekU_7a:80:63 (52:54:00:7a:80:63)
> Internet Protocol Version 4, Src: 192.168.40.1, Dst: 192.168.40.11
> Transmission Control Protocol, Src Port: 999, Dst Port: 2049, Seq:
> 4325, Ack: 4489, Len: 208
> Remote Procedure Call, Type:Call XID:0x3725a760
> Network File System, Ops(4): SEQUENCE, PUTFH, SETATTR, GETATTR
>     [Program Version: 4]
>     [V4 Procedure: COMPOUND (1)]
>     Tag: <EMPTY>
>     minorversion: 1
>     Operations (count: 4): SEQUENCE, PUTFH, SETATTR, GETATTR
>         Opcode: SEQUENCE (53)
>         Opcode: PUTFH (22)
>         Opcode: SETATTR (34)
>             StateID
>                 [StateID Hash: 0xafa9]
>                 StateID seqid: 0
>                 StateID Other: 000000000000000000000000
>                 [StateID Other hash: 0x60de]
>             [Expert Info (Warning/Protocol): Per RFCs 3530 and 5661
> an attribute mask is required but was not provided.]
>                 [Per RFCs 3530 and 5661 an attribute mask is required
> but was not provided.]
>                 [Severity level: Warning]
>                 [Group: Protocol]
>         Opcode: GETATTR (9)
>     [Main Opcode: SETATTR (34)]
>
> 0000  52 54 00 7a 80 63 52 54 00 d0 d8 ff 08 00 45 00  
> RT.z.cRT......E.
> 0010  01 04 a6 16 40 00 40 06 c2 80 c0 a8 28 01 c0 a8  
> ....@.@.....(...
> 0020  28 0b 03 e7 08 01 14 fe a6 02 de f4 09 10 80 18  
> (...............
> 0030  01 7b d2 53 00 00 01 01 08 0a 68 ba 83 03 e9 ee  
> .{.S......h.....
> 0040  7c 0b 80 00 00 cc 37 25 a7 60 00 00 00 00 00 00  
> |.....7%.`......
> 0050  00 02 00 01 86 a3 00 00 00 04 00 00 00 01 00 00  
> ................
> 0060  00 01 00 00 00 30 00 41 88 3d 00 00 00 16 63 6c  
> .....0.A.=....cl
> 0070  69 65 6e 74 2e 6e 66 73 2d 74 65 73 74 69 6e 67   ient.nfs-
> testing
> 0080  2e 63 6f 6d 00 00 00 00 00 00 00 00 00 00 00 00  
> .com............
> 0090  00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00  
> ................
> 00a0  00 00 00 00 00 01 00 00 00 04 00 00 00 35 37 00  
> .............57.
> 00b0  00 00 00 a0 00 00 36 00 00 00 00 a0 00 00 00 00  
> ......6.........
> 00c0  00 14 00 00 00 00 00 00 00 00 00 00 00 01 00 00  
> ................
> 00d0  00 16 00 00 00 10 00 00 69 8d 44 d8 0e 34 0f 7d  
> ........i.D..4.}
> 00e0  00 00 00 00 00 00 00 00 00 22 00 00 00 00 00 00  
> ........."......
> 00f0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  
> ................
> 0100  00 00 00 00 00 09 00 00 00 02 00 10 01 1a 00 30  
> ...............0
> 0110  a2 3a                                             .:
>
> On 2018-03-20 17:03:09, Trond Myklebust wrote:
> > Signed-off-by: Trond Myklebust <[email protected]>
> > ---
> >  fs/nfs/nfs4xdr.c | 17 ++---------------
> >  1 file changed, 2 insertions(+), 15 deletions(-)
> >
> > diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
> > index 80c5b519fd6a..3d088230c975 100644
> > --- a/fs/nfs/nfs4xdr.c
> > +++ b/fs/nfs/nfs4xdr.c
> > @@ -1052,9 +1052,7 @@ static void encode_attrs(struct xdr_stream
> > *xdr, const struct iattr *iap,
> >         int owner_namelen = 0;
> >         int owner_grouplen = 0;
> >         __be32 *p;
> > -       unsigned i;
> >         uint32_t len = 0;
> > -       uint32_t bmval_len;
> >         uint32_t bmval[3] = { 0 };
> >  
> >         /*
> > @@ -1123,19 +1121,8 @@ static void encode_attrs(struct xdr_stream
> > *xdr, const struct iattr *iap,
> >                 bmval[2] |= FATTR4_WORD2_SECURITY_LABEL;
> >         }
> >  
> > -       if (bmval[2] != 0)
> > -               bmval_len = 3;
> > -       else if (bmval[1] != 0)
> > -               bmval_len = 2;
> > -       else
> > -               bmval_len = 1;
> > -
> > -       p = reserve_space(xdr, 4 + (bmval_len << 2) + 4 + len);
> > -
> > -       *p++ = cpu_to_be32(bmval_len);
> > -       for (i = 0; i < bmval_len; i++)
> > -               *p++ = cpu_to_be32(bmval[i]);
> > -       *p++ = cpu_to_be32(len);
> > +       xdr_encode_bitmap4(xdr, bmval, ARRAY_SIZE(bmval));
> > +       xdr_stream_encode_opaque_inline(xdr, (void **)&p, len);
> >  
> >         if (bmval[0] & FATTR4_WORD0_SIZE)
> >                 p = xdr_encode_hyper(p, iap->ia_size);
> > --
> > 2.14.3
> >
>

Is the bitmap perhaps empty? The new code will just make that a zero
length array. I'm not sure if wireshark would deal correctly with that.

We shouldn't be trying to send a SETATTR with no attributes, but it is
possible that the server just doesn't support the attributes that we're
trying to set.

--
Trond Myklebust
Linux NFS client maintainer, Hammerspace
[email protected]