1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * XDR support for nfsd
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
61da177e4SLinus Torvalds */
71da177e4SLinus Torvalds
83dadecceSAl Viro #include "vfs.h"
99a74af21SBoaz Harrosh #include "xdr.h"
102e8138a2SJ. Bruce Fields #include "auth.h"
111da177e4SLinus Torvalds
121da177e4SLinus Torvalds /*
131da177e4SLinus Torvalds * Mapping of S_IF* types to NFS file types
141da177e4SLinus Torvalds */
1592b54a4fSChuck Lever static const u32 nfs_ftypes[] = {
161da177e4SLinus Torvalds NFNON, NFCHR, NFCHR, NFBAD,
171da177e4SLinus Torvalds NFDIR, NFBAD, NFBLK, NFBAD,
181da177e4SLinus Torvalds NFREG, NFBAD, NFLNK, NFBAD,
191da177e4SLinus Torvalds NFSOCK, NFBAD, NFLNK, NFBAD,
201da177e4SLinus Torvalds };
211da177e4SLinus Torvalds
221da177e4SLinus Torvalds
231da177e4SLinus Torvalds /*
24ebcd8e8bSChuck Lever * Basic NFSv2 data types (RFC 1094 Section 2.3)
251da177e4SLinus Torvalds */
26ebcd8e8bSChuck Lever
27f8cba473SChuck Lever /**
28f8cba473SChuck Lever * svcxdr_encode_stat - Encode an NFSv2 status code
29f8cba473SChuck Lever * @xdr: XDR stream
30f8cba473SChuck Lever * @status: status value to encode
31f8cba473SChuck Lever *
32f8cba473SChuck Lever * Return values:
33f8cba473SChuck Lever * %false: Send buffer space was exhausted
34f8cba473SChuck Lever * %true: Success
35f8cba473SChuck Lever */
36f8cba473SChuck Lever bool
svcxdr_encode_stat(struct xdr_stream * xdr,__be32 status)37a887eaedSChuck Lever svcxdr_encode_stat(struct xdr_stream *xdr, __be32 status)
38a887eaedSChuck Lever {
39a887eaedSChuck Lever __be32 *p;
40a887eaedSChuck Lever
41a887eaedSChuck Lever p = xdr_reserve_space(xdr, sizeof(status));
42a887eaedSChuck Lever if (!p)
43a887eaedSChuck Lever return false;
44a887eaedSChuck Lever *p = status;
45a887eaedSChuck Lever
46a887eaedSChuck Lever return true;
47a887eaedSChuck Lever }
48a887eaedSChuck Lever
49635a45d3SChuck Lever /**
50635a45d3SChuck Lever * svcxdr_decode_fhandle - Decode an NFSv2 file handle
51635a45d3SChuck Lever * @xdr: XDR stream positioned at an encoded NFSv2 FH
52635a45d3SChuck Lever * @fhp: OUT: filled-in server file handle
53635a45d3SChuck Lever *
54635a45d3SChuck Lever * Return values:
55635a45d3SChuck Lever * %false: The encoded file handle was not valid
56635a45d3SChuck Lever * %true: @fhp has been initialized
57635a45d3SChuck Lever */
58635a45d3SChuck Lever bool
svcxdr_decode_fhandle(struct xdr_stream * xdr,struct svc_fh * fhp)59ebcd8e8bSChuck Lever svcxdr_decode_fhandle(struct xdr_stream *xdr, struct svc_fh *fhp)
60ebcd8e8bSChuck Lever {
61ebcd8e8bSChuck Lever __be32 *p;
62ebcd8e8bSChuck Lever
63ebcd8e8bSChuck Lever p = xdr_inline_decode(xdr, NFS_FHSIZE);
64ebcd8e8bSChuck Lever if (!p)
65ebcd8e8bSChuck Lever return false;
66ebcd8e8bSChuck Lever fh_init(fhp, NFS_FHSIZE);
67d8b26071SNeilBrown memcpy(&fhp->fh_handle.fh_raw, p, NFS_FHSIZE);
68ebcd8e8bSChuck Lever fhp->fh_handle.fh_size = NFS_FHSIZE;
69ebcd8e8bSChuck Lever
70ebcd8e8bSChuck Lever return true;
71ebcd8e8bSChuck Lever }
72ebcd8e8bSChuck Lever
73e3b4ef22SChuck Lever static bool
svcxdr_encode_fhandle(struct xdr_stream * xdr,const struct svc_fh * fhp)74e3b4ef22SChuck Lever svcxdr_encode_fhandle(struct xdr_stream *xdr, const struct svc_fh *fhp)
751da177e4SLinus Torvalds {
76e3b4ef22SChuck Lever __be32 *p;
77e3b4ef22SChuck Lever
78e3b4ef22SChuck Lever p = xdr_reserve_space(xdr, NFS_FHSIZE);
79e3b4ef22SChuck Lever if (!p)
80e3b4ef22SChuck Lever return false;
81d8b26071SNeilBrown memcpy(p, &fhp->fh_handle.fh_raw, NFS_FHSIZE);
82e3b4ef22SChuck Lever
83e3b4ef22SChuck Lever return true;
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds
8692b54a4fSChuck Lever static __be32 *
encode_timeval(__be32 * p,const struct timespec64 * time)8792b54a4fSChuck Lever encode_timeval(__be32 *p, const struct timespec64 *time)
8892b54a4fSChuck Lever {
8992b54a4fSChuck Lever *p++ = cpu_to_be32((u32)time->tv_sec);
9092b54a4fSChuck Lever if (time->tv_nsec)
9192b54a4fSChuck Lever *p++ = cpu_to_be32(time->tv_nsec / NSEC_PER_USEC);
9292b54a4fSChuck Lever else
9392b54a4fSChuck Lever *p++ = xdr_zero;
9492b54a4fSChuck Lever return p;
9592b54a4fSChuck Lever }
9692b54a4fSChuck Lever
976d742c18SChuck Lever static bool
svcxdr_decode_filename(struct xdr_stream * xdr,char ** name,unsigned int * len)986d742c18SChuck Lever svcxdr_decode_filename(struct xdr_stream *xdr, char **name, unsigned int *len)
996d742c18SChuck Lever {
1006d742c18SChuck Lever u32 size, i;
1016d742c18SChuck Lever __be32 *p;
1026d742c18SChuck Lever char *c;
1036d742c18SChuck Lever
1046d742c18SChuck Lever if (xdr_stream_decode_u32(xdr, &size) < 0)
1056d742c18SChuck Lever return false;
1066d742c18SChuck Lever if (size == 0 || size > NFS_MAXNAMLEN)
1076d742c18SChuck Lever return false;
1086d742c18SChuck Lever p = xdr_inline_decode(xdr, size);
1096d742c18SChuck Lever if (!p)
1106d742c18SChuck Lever return false;
1116d742c18SChuck Lever
1126d742c18SChuck Lever *len = size;
1136d742c18SChuck Lever *name = (char *)p;
1146d742c18SChuck Lever for (i = 0, c = *name; i < size; i++, c++)
1156d742c18SChuck Lever if (*c == '\0' || *c == '/')
1166d742c18SChuck Lever return false;
1176d742c18SChuck Lever
1186d742c18SChuck Lever return true;
1196d742c18SChuck Lever }
1206d742c18SChuck Lever
1216d742c18SChuck Lever static bool
svcxdr_decode_diropargs(struct xdr_stream * xdr,struct svc_fh * fhp,char ** name,unsigned int * len)1226d742c18SChuck Lever svcxdr_decode_diropargs(struct xdr_stream *xdr, struct svc_fh *fhp,
1236d742c18SChuck Lever char **name, unsigned int *len)
1246d742c18SChuck Lever {
1256d742c18SChuck Lever return svcxdr_decode_fhandle(xdr, fhp) &&
1266d742c18SChuck Lever svcxdr_decode_filename(xdr, name, len);
1276d742c18SChuck Lever }
1286d742c18SChuck Lever
1292fdd6bd2SChuck Lever static bool
svcxdr_decode_sattr(struct svc_rqst * rqstp,struct xdr_stream * xdr,struct iattr * iap)1302fdd6bd2SChuck Lever svcxdr_decode_sattr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
1312fdd6bd2SChuck Lever struct iattr *iap)
1322fdd6bd2SChuck Lever {
1332fdd6bd2SChuck Lever u32 tmp1, tmp2;
1342fdd6bd2SChuck Lever __be32 *p;
1352fdd6bd2SChuck Lever
1362fdd6bd2SChuck Lever p = xdr_inline_decode(xdr, XDR_UNIT * 8);
1372fdd6bd2SChuck Lever if (!p)
1382fdd6bd2SChuck Lever return false;
1392fdd6bd2SChuck Lever
1402fdd6bd2SChuck Lever iap->ia_valid = 0;
1412fdd6bd2SChuck Lever
1422fdd6bd2SChuck Lever /*
1432fdd6bd2SChuck Lever * Some Sun clients put 0xffff in the mode field when they
1442fdd6bd2SChuck Lever * mean 0xffffffff.
1452fdd6bd2SChuck Lever */
1462fdd6bd2SChuck Lever tmp1 = be32_to_cpup(p++);
1472fdd6bd2SChuck Lever if (tmp1 != (u32)-1 && tmp1 != 0xffff) {
1482fdd6bd2SChuck Lever iap->ia_valid |= ATTR_MODE;
1492fdd6bd2SChuck Lever iap->ia_mode = tmp1;
1502fdd6bd2SChuck Lever }
1512fdd6bd2SChuck Lever
1522fdd6bd2SChuck Lever tmp1 = be32_to_cpup(p++);
1532fdd6bd2SChuck Lever if (tmp1 != (u32)-1) {
1542fdd6bd2SChuck Lever iap->ia_uid = make_kuid(nfsd_user_namespace(rqstp), tmp1);
1552fdd6bd2SChuck Lever if (uid_valid(iap->ia_uid))
1562fdd6bd2SChuck Lever iap->ia_valid |= ATTR_UID;
1572fdd6bd2SChuck Lever }
1582fdd6bd2SChuck Lever
1592fdd6bd2SChuck Lever tmp1 = be32_to_cpup(p++);
1602fdd6bd2SChuck Lever if (tmp1 != (u32)-1) {
1612fdd6bd2SChuck Lever iap->ia_gid = make_kgid(nfsd_user_namespace(rqstp), tmp1);
1622fdd6bd2SChuck Lever if (gid_valid(iap->ia_gid))
1632fdd6bd2SChuck Lever iap->ia_valid |= ATTR_GID;
1642fdd6bd2SChuck Lever }
1652fdd6bd2SChuck Lever
1662fdd6bd2SChuck Lever tmp1 = be32_to_cpup(p++);
1672fdd6bd2SChuck Lever if (tmp1 != (u32)-1) {
1682fdd6bd2SChuck Lever iap->ia_valid |= ATTR_SIZE;
1692fdd6bd2SChuck Lever iap->ia_size = tmp1;
1702fdd6bd2SChuck Lever }
1712fdd6bd2SChuck Lever
1722fdd6bd2SChuck Lever tmp1 = be32_to_cpup(p++);
1732fdd6bd2SChuck Lever tmp2 = be32_to_cpup(p++);
1742fdd6bd2SChuck Lever if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
1752fdd6bd2SChuck Lever iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
1762fdd6bd2SChuck Lever iap->ia_atime.tv_sec = tmp1;
1772fdd6bd2SChuck Lever iap->ia_atime.tv_nsec = tmp2 * NSEC_PER_USEC;
1782fdd6bd2SChuck Lever }
1792fdd6bd2SChuck Lever
1802fdd6bd2SChuck Lever tmp1 = be32_to_cpup(p++);
1812fdd6bd2SChuck Lever tmp2 = be32_to_cpup(p++);
1822fdd6bd2SChuck Lever if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
1832fdd6bd2SChuck Lever iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
1842fdd6bd2SChuck Lever iap->ia_mtime.tv_sec = tmp1;
1852fdd6bd2SChuck Lever iap->ia_mtime.tv_nsec = tmp2 * NSEC_PER_USEC;
1862fdd6bd2SChuck Lever /*
1872fdd6bd2SChuck Lever * Passing the invalid value useconds=1000000 for mtime
1882fdd6bd2SChuck Lever * is a Sun convention for "set both mtime and atime to
1892fdd6bd2SChuck Lever * current server time". It's needed to make permissions
1902fdd6bd2SChuck Lever * checks for the "touch" program across v2 mounts to
1912fdd6bd2SChuck Lever * Solaris and Irix boxes work correctly. See description of
1922fdd6bd2SChuck Lever * sattr in section 6.1 of "NFS Illustrated" by
1932fdd6bd2SChuck Lever * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
1942fdd6bd2SChuck Lever */
1952fdd6bd2SChuck Lever if (tmp2 == 1000000)
1962fdd6bd2SChuck Lever iap->ia_valid &= ~(ATTR_ATIME_SET|ATTR_MTIME_SET);
1972fdd6bd2SChuck Lever }
1982fdd6bd2SChuck Lever
1992fdd6bd2SChuck Lever return true;
2002fdd6bd2SChuck Lever }
2012fdd6bd2SChuck Lever
202f8cba473SChuck Lever /**
203f8cba473SChuck Lever * svcxdr_encode_fattr - Encode NFSv2 file attributes
204f8cba473SChuck Lever * @rqstp: Context of a completed RPC transaction
205f8cba473SChuck Lever * @xdr: XDR stream
206f8cba473SChuck Lever * @fhp: File handle to encode
207f8cba473SChuck Lever * @stat: Attributes to encode
208f8cba473SChuck Lever *
209f8cba473SChuck Lever * Return values:
210f8cba473SChuck Lever * %false: Send buffer space was exhausted
211f8cba473SChuck Lever * %true: Success
212f8cba473SChuck Lever */
213f8cba473SChuck Lever bool
svcxdr_encode_fattr(struct svc_rqst * rqstp,struct xdr_stream * xdr,const struct svc_fh * fhp,const struct kstat * stat)21492b54a4fSChuck Lever svcxdr_encode_fattr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
21592b54a4fSChuck Lever const struct svc_fh *fhp, const struct kstat *stat)
21692b54a4fSChuck Lever {
21792b54a4fSChuck Lever struct user_namespace *userns = nfsd_user_namespace(rqstp);
21892b54a4fSChuck Lever struct dentry *dentry = fhp->fh_dentry;
21992b54a4fSChuck Lever int type = stat->mode & S_IFMT;
22092b54a4fSChuck Lever struct timespec64 time;
22192b54a4fSChuck Lever __be32 *p;
22292b54a4fSChuck Lever u32 fsid;
22392b54a4fSChuck Lever
22492b54a4fSChuck Lever p = xdr_reserve_space(xdr, XDR_UNIT * 17);
22592b54a4fSChuck Lever if (!p)
226e3b4ef22SChuck Lever return false;
22792b54a4fSChuck Lever
22892b54a4fSChuck Lever *p++ = cpu_to_be32(nfs_ftypes[type >> 12]);
22992b54a4fSChuck Lever *p++ = cpu_to_be32((u32)stat->mode);
23092b54a4fSChuck Lever *p++ = cpu_to_be32((u32)stat->nlink);
23192b54a4fSChuck Lever *p++ = cpu_to_be32((u32)from_kuid_munged(userns, stat->uid));
23292b54a4fSChuck Lever *p++ = cpu_to_be32((u32)from_kgid_munged(userns, stat->gid));
23392b54a4fSChuck Lever
23492b54a4fSChuck Lever if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN)
23592b54a4fSChuck Lever *p++ = cpu_to_be32(NFS_MAXPATHLEN);
23692b54a4fSChuck Lever else
23792b54a4fSChuck Lever *p++ = cpu_to_be32((u32) stat->size);
23892b54a4fSChuck Lever *p++ = cpu_to_be32((u32) stat->blksize);
23992b54a4fSChuck Lever if (S_ISCHR(type) || S_ISBLK(type))
24092b54a4fSChuck Lever *p++ = cpu_to_be32(new_encode_dev(stat->rdev));
24192b54a4fSChuck Lever else
24292b54a4fSChuck Lever *p++ = cpu_to_be32(0xffffffff);
24392b54a4fSChuck Lever *p++ = cpu_to_be32((u32)stat->blocks);
24492b54a4fSChuck Lever
24592b54a4fSChuck Lever switch (fsid_source(fhp)) {
24692b54a4fSChuck Lever case FSIDSOURCE_FSID:
24792b54a4fSChuck Lever fsid = (u32)fhp->fh_export->ex_fsid;
24892b54a4fSChuck Lever break;
24992b54a4fSChuck Lever case FSIDSOURCE_UUID:
25092b54a4fSChuck Lever fsid = ((u32 *)fhp->fh_export->ex_uuid)[0];
25192b54a4fSChuck Lever fsid ^= ((u32 *)fhp->fh_export->ex_uuid)[1];
25292b54a4fSChuck Lever fsid ^= ((u32 *)fhp->fh_export->ex_uuid)[2];
25392b54a4fSChuck Lever fsid ^= ((u32 *)fhp->fh_export->ex_uuid)[3];
25492b54a4fSChuck Lever break;
25592b54a4fSChuck Lever default:
25692b54a4fSChuck Lever fsid = new_encode_dev(stat->dev);
25792b54a4fSChuck Lever break;
25892b54a4fSChuck Lever }
25992b54a4fSChuck Lever *p++ = cpu_to_be32(fsid);
26092b54a4fSChuck Lever
26192b54a4fSChuck Lever *p++ = cpu_to_be32((u32)stat->ino);
26292b54a4fSChuck Lever p = encode_timeval(p, &stat->atime);
26392b54a4fSChuck Lever time = stat->mtime;
26492b54a4fSChuck Lever lease_get_mtime(d_inode(dentry), &time);
26592b54a4fSChuck Lever p = encode_timeval(p, &time);
26692b54a4fSChuck Lever encode_timeval(p, &stat->ctime);
26792b54a4fSChuck Lever
268e3b4ef22SChuck Lever return true;
26992b54a4fSChuck Lever }
27092b54a4fSChuck Lever
2711da177e4SLinus Torvalds /*
2721da177e4SLinus Torvalds * XDR decode functions
2731da177e4SLinus Torvalds */
2741da177e4SLinus Torvalds
275c44b31c2SChuck Lever bool
nfssvc_decode_fhandleargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)27616c66364SChuck Lever nfssvc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
2771da177e4SLinus Torvalds {
278026fec7eSChristoph Hellwig struct nfsd_fhandle *args = rqstp->rq_argp;
279026fec7eSChristoph Hellwig
280ebcd8e8bSChuck Lever return svcxdr_decode_fhandle(xdr, &args->fh);
2811da177e4SLinus Torvalds }
2821da177e4SLinus Torvalds
283c44b31c2SChuck Lever bool
nfssvc_decode_sattrargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)28416c66364SChuck Lever nfssvc_decode_sattrargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
2851da177e4SLinus Torvalds {
286026fec7eSChristoph Hellwig struct nfsd_sattrargs *args = rqstp->rq_argp;
287026fec7eSChristoph Hellwig
2882fdd6bd2SChuck Lever return svcxdr_decode_fhandle(xdr, &args->fh) &&
2892fdd6bd2SChuck Lever svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
2901da177e4SLinus Torvalds }
2911da177e4SLinus Torvalds
292c44b31c2SChuck Lever bool
nfssvc_decode_diropargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)29316c66364SChuck Lever nfssvc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
2941da177e4SLinus Torvalds {
295026fec7eSChristoph Hellwig struct nfsd_diropargs *args = rqstp->rq_argp;
296026fec7eSChristoph Hellwig
2976d742c18SChuck Lever return svcxdr_decode_diropargs(xdr, &args->fh, &args->name, &args->len);
2981da177e4SLinus Torvalds }
2991da177e4SLinus Torvalds
300c44b31c2SChuck Lever bool
nfssvc_decode_readargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)30116c66364SChuck Lever nfssvc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
3021da177e4SLinus Torvalds {
303026fec7eSChristoph Hellwig struct nfsd_readargs *args = rqstp->rq_argp;
3048c293ef9SChuck Lever u32 totalcount;
3058c293ef9SChuck Lever
3068c293ef9SChuck Lever if (!svcxdr_decode_fhandle(xdr, &args->fh))
307c44b31c2SChuck Lever return false;
3088c293ef9SChuck Lever if (xdr_stream_decode_u32(xdr, &args->offset) < 0)
309c44b31c2SChuck Lever return false;
3108c293ef9SChuck Lever if (xdr_stream_decode_u32(xdr, &args->count) < 0)
311c44b31c2SChuck Lever return false;
3128c293ef9SChuck Lever /* totalcount is ignored */
3138c293ef9SChuck Lever if (xdr_stream_decode_u32(xdr, &totalcount) < 0)
314c44b31c2SChuck Lever return false;
3151da177e4SLinus Torvalds
316c44b31c2SChuck Lever return true;
3171da177e4SLinus Torvalds }
3181da177e4SLinus Torvalds
319c44b31c2SChuck Lever bool
nfssvc_decode_writeargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)32016c66364SChuck Lever nfssvc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
3211da177e4SLinus Torvalds {
322026fec7eSChristoph Hellwig struct nfsd_writeargs *args = rqstp->rq_argp;
323a51b5b73SChuck Lever u32 beginoffset, totalcount;
324f34b9568SPeter Staubach
325a51b5b73SChuck Lever if (!svcxdr_decode_fhandle(xdr, &args->fh))
326c44b31c2SChuck Lever return false;
327a51b5b73SChuck Lever /* beginoffset is ignored */
328a51b5b73SChuck Lever if (xdr_stream_decode_u32(xdr, &beginoffset) < 0)
329c44b31c2SChuck Lever return false;
330a51b5b73SChuck Lever if (xdr_stream_decode_u32(xdr, &args->offset) < 0)
331c44b31c2SChuck Lever return false;
332a51b5b73SChuck Lever /* totalcount is ignored */
333a51b5b73SChuck Lever if (xdr_stream_decode_u32(xdr, &totalcount) < 0)
334c44b31c2SChuck Lever return false;
3351da177e4SLinus Torvalds
336a51b5b73SChuck Lever /* opaque data */
337a51b5b73SChuck Lever if (xdr_stream_decode_u32(xdr, &args->len) < 0)
338c44b31c2SChuck Lever return false;
339a51b5b73SChuck Lever if (args->len > NFSSVC_MAXBLKSIZE_V2)
340c44b31c2SChuck Lever return false;
341f34b9568SPeter Staubach
342d4da5baaSChuck Lever return xdr_stream_subsegment(xdr, &args->payload, args->len);
3431da177e4SLinus Torvalds }
3441da177e4SLinus Torvalds
345c44b31c2SChuck Lever bool
nfssvc_decode_createargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)34616c66364SChuck Lever nfssvc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
3471da177e4SLinus Torvalds {
348026fec7eSChristoph Hellwig struct nfsd_createargs *args = rqstp->rq_argp;
349026fec7eSChristoph Hellwig
3507dcf65b9SChuck Lever return svcxdr_decode_diropargs(xdr, &args->fh,
3517dcf65b9SChuck Lever &args->name, &args->len) &&
3527dcf65b9SChuck Lever svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
3531da177e4SLinus Torvalds }
3541da177e4SLinus Torvalds
355c44b31c2SChuck Lever bool
nfssvc_decode_renameargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)35616c66364SChuck Lever nfssvc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
3571da177e4SLinus Torvalds {
358026fec7eSChristoph Hellwig struct nfsd_renameargs *args = rqstp->rq_argp;
359026fec7eSChristoph Hellwig
36062aa557eSChuck Lever return svcxdr_decode_diropargs(xdr, &args->ffh,
36162aa557eSChuck Lever &args->fname, &args->flen) &&
36262aa557eSChuck Lever svcxdr_decode_diropargs(xdr, &args->tfh,
36362aa557eSChuck Lever &args->tname, &args->tlen);
3641da177e4SLinus Torvalds }
3651da177e4SLinus Torvalds
366c44b31c2SChuck Lever bool
nfssvc_decode_linkargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)36716c66364SChuck Lever nfssvc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
3681da177e4SLinus Torvalds {
369026fec7eSChristoph Hellwig struct nfsd_linkargs *args = rqstp->rq_argp;
370026fec7eSChristoph Hellwig
37177edcdf9SChuck Lever return svcxdr_decode_fhandle(xdr, &args->ffh) &&
37277edcdf9SChuck Lever svcxdr_decode_diropargs(xdr, &args->tfh,
37377edcdf9SChuck Lever &args->tname, &args->tlen);
3741da177e4SLinus Torvalds }
3751da177e4SLinus Torvalds
376c44b31c2SChuck Lever bool
nfssvc_decode_symlinkargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)37716c66364SChuck Lever nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
3781da177e4SLinus Torvalds {
379026fec7eSChristoph Hellwig struct nfsd_symlinkargs *args = rqstp->rq_argp;
38009f75a53SChuck Lever struct kvec *head = rqstp->rq_arg.head;
381026fec7eSChristoph Hellwig
38209f75a53SChuck Lever if (!svcxdr_decode_diropargs(xdr, &args->ffh, &args->fname, &args->flen))
383c44b31c2SChuck Lever return false;
38409f75a53SChuck Lever if (xdr_stream_decode_u32(xdr, &args->tlen) < 0)
385c44b31c2SChuck Lever return false;
38638a70315SChuck Lever if (args->tlen == 0)
387c44b31c2SChuck Lever return false;
38838a70315SChuck Lever
38909f75a53SChuck Lever args->first.iov_len = head->iov_len - xdr_stream_pos(xdr);
39009f75a53SChuck Lever args->first.iov_base = xdr_inline_decode(xdr, args->tlen);
39109f75a53SChuck Lever if (!args->first.iov_base)
392c44b31c2SChuck Lever return false;
39309f75a53SChuck Lever return svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
3941da177e4SLinus Torvalds }
3951da177e4SLinus Torvalds
396c44b31c2SChuck Lever bool
nfssvc_decode_readdirargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)39716c66364SChuck Lever nfssvc_decode_readdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
3981da177e4SLinus Torvalds {
399026fec7eSChristoph Hellwig struct nfsd_readdirargs *args = rqstp->rq_argp;
400026fec7eSChristoph Hellwig
4018688361aSChuck Lever if (!svcxdr_decode_fhandle(xdr, &args->fh))
402c44b31c2SChuck Lever return false;
4038688361aSChuck Lever if (xdr_stream_decode_u32(xdr, &args->cookie) < 0)
404c44b31c2SChuck Lever return false;
4058688361aSChuck Lever if (xdr_stream_decode_u32(xdr, &args->count) < 0)
406c44b31c2SChuck Lever return false;
4071da177e4SLinus Torvalds
408c44b31c2SChuck Lever return true;
4091da177e4SLinus Torvalds }
4101da177e4SLinus Torvalds
4111da177e4SLinus Torvalds /*
4121da177e4SLinus Torvalds * XDR encode functions
4131da177e4SLinus Torvalds */
4141da177e4SLinus Torvalds
415130e2054SChuck Lever bool
nfssvc_encode_statres(struct svc_rqst * rqstp,struct xdr_stream * xdr)416fda49441SChuck Lever nfssvc_encode_statres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
417cc028a10SChuck Lever {
418cc028a10SChuck Lever struct nfsd_stat *resp = rqstp->rq_resp;
419cc028a10SChuck Lever
420a887eaedSChuck Lever return svcxdr_encode_stat(xdr, resp->status);
421cc028a10SChuck Lever }
422cc028a10SChuck Lever
423130e2054SChuck Lever bool
nfssvc_encode_attrstatres(struct svc_rqst * rqstp,struct xdr_stream * xdr)424fda49441SChuck Lever nfssvc_encode_attrstatres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
4251da177e4SLinus Torvalds {
42663f8de37SChristoph Hellwig struct nfsd_attrstat *resp = rqstp->rq_resp;
42763f8de37SChristoph Hellwig
42892b54a4fSChuck Lever if (!svcxdr_encode_stat(xdr, resp->status))
429130e2054SChuck Lever return false;
43092b54a4fSChuck Lever switch (resp->status) {
43192b54a4fSChuck Lever case nfs_ok:
43292b54a4fSChuck Lever if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
433130e2054SChuck Lever return false;
43492b54a4fSChuck Lever break;
43592b54a4fSChuck Lever }
43692b54a4fSChuck Lever
437130e2054SChuck Lever return true;
4381da177e4SLinus Torvalds }
4391da177e4SLinus Torvalds
440130e2054SChuck Lever bool
nfssvc_encode_diropres(struct svc_rqst * rqstp,struct xdr_stream * xdr)441fda49441SChuck Lever nfssvc_encode_diropres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
4421da177e4SLinus Torvalds {
44363f8de37SChristoph Hellwig struct nfsd_diropres *resp = rqstp->rq_resp;
44463f8de37SChristoph Hellwig
445e3b4ef22SChuck Lever if (!svcxdr_encode_stat(xdr, resp->status))
446130e2054SChuck Lever return false;
447e3b4ef22SChuck Lever switch (resp->status) {
448e3b4ef22SChuck Lever case nfs_ok:
449e3b4ef22SChuck Lever if (!svcxdr_encode_fhandle(xdr, &resp->fh))
450130e2054SChuck Lever return false;
451e3b4ef22SChuck Lever if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
452130e2054SChuck Lever return false;
453e3b4ef22SChuck Lever break;
454e3b4ef22SChuck Lever }
455e3b4ef22SChuck Lever
456130e2054SChuck Lever return true;
4571da177e4SLinus Torvalds }
4581da177e4SLinus Torvalds
459130e2054SChuck Lever bool
nfssvc_encode_readlinkres(struct svc_rqst * rqstp,struct xdr_stream * xdr)460fda49441SChuck Lever nfssvc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
4611da177e4SLinus Torvalds {
46263f8de37SChristoph Hellwig struct nfsd_readlinkres *resp = rqstp->rq_resp;
46376e5492bSChuck Lever struct kvec *head = rqstp->rq_res.head;
46463f8de37SChristoph Hellwig
465d9014b0fSChuck Lever if (!svcxdr_encode_stat(xdr, resp->status))
466130e2054SChuck Lever return false;
467d9014b0fSChuck Lever switch (resp->status) {
468d9014b0fSChuck Lever case nfs_ok:
469d9014b0fSChuck Lever if (xdr_stream_encode_u32(xdr, resp->len) < 0)
470130e2054SChuck Lever return false;
471*82078b98SChuck Lever svcxdr_encode_opaque_pages(rqstp, xdr, &resp->page, 0,
472*82078b98SChuck Lever resp->len);
473d9014b0fSChuck Lever if (svc_encode_result_payload(rqstp, head->iov_len, resp->len) < 0)
474130e2054SChuck Lever return false;
475d9014b0fSChuck Lever break;
476d9014b0fSChuck Lever }
477d9014b0fSChuck Lever
478130e2054SChuck Lever return true;
4791da177e4SLinus Torvalds }
4801da177e4SLinus Torvalds
481130e2054SChuck Lever bool
nfssvc_encode_readres(struct svc_rqst * rqstp,struct xdr_stream * xdr)482fda49441SChuck Lever nfssvc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
4831da177e4SLinus Torvalds {
48463f8de37SChristoph Hellwig struct nfsd_readres *resp = rqstp->rq_resp;
48576e5492bSChuck Lever struct kvec *head = rqstp->rq_res.head;
48663f8de37SChristoph Hellwig
487a6f8d9dcSChuck Lever if (!svcxdr_encode_stat(xdr, resp->status))
488130e2054SChuck Lever return false;
489a6f8d9dcSChuck Lever switch (resp->status) {
490a6f8d9dcSChuck Lever case nfs_ok:
491a6f8d9dcSChuck Lever if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
492130e2054SChuck Lever return false;
493a6f8d9dcSChuck Lever if (xdr_stream_encode_u32(xdr, resp->count) < 0)
494130e2054SChuck Lever return false;
495*82078b98SChuck Lever svcxdr_encode_opaque_pages(rqstp, xdr, resp->pages,
496*82078b98SChuck Lever rqstp->rq_res.page_base,
497a6f8d9dcSChuck Lever resp->count);
498a6f8d9dcSChuck Lever if (svc_encode_result_payload(rqstp, head->iov_len, resp->count) < 0)
499130e2054SChuck Lever return false;
500a6f8d9dcSChuck Lever break;
501a6f8d9dcSChuck Lever }
502a6f8d9dcSChuck Lever
503130e2054SChuck Lever return true;
5041da177e4SLinus Torvalds }
5051da177e4SLinus Torvalds
506130e2054SChuck Lever bool
nfssvc_encode_readdirres(struct svc_rqst * rqstp,struct xdr_stream * xdr)507fda49441SChuck Lever nfssvc_encode_readdirres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
5081da177e4SLinus Torvalds {
50963f8de37SChristoph Hellwig struct nfsd_readdirres *resp = rqstp->rq_resp;
510f5dcccd6SChuck Lever struct xdr_buf *dirlist = &resp->dirlist;
51163f8de37SChristoph Hellwig
51294c8f8c6SChuck Lever if (!svcxdr_encode_stat(xdr, resp->status))
513130e2054SChuck Lever return false;
51494c8f8c6SChuck Lever switch (resp->status) {
51594c8f8c6SChuck Lever case nfs_ok:
516*82078b98SChuck Lever svcxdr_encode_opaque_pages(rqstp, xdr, dirlist->pages, 0,
517*82078b98SChuck Lever dirlist->len);
51894c8f8c6SChuck Lever /* no more entries */
51994c8f8c6SChuck Lever if (xdr_stream_encode_item_absent(xdr) < 0)
520130e2054SChuck Lever return false;
52194c8f8c6SChuck Lever if (xdr_stream_encode_bool(xdr, resp->common.err == nfserr_eof) < 0)
522130e2054SChuck Lever return false;
52394c8f8c6SChuck Lever break;
52494c8f8c6SChuck Lever }
5251da177e4SLinus Torvalds
526130e2054SChuck Lever return true;
5271da177e4SLinus Torvalds }
5281da177e4SLinus Torvalds
529130e2054SChuck Lever bool
nfssvc_encode_statfsres(struct svc_rqst * rqstp,struct xdr_stream * xdr)530fda49441SChuck Lever nfssvc_encode_statfsres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
5311da177e4SLinus Torvalds {
53263f8de37SChristoph Hellwig struct nfsd_statfsres *resp = rqstp->rq_resp;
5331da177e4SLinus Torvalds struct kstatfs *stat = &resp->stats;
534fda49441SChuck Lever __be32 *p;
5351da177e4SLinus Torvalds
536bf15229fSChuck Lever if (!svcxdr_encode_stat(xdr, resp->status))
537130e2054SChuck Lever return false;
538bf15229fSChuck Lever switch (resp->status) {
539bf15229fSChuck Lever case nfs_ok:
540bf15229fSChuck Lever p = xdr_reserve_space(xdr, XDR_UNIT * 5);
541bf15229fSChuck Lever if (!p)
542130e2054SChuck Lever return false;
543bf15229fSChuck Lever *p++ = cpu_to_be32(NFSSVC_MAXBLKSIZE_V2);
544bf15229fSChuck Lever *p++ = cpu_to_be32(stat->f_bsize);
545bf15229fSChuck Lever *p++ = cpu_to_be32(stat->f_blocks);
546bf15229fSChuck Lever *p++ = cpu_to_be32(stat->f_bfree);
547bf15229fSChuck Lever *p = cpu_to_be32(stat->f_bavail);
548bf15229fSChuck Lever break;
549bf15229fSChuck Lever }
550f0af2210SChuck Lever
551130e2054SChuck Lever return true;
5521da177e4SLinus Torvalds }
5531da177e4SLinus Torvalds
554d5253200SChuck Lever /**
555d5253200SChuck Lever * nfssvc_encode_nfscookie - Encode a directory offset cookie
556d5253200SChuck Lever * @resp: readdir result context
557d5253200SChuck Lever * @offset: offset cookie to encode
558d5253200SChuck Lever *
559f5dcccd6SChuck Lever * The buffer space for the offset cookie has already been reserved
560f5dcccd6SChuck Lever * by svcxdr_encode_entry_common().
561d5253200SChuck Lever */
nfssvc_encode_nfscookie(struct nfsd_readdirres * resp,u32 offset)562d5253200SChuck Lever void nfssvc_encode_nfscookie(struct nfsd_readdirres *resp, u32 offset)
563d5253200SChuck Lever {
564f5dcccd6SChuck Lever __be32 cookie = cpu_to_be32(offset);
565f5dcccd6SChuck Lever
566f5dcccd6SChuck Lever if (!resp->cookie_offset)
567d5253200SChuck Lever return;
568d5253200SChuck Lever
569f5dcccd6SChuck Lever write_bytes_to_xdr_buf(&resp->dirlist, resp->cookie_offset, &cookie,
570f5dcccd6SChuck Lever sizeof(cookie));
571f5dcccd6SChuck Lever resp->cookie_offset = 0;
572f5dcccd6SChuck Lever }
573f5dcccd6SChuck Lever
574f5dcccd6SChuck Lever static bool
svcxdr_encode_entry_common(struct nfsd_readdirres * resp,const char * name,int namlen,loff_t offset,u64 ino)575f5dcccd6SChuck Lever svcxdr_encode_entry_common(struct nfsd_readdirres *resp, const char *name,
576f5dcccd6SChuck Lever int namlen, loff_t offset, u64 ino)
577f5dcccd6SChuck Lever {
578f5dcccd6SChuck Lever struct xdr_buf *dirlist = &resp->dirlist;
579f5dcccd6SChuck Lever struct xdr_stream *xdr = &resp->xdr;
580f5dcccd6SChuck Lever
581f5dcccd6SChuck Lever if (xdr_stream_encode_item_present(xdr) < 0)
582f5dcccd6SChuck Lever return false;
583f5dcccd6SChuck Lever /* fileid */
584f5dcccd6SChuck Lever if (xdr_stream_encode_u32(xdr, (u32)ino) < 0)
585f5dcccd6SChuck Lever return false;
586f5dcccd6SChuck Lever /* name */
587f5dcccd6SChuck Lever if (xdr_stream_encode_opaque(xdr, name, min(namlen, NFS2_MAXNAMLEN)) < 0)
588f5dcccd6SChuck Lever return false;
589f5dcccd6SChuck Lever /* cookie */
590f5dcccd6SChuck Lever resp->cookie_offset = dirlist->len;
591f5dcccd6SChuck Lever if (xdr_stream_encode_u32(xdr, ~0U) < 0)
592f5dcccd6SChuck Lever return false;
593f5dcccd6SChuck Lever
594f5dcccd6SChuck Lever return true;
595f5dcccd6SChuck Lever }
596f5dcccd6SChuck Lever
597f5dcccd6SChuck Lever /**
5988a2cf9f5SChuck Lever * nfssvc_encode_entry - encode one NFSv2 READDIR entry
599f5dcccd6SChuck Lever * @data: directory context
600f5dcccd6SChuck Lever * @name: name of the object to be encoded
601f5dcccd6SChuck Lever * @namlen: length of that name, in bytes
602f5dcccd6SChuck Lever * @offset: the offset of the previous entry
603f5dcccd6SChuck Lever * @ino: the fileid of this entry
604f5dcccd6SChuck Lever * @d_type: unused
605f5dcccd6SChuck Lever *
606f5dcccd6SChuck Lever * Return values:
607f5dcccd6SChuck Lever * %0: Entry was successfully encoded.
608f5dcccd6SChuck Lever * %-EINVAL: An encoding problem occured, secondary status code in resp->common.err
609f5dcccd6SChuck Lever *
610f5dcccd6SChuck Lever * On exit, the following fields are updated:
611f5dcccd6SChuck Lever * - resp->xdr
612f5dcccd6SChuck Lever * - resp->common.err
613f5dcccd6SChuck Lever * - resp->cookie_offset
614f5dcccd6SChuck Lever */
nfssvc_encode_entry(void * data,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)6158a2cf9f5SChuck Lever int nfssvc_encode_entry(void *data, const char *name, int namlen,
616f5dcccd6SChuck Lever loff_t offset, u64 ino, unsigned int d_type)
617f5dcccd6SChuck Lever {
618f5dcccd6SChuck Lever struct readdir_cd *ccd = data;
619f5dcccd6SChuck Lever struct nfsd_readdirres *resp = container_of(ccd,
620f5dcccd6SChuck Lever struct nfsd_readdirres,
621f5dcccd6SChuck Lever common);
622f5dcccd6SChuck Lever unsigned int starting_length = resp->dirlist.len;
623f5dcccd6SChuck Lever
624f5dcccd6SChuck Lever /* The offset cookie for the previous entry */
625f5dcccd6SChuck Lever nfssvc_encode_nfscookie(resp, offset);
626f5dcccd6SChuck Lever
627f5dcccd6SChuck Lever if (!svcxdr_encode_entry_common(resp, name, namlen, offset, ino))
628f5dcccd6SChuck Lever goto out_toosmall;
629f5dcccd6SChuck Lever
630f5dcccd6SChuck Lever xdr_commit_encode(&resp->xdr);
631f5dcccd6SChuck Lever resp->common.err = nfs_ok;
632f5dcccd6SChuck Lever return 0;
633f5dcccd6SChuck Lever
634f5dcccd6SChuck Lever out_toosmall:
635f5dcccd6SChuck Lever resp->cookie_offset = 0;
636f5dcccd6SChuck Lever resp->common.err = nfserr_toosmall;
637f5dcccd6SChuck Lever resp->dirlist.len = starting_length;
638f5dcccd6SChuck Lever return -EINVAL;
639d5253200SChuck Lever }
640d5253200SChuck Lever
6411da177e4SLinus Torvalds /*
6421da177e4SLinus Torvalds * XDR release functions
6431da177e4SLinus Torvalds */
nfssvc_release_attrstat(struct svc_rqst * rqstp)6441841b9b6SChuck Lever void nfssvc_release_attrstat(struct svc_rqst *rqstp)
6451da177e4SLinus Torvalds {
6461841b9b6SChuck Lever struct nfsd_attrstat *resp = rqstp->rq_resp;
6471841b9b6SChuck Lever
6481841b9b6SChuck Lever fh_put(&resp->fh);
6491841b9b6SChuck Lever }
6501841b9b6SChuck Lever
nfssvc_release_diropres(struct svc_rqst * rqstp)6511841b9b6SChuck Lever void nfssvc_release_diropres(struct svc_rqst *rqstp)
6521841b9b6SChuck Lever {
6531841b9b6SChuck Lever struct nfsd_diropres *resp = rqstp->rq_resp;
6541841b9b6SChuck Lever
6551841b9b6SChuck Lever fh_put(&resp->fh);
6561841b9b6SChuck Lever }
6571841b9b6SChuck Lever
nfssvc_release_readres(struct svc_rqst * rqstp)6581841b9b6SChuck Lever void nfssvc_release_readres(struct svc_rqst *rqstp)
6591841b9b6SChuck Lever {
6601841b9b6SChuck Lever struct nfsd_readres *resp = rqstp->rq_resp;
6618537488bSChristoph Hellwig
6621da177e4SLinus Torvalds fh_put(&resp->fh);
6631da177e4SLinus Torvalds }
664