162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * XDR support for nfsd
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "vfs.h"
962306a36Sopenharmony_ci#include "xdr.h"
1062306a36Sopenharmony_ci#include "auth.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/*
1362306a36Sopenharmony_ci * Mapping of S_IF* types to NFS file types
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_cistatic const u32 nfs_ftypes[] = {
1662306a36Sopenharmony_ci	NFNON,  NFCHR,  NFCHR, NFBAD,
1762306a36Sopenharmony_ci	NFDIR,  NFBAD,  NFBLK, NFBAD,
1862306a36Sopenharmony_ci	NFREG,  NFBAD,  NFLNK, NFBAD,
1962306a36Sopenharmony_ci	NFSOCK, NFBAD,  NFLNK, NFBAD,
2062306a36Sopenharmony_ci};
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/*
2462306a36Sopenharmony_ci * Basic NFSv2 data types (RFC 1094 Section 2.3)
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/**
2862306a36Sopenharmony_ci * svcxdr_encode_stat - Encode an NFSv2 status code
2962306a36Sopenharmony_ci * @xdr: XDR stream
3062306a36Sopenharmony_ci * @status: status value to encode
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * Return values:
3362306a36Sopenharmony_ci *   %false: Send buffer space was exhausted
3462306a36Sopenharmony_ci *   %true: Success
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_cibool
3762306a36Sopenharmony_cisvcxdr_encode_stat(struct xdr_stream *xdr, __be32 status)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	__be32 *p;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, sizeof(status));
4262306a36Sopenharmony_ci	if (!p)
4362306a36Sopenharmony_ci		return false;
4462306a36Sopenharmony_ci	*p = status;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	return true;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/**
5062306a36Sopenharmony_ci * svcxdr_decode_fhandle - Decode an NFSv2 file handle
5162306a36Sopenharmony_ci * @xdr: XDR stream positioned at an encoded NFSv2 FH
5262306a36Sopenharmony_ci * @fhp: OUT: filled-in server file handle
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * Return values:
5562306a36Sopenharmony_ci *  %false: The encoded file handle was not valid
5662306a36Sopenharmony_ci *  %true: @fhp has been initialized
5762306a36Sopenharmony_ci */
5862306a36Sopenharmony_cibool
5962306a36Sopenharmony_cisvcxdr_decode_fhandle(struct xdr_stream *xdr, struct svc_fh *fhp)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	__be32 *p;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	p = xdr_inline_decode(xdr, NFS_FHSIZE);
6462306a36Sopenharmony_ci	if (!p)
6562306a36Sopenharmony_ci		return false;
6662306a36Sopenharmony_ci	fh_init(fhp, NFS_FHSIZE);
6762306a36Sopenharmony_ci	memcpy(&fhp->fh_handle.fh_raw, p, NFS_FHSIZE);
6862306a36Sopenharmony_ci	fhp->fh_handle.fh_size = NFS_FHSIZE;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return true;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic bool
7462306a36Sopenharmony_cisvcxdr_encode_fhandle(struct xdr_stream *xdr, const struct svc_fh *fhp)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	__be32 *p;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, NFS_FHSIZE);
7962306a36Sopenharmony_ci	if (!p)
8062306a36Sopenharmony_ci		return false;
8162306a36Sopenharmony_ci	memcpy(p, &fhp->fh_handle.fh_raw, NFS_FHSIZE);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return true;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic __be32 *
8762306a36Sopenharmony_ciencode_timeval(__be32 *p, const struct timespec64 *time)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	*p++ = cpu_to_be32((u32)time->tv_sec);
9062306a36Sopenharmony_ci	if (time->tv_nsec)
9162306a36Sopenharmony_ci		*p++ = cpu_to_be32(time->tv_nsec / NSEC_PER_USEC);
9262306a36Sopenharmony_ci	else
9362306a36Sopenharmony_ci		*p++ = xdr_zero;
9462306a36Sopenharmony_ci	return p;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic bool
9862306a36Sopenharmony_cisvcxdr_decode_filename(struct xdr_stream *xdr, char **name, unsigned int *len)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	u32 size, i;
10162306a36Sopenharmony_ci	__be32 *p;
10262306a36Sopenharmony_ci	char *c;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (xdr_stream_decode_u32(xdr, &size) < 0)
10562306a36Sopenharmony_ci		return false;
10662306a36Sopenharmony_ci	if (size == 0 || size > NFS_MAXNAMLEN)
10762306a36Sopenharmony_ci		return false;
10862306a36Sopenharmony_ci	p = xdr_inline_decode(xdr, size);
10962306a36Sopenharmony_ci	if (!p)
11062306a36Sopenharmony_ci		return false;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	*len = size;
11362306a36Sopenharmony_ci	*name = (char *)p;
11462306a36Sopenharmony_ci	for (i = 0, c = *name; i < size; i++, c++)
11562306a36Sopenharmony_ci		if (*c == '\0' || *c == '/')
11662306a36Sopenharmony_ci			return false;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	return true;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic bool
12262306a36Sopenharmony_cisvcxdr_decode_diropargs(struct xdr_stream *xdr, struct svc_fh *fhp,
12362306a36Sopenharmony_ci			char **name, unsigned int *len)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	return svcxdr_decode_fhandle(xdr, fhp) &&
12662306a36Sopenharmony_ci		svcxdr_decode_filename(xdr, name, len);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic bool
13062306a36Sopenharmony_cisvcxdr_decode_sattr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
13162306a36Sopenharmony_ci		    struct iattr *iap)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	u32 tmp1, tmp2;
13462306a36Sopenharmony_ci	__be32 *p;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	p = xdr_inline_decode(xdr, XDR_UNIT * 8);
13762306a36Sopenharmony_ci	if (!p)
13862306a36Sopenharmony_ci		return false;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	iap->ia_valid = 0;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/*
14362306a36Sopenharmony_ci	 * Some Sun clients put 0xffff in the mode field when they
14462306a36Sopenharmony_ci	 * mean 0xffffffff.
14562306a36Sopenharmony_ci	 */
14662306a36Sopenharmony_ci	tmp1 = be32_to_cpup(p++);
14762306a36Sopenharmony_ci	if (tmp1 != (u32)-1 && tmp1 != 0xffff) {
14862306a36Sopenharmony_ci		iap->ia_valid |= ATTR_MODE;
14962306a36Sopenharmony_ci		iap->ia_mode = tmp1;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	tmp1 = be32_to_cpup(p++);
15362306a36Sopenharmony_ci	if (tmp1 != (u32)-1) {
15462306a36Sopenharmony_ci		iap->ia_uid = make_kuid(nfsd_user_namespace(rqstp), tmp1);
15562306a36Sopenharmony_ci		if (uid_valid(iap->ia_uid))
15662306a36Sopenharmony_ci			iap->ia_valid |= ATTR_UID;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	tmp1 = be32_to_cpup(p++);
16062306a36Sopenharmony_ci	if (tmp1 != (u32)-1) {
16162306a36Sopenharmony_ci		iap->ia_gid = make_kgid(nfsd_user_namespace(rqstp), tmp1);
16262306a36Sopenharmony_ci		if (gid_valid(iap->ia_gid))
16362306a36Sopenharmony_ci			iap->ia_valid |= ATTR_GID;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	tmp1 = be32_to_cpup(p++);
16762306a36Sopenharmony_ci	if (tmp1 != (u32)-1) {
16862306a36Sopenharmony_ci		iap->ia_valid |= ATTR_SIZE;
16962306a36Sopenharmony_ci		iap->ia_size = tmp1;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	tmp1 = be32_to_cpup(p++);
17362306a36Sopenharmony_ci	tmp2 = be32_to_cpup(p++);
17462306a36Sopenharmony_ci	if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
17562306a36Sopenharmony_ci		iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
17662306a36Sopenharmony_ci		iap->ia_atime.tv_sec = tmp1;
17762306a36Sopenharmony_ci		iap->ia_atime.tv_nsec = tmp2 * NSEC_PER_USEC;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	tmp1 = be32_to_cpup(p++);
18162306a36Sopenharmony_ci	tmp2 = be32_to_cpup(p++);
18262306a36Sopenharmony_ci	if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
18362306a36Sopenharmony_ci		iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
18462306a36Sopenharmony_ci		iap->ia_mtime.tv_sec = tmp1;
18562306a36Sopenharmony_ci		iap->ia_mtime.tv_nsec = tmp2 * NSEC_PER_USEC;
18662306a36Sopenharmony_ci		/*
18762306a36Sopenharmony_ci		 * Passing the invalid value useconds=1000000 for mtime
18862306a36Sopenharmony_ci		 * is a Sun convention for "set both mtime and atime to
18962306a36Sopenharmony_ci		 * current server time".  It's needed to make permissions
19062306a36Sopenharmony_ci		 * checks for the "touch" program across v2 mounts to
19162306a36Sopenharmony_ci		 * Solaris and Irix boxes work correctly. See description of
19262306a36Sopenharmony_ci		 * sattr in section 6.1 of "NFS Illustrated" by
19362306a36Sopenharmony_ci		 * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
19462306a36Sopenharmony_ci		 */
19562306a36Sopenharmony_ci		if (tmp2 == 1000000)
19662306a36Sopenharmony_ci			iap->ia_valid &= ~(ATTR_ATIME_SET|ATTR_MTIME_SET);
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	return true;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci/**
20362306a36Sopenharmony_ci * svcxdr_encode_fattr - Encode NFSv2 file attributes
20462306a36Sopenharmony_ci * @rqstp: Context of a completed RPC transaction
20562306a36Sopenharmony_ci * @xdr: XDR stream
20662306a36Sopenharmony_ci * @fhp: File handle to encode
20762306a36Sopenharmony_ci * @stat: Attributes to encode
20862306a36Sopenharmony_ci *
20962306a36Sopenharmony_ci * Return values:
21062306a36Sopenharmony_ci *   %false: Send buffer space was exhausted
21162306a36Sopenharmony_ci *   %true: Success
21262306a36Sopenharmony_ci */
21362306a36Sopenharmony_cibool
21462306a36Sopenharmony_cisvcxdr_encode_fattr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
21562306a36Sopenharmony_ci		    const struct svc_fh *fhp, const struct kstat *stat)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	struct user_namespace *userns = nfsd_user_namespace(rqstp);
21862306a36Sopenharmony_ci	struct dentry *dentry = fhp->fh_dentry;
21962306a36Sopenharmony_ci	int type = stat->mode & S_IFMT;
22062306a36Sopenharmony_ci	struct timespec64 time;
22162306a36Sopenharmony_ci	__be32 *p;
22262306a36Sopenharmony_ci	u32 fsid;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, XDR_UNIT * 17);
22562306a36Sopenharmony_ci	if (!p)
22662306a36Sopenharmony_ci		return false;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	*p++ = cpu_to_be32(nfs_ftypes[type >> 12]);
22962306a36Sopenharmony_ci	*p++ = cpu_to_be32((u32)stat->mode);
23062306a36Sopenharmony_ci	*p++ = cpu_to_be32((u32)stat->nlink);
23162306a36Sopenharmony_ci	*p++ = cpu_to_be32((u32)from_kuid_munged(userns, stat->uid));
23262306a36Sopenharmony_ci	*p++ = cpu_to_be32((u32)from_kgid_munged(userns, stat->gid));
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN)
23562306a36Sopenharmony_ci		*p++ = cpu_to_be32(NFS_MAXPATHLEN);
23662306a36Sopenharmony_ci	else
23762306a36Sopenharmony_ci		*p++ = cpu_to_be32((u32) stat->size);
23862306a36Sopenharmony_ci	*p++ = cpu_to_be32((u32) stat->blksize);
23962306a36Sopenharmony_ci	if (S_ISCHR(type) || S_ISBLK(type))
24062306a36Sopenharmony_ci		*p++ = cpu_to_be32(new_encode_dev(stat->rdev));
24162306a36Sopenharmony_ci	else
24262306a36Sopenharmony_ci		*p++ = cpu_to_be32(0xffffffff);
24362306a36Sopenharmony_ci	*p++ = cpu_to_be32((u32)stat->blocks);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	switch (fsid_source(fhp)) {
24662306a36Sopenharmony_ci	case FSIDSOURCE_FSID:
24762306a36Sopenharmony_ci		fsid = (u32)fhp->fh_export->ex_fsid;
24862306a36Sopenharmony_ci		break;
24962306a36Sopenharmony_ci	case FSIDSOURCE_UUID:
25062306a36Sopenharmony_ci		fsid = ((u32 *)fhp->fh_export->ex_uuid)[0];
25162306a36Sopenharmony_ci		fsid ^= ((u32 *)fhp->fh_export->ex_uuid)[1];
25262306a36Sopenharmony_ci		fsid ^= ((u32 *)fhp->fh_export->ex_uuid)[2];
25362306a36Sopenharmony_ci		fsid ^= ((u32 *)fhp->fh_export->ex_uuid)[3];
25462306a36Sopenharmony_ci		break;
25562306a36Sopenharmony_ci	default:
25662306a36Sopenharmony_ci		fsid = new_encode_dev(stat->dev);
25762306a36Sopenharmony_ci		break;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci	*p++ = cpu_to_be32(fsid);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	*p++ = cpu_to_be32((u32)stat->ino);
26262306a36Sopenharmony_ci	p = encode_timeval(p, &stat->atime);
26362306a36Sopenharmony_ci	time = stat->mtime;
26462306a36Sopenharmony_ci	lease_get_mtime(d_inode(dentry), &time);
26562306a36Sopenharmony_ci	p = encode_timeval(p, &time);
26662306a36Sopenharmony_ci	encode_timeval(p, &stat->ctime);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return true;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci/*
27262306a36Sopenharmony_ci * XDR decode functions
27362306a36Sopenharmony_ci */
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cibool
27662306a36Sopenharmony_cinfssvc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct nfsd_fhandle *args = rqstp->rq_argp;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	return svcxdr_decode_fhandle(xdr, &args->fh);
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cibool
28462306a36Sopenharmony_cinfssvc_decode_sattrargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct nfsd_sattrargs *args = rqstp->rq_argp;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	return svcxdr_decode_fhandle(xdr, &args->fh) &&
28962306a36Sopenharmony_ci		svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cibool
29362306a36Sopenharmony_cinfssvc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	struct nfsd_diropargs *args = rqstp->rq_argp;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	return svcxdr_decode_diropargs(xdr, &args->fh, &args->name, &args->len);
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cibool
30162306a36Sopenharmony_cinfssvc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct nfsd_readargs *args = rqstp->rq_argp;
30462306a36Sopenharmony_ci	u32 totalcount;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	if (!svcxdr_decode_fhandle(xdr, &args->fh))
30762306a36Sopenharmony_ci		return false;
30862306a36Sopenharmony_ci	if (xdr_stream_decode_u32(xdr, &args->offset) < 0)
30962306a36Sopenharmony_ci		return false;
31062306a36Sopenharmony_ci	if (xdr_stream_decode_u32(xdr, &args->count) < 0)
31162306a36Sopenharmony_ci		return false;
31262306a36Sopenharmony_ci	/* totalcount is ignored */
31362306a36Sopenharmony_ci	if (xdr_stream_decode_u32(xdr, &totalcount) < 0)
31462306a36Sopenharmony_ci		return false;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	return true;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cibool
32062306a36Sopenharmony_cinfssvc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	struct nfsd_writeargs *args = rqstp->rq_argp;
32362306a36Sopenharmony_ci	u32 beginoffset, totalcount;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	if (!svcxdr_decode_fhandle(xdr, &args->fh))
32662306a36Sopenharmony_ci		return false;
32762306a36Sopenharmony_ci	/* beginoffset is ignored */
32862306a36Sopenharmony_ci	if (xdr_stream_decode_u32(xdr, &beginoffset) < 0)
32962306a36Sopenharmony_ci		return false;
33062306a36Sopenharmony_ci	if (xdr_stream_decode_u32(xdr, &args->offset) < 0)
33162306a36Sopenharmony_ci		return false;
33262306a36Sopenharmony_ci	/* totalcount is ignored */
33362306a36Sopenharmony_ci	if (xdr_stream_decode_u32(xdr, &totalcount) < 0)
33462306a36Sopenharmony_ci		return false;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/* opaque data */
33762306a36Sopenharmony_ci	if (xdr_stream_decode_u32(xdr, &args->len) < 0)
33862306a36Sopenharmony_ci		return false;
33962306a36Sopenharmony_ci	if (args->len > NFSSVC_MAXBLKSIZE_V2)
34062306a36Sopenharmony_ci		return false;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	return xdr_stream_subsegment(xdr, &args->payload, args->len);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cibool
34662306a36Sopenharmony_cinfssvc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	struct nfsd_createargs *args = rqstp->rq_argp;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	return svcxdr_decode_diropargs(xdr, &args->fh,
35162306a36Sopenharmony_ci				       &args->name, &args->len) &&
35262306a36Sopenharmony_ci		svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cibool
35662306a36Sopenharmony_cinfssvc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	struct nfsd_renameargs *args = rqstp->rq_argp;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	return svcxdr_decode_diropargs(xdr, &args->ffh,
36162306a36Sopenharmony_ci				       &args->fname, &args->flen) &&
36262306a36Sopenharmony_ci		svcxdr_decode_diropargs(xdr, &args->tfh,
36362306a36Sopenharmony_ci					&args->tname, &args->tlen);
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cibool
36762306a36Sopenharmony_cinfssvc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct nfsd_linkargs *args = rqstp->rq_argp;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	return svcxdr_decode_fhandle(xdr, &args->ffh) &&
37262306a36Sopenharmony_ci		svcxdr_decode_diropargs(xdr, &args->tfh,
37362306a36Sopenharmony_ci					&args->tname, &args->tlen);
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cibool
37762306a36Sopenharmony_cinfssvc_decode_symlinkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	struct nfsd_symlinkargs *args = rqstp->rq_argp;
38062306a36Sopenharmony_ci	struct kvec *head = rqstp->rq_arg.head;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (!svcxdr_decode_diropargs(xdr, &args->ffh, &args->fname, &args->flen))
38362306a36Sopenharmony_ci		return false;
38462306a36Sopenharmony_ci	if (xdr_stream_decode_u32(xdr, &args->tlen) < 0)
38562306a36Sopenharmony_ci		return false;
38662306a36Sopenharmony_ci	if (args->tlen == 0)
38762306a36Sopenharmony_ci		return false;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	args->first.iov_len = head->iov_len - xdr_stream_pos(xdr);
39062306a36Sopenharmony_ci	args->first.iov_base = xdr_inline_decode(xdr, args->tlen);
39162306a36Sopenharmony_ci	if (!args->first.iov_base)
39262306a36Sopenharmony_ci		return false;
39362306a36Sopenharmony_ci	return svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cibool
39762306a36Sopenharmony_cinfssvc_decode_readdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct nfsd_readdirargs *args = rqstp->rq_argp;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	if (!svcxdr_decode_fhandle(xdr, &args->fh))
40262306a36Sopenharmony_ci		return false;
40362306a36Sopenharmony_ci	if (xdr_stream_decode_u32(xdr, &args->cookie) < 0)
40462306a36Sopenharmony_ci		return false;
40562306a36Sopenharmony_ci	if (xdr_stream_decode_u32(xdr, &args->count) < 0)
40662306a36Sopenharmony_ci		return false;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	return true;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci/*
41262306a36Sopenharmony_ci * XDR encode functions
41362306a36Sopenharmony_ci */
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cibool
41662306a36Sopenharmony_cinfssvc_encode_statres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	struct nfsd_stat *resp = rqstp->rq_resp;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	return svcxdr_encode_stat(xdr, resp->status);
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cibool
42462306a36Sopenharmony_cinfssvc_encode_attrstatres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct nfsd_attrstat *resp = rqstp->rq_resp;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (!svcxdr_encode_stat(xdr, resp->status))
42962306a36Sopenharmony_ci		return false;
43062306a36Sopenharmony_ci	switch (resp->status) {
43162306a36Sopenharmony_ci	case nfs_ok:
43262306a36Sopenharmony_ci		if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
43362306a36Sopenharmony_ci			return false;
43462306a36Sopenharmony_ci		break;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	return true;
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cibool
44162306a36Sopenharmony_cinfssvc_encode_diropres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	struct nfsd_diropres *resp = rqstp->rq_resp;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (!svcxdr_encode_stat(xdr, resp->status))
44662306a36Sopenharmony_ci		return false;
44762306a36Sopenharmony_ci	switch (resp->status) {
44862306a36Sopenharmony_ci	case nfs_ok:
44962306a36Sopenharmony_ci		if (!svcxdr_encode_fhandle(xdr, &resp->fh))
45062306a36Sopenharmony_ci			return false;
45162306a36Sopenharmony_ci		if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
45262306a36Sopenharmony_ci			return false;
45362306a36Sopenharmony_ci		break;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	return true;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cibool
46062306a36Sopenharmony_cinfssvc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct nfsd_readlinkres *resp = rqstp->rq_resp;
46362306a36Sopenharmony_ci	struct kvec *head = rqstp->rq_res.head;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	if (!svcxdr_encode_stat(xdr, resp->status))
46662306a36Sopenharmony_ci		return false;
46762306a36Sopenharmony_ci	switch (resp->status) {
46862306a36Sopenharmony_ci	case nfs_ok:
46962306a36Sopenharmony_ci		if (xdr_stream_encode_u32(xdr, resp->len) < 0)
47062306a36Sopenharmony_ci			return false;
47162306a36Sopenharmony_ci		svcxdr_encode_opaque_pages(rqstp, xdr, &resp->page, 0,
47262306a36Sopenharmony_ci					   resp->len);
47362306a36Sopenharmony_ci		if (svc_encode_result_payload(rqstp, head->iov_len, resp->len) < 0)
47462306a36Sopenharmony_ci			return false;
47562306a36Sopenharmony_ci		break;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	return true;
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cibool
48262306a36Sopenharmony_cinfssvc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	struct nfsd_readres *resp = rqstp->rq_resp;
48562306a36Sopenharmony_ci	struct kvec *head = rqstp->rq_res.head;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if (!svcxdr_encode_stat(xdr, resp->status))
48862306a36Sopenharmony_ci		return false;
48962306a36Sopenharmony_ci	switch (resp->status) {
49062306a36Sopenharmony_ci	case nfs_ok:
49162306a36Sopenharmony_ci		if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
49262306a36Sopenharmony_ci			return false;
49362306a36Sopenharmony_ci		if (xdr_stream_encode_u32(xdr, resp->count) < 0)
49462306a36Sopenharmony_ci			return false;
49562306a36Sopenharmony_ci		svcxdr_encode_opaque_pages(rqstp, xdr, resp->pages,
49662306a36Sopenharmony_ci					   rqstp->rq_res.page_base,
49762306a36Sopenharmony_ci					   resp->count);
49862306a36Sopenharmony_ci		if (svc_encode_result_payload(rqstp, head->iov_len, resp->count) < 0)
49962306a36Sopenharmony_ci			return false;
50062306a36Sopenharmony_ci		break;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	return true;
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cibool
50762306a36Sopenharmony_cinfssvc_encode_readdirres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	struct nfsd_readdirres *resp = rqstp->rq_resp;
51062306a36Sopenharmony_ci	struct xdr_buf *dirlist = &resp->dirlist;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	if (!svcxdr_encode_stat(xdr, resp->status))
51362306a36Sopenharmony_ci		return false;
51462306a36Sopenharmony_ci	switch (resp->status) {
51562306a36Sopenharmony_ci	case nfs_ok:
51662306a36Sopenharmony_ci		svcxdr_encode_opaque_pages(rqstp, xdr, dirlist->pages, 0,
51762306a36Sopenharmony_ci					   dirlist->len);
51862306a36Sopenharmony_ci		/* no more entries */
51962306a36Sopenharmony_ci		if (xdr_stream_encode_item_absent(xdr) < 0)
52062306a36Sopenharmony_ci			return false;
52162306a36Sopenharmony_ci		if (xdr_stream_encode_bool(xdr, resp->common.err == nfserr_eof) < 0)
52262306a36Sopenharmony_ci			return false;
52362306a36Sopenharmony_ci		break;
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	return true;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cibool
53062306a36Sopenharmony_cinfssvc_encode_statfsres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	struct nfsd_statfsres *resp = rqstp->rq_resp;
53362306a36Sopenharmony_ci	struct kstatfs	*stat = &resp->stats;
53462306a36Sopenharmony_ci	__be32 *p;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	if (!svcxdr_encode_stat(xdr, resp->status))
53762306a36Sopenharmony_ci		return false;
53862306a36Sopenharmony_ci	switch (resp->status) {
53962306a36Sopenharmony_ci	case nfs_ok:
54062306a36Sopenharmony_ci		p = xdr_reserve_space(xdr, XDR_UNIT * 5);
54162306a36Sopenharmony_ci		if (!p)
54262306a36Sopenharmony_ci			return false;
54362306a36Sopenharmony_ci		*p++ = cpu_to_be32(NFSSVC_MAXBLKSIZE_V2);
54462306a36Sopenharmony_ci		*p++ = cpu_to_be32(stat->f_bsize);
54562306a36Sopenharmony_ci		*p++ = cpu_to_be32(stat->f_blocks);
54662306a36Sopenharmony_ci		*p++ = cpu_to_be32(stat->f_bfree);
54762306a36Sopenharmony_ci		*p = cpu_to_be32(stat->f_bavail);
54862306a36Sopenharmony_ci		break;
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	return true;
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci/**
55562306a36Sopenharmony_ci * nfssvc_encode_nfscookie - Encode a directory offset cookie
55662306a36Sopenharmony_ci * @resp: readdir result context
55762306a36Sopenharmony_ci * @offset: offset cookie to encode
55862306a36Sopenharmony_ci *
55962306a36Sopenharmony_ci * The buffer space for the offset cookie has already been reserved
56062306a36Sopenharmony_ci * by svcxdr_encode_entry_common().
56162306a36Sopenharmony_ci */
56262306a36Sopenharmony_civoid nfssvc_encode_nfscookie(struct nfsd_readdirres *resp, u32 offset)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	__be32 cookie = cpu_to_be32(offset);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	if (!resp->cookie_offset)
56762306a36Sopenharmony_ci		return;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	write_bytes_to_xdr_buf(&resp->dirlist, resp->cookie_offset, &cookie,
57062306a36Sopenharmony_ci			       sizeof(cookie));
57162306a36Sopenharmony_ci	resp->cookie_offset = 0;
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic bool
57562306a36Sopenharmony_cisvcxdr_encode_entry_common(struct nfsd_readdirres *resp, const char *name,
57662306a36Sopenharmony_ci			   int namlen, loff_t offset, u64 ino)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	struct xdr_buf *dirlist = &resp->dirlist;
57962306a36Sopenharmony_ci	struct xdr_stream *xdr = &resp->xdr;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (xdr_stream_encode_item_present(xdr) < 0)
58262306a36Sopenharmony_ci		return false;
58362306a36Sopenharmony_ci	/* fileid */
58462306a36Sopenharmony_ci	if (xdr_stream_encode_u32(xdr, (u32)ino) < 0)
58562306a36Sopenharmony_ci		return false;
58662306a36Sopenharmony_ci	/* name */
58762306a36Sopenharmony_ci	if (xdr_stream_encode_opaque(xdr, name, min(namlen, NFS2_MAXNAMLEN)) < 0)
58862306a36Sopenharmony_ci		return false;
58962306a36Sopenharmony_ci	/* cookie */
59062306a36Sopenharmony_ci	resp->cookie_offset = dirlist->len;
59162306a36Sopenharmony_ci	if (xdr_stream_encode_u32(xdr, ~0U) < 0)
59262306a36Sopenharmony_ci		return false;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	return true;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci/**
59862306a36Sopenharmony_ci * nfssvc_encode_entry - encode one NFSv2 READDIR entry
59962306a36Sopenharmony_ci * @data: directory context
60062306a36Sopenharmony_ci * @name: name of the object to be encoded
60162306a36Sopenharmony_ci * @namlen: length of that name, in bytes
60262306a36Sopenharmony_ci * @offset: the offset of the previous entry
60362306a36Sopenharmony_ci * @ino: the fileid of this entry
60462306a36Sopenharmony_ci * @d_type: unused
60562306a36Sopenharmony_ci *
60662306a36Sopenharmony_ci * Return values:
60762306a36Sopenharmony_ci *   %0: Entry was successfully encoded.
60862306a36Sopenharmony_ci *   %-EINVAL: An encoding problem occured, secondary status code in resp->common.err
60962306a36Sopenharmony_ci *
61062306a36Sopenharmony_ci * On exit, the following fields are updated:
61162306a36Sopenharmony_ci *   - resp->xdr
61262306a36Sopenharmony_ci *   - resp->common.err
61362306a36Sopenharmony_ci *   - resp->cookie_offset
61462306a36Sopenharmony_ci */
61562306a36Sopenharmony_ciint nfssvc_encode_entry(void *data, const char *name, int namlen,
61662306a36Sopenharmony_ci			loff_t offset, u64 ino, unsigned int d_type)
61762306a36Sopenharmony_ci{
61862306a36Sopenharmony_ci	struct readdir_cd *ccd = data;
61962306a36Sopenharmony_ci	struct nfsd_readdirres *resp = container_of(ccd,
62062306a36Sopenharmony_ci						    struct nfsd_readdirres,
62162306a36Sopenharmony_ci						    common);
62262306a36Sopenharmony_ci	unsigned int starting_length = resp->dirlist.len;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	/* The offset cookie for the previous entry */
62562306a36Sopenharmony_ci	nfssvc_encode_nfscookie(resp, offset);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	if (!svcxdr_encode_entry_common(resp, name, namlen, offset, ino))
62862306a36Sopenharmony_ci		goto out_toosmall;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	xdr_commit_encode(&resp->xdr);
63162306a36Sopenharmony_ci	resp->common.err = nfs_ok;
63262306a36Sopenharmony_ci	return 0;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ciout_toosmall:
63562306a36Sopenharmony_ci	resp->cookie_offset = 0;
63662306a36Sopenharmony_ci	resp->common.err = nfserr_toosmall;
63762306a36Sopenharmony_ci	resp->dirlist.len = starting_length;
63862306a36Sopenharmony_ci	return -EINVAL;
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci/*
64262306a36Sopenharmony_ci * XDR release functions
64362306a36Sopenharmony_ci */
64462306a36Sopenharmony_civoid nfssvc_release_attrstat(struct svc_rqst *rqstp)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	struct nfsd_attrstat *resp = rqstp->rq_resp;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	fh_put(&resp->fh);
64962306a36Sopenharmony_ci}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_civoid nfssvc_release_diropres(struct svc_rqst *rqstp)
65262306a36Sopenharmony_ci{
65362306a36Sopenharmony_ci	struct nfsd_diropres *resp = rqstp->rq_resp;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	fh_put(&resp->fh);
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_civoid nfssvc_release_readres(struct svc_rqst *rqstp)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	struct nfsd_readres *resp = rqstp->rq_resp;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	fh_put(&resp->fh);
66362306a36Sopenharmony_ci}
664