public inbox for linux-nfs@vger.kernel.org
 help / color / mirror / Atom feed
From: Chuck Lever <chuck.lever@oracle.com>
To: Frank van der Linden <fllinden@amazon.com>
Cc: Bruce Fields <bfields@fieldses.org>,
	Linux NFS Mailing List <linux-nfs@vger.kernel.org>
Subject: Re: [PATCH 11/14] nfsd: add user xattr RPC XDR encoding/decoding logic
Date: Thu, 12 Mar 2020 15:16:37 -0400	[thread overview]
Message-ID: <6955728A-CFCC-40FC-9E02-671255EDD45F@oracle.com> (raw)
In-Reply-To: <20200311195954.27117-12-fllinden@amazon.com>



> On Mar 11, 2020, at 3:59 PM, Frank van der Linden <fllinden@amazon.com> wrote:
> 
> Add functions to calculate the reply size for the user extended attribute
> operations, and implement the XDR encode / decode logic for these
> operations.
> 
> Signed-off-by: Frank van der Linden <fllinden@amazon.com>
> ---
> fs/nfsd/nfs4proc.c |  36 +++++
> fs/nfsd/nfs4xdr.c  | 448 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 484 insertions(+)
> 
> diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> index a76b9025a357..44d488bdebd9 100644
> --- a/fs/nfsd/nfs4proc.c
> +++ b/fs/nfsd/nfs4proc.c
> @@ -2778,6 +2778,42 @@ static inline u32 nfsd4_seek_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
> 	return (op_encode_hdr_size + 3) * sizeof(__be32);
> }
> 
> +static inline u32 nfsd4_getxattr_rsize(struct svc_rqst *rqstp,
> +				       struct nfsd4_op *op)
> +{
> +	u32 maxcount, rlen;
> +
> +	maxcount = svc_max_payload(rqstp);
> +	rlen = min_t(u32, XATTR_SIZE_MAX, maxcount);
> +
> +	return (op_encode_hdr_size + 1 + XDR_QUADLEN(rlen)) * sizeof(__be32);
> +}
> +
> +static inline u32 nfsd4_setxattr_rsize(struct svc_rqst *rqstp,
> +				       struct nfsd4_op *op)
> +{
> +	return (op_encode_hdr_size + op_encode_change_info_maxsz)
> +		* sizeof(__be32);
> +}
> +static inline u32 nfsd4_listxattrs_rsize(struct svc_rqst *rqstp,
> +					 struct nfsd4_op *op)
> +{
> +	u32 maxcount, rlen;
> +
> +	maxcount = svc_max_payload(rqstp);
> +	rlen = min(op->u.listxattrs.lsxa_maxcount, maxcount);
> +
> +	return (op_encode_hdr_size + 4 + XDR_QUADLEN(rlen)) * sizeof(__be32);
> +}
> +
> +static inline u32 nfsd4_removexattr_rsize(struct svc_rqst *rqstp,
> +					  struct nfsd4_op *op)
> +{
> +	return (op_encode_hdr_size + op_encode_change_info_maxsz)
> +		* sizeof(__be32);
> +}
> +
> +
> static const struct nfsd4_operation nfsd4_ops[LAST_NFS4_OP + 1] = {
> 	[OP_ACCESS] = {
> 		.op_func = nfsd4_access,
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index b12d7ac6f52c..41c8b95ca1c5 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -1879,6 +1879,224 @@ nfsd4_decode_seek(struct nfsd4_compoundargs *argp, struct nfsd4_seek *seek)
> 	DECODE_TAIL;
> }
> 
> +/*
> + * XDR data that is more than PAGE_SIZE in size is normally part of a
> + * read or write. However, the size of extended attributes is limited
> + * by the maximum request size, and then further limited by the underlying
> + * filesystem limits. This can exceed PAGE_SIZE (currently, XATTR_SIZE_MAX
> + * is 64k). Since there is no kvec- or page-based interface to xattrs,
> + * and we're not dealing with contiguous pages, we need to do some copying.
> + */
> +
> +/*
> + * Decode int to buffer.

"int" is of course a C keyword, so this typo had me confused!
I think you mean "Decode into buffer."


> Uses head and pages constructed by
> + * svcxdr_construct_vector.
> + */
> +static int
> +nfsd4_vbuf_from_stream(struct nfsd4_compoundargs *argp, struct kvec *head,
> +		       struct page **pages, char **bufp, u32 buflen)
> +{
> +	char *tmp, *dp;
> +	u32 len;
> +
> +	if (buflen <= head->iov_len) {
> +		/*
> +		 * We're in luck, the head has enough space. Just return
> +		 * the head, no need for copying.
> +		 */
> +		*bufp = head->iov_base;
> +		return 0;
> +	}
> +
> +	tmp = svcxdr_tmpalloc(argp, buflen);
> +	if (tmp == NULL)
> +		return nfserr_jukebox;
> +
> +	dp = tmp;
> +	memcpy(dp, head->iov_base, head->iov_len);
> +	buflen -= head->iov_len;
> +	dp += head->iov_len;
> +
> +	while (buflen > 0) {
> +		len = min_t(u32, buflen, PAGE_SIZE);
> +		memcpy(dp, page_address(*pages), len);
> +
> +		buflen -= len;
> +		dp += len;
> +		pages++;
> +	}
> +
> +	*bufp = tmp;
> +	return 0;
> +}
> +
> +/*
> + * Get a user extended attribute name from the XDR buffer.
> + * It will not have the "user." prefix, so prepend it.
> + * Lastly, check for nul characters in the name.
> + */
> +static int
> +nfsd4_decode_xattr_name(struct nfsd4_compoundargs *argp, char **namep)
> +{
> +	DECODE_HEAD;
> +	char *name, *sp, *dp;
> +	u32 namelen, cnt;
> +
> +	READ_BUF(4);
> +	namelen = be32_to_cpup(p++);
> +
> +	if (namelen > (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN))
> +		return nfserr_nametoolong;
> +
> +	if (namelen == 0)
> +		goto xdr_error;
> +
> +	READ_BUF(namelen);
> +
> +	name = svcxdr_tmpalloc(argp, namelen + XATTR_USER_PREFIX_LEN + 1);
> +	if (!name)
> +		return nfserr_jukebox;
> +
> +	memcpy(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
> +
> +	/*
> +	 * Copy the extended attribute name over while checking for 0
> +	 * characters.
> +	 */
> +	sp = (char *)p;
> +	dp = name + XATTR_USER_PREFIX_LEN;
> +	cnt = namelen;
> +
> +	while (cnt-- > 0) {
> +		if (*sp == '\0')
> +			goto xdr_error;
> +		*dp++ = *sp++;
> +	}
> +	*dp = '\0';
> +
> +	*namep = name;
> +
> +	DECODE_TAIL;
> +}
> +
> +/*
> + * A GETXATTR op request comes without a length specifier. We just set the
> + * maximum length for the reply based on XATTR_SIZE_MAX and the maximum
> + * channel reply size, allocate a buffer of that length and pass it to
> + * vfs_getxattr.
> + */
> +static __be32
> +nfsd4_decode_getxattr(struct nfsd4_compoundargs *argp,
> +		      struct nfsd4_getxattr *getxattr)
> +{
> +	int status;
> +	u32 maxcount;
> +
> +	status = nfsd4_decode_xattr_name(argp, &getxattr->getxa_name);
> +	if (status)
> +		return status;
> +
> +	maxcount = svc_max_payload(argp->rqstp);
> +	maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount);
> +
> +	getxattr->getxa_buf = svcxdr_tmpalloc(argp, maxcount);
> +	if (!getxattr->getxa_buf)
> +		status = nfserr_jukebox;
> +	getxattr->getxa_len = maxcount;
> +
> +	return status;
> +}
> +
> +static __be32
> +nfsd4_decode_setxattr(struct nfsd4_compoundargs *argp,
> +		      struct nfsd4_setxattr *setxattr)
> +{
> +	DECODE_HEAD;
> +	u32 flags, maxcount, size;
> +	struct kvec head;
> +	struct page **pagelist;
> +
> +	READ_BUF(4);
> +	flags = be32_to_cpup(p++);
> +
> +	if (flags > SETXATTR4_REPLACE)
> +		return nfserr_inval;
> +	setxattr->setxa_flags = flags;
> +
> +	status = nfsd4_decode_xattr_name(argp, &setxattr->setxa_name);
> +	if (status)
> +		return status;
> +
> +	maxcount = svc_max_payload(argp->rqstp);
> +	maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount);
> +
> +	READ_BUF(4);
> +	size = be32_to_cpup(p++);
> +	if (size > maxcount)
> +		return nfserr_xattr2big;
> +
> +	setxattr->setxa_len = size;
> +	if (size > 0) {
> +		status = svcxdr_construct_vector(argp, &head, &pagelist, size);
> +		if (status)
> +			return status;
> +
> +		status = nfsd4_vbuf_from_stream(argp, &head, pagelist,
> +		    &setxattr->setxa_buf, size);
> +	}

Now I'm wondering if read_bytes_from_xdr_buf() might be adequate
for this purpose, so you can avoid open-coding all of this logic.


> +
> +	DECODE_TAIL;
> +}
> +
> +static __be32
> +nfsd4_decode_listxattrs(struct nfsd4_compoundargs *argp,
> +			struct nfsd4_listxattrs *listxattrs)
> +{
> +	DECODE_HEAD;
> +	u32 maxcount;
> +
> +	READ_BUF(12);
> +	p = xdr_decode_hyper(p, &listxattrs->lsxa_cookie);
> +
> +	/*
> +	 * If the cookie  is too large to have even one user.x attribute
> +	 * plus trailing '\0' left in a maximum size buffer, it's invalid.
> +	 */
> +	if (listxattrs->lsxa_cookie >=
> +	    (XATTR_LIST_MAX / (XATTR_USER_PREFIX_LEN + 2)))
> +		return nfserr_badcookie;
> +
> +	maxcount = be32_to_cpup(p++);
> +	if (maxcount < 8)
> +		/* Always need at least 2 words (length and one character) */
> +		return nfserr_inval;
> +
> +	maxcount = min(maxcount, svc_max_payload(argp->rqstp));
> +	listxattrs->lsxa_maxcount = maxcount;
> +
> +	/*
> +	 * Unfortunately, there is no interface to only list xattrs for
> +	 * one prefix. So there is no good way to convert maxcount to
> +	 * a maximum value to pass to vfs_listxattr, as we don't know
> +	 * how many of the returned attributes will be user attributes.
> +	 *
> +	 * So, always ask vfs_listxattr for the maximum size, and encode
> +	 * as many as possible.
> +	 */
> +	listxattrs->lsxa_buf = svcxdr_tmpalloc(argp, XATTR_LIST_MAX);
> +	if (!listxattrs->lsxa_buf)
> +		status = nfserr_jukebox;
> +
> +	DECODE_TAIL;
> +}
> +
> +static __be32
> +nfsd4_decode_removexattr(struct nfsd4_compoundargs *argp,
> +			 struct nfsd4_removexattr *removexattr)
> +{
> +	return nfsd4_decode_xattr_name(argp, &removexattr->rmxa_name);
> +}
> +
> static __be32
> nfsd4_decode_noop(struct nfsd4_compoundargs *argp, void *p)
> {
> @@ -1975,6 +2193,11 @@ static const nfsd4_dec nfsd4_dec_ops[] = {
> 	[OP_SEEK]		= (nfsd4_dec)nfsd4_decode_seek,
> 	[OP_WRITE_SAME]		= (nfsd4_dec)nfsd4_decode_notsupp,
> 	[OP_CLONE]		= (nfsd4_dec)nfsd4_decode_clone,
> +	/* RFC 8276 extended atributes operations */
> +	[OP_GETXATTR]		= (nfsd4_dec)nfsd4_decode_getxattr,
> +	[OP_SETXATTR]		= (nfsd4_dec)nfsd4_decode_setxattr,
> +	[OP_LISTXATTRS]		= (nfsd4_dec)nfsd4_decode_listxattrs,
> +	[OP_REMOVEXATTR]	= (nfsd4_dec)nfsd4_decode_removexattr,
> };
> 
> static inline bool
> @@ -4458,6 +4681,225 @@ nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p)
> 	return nfserr;
> }
> 
> +/*
> + * Encode kmalloc-ed buffer in to XDR stream.
> + */
> +static int
> +nfsd4_vbuf_to_stream(struct xdr_stream *xdr, char *buf, u32 buflen)
> +{
> +	u32 cplen;
> +	__be32 *p;
> +
> +	cplen = min_t(unsigned long, buflen,
> +		      ((void *)xdr->end - (void *)xdr->p));
> +	p = xdr_reserve_space(xdr, cplen);
> +	if (!p)
> +		return nfserr_resource;
> +
> +	memcpy(p, buf, cplen);
> +	buf += cplen;
> +	buflen -= cplen;
> +
> +	while (buflen) {
> +		cplen = min_t(u32, buflen, PAGE_SIZE);
> +		p = xdr_reserve_space(xdr, cplen);
> +		if (!p)
> +			return nfserr_resource;
> +
> +		memcpy(p, buf, cplen);
> +
> +		if (cplen < PAGE_SIZE) {
> +			/*
> +			 * We're done, with a length that wasn't page
> +			 * aligned, so possibly not word aligned. Pad
> +			 * any trailing bytes with 0.
> +			 */
> +			xdr_encode_opaque_fixed(p, NULL, cplen);
> +			break;
> +		}
> +
> +		buflen -= PAGE_SIZE;
> +		buf += PAGE_SIZE;
> +	}
> +
> +	return 0;
> +}
> +
> +static __be32
> +nfsd4_encode_getxattr(struct nfsd4_compoundres *resp, __be32 nfserr,
> +		      struct nfsd4_getxattr *getxattr)
> +{
> +	struct xdr_stream *xdr = &resp->xdr;
> +	__be32 *p;
> +
> +	p = xdr_reserve_space(xdr, 4);
> +	if (!p)
> +		return nfserr_resource;
> +
> +	*p = cpu_to_be32(getxattr->getxa_len);
> +
> +	if (getxattr->getxa_len == 0)
> +		return 0;
> +
> +	return nfsd4_vbuf_to_stream(xdr, getxattr->getxa_buf,
> +				    getxattr->getxa_len);
> +}
> +
> +static __be32
> +nfsd4_encode_setxattr(struct nfsd4_compoundres *resp, __be32 nfserr,
> +		      struct nfsd4_setxattr *setxattr)
> +{
> +	struct xdr_stream *xdr = &resp->xdr;
> +	__be32 *p;
> +
> +	p = xdr_reserve_space(xdr, 20);
> +	if (!p)
> +		return nfserr_resource;
> +
> +	encode_cinfo(p, &setxattr->setxa_cinfo);
> +
> +	return 0;
> +}
> +
> +/*
> + * See if there are cookie values that can be rejected outright.
> + */
> +static int
> +nfsd4_listxattr_validate_cookie(struct nfsd4_listxattrs *listxattrs,
> +				u32 *offsetp)
> +{
> +	u64 cookie = listxattrs->lsxa_cookie;
> +
> +	/*
> +	 * If the cookie is larger than the maximum number we can fit
> +	 * in either the buffer we just got back from vfs_listxattr, or,
> +	 * XDR-encoded, in the return buffer, it's invalid.
> +	 */
> +	if (cookie > (listxattrs->lsxa_len) / (XATTR_USER_PREFIX_LEN + 2))
> +		return nfserr_badcookie;
> +
> +	if (cookie > (listxattrs->lsxa_maxcount /
> +		      (XDR_QUADLEN(XATTR_USER_PREFIX_LEN + 2) + 4)))
> +		return nfserr_badcookie;
> +
> +	*offsetp = (u32)cookie;
> +	return 0;
> +}
> +
> +static __be32
> +nfsd4_encode_listxattrs(struct nfsd4_compoundres *resp, __be32 nfserr,
> +			struct nfsd4_listxattrs *listxattrs)
> +{
> +	struct xdr_stream *xdr = &resp->xdr;
> +	u32 cookie_offset, count_offset, eof;
> +	u32 left, xdrleft, slen, count;
> +	u32 xdrlen, offset;
> +	u64 cookie;
> +	char *sp;
> +	int status;
> +	__be32 *p;
> +	u32 nuser;
> +
> +	eof = 1;
> +
> +	status = nfsd4_listxattr_validate_cookie(listxattrs, &offset);
> +	if (status)
> +		return status;
> +
> +	/*
> +	 * Reserve space for the cookie and the name array count. Record
> +	 * the offsets to save them later.
> +	 */
> +	cookie_offset = xdr->buf->len;
> +	count_offset = cookie_offset + 8;
> +	p = xdr_reserve_space(xdr, 12);
> +	if (!p)
> +		return nfserr_resource;
> +
> +	count = 0;
> +	left = listxattrs->lsxa_len;
> +	sp = listxattrs->lsxa_buf;
> +	nuser = 0;
> +
> +	xdrleft = listxattrs->lsxa_maxcount;
> +
> +	while (left > 0 && xdrleft > 0) {
> +		slen = strlen(sp);
> +
> +		/*
> +		 * Check if this a user. attribute, skip it if not.
> +		 */
> +		if (strncmp(sp, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
> +			goto contloop;
> +
> +		slen -= XATTR_USER_PREFIX_LEN;
> +		xdrlen = 4 + ((slen + 3) & ~3);
> +		if (xdrlen > xdrleft) {
> +			if (count == 0) {
> +				/*
> +				 * Can't even fit the first attribute name.
> +				 */
> +				return nfserr_toosmall;
> +			}
> +			eof = 0;
> +			goto wreof;
> +		}
> +
> +		left -= XATTR_USER_PREFIX_LEN;
> +		sp += XATTR_USER_PREFIX_LEN;
> +		if (nuser++ < offset)
> +			goto contloop;
> +
> +
> +		p = xdr_reserve_space(xdr, xdrlen);
> +		if (!p)
> +			return nfserr_resource;
> +
> +		p = xdr_encode_opaque(p, sp, slen);
> +
> +		xdrleft -= xdrlen;
> +		count++;
> +contloop:
> +		sp += slen + 1;
> +		left -= slen + 1;
> +	}
> +
> +	/*
> +	 * If there were user attributes to copy, but we didn't copy
> +	 * any, the offset was too large (e.g. the cookie was invalid).
> +	 */
> +	if (nuser > 0 && count == 0)
> +		return nfserr_badcookie;
> +
> +wreof:
> +	p = xdr_reserve_space(xdr, 4);
> +	if (!p)
> +		return nfserr_resource;
> +	*p = cpu_to_be32(eof);
> +
> +	cookie = offset + count;
> +
> +	write_bytes_to_xdr_buf(xdr->buf, cookie_offset, &cookie, 8);
> +	count = htonl(count);
> +	write_bytes_to_xdr_buf(xdr->buf, count_offset, &count, 4);
> +	return 0;
> +}
> +
> +static __be32
> +nfsd4_encode_removexattr(struct nfsd4_compoundres *resp, __be32 nfserr,
> +			 struct nfsd4_removexattr *removexattr)
> +{
> +	struct xdr_stream *xdr = &resp->xdr;
> +	__be32 *p;
> +
> +	p = xdr_reserve_space(xdr, 20);
> +	if (!p)
> +		return nfserr_resource;
> +
> +	p = encode_cinfo(p, &removexattr->rmxa_cinfo);
> +	return 0;
> +}
> +
> typedef __be32(* nfsd4_enc)(struct nfsd4_compoundres *, __be32, void *);
> 
> /*
> @@ -4547,6 +4989,12 @@ static const nfsd4_enc nfsd4_enc_ops[] = {
> 	[OP_SEEK]		= (nfsd4_enc)nfsd4_encode_seek,
> 	[OP_WRITE_SAME]		= (nfsd4_enc)nfsd4_encode_noop,
> 	[OP_CLONE]		= (nfsd4_enc)nfsd4_encode_noop,
> +
> +	/* RFC 8276 extended atributes operations */
> +	[OP_GETXATTR]		= (nfsd4_enc)nfsd4_encode_getxattr,
> +	[OP_SETXATTR]		= (nfsd4_enc)nfsd4_encode_setxattr,
> +	[OP_LISTXATTRS]		= (nfsd4_enc)nfsd4_encode_listxattrs,
> +	[OP_REMOVEXATTR]	= (nfsd4_enc)nfsd4_encode_removexattr,
> };
> 
> /*
> -- 
> 2.16.6
> 

--
Chuck Lever




  parent reply	other threads:[~2020-03-12 19:16 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-11 19:59 [PATCH 00/14] server side user xattr support (RFC 8276) Frank van der Linden
2020-03-11 19:59 ` [PATCH 01/14] nfs,nfsd: NFSv4.2 extended attribute protocol definitions Frank van der Linden
2020-03-11 19:59 ` [PATCH 02/14] xattr: modify vfs_{set,remove}xattr for NFS server use Frank van der Linden
2020-03-12 16:23   ` Chuck Lever
2020-03-13 15:35   ` J. Bruce Fields
2020-03-13 16:07     ` [PATCH 02/14] xattr: modify vfs_{set, remove}xattr " Frank van der Linden
2020-03-13 21:06       ` J. Bruce Fields
2020-03-11 19:59 ` [PATCH 03/14] nfsd: split off the write decode code in to a separate function Frank van der Linden
2020-03-11 19:59 ` [PATCH 04/14] nfsd: make sure the nfsd4_ops array has the right size Frank van der Linden
2020-03-11 19:59 ` [PATCH 05/14] nfsd: add defines for NFSv4.2 extended attribute support Frank van der Linden
2020-03-12 16:23   ` Chuck Lever
2020-03-11 19:59 ` [PATCH 06/14] nfsd: define xattr functions to call in to their vfs counterparts Frank van der Linden
2020-03-12  7:37   ` kbuild test robot
2020-03-12 16:23   ` Chuck Lever
2020-03-12 17:16     ` Frank van der Linden
2020-03-12 17:57       ` Chuck Lever
2020-03-11 19:59 ` [PATCH 07/14] nfsd: take xattr bits in to account for permission checks Frank van der Linden
2020-03-11 19:59 ` [PATCH 08/14] nfsd: add structure definitions for xattr requests / responses Frank van der Linden
2020-03-11 19:59 ` [PATCH 09/14] nfsd: use kvmalloc in svcxdr_tmpalloc Frank van der Linden
2020-03-11 19:59 ` [PATCH 10/14] nfsd: implement the xattr procedure functions Frank van der Linden
2020-03-12 10:28   ` kbuild test robot
2020-03-12 16:24   ` Chuck Lever
2020-03-11 19:59 ` [PATCH 11/14] nfsd: add user xattr RPC XDR encoding/decoding logic Frank van der Linden
2020-03-12 13:28   ` kbuild test robot
2020-03-12 16:24   ` Chuck Lever
2020-03-19 22:13     ` Frank van der Linden
2020-03-19 22:15       ` Chuck Lever
2020-03-25 23:44     ` Frank van der Linden
2020-03-26 14:12       ` Chuck Lever
2020-03-12 19:16   ` Chuck Lever [this message]
2020-03-20 16:47     ` Frank van der Linden
2020-03-20 17:34       ` Chuck Lever
2020-03-20 17:54         ` J. Bruce Fields
2020-03-20 19:44         ` Frank van der Linden
2020-03-11 19:59 ` [PATCH 12/14] nfsd: add xattr operations to ops array Frank van der Linden
2020-03-11 19:59 ` [PATCH 13/14] xattr: add a function to check if a namespace is supported Frank van der Linden
2020-03-12 16:24   ` Chuck Lever
2020-03-11 19:59 ` [PATCH 14/14] nfsd: add fattr support for user extended attributes Frank van der Linden

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=6955728A-CFCC-40FC-9E02-671255EDD45F@oracle.com \
    --to=chuck.lever@oracle.com \
    --cc=bfields@fieldses.org \
    --cc=fllinden@amazon.com \
    --cc=linux-nfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox