// SPDX-License-Identifier: GPL-2.0
/*
* linux/fs/nfs/nfs2xdr.c
*
* XDR functions to encode/decode NFS RPC arguments and results.
*
* Copyright (C) 1992, 1993, 1994 Rick Sladkey
* Copyright (C) 1996 Olaf Kirch
* 04 Aug 1998 Ion Badulescu <ionut@cs.columbia.edu>
* FIFO's need special handling in NFSv2
*/
#include <linux/param.h>
#include <linux/time.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/in.h>
#include <linux/pagemap.h>
#include <linux/proc_fs.h>
#include <linux/sunrpc/clnt.h>
#include <linux/nfs.h>
#include <linux/nfs2.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_common.h>
#include "nfstrace.h"
#include "internal.h"
#define NFSDBG_FACILITY NFSDBG_XDR
/*
* Declare the space requirements for NFS arguments and replies as
* number of 32bit-words
*/
#define NFS_pagepad_sz (1) /* Page padding */
#define NFS_fhandle_sz (8)
#define NFS_sattr_sz (8)
#define NFS_filename_sz (1+(NFS2_MAXNAMLEN>>2))
#define NFS_path_sz (1+(NFS2_MAXPATHLEN>>2))
#define NFS_fattr_sz (17)
#define NFS_info_sz (5)
#define NFS_entry_sz (NFS_filename_sz+3)
#define NFS_diropargs_sz (NFS_fhandle_sz+NFS_filename_sz)
#define NFS_removeargs_sz (NFS_fhandle_sz+NFS_filename_sz)
#define NFS_sattrargs_sz (NFS_fhandle_sz+NFS_sattr_sz)
#define NFS_readlinkargs_sz (NFS_fhandle_sz)
#define NFS_readargs_sz (NFS_fhandle_sz+3)
#define NFS_writeargs_sz (NFS_fhandle_sz+4)
#define NFS_createargs_sz (NFS_diropargs_sz+NFS_sattr_sz)
#define NFS_renameargs_sz (NFS_diropargs_sz+NFS_diropargs_sz)
#define NFS_linkargs_sz (NFS_fhandle_sz+NFS_diropargs_sz)
#define NFS_symlinkargs_sz (NFS_diropargs_sz+1+NFS_sattr_sz)
#define NFS_readdirargs_sz (NFS_fhandle_sz+2)
#define NFS_attrstat_sz (1+NFS_fattr_sz)
#define NFS_diropres_sz (1+NFS_fhandle_sz+NFS_fattr_sz)
#define NFS_readlinkres_sz (2+NFS_pagepad_sz)
#define NFS_readres_sz (1+NFS_fattr_sz+1+NFS_pagepad_sz)
#define NFS_writeres_sz (NFS_attrstat_sz)
#define NFS_stat_sz (1)
#define NFS_readdirres_sz (1+NFS_pagepad_sz)
#define NFS_statfsres_sz (1+NFS_info_sz)
/*
* Encode/decode NFSv2 basic data types
*
* Basic NFSv2 data types are defined in section 2.3 of RFC 1094:
* "NFS: Network File System Protocol Specification".
*
* Not all basic data types have their own encoding and decoding
* functions. For run-time efficiency, some data types are encoded
* or decoded inline.
*/
static struct user_namespace *rpc_userns(const struct rpc_clnt *clnt)
{
if (clnt && clnt->cl_cred)
return clnt->cl_cred->user_ns;
return &init_user_ns;
}
static struct user_namespace *rpc_rqst_userns(const struct rpc_rqst *rqstp)
{
if (rqstp->rq_task)
return rpc_userns(rqstp->rq_task->tk_client);
return &init_user_ns;
}
/*
* typedef opaque nfsdata<>;
*/
static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_pgio_res *result)
{
u32 recvd, count;
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
return -EIO;
count = be32_to_cpup(p);
recvd = xdr_read_pages(xdr, count);
if (unlikely(count > recvd))
goto out_cheating;
out:
result->eof = 0; /* NFSv2 does not pass EOF flag on the wire. */
result->count = count;
return count;
out_cheating:
dprintk("NFS: server cheating in read result: "
"count %u > recvd %u\n", count, recvd);
count = recvd;
goto out;
}
/*
* enum stat {
* NFS_OK = 0,
* NFSERR_PERM = 1,
* NFSERR_NOENT = 2,
* NFSERR_IO = 5,
* NFSERR_NXIO = 6,
* NFSERR_ACCES = 13,
* NFSERR_EXIST = 17,
* NFSERR_NODEV = 19,
* NFSERR_NOTDIR = 20,
* NFSERR_ISDIR = 21,
* NFSERR_FBIG = 27,
* NFSERR_NOSPC = 28,
* NFSERR_ROFS = 30,
* NFSERR_NAMETOOLONG = 63,
* NFSERR_NOTEMPTY = 66,
* NFSERR_DQUOT = 69,
* NFSERR_STALE = 70,
* NFSERR_WFLUSH = 99
* };
*/
static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status)
{
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
return -EIO;
if (unlikely(*p != cpu_to_be32(NFS_OK)))
goto out_status;
*status = 0;
return 0;
out_status:
*status = be32_to_cpup(p);
trace_nfs_xdr_status(xdr, (int)*status);
return 0;
}
/*
* 2.3.2. ftype
*
* enum ftype {
* NFNON = 0,
* NFREG = 1,
* NFDIR = 2,
* NFBLK = 3,
* NFCHR = 4,
* NFLNK = 5
* };
*
*/
static __be32 *xdr_decode_ftype(__be32 *p, u32 *type)
{
*type = be32_to_cpup(p++);
if (unlikely(*type > NF2FIFO))
*type = NFBAD;
return p;
}
/*
* 2.3.3. fhandle
*
* typedef opaque fhandle[FHSIZE];
*/
static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh)
{
__be32 *p;
p = xdr_reserve_space(xdr, NFS2_FHSIZE);
memcpy(p, fh->data, NFS2_FHSIZE);
}
static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh)
{
__be32 *p;
p = xdr_inline_decode(xdr, NFS2_FHSIZE);
if (unlikely(!p))
return -EIO;
fh->size = NFS2_FHSIZE;
memcpy(fh->data, p, NFS2_FHSIZE);
return 0;
}
/*
* 2.3.4. timeval
*
* struct timeval {
* unsigned int seconds;
* unsigned int useconds;
* };
*/
static __be32 *xdr_encode_time(__be32 *p, const struct timespec64 *timep)
{
*p++ = cpu_to_be32((u32)timep->tv_sec);
if (timep->tv_nsec != 0)
*p++ = cpu_to_be32(timep->tv_nsec / NSEC_PER_USEC);
else
*p++ = cpu_to_be32(0);
return p;
}
/*
* Passing the invalid value useconds=1000000 is a Sun convention for
* "