linux/fs/nfs/nfs42xdr.c
Frank van der Linden 3e1f02123f NFSv4.2: add client side XDR handling for extended attributes
Define the argument and response structures that will be used for
RFC 8276 extended attribute RPC calls, and implement the necessary
functions to encode/decode the extended attribute operations.

Signed-off-by: Frank van der Linden <fllinden@amazon.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
2020-07-13 17:52:45 -04:00

1430 lines
36 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014 Anna Schumaker <Anna.Schumaker@Netapp.com>
*/
#ifndef __LINUX_FS_NFS_NFS4_2XDR_H
#define __LINUX_FS_NFS_NFS4_2XDR_H
#include "nfs42.h"
#define encode_fallocate_maxsz (encode_stateid_maxsz + \
2 /* offset */ + \
2 /* length */)
#define NFS42_WRITE_RES_SIZE (1 /* wr_callback_id size */ +\
XDR_QUADLEN(NFS4_STATEID_SIZE) + \
2 /* wr_count */ + \
1 /* wr_committed */ + \
XDR_QUADLEN(NFS4_VERIFIER_SIZE))
#define encode_allocate_maxsz (op_encode_hdr_maxsz + \
encode_fallocate_maxsz)
#define decode_allocate_maxsz (op_decode_hdr_maxsz)
#define encode_copy_maxsz (op_encode_hdr_maxsz + \
XDR_QUADLEN(NFS4_STATEID_SIZE) + \
XDR_QUADLEN(NFS4_STATEID_SIZE) + \
2 + 2 + 2 + 1 + 1 + 1 +\
1 + /* One cnr_source_server */\
1 + /* nl4_type */ \
1 + XDR_QUADLEN(NFS4_OPAQUE_LIMIT))
#define decode_copy_maxsz (op_decode_hdr_maxsz + \
NFS42_WRITE_RES_SIZE + \
1 /* cr_consecutive */ + \
1 /* cr_synchronous */)
#define encode_offload_cancel_maxsz (op_encode_hdr_maxsz + \
XDR_QUADLEN(NFS4_STATEID_SIZE))
#define decode_offload_cancel_maxsz (op_decode_hdr_maxsz)
#define encode_copy_notify_maxsz (op_encode_hdr_maxsz + \
XDR_QUADLEN(NFS4_STATEID_SIZE) + \
1 + /* nl4_type */ \
1 + XDR_QUADLEN(NFS4_OPAQUE_LIMIT))
#define decode_copy_notify_maxsz (op_decode_hdr_maxsz + \
3 + /* cnr_lease_time */\
XDR_QUADLEN(NFS4_STATEID_SIZE) + \
1 + /* Support 1 cnr_source_server */\
1 + /* nl4_type */ \
1 + XDR_QUADLEN(NFS4_OPAQUE_LIMIT))
#define encode_deallocate_maxsz (op_encode_hdr_maxsz + \
encode_fallocate_maxsz)
#define decode_deallocate_maxsz (op_decode_hdr_maxsz)
#define encode_seek_maxsz (op_encode_hdr_maxsz + \
encode_stateid_maxsz + \
2 /* offset */ + \
1 /* whence */)
#define decode_seek_maxsz (op_decode_hdr_maxsz + \
1 /* eof */ + \
1 /* whence */ + \
2 /* offset */ + \
2 /* length */)
#define encode_io_info_maxsz 4
#define encode_layoutstats_maxsz (op_decode_hdr_maxsz + \
2 /* offset */ + \
2 /* length */ + \
encode_stateid_maxsz + \
encode_io_info_maxsz + \
encode_io_info_maxsz + \
1 /* opaque devaddr4 length */ + \
XDR_QUADLEN(PNFS_LAYOUTSTATS_MAXSIZE))
#define decode_layoutstats_maxsz (op_decode_hdr_maxsz)
#define encode_device_error_maxsz (XDR_QUADLEN(NFS4_DEVICEID4_SIZE) + \
1 /* status */ + 1 /* opnum */)
#define encode_layouterror_maxsz (op_decode_hdr_maxsz + \
2 /* offset */ + \
2 /* length */ + \
encode_stateid_maxsz + \
1 /* Array size */ + \
encode_device_error_maxsz)
#define decode_layouterror_maxsz (op_decode_hdr_maxsz)
#define encode_clone_maxsz (encode_stateid_maxsz + \
encode_stateid_maxsz + \
2 /* src offset */ + \
2 /* dst offset */ + \
2 /* count */)
#define decode_clone_maxsz (op_decode_hdr_maxsz)
#define NFS4_enc_allocate_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
encode_allocate_maxsz + \
encode_getattr_maxsz)
#define NFS4_dec_allocate_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
decode_allocate_maxsz + \
decode_getattr_maxsz)
#define NFS4_enc_copy_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
encode_savefh_maxsz + \
encode_putfh_maxsz + \
encode_copy_maxsz + \
encode_commit_maxsz)
#define NFS4_dec_copy_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
decode_savefh_maxsz + \
decode_putfh_maxsz + \
decode_copy_maxsz + \
decode_commit_maxsz)
#define NFS4_enc_offload_cancel_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
encode_offload_cancel_maxsz)
#define NFS4_dec_offload_cancel_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
decode_offload_cancel_maxsz)
#define NFS4_enc_copy_notify_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
encode_copy_notify_maxsz)
#define NFS4_dec_copy_notify_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
decode_copy_notify_maxsz)
#define NFS4_enc_deallocate_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
encode_deallocate_maxsz + \
encode_getattr_maxsz)
#define NFS4_dec_deallocate_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
decode_deallocate_maxsz + \
decode_getattr_maxsz)
#define NFS4_enc_seek_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
encode_seek_maxsz)
#define NFS4_dec_seek_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
decode_seek_maxsz)
#define NFS4_enc_layoutstats_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
PNFS_LAYOUTSTATS_MAXDEV * encode_layoutstats_maxsz)
#define NFS4_dec_layoutstats_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
PNFS_LAYOUTSTATS_MAXDEV * decode_layoutstats_maxsz)
#define NFS4_enc_layouterror_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
NFS42_LAYOUTERROR_MAX * \
encode_layouterror_maxsz)
#define NFS4_dec_layouterror_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
NFS42_LAYOUTERROR_MAX * \
decode_layouterror_maxsz)
#define NFS4_enc_clone_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
encode_savefh_maxsz + \
encode_putfh_maxsz + \
encode_clone_maxsz + \
encode_getattr_maxsz)
#define NFS4_dec_clone_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
decode_savefh_maxsz + \
decode_putfh_maxsz + \
decode_clone_maxsz + \
decode_getattr_maxsz)
/* Not limited by NFS itself, limited by the generic xattr code */
#define nfs4_xattr_name_maxsz XDR_QUADLEN(XATTR_NAME_MAX)
#define encode_getxattr_maxsz (op_encode_hdr_maxsz + 1 + \
nfs4_xattr_name_maxsz)
#define decode_getxattr_maxsz (op_decode_hdr_maxsz + 1 + 1)
#define encode_setxattr_maxsz (op_encode_hdr_maxsz + \
1 + nfs4_xattr_name_maxsz + 1)
#define decode_setxattr_maxsz (op_decode_hdr_maxsz + decode_change_info_maxsz)
#define encode_listxattrs_maxsz (op_encode_hdr_maxsz + 2 + 1)
#define decode_listxattrs_maxsz (op_decode_hdr_maxsz + 2 + 1 + 1)
#define encode_removexattr_maxsz (op_encode_hdr_maxsz + 1 + \
nfs4_xattr_name_maxsz)
#define decode_removexattr_maxsz (op_decode_hdr_maxsz + \
decode_change_info_maxsz)
#define NFS4_enc_getxattr_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
encode_getxattr_maxsz)
#define NFS4_dec_getxattr_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
decode_getxattr_maxsz)
#define NFS4_enc_setxattr_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
encode_setxattr_maxsz)
#define NFS4_dec_setxattr_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
decode_setxattr_maxsz)
#define NFS4_enc_listxattrs_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
encode_listxattrs_maxsz)
#define NFS4_dec_listxattrs_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
decode_listxattrs_maxsz)
#define NFS4_enc_removexattr_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
encode_removexattr_maxsz)
#define NFS4_dec_removexattr_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
decode_removexattr_maxsz)
/*
* These values specify the maximum amount of data that is not
* associated with the extended attribute name or extended
* attribute list in the SETXATTR, GETXATTR and LISTXATTR
* respectively.
*/
const u32 nfs42_maxsetxattr_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
compound_encode_hdr_maxsz +
encode_sequence_maxsz +
encode_putfh_maxsz + 1 +
nfs4_xattr_name_maxsz)
* XDR_UNIT);
const u32 nfs42_maxgetxattr_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
compound_decode_hdr_maxsz +
decode_sequence_maxsz +
decode_putfh_maxsz + 1) * XDR_UNIT);
const u32 nfs42_maxlistxattrs_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
compound_decode_hdr_maxsz +
decode_sequence_maxsz +
decode_putfh_maxsz + 3) * XDR_UNIT);
static void encode_fallocate(struct xdr_stream *xdr,
const struct nfs42_falloc_args *args)
{
encode_nfs4_stateid(xdr, &args->falloc_stateid);
encode_uint64(xdr, args->falloc_offset);
encode_uint64(xdr, args->falloc_length);
}
static void encode_allocate(struct xdr_stream *xdr,
const struct nfs42_falloc_args *args,
struct compound_hdr *hdr)
{
encode_op_hdr(xdr, OP_ALLOCATE, decode_allocate_maxsz, hdr);
encode_fallocate(xdr, args);
}
static void encode_nl4_server(struct xdr_stream *xdr,
const struct nl4_server *ns)
{
encode_uint32(xdr, ns->nl4_type);
switch (ns->nl4_type) {
case NL4_NAME:
case NL4_URL:
encode_string(xdr, ns->u.nl4_str_sz, ns->u.nl4_str);
break;
case NL4_NETADDR:
encode_string(xdr, ns->u.nl4_addr.netid_len,
ns->u.nl4_addr.netid);
encode_string(xdr, ns->u.nl4_addr.addr_len,
ns->u.nl4_addr.addr);
break;
default:
WARN_ON_ONCE(1);
}
}
static void encode_copy(struct xdr_stream *xdr,
const struct nfs42_copy_args *args,
struct compound_hdr *hdr)
{
encode_op_hdr(xdr, OP_COPY, decode_copy_maxsz, hdr);
encode_nfs4_stateid(xdr, &args->src_stateid);
encode_nfs4_stateid(xdr, &args->dst_stateid);
encode_uint64(xdr, args->src_pos);
encode_uint64(xdr, args->dst_pos);
encode_uint64(xdr, args->count);
encode_uint32(xdr, 1); /* consecutive = true */
encode_uint32(xdr, args->sync);
if (args->cp_src == NULL) { /* intra-ssc */
encode_uint32(xdr, 0); /* no src server list */
return;
}
encode_uint32(xdr, 1); /* supporting 1 server */
encode_nl4_server(xdr, args->cp_src);
}
static void encode_offload_cancel(struct xdr_stream *xdr,
const struct nfs42_offload_status_args *args,
struct compound_hdr *hdr)
{
encode_op_hdr(xdr, OP_OFFLOAD_CANCEL, decode_offload_cancel_maxsz, hdr);
encode_nfs4_stateid(xdr, &args->osa_stateid);
}
static void encode_copy_notify(struct xdr_stream *xdr,
const struct nfs42_copy_notify_args *args,
struct compound_hdr *hdr)
{
encode_op_hdr(xdr, OP_COPY_NOTIFY, decode_copy_notify_maxsz, hdr);
encode_nfs4_stateid(xdr, &args->cna_src_stateid);
encode_nl4_server(xdr, &args->cna_dst);
}
static void encode_deallocate(struct xdr_stream *xdr,
const struct nfs42_falloc_args *args,
struct compound_hdr *hdr)
{
encode_op_hdr(xdr, OP_DEALLOCATE, decode_deallocate_maxsz, hdr);
encode_fallocate(xdr, args);
}
static void encode_seek(struct xdr_stream *xdr,
const struct nfs42_seek_args *args,
struct compound_hdr *hdr)
{
encode_op_hdr(xdr, OP_SEEK, decode_seek_maxsz, hdr);
encode_nfs4_stateid(xdr, &args->sa_stateid);
encode_uint64(xdr, args->sa_offset);
encode_uint32(xdr, args->sa_what);
}
static void encode_layoutstats(struct xdr_stream *xdr,
const struct nfs42_layoutstat_args *args,
struct nfs42_layoutstat_devinfo *devinfo,
struct compound_hdr *hdr)
{
__be32 *p;
encode_op_hdr(xdr, OP_LAYOUTSTATS, decode_layoutstats_maxsz, hdr);
p = reserve_space(xdr, 8 + 8);
p = xdr_encode_hyper(p, devinfo->offset);
p = xdr_encode_hyper(p, devinfo->length);
encode_nfs4_stateid(xdr, &args->stateid);
p = reserve_space(xdr, 4*8 + NFS4_DEVICEID4_SIZE + 4);
p = xdr_encode_hyper(p, devinfo->read_count);
p = xdr_encode_hyper(p, devinfo->read_bytes);
p = xdr_encode_hyper(p, devinfo->write_count);
p = xdr_encode_hyper(p, devinfo->write_bytes);
p = xdr_encode_opaque_fixed(p, devinfo->dev_id.data,
NFS4_DEVICEID4_SIZE);
/* Encode layoutupdate4 */
*p++ = cpu_to_be32(devinfo->layout_type);
if (devinfo->ld_private.ops)
devinfo->ld_private.ops->encode(xdr, args,
&devinfo->ld_private);
else
encode_uint32(xdr, 0);
}
static void encode_clone(struct xdr_stream *xdr,
const struct nfs42_clone_args *args,
struct compound_hdr *hdr)
{
__be32 *p;
encode_op_hdr(xdr, OP_CLONE, decode_clone_maxsz, hdr);
encode_nfs4_stateid(xdr, &args->src_stateid);
encode_nfs4_stateid(xdr, &args->dst_stateid);
p = reserve_space(xdr, 3*8);
p = xdr_encode_hyper(p, args->src_offset);
p = xdr_encode_hyper(p, args->dst_offset);
xdr_encode_hyper(p, args->count);
}
static void encode_device_error(struct xdr_stream *xdr,
const struct nfs42_device_error *error)
{
__be32 *p;
p = reserve_space(xdr, NFS4_DEVICEID4_SIZE + 2*4);
p = xdr_encode_opaque_fixed(p, error->dev_id.data,
NFS4_DEVICEID4_SIZE);
*p++ = cpu_to_be32(error->status);
*p = cpu_to_be32(error->opnum);
}
static void encode_layouterror(struct xdr_stream *xdr,
const struct nfs42_layout_error *args,
struct compound_hdr *hdr)
{
__be32 *p;
encode_op_hdr(xdr, OP_LAYOUTERROR, decode_layouterror_maxsz, hdr);
p = reserve_space(xdr, 8 + 8);
p = xdr_encode_hyper(p, args->offset);
p = xdr_encode_hyper(p, args->length);
encode_nfs4_stateid(xdr, &args->stateid);
p = reserve_space(xdr, 4);
*p = cpu_to_be32(1);
encode_device_error(xdr, &args->errors[0]);
}
static void encode_setxattr(struct xdr_stream *xdr,
const struct nfs42_setxattrargs *arg,
struct compound_hdr *hdr)
{
__be32 *p;
BUILD_BUG_ON(XATTR_CREATE != SETXATTR4_CREATE);
BUILD_BUG_ON(XATTR_REPLACE != SETXATTR4_REPLACE);
encode_op_hdr(xdr, OP_SETXATTR, decode_setxattr_maxsz, hdr);
p = reserve_space(xdr, 4);
*p = cpu_to_be32(arg->xattr_flags);
encode_string(xdr, strlen(arg->xattr_name), arg->xattr_name);
p = reserve_space(xdr, 4);
*p = cpu_to_be32(arg->xattr_len);
if (arg->xattr_len)
xdr_write_pages(xdr, arg->xattr_pages, 0, arg->xattr_len);
}
static int decode_setxattr(struct xdr_stream *xdr,
struct nfs4_change_info *cinfo)
{
int status;
status = decode_op_hdr(xdr, OP_SETXATTR);
if (status)
goto out;
status = decode_change_info(xdr, cinfo);
out:
return status;
}
static void encode_getxattr(struct xdr_stream *xdr, const char *name,
struct compound_hdr *hdr)
{
encode_op_hdr(xdr, OP_GETXATTR, decode_getxattr_maxsz, hdr);
encode_string(xdr, strlen(name), name);
}
static int decode_getxattr(struct xdr_stream *xdr,
struct nfs42_getxattrres *res,
struct rpc_rqst *req)
{
int status;
__be32 *p;
u32 len, rdlen;
status = decode_op_hdr(xdr, OP_GETXATTR);
if (status)
return status;
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
return -EIO;
len = be32_to_cpup(p);
if (len > req->rq_rcv_buf.page_len)
return -ERANGE;
res->xattr_len = len;
if (len > 0) {
rdlen = xdr_read_pages(xdr, len);
if (rdlen < len)
return -EIO;
}
return 0;
}
static void encode_removexattr(struct xdr_stream *xdr, const char *name,
struct compound_hdr *hdr)
{
encode_op_hdr(xdr, OP_REMOVEXATTR, decode_removexattr_maxsz, hdr);
encode_string(xdr, strlen(name), name);
}
static int decode_removexattr(struct xdr_stream *xdr,
struct nfs4_change_info *cinfo)
{
int status;
status = decode_op_hdr(xdr, OP_REMOVEXATTR);
if (status)
goto out;
status = decode_change_info(xdr, cinfo);
out:
return status;
}
static void encode_listxattrs(struct xdr_stream *xdr,
const struct nfs42_listxattrsargs *arg,
struct compound_hdr *hdr)
{
__be32 *p;
encode_op_hdr(xdr, OP_LISTXATTRS, decode_listxattrs_maxsz + 1, hdr);
p = reserve_space(xdr, 12);
if (unlikely(!p))
return;
p = xdr_encode_hyper(p, arg->cookie);
/*
* RFC 8276 says to specify the full max length of the LISTXATTRS
* XDR reply. Count is set to the XDR length of the names array
* plus the EOF marker. So, add the cookie and the names count.
*/
*p = cpu_to_be32(arg->count + 8 + 4);
}
static int decode_listxattrs(struct xdr_stream *xdr,
struct nfs42_listxattrsres *res)
{
int status;
__be32 *p;
u32 count, len, ulen;
size_t left, copied;
char *buf;
status = decode_op_hdr(xdr, OP_LISTXATTRS);
if (status) {
/*
* Special case: for LISTXATTRS, NFS4ERR_TOOSMALL
* should be translated to ERANGE.
*/
if (status == -ETOOSMALL)
status = -ERANGE;
goto out;
}
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
return -EIO;
xdr_decode_hyper(p, &res->cookie);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
return -EIO;
left = res->xattr_len;
buf = res->xattr_buf;
count = be32_to_cpup(p);
copied = 0;
/*
* We have asked for enough room to encode the maximum number
* of possible attribute names, so everything should fit.
*
* But, don't rely on that assumption. Just decode entries
* until they don't fit anymore, just in case the server did
* something odd.
*/
while (count--) {
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
return -EIO;
len = be32_to_cpup(p);
if (len > (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)) {
status = -ERANGE;
goto out;
}
p = xdr_inline_decode(xdr, len);
if (unlikely(!p))
return -EIO;
ulen = len + XATTR_USER_PREFIX_LEN + 1;
if (buf) {
if (ulen > left) {
status = -ERANGE;
goto out;
}
memcpy(buf, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
memcpy(buf + XATTR_USER_PREFIX_LEN, p, len);
buf[ulen - 1] = 0;
buf += ulen;
left -= ulen;
}
copied += ulen;
}
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
return -EIO;
res->eof = be32_to_cpup(p);
res->copied = copied;
out:
if (status == -ERANGE && res->xattr_len == XATTR_LIST_MAX)
status = -E2BIG;
return status;
}
/*
* Encode ALLOCATE request
*/
static void nfs4_xdr_enc_allocate(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct nfs42_falloc_args *args = data;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->falloc_fh, &hdr);
encode_allocate(xdr, args, &hdr);
encode_getfattr(xdr, args->falloc_bitmask, &hdr);
encode_nops(&hdr);
}
static void encode_copy_commit(struct xdr_stream *xdr,
const struct nfs42_copy_args *args,
struct compound_hdr *hdr)
{
__be32 *p;
encode_op_hdr(xdr, OP_COMMIT, decode_commit_maxsz, hdr);
p = reserve_space(xdr, 12);
p = xdr_encode_hyper(p, args->dst_pos);
*p = cpu_to_be32(args->count);
}
/*
* Encode COPY request
*/
static void nfs4_xdr_enc_copy(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct nfs42_copy_args *args = data;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->src_fh, &hdr);
encode_savefh(xdr, &hdr);
encode_putfh(xdr, args->dst_fh, &hdr);
encode_copy(xdr, args, &hdr);
if (args->sync)
encode_copy_commit(xdr, args, &hdr);
encode_nops(&hdr);
}
/*
* Encode OFFLOAD_CANEL request
*/
static void nfs4_xdr_enc_offload_cancel(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct nfs42_offload_status_args *args = data;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->osa_seq_args),
};
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->osa_seq_args, &hdr);
encode_putfh(xdr, args->osa_src_fh, &hdr);
encode_offload_cancel(xdr, args, &hdr);
encode_nops(&hdr);
}
/*
* Encode COPY_NOTIFY request
*/
static void nfs4_xdr_enc_copy_notify(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct nfs42_copy_notify_args *args = data;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->cna_seq_args),
};
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->cna_seq_args, &hdr);
encode_putfh(xdr, args->cna_src_fh, &hdr);
encode_copy_notify(xdr, args, &hdr);
encode_nops(&hdr);
}
/*
* Encode DEALLOCATE request
*/
static void nfs4_xdr_enc_deallocate(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct nfs42_falloc_args *args = data;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->falloc_fh, &hdr);
encode_deallocate(xdr, args, &hdr);
encode_getfattr(xdr, args->falloc_bitmask, &hdr);
encode_nops(&hdr);
}
/*
* Encode SEEK request
*/
static void nfs4_xdr_enc_seek(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct nfs42_seek_args *args = data;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->sa_fh, &hdr);
encode_seek(xdr, args, &hdr);
encode_nops(&hdr);
}
/*
* Encode LAYOUTSTATS request
*/
static void nfs4_xdr_enc_layoutstats(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct nfs42_layoutstat_args *args = data;
int i;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr);
WARN_ON(args->num_dev > PNFS_LAYOUTSTATS_MAXDEV);
for (i = 0; i < args->num_dev; i++)
encode_layoutstats(xdr, args, &args->devinfo[i], &hdr);
encode_nops(&hdr);
}
/*
* Encode CLONE request
*/
static void nfs4_xdr_enc_clone(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct nfs42_clone_args *args = data;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->src_fh, &hdr);
encode_savefh(xdr, &hdr);
encode_putfh(xdr, args->dst_fh, &hdr);
encode_clone(xdr, args, &hdr);
encode_getfattr(xdr, args->dst_bitmask, &hdr);
encode_nops(&hdr);
}
/*
* Encode LAYOUTERROR request
*/
static void nfs4_xdr_enc_layouterror(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct nfs42_layouterror_args *args = data;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
int i;
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, NFS_FH(args->inode), &hdr);
for (i = 0; i < args->num_errors; i++)
encode_layouterror(xdr, &args->errors[i], &hdr);
encode_nops(&hdr);
}
static int decode_allocate(struct xdr_stream *xdr, struct nfs42_falloc_res *res)
{
return decode_op_hdr(xdr, OP_ALLOCATE);
}
static int decode_write_response(struct xdr_stream *xdr,
struct nfs42_write_res *res)
{
__be32 *p;
int status, count;
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
return -EIO;
count = be32_to_cpup(p);
if (count > 1)
return -EREMOTEIO;
else if (count == 1) {
status = decode_opaque_fixed(xdr, &res->stateid,
NFS4_STATEID_SIZE);
if (unlikely(status))
return -EIO;
}
p = xdr_inline_decode(xdr, 8 + 4);
if (unlikely(!p))
return -EIO;
p = xdr_decode_hyper(p, &res->count);
res->verifier.committed = be32_to_cpup(p);
return decode_verifier(xdr, &res->verifier.verifier);
}
static int decode_nl4_server(struct xdr_stream *xdr, struct nl4_server *ns)
{
struct nfs42_netaddr *naddr;
uint32_t dummy;
char *dummy_str;
__be32 *p;
int status;
/* nl_type */
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
return -EIO;
ns->nl4_type = be32_to_cpup(p);
switch (ns->nl4_type) {
case NL4_NAME:
case NL4_URL:
status = decode_opaque_inline(xdr, &dummy, &dummy_str);
if (unlikely(status))
return status;
if (unlikely(dummy > NFS4_OPAQUE_LIMIT))
return -EIO;
memcpy(&ns->u.nl4_str, dummy_str, dummy);
ns->u.nl4_str_sz = dummy;
break;
case NL4_NETADDR:
naddr = &ns->u.nl4_addr;
/* netid string */
status = decode_opaque_inline(xdr, &dummy, &dummy_str);
if (unlikely(status))
return status;
if (unlikely(dummy > RPCBIND_MAXNETIDLEN))
return -EIO;
naddr->netid_len = dummy;
memcpy(naddr->netid, dummy_str, naddr->netid_len);
/* uaddr string */
status = decode_opaque_inline(xdr, &dummy, &dummy_str);
if (unlikely(status))
return status;
if (unlikely(dummy > RPCBIND_MAXUADDRLEN))
return -EIO;
naddr->addr_len = dummy;
memcpy(naddr->addr, dummy_str, naddr->addr_len);
break;
default:
WARN_ON_ONCE(1);
return -EIO;
}
return 0;
}
static int decode_copy_requirements(struct xdr_stream *xdr,
struct nfs42_copy_res *res) {
__be32 *p;
p = xdr_inline_decode(xdr, 4 + 4);
if (unlikely(!p))
return -EIO;
res->consecutive = be32_to_cpup(p++);
res->synchronous = be32_to_cpup(p++);
return 0;
}
static int decode_copy(struct xdr_stream *xdr, struct nfs42_copy_res *res)
{
int status;
status = decode_op_hdr(xdr, OP_COPY);
if (status == NFS4ERR_OFFLOAD_NO_REQS) {
status = decode_copy_requirements(xdr, res);
if (status)
return status;
return NFS4ERR_OFFLOAD_NO_REQS;
} else if (status)
return status;
status = decode_write_response(xdr, &res->write_res);
if (status)
return status;
return decode_copy_requirements(xdr, res);
}
static int decode_offload_cancel(struct xdr_stream *xdr,
struct nfs42_offload_status_res *res)
{
return decode_op_hdr(xdr, OP_OFFLOAD_CANCEL);
}
static int decode_copy_notify(struct xdr_stream *xdr,
struct nfs42_copy_notify_res *res)
{
__be32 *p;
int status, count;
status = decode_op_hdr(xdr, OP_COPY_NOTIFY);
if (status)
return status;
/* cnr_lease_time */
p = xdr_inline_decode(xdr, 12);
if (unlikely(!p))
return -EIO;
p = xdr_decode_hyper(p, &res->cnr_lease_time.seconds);
res->cnr_lease_time.nseconds = be32_to_cpup(p);
status = decode_opaque_fixed(xdr, &res->cnr_stateid, NFS4_STATEID_SIZE);
if (unlikely(status))
return -EIO;
/* number of source addresses */
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
return -EIO;
count = be32_to_cpup(p);
if (count > 1)
pr_warn("NFS: %s: nsvr %d > Supported. Use first servers\n",
__func__, count);
status = decode_nl4_server(xdr, &res->cnr_src);
if (unlikely(status))
return -EIO;
return 0;
}
static int decode_deallocate(struct xdr_stream *xdr, struct nfs42_falloc_res *res)
{
return decode_op_hdr(xdr, OP_DEALLOCATE);
}
static int decode_seek(struct xdr_stream *xdr, struct nfs42_seek_res *res)
{
int status;
__be32 *p;
status = decode_op_hdr(xdr, OP_SEEK);
if (status)
return status;
p = xdr_inline_decode(xdr, 4 + 8);
if (unlikely(!p))
return -EIO;
res->sr_eof = be32_to_cpup(p++);
p = xdr_decode_hyper(p, &res->sr_offset);
return 0;
}
static int decode_layoutstats(struct xdr_stream *xdr)
{
return decode_op_hdr(xdr, OP_LAYOUTSTATS);
}
static int decode_clone(struct xdr_stream *xdr)
{
return decode_op_hdr(xdr, OP_CLONE);
}
static int decode_layouterror(struct xdr_stream *xdr)
{
return decode_op_hdr(xdr, OP_LAYOUTERROR);
}
/*
* Decode ALLOCATE request
*/
static int nfs4_xdr_dec_allocate(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
void *data)
{
struct nfs42_falloc_res *res = data;
struct compound_hdr hdr;
int status;
status = decode_compound_hdr(xdr, &hdr);
if (status)
goto out;
status = decode_sequence(xdr, &res->seq_res, rqstp);
if (status)
goto out;
status = decode_putfh(xdr);
if (status)
goto out;
status = decode_allocate(xdr, res);
if (status)
goto out;
decode_getfattr(xdr, res->falloc_fattr, res->falloc_server);
out:
return status;
}
/*
* Decode COPY response
*/
static int nfs4_xdr_dec_copy(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
void *data)
{
struct nfs42_copy_res *res = data;
struct compound_hdr hdr;
int status;
status = decode_compound_hdr(xdr, &hdr);
if (status)
goto out;
status = decode_sequence(xdr, &res->seq_res, rqstp);
if (status)
goto out;
status = decode_putfh(xdr);
if (status)
goto out;
status = decode_savefh(xdr);
if (status)
goto out;
status = decode_putfh(xdr);
if (status)
goto out;
status = decode_copy(xdr, res);
if (status)
goto out;
if (res->commit_res.verf)
status = decode_commit(xdr, &res->commit_res);
out:
return status;
}
/*
* Decode OFFLOAD_CANCEL response
*/
static int nfs4_xdr_dec_offload_cancel(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
void *data)
{
struct nfs42_offload_status_res *res = data;
struct compound_hdr hdr;
int status;
status = decode_compound_hdr(xdr, &hdr);
if (status)
goto out;
status = decode_sequence(xdr, &res->osr_seq_res, rqstp);
if (status)
goto out;
status = decode_putfh(xdr);
if (status)
goto out;
status = decode_offload_cancel(xdr, res);
out:
return status;
}
/*
* Decode COPY_NOTIFY response
*/
static int nfs4_xdr_dec_copy_notify(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
void *data)
{
struct nfs42_copy_notify_res *res = data;
struct compound_hdr hdr;
int status;
status = decode_compound_hdr(xdr, &hdr);
if (status)
goto out;
status = decode_sequence(xdr, &res->cnr_seq_res, rqstp);
if (status)
goto out;
status = decode_putfh(xdr);
if (status)
goto out;
status = decode_copy_notify(xdr, res);
out:
return status;
}
/*
* Decode DEALLOCATE request
*/
static int nfs4_xdr_dec_deallocate(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
void *data)
{
struct nfs42_falloc_res *res = data;
struct compound_hdr hdr;
int status;
status = decode_compound_hdr(xdr, &hdr);
if (status)
goto out;
status = decode_sequence(xdr, &res->seq_res, rqstp);
if (status)
goto out;
status = decode_putfh(xdr);
if (status)
goto out;
status = decode_deallocate(xdr, res);
if (status)
goto out;
decode_getfattr(xdr, res->falloc_fattr, res->falloc_server);
out:
return status;
}
/*
* Decode SEEK request
*/
static int nfs4_xdr_dec_seek(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
void *data)
{
struct nfs42_seek_res *res = data;
struct compound_hdr hdr;
int status;
status = decode_compound_hdr(xdr, &hdr);
if (status)
goto out;
status = decode_sequence(xdr, &res->seq_res, rqstp);
if (status)
goto out;
status = decode_putfh(xdr);
if (status)
goto out;
status = decode_seek(xdr, res);
out:
return status;
}
/*
* Decode LAYOUTSTATS request
*/
static int nfs4_xdr_dec_layoutstats(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
void *data)
{
struct nfs42_layoutstat_res *res = data;
struct compound_hdr hdr;
int status, i;
status = decode_compound_hdr(xdr, &hdr);
if (status)
goto out;
status = decode_sequence(xdr, &res->seq_res, rqstp);
if (status)
goto out;
status = decode_putfh(xdr);
if (status)
goto out;
WARN_ON(res->num_dev > PNFS_LAYOUTSTATS_MAXDEV);
for (i = 0; i < res->num_dev; i++) {
status = decode_layoutstats(xdr);
if (status)
goto out;
}
out:
res->rpc_status = status;
return status;
}
/*
* Decode CLONE request
*/
static int nfs4_xdr_dec_clone(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
void *data)
{
struct nfs42_clone_res *res = data;
struct compound_hdr hdr;
int status;
status = decode_compound_hdr(xdr, &hdr);
if (status)
goto out;
status = decode_sequence(xdr, &res->seq_res, rqstp);
if (status)
goto out;
status = decode_putfh(xdr);
if (status)
goto out;
status = decode_savefh(xdr);
if (status)
goto out;
status = decode_putfh(xdr);
if (status)
goto out;
status = decode_clone(xdr);
if (status)
goto out;
status = decode_getfattr(xdr, res->dst_fattr, res->server);
out:
res->rpc_status = status;
return status;
}
/*
* Decode LAYOUTERROR request
*/
static int nfs4_xdr_dec_layouterror(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
void *data)
{
struct nfs42_layouterror_res *res = data;
struct compound_hdr hdr;
int status, i;
status = decode_compound_hdr(xdr, &hdr);
if (status)
goto out;
status = decode_sequence(xdr, &res->seq_res, rqstp);
if (status)
goto out;
status = decode_putfh(xdr);
for (i = 0; i < res->num_errors && status == 0; i++)
status = decode_layouterror(xdr);
out:
res->rpc_status = status;
return status;
}
#ifdef CONFIG_NFS_V4_2
static void nfs4_xdr_enc_setxattr(struct rpc_rqst *req, struct xdr_stream *xdr,
const void *data)
{
const struct nfs42_setxattrargs *args = data;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr);
encode_setxattr(xdr, args, &hdr);
encode_nops(&hdr);
}
static int nfs4_xdr_dec_setxattr(struct rpc_rqst *req, struct xdr_stream *xdr,
void *data)
{
struct nfs42_setxattrres *res = data;
struct compound_hdr hdr;
int status;
status = decode_compound_hdr(xdr, &hdr);
if (status)
goto out;
status = decode_sequence(xdr, &res->seq_res, req);
if (status)
goto out;
status = decode_putfh(xdr);
if (status)
goto out;
status = decode_setxattr(xdr, &res->cinfo);
out:
return status;
}
static void nfs4_xdr_enc_getxattr(struct rpc_rqst *req, struct xdr_stream *xdr,
const void *data)
{
const struct nfs42_getxattrargs *args = data;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
size_t plen;
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr);
encode_getxattr(xdr, args->xattr_name, &hdr);
plen = args->xattr_len ? args->xattr_len : XATTR_SIZE_MAX;
rpc_prepare_reply_pages(req, args->xattr_pages, 0, plen,
hdr.replen);
req->rq_rcv_buf.flags |= XDRBUF_SPARSE_PAGES;
encode_nops(&hdr);
}
static int nfs4_xdr_dec_getxattr(struct rpc_rqst *rqstp,
struct xdr_stream *xdr, void *data)
{
struct nfs42_getxattrres *res = data;
struct compound_hdr hdr;
int status;
status = decode_compound_hdr(xdr, &hdr);
if (status)
goto out;
status = decode_sequence(xdr, &res->seq_res, rqstp);
if (status)
goto out;
status = decode_putfh(xdr);
if (status)
goto out;
status = decode_getxattr(xdr, res, rqstp);
out:
return status;
}
static void nfs4_xdr_enc_listxattrs(struct rpc_rqst *req,
struct xdr_stream *xdr, const void *data)
{
const struct nfs42_listxattrsargs *args = data;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr);
encode_listxattrs(xdr, args, &hdr);
rpc_prepare_reply_pages(req, args->xattr_pages, 0, args->count,
hdr.replen);
req->rq_rcv_buf.flags |= XDRBUF_SPARSE_PAGES;
encode_nops(&hdr);
}
static int nfs4_xdr_dec_listxattrs(struct rpc_rqst *rqstp,
struct xdr_stream *xdr, void *data)
{
struct nfs42_listxattrsres *res = data;
struct compound_hdr hdr;
int status;
xdr_set_scratch_buffer(xdr, page_address(res->scratch), PAGE_SIZE);
status = decode_compound_hdr(xdr, &hdr);
if (status)
goto out;
status = decode_sequence(xdr, &res->seq_res, rqstp);
if (status)
goto out;
status = decode_putfh(xdr);
if (status)
goto out;
status = decode_listxattrs(xdr, res);
out:
return status;
}
static void nfs4_xdr_enc_removexattr(struct rpc_rqst *req,
struct xdr_stream *xdr, const void *data)
{
const struct nfs42_removexattrargs *args = data;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr);
encode_removexattr(xdr, args->xattr_name, &hdr);
encode_nops(&hdr);
}
static int nfs4_xdr_dec_removexattr(struct rpc_rqst *req,
struct xdr_stream *xdr, void *data)
{
struct nfs42_removexattrres *res = data;
struct compound_hdr hdr;
int status;
status = decode_compound_hdr(xdr, &hdr);
if (status)
goto out;
status = decode_sequence(xdr, &res->seq_res, req);
if (status)
goto out;
status = decode_putfh(xdr);
if (status)
goto out;
status = decode_removexattr(xdr, &res->cinfo);
out:
return status;
}
#endif
#endif /* __LINUX_FS_NFS_NFS4_2XDR_H */