// SPDX-License-Identifier: GPL-2.0
/*
* linux/fs/nfs/callback_xdr.c
*
* Copyright (C) 2004 Trond Myklebust
*
* NFSv4 callback encode/decode procedures
*/
#include <linux/kernel.h>
#include <linux/sunrpc/svc.h>
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
#include <linux/ratelimit.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/sunrpc/bc_xprt.h>
#include "nfs4_fs.h"
#include "callback.h"
#include "internal.h"
#include "nfs4session.h"
#include "nfs4trace.h"
#define CB_OP_TAGLEN_MAXSZ (512)
#define CB_OP_HDR_RES_MAXSZ (2 * 4) // opcode, status
#define CB_OP_GETATTR_BITMAP_MAXSZ (4 * 4) // bitmap length, 3 bitmaps
#define CB_OP_GETATTR_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \
CB_OP_GETATTR_BITMAP_MAXSZ + \
/* change, size, atime, ctime,
* mtime, deleg_atime, deleg_mtime */\
(2 + 2 + 3 + 3 + 3 + 3 + 3) * 4)
#define CB_OP_RECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
#if defined(CONFIG_NFS_V4_1)
#define CB_OP_LAYOUTRECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
#define CB_OP_DEVICENOTIFY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
#define CB_OP_SEQUENCE_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \
NFS4_MAX_SESSIONID_LEN + \
(1 + 3) * 4) // seqid, 3 slotids
#define CB_OP_RECALLANY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
#define CB_OP_RECALLSLOT_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
#define CB_OP_NOTIFY_LOCK_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
#endif /* CONFIG_NFS_V4_1 */
#ifdef CONFIG_NFS_V4_2
#define CB_OP_OFFLOAD_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
#endif /* CONFIG_NFS_V4_2 */
#define NFSDBG_FACILITY NFSDBG_CALLBACK
/* Internal error code */
#define NFS4ERR_RESOURCE_HDR 11050
struct callback_op {
__be32 (*process_op)(void *, void *, struct cb_process_state *);
__be32 (*decode_args)(struct svc_rqst *, struct xdr_stream *, void *);
__be32 (*encode_res)(struct svc_rqst *, struct xdr_stream *,
const void *);
long res_maxsize;
};
static struct callback_op callback_ops[];
static __be32 nfs4_callback_null(struct svc_rqst *rqstp)
{
return htonl(NFS4_OK);
}
/*
* svc_process_common() looks for an XDR encoder to know when
* not to drop a Reply.
*/
static bool nfs4_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
return true;
}
static __be32 decode_string(struct xdr_stream *xdr, unsigned int *len,
const char **str, size_t maxlen)
{
ssize_t err;
err = xdr_stream_decode_opaque_inline(xdr, (void **)str, maxlen);
if (err < 0)
return cpu_to_be32(NFS4ERR_RESOURCE);
*len = err;
return 0;
}
static __be32 decode_fh(struct xdr_stream *xdr, struct nfs_fh *fh)
{
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
fh->size = ntohl(*p);
if (fh->size > NFS4_FHSIZE)
return htonl(NFS4ERR_BADHANDLE);
p = xdr_inline_decode(xdr, fh->size);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
memcpy(&fh->data[0], p, fh->size);
memset(&fh->data[fh->size], 0, sizeof(fh->data) - fh->size);
return 0;
}
static __be32 decode_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
{
__be32 *p;
unsigned int attrlen;
p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
attrlen = ntohl(*p);
p = xdr_inline_decode(xdr, attrlen << 2);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
if (likely(attrlen > 0))
bitmap[0] = ntohl(*p++);
if (attrlen > 1)
bitmap[1] = ntohl(*p++);
if (attrlen > 2)
bitmap[2] = ntohl(*p);
return 0;
}
static __be32 decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
{
__be32 *p;
p = xdr_inline_decode(xdr, NFS4_STATEID_SIZE);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
memcpy(stateid->data, p, NFS4_STATEID_SIZE);
return 0;
}
static __be32 decode_delegation_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
{
stateid->type = NFS4_DELEGATION_STATEID_TYPE;
return decode_stateid(xdr, stateid);
}
static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr