11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds * Server-side XDR for NFSv4
31da177e4SLinus Torvalds *
41da177e4SLinus Torvalds * Copyright (c) 2002 The Regents of the University of Michigan.
51da177e4SLinus Torvalds * All rights reserved.
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * Kendrick Smith <kmsmith@umich.edu>
81da177e4SLinus Torvalds * Andy Adamson <andros@umich.edu>
91da177e4SLinus Torvalds *
101da177e4SLinus Torvalds * Redistribution and use in source and binary forms, with or without
111da177e4SLinus Torvalds * modification, are permitted provided that the following conditions
121da177e4SLinus Torvalds * are met:
131da177e4SLinus Torvalds *
141da177e4SLinus Torvalds * 1. Redistributions of source code must retain the above copyright
151da177e4SLinus Torvalds * notice, this list of conditions and the following disclaimer.
161da177e4SLinus Torvalds * 2. Redistributions in binary form must reproduce the above copyright
171da177e4SLinus Torvalds * notice, this list of conditions and the following disclaimer in the
181da177e4SLinus Torvalds * documentation and/or other materials provided with the distribution.
191da177e4SLinus Torvalds * 3. Neither the name of the University nor the names of its
201da177e4SLinus Torvalds * contributors may be used to endorse or promote products derived
211da177e4SLinus Torvalds * from this software without specific prior written permission.
221da177e4SLinus Torvalds *
231da177e4SLinus Torvalds * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
241da177e4SLinus Torvalds * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
251da177e4SLinus Torvalds * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
261da177e4SLinus Torvalds * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
271da177e4SLinus Torvalds * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
281da177e4SLinus Torvalds * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
291da177e4SLinus Torvalds * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
301da177e4SLinus Torvalds * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
311da177e4SLinus Torvalds * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
321da177e4SLinus Torvalds * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
331da177e4SLinus Torvalds * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
341da177e4SLinus Torvalds */
351da177e4SLinus Torvalds
3696bcad50SChristoph Hellwig #include <linux/file.h>
375a0e3ad6STejun Heo #include <linux/slab.h>
381da177e4SLinus Torvalds #include <linux/namei.h>
39341eb184SBoaz Harrosh #include <linux/statfs.h>
400733d213SAndy Adamson #include <linux/utsname.h>
4117456804SBryan Schumaker #include <linux/pagemap.h>
424796f457SJ. Bruce Fields #include <linux/sunrpc/svcauth_gss.h>
4384e1b21dSOlga Kornievskaia #include <linux/sunrpc/addr.h>
4423e50fe3SFrank van der Linden #include <linux/xattr.h>
4580e591ceSChuck Lever #include <linux/vmalloc.h>
4680e591ceSChuck Lever
4723e50fe3SFrank van der Linden #include <uapi/linux/xattr.h>
489a74af21SBoaz Harrosh
492ca72e17SJ. Bruce Fields #include "idmap.h"
502ca72e17SJ. Bruce Fields #include "acl.h"
519a74af21SBoaz Harrosh #include "xdr4.h"
520a3adadeSJ. Bruce Fields #include "vfs.h"
5317456804SBryan Schumaker #include "state.h"
541091006cSJ. Bruce Fields #include "cache.h"
553d733711SStanislav Kinsbursky #include "netns.h"
569cf514ccSChristoph Hellwig #include "pnfs.h"
575c4583b2SJeff Layton #include "filecache.h"
582ca72e17SJ. Bruce Fields
5908281341SChuck Lever #include "trace.h"
6008281341SChuck Lever
6118032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
6218032ca0SDavid Quigley #include <linux/security.h>
6318032ca0SDavid Quigley #endif
6418032ca0SDavid Quigley
6518032ca0SDavid Quigley
661da177e4SLinus Torvalds #define NFSDDBG_FACILITY NFSDDBG_XDR
671da177e4SLinus Torvalds
685cf23dbbSJ. Bruce Fields const u32 nfsd_suppattrs[3][3] = {
69916d2d84SJ. Bruce Fields {NFSD4_SUPPORTED_ATTRS_WORD0,
70916d2d84SJ. Bruce Fields NFSD4_SUPPORTED_ATTRS_WORD1,
71916d2d84SJ. Bruce Fields NFSD4_SUPPORTED_ATTRS_WORD2},
72916d2d84SJ. Bruce Fields
73916d2d84SJ. Bruce Fields {NFSD4_1_SUPPORTED_ATTRS_WORD0,
74916d2d84SJ. Bruce Fields NFSD4_1_SUPPORTED_ATTRS_WORD1,
75916d2d84SJ. Bruce Fields NFSD4_1_SUPPORTED_ATTRS_WORD2},
76916d2d84SJ. Bruce Fields
77916d2d84SJ. Bruce Fields {NFSD4_1_SUPPORTED_ATTRS_WORD0,
78916d2d84SJ. Bruce Fields NFSD4_1_SUPPORTED_ATTRS_WORD1,
79916d2d84SJ. Bruce Fields NFSD4_2_SUPPORTED_ATTRS_WORD2},
80916d2d84SJ. Bruce Fields };
81916d2d84SJ. Bruce Fields
8242ca0993SJ.Bruce Fields /*
8342ca0993SJ.Bruce Fields * As per referral draft, the fsid for a referral MUST be different from the fsid of the containing
8442ca0993SJ.Bruce Fields * directory in order to indicate to the client that a filesystem boundary is present
8542ca0993SJ.Bruce Fields * We use a fixed fsid for a referral
8642ca0993SJ.Bruce Fields */
8742ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MAJOR 0x8000000ULL
8842ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MINOR 0x8000000ULL
8942ca0993SJ.Bruce Fields
90b37ad28bSAl Viro static __be32
check_filename(char * str,int len)91a36b1725SJ. Bruce Fields check_filename(char *str, int len)
921da177e4SLinus Torvalds {
931da177e4SLinus Torvalds int i;
941da177e4SLinus Torvalds
951da177e4SLinus Torvalds if (len == 0)
961da177e4SLinus Torvalds return nfserr_inval;
97000dfa18SChuck Lever if (len > NFS4_MAXNAMLEN)
98000dfa18SChuck Lever return nfserr_nametoolong;
991da177e4SLinus Torvalds if (isdotent(str, len))
100a36b1725SJ. Bruce Fields return nfserr_badname;
1011da177e4SLinus Torvalds for (i = 0; i < len; i++)
1021da177e4SLinus Torvalds if (str[i] == '/')
103a36b1725SJ. Bruce Fields return nfserr_badname;
1041da177e4SLinus Torvalds return 0;
1051da177e4SLinus Torvalds }
1061da177e4SLinus Torvalds
zero_clientid(clientid_t * clid)10760adfc50SAndy Adamson static int zero_clientid(clientid_t *clid)
10860adfc50SAndy Adamson {
10960adfc50SAndy Adamson return (clid->cl_boot == 0) && (clid->cl_id == 0);
11060adfc50SAndy Adamson }
11160adfc50SAndy Adamson
1122d8498dbSChristoph Hellwig /**
113d5e23383SJ. Bruce Fields * svcxdr_tmpalloc - allocate memory to be freed after compound processing
114ce043ac8SJ. Bruce Fields * @argp: NFSv4 compound argument structure
115ed992753STrond Myklebust * @len: length of buffer to allocate
1162d8498dbSChristoph Hellwig *
117ed992753STrond Myklebust * Allocates a buffer of size @len to be freed when processing the compound
118ed992753STrond Myklebust * operation described in @argp finishes.
1192d8498dbSChristoph Hellwig */
120d5e23383SJ. Bruce Fields static void *
svcxdr_tmpalloc(struct nfsd4_compoundargs * argp,size_t len)121dbc834e5SDan Carpenter svcxdr_tmpalloc(struct nfsd4_compoundargs *argp, size_t len)
1221da177e4SLinus Torvalds {
123d5e23383SJ. Bruce Fields struct svcxdr_tmpbuf *tb;
1241da177e4SLinus Torvalds
125dbc834e5SDan Carpenter tb = kmalloc(struct_size(tb, buf, len), GFP_KERNEL);
1261da177e4SLinus Torvalds if (!tb)
127d5e23383SJ. Bruce Fields return NULL;
1281da177e4SLinus Torvalds tb->next = argp->to_free;
1291da177e4SLinus Torvalds argp->to_free = tb;
130d5e23383SJ. Bruce Fields return tb->buf;
1311da177e4SLinus Torvalds }
1321da177e4SLinus Torvalds
13329c353b3SJ. Bruce Fields /*
13429c353b3SJ. Bruce Fields * For xdr strings that need to be passed to other kernel api's
13529c353b3SJ. Bruce Fields * as null-terminated strings.
13629c353b3SJ. Bruce Fields *
13729c353b3SJ. Bruce Fields * Note null-terminating in place usually isn't safe since the
13829c353b3SJ. Bruce Fields * buffer might end on a page boundary.
13929c353b3SJ. Bruce Fields */
14029c353b3SJ. Bruce Fields static char *
svcxdr_dupstr(struct nfsd4_compoundargs * argp,void * buf,size_t len)141dbc834e5SDan Carpenter svcxdr_dupstr(struct nfsd4_compoundargs *argp, void *buf, size_t len)
14229c353b3SJ. Bruce Fields {
143dbc834e5SDan Carpenter char *p = svcxdr_tmpalloc(argp, size_add(len, 1));
14429c353b3SJ. Bruce Fields
14529c353b3SJ. Bruce Fields if (!p)
14629c353b3SJ. Bruce Fields return NULL;
14729c353b3SJ. Bruce Fields memcpy(p, buf, len);
14829c353b3SJ. Bruce Fields p[len] = '\0';
14929c353b3SJ. Bruce Fields return p;
1501da177e4SLinus Torvalds }
1511da177e4SLinus Torvalds
1527b723008SChuck Lever static void *
svcxdr_savemem(struct nfsd4_compoundargs * argp,__be32 * p,size_t len)153dbc834e5SDan Carpenter svcxdr_savemem(struct nfsd4_compoundargs *argp, __be32 *p, size_t len)
1547b723008SChuck Lever {
1557b723008SChuck Lever __be32 *tmp;
1567b723008SChuck Lever
1577b723008SChuck Lever /*
1587b723008SChuck Lever * The location of the decoded data item is stable,
1597b723008SChuck Lever * so @p is OK to use. This is the common case.
1607b723008SChuck Lever */
1617b723008SChuck Lever if (p != argp->xdr->scratch.iov_base)
1627b723008SChuck Lever return p;
1637b723008SChuck Lever
1647b723008SChuck Lever tmp = svcxdr_tmpalloc(argp, len);
1657b723008SChuck Lever if (!tmp)
1667b723008SChuck Lever return NULL;
1677b723008SChuck Lever memcpy(tmp, p, len);
1687b723008SChuck Lever return tmp;
1697b723008SChuck Lever }
1707b723008SChuck Lever
1715dcbfabbSChuck Lever /*
1725dcbfabbSChuck Lever * NFSv4 basic data type decoders
1735dcbfabbSChuck Lever */
1745dcbfabbSChuck Lever
1751a994408SChuck Lever /*
1761a994408SChuck Lever * This helper handles variable-length opaques which belong to protocol
1771a994408SChuck Lever * elements that this implementation does not support.
1781a994408SChuck Lever */
1791a994408SChuck Lever static __be32
nfsd4_decode_ignored_string(struct nfsd4_compoundargs * argp,u32 maxlen)1801a994408SChuck Lever nfsd4_decode_ignored_string(struct nfsd4_compoundargs *argp, u32 maxlen)
1811a994408SChuck Lever {
1821a994408SChuck Lever u32 len;
1831a994408SChuck Lever
1841a994408SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &len) < 0)
1851a994408SChuck Lever return nfserr_bad_xdr;
1861a994408SChuck Lever if (maxlen && len > maxlen)
1871a994408SChuck Lever return nfserr_bad_xdr;
1881a994408SChuck Lever if (!xdr_inline_decode(argp->xdr, len))
1891a994408SChuck Lever return nfserr_bad_xdr;
1901a994408SChuck Lever
1911a994408SChuck Lever return nfs_ok;
1921a994408SChuck Lever }
1931a994408SChuck Lever
1945dcbfabbSChuck Lever static __be32
nfsd4_decode_opaque(struct nfsd4_compoundargs * argp,struct xdr_netobj * o)1955dcbfabbSChuck Lever nfsd4_decode_opaque(struct nfsd4_compoundargs *argp, struct xdr_netobj *o)
1965dcbfabbSChuck Lever {
1975dcbfabbSChuck Lever __be32 *p;
1985dcbfabbSChuck Lever u32 len;
1995dcbfabbSChuck Lever
2005dcbfabbSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &len) < 0)
2015dcbfabbSChuck Lever return nfserr_bad_xdr;
2025dcbfabbSChuck Lever if (len == 0 || len > NFS4_OPAQUE_LIMIT)
2035dcbfabbSChuck Lever return nfserr_bad_xdr;
2045dcbfabbSChuck Lever p = xdr_inline_decode(argp->xdr, len);
2055dcbfabbSChuck Lever if (!p)
2065dcbfabbSChuck Lever return nfserr_bad_xdr;
2077b723008SChuck Lever o->data = svcxdr_savemem(argp, p, len);
2085dcbfabbSChuck Lever if (!o->data)
2095dcbfabbSChuck Lever return nfserr_jukebox;
2105dcbfabbSChuck Lever o->len = len;
2115dcbfabbSChuck Lever
2125dcbfabbSChuck Lever return nfs_ok;
2135dcbfabbSChuck Lever }
2145dcbfabbSChuck Lever
2154c94e13eSChristoph Hellwig static __be32
nfsd4_decode_component4(struct nfsd4_compoundargs * argp,char ** namp,u32 * lenp)216000dfa18SChuck Lever nfsd4_decode_component4(struct nfsd4_compoundargs *argp, char **namp, u32 *lenp)
217000dfa18SChuck Lever {
218000dfa18SChuck Lever __be32 *p, status;
219000dfa18SChuck Lever
220000dfa18SChuck Lever if (xdr_stream_decode_u32(argp->xdr, lenp) < 0)
221000dfa18SChuck Lever return nfserr_bad_xdr;
222000dfa18SChuck Lever p = xdr_inline_decode(argp->xdr, *lenp);
223000dfa18SChuck Lever if (!p)
224000dfa18SChuck Lever return nfserr_bad_xdr;
225000dfa18SChuck Lever status = check_filename((char *)p, *lenp);
226000dfa18SChuck Lever if (status)
227000dfa18SChuck Lever return status;
2287b723008SChuck Lever *namp = svcxdr_savemem(argp, p, *lenp);
229000dfa18SChuck Lever if (!*namp)
230000dfa18SChuck Lever return nfserr_jukebox;
231000dfa18SChuck Lever
232000dfa18SChuck Lever return nfs_ok;
233000dfa18SChuck Lever }
234000dfa18SChuck Lever
235000dfa18SChuck Lever static __be32
nfsd4_decode_nfstime4(struct nfsd4_compoundargs * argp,struct timespec64 * tv)2361c3eff7eSChuck Lever nfsd4_decode_nfstime4(struct nfsd4_compoundargs *argp, struct timespec64 *tv)
2371c3eff7eSChuck Lever {
2381c3eff7eSChuck Lever __be32 *p;
2391c3eff7eSChuck Lever
2401c3eff7eSChuck Lever p = xdr_inline_decode(argp->xdr, XDR_UNIT * 3);
2411c3eff7eSChuck Lever if (!p)
2421c3eff7eSChuck Lever return nfserr_bad_xdr;
2431c3eff7eSChuck Lever p = xdr_decode_hyper(p, &tv->tv_sec);
2441c3eff7eSChuck Lever tv->tv_nsec = be32_to_cpup(p++);
2451c3eff7eSChuck Lever if (tv->tv_nsec >= (u32)1000000000)
2461c3eff7eSChuck Lever return nfserr_inval;
2471c3eff7eSChuck Lever return nfs_ok;
2481c3eff7eSChuck Lever }
2491c3eff7eSChuck Lever
2501c3eff7eSChuck Lever static __be32
nfsd4_decode_verifier4(struct nfsd4_compoundargs * argp,nfs4_verifier * verf)251796dd1c6SChuck Lever nfsd4_decode_verifier4(struct nfsd4_compoundargs *argp, nfs4_verifier *verf)
252796dd1c6SChuck Lever {
253796dd1c6SChuck Lever __be32 *p;
254796dd1c6SChuck Lever
255796dd1c6SChuck Lever p = xdr_inline_decode(argp->xdr, NFS4_VERIFIER_SIZE);
256796dd1c6SChuck Lever if (!p)
257796dd1c6SChuck Lever return nfserr_bad_xdr;
258796dd1c6SChuck Lever memcpy(verf->data, p, sizeof(verf->data));
259796dd1c6SChuck Lever return nfs_ok;
260796dd1c6SChuck Lever }
261796dd1c6SChuck Lever
262d1c263a0SChuck Lever /**
263d1c263a0SChuck Lever * nfsd4_decode_bitmap4 - Decode an NFSv4 bitmap4
264d1c263a0SChuck Lever * @argp: NFSv4 compound argument structure
265d1c263a0SChuck Lever * @bmval: pointer to an array of u32's to decode into
266d1c263a0SChuck Lever * @bmlen: size of the @bmval array
267d1c263a0SChuck Lever *
268d1c263a0SChuck Lever * The server needs to return nfs_ok rather than nfserr_bad_xdr when
269d1c263a0SChuck Lever * encountering bitmaps containing bits it does not recognize. This
270d1c263a0SChuck Lever * includes bits in bitmap words past WORDn, where WORDn is the last
271d1c263a0SChuck Lever * bitmap WORD the implementation currently supports. Thus we are
272d1c263a0SChuck Lever * careful here to simply ignore bits in bitmap words that this
273d1c263a0SChuck Lever * implementation has yet to support explicitly.
274d1c263a0SChuck Lever *
275d1c263a0SChuck Lever * Return values:
276d1c263a0SChuck Lever * %nfs_ok: @bmval populated successfully
277d1c263a0SChuck Lever * %nfserr_bad_xdr: the encoded bitmap was invalid
278d1c263a0SChuck Lever */
279d1c263a0SChuck Lever static __be32
nfsd4_decode_bitmap4(struct nfsd4_compoundargs * argp,u32 * bmval,u32 bmlen)280d1c263a0SChuck Lever nfsd4_decode_bitmap4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen)
281d1c263a0SChuck Lever {
282cd2e999cSChuck Lever ssize_t status;
283d1c263a0SChuck Lever
284cd2e999cSChuck Lever status = xdr_stream_decode_uint32_array(argp->xdr, bmval, bmlen);
285cd2e999cSChuck Lever return status == -EBADMSG ? nfserr_bad_xdr : nfs_ok;
286d1c263a0SChuck Lever }
287d1c263a0SChuck Lever
288b37ad28bSAl Viro static __be32
nfsd4_decode_nfsace4(struct nfsd4_compoundargs * argp,struct nfs4_ace * ace)289c941a968SChuck Lever nfsd4_decode_nfsace4(struct nfsd4_compoundargs *argp, struct nfs4_ace *ace)
290c941a968SChuck Lever {
291c941a968SChuck Lever __be32 *p, status;
292c941a968SChuck Lever u32 length;
293c941a968SChuck Lever
294c941a968SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &ace->type) < 0)
295c941a968SChuck Lever return nfserr_bad_xdr;
296c941a968SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &ace->flag) < 0)
297c941a968SChuck Lever return nfserr_bad_xdr;
298c941a968SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &ace->access_mask) < 0)
299c941a968SChuck Lever return nfserr_bad_xdr;
300c941a968SChuck Lever
301c941a968SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &length) < 0)
302c941a968SChuck Lever return nfserr_bad_xdr;
303c941a968SChuck Lever p = xdr_inline_decode(argp->xdr, length);
304c941a968SChuck Lever if (!p)
305c941a968SChuck Lever return nfserr_bad_xdr;
306c941a968SChuck Lever ace->whotype = nfs4_acl_get_whotype((char *)p, length);
307c941a968SChuck Lever if (ace->whotype != NFS4_ACL_WHO_NAMED)
308c941a968SChuck Lever status = nfs_ok;
309c941a968SChuck Lever else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
310c941a968SChuck Lever status = nfsd_map_name_to_gid(argp->rqstp,
311c941a968SChuck Lever (char *)p, length, &ace->who_gid);
312c941a968SChuck Lever else
313c941a968SChuck Lever status = nfsd_map_name_to_uid(argp->rqstp,
314c941a968SChuck Lever (char *)p, length, &ace->who_uid);
315c941a968SChuck Lever
316c941a968SChuck Lever return status;
317c941a968SChuck Lever }
318c941a968SChuck Lever
319c941a968SChuck Lever /* A counted array of nfsace4's */
320c941a968SChuck Lever static noinline __be32
nfsd4_decode_acl(struct nfsd4_compoundargs * argp,struct nfs4_acl ** acl)321c941a968SChuck Lever nfsd4_decode_acl(struct nfsd4_compoundargs *argp, struct nfs4_acl **acl)
322c941a968SChuck Lever {
323c941a968SChuck Lever struct nfs4_ace *ace;
324c941a968SChuck Lever __be32 status;
325c941a968SChuck Lever u32 count;
326c941a968SChuck Lever
327c941a968SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
328c941a968SChuck Lever return nfserr_bad_xdr;
329c941a968SChuck Lever
330c941a968SChuck Lever if (count > xdr_stream_remaining(argp->xdr) / 20)
331c941a968SChuck Lever /*
332c941a968SChuck Lever * Even with 4-byte names there wouldn't be
333c941a968SChuck Lever * space for that many aces; something fishy is
334c941a968SChuck Lever * going on:
335c941a968SChuck Lever */
336c941a968SChuck Lever return nfserr_fbig;
337c941a968SChuck Lever
338c941a968SChuck Lever *acl = svcxdr_tmpalloc(argp, nfs4_acl_bytes(count));
339c941a968SChuck Lever if (*acl == NULL)
340c941a968SChuck Lever return nfserr_jukebox;
341c941a968SChuck Lever
342c941a968SChuck Lever (*acl)->naces = count;
343c941a968SChuck Lever for (ace = (*acl)->aces; ace < (*acl)->aces + count; ace++) {
344c941a968SChuck Lever status = nfsd4_decode_nfsace4(argp, ace);
345c941a968SChuck Lever if (status)
346c941a968SChuck Lever return status;
347c941a968SChuck Lever }
348c941a968SChuck Lever
349c941a968SChuck Lever return nfs_ok;
350c941a968SChuck Lever }
351c941a968SChuck Lever
352dabe9182SChuck Lever static noinline __be32
nfsd4_decode_security_label(struct nfsd4_compoundargs * argp,struct xdr_netobj * label)353dabe9182SChuck Lever nfsd4_decode_security_label(struct nfsd4_compoundargs *argp,
354dabe9182SChuck Lever struct xdr_netobj *label)
355dabe9182SChuck Lever {
356dabe9182SChuck Lever u32 lfs, pi, length;
357dabe9182SChuck Lever __be32 *p;
358dabe9182SChuck Lever
359dabe9182SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lfs) < 0)
360dabe9182SChuck Lever return nfserr_bad_xdr;
361dabe9182SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &pi) < 0)
362dabe9182SChuck Lever return nfserr_bad_xdr;
363dabe9182SChuck Lever
364dabe9182SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &length) < 0)
365dabe9182SChuck Lever return nfserr_bad_xdr;
366dabe9182SChuck Lever if (length > NFS4_MAXLABELLEN)
367dabe9182SChuck Lever return nfserr_badlabel;
368dabe9182SChuck Lever p = xdr_inline_decode(argp->xdr, length);
369dabe9182SChuck Lever if (!p)
370dabe9182SChuck Lever return nfserr_bad_xdr;
371dabe9182SChuck Lever label->len = length;
372dabe9182SChuck Lever label->data = svcxdr_dupstr(argp, p, length);
373dabe9182SChuck Lever if (!label->data)
374dabe9182SChuck Lever return nfserr_jukebox;
375dabe9182SChuck Lever
376dabe9182SChuck Lever return nfs_ok;
377dabe9182SChuck Lever }
378dabe9182SChuck Lever
379c941a968SChuck Lever static __be32
nfsd4_decode_fattr4(struct nfsd4_compoundargs * argp,u32 * bmval,u32 bmlen,struct iattr * iattr,struct nfs4_acl ** acl,struct xdr_netobj * label,int * umask)380d1c263a0SChuck Lever nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen,
38118032ca0SDavid Quigley struct iattr *iattr, struct nfs4_acl **acl,
38247057abdSAndreas Gruenbacher struct xdr_netobj *label, int *umask)
3831da177e4SLinus Torvalds {
384081d53feSChuck Lever unsigned int starting_pos;
385081d53feSChuck Lever u32 attrlist4_count;
386d1c263a0SChuck Lever __be32 *p, status;
3871da177e4SLinus Torvalds
3881da177e4SLinus Torvalds iattr->ia_valid = 0;
389d1c263a0SChuck Lever status = nfsd4_decode_bitmap4(argp, bmval, bmlen);
390d1c263a0SChuck Lever if (status)
391d1c263a0SChuck Lever return nfserr_bad_xdr;
3921da177e4SLinus Torvalds
393e864c189SJ. Bruce Fields if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0
394e864c189SJ. Bruce Fields || bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1
395e864c189SJ. Bruce Fields || bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2) {
396e864c189SJ. Bruce Fields if (nfsd_attrs_supported(argp->minorversion, bmval))
397e864c189SJ. Bruce Fields return nfserr_inval;
398e864c189SJ. Bruce Fields return nfserr_attrnotsupp;
399e864c189SJ. Bruce Fields }
400e864c189SJ. Bruce Fields
401081d53feSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &attrlist4_count) < 0)
402081d53feSChuck Lever return nfserr_bad_xdr;
403081d53feSChuck Lever starting_pos = xdr_stream_pos(argp->xdr);
4041da177e4SLinus Torvalds
4051da177e4SLinus Torvalds if (bmval[0] & FATTR4_WORD0_SIZE) {
4062ac1b9b2SChuck Lever u64 size;
4072ac1b9b2SChuck Lever
4082ac1b9b2SChuck Lever if (xdr_stream_decode_u64(argp->xdr, &size) < 0)
4092ac1b9b2SChuck Lever return nfserr_bad_xdr;
4102ac1b9b2SChuck Lever iattr->ia_size = size;
4111da177e4SLinus Torvalds iattr->ia_valid |= ATTR_SIZE;
4121da177e4SLinus Torvalds }
4131da177e4SLinus Torvalds if (bmval[0] & FATTR4_WORD0_ACL) {
414c941a968SChuck Lever status = nfsd4_decode_acl(argp, acl);
4153c726023SJ. Bruce Fields if (status)
4163c726023SJ. Bruce Fields return status;
4171da177e4SLinus Torvalds } else
4181da177e4SLinus Torvalds *acl = NULL;
4191da177e4SLinus Torvalds if (bmval[1] & FATTR4_WORD1_MODE) {
4201c8f0ad7SChuck Lever u32 mode;
4211c8f0ad7SChuck Lever
4221c8f0ad7SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &mode) < 0)
4231c8f0ad7SChuck Lever return nfserr_bad_xdr;
4241c8f0ad7SChuck Lever iattr->ia_mode = mode;
4251da177e4SLinus Torvalds iattr->ia_mode &= (S_IFMT | S_IALLUGO);
4261da177e4SLinus Torvalds iattr->ia_valid |= ATTR_MODE;
4271da177e4SLinus Torvalds }
4281da177e4SLinus Torvalds if (bmval[1] & FATTR4_WORD1_OWNER) {
4299853a5acSChuck Lever u32 length;
4309853a5acSChuck Lever
4319853a5acSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &length) < 0)
4329853a5acSChuck Lever return nfserr_bad_xdr;
4339853a5acSChuck Lever p = xdr_inline_decode(argp->xdr, length);
4349853a5acSChuck Lever if (!p)
4359853a5acSChuck Lever return nfserr_bad_xdr;
4369853a5acSChuck Lever status = nfsd_map_name_to_uid(argp->rqstp, (char *)p, length,
4379853a5acSChuck Lever &iattr->ia_uid);
4389853a5acSChuck Lever if (status)
43947c85291SNeilBrown return status;
4401da177e4SLinus Torvalds iattr->ia_valid |= ATTR_UID;
4411da177e4SLinus Torvalds }
4421da177e4SLinus Torvalds if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) {
443393c31ddSChuck Lever u32 length;
444393c31ddSChuck Lever
445393c31ddSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &length) < 0)
446393c31ddSChuck Lever return nfserr_bad_xdr;
447393c31ddSChuck Lever p = xdr_inline_decode(argp->xdr, length);
448393c31ddSChuck Lever if (!p)
449393c31ddSChuck Lever return nfserr_bad_xdr;
450393c31ddSChuck Lever status = nfsd_map_name_to_gid(argp->rqstp, (char *)p, length,
451393c31ddSChuck Lever &iattr->ia_gid);
452393c31ddSChuck Lever if (status)
45347c85291SNeilBrown return status;
4541da177e4SLinus Torvalds iattr->ia_valid |= ATTR_GID;
4551da177e4SLinus Torvalds }
4561da177e4SLinus Torvalds if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
4571c3eff7eSChuck Lever u32 set_it;
4581c3eff7eSChuck Lever
4591c3eff7eSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &set_it) < 0)
4601c3eff7eSChuck Lever return nfserr_bad_xdr;
4611c3eff7eSChuck Lever switch (set_it) {
4621da177e4SLinus Torvalds case NFS4_SET_TO_CLIENT_TIME:
4631c3eff7eSChuck Lever status = nfsd4_decode_nfstime4(argp, &iattr->ia_atime);
4644c94e13eSChristoph Hellwig if (status)
4654c94e13eSChristoph Hellwig return status;
4661da177e4SLinus Torvalds iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET);
4671da177e4SLinus Torvalds break;
4681da177e4SLinus Torvalds case NFS4_SET_TO_SERVER_TIME:
4691da177e4SLinus Torvalds iattr->ia_valid |= ATTR_ATIME;
4701da177e4SLinus Torvalds break;
4711da177e4SLinus Torvalds default:
4721c3eff7eSChuck Lever return nfserr_bad_xdr;
4731da177e4SLinus Torvalds }
4741da177e4SLinus Torvalds }
4755b2f3e07SChuck Lever if (bmval[1] & FATTR4_WORD1_TIME_CREATE) {
4765b2f3e07SChuck Lever struct timespec64 ts;
4775b2f3e07SChuck Lever
4785b2f3e07SChuck Lever /* No Linux filesystem supports setting this attribute. */
4795b2f3e07SChuck Lever bmval[1] &= ~FATTR4_WORD1_TIME_CREATE;
4805b2f3e07SChuck Lever status = nfsd4_decode_nfstime4(argp, &ts);
4815b2f3e07SChuck Lever if (status)
4825b2f3e07SChuck Lever return status;
4835b2f3e07SChuck Lever }
4841da177e4SLinus Torvalds if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
4851c3eff7eSChuck Lever u32 set_it;
4861c3eff7eSChuck Lever
4871c3eff7eSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &set_it) < 0)
4881c3eff7eSChuck Lever return nfserr_bad_xdr;
4891c3eff7eSChuck Lever switch (set_it) {
4901da177e4SLinus Torvalds case NFS4_SET_TO_CLIENT_TIME:
4911c3eff7eSChuck Lever status = nfsd4_decode_nfstime4(argp, &iattr->ia_mtime);
4924c94e13eSChristoph Hellwig if (status)
4934c94e13eSChristoph Hellwig return status;
4941da177e4SLinus Torvalds iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET);
4951da177e4SLinus Torvalds break;
4961da177e4SLinus Torvalds case NFS4_SET_TO_SERVER_TIME:
4971da177e4SLinus Torvalds iattr->ia_valid |= ATTR_MTIME;
4981da177e4SLinus Torvalds break;
4991da177e4SLinus Torvalds default:
5001c3eff7eSChuck Lever return nfserr_bad_xdr;
5011da177e4SLinus Torvalds }
5021da177e4SLinus Torvalds }
50318032ca0SDavid Quigley label->len = 0;
5042285ae76SArnd Bergmann if (IS_ENABLED(CONFIG_NFSD_V4_SECURITY_LABEL) &&
5052285ae76SArnd Bergmann bmval[2] & FATTR4_WORD2_SECURITY_LABEL) {
506dabe9182SChuck Lever status = nfsd4_decode_security_label(argp, label);
507dabe9182SChuck Lever if (status)
508dabe9182SChuck Lever return status;
50918032ca0SDavid Quigley }
51047057abdSAndreas Gruenbacher if (bmval[2] & FATTR4_WORD2_MODE_UMASK) {
51166f0476cSChuck Lever u32 mode, mask;
51266f0476cSChuck Lever
51347057abdSAndreas Gruenbacher if (!umask)
51466f0476cSChuck Lever return nfserr_bad_xdr;
51566f0476cSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &mode) < 0)
51666f0476cSChuck Lever return nfserr_bad_xdr;
51766f0476cSChuck Lever iattr->ia_mode = mode & (S_IFMT | S_IALLUGO);
51866f0476cSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &mask) < 0)
51966f0476cSChuck Lever return nfserr_bad_xdr;
52066f0476cSChuck Lever *umask = mask & S_IRWXUGO;
52147057abdSAndreas Gruenbacher iattr->ia_valid |= ATTR_MODE;
52247057abdSAndreas Gruenbacher }
523081d53feSChuck Lever
524081d53feSChuck Lever /* request sanity: did attrlist4 contain the expected number of words? */
525081d53feSChuck Lever if (attrlist4_count != xdr_stream_pos(argp->xdr) - starting_pos)
526081d53feSChuck Lever return nfserr_bad_xdr;
5271da177e4SLinus Torvalds
528d1c263a0SChuck Lever return nfs_ok;
5291da177e4SLinus Torvalds }
5301da177e4SLinus Torvalds
531b37ad28bSAl Viro static __be32
nfsd4_decode_stateid4(struct nfsd4_compoundargs * argp,stateid_t * sid)532d3d2f381SChuck Lever nfsd4_decode_stateid4(struct nfsd4_compoundargs *argp, stateid_t *sid)
533d3d2f381SChuck Lever {
534d3d2f381SChuck Lever __be32 *p;
535d3d2f381SChuck Lever
536d3d2f381SChuck Lever p = xdr_inline_decode(argp->xdr, NFS4_STATEID_SIZE);
537d3d2f381SChuck Lever if (!p)
538d3d2f381SChuck Lever return nfserr_bad_xdr;
539d3d2f381SChuck Lever sid->si_generation = be32_to_cpup(p++);
540d3d2f381SChuck Lever memcpy(&sid->si_opaque, p, sizeof(sid->si_opaque));
541d3d2f381SChuck Lever return nfs_ok;
542d3d2f381SChuck Lever }
543d3d2f381SChuck Lever
544144e8269SChuck Lever static __be32
nfsd4_decode_clientid4(struct nfsd4_compoundargs * argp,clientid_t * clientid)545144e8269SChuck Lever nfsd4_decode_clientid4(struct nfsd4_compoundargs *argp, clientid_t *clientid)
546144e8269SChuck Lever {
547144e8269SChuck Lever __be32 *p;
548144e8269SChuck Lever
549144e8269SChuck Lever p = xdr_inline_decode(argp->xdr, sizeof(__be64));
550144e8269SChuck Lever if (!p)
551144e8269SChuck Lever return nfserr_bad_xdr;
552144e8269SChuck Lever memcpy(clientid, p, sizeof(*clientid));
553144e8269SChuck Lever return nfs_ok;
554144e8269SChuck Lever }
555144e8269SChuck Lever
556144e8269SChuck Lever static __be32
nfsd4_decode_state_owner4(struct nfsd4_compoundargs * argp,clientid_t * clientid,struct xdr_netobj * owner)557144e8269SChuck Lever nfsd4_decode_state_owner4(struct nfsd4_compoundargs *argp,
558144e8269SChuck Lever clientid_t *clientid, struct xdr_netobj *owner)
559144e8269SChuck Lever {
560144e8269SChuck Lever __be32 status;
561144e8269SChuck Lever
562144e8269SChuck Lever status = nfsd4_decode_clientid4(argp, clientid);
563144e8269SChuck Lever if (status)
564144e8269SChuck Lever return status;
565144e8269SChuck Lever return nfsd4_decode_opaque(argp, owner);
566144e8269SChuck Lever }
567144e8269SChuck Lever
56804495971SChuck Lever #ifdef CONFIG_NFSD_PNFS
56904495971SChuck Lever static __be32
nfsd4_decode_deviceid4(struct nfsd4_compoundargs * argp,struct nfsd4_deviceid * devid)57004495971SChuck Lever nfsd4_decode_deviceid4(struct nfsd4_compoundargs *argp,
57104495971SChuck Lever struct nfsd4_deviceid *devid)
57204495971SChuck Lever {
57304495971SChuck Lever __be32 *p;
57404495971SChuck Lever
57504495971SChuck Lever p = xdr_inline_decode(argp->xdr, NFS4_DEVICEID4_SIZE);
57604495971SChuck Lever if (!p)
57704495971SChuck Lever return nfserr_bad_xdr;
57804495971SChuck Lever memcpy(devid, p, sizeof(*devid));
57904495971SChuck Lever return nfs_ok;
58004495971SChuck Lever }
5815185980dSChuck Lever
5825185980dSChuck Lever static __be32
nfsd4_decode_layoutupdate4(struct nfsd4_compoundargs * argp,struct nfsd4_layoutcommit * lcp)5835185980dSChuck Lever nfsd4_decode_layoutupdate4(struct nfsd4_compoundargs *argp,
5845185980dSChuck Lever struct nfsd4_layoutcommit *lcp)
5855185980dSChuck Lever {
5865185980dSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lcp->lc_layout_type) < 0)
5875185980dSChuck Lever return nfserr_bad_xdr;
5885185980dSChuck Lever if (lcp->lc_layout_type < LAYOUT_NFSV4_1_FILES)
5895185980dSChuck Lever return nfserr_bad_xdr;
5905185980dSChuck Lever if (lcp->lc_layout_type >= LAYOUT_TYPE_MAX)
5915185980dSChuck Lever return nfserr_bad_xdr;
5925185980dSChuck Lever
5935185980dSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lcp->lc_up_len) < 0)
5945185980dSChuck Lever return nfserr_bad_xdr;
5955185980dSChuck Lever if (lcp->lc_up_len > 0) {
5965185980dSChuck Lever lcp->lc_up_layout = xdr_inline_decode(argp->xdr, lcp->lc_up_len);
5975185980dSChuck Lever if (!lcp->lc_up_layout)
5985185980dSChuck Lever return nfserr_bad_xdr;
5995185980dSChuck Lever }
6005185980dSChuck Lever
6015185980dSChuck Lever return nfs_ok;
6025185980dSChuck Lever }
6035185980dSChuck Lever
604645fcad3SChuck Lever static __be32
nfsd4_decode_layoutreturn4(struct nfsd4_compoundargs * argp,struct nfsd4_layoutreturn * lrp)605645fcad3SChuck Lever nfsd4_decode_layoutreturn4(struct nfsd4_compoundargs *argp,
606645fcad3SChuck Lever struct nfsd4_layoutreturn *lrp)
607645fcad3SChuck Lever {
608645fcad3SChuck Lever __be32 status;
609645fcad3SChuck Lever
610645fcad3SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lrp->lr_return_type) < 0)
611645fcad3SChuck Lever return nfserr_bad_xdr;
612645fcad3SChuck Lever switch (lrp->lr_return_type) {
613645fcad3SChuck Lever case RETURN_FILE:
614645fcad3SChuck Lever if (xdr_stream_decode_u64(argp->xdr, &lrp->lr_seg.offset) < 0)
615645fcad3SChuck Lever return nfserr_bad_xdr;
616645fcad3SChuck Lever if (xdr_stream_decode_u64(argp->xdr, &lrp->lr_seg.length) < 0)
617645fcad3SChuck Lever return nfserr_bad_xdr;
618645fcad3SChuck Lever status = nfsd4_decode_stateid4(argp, &lrp->lr_sid);
619645fcad3SChuck Lever if (status)
620645fcad3SChuck Lever return status;
621645fcad3SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lrp->lrf_body_len) < 0)
622645fcad3SChuck Lever return nfserr_bad_xdr;
623645fcad3SChuck Lever if (lrp->lrf_body_len > 0) {
624645fcad3SChuck Lever lrp->lrf_body = xdr_inline_decode(argp->xdr, lrp->lrf_body_len);
625645fcad3SChuck Lever if (!lrp->lrf_body)
626645fcad3SChuck Lever return nfserr_bad_xdr;
627645fcad3SChuck Lever }
628645fcad3SChuck Lever break;
629645fcad3SChuck Lever case RETURN_FSID:
630645fcad3SChuck Lever case RETURN_ALL:
631645fcad3SChuck Lever lrp->lr_seg.offset = 0;
632645fcad3SChuck Lever lrp->lr_seg.length = NFS4_MAX_UINT64;
633645fcad3SChuck Lever break;
634645fcad3SChuck Lever default:
635645fcad3SChuck Lever return nfserr_bad_xdr;
636645fcad3SChuck Lever }
637645fcad3SChuck Lever
638645fcad3SChuck Lever return nfs_ok;
639645fcad3SChuck Lever }
640645fcad3SChuck Lever
64104495971SChuck Lever #endif /* CONFIG_NFSD_PNFS */
64204495971SChuck Lever
643571e0451SChuck Lever static __be32
nfsd4_decode_sessionid4(struct nfsd4_compoundargs * argp,struct nfs4_sessionid * sessionid)644571e0451SChuck Lever nfsd4_decode_sessionid4(struct nfsd4_compoundargs *argp,
645571e0451SChuck Lever struct nfs4_sessionid *sessionid)
646571e0451SChuck Lever {
647571e0451SChuck Lever __be32 *p;
648571e0451SChuck Lever
649571e0451SChuck Lever p = xdr_inline_decode(argp->xdr, NFS4_MAX_SESSIONID_LEN);
650571e0451SChuck Lever if (!p)
651571e0451SChuck Lever return nfserr_bad_xdr;
652571e0451SChuck Lever memcpy(sessionid->data, p, sizeof(sessionid->data));
653571e0451SChuck Lever return nfs_ok;
654571e0451SChuck Lever }
655571e0451SChuck Lever
6561a994408SChuck Lever /* Defined in Appendix A of RFC 5531 */
6571a994408SChuck Lever static __be32
nfsd4_decode_authsys_parms(struct nfsd4_compoundargs * argp,struct nfsd4_cb_sec * cbs)6581a994408SChuck Lever nfsd4_decode_authsys_parms(struct nfsd4_compoundargs *argp,
6591a994408SChuck Lever struct nfsd4_cb_sec *cbs)
660acb2887eSJ. Bruce Fields {
6611a994408SChuck Lever u32 stamp, gidcount, uid, gid;
6621a994408SChuck Lever __be32 *p, status;
663acb2887eSJ. Bruce Fields
6641a994408SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &stamp) < 0)
6651a994408SChuck Lever return nfserr_bad_xdr;
666acb2887eSJ. Bruce Fields /* machine name */
6671a994408SChuck Lever status = nfsd4_decode_ignored_string(argp, 255);
6681a994408SChuck Lever if (status)
6691a994408SChuck Lever return status;
6701a994408SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &uid) < 0)
6711a994408SChuck Lever return nfserr_bad_xdr;
6721a994408SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &gid) < 0)
6731a994408SChuck Lever return nfserr_bad_xdr;
6741a994408SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &gidcount) < 0)
6751a994408SChuck Lever return nfserr_bad_xdr;
6761a994408SChuck Lever if (gidcount > 16)
6771a994408SChuck Lever return nfserr_bad_xdr;
6781a994408SChuck Lever p = xdr_inline_decode(argp->xdr, gidcount << 2);
6791a994408SChuck Lever if (!p)
6801a994408SChuck Lever return nfserr_bad_xdr;
68112fc3e92SJ. Bruce Fields if (cbs->flavor == (u32)(-1)) {
6821a994408SChuck Lever struct user_namespace *userns = nfsd_user_namespace(argp->rqstp);
6831a994408SChuck Lever
684e45d1a18STrond Myklebust kuid_t kuid = make_kuid(userns, uid);
685e45d1a18STrond Myklebust kgid_t kgid = make_kgid(userns, gid);
68603bc6d1cSEric W. Biederman if (uid_valid(kuid) && gid_valid(kgid)) {
68703bc6d1cSEric W. Biederman cbs->uid = kuid;
68803bc6d1cSEric W. Biederman cbs->gid = kgid;
68912fc3e92SJ. Bruce Fields cbs->flavor = RPC_AUTH_UNIX;
69003bc6d1cSEric W. Biederman } else {
6911a994408SChuck Lever dprintk("RPC_AUTH_UNIX with invalid uid or gid, ignoring!\n");
69203bc6d1cSEric W. Biederman }
69312fc3e92SJ. Bruce Fields }
6941a994408SChuck Lever
6951a994408SChuck Lever return nfs_ok;
6961a994408SChuck Lever }
6971a994408SChuck Lever
6981a994408SChuck Lever static __be32
nfsd4_decode_gss_cb_handles4(struct nfsd4_compoundargs * argp,struct nfsd4_cb_sec * cbs)6991a994408SChuck Lever nfsd4_decode_gss_cb_handles4(struct nfsd4_compoundargs *argp,
7001a994408SChuck Lever struct nfsd4_cb_sec *cbs)
7011a994408SChuck Lever {
7021a994408SChuck Lever __be32 status;
7031a994408SChuck Lever u32 service;
7041a994408SChuck Lever
7051a994408SChuck Lever dprintk("RPC_AUTH_GSS callback secflavor not supported!\n");
7061a994408SChuck Lever
7071a994408SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &service) < 0)
7081a994408SChuck Lever return nfserr_bad_xdr;
7091a994408SChuck Lever if (service < RPC_GSS_SVC_NONE || service > RPC_GSS_SVC_PRIVACY)
7101a994408SChuck Lever return nfserr_bad_xdr;
7111a994408SChuck Lever /* gcbp_handle_from_server */
7121a994408SChuck Lever status = nfsd4_decode_ignored_string(argp, 0);
7131a994408SChuck Lever if (status)
7141a994408SChuck Lever return status;
7151a994408SChuck Lever /* gcbp_handle_from_client */
7161a994408SChuck Lever status = nfsd4_decode_ignored_string(argp, 0);
7171a994408SChuck Lever if (status)
7181a994408SChuck Lever return status;
7191a994408SChuck Lever
7201a994408SChuck Lever return nfs_ok;
7211a994408SChuck Lever }
7221a994408SChuck Lever
7231a994408SChuck Lever /* a counted array of callback_sec_parms4 items */
7241a994408SChuck Lever static __be32
nfsd4_decode_cb_sec(struct nfsd4_compoundargs * argp,struct nfsd4_cb_sec * cbs)7251a994408SChuck Lever nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_cb_sec *cbs)
7261a994408SChuck Lever {
7271a994408SChuck Lever u32 i, secflavor, nr_secflavs;
7281a994408SChuck Lever __be32 status;
7291a994408SChuck Lever
7301a994408SChuck Lever /* callback_sec_params4 */
7311a994408SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &nr_secflavs) < 0)
7321a994408SChuck Lever return nfserr_bad_xdr;
7331a994408SChuck Lever if (nr_secflavs)
7341a994408SChuck Lever cbs->flavor = (u32)(-1);
7351a994408SChuck Lever else
7361a994408SChuck Lever /* Is this legal? Be generous, take it to mean AUTH_NONE: */
7371a994408SChuck Lever cbs->flavor = 0;
7381a994408SChuck Lever
7391a994408SChuck Lever for (i = 0; i < nr_secflavs; ++i) {
7401a994408SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &secflavor) < 0)
7411a994408SChuck Lever return nfserr_bad_xdr;
7421a994408SChuck Lever switch (secflavor) {
7431a994408SChuck Lever case RPC_AUTH_NULL:
7441a994408SChuck Lever /* void */
7451a994408SChuck Lever if (cbs->flavor == (u32)(-1))
7461a994408SChuck Lever cbs->flavor = RPC_AUTH_NULL;
7471a994408SChuck Lever break;
7481a994408SChuck Lever case RPC_AUTH_UNIX:
7491a994408SChuck Lever status = nfsd4_decode_authsys_parms(argp, cbs);
7501a994408SChuck Lever if (status)
7511a994408SChuck Lever return status;
752acb2887eSJ. Bruce Fields break;
753acb2887eSJ. Bruce Fields case RPC_AUTH_GSS:
7541a994408SChuck Lever status = nfsd4_decode_gss_cb_handles4(argp, cbs);
7551a994408SChuck Lever if (status)
7561a994408SChuck Lever return status;
757acb2887eSJ. Bruce Fields break;
758acb2887eSJ. Bruce Fields default:
759acb2887eSJ. Bruce Fields return nfserr_inval;
760acb2887eSJ. Bruce Fields }
761acb2887eSJ. Bruce Fields }
7621a994408SChuck Lever
7631a994408SChuck Lever return nfs_ok;
764acb2887eSJ. Bruce Fields }
765acb2887eSJ. Bruce Fields
7661a994408SChuck Lever
767d169a6a9SChuck Lever /*
768d169a6a9SChuck Lever * NFSv4 operation argument decoders
769d169a6a9SChuck Lever */
770d169a6a9SChuck Lever
771d169a6a9SChuck Lever static __be32
nfsd4_decode_access(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)772d169a6a9SChuck Lever nfsd4_decode_access(struct nfsd4_compoundargs *argp,
773e78e274eSKees Cook union nfsd4_op_u *u)
774d169a6a9SChuck Lever {
775e78e274eSKees Cook struct nfsd4_access *access = &u->access;
776d169a6a9SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &access->ac_req_access) < 0)
777d169a6a9SChuck Lever return nfserr_bad_xdr;
778d169a6a9SChuck Lever return nfs_ok;
779d169a6a9SChuck Lever }
780d169a6a9SChuck Lever
781b37ad28bSAl Viro static __be32
nfsd4_decode_close(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)782e78e274eSKees Cook nfsd4_decode_close(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
7831da177e4SLinus Torvalds {
784e78e274eSKees Cook struct nfsd4_close *close = &u->close;
785d3d2f381SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &close->cl_seqid) < 0)
786d3d2f381SChuck Lever return nfserr_bad_xdr;
787d3d2f381SChuck Lever return nfsd4_decode_stateid4(argp, &close->cl_stateid);
7881da177e4SLinus Torvalds }
7891da177e4SLinus Torvalds
7901da177e4SLinus Torvalds
791b37ad28bSAl Viro static __be32
nfsd4_decode_commit(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)792e78e274eSKees Cook nfsd4_decode_commit(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
7931da177e4SLinus Torvalds {
794e78e274eSKees Cook struct nfsd4_commit *commit = &u->commit;
795cbd9abb3SChuck Lever if (xdr_stream_decode_u64(argp->xdr, &commit->co_offset) < 0)
796cbd9abb3SChuck Lever return nfserr_bad_xdr;
797cbd9abb3SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &commit->co_count) < 0)
798cbd9abb3SChuck Lever return nfserr_bad_xdr;
7993fdc5464SChuck Lever memset(&commit->co_verf, 0, sizeof(commit->co_verf));
800cbd9abb3SChuck Lever return nfs_ok;
8011da177e4SLinus Torvalds }
8021da177e4SLinus Torvalds
803b37ad28bSAl Viro static __be32
nfsd4_decode_create(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)804e78e274eSKees Cook nfsd4_decode_create(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
8051da177e4SLinus Torvalds {
806e78e274eSKees Cook struct nfsd4_create *create = &u->create;
807000dfa18SChuck Lever __be32 *p, status;
8081da177e4SLinus Torvalds
8093fdc5464SChuck Lever memset(create, 0, sizeof(*create));
810000dfa18SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &create->cr_type) < 0)
811000dfa18SChuck Lever return nfserr_bad_xdr;
8121da177e4SLinus Torvalds switch (create->cr_type) {
8131da177e4SLinus Torvalds case NF4LNK:
814000dfa18SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &create->cr_datalen) < 0)
815000dfa18SChuck Lever return nfserr_bad_xdr;
816000dfa18SChuck Lever p = xdr_inline_decode(argp->xdr, create->cr_datalen);
817000dfa18SChuck Lever if (!p)
818000dfa18SChuck Lever return nfserr_bad_xdr;
81929c353b3SJ. Bruce Fields create->cr_data = svcxdr_dupstr(argp, p, create->cr_datalen);
8207fb84306SJ. Bruce Fields if (!create->cr_data)
82176f47128SJ. Bruce Fields return nfserr_jukebox;
8221da177e4SLinus Torvalds break;
8231da177e4SLinus Torvalds case NF4BLK:
8241da177e4SLinus Torvalds case NF4CHR:
825000dfa18SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &create->cr_specdata1) < 0)
826000dfa18SChuck Lever return nfserr_bad_xdr;
827000dfa18SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &create->cr_specdata2) < 0)
828000dfa18SChuck Lever return nfserr_bad_xdr;
8291da177e4SLinus Torvalds break;
8301da177e4SLinus Torvalds case NF4SOCK:
8311da177e4SLinus Torvalds case NF4FIFO:
8321da177e4SLinus Torvalds case NF4DIR:
8331da177e4SLinus Torvalds default:
8341da177e4SLinus Torvalds break;
8351da177e4SLinus Torvalds }
836000dfa18SChuck Lever status = nfsd4_decode_component4(argp, &create->cr_name,
837000dfa18SChuck Lever &create->cr_namelen);
838000dfa18SChuck Lever if (status)
8391da177e4SLinus Torvalds return status;
840d1c263a0SChuck Lever status = nfsd4_decode_fattr4(argp, create->cr_bmval,
841d1c263a0SChuck Lever ARRAY_SIZE(create->cr_bmval),
842d1c263a0SChuck Lever &create->cr_iattr, &create->cr_acl,
843d1c263a0SChuck Lever &create->cr_label, &create->cr_umask);
844c0d6fc8aSBenny Halevy if (status)
845000dfa18SChuck Lever return status;
8461da177e4SLinus Torvalds
847000dfa18SChuck Lever return nfs_ok;
8481da177e4SLinus Torvalds }
8491da177e4SLinus Torvalds
850b37ad28bSAl Viro static inline __be32
nfsd4_decode_delegreturn(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)851e78e274eSKees Cook nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
8521da177e4SLinus Torvalds {
853e78e274eSKees Cook struct nfsd4_delegreturn *dr = &u->delegreturn;
85495e6482cSChuck Lever return nfsd4_decode_stateid4(argp, &dr->dr_stateid);
8551da177e4SLinus Torvalds }
8561da177e4SLinus Torvalds
857b37ad28bSAl Viro static inline __be32
nfsd4_decode_getattr(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)858e78e274eSKees Cook nfsd4_decode_getattr(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
8591da177e4SLinus Torvalds {
860e78e274eSKees Cook struct nfsd4_getattr *getattr = &u->getattr;
8613fdc5464SChuck Lever memset(getattr, 0, sizeof(*getattr));
862f759eff2SChuck Lever return nfsd4_decode_bitmap4(argp, getattr->ga_bmval,
863f759eff2SChuck Lever ARRAY_SIZE(getattr->ga_bmval));
8641da177e4SLinus Torvalds }
8651da177e4SLinus Torvalds
866b37ad28bSAl Viro static __be32
nfsd4_decode_link(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)867e78e274eSKees Cook nfsd4_decode_link(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
8681da177e4SLinus Torvalds {
869e78e274eSKees Cook struct nfsd4_link *link = &u->link;
8703fdc5464SChuck Lever memset(link, 0, sizeof(*link));
8715c505d12SChuck Lever return nfsd4_decode_component4(argp, &link->li_name, &link->li_namelen);
8721da177e4SLinus Torvalds }
8731da177e4SLinus Torvalds
874b37ad28bSAl Viro static __be32
nfsd4_decode_open_to_lock_owner4(struct nfsd4_compoundargs * argp,struct nfsd4_lock * lock)8758918cc0dSChuck Lever nfsd4_decode_open_to_lock_owner4(struct nfsd4_compoundargs *argp,
8768918cc0dSChuck Lever struct nfsd4_lock *lock)
8778918cc0dSChuck Lever {
8788918cc0dSChuck Lever __be32 status;
8798918cc0dSChuck Lever
8808918cc0dSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lock->lk_new_open_seqid) < 0)
8818918cc0dSChuck Lever return nfserr_bad_xdr;
8828918cc0dSChuck Lever status = nfsd4_decode_stateid4(argp, &lock->lk_new_open_stateid);
8838918cc0dSChuck Lever if (status)
8848918cc0dSChuck Lever return status;
8858918cc0dSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lock->lk_new_lock_seqid) < 0)
8868918cc0dSChuck Lever return nfserr_bad_xdr;
8878918cc0dSChuck Lever return nfsd4_decode_state_owner4(argp, &lock->lk_new_clientid,
8888918cc0dSChuck Lever &lock->lk_new_owner);
8898918cc0dSChuck Lever }
8908918cc0dSChuck Lever
8918918cc0dSChuck Lever static __be32
nfsd4_decode_exist_lock_owner4(struct nfsd4_compoundargs * argp,struct nfsd4_lock * lock)8928918cc0dSChuck Lever nfsd4_decode_exist_lock_owner4(struct nfsd4_compoundargs *argp,
8938918cc0dSChuck Lever struct nfsd4_lock *lock)
8948918cc0dSChuck Lever {
8958918cc0dSChuck Lever __be32 status;
8968918cc0dSChuck Lever
8978918cc0dSChuck Lever status = nfsd4_decode_stateid4(argp, &lock->lk_old_lock_stateid);
8988918cc0dSChuck Lever if (status)
8998918cc0dSChuck Lever return status;
9008918cc0dSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lock->lk_old_lock_seqid) < 0)
9018918cc0dSChuck Lever return nfserr_bad_xdr;
9028918cc0dSChuck Lever
9038918cc0dSChuck Lever return nfs_ok;
9048918cc0dSChuck Lever }
9058918cc0dSChuck Lever
9068918cc0dSChuck Lever static __be32
nfsd4_decode_locker4(struct nfsd4_compoundargs * argp,struct nfsd4_lock * lock)9078918cc0dSChuck Lever nfsd4_decode_locker4(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
9088918cc0dSChuck Lever {
9098918cc0dSChuck Lever if (xdr_stream_decode_bool(argp->xdr, &lock->lk_is_new) < 0)
9108918cc0dSChuck Lever return nfserr_bad_xdr;
9118918cc0dSChuck Lever if (lock->lk_is_new)
9128918cc0dSChuck Lever return nfsd4_decode_open_to_lock_owner4(argp, lock);
9138918cc0dSChuck Lever return nfsd4_decode_exist_lock_owner4(argp, lock);
9148918cc0dSChuck Lever }
9158918cc0dSChuck Lever
9168918cc0dSChuck Lever static __be32
nfsd4_decode_lock(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)917e78e274eSKees Cook nfsd4_decode_lock(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
9181da177e4SLinus Torvalds {
919e78e274eSKees Cook struct nfsd4_lock *lock = &u->lock;
9203fdc5464SChuck Lever memset(lock, 0, sizeof(*lock));
9217c59deedSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lock->lk_type) < 0)
9227c59deedSChuck Lever return nfserr_bad_xdr;
9231da177e4SLinus Torvalds if ((lock->lk_type < NFS4_READ_LT) || (lock->lk_type > NFS4_WRITEW_LT))
9247c59deedSChuck Lever return nfserr_bad_xdr;
9257c59deedSChuck Lever if (xdr_stream_decode_bool(argp->xdr, &lock->lk_reclaim) < 0)
9267c59deedSChuck Lever return nfserr_bad_xdr;
9277c59deedSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &lock->lk_offset) < 0)
9287c59deedSChuck Lever return nfserr_bad_xdr;
9297c59deedSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &lock->lk_length) < 0)
9307c59deedSChuck Lever return nfserr_bad_xdr;
9317c59deedSChuck Lever return nfsd4_decode_locker4(argp, lock);
9321da177e4SLinus Torvalds }
9331da177e4SLinus Torvalds
934b37ad28bSAl Viro static __be32
nfsd4_decode_lockt(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)935e78e274eSKees Cook nfsd4_decode_lockt(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
9361da177e4SLinus Torvalds {
937e78e274eSKees Cook struct nfsd4_lockt *lockt = &u->lockt;
9383fdc5464SChuck Lever memset(lockt, 0, sizeof(*lockt));
9390a146f04SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lockt->lt_type) < 0)
9400a146f04SChuck Lever return nfserr_bad_xdr;
9411da177e4SLinus Torvalds if ((lockt->lt_type < NFS4_READ_LT) || (lockt->lt_type > NFS4_WRITEW_LT))
9420a146f04SChuck Lever return nfserr_bad_xdr;
9430a146f04SChuck Lever if (xdr_stream_decode_u64(argp->xdr, &lockt->lt_offset) < 0)
9440a146f04SChuck Lever return nfserr_bad_xdr;
9450a146f04SChuck Lever if (xdr_stream_decode_u64(argp->xdr, &lockt->lt_length) < 0)
9460a146f04SChuck Lever return nfserr_bad_xdr;
9470a146f04SChuck Lever return nfsd4_decode_state_owner4(argp, &lockt->lt_clientid,
9480a146f04SChuck Lever &lockt->lt_owner);
9491da177e4SLinus Torvalds }
9501da177e4SLinus Torvalds
951b37ad28bSAl Viro static __be32
nfsd4_decode_locku(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)952e78e274eSKees Cook nfsd4_decode_locku(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
9531da177e4SLinus Torvalds {
954e78e274eSKees Cook struct nfsd4_locku *locku = &u->locku;
955ca9cf9fcSChuck Lever __be32 status;
9561da177e4SLinus Torvalds
957ca9cf9fcSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &locku->lu_type) < 0)
958ca9cf9fcSChuck Lever return nfserr_bad_xdr;
9591da177e4SLinus Torvalds if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT))
960ca9cf9fcSChuck Lever return nfserr_bad_xdr;
961ca9cf9fcSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &locku->lu_seqid) < 0)
962ca9cf9fcSChuck Lever return nfserr_bad_xdr;
963ca9cf9fcSChuck Lever status = nfsd4_decode_stateid4(argp, &locku->lu_stateid);
964e31a1b66SBenny Halevy if (status)
965e31a1b66SBenny Halevy return status;
966ca9cf9fcSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &locku->lu_offset) < 0)
967ca9cf9fcSChuck Lever return nfserr_bad_xdr;
968ca9cf9fcSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &locku->lu_length) < 0)
969ca9cf9fcSChuck Lever return nfserr_bad_xdr;
9701da177e4SLinus Torvalds
971ca9cf9fcSChuck Lever return nfs_ok;
9721da177e4SLinus Torvalds }
9731da177e4SLinus Torvalds
974b37ad28bSAl Viro static __be32
nfsd4_decode_lookup(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)975e78e274eSKees Cook nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
9761da177e4SLinus Torvalds {
977e78e274eSKees Cook struct nfsd4_lookup *lookup = &u->lookup;
9783d5877e8SChuck Lever return nfsd4_decode_component4(argp, &lookup->lo_name, &lookup->lo_len);
9791da177e4SLinus Torvalds }
9801da177e4SLinus Torvalds
981bf33bab3SChuck Lever static __be32
nfsd4_decode_createhow4(struct nfsd4_compoundargs * argp,struct nfsd4_open * open)982bf33bab3SChuck Lever nfsd4_decode_createhow4(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
983bf33bab3SChuck Lever {
984bf33bab3SChuck Lever __be32 status;
985bf33bab3SChuck Lever
986bf33bab3SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &open->op_createmode) < 0)
987bf33bab3SChuck Lever return nfserr_bad_xdr;
988bf33bab3SChuck Lever switch (open->op_createmode) {
989bf33bab3SChuck Lever case NFS4_CREATE_UNCHECKED:
990bf33bab3SChuck Lever case NFS4_CREATE_GUARDED:
991bf33bab3SChuck Lever status = nfsd4_decode_fattr4(argp, open->op_bmval,
992bf33bab3SChuck Lever ARRAY_SIZE(open->op_bmval),
993bf33bab3SChuck Lever &open->op_iattr, &open->op_acl,
994bf33bab3SChuck Lever &open->op_label, &open->op_umask);
995bf33bab3SChuck Lever if (status)
996bf33bab3SChuck Lever return status;
997bf33bab3SChuck Lever break;
998bf33bab3SChuck Lever case NFS4_CREATE_EXCLUSIVE:
999bf33bab3SChuck Lever status = nfsd4_decode_verifier4(argp, &open->op_verf);
1000bf33bab3SChuck Lever if (status)
1001bf33bab3SChuck Lever return status;
1002bf33bab3SChuck Lever break;
1003bf33bab3SChuck Lever case NFS4_CREATE_EXCLUSIVE4_1:
1004bf33bab3SChuck Lever if (argp->minorversion < 1)
1005bf33bab3SChuck Lever return nfserr_bad_xdr;
1006bf33bab3SChuck Lever status = nfsd4_decode_verifier4(argp, &open->op_verf);
1007bf33bab3SChuck Lever if (status)
1008bf33bab3SChuck Lever return status;
1009bf33bab3SChuck Lever status = nfsd4_decode_fattr4(argp, open->op_bmval,
1010bf33bab3SChuck Lever ARRAY_SIZE(open->op_bmval),
1011bf33bab3SChuck Lever &open->op_iattr, &open->op_acl,
1012bf33bab3SChuck Lever &open->op_label, &open->op_umask);
1013bf33bab3SChuck Lever if (status)
1014bf33bab3SChuck Lever return status;
1015bf33bab3SChuck Lever break;
1016bf33bab3SChuck Lever default:
1017bf33bab3SChuck Lever return nfserr_bad_xdr;
1018bf33bab3SChuck Lever }
1019bf33bab3SChuck Lever
1020bf33bab3SChuck Lever return nfs_ok;
1021bf33bab3SChuck Lever }
1022bf33bab3SChuck Lever
1023e6ec04b2SChuck Lever static __be32
nfsd4_decode_openflag4(struct nfsd4_compoundargs * argp,struct nfsd4_open * open)1024e6ec04b2SChuck Lever nfsd4_decode_openflag4(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
1025e6ec04b2SChuck Lever {
1026e6ec04b2SChuck Lever __be32 status;
1027e6ec04b2SChuck Lever
1028e6ec04b2SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &open->op_create) < 0)
1029e6ec04b2SChuck Lever return nfserr_bad_xdr;
1030e6ec04b2SChuck Lever switch (open->op_create) {
1031e6ec04b2SChuck Lever case NFS4_OPEN_NOCREATE:
1032e6ec04b2SChuck Lever break;
1033e6ec04b2SChuck Lever case NFS4_OPEN_CREATE:
1034e6ec04b2SChuck Lever status = nfsd4_decode_createhow4(argp, open);
1035e6ec04b2SChuck Lever if (status)
1036e6ec04b2SChuck Lever return status;
1037e6ec04b2SChuck Lever break;
1038e6ec04b2SChuck Lever default:
1039e6ec04b2SChuck Lever return nfserr_bad_xdr;
1040e6ec04b2SChuck Lever }
1041e6ec04b2SChuck Lever
1042e6ec04b2SChuck Lever return nfs_ok;
1043e6ec04b2SChuck Lever }
1044e6ec04b2SChuck Lever
nfsd4_decode_share_access(struct nfsd4_compoundargs * argp,u32 * share_access,u32 * deleg_want,u32 * deleg_when)10452c8bd7e0SBenny Halevy static __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *share_access, u32 *deleg_want, u32 *deleg_when)
104604f9e664SJ. Bruce Fields {
104704f9e664SJ. Bruce Fields u32 w;
104804f9e664SJ. Bruce Fields
10499aa62f51SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &w) < 0)
10509aa62f51SChuck Lever return nfserr_bad_xdr;
10512c8bd7e0SBenny Halevy *share_access = w & NFS4_SHARE_ACCESS_MASK;
10522c8bd7e0SBenny Halevy *deleg_want = w & NFS4_SHARE_WANT_MASK;
10532c8bd7e0SBenny Halevy if (deleg_when)
10542c8bd7e0SBenny Halevy *deleg_when = w & NFS4_SHARE_WHEN_MASK;
10552c8bd7e0SBenny Halevy
105604f9e664SJ. Bruce Fields switch (w & NFS4_SHARE_ACCESS_MASK) {
105704f9e664SJ. Bruce Fields case NFS4_SHARE_ACCESS_READ:
105804f9e664SJ. Bruce Fields case NFS4_SHARE_ACCESS_WRITE:
105904f9e664SJ. Bruce Fields case NFS4_SHARE_ACCESS_BOTH:
106004f9e664SJ. Bruce Fields break;
106104f9e664SJ. Bruce Fields default:
106204f9e664SJ. Bruce Fields return nfserr_bad_xdr;
106304f9e664SJ. Bruce Fields }
1064fc0d14feSBenny Halevy w &= ~NFS4_SHARE_ACCESS_MASK;
106504f9e664SJ. Bruce Fields if (!w)
106604f9e664SJ. Bruce Fields return nfs_ok;
106704f9e664SJ. Bruce Fields if (!argp->minorversion)
106804f9e664SJ. Bruce Fields return nfserr_bad_xdr;
106904f9e664SJ. Bruce Fields switch (w & NFS4_SHARE_WANT_MASK) {
107004f9e664SJ. Bruce Fields case NFS4_SHARE_WANT_NO_PREFERENCE:
107104f9e664SJ. Bruce Fields case NFS4_SHARE_WANT_READ_DELEG:
107204f9e664SJ. Bruce Fields case NFS4_SHARE_WANT_WRITE_DELEG:
107304f9e664SJ. Bruce Fields case NFS4_SHARE_WANT_ANY_DELEG:
107404f9e664SJ. Bruce Fields case NFS4_SHARE_WANT_NO_DELEG:
107504f9e664SJ. Bruce Fields case NFS4_SHARE_WANT_CANCEL:
107604f9e664SJ. Bruce Fields break;
107704f9e664SJ. Bruce Fields default:
107804f9e664SJ. Bruce Fields return nfserr_bad_xdr;
107904f9e664SJ. Bruce Fields }
108092bac8c5SBenny Halevy w &= ~NFS4_SHARE_WANT_MASK;
108104f9e664SJ. Bruce Fields if (!w)
108204f9e664SJ. Bruce Fields return nfs_ok;
10832c8bd7e0SBenny Halevy
10842c8bd7e0SBenny Halevy if (!deleg_when) /* open_downgrade */
10852c8bd7e0SBenny Halevy return nfserr_inval;
108604f9e664SJ. Bruce Fields switch (w) {
108704f9e664SJ. Bruce Fields case NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL:
108804f9e664SJ. Bruce Fields case NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED:
1089c668fc6dSBenny Halevy case (NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL |
1090c668fc6dSBenny Halevy NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED):
109104f9e664SJ. Bruce Fields return nfs_ok;
109204f9e664SJ. Bruce Fields }
109304f9e664SJ. Bruce Fields return nfserr_bad_xdr;
109404f9e664SJ. Bruce Fields }
109504f9e664SJ. Bruce Fields
nfsd4_decode_share_deny(struct nfsd4_compoundargs * argp,u32 * x)109604f9e664SJ. Bruce Fields static __be32 nfsd4_decode_share_deny(struct nfsd4_compoundargs *argp, u32 *x)
109704f9e664SJ. Bruce Fields {
1098b07bebd9SChuck Lever if (xdr_stream_decode_u32(argp->xdr, x) < 0)
1099b07bebd9SChuck Lever return nfserr_bad_xdr;
1100b07bebd9SChuck Lever /* Note: unlike access bits, deny bits may be zero. */
110101cd4afaSDan Carpenter if (*x & ~NFS4_SHARE_DENY_BOTH)
110204f9e664SJ. Bruce Fields return nfserr_bad_xdr;
1103b07bebd9SChuck Lever
110404f9e664SJ. Bruce Fields return nfs_ok;
110504f9e664SJ. Bruce Fields }
110604f9e664SJ. Bruce Fields
1107b37ad28bSAl Viro static __be32
nfsd4_decode_open_claim4(struct nfsd4_compoundargs * argp,struct nfsd4_open * open)11081708e50bSChuck Lever nfsd4_decode_open_claim4(struct nfsd4_compoundargs *argp,
11091708e50bSChuck Lever struct nfsd4_open *open)
11101708e50bSChuck Lever {
11111708e50bSChuck Lever __be32 status;
11121708e50bSChuck Lever
11131708e50bSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &open->op_claim_type) < 0)
11141708e50bSChuck Lever return nfserr_bad_xdr;
11151708e50bSChuck Lever switch (open->op_claim_type) {
11161708e50bSChuck Lever case NFS4_OPEN_CLAIM_NULL:
11171708e50bSChuck Lever case NFS4_OPEN_CLAIM_DELEGATE_PREV:
11181708e50bSChuck Lever status = nfsd4_decode_component4(argp, &open->op_fname,
11191708e50bSChuck Lever &open->op_fnamelen);
11201708e50bSChuck Lever if (status)
11211708e50bSChuck Lever return status;
11221708e50bSChuck Lever break;
11231708e50bSChuck Lever case NFS4_OPEN_CLAIM_PREVIOUS:
11241708e50bSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &open->op_delegate_type) < 0)
11251708e50bSChuck Lever return nfserr_bad_xdr;
11261708e50bSChuck Lever break;
11271708e50bSChuck Lever case NFS4_OPEN_CLAIM_DELEGATE_CUR:
11281708e50bSChuck Lever status = nfsd4_decode_stateid4(argp, &open->op_delegate_stateid);
11291708e50bSChuck Lever if (status)
11301708e50bSChuck Lever return status;
11311708e50bSChuck Lever status = nfsd4_decode_component4(argp, &open->op_fname,
11321708e50bSChuck Lever &open->op_fnamelen);
11331708e50bSChuck Lever if (status)
11341708e50bSChuck Lever return status;
11351708e50bSChuck Lever break;
11361708e50bSChuck Lever case NFS4_OPEN_CLAIM_FH:
11371708e50bSChuck Lever case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
11381708e50bSChuck Lever if (argp->minorversion < 1)
11391708e50bSChuck Lever return nfserr_bad_xdr;
11401708e50bSChuck Lever /* void */
11411708e50bSChuck Lever break;
11421708e50bSChuck Lever case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
11431708e50bSChuck Lever if (argp->minorversion < 1)
11441708e50bSChuck Lever return nfserr_bad_xdr;
11451708e50bSChuck Lever status = nfsd4_decode_stateid4(argp, &open->op_delegate_stateid);
11461708e50bSChuck Lever if (status)
11471708e50bSChuck Lever return status;
11481708e50bSChuck Lever break;
11491708e50bSChuck Lever default:
11501708e50bSChuck Lever return nfserr_bad_xdr;
11511708e50bSChuck Lever }
11521708e50bSChuck Lever
11531708e50bSChuck Lever return nfs_ok;
11541708e50bSChuck Lever }
11551708e50bSChuck Lever
11561708e50bSChuck Lever static __be32
nfsd4_decode_open(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1157e78e274eSKees Cook nfsd4_decode_open(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
11581da177e4SLinus Torvalds {
1159e78e274eSKees Cook struct nfsd4_open *open = &u->open;
116061e5e0b3SChuck Lever __be32 status;
11612c8bd7e0SBenny Halevy u32 dummy;
11621da177e4SLinus Torvalds
11633fdc5464SChuck Lever memset(open, 0, sizeof(*open));
11641da177e4SLinus Torvalds
116561e5e0b3SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &open->op_seqid) < 0)
116661e5e0b3SChuck Lever return nfserr_bad_xdr;
116761e5e0b3SChuck Lever /* deleg_want is ignored */
11682c8bd7e0SBenny Halevy status = nfsd4_decode_share_access(argp, &open->op_share_access,
11692c8bd7e0SBenny Halevy &open->op_deleg_want, &dummy);
117004f9e664SJ. Bruce Fields if (status)
117161e5e0b3SChuck Lever return status;
117204f9e664SJ. Bruce Fields status = nfsd4_decode_share_deny(argp, &open->op_share_deny);
117304f9e664SJ. Bruce Fields if (status)
117461e5e0b3SChuck Lever return status;
117561e5e0b3SChuck Lever status = nfsd4_decode_state_owner4(argp, &open->op_clientid,
117661e5e0b3SChuck Lever &open->op_owner);
1177a084daf5SJ. Bruce Fields if (status)
117861e5e0b3SChuck Lever return status;
1179e6ec04b2SChuck Lever status = nfsd4_decode_openflag4(argp, open);
1180796dd1c6SChuck Lever if (status)
1181796dd1c6SChuck Lever return status;
118261e5e0b3SChuck Lever return nfsd4_decode_open_claim4(argp, open);
11831da177e4SLinus Torvalds }
11841da177e4SLinus Torvalds
1185b37ad28bSAl Viro static __be32
nfsd4_decode_open_confirm(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1186e78e274eSKees Cook nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp,
1187e78e274eSKees Cook union nfsd4_op_u *u)
11881da177e4SLinus Torvalds {
1189e78e274eSKees Cook struct nfsd4_open_confirm *open_conf = &u->open_confirm;
119006bee693SChuck Lever __be32 status;
11911da177e4SLinus Torvalds
1192e1a90ebdSAnna Schumaker if (argp->minorversion >= 1)
1193e1a90ebdSAnna Schumaker return nfserr_notsupp;
1194e1a90ebdSAnna Schumaker
119506bee693SChuck Lever status = nfsd4_decode_stateid4(argp, &open_conf->oc_req_stateid);
1196e31a1b66SBenny Halevy if (status)
1197e31a1b66SBenny Halevy return status;
119806bee693SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &open_conf->oc_seqid) < 0)
119906bee693SChuck Lever return nfserr_bad_xdr;
12001da177e4SLinus Torvalds
12013fdc5464SChuck Lever memset(&open_conf->oc_resp_stateid, 0,
12023fdc5464SChuck Lever sizeof(open_conf->oc_resp_stateid));
120306bee693SChuck Lever return nfs_ok;
12041da177e4SLinus Torvalds }
12051da177e4SLinus Torvalds
1206b37ad28bSAl Viro static __be32
nfsd4_decode_open_downgrade(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1207e78e274eSKees Cook nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp,
1208e78e274eSKees Cook union nfsd4_op_u *u)
12091da177e4SLinus Torvalds {
1210e78e274eSKees Cook struct nfsd4_open_downgrade *open_down = &u->open_downgrade;
1211dca71651SChuck Lever __be32 status;
12121da177e4SLinus Torvalds
12133fdc5464SChuck Lever memset(open_down, 0, sizeof(*open_down));
1214dca71651SChuck Lever status = nfsd4_decode_stateid4(argp, &open_down->od_stateid);
1215e31a1b66SBenny Halevy if (status)
1216e31a1b66SBenny Halevy return status;
1217dca71651SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &open_down->od_seqid) < 0)
1218dca71651SChuck Lever return nfserr_bad_xdr;
1219dca71651SChuck Lever /* deleg_want is ignored */
12202c8bd7e0SBenny Halevy status = nfsd4_decode_share_access(argp, &open_down->od_share_access,
12212c8bd7e0SBenny Halevy &open_down->od_deleg_want, NULL);
122204f9e664SJ. Bruce Fields if (status)
122304f9e664SJ. Bruce Fields return status;
1224dca71651SChuck Lever return nfsd4_decode_share_deny(argp, &open_down->od_share_deny);
12251da177e4SLinus Torvalds }
12261da177e4SLinus Torvalds
1227b37ad28bSAl Viro static __be32
nfsd4_decode_putfh(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1228e78e274eSKees Cook nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
12291da177e4SLinus Torvalds {
1230e78e274eSKees Cook struct nfsd4_putfh *putfh = &u->putfh;
1231a73bed98SChuck Lever __be32 *p;
12321da177e4SLinus Torvalds
1233a73bed98SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &putfh->pf_fhlen) < 0)
1234a73bed98SChuck Lever return nfserr_bad_xdr;
12351da177e4SLinus Torvalds if (putfh->pf_fhlen > NFS4_FHSIZE)
1236a73bed98SChuck Lever return nfserr_bad_xdr;
1237a73bed98SChuck Lever p = xdr_inline_decode(argp->xdr, putfh->pf_fhlen);
1238a73bed98SChuck Lever if (!p)
1239a73bed98SChuck Lever return nfserr_bad_xdr;
12407b723008SChuck Lever putfh->pf_fhval = svcxdr_savemem(argp, p, putfh->pf_fhlen);
1241a73bed98SChuck Lever if (!putfh->pf_fhval)
1242a73bed98SChuck Lever return nfserr_jukebox;
12431da177e4SLinus Torvalds
12443fdc5464SChuck Lever putfh->no_verify = false;
1245a73bed98SChuck Lever return nfs_ok;
12461da177e4SLinus Torvalds }
12471da177e4SLinus Torvalds
1248b37ad28bSAl Viro static __be32
nfsd4_decode_read(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1249e78e274eSKees Cook nfsd4_decode_read(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
1250e1a90ebdSAnna Schumaker {
1251e1a90ebdSAnna Schumaker struct nfsd4_read *read = &u->read;
1252e1a90ebdSAnna Schumaker __be32 status;
1253e1a90ebdSAnna Schumaker
1254e1a90ebdSAnna Schumaker memset(read, 0, sizeof(*read));
1255e1a90ebdSAnna Schumaker status = nfsd4_decode_stateid4(argp, &read->rd_stateid);
1256e1a90ebdSAnna Schumaker if (status)
1257e78e274eSKees Cook return status;
12581da177e4SLinus Torvalds if (xdr_stream_decode_u64(argp->xdr, &read->rd_offset) < 0)
1259e78e274eSKees Cook return nfserr_bad_xdr;
12603909c3bcSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &read->rd_length) < 0)
12611da177e4SLinus Torvalds return nfserr_bad_xdr;
12623fdc5464SChuck Lever
12633909c3bcSChuck Lever return nfs_ok;
1264e31a1b66SBenny Halevy }
1265e31a1b66SBenny Halevy
12663909c3bcSChuck Lever static __be32
nfsd4_decode_readdir(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)12673909c3bcSChuck Lever nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
12683909c3bcSChuck Lever {
12693909c3bcSChuck Lever struct nfsd4_readdir *readdir = &u->readdir;
12701da177e4SLinus Torvalds __be32 status;
12713909c3bcSChuck Lever
12721da177e4SLinus Torvalds memset(readdir, 0, sizeof(*readdir));
12731da177e4SLinus Torvalds if (xdr_stream_decode_u64(argp->xdr, &readdir->rd_cookie) < 0)
1274b37ad28bSAl Viro return nfserr_bad_xdr;
1275e78e274eSKees Cook status = nfsd4_decode_verifier4(argp, &readdir->rd_verf);
12761da177e4SLinus Torvalds if (status)
1277e78e274eSKees Cook return status;
12780dfaf2a3SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &readdir->rd_dircount) < 0)
12791da177e4SLinus Torvalds return nfserr_bad_xdr;
12803fdc5464SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &readdir->rd_maxcount) < 0)
12810dfaf2a3SChuck Lever return nfserr_bad_xdr;
12820dfaf2a3SChuck Lever if (xdr_stream_decode_uint32_array(argp->xdr, readdir->rd_bmval,
12830dfaf2a3SChuck Lever ARRAY_SIZE(readdir->rd_bmval)) < 0)
12840dfaf2a3SChuck Lever return nfserr_bad_xdr;
12850dfaf2a3SChuck Lever
12860dfaf2a3SChuck Lever return nfs_ok;
12870dfaf2a3SChuck Lever }
12880dfaf2a3SChuck Lever
12890dfaf2a3SChuck Lever static __be32
nfsd4_decode_remove(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)12900dfaf2a3SChuck Lever nfsd4_decode_remove(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
12910dfaf2a3SChuck Lever {
12920dfaf2a3SChuck Lever struct nfsd4_remove *remove = &u->remove;
12931da177e4SLinus Torvalds memset(&remove->rm_cinfo, 0, sizeof(remove->rm_cinfo));
12940dfaf2a3SChuck Lever return nfsd4_decode_component4(argp, &remove->rm_name, &remove->rm_namelen);
12951da177e4SLinus Torvalds }
12961da177e4SLinus Torvalds
1297b37ad28bSAl Viro static __be32
nfsd4_decode_rename(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1298e78e274eSKees Cook nfsd4_decode_rename(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
12991da177e4SLinus Torvalds {
1300e78e274eSKees Cook struct nfsd4_rename *rename = &u->rename;
13013fdc5464SChuck Lever __be32 status;
1302b7f5fbf2SChuck Lever
13031da177e4SLinus Torvalds memset(rename, 0, sizeof(*rename));
13041da177e4SLinus Torvalds status = nfsd4_decode_component4(argp, &rename->rn_sname, &rename->rn_snamelen);
1305b37ad28bSAl Viro if (status)
1306e78e274eSKees Cook return status;
13071da177e4SLinus Torvalds return nfsd4_decode_component4(argp, &rename->rn_tname, &rename->rn_tnamelen);
1308e78e274eSKees Cook }
1309ba881a0aSChuck Lever
13101da177e4SLinus Torvalds static __be32
nfsd4_decode_renew(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)13113fdc5464SChuck Lever nfsd4_decode_renew(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
1312ba881a0aSChuck Lever {
1313ba881a0aSChuck Lever clientid_t *clientid = &u->renew;
13141da177e4SLinus Torvalds return nfsd4_decode_clientid4(argp, clientid);
1315ba881a0aSChuck Lever }
13161da177e4SLinus Torvalds
13171da177e4SLinus Torvalds static __be32
nfsd4_decode_secinfo(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1318b37ad28bSAl Viro nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp,
1319e78e274eSKees Cook union nfsd4_op_u *u)
13201da177e4SLinus Torvalds {
1321e78e274eSKees Cook struct nfsd4_secinfo *secinfo = &u->secinfo;
1322d12f9045SChuck Lever secinfo->si_exp = NULL;
13231da177e4SLinus Torvalds return nfsd4_decode_component4(argp, &secinfo->si_name, &secinfo->si_namelen);
13241da177e4SLinus Torvalds }
1325b37ad28bSAl Viro
1326dcb488a3SAndy Adamson static __be32
nfsd4_decode_setattr(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1327e78e274eSKees Cook nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
1328dcb488a3SAndy Adamson {
1329e78e274eSKees Cook struct nfsd4_setattr *setattr = &u->setattr;
13303fdc5464SChuck Lever __be32 status;
1331d0abdae5SChuck Lever
1332dcb488a3SAndy Adamson memset(setattr, 0, sizeof(*setattr));
1333dcb488a3SAndy Adamson status = nfsd4_decode_stateid4(argp, &setattr->sa_stateid);
1334dcb488a3SAndy Adamson if (status)
1335e78e274eSKees Cook return status;
13361da177e4SLinus Torvalds return nfsd4_decode_fattr4(argp, setattr->sa_bmval,
1337e78e274eSKees Cook ARRAY_SIZE(setattr->sa_bmval),
1338e31a1b66SBenny Halevy &setattr->sa_iattr, &setattr->sa_acl,
13391da177e4SLinus Torvalds &setattr->sa_label, NULL);
13403fdc5464SChuck Lever }
134144592fe9SChuck Lever
1342e31a1b66SBenny Halevy static __be32
nfsd4_decode_setclientid(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1343e31a1b66SBenny Halevy nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
1344d1c263a0SChuck Lever {
1345d1c263a0SChuck Lever struct nfsd4_setclientid *setclientid = &u->setclientid;
1346d1c263a0SChuck Lever __be32 *p, status;
1347d1c263a0SChuck Lever
13481da177e4SLinus Torvalds memset(setclientid, 0, sizeof(*setclientid));
13491da177e4SLinus Torvalds
1350b37ad28bSAl Viro if (argp->minorversion >= 1)
1351e78e274eSKees Cook return nfserr_notsupp;
13521da177e4SLinus Torvalds
1353e78e274eSKees Cook status = nfsd4_decode_verifier4(argp, &setclientid->se_verf);
135492fa6c08SChuck Lever if (status)
13551da177e4SLinus Torvalds return status;
13563fdc5464SChuck Lever status = nfsd4_decode_opaque(argp, &setclientid->se_name);
13573fdc5464SChuck Lever if (status)
1358e1a90ebdSAnna Schumaker return status;
1359e1a90ebdSAnna Schumaker if (xdr_stream_decode_u32(argp->xdr, &setclientid->se_callback_prog) < 0)
1360e1a90ebdSAnna Schumaker return nfserr_bad_xdr;
136192fa6c08SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &setclientid->se_callback_netid_len) < 0)
136292fa6c08SChuck Lever return nfserr_bad_xdr;
136392fa6c08SChuck Lever p = xdr_inline_decode(argp->xdr, setclientid->se_callback_netid_len);
1364a084daf5SJ. Bruce Fields if (!p)
1365a084daf5SJ. Bruce Fields return nfserr_bad_xdr;
136692fa6c08SChuck Lever setclientid->se_callback_netid_val = svcxdr_savemem(argp, p,
136792fa6c08SChuck Lever setclientid->se_callback_netid_len);
1368a084daf5SJ. Bruce Fields if (!setclientid->se_callback_netid_val)
136992fa6c08SChuck Lever return nfserr_jukebox;
137092fa6c08SChuck Lever
137192fa6c08SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &setclientid->se_callback_addr_len) < 0)
137292fa6c08SChuck Lever return nfserr_bad_xdr;
137392fa6c08SChuck Lever p = xdr_inline_decode(argp->xdr, setclientid->se_callback_addr_len);
13747b723008SChuck Lever if (!p)
137592fa6c08SChuck Lever return nfserr_bad_xdr;
137692fa6c08SChuck Lever setclientid->se_callback_addr_val = svcxdr_savemem(argp, p,
137792fa6c08SChuck Lever setclientid->se_callback_addr_len);
13781da177e4SLinus Torvalds if (!setclientid->se_callback_addr_val)
137992fa6c08SChuck Lever return nfserr_jukebox;
138092fa6c08SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &setclientid->se_callback_ident) < 0)
138192fa6c08SChuck Lever return nfserr_bad_xdr;
138292fa6c08SChuck Lever
138392fa6c08SChuck Lever return nfs_ok;
13847b723008SChuck Lever }
138592fa6c08SChuck Lever
138692fa6c08SChuck Lever static __be32
nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)138792fa6c08SChuck Lever nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp,
138892fa6c08SChuck Lever union nfsd4_op_u *u)
138992fa6c08SChuck Lever {
13901da177e4SLinus Torvalds struct nfsd4_setclientid_confirm *scd_c = &u->setclientid_confirm;
139192fa6c08SChuck Lever __be32 status;
13921da177e4SLinus Torvalds
13931da177e4SLinus Torvalds if (argp->minorversion >= 1)
1394b37ad28bSAl Viro return nfserr_notsupp;
1395e78e274eSKees Cook
1396e78e274eSKees Cook status = nfsd4_decode_clientid4(argp, &scd_c->sc_clientid);
13971da177e4SLinus Torvalds if (status)
1398e78e274eSKees Cook return status;
1399d1ca5514SChuck Lever return nfsd4_decode_verifier4(argp, &scd_c->sc_confirm);
14001da177e4SLinus Torvalds }
1401e1a90ebdSAnna Schumaker
1402e1a90ebdSAnna Schumaker /* Also used for NVERIFY */
1403e1a90ebdSAnna Schumaker static __be32
nfsd4_decode_verify(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1404d1ca5514SChuck Lever nfsd4_decode_verify(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
1405d1ca5514SChuck Lever {
1406d1ca5514SChuck Lever struct nfsd4_verify *verify = &u->verify;
1407d1ca5514SChuck Lever __be32 *p, status;
14081da177e4SLinus Torvalds
14091da177e4SLinus Torvalds memset(verify, 0, sizeof(*verify));
14101da177e4SLinus Torvalds
1411b37ad28bSAl Viro status = nfsd4_decode_bitmap4(argp, verify->ve_bmval,
1412e78e274eSKees Cook ARRAY_SIZE(verify->ve_bmval));
14131da177e4SLinus Torvalds if (status)
1414e78e274eSKees Cook return status;
141567cd453eSChuck Lever
14161da177e4SLinus Torvalds /* For convenience's sake, we compare raw xdr'd attributes in
14173fdc5464SChuck Lever * nfsd4_proc_verify */
14183fdc5464SChuck Lever
141967cd453eSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &verify->ve_attrlen) < 0)
142067cd453eSChuck Lever return nfserr_bad_xdr;
142167cd453eSChuck Lever p = xdr_inline_decode(argp->xdr, verify->ve_attrlen);
142267cd453eSChuck Lever if (!p)
14231da177e4SLinus Torvalds return nfserr_bad_xdr;
14241da177e4SLinus Torvalds verify->ve_attrval = svcxdr_savemem(argp, p, verify->ve_attrlen);
1425e5f95703SJ. Bruce Fields if (!verify->ve_attrval)
1426e5f95703SJ. Bruce Fields return nfserr_jukebox;
142767cd453eSChuck Lever
142867cd453eSChuck Lever return nfs_ok;
142967cd453eSChuck Lever }
143067cd453eSChuck Lever
143167cd453eSChuck Lever static __be32
nfsd4_decode_write(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)14327b723008SChuck Lever nfsd4_decode_write(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
143367cd453eSChuck Lever {
143467cd453eSChuck Lever struct nfsd4_write *write = &u->write;
14351da177e4SLinus Torvalds __be32 status;
143667cd453eSChuck Lever
14371da177e4SLinus Torvalds status = nfsd4_decode_stateid4(argp, &write->wr_stateid);
14381da177e4SLinus Torvalds if (status)
1439b37ad28bSAl Viro return status;
1440e78e274eSKees Cook if (xdr_stream_decode_u64(argp->xdr, &write->wr_offset) < 0)
14411da177e4SLinus Torvalds return nfserr_bad_xdr;
1442e78e274eSKees Cook if (xdr_stream_decode_u32(argp->xdr, &write->wr_stable_how) < 0)
1443244e2befSChuck Lever return nfserr_bad_xdr;
14441da177e4SLinus Torvalds if (write->wr_stable_how > NFS_FILE_SYNC)
1445244e2befSChuck Lever return nfserr_bad_xdr;
1446e31a1b66SBenny Halevy if (xdr_stream_decode_u32(argp->xdr, &write->wr_buflen) < 0)
1447e31a1b66SBenny Halevy return nfserr_bad_xdr;
1448244e2befSChuck Lever if (!xdr_stream_subsegment(argp->xdr, &write->wr_payload, write->wr_buflen))
1449244e2befSChuck Lever return nfserr_bad_xdr;
1450244e2befSChuck Lever
1451244e2befSChuck Lever write->wr_bytes_written = 0;
145254bbb7d2SKinglong Mee write->wr_how_written = 0;
1453244e2befSChuck Lever memset(&write->wr_verifier, 0, sizeof(write->wr_verifier));
1454244e2befSChuck Lever return nfs_ok;
1455244e2befSChuck Lever }
1456c1346a12SChuck Lever
1457244e2befSChuck Lever static __be32
nfsd4_decode_release_lockowner(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)14581da177e4SLinus Torvalds nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp,
14593fdc5464SChuck Lever union nfsd4_op_u *u)
14603fdc5464SChuck Lever {
14613fdc5464SChuck Lever struct nfsd4_release_lockowner *rlockowner = &u->release_lockowner;
1462244e2befSChuck Lever __be32 status;
14631da177e4SLinus Torvalds
14641da177e4SLinus Torvalds if (argp->minorversion >= 1)
1465b37ad28bSAl Viro return nfserr_notsupp;
1466e78e274eSKees Cook
1467e78e274eSKees Cook status = nfsd4_decode_state_owner4(argp, &rlockowner->rl_clientid,
14681da177e4SLinus Torvalds &rlockowner->rl_owner);
1469e78e274eSKees Cook if (status)
1470a4a80c15SChuck Lever return status;
14711da177e4SLinus Torvalds
1472e1a90ebdSAnna Schumaker if (argp->minorversion && !zero_clientid(&rlockowner->rl_clientid))
1473e1a90ebdSAnna Schumaker return nfserr_inval;
1474e1a90ebdSAnna Schumaker
1475a4a80c15SChuck Lever return nfs_ok;
1476a4a80c15SChuck Lever }
1477a4a80c15SChuck Lever
nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1478a4a80c15SChuck Lever static __be32 nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs *argp,
14791da177e4SLinus Torvalds union nfsd4_op_u *u)
148060adfc50SAndy Adamson {
148160adfc50SAndy Adamson struct nfsd4_backchannel_ctl *bc = &u->backchannel_ctl;
1482a4a80c15SChuck Lever memset(bc, 0, sizeof(*bc));
1483a4a80c15SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &bc->bc_cb_program) < 0)
14841da177e4SLinus Torvalds return nfserr_bad_xdr;
14851da177e4SLinus Torvalds return nfsd4_decode_cb_sec(argp, &bc->bc_cb_sec);
1486e78e274eSKees Cook }
1487e78e274eSKees Cook
nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)14880f81d960SChuck Lever static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp,
1489e78e274eSKees Cook union nfsd4_op_u *u)
14903fdc5464SChuck Lever {
14910f81d960SChuck Lever struct nfsd4_bind_conn_to_session *bcts = &u->bind_conn_to_session;
14920f81d960SChuck Lever u32 use_conn_in_rdma_mode;
14930f81d960SChuck Lever __be32 status;
14940f81d960SChuck Lever
14950f81d960SChuck Lever memset(bcts, 0, sizeof(*bcts));
1496e78e274eSKees Cook status = nfsd4_decode_sessionid4(argp, &bcts->sessionid);
1497e78e274eSKees Cook if (status)
1498571e0451SChuck Lever return status;
1499e78e274eSKees Cook if (xdr_stream_decode_u32(argp->xdr, &bcts->dir) < 0)
1500571e0451SChuck Lever return nfserr_bad_xdr;
1501571e0451SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &use_conn_in_rdma_mode) < 0)
1502571e0451SChuck Lever return nfserr_bad_xdr;
15033fdc5464SChuck Lever
1504571e0451SChuck Lever return nfs_ok;
1505571e0451SChuck Lever }
1506571e0451SChuck Lever
1507571e0451SChuck Lever static __be32
nfsd4_decode_state_protect_ops(struct nfsd4_compoundargs * argp,struct nfsd4_exchange_id * exid)1508571e0451SChuck Lever nfsd4_decode_state_protect_ops(struct nfsd4_compoundargs *argp,
1509571e0451SChuck Lever struct nfsd4_exchange_id *exid)
1510571e0451SChuck Lever {
1511571e0451SChuck Lever __be32 status;
1512571e0451SChuck Lever
1513571e0451SChuck Lever status = nfsd4_decode_bitmap4(argp, exid->spo_must_enforce,
1514571e0451SChuck Lever ARRAY_SIZE(exid->spo_must_enforce));
1515b37ad28bSAl Viro if (status)
15162548aa78SChuck Lever return nfserr_bad_xdr;
15172548aa78SChuck Lever status = nfsd4_decode_bitmap4(argp, exid->spo_must_allow,
15182548aa78SChuck Lever ARRAY_SIZE(exid->spo_must_allow));
15192548aa78SChuck Lever if (status)
15202548aa78SChuck Lever return nfserr_bad_xdr;
15212548aa78SChuck Lever
15222548aa78SChuck Lever return nfs_ok;
15232548aa78SChuck Lever }
15242548aa78SChuck Lever
15252548aa78SChuck Lever /*
15262548aa78SChuck Lever * This implementation currently does not support SP4_SSV.
15272548aa78SChuck Lever * This decoder simply skips over these arguments.
15282548aa78SChuck Lever */
15292548aa78SChuck Lever static noinline __be32
nfsd4_decode_ssv_sp_parms(struct nfsd4_compoundargs * argp,struct nfsd4_exchange_id * exid)15302548aa78SChuck Lever nfsd4_decode_ssv_sp_parms(struct nfsd4_compoundargs *argp,
15312548aa78SChuck Lever struct nfsd4_exchange_id *exid)
15322548aa78SChuck Lever {
1533547bfeb4SChuck Lever u32 count, window, num_gss_handles;
1534547bfeb4SChuck Lever __be32 status;
1535547bfeb4SChuck Lever
1536547bfeb4SChuck Lever /* ssp_ops */
1537547bfeb4SChuck Lever status = nfsd4_decode_state_protect_ops(argp, exid);
1538547bfeb4SChuck Lever if (status)
1539547bfeb4SChuck Lever return status;
1540547bfeb4SChuck Lever
1541547bfeb4SChuck Lever /* ssp_hash_algs<> */
1542547bfeb4SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
1543547bfeb4SChuck Lever return nfserr_bad_xdr;
1544547bfeb4SChuck Lever while (count--) {
1545547bfeb4SChuck Lever status = nfsd4_decode_ignored_string(argp, 0);
1546547bfeb4SChuck Lever if (status)
1547547bfeb4SChuck Lever return status;
1548547bfeb4SChuck Lever }
1549547bfeb4SChuck Lever
1550547bfeb4SChuck Lever /* ssp_encr_algs<> */
1551547bfeb4SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
1552547bfeb4SChuck Lever return nfserr_bad_xdr;
1553547bfeb4SChuck Lever while (count--) {
1554547bfeb4SChuck Lever status = nfsd4_decode_ignored_string(argp, 0);
1555547bfeb4SChuck Lever if (status)
1556547bfeb4SChuck Lever return status;
1557547bfeb4SChuck Lever }
1558547bfeb4SChuck Lever
1559547bfeb4SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &window) < 0)
1560547bfeb4SChuck Lever return nfserr_bad_xdr;
1561547bfeb4SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &num_gss_handles) < 0)
1562547bfeb4SChuck Lever return nfserr_bad_xdr;
1563547bfeb4SChuck Lever
1564547bfeb4SChuck Lever return nfs_ok;
1565547bfeb4SChuck Lever }
1566547bfeb4SChuck Lever
1567547bfeb4SChuck Lever static __be32
nfsd4_decode_state_protect4_a(struct nfsd4_compoundargs * argp,struct nfsd4_exchange_id * exid)1568547bfeb4SChuck Lever nfsd4_decode_state_protect4_a(struct nfsd4_compoundargs *argp,
1569547bfeb4SChuck Lever struct nfsd4_exchange_id *exid)
1570547bfeb4SChuck Lever {
1571547bfeb4SChuck Lever __be32 status;
1572547bfeb4SChuck Lever
1573547bfeb4SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &exid->spa_how) < 0)
1574547bfeb4SChuck Lever return nfserr_bad_xdr;
15752548aa78SChuck Lever switch (exid->spa_how) {
1576523ec6edSChuck Lever case SP4_NONE:
15770733d213SAndy Adamson break;
15782db134ebSAndy Adamson case SP4_MACH_CRED:
1579523ec6edSChuck Lever status = nfsd4_decode_state_protect_ops(argp, exid);
15800733d213SAndy Adamson if (status)
1581523ec6edSChuck Lever return status;
1582a084daf5SJ. Bruce Fields break;
15830733d213SAndy Adamson case SP4_SSV:
15840733d213SAndy Adamson status = nfsd4_decode_ssv_sp_parms(argp, exid);
15850733d213SAndy Adamson if (status)
15860733d213SAndy Adamson return status;
15872548aa78SChuck Lever break;
1588ed941643SAndrew Elble default:
15892548aa78SChuck Lever return nfserr_bad_xdr;
15900733d213SAndy Adamson }
15910733d213SAndy Adamson
1592547bfeb4SChuck Lever return nfs_ok;
15932548aa78SChuck Lever }
15942548aa78SChuck Lever
15950733d213SAndy Adamson static __be32
nfsd4_decode_nfs_impl_id4(struct nfsd4_compoundargs * argp,struct nfsd4_exchange_id * exid)15960733d213SAndy Adamson nfsd4_decode_nfs_impl_id4(struct nfsd4_compoundargs *argp,
1597523ec6edSChuck Lever struct nfsd4_exchange_id *exid)
15980733d213SAndy Adamson {
15990733d213SAndy Adamson __be32 status;
1600523ec6edSChuck Lever u32 count;
1601523ec6edSChuck Lever
1602523ec6edSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
1603523ec6edSChuck Lever return nfserr_bad_xdr;
160410ff8422SChuck Lever switch (count) {
160510ff8422SChuck Lever case 0:
160610ff8422SChuck Lever break;
160710ff8422SChuck Lever case 1:
160810ff8422SChuck Lever /* Note that RFC 8881 places no length limit on
160910ff8422SChuck Lever * nii_domain, but this implementation permits no
161010ff8422SChuck Lever * more than NFS4_OPAQUE_LIMIT bytes */
161110ff8422SChuck Lever status = nfsd4_decode_opaque(argp, &exid->nii_domain);
161210ff8422SChuck Lever if (status)
161310ff8422SChuck Lever return status;
161410ff8422SChuck Lever /* Note that RFC 8881 places no length limit on
161510ff8422SChuck Lever * nii_name, but this implementation permits no
161610ff8422SChuck Lever * more than NFS4_OPAQUE_LIMIT bytes */
161710ff8422SChuck Lever status = nfsd4_decode_opaque(argp, &exid->nii_name);
161810ff8422SChuck Lever if (status)
161910ff8422SChuck Lever return status;
162010ff8422SChuck Lever status = nfsd4_decode_nfstime4(argp, &exid->nii_time);
162110ff8422SChuck Lever if (status)
162210ff8422SChuck Lever return status;
162310ff8422SChuck Lever break;
162410ff8422SChuck Lever default:
162510ff8422SChuck Lever return nfserr_bad_xdr;
162610ff8422SChuck Lever }
162710ff8422SChuck Lever
162810ff8422SChuck Lever return nfs_ok;
162910ff8422SChuck Lever }
163010ff8422SChuck Lever
163110ff8422SChuck Lever static __be32
nfsd4_decode_exchange_id(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)163210ff8422SChuck Lever nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
163310ff8422SChuck Lever union nfsd4_op_u *u)
163410ff8422SChuck Lever {
163510ff8422SChuck Lever struct nfsd4_exchange_id *exid = &u->exchange_id;
163610ff8422SChuck Lever __be32 status;
163710ff8422SChuck Lever
163810ff8422SChuck Lever memset(exid, 0, sizeof(*exid));
163910ff8422SChuck Lever status = nfsd4_decode_verifier4(argp, &exid->verifier);
1640523ec6edSChuck Lever if (status)
1641e78e274eSKees Cook return status;
1642523ec6edSChuck Lever status = nfsd4_decode_opaque(argp, &exid->clname);
1643e78e274eSKees Cook if (status)
164410ff8422SChuck Lever return status;
1645523ec6edSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &exid->flags) < 0)
16463fdc5464SChuck Lever return nfserr_bad_xdr;
1647523ec6edSChuck Lever status = nfsd4_decode_state_protect4_a(argp, exid);
1648523ec6edSChuck Lever if (status)
1649523ec6edSChuck Lever return status;
1650523ec6edSChuck Lever return nfsd4_decode_nfs_impl_id4(argp, exid);
1651523ec6edSChuck Lever }
1652523ec6edSChuck Lever
1653523ec6edSChuck Lever static __be32
nfsd4_decode_channel_attrs4(struct nfsd4_compoundargs * argp,struct nfsd4_channel_attrs * ca)1654523ec6edSChuck Lever nfsd4_decode_channel_attrs4(struct nfsd4_compoundargs *argp,
1655523ec6edSChuck Lever struct nfsd4_channel_attrs *ca)
1656523ec6edSChuck Lever {
1657523ec6edSChuck Lever __be32 *p;
165810ff8422SChuck Lever
16592db134ebSAndy Adamson p = xdr_inline_decode(argp->xdr, XDR_UNIT * 7);
16602db134ebSAndy Adamson if (!p)
16612db134ebSAndy Adamson return nfserr_bad_xdr;
16623a3f1fbaSChuck Lever
16633a3f1fbaSChuck Lever /* headerpadsz is ignored */
16643a3f1fbaSChuck Lever p++;
16653a3f1fbaSChuck Lever ca->maxreq_sz = be32_to_cpup(p++);
16663a3f1fbaSChuck Lever ca->maxresp_sz = be32_to_cpup(p++);
16673a3f1fbaSChuck Lever ca->maxresp_cached = be32_to_cpup(p++);
16683a3f1fbaSChuck Lever ca->maxops = be32_to_cpup(p++);
16693a3f1fbaSChuck Lever ca->maxreqs = be32_to_cpup(p++);
16703a3f1fbaSChuck Lever ca->nr_rdma_attrs = be32_to_cpup(p);
16713a3f1fbaSChuck Lever switch (ca->nr_rdma_attrs) {
16723a3f1fbaSChuck Lever case 0:
16733a3f1fbaSChuck Lever break;
16743a3f1fbaSChuck Lever case 1:
16753a3f1fbaSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &ca->rdma_attrs) < 0)
16763a3f1fbaSChuck Lever return nfserr_bad_xdr;
16773a3f1fbaSChuck Lever break;
16783a3f1fbaSChuck Lever default:
16793a3f1fbaSChuck Lever return nfserr_bad_xdr;
16803a3f1fbaSChuck Lever }
16813a3f1fbaSChuck Lever
16823a3f1fbaSChuck Lever return nfs_ok;
16833a3f1fbaSChuck Lever }
16843a3f1fbaSChuck Lever
16853a3f1fbaSChuck Lever static __be32
nfsd4_decode_create_session(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)16863a3f1fbaSChuck Lever nfsd4_decode_create_session(struct nfsd4_compoundargs *argp,
16873a3f1fbaSChuck Lever union nfsd4_op_u *u)
16883a3f1fbaSChuck Lever {
16893a3f1fbaSChuck Lever struct nfsd4_create_session *sess = &u->create_session;
16903a3f1fbaSChuck Lever __be32 status;
16913a3f1fbaSChuck Lever
16923a3f1fbaSChuck Lever memset(sess, 0, sizeof(*sess));
16933a3f1fbaSChuck Lever status = nfsd4_decode_clientid4(argp, &sess->clientid);
16942db134ebSAndy Adamson if (status)
1695e78e274eSKees Cook return status;
16962db134ebSAndy Adamson if (xdr_stream_decode_u32(argp->xdr, &sess->seqid) < 0)
1697e78e274eSKees Cook return nfserr_bad_xdr;
169881243e3fSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &sess->flags) < 0)
1699ec6b5d7bSAndy Adamson return nfserr_bad_xdr;
17003fdc5464SChuck Lever status = nfsd4_decode_channel_attrs4(argp, &sess->fore_channel);
170181243e3fSChuck Lever if (status)
170281243e3fSChuck Lever return status;
170381243e3fSChuck Lever status = nfsd4_decode_channel_attrs4(argp, &sess->back_channel);
170481243e3fSChuck Lever if (status)
170581243e3fSChuck Lever return status;
170681243e3fSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &sess->callback_prog) < 0)
170781243e3fSChuck Lever return nfserr_bad_xdr;
17083a3f1fbaSChuck Lever return nfsd4_decode_cb_sec(argp, &sess->cb_sec);
17093a3f1fbaSChuck Lever }
17103a3f1fbaSChuck Lever
17113a3f1fbaSChuck Lever static __be32
nfsd4_decode_destroy_session(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)17123a3f1fbaSChuck Lever nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp,
17133a3f1fbaSChuck Lever union nfsd4_op_u *u)
171481243e3fSChuck Lever {
171581243e3fSChuck Lever struct nfsd4_destroy_session *destroy_session = &u->destroy_session;
17163fdc5464SChuck Lever return nfsd4_decode_sessionid4(argp, &destroy_session->sessionid);
17172db134ebSAndy Adamson }
17182db134ebSAndy Adamson
17192db134ebSAndy Adamson static __be32
nfsd4_decode_free_stateid(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)17202db134ebSAndy Adamson nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp,
1721e78e274eSKees Cook union nfsd4_op_u *u)
17222db134ebSAndy Adamson {
1723e78e274eSKees Cook struct nfsd4_free_stateid *free_stateid = &u->free_stateid;
172494e254afSChuck Lever return nfsd4_decode_stateid4(argp, &free_stateid->fr_stateid);
17252db134ebSAndy Adamson }
17262db134ebSAndy Adamson
17272db134ebSAndy Adamson static __be32
nfsd4_decode_get_dir_delegation(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1728e1ca12dfSBryan Schumaker nfsd4_decode_get_dir_delegation(struct nfsd4_compoundargs *argp,
1729e78e274eSKees Cook union nfsd4_op_u *u)
1730e1ca12dfSBryan Schumaker {
1731e78e274eSKees Cook struct nfsd4_get_dir_delegation *gdd = &u->get_dir_delegation;
1732aec387d5SChuck Lever __be32 status;
1733e1ca12dfSBryan Schumaker
1734e1ca12dfSBryan Schumaker memset(gdd, 0, sizeof(*gdd));
173533a1e6eaSJeff Layton
173633a1e6eaSJeff Layton if (xdr_stream_decode_bool(argp->xdr, &gdd->gdda_signal_deleg_avail) < 0)
173733a1e6eaSJeff Layton return nfserr_bad_xdr;
173833a1e6eaSJeff Layton status = nfsd4_decode_bitmap4(argp, gdd->gdda_notification_types,
173933a1e6eaSJeff Layton ARRAY_SIZE(gdd->gdda_notification_types));
174033a1e6eaSJeff Layton if (status)
174133a1e6eaSJeff Layton return status;
174233a1e6eaSJeff Layton status = nfsd4_decode_nfstime4(argp, &gdd->gdda_child_attr_delay);
174333a1e6eaSJeff Layton if (status)
174433a1e6eaSJeff Layton return status;
174533a1e6eaSJeff Layton status = nfsd4_decode_nfstime4(argp, &gdd->gdda_dir_attr_delay);
174633a1e6eaSJeff Layton if (status)
174733a1e6eaSJeff Layton return status;
174833a1e6eaSJeff Layton status = nfsd4_decode_bitmap4(argp, gdd->gdda_child_attributes,
174933a1e6eaSJeff Layton ARRAY_SIZE(gdd->gdda_child_attributes));
175033a1e6eaSJeff Layton if (status)
175133a1e6eaSJeff Layton return status;
175233a1e6eaSJeff Layton return nfsd4_decode_bitmap4(argp, gdd->gdda_dir_attributes,
175333a1e6eaSJeff Layton ARRAY_SIZE(gdd->gdda_dir_attributes));
175433a1e6eaSJeff Layton }
175533a1e6eaSJeff Layton
175633a1e6eaSJeff Layton #ifdef CONFIG_NFSD_PNFS
175733a1e6eaSJeff Layton static __be32
nfsd4_decode_getdeviceinfo(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)175833a1e6eaSJeff Layton nfsd4_decode_getdeviceinfo(struct nfsd4_compoundargs *argp,
175933a1e6eaSJeff Layton union nfsd4_op_u *u)
176033a1e6eaSJeff Layton {
176133a1e6eaSJeff Layton struct nfsd4_getdeviceinfo *gdev = &u->getdeviceinfo;
176233a1e6eaSJeff Layton __be32 status;
176333a1e6eaSJeff Layton
17649cf514ccSChristoph Hellwig memset(gdev, 0, sizeof(*gdev));
17659cf514ccSChristoph Hellwig status = nfsd4_decode_deviceid4(argp, &gdev->gd_devid);
17669cf514ccSChristoph Hellwig if (status)
1767e78e274eSKees Cook return status;
17689cf514ccSChristoph Hellwig if (xdr_stream_decode_u32(argp->xdr, &gdev->gd_layout_type) < 0)
1769e78e274eSKees Cook return nfserr_bad_xdr;
177004495971SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &gdev->gd_maxcount) < 0)
17719cf514ccSChristoph Hellwig return nfserr_bad_xdr;
17723fdc5464SChuck Lever if (xdr_stream_decode_uint32_array(argp->xdr,
177304495971SChuck Lever &gdev->gd_notify_types, 1) < 0)
177404495971SChuck Lever return nfserr_bad_xdr;
177504495971SChuck Lever
177604495971SChuck Lever return nfs_ok;
177704495971SChuck Lever }
177804495971SChuck Lever
177904495971SChuck Lever static __be32
nfsd4_decode_layoutcommit(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)178004495971SChuck Lever nfsd4_decode_layoutcommit(struct nfsd4_compoundargs *argp,
178104495971SChuck Lever union nfsd4_op_u *u)
178204495971SChuck Lever {
178304495971SChuck Lever struct nfsd4_layoutcommit *lcp = &u->layoutcommit;
178404495971SChuck Lever __be32 *p, status;
17859cf514ccSChristoph Hellwig
17869cf514ccSChristoph Hellwig memset(lcp, 0, sizeof(*lcp));
17879cf514ccSChristoph Hellwig if (xdr_stream_decode_u64(argp->xdr, &lcp->lc_seg.offset) < 0)
17885185980dSChuck Lever return nfserr_bad_xdr;
1789e78e274eSKees Cook if (xdr_stream_decode_u64(argp->xdr, &lcp->lc_seg.length) < 0)
17905185980dSChuck Lever return nfserr_bad_xdr;
1791e78e274eSKees Cook if (xdr_stream_decode_bool(argp->xdr, &lcp->lc_reclaim) < 0)
17925185980dSChuck Lever return nfserr_bad_xdr;
17935185980dSChuck Lever status = nfsd4_decode_stateid4(argp, &lcp->lc_sid);
17943fdc5464SChuck Lever if (status)
17955185980dSChuck Lever return status;
17965185980dSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lcp->lc_newoffset) < 0)
17975185980dSChuck Lever return nfserr_bad_xdr;
17985185980dSChuck Lever if (lcp->lc_newoffset) {
17995185980dSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &lcp->lc_last_wr) < 0)
18005185980dSChuck Lever return nfserr_bad_xdr;
18015185980dSChuck Lever } else
18025185980dSChuck Lever lcp->lc_last_wr = 0;
18035185980dSChuck Lever p = xdr_inline_decode(argp->xdr, XDR_UNIT);
18045185980dSChuck Lever if (!p)
18055185980dSChuck Lever return nfserr_bad_xdr;
18065185980dSChuck Lever if (xdr_item_is_present(p)) {
18075185980dSChuck Lever status = nfsd4_decode_nfstime4(argp, &lcp->lc_mtime);
18085185980dSChuck Lever if (status)
18095185980dSChuck Lever return status;
18105185980dSChuck Lever } else {
18115185980dSChuck Lever lcp->lc_mtime.tv_nsec = UTIME_NOW;
18125185980dSChuck Lever }
18135185980dSChuck Lever return nfsd4_decode_layoutupdate4(argp, lcp);
18145185980dSChuck Lever }
18155185980dSChuck Lever
18165185980dSChuck Lever static __be32
nfsd4_decode_layoutget(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)18175185980dSChuck Lever nfsd4_decode_layoutget(struct nfsd4_compoundargs *argp,
18185185980dSChuck Lever union nfsd4_op_u *u)
18195185980dSChuck Lever {
18205185980dSChuck Lever struct nfsd4_layoutget *lgp = &u->layoutget;
18215185980dSChuck Lever __be32 status;
18225185980dSChuck Lever
18235185980dSChuck Lever memset(lgp, 0, sizeof(*lgp));
18245185980dSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_signal) < 0)
18259cf514ccSChristoph Hellwig return nfserr_bad_xdr;
1826e78e274eSKees Cook if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_layout_type) < 0)
18279cf514ccSChristoph Hellwig return nfserr_bad_xdr;
1828e78e274eSKees Cook if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_seg.iomode) < 0)
1829c8e88e3aSChuck Lever return nfserr_bad_xdr;
18309cf514ccSChristoph Hellwig if (xdr_stream_decode_u64(argp->xdr, &lgp->lg_seg.offset) < 0)
18313fdc5464SChuck Lever return nfserr_bad_xdr;
1832c8e88e3aSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &lgp->lg_seg.length) < 0)
1833c8e88e3aSChuck Lever return nfserr_bad_xdr;
1834c8e88e3aSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &lgp->lg_minlength) < 0)
1835c8e88e3aSChuck Lever return nfserr_bad_xdr;
1836c8e88e3aSChuck Lever status = nfsd4_decode_stateid4(argp, &lgp->lg_sid);
1837c8e88e3aSChuck Lever if (status)
1838c8e88e3aSChuck Lever return status;
1839c8e88e3aSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_maxcount) < 0)
1840c8e88e3aSChuck Lever return nfserr_bad_xdr;
1841c8e88e3aSChuck Lever
1842c8e88e3aSChuck Lever return nfs_ok;
1843c8e88e3aSChuck Lever }
1844c8e88e3aSChuck Lever
1845db59c0efSKinglong Mee static __be32
nfsd4_decode_layoutreturn(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1846db59c0efSKinglong Mee nfsd4_decode_layoutreturn(struct nfsd4_compoundargs *argp,
1847c8e88e3aSChuck Lever union nfsd4_op_u *u)
1848c8e88e3aSChuck Lever {
1849db59c0efSKinglong Mee struct nfsd4_layoutreturn *lrp = &u->layoutreturn;
1850c8e88e3aSChuck Lever memset(lrp, 0, sizeof(*lrp));
18519cf514ccSChristoph Hellwig if (xdr_stream_decode_bool(argp->xdr, &lrp->lr_reclaim) < 0)
18529cf514ccSChristoph Hellwig return nfserr_bad_xdr;
18539cf514ccSChristoph Hellwig if (xdr_stream_decode_u32(argp->xdr, &lrp->lr_layout_type) < 0)
18549cf514ccSChristoph Hellwig return nfserr_bad_xdr;
1855e78e274eSKees Cook if (xdr_stream_decode_u32(argp->xdr, &lrp->lr_seg.iomode) < 0)
18569cf514ccSChristoph Hellwig return nfserr_bad_xdr;
1857e78e274eSKees Cook return nfsd4_decode_layoutreturn4(argp, lrp);
18583fdc5464SChuck Lever }
1859645fcad3SChuck Lever #endif /* CONFIG_NFSD_PNFS */
1860645fcad3SChuck Lever
nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1861645fcad3SChuck Lever static __be32 nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp,
1862645fcad3SChuck Lever union nfsd4_op_u *u)
1863645fcad3SChuck Lever {
1864645fcad3SChuck Lever struct nfsd4_secinfo_no_name *sin = &u->secinfo_no_name;
1865645fcad3SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &sin->sin_style) < 0)
18669cf514ccSChristoph Hellwig return nfserr_bad_xdr;
18679cf514ccSChristoph Hellwig
18689cf514ccSChristoph Hellwig sin->sin_exp = NULL;
186953d70873SChuck Lever return nfs_ok;
1870e78e274eSKees Cook }
187153d70873SChuck Lever
1872e78e274eSKees Cook static __be32
nfsd4_decode_sequence(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)187353d70873SChuck Lever nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
187453d70873SChuck Lever union nfsd4_op_u *u)
18753fdc5464SChuck Lever {
18763fdc5464SChuck Lever struct nfsd4_sequence *seq = &u->sequence;
187753d70873SChuck Lever __be32 *p, status;
187853d70873SChuck Lever
187953d70873SChuck Lever status = nfsd4_decode_sessionid4(argp, &seq->sessionid);
18802db134ebSAndy Adamson if (status)
1881cf907b11SChuck Lever return status;
1882e78e274eSKees Cook p = xdr_inline_decode(argp->xdr, XDR_UNIT * 4);
1883cf907b11SChuck Lever if (!p)
1884e78e274eSKees Cook return nfserr_bad_xdr;
1885cf907b11SChuck Lever seq->seqid = be32_to_cpup(p++);
1886cf907b11SChuck Lever seq->slotid = be32_to_cpup(p++);
1887cf907b11SChuck Lever seq->maxslots = be32_to_cpup(p++);
1888cf907b11SChuck Lever seq->cachethis = be32_to_cpup(p);
1889cf907b11SChuck Lever
1890cf907b11SChuck Lever seq->status_flags = 0;
1891cf907b11SChuck Lever return nfs_ok;
1892cf907b11SChuck Lever }
1893cf907b11SChuck Lever
1894cf907b11SChuck Lever static __be32
nfsd4_decode_test_stateid(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1895cf907b11SChuck Lever nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp,
1896cf907b11SChuck Lever union nfsd4_op_u *u)
1897cf907b11SChuck Lever {
18983fdc5464SChuck Lever struct nfsd4_test_stateid *test_stateid = &u->test_stateid;
1899cf907b11SChuck Lever struct nfsd4_test_stateid_id *stateid;
1900cf907b11SChuck Lever __be32 status;
1901cf907b11SChuck Lever u32 i;
1902cf907b11SChuck Lever
1903e78e274eSKees Cook memset(test_stateid, 0, sizeof(*test_stateid));
1904e78e274eSKees Cook if (xdr_stream_decode_u32(argp->xdr, &test_stateid->ts_num_ids) < 0)
1905b7a0c8f6SChuck Lever return nfserr_bad_xdr;
1906e78e274eSKees Cook
1907b7a0c8f6SChuck Lever INIT_LIST_HEAD(&test_stateid->ts_stateid_list);
1908b7a0c8f6SChuck Lever for (i = 0; i < test_stateid->ts_num_ids; i++) {
1909b7a0c8f6SChuck Lever stateid = svcxdr_tmpalloc(argp, sizeof(*stateid));
1910b7a0c8f6SChuck Lever if (!stateid)
19113fdc5464SChuck Lever return nfserr_jukebox;
1912b7a0c8f6SChuck Lever INIT_LIST_HEAD(&stateid->ts_id_list);
1913b7a0c8f6SChuck Lever list_add_tail(&stateid->ts_id_list, &test_stateid->ts_stateid_list);
1914b7a0c8f6SChuck Lever status = nfsd4_decode_stateid4(argp, &stateid->ts_id_stateid);
1915b7a0c8f6SChuck Lever if (status)
1916b7a0c8f6SChuck Lever return status;
1917b7a0c8f6SChuck Lever }
1918b7a0c8f6SChuck Lever
1919bb4d8427SChuck Lever return nfs_ok;
1920b7a0c8f6SChuck Lever }
1921b7a0c8f6SChuck Lever
nfsd4_decode_destroy_clientid(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1922b7a0c8f6SChuck Lever static __be32 nfsd4_decode_destroy_clientid(struct nfsd4_compoundargs *argp,
1923b7a0c8f6SChuck Lever union nfsd4_op_u *u)
1924b7a0c8f6SChuck Lever {
1925b7a0c8f6SChuck Lever struct nfsd4_destroy_clientid *dc = &u->destroy_clientid;
1926b7a0c8f6SChuck Lever return nfsd4_decode_clientid4(argp, &dc->clientid);
1927b7a0c8f6SChuck Lever }
1928b7a0c8f6SChuck Lever
nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1929b7a0c8f6SChuck Lever static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp,
1930c95f2ec3SChuck Lever union nfsd4_op_u *u)
1931e78e274eSKees Cook {
1932c95f2ec3SChuck Lever struct nfsd4_reclaim_complete *rc = &u->reclaim_complete;
1933e78e274eSKees Cook if (xdr_stream_decode_bool(argp->xdr, &rc->rca_one_fs) < 0)
1934c95f2ec3SChuck Lever return nfserr_bad_xdr;
1935c95f2ec3SChuck Lever return nfs_ok;
1936c95f2ec3SChuck Lever }
19370d646784SChuck Lever
1938e78e274eSKees Cook static __be32
nfsd4_decode_fallocate(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)19390d646784SChuck Lever nfsd4_decode_fallocate(struct nfsd4_compoundargs *argp,
1940e78e274eSKees Cook union nfsd4_op_u *u)
19410d646784SChuck Lever {
19420d646784SChuck Lever struct nfsd4_fallocate *fallocate = &u->allocate;
19430d646784SChuck Lever __be32 status;
19440d646784SChuck Lever
19450d646784SChuck Lever status = nfsd4_decode_stateid4(argp, &fallocate->falloc_stateid);
1946b7a0c8f6SChuck Lever if (status)
194795d871f0SAnna Schumaker return status;
1948e78e274eSKees Cook if (xdr_stream_decode_u64(argp->xdr, &fallocate->falloc_offset) < 0)
194995d871f0SAnna Schumaker return nfserr_bad_xdr;
1950e78e274eSKees Cook if (xdr_stream_decode_u64(argp->xdr, &fallocate->falloc_length) < 0)
19516aef27aaSChuck Lever return nfserr_bad_xdr;
195295d871f0SAnna Schumaker
19536aef27aaSChuck Lever return nfs_ok;
195495d871f0SAnna Schumaker }
195595d871f0SAnna Schumaker
nfsd4_decode_nl4_server(struct nfsd4_compoundargs * argp,struct nl4_server * ns)19566aef27aaSChuck Lever static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
19576aef27aaSChuck Lever struct nl4_server *ns)
19586aef27aaSChuck Lever {
19596aef27aaSChuck Lever struct nfs42_netaddr *naddr;
196095d871f0SAnna Schumaker __be32 *p;
19616aef27aaSChuck Lever
196295d871f0SAnna Schumaker if (xdr_stream_decode_u32(argp->xdr, &ns->nl4_type) < 0)
196395d871f0SAnna Schumaker return nfserr_bad_xdr;
196484e1b21dSOlga Kornievskaia
196584e1b21dSOlga Kornievskaia /* currently support for 1 inter-server source server */
196684e1b21dSOlga Kornievskaia switch (ns->nl4_type) {
196784e1b21dSOlga Kornievskaia case NL4_NETADDR:
1968f49e4b4dSChuck Lever naddr = &ns->u.nl4_addr;
196984e1b21dSOlga Kornievskaia
1970f49e4b4dSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &naddr->netid_len) < 0)
1971f49e4b4dSChuck Lever return nfserr_bad_xdr;
197284e1b21dSOlga Kornievskaia if (naddr->netid_len > RPCBIND_MAXNETIDLEN)
197384e1b21dSOlga Kornievskaia return nfserr_bad_xdr;
197484e1b21dSOlga Kornievskaia
197584e1b21dSOlga Kornievskaia p = xdr_inline_decode(argp->xdr, naddr->netid_len);
197684e1b21dSOlga Kornievskaia if (!p)
197784e1b21dSOlga Kornievskaia return nfserr_bad_xdr;
1978f49e4b4dSChuck Lever memcpy(naddr->netid, p, naddr->netid_len);
1979f49e4b4dSChuck Lever
198084e1b21dSOlga Kornievskaia if (xdr_stream_decode_u32(argp->xdr, &naddr->addr_len) < 0)
1981f49e4b4dSChuck Lever return nfserr_bad_xdr;
198284e1b21dSOlga Kornievskaia if (naddr->addr_len > RPCBIND_MAXUADDRLEN)
1983f49e4b4dSChuck Lever return nfserr_bad_xdr;
1984f49e4b4dSChuck Lever
1985f49e4b4dSChuck Lever p = xdr_inline_decode(argp->xdr, naddr->addr_len);
1986f49e4b4dSChuck Lever if (!p)
198784e1b21dSOlga Kornievskaia return nfserr_bad_xdr;
1988f49e4b4dSChuck Lever memcpy(naddr->addr, p, naddr->addr_len);
1989f49e4b4dSChuck Lever break;
199084e1b21dSOlga Kornievskaia default:
1991f49e4b4dSChuck Lever return nfserr_bad_xdr;
199284e1b21dSOlga Kornievskaia }
1993f49e4b4dSChuck Lever
1994f49e4b4dSChuck Lever return nfs_ok;
1995f49e4b4dSChuck Lever }
1996f49e4b4dSChuck Lever
199784e1b21dSOlga Kornievskaia static __be32
nfsd4_decode_copy(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)199884e1b21dSOlga Kornievskaia nfsd4_decode_copy(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
1999f49e4b4dSChuck Lever {
200084e1b21dSOlga Kornievskaia struct nfsd4_copy *copy = &u->copy;
2001f49e4b4dSChuck Lever u32 consecutive, i, count, sync;
2002f49e4b4dSChuck Lever struct nl4_server *ns_dummy;
200384e1b21dSOlga Kornievskaia __be32 status;
200484e1b21dSOlga Kornievskaia
2005ffa0160aSChristoph Hellwig memset(copy, 0, sizeof(*copy));
2006e78e274eSKees Cook status = nfsd4_decode_stateid4(argp, ©->cp_src_stateid);
200729ae7f9dSAnna Schumaker if (status)
2008e78e274eSKees Cook return status;
20091913cdf5SChuck Lever status = nfsd4_decode_stateid4(argp, ©->cp_dst_stateid);
201084e1b21dSOlga Kornievskaia if (status)
2011e8febea7SChuck Lever return status;
201229ae7f9dSAnna Schumaker if (xdr_stream_decode_u64(argp->xdr, ©->cp_src_pos) < 0)
20133fdc5464SChuck Lever return nfserr_bad_xdr;
2014e8febea7SChuck Lever if (xdr_stream_decode_u64(argp->xdr, ©->cp_dst_pos) < 0)
201529ae7f9dSAnna Schumaker return nfserr_bad_xdr;
201629ae7f9dSAnna Schumaker if (xdr_stream_decode_u64(argp->xdr, ©->cp_count) < 0)
2017e8febea7SChuck Lever return nfserr_bad_xdr;
201829ae7f9dSAnna Schumaker /* ca_consecutive: we always do consecutive copies */
201929ae7f9dSAnna Schumaker if (xdr_stream_decode_u32(argp->xdr, &consecutive) < 0)
2020e8febea7SChuck Lever return nfserr_bad_xdr;
2021e8febea7SChuck Lever if (xdr_stream_decode_bool(argp->xdr, &sync) < 0)
2022e8febea7SChuck Lever return nfserr_bad_xdr;
2023e8febea7SChuck Lever nfsd4_copy_set_sync(copy, sync);
2024e8febea7SChuck Lever
2025e8febea7SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
2026e8febea7SChuck Lever return nfserr_bad_xdr;
2027e8febea7SChuck Lever copy->cp_src = svcxdr_tmpalloc(argp, sizeof(*copy->cp_src));
2028e8febea7SChuck Lever if (copy->cp_src == NULL)
20291913cdf5SChuck Lever return nfserr_jukebox;
2030e8febea7SChuck Lever if (count == 0) { /* intra-server copy */
20311913cdf5SChuck Lever __set_bit(NFSD4_COPY_F_INTRA, ©->cp_flags);
203229ae7f9dSAnna Schumaker return nfs_ok;
2033e8febea7SChuck Lever }
2034e8febea7SChuck Lever
203587689df6SChuck Lever /* decode all the supplied server addresses but use only the first */
203687689df6SChuck Lever status = nfsd4_decode_nl4_server(argp, copy->cp_src);
203787689df6SChuck Lever if (status)
203884e1b21dSOlga Kornievskaia return status;
20391913cdf5SChuck Lever
2040e8febea7SChuck Lever ns_dummy = kmalloc(sizeof(struct nl4_server), GFP_KERNEL);
204184e1b21dSOlga Kornievskaia if (ns_dummy == NULL)
204284e1b21dSOlga Kornievskaia return nfserr_jukebox;
2043e8febea7SChuck Lever for (i = 0; i < count - 1; i++) {
204487689df6SChuck Lever status = nfsd4_decode_nl4_server(argp, ns_dummy);
204584e1b21dSOlga Kornievskaia if (status) {
204684e1b21dSOlga Kornievskaia kfree(ns_dummy);
204784e1b21dSOlga Kornievskaia return status;
204884e1b21dSOlga Kornievskaia }
204984e1b21dSOlga Kornievskaia }
2050bb4d8427SChuck Lever kfree(ns_dummy);
205184e1b21dSOlga Kornievskaia
205284e1b21dSOlga Kornievskaia return nfs_ok;
205384e1b21dSOlga Kornievskaia }
205484e1b21dSOlga Kornievskaia
205584e1b21dSOlga Kornievskaia static __be32
nfsd4_decode_copy_notify(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)205684e1b21dSOlga Kornievskaia nfsd4_decode_copy_notify(struct nfsd4_compoundargs *argp,
205784e1b21dSOlga Kornievskaia union nfsd4_op_u *u)
205884e1b21dSOlga Kornievskaia {
205929ae7f9dSAnna Schumaker struct nfsd4_copy_notify *cn = &u->copy_notify;
2060e8febea7SChuck Lever __be32 status;
206129ae7f9dSAnna Schumaker
206229ae7f9dSAnna Schumaker memset(cn, 0, sizeof(*cn));
206329ae7f9dSAnna Schumaker cn->cpn_src = svcxdr_tmpalloc(argp, sizeof(*cn->cpn_src));
206451911868SOlga Kornievskaia if (cn->cpn_src == NULL)
2065e78e274eSKees Cook return nfserr_jukebox;
206651911868SOlga Kornievskaia cn->cpn_dst = svcxdr_tmpalloc(argp, sizeof(*cn->cpn_dst));
2067e78e274eSKees Cook if (cn->cpn_dst == NULL)
20685aff7d08SChuck Lever return nfserr_jukebox;
206951911868SOlga Kornievskaia
20703fdc5464SChuck Lever status = nfsd4_decode_stateid4(argp, &cn->cpn_src_stateid);
207109426ef2SChuck Lever if (status)
207209426ef2SChuck Lever return status;
207309426ef2SChuck Lever return nfsd4_decode_nl4_server(argp, cn->cpn_dst);
207409426ef2SChuck Lever }
207509426ef2SChuck Lever
207609426ef2SChuck Lever static __be32
nfsd4_decode_offload_status(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)207709426ef2SChuck Lever nfsd4_decode_offload_status(struct nfsd4_compoundargs *argp,
2078f9a953fbSChuck Lever union nfsd4_op_u *u)
207951911868SOlga Kornievskaia {
208051911868SOlga Kornievskaia struct nfsd4_offload_status *os = &u->offload_status;
208109426ef2SChuck Lever os->count = 0;
208251911868SOlga Kornievskaia os->status = 0;
208351911868SOlga Kornievskaia return nfsd4_decode_stateid4(argp, &os->stateid);
208451911868SOlga Kornievskaia }
2085f9a953fbSChuck Lever
2086e78e274eSKees Cook static __be32
nfsd4_decode_seek(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)2087f9a953fbSChuck Lever nfsd4_decode_seek(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
2088e78e274eSKees Cook {
20893fdc5464SChuck Lever struct nfsd4_seek *seek = &u->seek;
20903fdc5464SChuck Lever __be32 status;
20912846bb05SChuck Lever
2092f9a953fbSChuck Lever status = nfsd4_decode_stateid4(argp, &seek->seek_stateid);
2093f9a953fbSChuck Lever if (status)
2094f9a953fbSChuck Lever return status;
2095e78e274eSKees Cook if (xdr_stream_decode_u64(argp->xdr, &seek->seek_offset) < 0)
209624bab491SAnna Schumaker return nfserr_bad_xdr;
2097e78e274eSKees Cook if (xdr_stream_decode_u32(argp->xdr, &seek->seek_whence) < 0)
20989d32b412SChuck Lever return nfserr_bad_xdr;
209924bab491SAnna Schumaker
21009d32b412SChuck Lever seek->seek_eof = 0;
210124bab491SAnna Schumaker seek->seek_pos = 0;
210224bab491SAnna Schumaker return nfs_ok;
21039d32b412SChuck Lever }
21049d32b412SChuck Lever
21059d32b412SChuck Lever static __be32
nfsd4_decode_clone(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)21069d32b412SChuck Lever nfsd4_decode_clone(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
210724bab491SAnna Schumaker {
21083fdc5464SChuck Lever struct nfsd4_clone *clone = &u->clone;
21093fdc5464SChuck Lever __be32 status;
21109d32b412SChuck Lever
211124bab491SAnna Schumaker status = nfsd4_decode_stateid4(argp, &clone->cl_src_stateid);
211224bab491SAnna Schumaker if (status)
21133dfd0b0eSChuck Lever return status;
2114e78e274eSKees Cook status = nfsd4_decode_stateid4(argp, &clone->cl_dst_stateid);
21153dfd0b0eSChuck Lever if (status)
2116e78e274eSKees Cook return status;
21173dfd0b0eSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &clone->cl_src_pos) < 0)
21183dfd0b0eSChuck Lever return nfserr_bad_xdr;
21193dfd0b0eSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &clone->cl_dst_pos) < 0)
21203dfd0b0eSChuck Lever return nfserr_bad_xdr;
21213dfd0b0eSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &clone->cl_count) < 0)
21223dfd0b0eSChuck Lever return nfserr_bad_xdr;
21233dfd0b0eSChuck Lever
21243dfd0b0eSChuck Lever return nfs_ok;
21253dfd0b0eSChuck Lever }
21263dfd0b0eSChuck Lever
21273dfd0b0eSChuck Lever /*
21283dfd0b0eSChuck Lever * XDR data that is more than PAGE_SIZE in size is normally part of a
21293dfd0b0eSChuck Lever * read or write. However, the size of extended attributes is limited
21303dfd0b0eSChuck Lever * by the maximum request size, and then further limited by the underlying
21313dfd0b0eSChuck Lever * filesystem limits. This can exceed PAGE_SIZE (currently, XATTR_SIZE_MAX
21323dfd0b0eSChuck Lever * is 64k). Since there is no kvec- or page-based interface to xattrs,
21333dfd0b0eSChuck Lever * and we're not dealing with contiguous pages, we need to do some copying.
21343dfd0b0eSChuck Lever */
213523e50fe3SFrank van der Linden
213623e50fe3SFrank van der Linden /*
213723e50fe3SFrank van der Linden * Decode data into buffer.
213823e50fe3SFrank van der Linden */
213923e50fe3SFrank van der Linden static __be32
nfsd4_vbuf_from_vector(struct nfsd4_compoundargs * argp,struct xdr_buf * xdr,char ** bufp,size_t buflen)214023e50fe3SFrank van der Linden nfsd4_vbuf_from_vector(struct nfsd4_compoundargs *argp, struct xdr_buf *xdr,
214123e50fe3SFrank van der Linden char **bufp, size_t buflen)
214223e50fe3SFrank van der Linden {
214323e50fe3SFrank van der Linden struct page **pages = xdr->pages;
214423e50fe3SFrank van der Linden struct kvec *head = xdr->head;
2145c1346a12SChuck Lever char *tmp, *dp;
214623e50fe3SFrank van der Linden u32 len;
214723e50fe3SFrank van der Linden
2148c1346a12SChuck Lever if (buflen <= head->iov_len) {
2149dbc834e5SDan Carpenter /*
215023e50fe3SFrank van der Linden * We're in luck, the head has enough space. Just return
2151c1346a12SChuck Lever * the head, no need for copying.
2152c1346a12SChuck Lever */
215323e50fe3SFrank van der Linden *bufp = head->iov_base;
215423e50fe3SFrank van der Linden return 0;
215523e50fe3SFrank van der Linden }
215623e50fe3SFrank van der Linden
215723e50fe3SFrank van der Linden tmp = svcxdr_tmpalloc(argp, buflen);
215823e50fe3SFrank van der Linden if (tmp == NULL)
215923e50fe3SFrank van der Linden return nfserr_jukebox;
216023e50fe3SFrank van der Linden
216123e50fe3SFrank van der Linden dp = tmp;
216223e50fe3SFrank van der Linden memcpy(dp, head->iov_base, head->iov_len);
216323e50fe3SFrank van der Linden buflen -= head->iov_len;
216423e50fe3SFrank van der Linden dp += head->iov_len;
216523e50fe3SFrank van der Linden
216623e50fe3SFrank van der Linden while (buflen > 0) {
216723e50fe3SFrank van der Linden len = min_t(u32, buflen, PAGE_SIZE);
216823e50fe3SFrank van der Linden memcpy(dp, page_address(*pages), len);
216923e50fe3SFrank van der Linden
217023e50fe3SFrank van der Linden buflen -= len;
217123e50fe3SFrank van der Linden dp += len;
217223e50fe3SFrank van der Linden pages++;
217323e50fe3SFrank van der Linden }
217423e50fe3SFrank van der Linden
217523e50fe3SFrank van der Linden *bufp = tmp;
217623e50fe3SFrank van der Linden return 0;
217723e50fe3SFrank van der Linden }
217823e50fe3SFrank van der Linden
217923e50fe3SFrank van der Linden /*
218023e50fe3SFrank van der Linden * Get a user extended attribute name from the XDR buffer.
218123e50fe3SFrank van der Linden * It will not have the "user." prefix, so prepend it.
218223e50fe3SFrank van der Linden * Lastly, check for nul characters in the name.
218323e50fe3SFrank van der Linden */
218423e50fe3SFrank van der Linden static __be32
nfsd4_decode_xattr_name(struct nfsd4_compoundargs * argp,char ** namep)218523e50fe3SFrank van der Linden nfsd4_decode_xattr_name(struct nfsd4_compoundargs *argp, char **namep)
218623e50fe3SFrank van der Linden {
218723e50fe3SFrank van der Linden char *name, *sp, *dp;
218823e50fe3SFrank van der Linden u32 namelen, cnt;
218923e50fe3SFrank van der Linden __be32 *p;
219023e50fe3SFrank van der Linden
219123e50fe3SFrank van der Linden if (xdr_stream_decode_u32(argp->xdr, &namelen) < 0)
219223e50fe3SFrank van der Linden return nfserr_bad_xdr;
219323e50fe3SFrank van der Linden if (namelen > (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN))
219423e50fe3SFrank van der Linden return nfserr_nametoolong;
219523e50fe3SFrank van der Linden if (namelen == 0)
219623e50fe3SFrank van der Linden return nfserr_bad_xdr;
2197830c7150SChuck Lever p = xdr_inline_decode(argp->xdr, namelen);
219823e50fe3SFrank van der Linden if (!p)
2199830c7150SChuck Lever return nfserr_bad_xdr;
2200830c7150SChuck Lever name = svcxdr_tmpalloc(argp, namelen + XATTR_USER_PREFIX_LEN + 1);
220123e50fe3SFrank van der Linden if (!name)
220223e50fe3SFrank van der Linden return nfserr_jukebox;
220323e50fe3SFrank van der Linden memcpy(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
2204830c7150SChuck Lever
2205830c7150SChuck Lever /*
2206830c7150SChuck Lever * Copy the extended attribute name over while checking for 0
2207830c7150SChuck Lever * characters.
220823e50fe3SFrank van der Linden */
220923e50fe3SFrank van der Linden sp = (char *)p;
221023e50fe3SFrank van der Linden dp = name + XATTR_USER_PREFIX_LEN;
221123e50fe3SFrank van der Linden cnt = namelen;
221223e50fe3SFrank van der Linden
221323e50fe3SFrank van der Linden while (cnt-- > 0) {
221423e50fe3SFrank van der Linden if (*sp == '\0')
221523e50fe3SFrank van der Linden return nfserr_bad_xdr;
221623e50fe3SFrank van der Linden *dp++ = *sp++;
221723e50fe3SFrank van der Linden }
221823e50fe3SFrank van der Linden *dp = '\0';
221923e50fe3SFrank van der Linden
222023e50fe3SFrank van der Linden *namep = name;
222123e50fe3SFrank van der Linden
222223e50fe3SFrank van der Linden return nfs_ok;
2223830c7150SChuck Lever }
222423e50fe3SFrank van der Linden
222523e50fe3SFrank van der Linden /*
222623e50fe3SFrank van der Linden * A GETXATTR op request comes without a length specifier. We just set the
222723e50fe3SFrank van der Linden * maximum length for the reply based on XATTR_SIZE_MAX and the maximum
222823e50fe3SFrank van der Linden * channel reply size. nfsd_getxattr will probe the length of the xattr,
222923e50fe3SFrank van der Linden * check it against getxa_len, and allocate + return the value.
2230830c7150SChuck Lever */
223123e50fe3SFrank van der Linden static __be32
nfsd4_decode_getxattr(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)223223e50fe3SFrank van der Linden nfsd4_decode_getxattr(struct nfsd4_compoundargs *argp,
223323e50fe3SFrank van der Linden union nfsd4_op_u *u)
223423e50fe3SFrank van der Linden {
223523e50fe3SFrank van der Linden struct nfsd4_getxattr *getxattr = &u->getxattr;
223623e50fe3SFrank van der Linden __be32 status;
223723e50fe3SFrank van der Linden u32 maxcount;
223823e50fe3SFrank van der Linden
223923e50fe3SFrank van der Linden memset(getxattr, 0, sizeof(*getxattr));
224023e50fe3SFrank van der Linden status = nfsd4_decode_xattr_name(argp, &getxattr->getxa_name);
2241e78e274eSKees Cook if (status)
224223e50fe3SFrank van der Linden return status;
2243e78e274eSKees Cook
224423e50fe3SFrank van der Linden maxcount = svc_max_payload(argp->rqstp);
224523e50fe3SFrank van der Linden maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount);
224623e50fe3SFrank van der Linden
22473fdc5464SChuck Lever getxattr->getxa_len = maxcount;
224823e50fe3SFrank van der Linden return nfs_ok;
224923e50fe3SFrank van der Linden }
225023e50fe3SFrank van der Linden
225123e50fe3SFrank van der Linden static __be32
nfsd4_decode_setxattr(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)225223e50fe3SFrank van der Linden nfsd4_decode_setxattr(struct nfsd4_compoundargs *argp,
225323e50fe3SFrank van der Linden union nfsd4_op_u *u)
225423e50fe3SFrank van der Linden {
225523e50fe3SFrank van der Linden struct nfsd4_setxattr *setxattr = &u->setxattr;
22563fdc5464SChuck Lever u32 flags, maxcount, size;
225723e50fe3SFrank van der Linden __be32 status;
225823e50fe3SFrank van der Linden
225923e50fe3SFrank van der Linden memset(setxattr, 0, sizeof(*setxattr));
226023e50fe3SFrank van der Linden
2261e78e274eSKees Cook if (xdr_stream_decode_u32(argp->xdr, &flags) < 0)
226223e50fe3SFrank van der Linden return nfserr_bad_xdr;
2263e78e274eSKees Cook
226423e50fe3SFrank van der Linden if (flags > SETXATTR4_REPLACE)
2265403366a7SChuck Lever return nfserr_inval;
226623e50fe3SFrank van der Linden setxattr->setxa_flags = flags;
22673fdc5464SChuck Lever
22683fdc5464SChuck Lever status = nfsd4_decode_xattr_name(argp, &setxattr->setxa_name);
2269403366a7SChuck Lever if (status)
2270403366a7SChuck Lever return status;
227123e50fe3SFrank van der Linden
227223e50fe3SFrank van der Linden maxcount = svc_max_payload(argp->rqstp);
227323e50fe3SFrank van der Linden maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount);
227423e50fe3SFrank van der Linden
227523e50fe3SFrank van der Linden if (xdr_stream_decode_u32(argp->xdr, &size) < 0)
227623e50fe3SFrank van der Linden return nfserr_bad_xdr;
227723e50fe3SFrank van der Linden if (size > maxcount)
227823e50fe3SFrank van der Linden return nfserr_xattr2big;
227923e50fe3SFrank van der Linden
228023e50fe3SFrank van der Linden setxattr->setxa_len = size;
228123e50fe3SFrank van der Linden if (size > 0) {
228223e50fe3SFrank van der Linden struct xdr_buf payload;
2283403366a7SChuck Lever
2284403366a7SChuck Lever if (!xdr_stream_subsegment(argp->xdr, &payload, size))
228523e50fe3SFrank van der Linden return nfserr_bad_xdr;
228623e50fe3SFrank van der Linden status = nfsd4_vbuf_from_vector(argp, &payload,
228723e50fe3SFrank van der Linden &setxattr->setxa_buf, size);
228823e50fe3SFrank van der Linden }
228923e50fe3SFrank van der Linden
2290c1346a12SChuck Lever return nfs_ok;
229123e50fe3SFrank van der Linden }
2292c1346a12SChuck Lever
2293403366a7SChuck Lever static __be32
nfsd4_decode_listxattrs(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)2294c1346a12SChuck Lever nfsd4_decode_listxattrs(struct nfsd4_compoundargs *argp,
229523e50fe3SFrank van der Linden union nfsd4_op_u *u)
229623e50fe3SFrank van der Linden {
229723e50fe3SFrank van der Linden struct nfsd4_listxattrs *listxattrs = &u->listxattrs;
2298403366a7SChuck Lever u32 maxcount;
229923e50fe3SFrank van der Linden
230023e50fe3SFrank van der Linden memset(listxattrs, 0, sizeof(*listxattrs));
230123e50fe3SFrank van der Linden
230223e50fe3SFrank van der Linden if (xdr_stream_decode_u64(argp->xdr, &listxattrs->lsxa_cookie) < 0)
2303e78e274eSKees Cook return nfserr_bad_xdr;
230423e50fe3SFrank van der Linden
2305e78e274eSKees Cook /*
230623e50fe3SFrank van der Linden * If the cookie is too large to have even one user.x attribute
230723e50fe3SFrank van der Linden * plus trailing '\0' left in a maximum size buffer, it's invalid.
23083fdc5464SChuck Lever */
23093fdc5464SChuck Lever if (listxattrs->lsxa_cookie >=
23102212036cSChuck Lever (XATTR_LIST_MAX / (XATTR_USER_PREFIX_LEN + 2)))
23112212036cSChuck Lever return nfserr_badcookie;
231223e50fe3SFrank van der Linden
231323e50fe3SFrank van der Linden if (xdr_stream_decode_u32(argp->xdr, &maxcount) < 0)
231423e50fe3SFrank van der Linden return nfserr_bad_xdr;
231523e50fe3SFrank van der Linden if (maxcount < 8)
231623e50fe3SFrank van der Linden /* Always need at least 2 words (length and one character) */
231723e50fe3SFrank van der Linden return nfserr_inval;
231823e50fe3SFrank van der Linden
231923e50fe3SFrank van der Linden maxcount = min(maxcount, svc_max_payload(argp->rqstp));
232023e50fe3SFrank van der Linden listxattrs->lsxa_maxcount = maxcount;
23212212036cSChuck Lever
23222212036cSChuck Lever return nfs_ok;
232323e50fe3SFrank van der Linden }
232423e50fe3SFrank van der Linden
232523e50fe3SFrank van der Linden static __be32
nfsd4_decode_removexattr(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)232623e50fe3SFrank van der Linden nfsd4_decode_removexattr(struct nfsd4_compoundargs *argp,
232723e50fe3SFrank van der Linden union nfsd4_op_u *u)
232823e50fe3SFrank van der Linden {
232923e50fe3SFrank van der Linden struct nfsd4_removexattr *removexattr = &u->removexattr;
23302212036cSChuck Lever memset(removexattr, 0, sizeof(*removexattr));
233123e50fe3SFrank van der Linden return nfsd4_decode_xattr_name(argp, &removexattr->rmxa_name);
233223e50fe3SFrank van der Linden }
233323e50fe3SFrank van der Linden
233423e50fe3SFrank van der Linden static __be32
nfsd4_decode_noop(struct nfsd4_compoundargs * argp,union nfsd4_op_u * p)2335e78e274eSKees Cook nfsd4_decode_noop(struct nfsd4_compoundargs *argp, union nfsd4_op_u *p)
233623e50fe3SFrank van der Linden {
2337e78e274eSKees Cook return nfs_ok;
23383fdc5464SChuck Lever }
233923e50fe3SFrank van der Linden
234023e50fe3SFrank van der Linden static __be32
nfsd4_decode_notsupp(struct nfsd4_compoundargs * argp,union nfsd4_op_u * p)234123e50fe3SFrank van der Linden nfsd4_decode_notsupp(struct nfsd4_compoundargs *argp, union nfsd4_op_u *p)
234224bab491SAnna Schumaker {
2343e78e274eSKees Cook return nfserr_notsupp;
2344347e0ad9SBenny Halevy }
2345347e0ad9SBenny Halevy
2346347e0ad9SBenny Halevy typedef __be32(*nfsd4_dec)(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u);
2347347e0ad9SBenny Halevy
23483c375c6fSBenny Halevy static const nfsd4_dec nfsd4_dec_ops[] = {
2349e78e274eSKees Cook [OP_ACCESS] = nfsd4_decode_access,
23503c375c6fSBenny Halevy [OP_CLOSE] = nfsd4_decode_close,
23511e685ec2SBenny Halevy [OP_COMMIT] = nfsd4_decode_commit,
23523c375c6fSBenny Halevy [OP_CREATE] = nfsd4_decode_create,
23533c375c6fSBenny Halevy [OP_DELEGPURGE] = nfsd4_decode_notsupp,
2354e78e274eSKees Cook [OP_DELEGRETURN] = nfsd4_decode_delegreturn,
2355347e0ad9SBenny Halevy [OP_GETATTR] = nfsd4_decode_getattr,
2356c1df609dSChuck Lever [OP_GETFH] = nfsd4_decode_noop,
2357e78e274eSKees Cook [OP_LINK] = nfsd4_decode_link,
2358e78e274eSKees Cook [OP_LOCK] = nfsd4_decode_lock,
2359e78e274eSKees Cook [OP_LOCKT] = nfsd4_decode_lockt,
2360e78e274eSKees Cook [OP_LOCKU] = nfsd4_decode_locku,
2361e78e274eSKees Cook [OP_LOOKUP] = nfsd4_decode_lookup,
2362e78e274eSKees Cook [OP_LOOKUPP] = nfsd4_decode_noop,
2363e78e274eSKees Cook [OP_NVERIFY] = nfsd4_decode_verify,
2364e78e274eSKees Cook [OP_OPEN] = nfsd4_decode_open,
2365e78e274eSKees Cook [OP_OPENATTR] = nfsd4_decode_notsupp,
2366e78e274eSKees Cook [OP_OPEN_CONFIRM] = nfsd4_decode_open_confirm,
2367e78e274eSKees Cook [OP_OPEN_DOWNGRADE] = nfsd4_decode_open_downgrade,
2368e78e274eSKees Cook [OP_PUTFH] = nfsd4_decode_putfh,
2369e78e274eSKees Cook [OP_PUTPUBFH] = nfsd4_decode_noop,
2370e78e274eSKees Cook [OP_PUTROOTFH] = nfsd4_decode_noop,
2371e78e274eSKees Cook [OP_READ] = nfsd4_decode_read,
2372e78e274eSKees Cook [OP_READDIR] = nfsd4_decode_readdir,
2373e78e274eSKees Cook [OP_READLINK] = nfsd4_decode_noop,
2374e78e274eSKees Cook [OP_REMOVE] = nfsd4_decode_remove,
2375e78e274eSKees Cook [OP_RENAME] = nfsd4_decode_rename,
2376e78e274eSKees Cook [OP_RENEW] = nfsd4_decode_renew,
2377e78e274eSKees Cook [OP_RESTOREFH] = nfsd4_decode_noop,
2378e78e274eSKees Cook [OP_SAVEFH] = nfsd4_decode_noop,
2379e78e274eSKees Cook [OP_SECINFO] = nfsd4_decode_secinfo,
2380e78e274eSKees Cook [OP_SETATTR] = nfsd4_decode_setattr,
2381e78e274eSKees Cook [OP_SETCLIENTID] = nfsd4_decode_setclientid,
2382e78e274eSKees Cook [OP_SETCLIENTID_CONFIRM] = nfsd4_decode_setclientid_confirm,
2383e78e274eSKees Cook [OP_VERIFY] = nfsd4_decode_verify,
2384e78e274eSKees Cook [OP_WRITE] = nfsd4_decode_write,
2385e78e274eSKees Cook [OP_RELEASE_LOCKOWNER] = nfsd4_decode_release_lockowner,
2386e78e274eSKees Cook
2387e78e274eSKees Cook /* new operations for NFSv4.1 */
2388e78e274eSKees Cook [OP_BACKCHANNEL_CTL] = nfsd4_decode_backchannel_ctl,
2389e78e274eSKees Cook [OP_BIND_CONN_TO_SESSION] = nfsd4_decode_bind_conn_to_session,
2390e78e274eSKees Cook [OP_EXCHANGE_ID] = nfsd4_decode_exchange_id,
2391e78e274eSKees Cook [OP_CREATE_SESSION] = nfsd4_decode_create_session,
2392e78e274eSKees Cook [OP_DESTROY_SESSION] = nfsd4_decode_destroy_session,
2393e78e274eSKees Cook [OP_FREE_STATEID] = nfsd4_decode_free_stateid,
23942db134ebSAndy Adamson [OP_GET_DIR_DELEGATION] = nfsd4_decode_get_dir_delegation,
23952db134ebSAndy Adamson #ifdef CONFIG_NFSD_PNFS
2396e78e274eSKees Cook [OP_GETDEVICEINFO] = nfsd4_decode_getdeviceinfo,
2397e78e274eSKees Cook [OP_GETDEVICELIST] = nfsd4_decode_notsupp,
2398e78e274eSKees Cook [OP_LAYOUTCOMMIT] = nfsd4_decode_layoutcommit,
2399e78e274eSKees Cook [OP_LAYOUTGET] = nfsd4_decode_layoutget,
2400e78e274eSKees Cook [OP_LAYOUTRETURN] = nfsd4_decode_layoutreturn,
2401e78e274eSKees Cook #else
240233a1e6eaSJeff Layton [OP_GETDEVICEINFO] = nfsd4_decode_notsupp,
24039cf514ccSChristoph Hellwig [OP_GETDEVICELIST] = nfsd4_decode_notsupp,
2404e78e274eSKees Cook [OP_LAYOUTCOMMIT] = nfsd4_decode_notsupp,
2405e78e274eSKees Cook [OP_LAYOUTGET] = nfsd4_decode_notsupp,
2406e78e274eSKees Cook [OP_LAYOUTRETURN] = nfsd4_decode_notsupp,
2407e78e274eSKees Cook #endif
2408e78e274eSKees Cook [OP_SECINFO_NO_NAME] = nfsd4_decode_secinfo_no_name,
24099cf514ccSChristoph Hellwig [OP_SEQUENCE] = nfsd4_decode_sequence,
2410e78e274eSKees Cook [OP_SET_SSV] = nfsd4_decode_notsupp,
2411e78e274eSKees Cook [OP_TEST_STATEID] = nfsd4_decode_test_stateid,
2412e78e274eSKees Cook [OP_WANT_DELEGATION] = nfsd4_decode_notsupp,
2413e78e274eSKees Cook [OP_DESTROY_CLIENTID] = nfsd4_decode_destroy_clientid,
2414e78e274eSKees Cook [OP_RECLAIM_COMPLETE] = nfsd4_decode_reclaim_complete,
24159cf514ccSChristoph Hellwig
2416e78e274eSKees Cook /* new operations for NFSv4.2 */
2417e78e274eSKees Cook [OP_ALLOCATE] = nfsd4_decode_fallocate,
2418e78e274eSKees Cook [OP_COPY] = nfsd4_decode_copy,
2419e78e274eSKees Cook [OP_COPY_NOTIFY] = nfsd4_decode_copy_notify,
2420e78e274eSKees Cook [OP_DEALLOCATE] = nfsd4_decode_fallocate,
2421e78e274eSKees Cook [OP_IO_ADVISE] = nfsd4_decode_notsupp,
2422e78e274eSKees Cook [OP_LAYOUTERROR] = nfsd4_decode_notsupp,
242387a15a80SAnna Schumaker [OP_LAYOUTSTATS] = nfsd4_decode_notsupp,
242487a15a80SAnna Schumaker [OP_OFFLOAD_CANCEL] = nfsd4_decode_offload_status,
2425e78e274eSKees Cook [OP_OFFLOAD_STATUS] = nfsd4_decode_offload_status,
2426e78e274eSKees Cook [OP_READ_PLUS] = nfsd4_decode_read,
2427e78e274eSKees Cook [OP_SEEK] = nfsd4_decode_seek,
2428e78e274eSKees Cook [OP_WRITE_SAME] = nfsd4_decode_notsupp,
2429e78e274eSKees Cook [OP_CLONE] = nfsd4_decode_clone,
2430e78e274eSKees Cook /* RFC 8276 extended atributes operations */
2431e78e274eSKees Cook [OP_GETXATTR] = nfsd4_decode_getxattr,
2432e78e274eSKees Cook [OP_SETXATTR] = nfsd4_decode_setxattr,
2433e78e274eSKees Cook [OP_LISTXATTRS] = nfsd4_decode_listxattrs,
2434e78e274eSKees Cook [OP_REMOVEXATTR] = nfsd4_decode_removexattr,
2435e78e274eSKees Cook };
2436e78e274eSKees Cook
2437e78e274eSKees Cook static inline bool
nfsd4_opnum_in_range(struct nfsd4_compoundargs * argp,struct nfsd4_op * op)243823e50fe3SFrank van der Linden nfsd4_opnum_in_range(struct nfsd4_compoundargs *argp, struct nfsd4_op *op)
2439e78e274eSKees Cook {
2440e78e274eSKees Cook if (op->opnum < FIRST_NFS4_OP)
2441e78e274eSKees Cook return false;
2442e78e274eSKees Cook else if (argp->minorversion == 0 && op->opnum > LAST_NFS40_OP)
24432db134ebSAndy Adamson return false;
24442db134ebSAndy Adamson else if (argp->minorversion == 1 && op->opnum > LAST_NFS41_OP)
2445e1a90ebdSAnna Schumaker return false;
2446e1a90ebdSAnna Schumaker else if (argp->minorversion == 2 && op->opnum > LAST_NFS42_OP)
2447e1a90ebdSAnna Schumaker return false;
24488217d146SAnna Schumaker return true;
2449e1a90ebdSAnna Schumaker }
24508217d146SAnna Schumaker
2451e1a90ebdSAnna Schumaker static bool
nfsd4_decode_compound(struct nfsd4_compoundargs * argp)24528217d146SAnna Schumaker nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
24538217d146SAnna Schumaker {
24548217d146SAnna Schumaker struct nfsd4_op *op;
2455e1a90ebdSAnna Schumaker bool cachethis = false;
2456e1a90ebdSAnna Schumaker int auth_slack= argp->rqstp->rq_auth_slack;
2457e1a90ebdSAnna Schumaker int max_reply = auth_slack + 8; /* opcnt, status */
2458f2feb96bSBenny Halevy int readcount = 0;
2459c44b31c2SChuck Lever int readbytes = 0;
24601da177e4SLinus Torvalds __be32 *p;
24611da177e4SLinus Torvalds int i;
24621da177e4SLinus Torvalds
24631091006cSJ. Bruce Fields if (xdr_stream_decode_u32(argp->xdr, &argp->taglen) < 0)
2464a5cddc88SJ. Bruce Fields return false;
2465a5cddc88SJ. Bruce Fields max_reply += XDR_UNIT;
2466b0e35fdaSJ. Bruce Fields argp->tag = NULL;
2467b0e35fdaSJ. Bruce Fields if (unlikely(argp->taglen)) {
2468d9b74bdaSChuck Lever if (argp->taglen > NFSD4_MAX_TAGLEN)
24691da177e4SLinus Torvalds return false;
24701da177e4SLinus Torvalds p = xdr_inline_decode(argp->xdr, argp->taglen);
2471d9b74bdaSChuck Lever if (!p)
2472c44b31c2SChuck Lever return false;
2473d9b74bdaSChuck Lever argp->tag = svcxdr_savemem(argp, p, argp->taglen);
2474d9b74bdaSChuck Lever if (!argp->tag)
2475d9b74bdaSChuck Lever return false;
24761da177e4SLinus Torvalds max_reply += xdr_align_size(argp->taglen);
2477c44b31c2SChuck Lever }
2478d9b74bdaSChuck Lever
2479d9b74bdaSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &argp->minorversion) < 0)
2480c44b31c2SChuck Lever return false;
24817b723008SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &argp->client_opcnt) < 0)
2482d9b74bdaSChuck Lever return false;
2483c44b31c2SChuck Lever argp->opcnt = min_t(u32, argp->client_opcnt,
2484d9b74bdaSChuck Lever NFSD_MAX_OPS_PER_COMPOUND);
2485d9b74bdaSChuck Lever
2486d9b74bdaSChuck Lever if (argp->opcnt > ARRAY_SIZE(argp->iops)) {
2487d9b74bdaSChuck Lever argp->ops = vcalloc(argp->opcnt, sizeof(*argp->ops));
2488c44b31c2SChuck Lever if (!argp->ops) {
24897518a3dcSChuck Lever argp->ops = argp->iops;
2490c44b31c2SChuck Lever return false;
24917518a3dcSChuck Lever }
24927518a3dcSChuck Lever }
24931da177e4SLinus Torvalds
2494e8c96f8cSTobias Klauser if (argp->minorversion > NFSD_SUPPORTED_MINOR_VERSION)
249580e591ceSChuck Lever argp->opcnt = 0;
24961da177e4SLinus Torvalds
24971da177e4SLinus Torvalds for (i = 0; i < argp->opcnt; i++) {
2498c44b31c2SChuck Lever op = &argp->ops[i];
24991da177e4SLinus Torvalds op->replay = NULL;
25001da177e4SLinus Torvalds op->opdesc = NULL;
25011da177e4SLinus Torvalds
2502e1a90ebdSAnna Schumaker if (xdr_stream_decode_u32(argp->xdr, &op->opnum) < 0)
250330cff1ffSBenny Halevy return false;
250430cff1ffSBenny Halevy if (nfsd4_opnum_in_range(argp, op)) {
25051da177e4SLinus Torvalds op->opdesc = OPDESC(op);
25061da177e4SLinus Torvalds op->status = nfsd4_dec_ops[op->opnum](argp, &op->u);
25071da177e4SLinus Torvalds if (op->status != nfs_ok)
2508804d8e0aSChuck Lever trace_nfsd_compound_decode_err(argp->rqstp,
25091da177e4SLinus Torvalds argp->opcnt, i,
25103a237b4aSChuck Lever op->opnum,
2511c44b31c2SChuck Lever op->status);
251208281341SChuck Lever } else {
2513804d8e0aSChuck Lever op->opnum = OP_ILLEGAL;
2514e1a90ebdSAnna Schumaker op->status = nfserr_op_illegal;
251508281341SChuck Lever }
251608281341SChuck Lever
251708281341SChuck Lever /*
251808281341SChuck Lever * We'll try to cache the result in the DRC if any one
251908281341SChuck Lever * op in the compound wants to be cached:
252008281341SChuck Lever */
25211da177e4SLinus Torvalds cachethis |= nfsd4_cache_this_op(op);
25221da177e4SLinus Torvalds
25231da177e4SLinus Torvalds if (op->opnum == OP_READ || op->opnum == OP_READ_PLUS) {
2524804d8e0aSChuck Lever readcount++;
25251091006cSJ. Bruce Fields readbytes += nfsd4_max_reply(argp->rqstp, op);
25261091006cSJ. Bruce Fields } else
25271091006cSJ. Bruce Fields max_reply += nfsd4_max_reply(argp->rqstp, op);
25281091006cSJ. Bruce Fields /*
25291091006cSJ. Bruce Fields * OP_LOCK and OP_LOCKT may return a conflicting lock.
25306ff40decSJ. Bruce Fields * (Special case because it will just skip encoding this
2531528b8493SAnna Schumaker * if it runs out of xdr buffer space, and it is the only
2532b0e35fdaSJ. Bruce Fields * operation that behaves this way.)
2533b0e35fdaSJ. Bruce Fields */
2534b0e35fdaSJ. Bruce Fields if (op->opnum == OP_LOCK || op->opnum == OP_LOCKT)
25354f0cefbfSJ. Bruce Fields max_reply += NFS4_OPAQUE_LIMIT;
2536f7b43d0cSJ. Bruce Fields
25377323f0d2SKinglong Mee if (op->status) {
25387323f0d2SKinglong Mee argp->opcnt = i+1;
25397323f0d2SKinglong Mee break;
25407323f0d2SKinglong Mee }
2541f7b43d0cSJ. Bruce Fields }
25427323f0d2SKinglong Mee /* Sessions make the DRC unnecessary: */
2543f7b43d0cSJ. Bruce Fields if (argp->minorversion)
2544e372ba60SJ. Bruce Fields cachethis = false;
2545e372ba60SJ. Bruce Fields svc_reserve(argp->rqstp, max_reply + readbytes);
2546e372ba60SJ. Bruce Fields argp->rqstp->rq_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE;
2547e372ba60SJ. Bruce Fields
2548e372ba60SJ. Bruce Fields argp->splice_ok = nfsd_read_splice_ok(argp->rqstp);
25491da177e4SLinus Torvalds if (readcount > 1 || max_reply > PAGE_SIZE - auth_slack)
25501091006cSJ. Bruce Fields argp->splice_ok = false;
25511091006cSJ. Bruce Fields
25521091006cSJ. Bruce Fields return true;
2553b0e35fdaSJ. Bruce Fields }
25541091006cSJ. Bruce Fields
nfsd4_encode_nfs_fh4(struct xdr_stream * xdr,struct knfsd_fh * fh_handle)25551da177e4SLinus Torvalds static __be32 nfsd4_encode_nfs_fh4(struct xdr_stream *xdr,
2556a2c91753SChuck Lever struct knfsd_fh *fh_handle)
2557a5cddc88SJ. Bruce Fields {
2558a2c91753SChuck Lever return nfsd4_encode_opaque(xdr, fh_handle->fh_raw, fh_handle->fh_size);
2559b0e35fdaSJ. Bruce Fields }
2560c44b31c2SChuck Lever
25611da177e4SLinus Torvalds /* This is a frequently-encoded type; open-coded for speed */
nfsd4_encode_nfstime4(struct xdr_stream * xdr,const struct timespec64 * tv)25621da177e4SLinus Torvalds static __be32 nfsd4_encode_nfstime4(struct xdr_stream *xdr,
25633283bf64SChuck Lever const struct timespec64 *tv)
25643283bf64SChuck Lever {
25653283bf64SChuck Lever __be32 *p;
25663283bf64SChuck Lever
25673283bf64SChuck Lever p = xdr_reserve_space(xdr, XDR_UNIT * 3);
25683283bf64SChuck Lever if (!p)
2569eed4d1adSChuck Lever return nfserr_resource;
257026217679SChuck Lever p = xdr_encode_hyper(p, tv->tv_sec);
2571eed4d1adSChuck Lever *p = cpu_to_be32(tv->tv_nsec);
257226217679SChuck Lever return nfs_ok;
257326217679SChuck Lever }
257426217679SChuck Lever
nfsd4_encode_specdata4(struct xdr_stream * xdr,unsigned int major,unsigned int minor)257526217679SChuck Lever static __be32 nfsd4_encode_specdata4(struct xdr_stream *xdr,
257626217679SChuck Lever unsigned int major, unsigned int minor)
257726217679SChuck Lever {
2578eed4d1adSChuck Lever __be32 status;
257926217679SChuck Lever
258026217679SChuck Lever status = nfsd4_encode_uint32_t(xdr, major);
258126217679SChuck Lever if (status != nfs_ok)
258226217679SChuck Lever return status;
2583a460cda2SChuck Lever return nfsd4_encode_uint32_t(xdr, minor);
2584a460cda2SChuck Lever }
2585a460cda2SChuck Lever
2586a460cda2SChuck Lever static __be32
nfsd4_encode_change_info4(struct xdr_stream * xdr,const struct nfsd4_change_info * c)2587a460cda2SChuck Lever nfsd4_encode_change_info4(struct xdr_stream *xdr, const struct nfsd4_change_info *c)
2588a460cda2SChuck Lever {
2589a460cda2SChuck Lever __be32 status;
2590a460cda2SChuck Lever
2591a460cda2SChuck Lever status = nfsd4_encode_bool(xdr, c->atomic);
2592a460cda2SChuck Lever if (status != nfs_ok)
2593a460cda2SChuck Lever return status;
259466a21db7SChuck Lever status = nfsd4_encode_changeid4(xdr, c->before_change);
2595263453d9SChuck Lever if (status != nfs_ok)
2596c654b8a9SJ. Bruce Fields return status;
2597263453d9SChuck Lever return nfsd4_encode_changeid4(xdr, c->after_change);
2598263453d9SChuck Lever }
2599263453d9SChuck Lever
nfsd4_encode_netaddr4(struct xdr_stream * xdr,const struct nfs42_netaddr * addr)2600263453d9SChuck Lever static __be32 nfsd4_encode_netaddr4(struct xdr_stream *xdr,
2601263453d9SChuck Lever const struct nfs42_netaddr *addr)
2602263453d9SChuck Lever {
2603263453d9SChuck Lever __be32 status;
2604263453d9SChuck Lever
2605263453d9SChuck Lever /* na_r_netid */
2606c654b8a9SJ. Bruce Fields status = nfsd4_encode_opaque(xdr, addr->netid, addr->netid_len);
26071da177e4SLinus Torvalds if (status != nfs_ok)
260821d316a7SChuck Lever return status;
260921d316a7SChuck Lever /* na_r_addr */
261021d316a7SChuck Lever return nfsd4_encode_opaque(xdr, addr->addr, addr->addr_len);
261121d316a7SChuck Lever }
261221d316a7SChuck Lever
261321d316a7SChuck Lever /* Encode as an array of strings the string given with components
261421d316a7SChuck Lever * separated @sep, escaped with esc_enter and esc_exit.
261521d316a7SChuck Lever */
nfsd4_encode_components_esc(struct xdr_stream * xdr,char sep,char * components,char esc_enter,char esc_exit)261621d316a7SChuck Lever static __be32 nfsd4_encode_components_esc(struct xdr_stream *xdr, char sep,
261721d316a7SChuck Lever char *components, char esc_enter,
261821d316a7SChuck Lever char esc_exit)
261921d316a7SChuck Lever {
262021d316a7SChuck Lever __be32 *p;
262181c3f413SJ.Bruce Fields __be32 pathlen;
2622e7a0444aSWeston Andros Adamson int pathlen_offset;
262381c3f413SJ.Bruce Fields int strlen, count=0;
2624ddd1ea56SJ. Bruce Fields char *str, *end, *next;
2625ddd1ea56SJ. Bruce Fields
2626ddd1ea56SJ. Bruce Fields dprintk("nfsd4_encode_components(%s)\n", components);
262781c3f413SJ.Bruce Fields
2628ddd1ea56SJ. Bruce Fields pathlen_offset = xdr->buf->len;
2629082d4bd7SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
2630082d4bd7SJ. Bruce Fields if (!p)
263181c3f413SJ.Bruce Fields return nfserr_resource;
2632e7a0444aSWeston Andros Adamson p++; /* We will fill this in with @count later */
263381c3f413SJ.Bruce Fields
263481c3f413SJ.Bruce Fields end = str = components;
2635082d4bd7SJ. Bruce Fields while (*end) {
2636082d4bd7SJ. Bruce Fields bool found_esc = false;
2637ddd1ea56SJ. Bruce Fields
2638ddd1ea56SJ. Bruce Fields /* try to parse as esc_start, ..., esc_end, sep */
263981c3f413SJ.Bruce Fields if (*str == esc_enter) {
2640082d4bd7SJ. Bruce Fields for (; *end && (*end != esc_exit); end++)
2641082d4bd7SJ. Bruce Fields /* find esc_exit or end of string */;
264281c3f413SJ.Bruce Fields next = end + 1;
264381c3f413SJ.Bruce Fields if (*end && (!*next || *next == sep)) {
2644e7a0444aSWeston Andros Adamson str++;
2645e7a0444aSWeston Andros Adamson found_esc = true;
2646e7a0444aSWeston Andros Adamson }
2647e7a0444aSWeston Andros Adamson }
2648e7a0444aSWeston Andros Adamson
2649e7a0444aSWeston Andros Adamson if (!found_esc)
2650e7a0444aSWeston Andros Adamson for (; *end && (*end != sep); end++)
2651e7a0444aSWeston Andros Adamson /* find sep or end of string */;
2652e7a0444aSWeston Andros Adamson
2653e7a0444aSWeston Andros Adamson strlen = end - str;
2654e7a0444aSWeston Andros Adamson if (strlen) {
2655e7a0444aSWeston Andros Adamson p = xdr_reserve_space(xdr, strlen + 4);
2656e7a0444aSWeston Andros Adamson if (!p)
2657e7a0444aSWeston Andros Adamson return nfserr_resource;
265881c3f413SJ.Bruce Fields p = xdr_encode_opaque(p, str, strlen);
2659e7a0444aSWeston Andros Adamson count++;
2660e7a0444aSWeston Andros Adamson }
266181c3f413SJ.Bruce Fields else
266281c3f413SJ.Bruce Fields end++;
2663ddd1ea56SJ. Bruce Fields if (found_esc)
2664ddd1ea56SJ. Bruce Fields end = next;
266581c3f413SJ.Bruce Fields
26660c0c267bSJ. Bruce Fields str = end;
266781c3f413SJ.Bruce Fields }
266881c3f413SJ.Bruce Fields pathlen = htonl(count);
266981c3f413SJ.Bruce Fields write_bytes_to_xdr_buf(xdr->buf, pathlen_offset, &pathlen, 4);
267081c3f413SJ.Bruce Fields return 0;
26715a64e569SBenjamin Coddington }
26725a64e569SBenjamin Coddington
26735a64e569SBenjamin Coddington /* Encode as an array of strings the string given with components
267481c3f413SJ.Bruce Fields * separated @sep.
267581c3f413SJ.Bruce Fields */
nfsd4_encode_components(struct xdr_stream * xdr,char sep,char * components)2676bf7491f1SBenjamin Coddington static __be32 nfsd4_encode_components(struct xdr_stream *xdr, char sep,
2677082d4bd7SJ. Bruce Fields char *components)
267881c3f413SJ.Bruce Fields {
267981c3f413SJ.Bruce Fields return nfsd4_encode_components_esc(xdr, sep, components, 0, 0);
268081c3f413SJ.Bruce Fields }
2681e7a0444aSWeston Andros Adamson
nfsd4_encode_fs_location4(struct xdr_stream * xdr,struct nfsd4_fs_location * location)2682e7a0444aSWeston Andros Adamson static __be32 nfsd4_encode_fs_location4(struct xdr_stream *xdr,
2683e7a0444aSWeston Andros Adamson struct nfsd4_fs_location *location)
2684ddd1ea56SJ. Bruce Fields {
2685ddd1ea56SJ. Bruce Fields __be32 status;
2686e7a0444aSWeston Andros Adamson
2687ddd1ea56SJ. Bruce Fields status = nfsd4_encode_components_esc(xdr, ':', location->hosts,
2688e7a0444aSWeston Andros Adamson '[', ']');
2689e7a0444aSWeston Andros Adamson if (status)
2690ddd1ea56SJ. Bruce Fields return status;
2691ddd1ea56SJ. Bruce Fields status = nfsd4_encode_components(xdr, '/', location->path);
269281c3f413SJ.Bruce Fields if (status)
2693b37ad28bSAl Viro return status;
269481c3f413SJ.Bruce Fields return nfs_ok;
2695ddd1ea56SJ. Bruce Fields }
2696e7a0444aSWeston Andros Adamson
nfsd4_encode_pathname4(struct xdr_stream * xdr,const struct path * root,const struct path * path)269781c3f413SJ.Bruce Fields static __be32 nfsd4_encode_pathname4(struct xdr_stream *xdr,
269881c3f413SJ.Bruce Fields const struct path *root,
2699ddd1ea56SJ. Bruce Fields const struct path *path)
270081c3f413SJ.Bruce Fields {
270181c3f413SJ.Bruce Fields struct path cur = *path;
2702a1469a37SChuck Lever __be32 *p;
270381c3f413SJ.Bruce Fields struct dentry **components = NULL;
270481c3f413SJ.Bruce Fields unsigned int ncomponents = 0;
2705a1469a37SChuck Lever __be32 err = nfserr_jukebox;
2706ddd1ea56SJ. Bruce Fields
2707ddd1ea56SJ. Bruce Fields dprintk("nfsd4_encode_components(");
270881c3f413SJ.Bruce Fields
2709301f0268SAl Viro path_get(&cur);
2710ddd1ea56SJ. Bruce Fields /* First walk the path up to the nfsd root, and store the
2711ed748aacSTrond Myklebust * dentries/path components in an array.
2712ed748aacSTrond Myklebust */
2713ed748aacSTrond Myklebust for (;;) {
271481c3f413SJ.Bruce Fields if (path_equal(&cur, root))
2715ed748aacSTrond Myklebust break;
271681c3f413SJ.Bruce Fields if (cur.dentry == cur.mnt->mnt_root) {
2717ed748aacSTrond Myklebust if (follow_up(&cur))
2718ed748aacSTrond Myklebust continue;
2719ed748aacSTrond Myklebust goto out_free;
2720ed748aacSTrond Myklebust }
2721ed748aacSTrond Myklebust if ((ncomponents & 15) == 0) {
2722b77a4b2eSKinglong Mee struct dentry **new;
2723ed748aacSTrond Myklebust new = krealloc(components,
2724ed748aacSTrond Myklebust sizeof(*new) * (ncomponents + 16),
2725ed748aacSTrond Myklebust GFP_KERNEL);
2726ed748aacSTrond Myklebust if (!new)
2727ed748aacSTrond Myklebust goto out_free;
272881c3f413SJ.Bruce Fields components = new;
2729ed748aacSTrond Myklebust }
2730ed748aacSTrond Myklebust components[ncomponents++] = cur.dentry;
2731ed748aacSTrond Myklebust cur.dentry = dget_parent(cur.dentry);
2732ed748aacSTrond Myklebust }
2733ed748aacSTrond Myklebust err = nfserr_resource;
2734ed748aacSTrond Myklebust p = xdr_reserve_space(xdr, 4);
2735ed748aacSTrond Myklebust if (!p)
2736ed748aacSTrond Myklebust goto out_free;
2737ed748aacSTrond Myklebust *p++ = cpu_to_be32(ncomponents);
2738ed748aacSTrond Myklebust
2739ed748aacSTrond Myklebust while (ncomponents) {
2740ed748aacSTrond Myklebust struct dentry *dentry = components[ncomponents - 1];
2741ddd1ea56SJ. Bruce Fields unsigned int len;
2742ddd1ea56SJ. Bruce Fields
2743ddd1ea56SJ. Bruce Fields spin_lock(&dentry->d_lock);
2744ed748aacSTrond Myklebust len = dentry->d_name.len;
2745c373b0a4SJ. Bruce Fields p = xdr_reserve_space(xdr, len + 4);
2746ed748aacSTrond Myklebust if (!p) {
2747ed748aacSTrond Myklebust spin_unlock(&dentry->d_lock);
2748ed748aacSTrond Myklebust goto out_free;
2749301f0268SAl Viro }
2750ed748aacSTrond Myklebust p = xdr_encode_opaque(p, dentry->d_name.name, len);
2751301f0268SAl Viro dprintk("/%pd", dentry);
2752301f0268SAl Viro spin_unlock(&dentry->d_lock);
2753ddd1ea56SJ. Bruce Fields dput(dentry);
2754ddd1ea56SJ. Bruce Fields ncomponents--;
2755301f0268SAl Viro }
2756ed748aacSTrond Myklebust
2757301f0268SAl Viro err = 0;
27580c0c267bSJ. Bruce Fields out_free:
2759a455589fSAl Viro dprintk(")\n");
2760301f0268SAl Viro while (ncomponents)
2761ed748aacSTrond Myklebust dput(components[--ncomponents]);
2762ed748aacSTrond Myklebust kfree(components);
2763ed748aacSTrond Myklebust path_put(&cur);
2764ed748aacSTrond Myklebust return err;
2765ed748aacSTrond Myklebust }
2766ed748aacSTrond Myklebust
nfsd4_encode_fs_locations4(struct xdr_stream * xdr,struct svc_rqst * rqstp,struct svc_export * exp)2767ed748aacSTrond Myklebust static __be32 nfsd4_encode_fs_locations4(struct xdr_stream *xdr,
2768ed748aacSTrond Myklebust struct svc_rqst *rqstp,
2769ed748aacSTrond Myklebust struct svc_export *exp)
2770ed748aacSTrond Myklebust {
2771ed748aacSTrond Myklebust struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs;
2772ed748aacSTrond Myklebust struct svc_export *exp_ps;
2773ed748aacSTrond Myklebust unsigned int i;
2774ed748aacSTrond Myklebust __be32 status;
2775a1469a37SChuck Lever
2776a1469a37SChuck Lever /* fs_root */
2777a1469a37SChuck Lever exp_ps = rqst_find_fsidzero_export(rqstp);
2778ed748aacSTrond Myklebust if (IS_ERR(exp_ps))
2779a1469a37SChuck Lever return nfserrno(PTR_ERR(exp_ps));
2780ed748aacSTrond Myklebust status = nfsd4_encode_pathname4(xdr, &exp_ps->ex_path, &exp->ex_path);
2781a1469a37SChuck Lever exp_put(exp_ps);
2782a1469a37SChuck Lever if (status != nfs_ok)
2783ed748aacSTrond Myklebust return status;
2784a1469a37SChuck Lever
2785ed748aacSTrond Myklebust /* locations<> */
2786ed748aacSTrond Myklebust if (xdr_stream_encode_u32(xdr, fslocs->locations_count) != XDR_UNIT)
2787ed748aacSTrond Myklebust return nfserr_resource;
2788a1469a37SChuck Lever for (i = 0; i < fslocs->locations_count; i++) {
2789ed748aacSTrond Myklebust status = nfsd4_encode_fs_location4(xdr, &fslocs->locations[i]);
2790a1469a37SChuck Lever if (status != nfs_ok)
279181c3f413SJ.Bruce Fields return status;
2792a1469a37SChuck Lever }
2793a1469a37SChuck Lever
2794a1469a37SChuck Lever return nfs_ok;
279581c3f413SJ.Bruce Fields }
279681c3f413SJ.Bruce Fields
nfsd4_encode_nfsace4(struct xdr_stream * xdr,struct svc_rqst * rqstp,struct nfs4_ace * ace)2797ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_nfsace4(struct xdr_stream *xdr, struct svc_rqst *rqstp,
2798a1469a37SChuck Lever struct nfs4_ace *ace)
279981c3f413SJ.Bruce Fields {
280081c3f413SJ.Bruce Fields __be32 status;
2801a1469a37SChuck Lever
2802a1469a37SChuck Lever /* type */
280381c3f413SJ.Bruce Fields status = nfsd4_encode_acetype4(xdr, ace->type);
28041da177e4SLinus Torvalds if (status != nfs_ok)
28050207ee08SChuck Lever return nfserr_resource;
2806ddd1ea56SJ. Bruce Fields /* flag */
28071da177e4SLinus Torvalds status = nfsd4_encode_aceflag4(xdr, ace->flag);
28080207ee08SChuck Lever if (status != nfs_ok)
28090207ee08SChuck Lever return nfserr_resource;
28100207ee08SChuck Lever /* access mask */
28110207ee08SChuck Lever status = nfsd4_encode_acemask4(xdr, ace->access_mask & NFS4_ACE_MASK_ALL);
28120207ee08SChuck Lever if (status != nfs_ok)
28130207ee08SChuck Lever return nfserr_resource;
28140207ee08SChuck Lever /* who */
28150207ee08SChuck Lever if (ace->whotype != NFS4_ACL_WHO_NAMED)
28160207ee08SChuck Lever return nfs4_acl_write_who(xdr, ace->whotype);
28170207ee08SChuck Lever if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
28180207ee08SChuck Lever return nfsd4_encode_group(xdr, rqstp, ace->who_gid);
28190207ee08SChuck Lever return nfsd4_encode_user(xdr, rqstp, ace->who_uid);
28200207ee08SChuck Lever }
28210207ee08SChuck Lever
28220207ee08SChuck Lever #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
28233554116dSJ. Bruce Fields FATTR4_WORD0_RDATTR_ERROR)
2824ddd1ea56SJ. Bruce Fields #define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
28250207ee08SChuck Lever #define WORD2_ABSENT_FS_ATTRS 0
2826ddd1ea56SJ. Bruce Fields
2827ddd1ea56SJ. Bruce Fields #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
28281da177e4SLinus Torvalds static inline __be32
nfsd4_encode_security_label(struct xdr_stream * xdr,struct svc_rqst * rqstp,void * context,int len)28291da177e4SLinus Torvalds nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
283042ca0993SJ.Bruce Fields void *context, int len)
283142ca0993SJ.Bruce Fields {
283242ca0993SJ.Bruce Fields __be32 *p;
2833c2227a39SKinglong Mee
283442ca0993SJ.Bruce Fields p = xdr_reserve_space(xdr, len + 4 + 4 + 4);
283518032ca0SDavid Quigley if (!p)
283618032ca0SDavid Quigley return nfserr_resource;
2837ddd1ea56SJ. Bruce Fields
2838ddd1ea56SJ. Bruce Fields /*
283918032ca0SDavid Quigley * For now we use a 0 here to indicate the null translation; in
2840ddd1ea56SJ. Bruce Fields * the future we may place a call to translation code here.
284118032ca0SDavid Quigley */
2842ddd1ea56SJ. Bruce Fields *p++ = cpu_to_be32(0); /* lfs */
2843ddd1ea56SJ. Bruce Fields *p++ = cpu_to_be32(0); /* pi */
284418032ca0SDavid Quigley p = xdr_encode_opaque(p, context, len);
284518032ca0SDavid Quigley return 0;
284618032ca0SDavid Quigley }
284718032ca0SDavid Quigley #else
284818032ca0SDavid Quigley static inline __be32
nfsd4_encode_security_label(struct xdr_stream * xdr,struct svc_rqst * rqstp,void * context,int len)284918032ca0SDavid Quigley nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
2850c373b0a4SJ. Bruce Fields void *context, int len)
2851c373b0a4SJ. Bruce Fields { return 0; }
285218032ca0SDavid Quigley #endif
285318032ca0SDavid Quigley
fattr_handle_absent_fs(u32 * bmval0,u32 * bmval1,u32 * bmval2,u32 * rdattr_err)285418032ca0SDavid Quigley static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *bmval2, u32 *rdattr_err)
285518032ca0SDavid Quigley {
285618032ca0SDavid Quigley /* As per referral draft: */
2857ddd1ea56SJ. Bruce Fields if (*bmval0 & ~WORD0_ABSENT_FS_ATTRS ||
2858ddd1ea56SJ. Bruce Fields *bmval1 & ~WORD1_ABSENT_FS_ATTRS) {
285918032ca0SDavid Quigley if (*bmval0 & FATTR4_WORD0_RDATTR_ERROR ||
286018032ca0SDavid Quigley *bmval0 & FATTR4_WORD0_FS_LOCATIONS)
286118032ca0SDavid Quigley *rdattr_err = NFSERR_MOVED;
2862c2227a39SKinglong Mee else
286342ca0993SJ.Bruce Fields return nfserr_moved;
286442ca0993SJ.Bruce Fields }
286542ca0993SJ.Bruce Fields *bmval0 &= WORD0_ABSENT_FS_ATTRS;
286642ca0993SJ.Bruce Fields *bmval1 &= WORD1_ABSENT_FS_ATTRS;
286742ca0993SJ.Bruce Fields *bmval2 &= WORD2_ABSENT_FS_ATTRS;
286842ca0993SJ.Bruce Fields return 0;
286942ca0993SJ.Bruce Fields }
287042ca0993SJ.Bruce Fields
287142ca0993SJ.Bruce Fields
nfsd4_get_mounted_on_ino(struct svc_export * exp,u64 * pino)287242ca0993SJ.Bruce Fields static int nfsd4_get_mounted_on_ino(struct svc_export *exp, u64 *pino)
287342ca0993SJ.Bruce Fields {
287442ca0993SJ.Bruce Fields struct path path = exp->ex_path;
2875c2227a39SKinglong Mee struct kstat stat;
287642ca0993SJ.Bruce Fields int err;
287742ca0993SJ.Bruce Fields
28781da177e4SLinus Torvalds path_get(&path);
2879ae7095a7SJ. Bruce Fields while (follow_up(&path)) {
28806106d911SJeff Layton if (path.dentry != path.mnt->mnt_root)
2881ae7095a7SJ. Bruce Fields break;
2882ae7095a7SJ. Bruce Fields }
28836106d911SJeff Layton err = vfs_getattr(&path, &stat, STATX_INO, AT_STATX_SYNC_AS_STAT);
2884ae7095a7SJ. Bruce Fields path_put(&path);
2885ae7095a7SJ. Bruce Fields if (!err)
2886ae7095a7SJ. Bruce Fields *pino = stat.ino;
2887ae7095a7SJ. Bruce Fields return err;
2888ae7095a7SJ. Bruce Fields }
2889ae7095a7SJ. Bruce Fields
2890ae7095a7SJ. Bruce Fields static __be32
nfsd4_encode_bitmap4(struct xdr_stream * xdr,u32 bmval0,u32 bmval1,u32 bmval2)28916106d911SJeff Layton nfsd4_encode_bitmap4(struct xdr_stream *xdr, u32 bmval0, u32 bmval1, u32 bmval2)
2892ae7095a7SJ. Bruce Fields {
28936106d911SJeff Layton __be32 *p;
28946106d911SJeff Layton
2895ae7095a7SJ. Bruce Fields if (bmval2) {
2896ae7095a7SJ. Bruce Fields p = xdr_reserve_space(xdr, XDR_UNIT * 4);
2897ae7095a7SJ. Bruce Fields if (!p)
289875976de6SKinglong Mee goto out_resource;
2899e64301f5SChuck Lever *p++ = cpu_to_be32(3);
290075976de6SKinglong Mee *p++ = cpu_to_be32(bmval0);
290175976de6SKinglong Mee *p++ = cpu_to_be32(bmval1);
290275976de6SKinglong Mee *p++ = cpu_to_be32(bmval2);
290375976de6SKinglong Mee } else if (bmval1) {
2904e64301f5SChuck Lever p = xdr_reserve_space(xdr, XDR_UNIT * 3);
290575976de6SKinglong Mee if (!p)
290675976de6SKinglong Mee goto out_resource;
290775976de6SKinglong Mee *p++ = cpu_to_be32(2);
290875976de6SKinglong Mee *p++ = cpu_to_be32(bmval0);
290975976de6SKinglong Mee *p++ = cpu_to_be32(bmval1);
291075976de6SKinglong Mee } else {
291175976de6SKinglong Mee p = xdr_reserve_space(xdr, XDR_UNIT * 2);
2912e64301f5SChuck Lever if (!p)
291375976de6SKinglong Mee goto out_resource;
291475976de6SKinglong Mee *p++ = cpu_to_be32(1);
291575976de6SKinglong Mee *p++ = cpu_to_be32(bmval0);
291675976de6SKinglong Mee }
291775976de6SKinglong Mee
291875976de6SKinglong Mee return nfs_ok;
2919e64301f5SChuck Lever out_resource:
292075976de6SKinglong Mee return nfserr_resource;
292175976de6SKinglong Mee }
292275976de6SKinglong Mee
292375976de6SKinglong Mee struct nfsd4_fattr_args {
292475976de6SKinglong Mee struct svc_rqst *rqstp;
292575976de6SKinglong Mee struct svc_fh *fhp;
2926e64301f5SChuck Lever struct svc_export *exp;
292775976de6SKinglong Mee struct dentry *dentry;
292875976de6SKinglong Mee struct kstat stat;
292975976de6SKinglong Mee struct kstatfs statfs;
293075976de6SKinglong Mee struct nfs4_acl *acl;
293183ab8678SChuck Lever u64 size;
2932c9090e27SChuck Lever #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
293383ab8678SChuck Lever void *context;
293436ed7e64SChuck Lever int contextlen;
2935c9090e27SChuck Lever #endif
293683ab8678SChuck Lever u32 rdattr_err;
293783ab8678SChuck Lever bool contextsupport;
293883ab8678SChuck Lever bool ignore_crossmnt;
293983ab8678SChuck Lever };
2940f59388a5SChuck Lever
2941f59388a5SChuck Lever typedef __be32(*nfsd4_enc_attr)(struct xdr_stream *xdr,
2942f59388a5SChuck Lever const struct nfsd4_fattr_args *args);
2943f59388a5SChuck Lever
nfsd4_encode_fattr4__noop(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)294483ab8678SChuck Lever static __be32 nfsd4_encode_fattr4__noop(struct xdr_stream *xdr,
294583ab8678SChuck Lever const struct nfsd4_fattr_args *args)
29461b9097e3SChuck Lever {
294783ab8678SChuck Lever return nfs_ok;
294883ab8678SChuck Lever }
2949fce7913bSChuck Lever
nfsd4_encode_fattr4__true(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)2950fce7913bSChuck Lever static __be32 nfsd4_encode_fattr4__true(struct xdr_stream *xdr,
2951fce7913bSChuck Lever const struct nfsd4_fattr_args *args)
2952fce7913bSChuck Lever {
2953fce7913bSChuck Lever return nfsd4_encode_bool(xdr, true);
2954fce7913bSChuck Lever }
2955fce7913bSChuck Lever
nfsd4_encode_fattr4__false(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)2956fce7913bSChuck Lever static __be32 nfsd4_encode_fattr4__false(struct xdr_stream *xdr,
2957fce7913bSChuck Lever const struct nfsd4_fattr_args *args)
2958c88cb472SChuck Lever {
2959c88cb472SChuck Lever return nfsd4_encode_bool(xdr, false);
2960c88cb472SChuck Lever }
2961c88cb472SChuck Lever
nfsd4_encode_fattr4_supported_attrs(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)2962c88cb472SChuck Lever static __be32 nfsd4_encode_fattr4_supported_attrs(struct xdr_stream *xdr,
2963c88cb472SChuck Lever const struct nfsd4_fattr_args *args)
29648c442288SChuck Lever {
29658c442288SChuck Lever struct nfsd4_compoundres *resp = args->rqstp->rq_resp;
29668c442288SChuck Lever u32 minorversion = resp->cstate.minorversion;
29678c442288SChuck Lever u32 supp[3];
29688c442288SChuck Lever
29698c442288SChuck Lever memcpy(supp, nfsd_suppattrs[minorversion], sizeof(supp));
2970c9090e27SChuck Lever if (!IS_POSIXACL(d_inode(args->dentry)))
2971c9090e27SChuck Lever supp[0] &= ~FATTR4_WORD0_ACL;
2972c9090e27SChuck Lever if (!args->contextsupport)
2973c9090e27SChuck Lever supp[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
2974c9090e27SChuck Lever
2975c9090e27SChuck Lever return nfsd4_encode_bitmap4(xdr, supp[0], supp[1], supp[2]);
2976c9090e27SChuck Lever }
2977c9090e27SChuck Lever
nfsd4_encode_fattr4_type(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)2978c9090e27SChuck Lever static __be32 nfsd4_encode_fattr4_type(struct xdr_stream *xdr,
2979c9090e27SChuck Lever const struct nfsd4_fattr_args *args)
2980c9090e27SChuck Lever {
2981c9090e27SChuck Lever __be32 *p;
2982c9090e27SChuck Lever
2983c9090e27SChuck Lever p = xdr_reserve_space(xdr, XDR_UNIT);
2984c9090e27SChuck Lever if (!p)
2985c9090e27SChuck Lever return nfserr_resource;
2986b06cf375SChuck Lever
2987b06cf375SChuck Lever switch (args->stat.mode & S_IFMT) {
2988b06cf375SChuck Lever case S_IFIFO:
2989b06cf375SChuck Lever *p = cpu_to_be32(NF4FIFO);
2990b06cf375SChuck Lever break;
2991b06cf375SChuck Lever case S_IFCHR:
2992b06cf375SChuck Lever *p = cpu_to_be32(NF4CHR);
2993b06cf375SChuck Lever break;
2994b06cf375SChuck Lever case S_IFDIR:
2995b06cf375SChuck Lever *p = cpu_to_be32(NF4DIR);
2996b06cf375SChuck Lever break;
2997b06cf375SChuck Lever case S_IFBLK:
2998b06cf375SChuck Lever *p = cpu_to_be32(NF4BLK);
2999b06cf375SChuck Lever break;
3000b06cf375SChuck Lever case S_IFLNK:
3001b06cf375SChuck Lever *p = cpu_to_be32(NF4LNK);
3002b06cf375SChuck Lever break;
3003b06cf375SChuck Lever case S_IFREG:
3004b06cf375SChuck Lever *p = cpu_to_be32(NF4REG);
3005b06cf375SChuck Lever break;
3006b06cf375SChuck Lever case S_IFSOCK:
3007b06cf375SChuck Lever *p = cpu_to_be32(NF4SOCK);
3008b06cf375SChuck Lever break;
3009b06cf375SChuck Lever default:
3010b06cf375SChuck Lever return nfserr_serverfault;
3011b06cf375SChuck Lever }
3012b06cf375SChuck Lever
3013b06cf375SChuck Lever return nfs_ok;
3014b06cf375SChuck Lever }
3015b06cf375SChuck Lever
nfsd4_encode_fattr4_fh_expire_type(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3016b06cf375SChuck Lever static __be32 nfsd4_encode_fattr4_fh_expire_type(struct xdr_stream *xdr,
3017b06cf375SChuck Lever const struct nfsd4_fattr_args *args)
3018b06cf375SChuck Lever {
3019b06cf375SChuck Lever u32 mask;
3020b06cf375SChuck Lever
3021b06cf375SChuck Lever mask = NFS4_FH_PERSISTENT;
3022b06cf375SChuck Lever if (!(args->exp->ex_flags & NFSEXP_NOSUBTREECHECK))
3023b06cf375SChuck Lever mask |= NFS4_FH_VOL_RENAME;
302436ed7e64SChuck Lever return nfsd4_encode_uint32_t(xdr, mask);
302536ed7e64SChuck Lever }
302636ed7e64SChuck Lever
nfsd4_encode_fattr4_change(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)302736ed7e64SChuck Lever static __be32 nfsd4_encode_fattr4_change(struct xdr_stream *xdr,
302836ed7e64SChuck Lever const struct nfsd4_fattr_args *args)
302936ed7e64SChuck Lever {
303036ed7e64SChuck Lever const struct svc_export *exp = args->exp;
303136ed7e64SChuck Lever u64 c;
303236ed7e64SChuck Lever
303336ed7e64SChuck Lever if (unlikely(exp->ex_flags & NFSEXP_V4ROOT)) {
303436ed7e64SChuck Lever u32 flush_time = convert_to_wallclock(exp->cd->flush_time);
3035263453d9SChuck Lever
3036263453d9SChuck Lever if (xdr_stream_encode_u32(xdr, flush_time) != XDR_UNIT)
3037263453d9SChuck Lever return nfserr_resource;
3038263453d9SChuck Lever if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
3039263453d9SChuck Lever return nfserr_resource;
3040263453d9SChuck Lever return nfs_ok;
3041263453d9SChuck Lever }
3042263453d9SChuck Lever
3043263453d9SChuck Lever c = nfsd4_change_attribute(&args->stat, d_inode(args->dentry));
3044263453d9SChuck Lever return nfsd4_encode_changeid4(xdr, c);
3045263453d9SChuck Lever }
3046263453d9SChuck Lever
nfsd4_encode_fattr4_size(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3047263453d9SChuck Lever static __be32 nfsd4_encode_fattr4_size(struct xdr_stream *xdr,
3048263453d9SChuck Lever const struct nfsd4_fattr_args *args)
3049263453d9SChuck Lever {
3050263453d9SChuck Lever return nfsd4_encode_uint64_t(xdr, args->size);
3051263453d9SChuck Lever }
3052263453d9SChuck Lever
nfsd4_encode_fattr4_fsid(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3053263453d9SChuck Lever static __be32 nfsd4_encode_fattr4_fsid(struct xdr_stream *xdr,
3054263453d9SChuck Lever const struct nfsd4_fattr_args *args)
3055d0b28aadSChuck Lever {
3056d0b28aadSChuck Lever __be32 *p;
3057d0b28aadSChuck Lever
3058d0b28aadSChuck Lever p = xdr_reserve_space(xdr, XDR_UNIT * 2 + XDR_UNIT * 2);
3059d0b28aadSChuck Lever if (!p)
3060d0b28aadSChuck Lever return nfserr_resource;
3061b6b62595SChuck Lever
3062b6b62595SChuck Lever if (unlikely(args->exp->ex_fslocs.migrated)) {
3063b6b62595SChuck Lever p = xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MAJOR);
3064b6b62595SChuck Lever xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MINOR);
3065b6b62595SChuck Lever return nfs_ok;
3066b6b62595SChuck Lever }
3067b6b62595SChuck Lever switch (fsid_source(args->fhp)) {
3068b6b62595SChuck Lever case FSIDSOURCE_FSID:
3069b6b62595SChuck Lever p = xdr_encode_hyper(p, (u64)args->exp->ex_fsid);
3070b6b62595SChuck Lever xdr_encode_hyper(p, (u64)0);
3071b6b62595SChuck Lever break;
3072b6b62595SChuck Lever case FSIDSOURCE_DEV:
3073b6b62595SChuck Lever *p++ = xdr_zero;
3074b6b62595SChuck Lever *p++ = cpu_to_be32(MAJOR(args->stat.dev));
3075b6b62595SChuck Lever *p++ = xdr_zero;
3076b6b62595SChuck Lever *p = cpu_to_be32(MINOR(args->stat.dev));
3077b6b62595SChuck Lever break;
3078b6b62595SChuck Lever case FSIDSOURCE_UUID:
3079b6b62595SChuck Lever xdr_encode_opaque_fixed(p, args->exp->ex_uuid, EX_UUID_LEN);
3080b6b62595SChuck Lever break;
3081b6b62595SChuck Lever }
3082b6b62595SChuck Lever
3083b6b62595SChuck Lever return nfs_ok;
3084b6b62595SChuck Lever }
3085b6b62595SChuck Lever
nfsd4_encode_fattr4_lease_time(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3086b6b62595SChuck Lever static __be32 nfsd4_encode_fattr4_lease_time(struct xdr_stream *xdr,
3087b6b62595SChuck Lever const struct nfsd4_fattr_args *args)
3088b6b62595SChuck Lever {
3089b6b62595SChuck Lever struct nfsd_net *nn = net_generic(SVC_NET(args->rqstp), nfsd_net_id);
3090b6b62595SChuck Lever
3091b6b62595SChuck Lever return nfsd4_encode_nfs_lease4(xdr, nn->nfsd4_lease);
3092b6b62595SChuck Lever }
3093b6b62595SChuck Lever
nfsd4_encode_fattr4_rdattr_error(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)30941252b283SChuck Lever static __be32 nfsd4_encode_fattr4_rdattr_error(struct xdr_stream *xdr,
30951252b283SChuck Lever const struct nfsd4_fattr_args *args)
30961252b283SChuck Lever {
30971252b283SChuck Lever return nfsd4_encode_uint32_t(xdr, args->rdattr_err);
30981252b283SChuck Lever }
30991252b283SChuck Lever
nfsd4_encode_fattr4_aclsupport(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)31001252b283SChuck Lever static __be32 nfsd4_encode_fattr4_aclsupport(struct xdr_stream *xdr,
31011252b283SChuck Lever const struct nfsd4_fattr_args *args)
3102782448e1SChuck Lever {
3103782448e1SChuck Lever u32 mask;
3104782448e1SChuck Lever
3105782448e1SChuck Lever mask = 0;
3106782448e1SChuck Lever if (IS_POSIXACL(d_inode(args->dentry)))
3107782448e1SChuck Lever mask = ACL4_SUPPORT_ALLOW_ACL | ACL4_SUPPORT_DENY_ACL;
31086515b7d7SChuck Lever return nfsd4_encode_uint32_t(xdr, mask);
31096515b7d7SChuck Lever }
31106515b7d7SChuck Lever
nfsd4_encode_fattr4_acl(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)31116515b7d7SChuck Lever static __be32 nfsd4_encode_fattr4_acl(struct xdr_stream *xdr,
31126515b7d7SChuck Lever const struct nfsd4_fattr_args *args)
31136515b7d7SChuck Lever {
31146515b7d7SChuck Lever struct nfs4_acl *acl = args->acl;
31156515b7d7SChuck Lever struct nfs4_ace *ace;
31166515b7d7SChuck Lever __be32 status;
31176515b7d7SChuck Lever
31186515b7d7SChuck Lever /* nfsace4<> */
311907455dc4SChuck Lever if (!acl) {
312007455dc4SChuck Lever if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
312107455dc4SChuck Lever return nfserr_resource;
312207455dc4SChuck Lever } else {
312307455dc4SChuck Lever if (xdr_stream_encode_u32(xdr, acl->naces) != XDR_UNIT)
312407455dc4SChuck Lever return nfserr_resource;
312507455dc4SChuck Lever for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
312607455dc4SChuck Lever status = nfsd4_encode_nfsace4(xdr, args->rqstp, ace);
312707455dc4SChuck Lever if (status != nfs_ok)
312807455dc4SChuck Lever return status;
312907455dc4SChuck Lever }
313007455dc4SChuck Lever }
313107455dc4SChuck Lever return nfs_ok;
313207455dc4SChuck Lever }
313307455dc4SChuck Lever
nfsd4_encode_fattr4_filehandle(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)313407455dc4SChuck Lever static __be32 nfsd4_encode_fattr4_filehandle(struct xdr_stream *xdr,
313507455dc4SChuck Lever const struct nfsd4_fattr_args *args)
313607455dc4SChuck Lever {
313707455dc4SChuck Lever return nfsd4_encode_nfs_fh4(xdr, &args->fhp->fh_handle);
313807455dc4SChuck Lever }
313907455dc4SChuck Lever
nfsd4_encode_fattr4_fileid(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)314007455dc4SChuck Lever static __be32 nfsd4_encode_fattr4_fileid(struct xdr_stream *xdr,
314107455dc4SChuck Lever const struct nfsd4_fattr_args *args)
31423283bf64SChuck Lever {
31433283bf64SChuck Lever return nfsd4_encode_uint64_t(xdr, args->stat.ino);
31443283bf64SChuck Lever }
31453283bf64SChuck Lever
nfsd4_encode_fattr4_files_avail(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)31463283bf64SChuck Lever static __be32 nfsd4_encode_fattr4_files_avail(struct xdr_stream *xdr,
31473283bf64SChuck Lever const struct nfsd4_fattr_args *args)
3148eb7ece81SChuck Lever {
3149eb7ece81SChuck Lever return nfsd4_encode_uint64_t(xdr, args->statfs.f_ffree);
3150eb7ece81SChuck Lever }
3151eb7ece81SChuck Lever
nfsd4_encode_fattr4_files_free(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3152eb7ece81SChuck Lever static __be32 nfsd4_encode_fattr4_files_free(struct xdr_stream *xdr,
3153eb7ece81SChuck Lever const struct nfsd4_fattr_args *args)
3154b0c3a5f8SChuck Lever {
3155b0c3a5f8SChuck Lever return nfsd4_encode_uint64_t(xdr, args->statfs.f_ffree);
3156b0c3a5f8SChuck Lever }
3157b0c3a5f8SChuck Lever
nfsd4_encode_fattr4_files_total(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3158b0c3a5f8SChuck Lever static __be32 nfsd4_encode_fattr4_files_total(struct xdr_stream *xdr,
3159b0c3a5f8SChuck Lever const struct nfsd4_fattr_args *args)
316074361e2bSChuck Lever {
316174361e2bSChuck Lever return nfsd4_encode_uint64_t(xdr, args->statfs.f_files);
316274361e2bSChuck Lever }
316374361e2bSChuck Lever
nfsd4_encode_fattr4_fs_locations(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)316474361e2bSChuck Lever static __be32 nfsd4_encode_fattr4_fs_locations(struct xdr_stream *xdr,
316574361e2bSChuck Lever const struct nfsd4_fattr_args *args)
3166b56b7526SChuck Lever {
3167b56b7526SChuck Lever return nfsd4_encode_fs_locations4(xdr, args->rqstp, args->exp);
3168b56b7526SChuck Lever }
3169b56b7526SChuck Lever
nfsd4_encode_fattr4_maxfilesize(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3170b56b7526SChuck Lever static __be32 nfsd4_encode_fattr4_maxfilesize(struct xdr_stream *xdr,
3171b56b7526SChuck Lever const struct nfsd4_fattr_args *args)
3172a1469a37SChuck Lever {
3173a1469a37SChuck Lever struct super_block *sb = args->exp->ex_path.mnt->mnt_sb;
3174a1469a37SChuck Lever
3175a1469a37SChuck Lever return nfsd4_encode_uint64_t(xdr, sb->s_maxbytes);
3176a1469a37SChuck Lever }
3177a1469a37SChuck Lever
nfsd4_encode_fattr4_maxlink(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)31787c605dccSChuck Lever static __be32 nfsd4_encode_fattr4_maxlink(struct xdr_stream *xdr,
31797c605dccSChuck Lever const struct nfsd4_fattr_args *args)
31807c605dccSChuck Lever {
31817c605dccSChuck Lever return nfsd4_encode_uint32_t(xdr, 255);
31827c605dccSChuck Lever }
31837c605dccSChuck Lever
nfsd4_encode_fattr4_maxname(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)31847c605dccSChuck Lever static __be32 nfsd4_encode_fattr4_maxname(struct xdr_stream *xdr,
31857c605dccSChuck Lever const struct nfsd4_fattr_args *args)
3186b066aa5cSChuck Lever {
3187b066aa5cSChuck Lever return nfsd4_encode_uint32_t(xdr, args->statfs.f_namelen);
3188b066aa5cSChuck Lever }
3189b066aa5cSChuck Lever
nfsd4_encode_fattr4_maxread(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3190b066aa5cSChuck Lever static __be32 nfsd4_encode_fattr4_maxread(struct xdr_stream *xdr,
3191b066aa5cSChuck Lever const struct nfsd4_fattr_args *args)
31929c1adaccSChuck Lever {
31939c1adaccSChuck Lever return nfsd4_encode_uint64_t(xdr, svc_max_payload(args->rqstp));
31949c1adaccSChuck Lever }
31959c1adaccSChuck Lever
nfsd4_encode_fattr4_maxwrite(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)31969c1adaccSChuck Lever static __be32 nfsd4_encode_fattr4_maxwrite(struct xdr_stream *xdr,
31979c1adaccSChuck Lever const struct nfsd4_fattr_args *args)
3198c17195c3SChuck Lever {
3199c17195c3SChuck Lever return nfsd4_encode_uint64_t(xdr, svc_max_payload(args->rqstp));
3200c17195c3SChuck Lever }
3201c17195c3SChuck Lever
nfsd4_encode_fattr4_mode(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3202c17195c3SChuck Lever static __be32 nfsd4_encode_fattr4_mode(struct xdr_stream *xdr,
3203c17195c3SChuck Lever const struct nfsd4_fattr_args *args)
3204951378dcSChuck Lever {
3205951378dcSChuck Lever return nfsd4_encode_mode4(xdr, args->stat.mode & S_IALLUGO);
3206951378dcSChuck Lever }
3207951378dcSChuck Lever
nfsd4_encode_fattr4_numlinks(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3208951378dcSChuck Lever static __be32 nfsd4_encode_fattr4_numlinks(struct xdr_stream *xdr,
3209951378dcSChuck Lever const struct nfsd4_fattr_args *args)
3210f4cf5042SChuck Lever {
3211f4cf5042SChuck Lever return nfsd4_encode_uint32_t(xdr, args->stat.nlink);
3212f4cf5042SChuck Lever }
3213f4cf5042SChuck Lever
nfsd4_encode_fattr4_owner(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3214f4cf5042SChuck Lever static __be32 nfsd4_encode_fattr4_owner(struct xdr_stream *xdr,
3215f4cf5042SChuck Lever const struct nfsd4_fattr_args *args)
32169f329feaSChuck Lever {
32179f329feaSChuck Lever return nfsd4_encode_user(xdr, args->rqstp, args->stat.uid);
32189f329feaSChuck Lever }
32199f329feaSChuck Lever
nfsd4_encode_fattr4_owner_group(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)32209f329feaSChuck Lever static __be32 nfsd4_encode_fattr4_owner_group(struct xdr_stream *xdr,
32219f329feaSChuck Lever const struct nfsd4_fattr_args *args)
3222fa51a520SChuck Lever {
3223fa51a520SChuck Lever return nfsd4_encode_group(xdr, args->rqstp, args->stat.gid);
3224fa51a520SChuck Lever }
3225fa51a520SChuck Lever
nfsd4_encode_fattr4_rawdev(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3226fa51a520SChuck Lever static __be32 nfsd4_encode_fattr4_rawdev(struct xdr_stream *xdr,
3227fa51a520SChuck Lever const struct nfsd4_fattr_args *args)
322862f31e56SChuck Lever {
322962f31e56SChuck Lever return nfsd4_encode_specdata4(xdr, MAJOR(args->stat.rdev),
323062f31e56SChuck Lever MINOR(args->stat.rdev));
323162f31e56SChuck Lever }
323262f31e56SChuck Lever
nfsd4_encode_fattr4_space_avail(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)323362f31e56SChuck Lever static __be32 nfsd4_encode_fattr4_space_avail(struct xdr_stream *xdr,
3234a460cda2SChuck Lever const struct nfsd4_fattr_args *args)
3235a460cda2SChuck Lever {
3236a460cda2SChuck Lever u64 avail = (u64)args->statfs.f_bavail * (u64)args->statfs.f_bsize;
3237a460cda2SChuck Lever
3238a460cda2SChuck Lever return nfsd4_encode_uint64_t(xdr, avail);
3239a460cda2SChuck Lever }
3240a460cda2SChuck Lever
nfsd4_encode_fattr4_space_free(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)324183afa091SChuck Lever static __be32 nfsd4_encode_fattr4_space_free(struct xdr_stream *xdr,
324283afa091SChuck Lever const struct nfsd4_fattr_args *args)
324383afa091SChuck Lever {
324483afa091SChuck Lever u64 free = (u64)args->statfs.f_bfree * (u64)args->statfs.f_bsize;
324583afa091SChuck Lever
324683afa091SChuck Lever return nfsd4_encode_uint64_t(xdr, free);
324783afa091SChuck Lever }
324883afa091SChuck Lever
nfsd4_encode_fattr4_space_total(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)324974ebc697SChuck Lever static __be32 nfsd4_encode_fattr4_space_total(struct xdr_stream *xdr,
325074ebc697SChuck Lever const struct nfsd4_fattr_args *args)
325174ebc697SChuck Lever {
325274ebc697SChuck Lever u64 total = (u64)args->statfs.f_blocks * (u64)args->statfs.f_bsize;
325374ebc697SChuck Lever
325474ebc697SChuck Lever return nfsd4_encode_uint64_t(xdr, total);
325574ebc697SChuck Lever }
325674ebc697SChuck Lever
nfsd4_encode_fattr4_space_used(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3257d0cde979SChuck Lever static __be32 nfsd4_encode_fattr4_space_used(struct xdr_stream *xdr,
3258d0cde979SChuck Lever const struct nfsd4_fattr_args *args)
3259d0cde979SChuck Lever {
3260d0cde979SChuck Lever return nfsd4_encode_uint64_t(xdr, (u64)args->stat.blocks << 9);
3261d0cde979SChuck Lever }
3262d0cde979SChuck Lever
nfsd4_encode_fattr4_time_access(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3263d0cde979SChuck Lever static __be32 nfsd4_encode_fattr4_time_access(struct xdr_stream *xdr,
3264d0cde979SChuck Lever const struct nfsd4_fattr_args *args)
32656d37ac3aSChuck Lever {
32666d37ac3aSChuck Lever return nfsd4_encode_nfstime4(xdr, &args->stat.atime);
32676d37ac3aSChuck Lever }
32686d37ac3aSChuck Lever
nfsd4_encode_fattr4_time_create(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)32696d37ac3aSChuck Lever static __be32 nfsd4_encode_fattr4_time_create(struct xdr_stream *xdr,
32706d37ac3aSChuck Lever const struct nfsd4_fattr_args *args)
3271eed4d1adSChuck Lever {
3272eed4d1adSChuck Lever return nfsd4_encode_nfstime4(xdr, &args->stat.btime);
3273eed4d1adSChuck Lever }
3274eed4d1adSChuck Lever
3275eed4d1adSChuck Lever /*
3276eed4d1adSChuck Lever * ctime (in NFSv4, time_metadata) is not writeable, and the client
32772e38722dSChuck Lever * doesn't really care what resolution could theoretically be stored by
32782e38722dSChuck Lever * the filesystem.
32792e38722dSChuck Lever *
32802e38722dSChuck Lever * The client cares how close together changes can be while still
32812e38722dSChuck Lever * guaranteeing ctime changes. For most filesystems (which have
32822e38722dSChuck Lever * timestamps with nanosecond fields) that is limited by the resolution
32831da177e4SLinus Torvalds * of the time returned from current_time() (which I'm assuming to be
3284993474e8SChuck Lever * 1/HZ).
3285993474e8SChuck Lever */
nfsd4_encode_fattr4_time_delta(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3286993474e8SChuck Lever static __be32 nfsd4_encode_fattr4_time_delta(struct xdr_stream *xdr,
3287993474e8SChuck Lever const struct nfsd4_fattr_args *args)
3288993474e8SChuck Lever {
3289993474e8SChuck Lever const struct inode *inode = d_inode(args->dentry);
3290993474e8SChuck Lever u32 ns = max_t(u32, NSEC_PER_SEC/HZ, inode->i_sb->s_time_gran);
3291993474e8SChuck Lever struct timespec64 ts = ns_to_timespec64(ns);
3292993474e8SChuck Lever
3293993474e8SChuck Lever return nfsd4_encode_nfstime4(xdr, &ts);
3294993474e8SChuck Lever }
3295993474e8SChuck Lever
nfsd4_encode_fattr4_time_metadata(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3296993474e8SChuck Lever static __be32 nfsd4_encode_fattr4_time_metadata(struct xdr_stream *xdr,
3297993474e8SChuck Lever const struct nfsd4_fattr_args *args)
3298993474e8SChuck Lever {
3299993474e8SChuck Lever return nfsd4_encode_nfstime4(xdr, &args->stat.ctime);
3300993474e8SChuck Lever }
3301993474e8SChuck Lever
nfsd4_encode_fattr4_time_modify(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3302993474e8SChuck Lever static __be32 nfsd4_encode_fattr4_time_modify(struct xdr_stream *xdr,
3303993474e8SChuck Lever const struct nfsd4_fattr_args *args)
3304673720bcSChuck Lever {
3305673720bcSChuck Lever return nfsd4_encode_nfstime4(xdr, &args->stat.mtime);
3306673720bcSChuck Lever }
3307673720bcSChuck Lever
nfsd4_encode_fattr4_mounted_on_fileid(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3308673720bcSChuck Lever static __be32 nfsd4_encode_fattr4_mounted_on_fileid(struct xdr_stream *xdr,
3309673720bcSChuck Lever const struct nfsd4_fattr_args *args)
3310d1828611SChuck Lever {
3311d1828611SChuck Lever u64 ino;
3312d1828611SChuck Lever int err;
3313d1828611SChuck Lever
3314d1828611SChuck Lever if (!args->ignore_crossmnt &&
3315d1828611SChuck Lever args->dentry == args->exp->ex_path.mnt->mnt_root) {
33161b9097e3SChuck Lever err = nfsd4_get_mounted_on_ino(args->exp, &ino);
33171b9097e3SChuck Lever if (err)
33181b9097e3SChuck Lever return nfserrno(err);
33191b9097e3SChuck Lever } else
33201b9097e3SChuck Lever ino = args->stat.ino;
33211b9097e3SChuck Lever
33221b9097e3SChuck Lever return nfsd4_encode_uint64_t(xdr, ino);
33231b9097e3SChuck Lever }
33241b9097e3SChuck Lever
33251b9097e3SChuck Lever #ifdef CONFIG_NFSD_PNFS
33261b9097e3SChuck Lever
nfsd4_encode_fattr4_fs_layout_types(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)33271b9097e3SChuck Lever static __be32 nfsd4_encode_fattr4_fs_layout_types(struct xdr_stream *xdr,
33281b9097e3SChuck Lever const struct nfsd4_fattr_args *args)
33291b9097e3SChuck Lever {
33301b9097e3SChuck Lever unsigned long mask = args->exp->ex_layout_types;
33311b9097e3SChuck Lever int i;
33321b9097e3SChuck Lever
3333e7a5b1b2SChuck Lever /* Hamming weight of @mask is the number of layout types to return */
3334e7a5b1b2SChuck Lever if (xdr_stream_encode_u32(xdr, hweight_long(mask)) != XDR_UNIT)
3335e7a5b1b2SChuck Lever return nfserr_resource;
3336e7a5b1b2SChuck Lever for (i = LAYOUT_NFSV4_1_FILES; i < LAYOUT_TYPE_MAX; ++i)
3337e7a5b1b2SChuck Lever if (mask & BIT(i)) {
3338e7a5b1b2SChuck Lever /* layouttype4 */
3339e7a5b1b2SChuck Lever if (xdr_stream_encode_u32(xdr, i) != XDR_UNIT)
3340e7a5b1b2SChuck Lever return nfserr_resource;
3341e7a5b1b2SChuck Lever }
3342e7a5b1b2SChuck Lever return nfs_ok;
3343e7a5b1b2SChuck Lever }
3344e7a5b1b2SChuck Lever
nfsd4_encode_fattr4_layout_types(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3345e7a5b1b2SChuck Lever static __be32 nfsd4_encode_fattr4_layout_types(struct xdr_stream *xdr,
3346e7a5b1b2SChuck Lever const struct nfsd4_fattr_args *args)
3347e7a5b1b2SChuck Lever {
3348e7a5b1b2SChuck Lever unsigned long mask = args->exp->ex_layout_types;
3349e7a5b1b2SChuck Lever int i;
3350e7a5b1b2SChuck Lever
3351e7a5b1b2SChuck Lever /* Hamming weight of @mask is the number of layout types to return */
3352e7a5b1b2SChuck Lever if (xdr_stream_encode_u32(xdr, hweight_long(mask)) != XDR_UNIT)
33534c15878eSChuck Lever return nfserr_resource;
33544c15878eSChuck Lever for (i = LAYOUT_NFSV4_1_FILES; i < LAYOUT_TYPE_MAX; ++i)
33554c15878eSChuck Lever if (mask & BIT(i)) {
33564c15878eSChuck Lever /* layouttype4 */
33574c15878eSChuck Lever if (xdr_stream_encode_u32(xdr, i) != XDR_UNIT)
33584c15878eSChuck Lever return nfserr_resource;
33594c15878eSChuck Lever }
33604c15878eSChuck Lever return nfs_ok;
33614c15878eSChuck Lever }
33624c15878eSChuck Lever
nfsd4_encode_fattr4_layout_blksize(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)33634c15878eSChuck Lever static __be32 nfsd4_encode_fattr4_layout_blksize(struct xdr_stream *xdr,
33644c15878eSChuck Lever const struct nfsd4_fattr_args *args)
33654c15878eSChuck Lever {
33664c15878eSChuck Lever return nfsd4_encode_uint32_t(xdr, args->stat.blksize);
33674c15878eSChuck Lever }
33684c15878eSChuck Lever
33694c15878eSChuck Lever #endif
33704c15878eSChuck Lever
nfsd4_encode_fattr4_suppattr_exclcreat(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)33714c584731SChuck Lever static __be32 nfsd4_encode_fattr4_suppattr_exclcreat(struct xdr_stream *xdr,
33724c584731SChuck Lever const struct nfsd4_fattr_args *args)
33734c584731SChuck Lever {
33744c584731SChuck Lever struct nfsd4_compoundres *resp = args->rqstp->rq_resp;
33754c584731SChuck Lever u32 supp[3];
33764c584731SChuck Lever
3377e7a5b1b2SChuck Lever memcpy(supp, nfsd_suppattrs[resp->cstate.minorversion], sizeof(supp));
3378e7a5b1b2SChuck Lever supp[0] &= NFSD_SUPPATTR_EXCLCREAT_WORD0;
3379345c3877SChuck Lever supp[1] &= NFSD_SUPPATTR_EXCLCREAT_WORD1;
3380345c3877SChuck Lever supp[2] &= NFSD_SUPPATTR_EXCLCREAT_WORD2;
3381345c3877SChuck Lever
3382345c3877SChuck Lever return nfsd4_encode_bitmap4(xdr, supp[0], supp[1], supp[2]);
3383345c3877SChuck Lever }
3384345c3877SChuck Lever
3385345c3877SChuck Lever #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
nfsd4_encode_fattr4_sec_label(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3386345c3877SChuck Lever static __be32 nfsd4_encode_fattr4_sec_label(struct xdr_stream *xdr,
3387345c3877SChuck Lever const struct nfsd4_fattr_args *args)
3388345c3877SChuck Lever {
3389345c3877SChuck Lever return nfsd4_encode_security_label(xdr, args->rqstp,
3390345c3877SChuck Lever args->context, args->contextlen);
3391345c3877SChuck Lever }
3392345c3877SChuck Lever #endif
3393f59388a5SChuck Lever
nfsd4_encode_fattr4_xattr_support(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3394f59388a5SChuck Lever static __be32 nfsd4_encode_fattr4_xattr_support(struct xdr_stream *xdr,
3395f59388a5SChuck Lever const struct nfsd4_fattr_args *args)
3396f59388a5SChuck Lever {
3397f59388a5SChuck Lever int err = xattr_supports_user_prefix(d_inode(args->dentry));
3398f59388a5SChuck Lever
3399f59388a5SChuck Lever return nfsd4_encode_bool(xdr, err == 0);
3400f59388a5SChuck Lever }
3401f59388a5SChuck Lever
3402b3dbf4e4SChuck Lever static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
3403b3dbf4e4SChuck Lever [FATTR4_SUPPORTED_ATTRS] = nfsd4_encode_fattr4_supported_attrs,
3404b3dbf4e4SChuck Lever [FATTR4_TYPE] = nfsd4_encode_fattr4_type,
3405b3dbf4e4SChuck Lever [FATTR4_FH_EXPIRE_TYPE] = nfsd4_encode_fattr4_fh_expire_type,
3406b3dbf4e4SChuck Lever [FATTR4_CHANGE] = nfsd4_encode_fattr4_change,
3407b3dbf4e4SChuck Lever [FATTR4_SIZE] = nfsd4_encode_fattr4_size,
3408b3dbf4e4SChuck Lever [FATTR4_LINK_SUPPORT] = nfsd4_encode_fattr4__true,
3409b3dbf4e4SChuck Lever [FATTR4_SYMLINK_SUPPORT] = nfsd4_encode_fattr4__true,
3410fce7913bSChuck Lever [FATTR4_NAMED_ATTR] = nfsd4_encode_fattr4__false,
3411fce7913bSChuck Lever [FATTR4_FSID] = nfsd4_encode_fattr4_fsid,
3412fce7913bSChuck Lever [FATTR4_UNIQUE_HANDLES] = nfsd4_encode_fattr4__true,
3413fce7913bSChuck Lever [FATTR4_LEASE_TIME] = nfsd4_encode_fattr4_lease_time,
3414fce7913bSChuck Lever [FATTR4_RDATTR_ERROR] = nfsd4_encode_fattr4_rdattr_error,
3415fce7913bSChuck Lever [FATTR4_ACL] = nfsd4_encode_fattr4_acl,
3416fce7913bSChuck Lever [FATTR4_ACLSUPPORT] = nfsd4_encode_fattr4_aclsupport,
3417fce7913bSChuck Lever [FATTR4_ARCHIVE] = nfsd4_encode_fattr4__noop,
3418fce7913bSChuck Lever [FATTR4_CANSETTIME] = nfsd4_encode_fattr4__true,
3419fce7913bSChuck Lever [FATTR4_CASE_INSENSITIVE] = nfsd4_encode_fattr4__false,
3420fce7913bSChuck Lever [FATTR4_CASE_PRESERVING] = nfsd4_encode_fattr4__true,
3421fce7913bSChuck Lever [FATTR4_CHOWN_RESTRICTED] = nfsd4_encode_fattr4__true,
3422fce7913bSChuck Lever [FATTR4_FILEHANDLE] = nfsd4_encode_fattr4_filehandle,
3423fce7913bSChuck Lever [FATTR4_FILEID] = nfsd4_encode_fattr4_fileid,
3424fce7913bSChuck Lever [FATTR4_FILES_AVAIL] = nfsd4_encode_fattr4_files_avail,
3425fce7913bSChuck Lever [FATTR4_FILES_FREE] = nfsd4_encode_fattr4_files_free,
3426fce7913bSChuck Lever [FATTR4_FILES_TOTAL] = nfsd4_encode_fattr4_files_total,
3427fce7913bSChuck Lever [FATTR4_FS_LOCATIONS] = nfsd4_encode_fattr4_fs_locations,
3428fce7913bSChuck Lever [FATTR4_HIDDEN] = nfsd4_encode_fattr4__noop,
3429fce7913bSChuck Lever [FATTR4_HOMOGENEOUS] = nfsd4_encode_fattr4__true,
3430fce7913bSChuck Lever [FATTR4_MAXFILESIZE] = nfsd4_encode_fattr4_maxfilesize,
3431fce7913bSChuck Lever [FATTR4_MAXLINK] = nfsd4_encode_fattr4_maxlink,
3432fce7913bSChuck Lever [FATTR4_MAXNAME] = nfsd4_encode_fattr4_maxname,
3433fce7913bSChuck Lever [FATTR4_MAXREAD] = nfsd4_encode_fattr4_maxread,
3434fce7913bSChuck Lever [FATTR4_MAXWRITE] = nfsd4_encode_fattr4_maxwrite,
3435fce7913bSChuck Lever [FATTR4_MIMETYPE] = nfsd4_encode_fattr4__noop,
3436fce7913bSChuck Lever [FATTR4_MODE] = nfsd4_encode_fattr4_mode,
3437fce7913bSChuck Lever [FATTR4_NO_TRUNC] = nfsd4_encode_fattr4__true,
3438fce7913bSChuck Lever [FATTR4_NUMLINKS] = nfsd4_encode_fattr4_numlinks,
3439fce7913bSChuck Lever [FATTR4_OWNER] = nfsd4_encode_fattr4_owner,
3440fce7913bSChuck Lever [FATTR4_OWNER_GROUP] = nfsd4_encode_fattr4_owner_group,
3441fce7913bSChuck Lever [FATTR4_QUOTA_AVAIL_HARD] = nfsd4_encode_fattr4__noop,
3442fce7913bSChuck Lever [FATTR4_QUOTA_AVAIL_SOFT] = nfsd4_encode_fattr4__noop,
3443fce7913bSChuck Lever [FATTR4_QUOTA_USED] = nfsd4_encode_fattr4__noop,
3444fce7913bSChuck Lever [FATTR4_RAWDEV] = nfsd4_encode_fattr4_rawdev,
3445fce7913bSChuck Lever [FATTR4_SPACE_AVAIL] = nfsd4_encode_fattr4_space_avail,
3446fce7913bSChuck Lever [FATTR4_SPACE_FREE] = nfsd4_encode_fattr4_space_free,
3447fce7913bSChuck Lever [FATTR4_SPACE_TOTAL] = nfsd4_encode_fattr4_space_total,
3448fce7913bSChuck Lever [FATTR4_SPACE_USED] = nfsd4_encode_fattr4_space_used,
3449fce7913bSChuck Lever [FATTR4_SYSTEM] = nfsd4_encode_fattr4__noop,
3450fce7913bSChuck Lever [FATTR4_TIME_ACCESS] = nfsd4_encode_fattr4_time_access,
3451fce7913bSChuck Lever [FATTR4_TIME_ACCESS_SET] = nfsd4_encode_fattr4__noop,
3452fce7913bSChuck Lever [FATTR4_TIME_BACKUP] = nfsd4_encode_fattr4__noop,
3453fce7913bSChuck Lever [FATTR4_TIME_CREATE] = nfsd4_encode_fattr4_time_create,
3454fce7913bSChuck Lever [FATTR4_TIME_DELTA] = nfsd4_encode_fattr4_time_delta,
3455fce7913bSChuck Lever [FATTR4_TIME_METADATA] = nfsd4_encode_fattr4_time_metadata,
3456fce7913bSChuck Lever [FATTR4_TIME_MODIFY] = nfsd4_encode_fattr4_time_modify,
3457fce7913bSChuck Lever [FATTR4_TIME_MODIFY_SET] = nfsd4_encode_fattr4__noop,
3458fce7913bSChuck Lever [FATTR4_MOUNTED_ON_FILEID] = nfsd4_encode_fattr4_mounted_on_fileid,
3459fce7913bSChuck Lever [FATTR4_DIR_NOTIF_DELAY] = nfsd4_encode_fattr4__noop,
3460fce7913bSChuck Lever [FATTR4_DIRENT_NOTIF_DELAY] = nfsd4_encode_fattr4__noop,
3461fce7913bSChuck Lever [FATTR4_DACL] = nfsd4_encode_fattr4__noop,
3462fce7913bSChuck Lever [FATTR4_SACL] = nfsd4_encode_fattr4__noop,
3463fce7913bSChuck Lever [FATTR4_CHANGE_POLICY] = nfsd4_encode_fattr4__noop,
3464fce7913bSChuck Lever [FATTR4_FS_STATUS] = nfsd4_encode_fattr4__noop,
3465fce7913bSChuck Lever
3466fce7913bSChuck Lever #ifdef CONFIG_NFSD_PNFS
3467fce7913bSChuck Lever [FATTR4_FS_LAYOUT_TYPES] = nfsd4_encode_fattr4_fs_layout_types,
3468fce7913bSChuck Lever [FATTR4_LAYOUT_HINT] = nfsd4_encode_fattr4__noop,
3469fce7913bSChuck Lever [FATTR4_LAYOUT_TYPES] = nfsd4_encode_fattr4_layout_types,
3470fce7913bSChuck Lever [FATTR4_LAYOUT_BLKSIZE] = nfsd4_encode_fattr4_layout_blksize,
3471fce7913bSChuck Lever [FATTR4_LAYOUT_ALIGNMENT] = nfsd4_encode_fattr4__noop,
3472fce7913bSChuck Lever #else
3473fce7913bSChuck Lever [FATTR4_FS_LAYOUT_TYPES] = nfsd4_encode_fattr4__noop,
3474fce7913bSChuck Lever [FATTR4_LAYOUT_HINT] = nfsd4_encode_fattr4__noop,
3475fce7913bSChuck Lever [FATTR4_LAYOUT_TYPES] = nfsd4_encode_fattr4__noop,
3476fce7913bSChuck Lever [FATTR4_LAYOUT_BLKSIZE] = nfsd4_encode_fattr4__noop,
3477fce7913bSChuck Lever [FATTR4_LAYOUT_ALIGNMENT] = nfsd4_encode_fattr4__noop,
3478fce7913bSChuck Lever #endif
3479fce7913bSChuck Lever
3480fce7913bSChuck Lever [FATTR4_FS_LOCATIONS_INFO] = nfsd4_encode_fattr4__noop,
3481fce7913bSChuck Lever [FATTR4_MDSTHRESHOLD] = nfsd4_encode_fattr4__noop,
3482fce7913bSChuck Lever [FATTR4_RETENTION_GET] = nfsd4_encode_fattr4__noop,
3483fce7913bSChuck Lever [FATTR4_RETENTION_SET] = nfsd4_encode_fattr4__noop,
3484fce7913bSChuck Lever [FATTR4_RETENTEVT_GET] = nfsd4_encode_fattr4__noop,
3485fce7913bSChuck Lever [FATTR4_RETENTEVT_SET] = nfsd4_encode_fattr4__noop,
3486fce7913bSChuck Lever [FATTR4_RETENTION_HOLD] = nfsd4_encode_fattr4__noop,
3487fce7913bSChuck Lever [FATTR4_MODE_SET_MASKED] = nfsd4_encode_fattr4__noop,
3488fce7913bSChuck Lever [FATTR4_SUPPATTR_EXCLCREAT] = nfsd4_encode_fattr4_suppattr_exclcreat,
3489fce7913bSChuck Lever [FATTR4_FS_CHARSET_CAP] = nfsd4_encode_fattr4__noop,
3490fce7913bSChuck Lever [FATTR4_CLONE_BLKSIZE] = nfsd4_encode_fattr4__noop,
3491fce7913bSChuck Lever [FATTR4_SPACE_FREED] = nfsd4_encode_fattr4__noop,
3492fce7913bSChuck Lever [FATTR4_CHANGE_ATTR_TYPE] = nfsd4_encode_fattr4__noop,
3493fce7913bSChuck Lever
3494fce7913bSChuck Lever #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
3495fce7913bSChuck Lever [FATTR4_SEC_LABEL] = nfsd4_encode_fattr4_sec_label,
3496fce7913bSChuck Lever #else
3497fce7913bSChuck Lever [FATTR4_SEC_LABEL] = nfsd4_encode_fattr4__noop,
3498fce7913bSChuck Lever #endif
3499fce7913bSChuck Lever
3500fce7913bSChuck Lever [FATTR4_MODE_UMASK] = nfsd4_encode_fattr4__noop,
3501fce7913bSChuck Lever [FATTR4_XATTR_SUPPORT] = nfsd4_encode_fattr4_xattr_support,
3502fce7913bSChuck Lever };
3503fce7913bSChuck Lever
3504fce7913bSChuck Lever /*
3505fce7913bSChuck Lever * Note: @fhp can be NULL; in this case, we might have to compose the filehandle
3506fce7913bSChuck Lever * ourselves.
3507fce7913bSChuck Lever */
3508fce7913bSChuck Lever static __be32
nfsd4_encode_fattr4(struct svc_rqst * rqstp,struct xdr_stream * xdr,struct svc_fh * fhp,struct svc_export * exp,struct dentry * dentry,const u32 * bmval,int ignore_crossmnt)3509fce7913bSChuck Lever nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
3510fce7913bSChuck Lever struct svc_fh *fhp, struct svc_export *exp,
3511fce7913bSChuck Lever struct dentry *dentry, const u32 *bmval,
3512993474e8SChuck Lever int ignore_crossmnt)
35131da177e4SLinus Torvalds {
35141da177e4SLinus Torvalds DECLARE_BITMAP(attr_bitmap, ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
35151da177e4SLinus Torvalds struct nfsd4_fattr_args args;
3516da2ebce6SJeff Layton struct svc_fh *tempfh = NULL;
3517ae1131d4SChuck Lever int starting_len = xdr->buf->len;
3518ae1131d4SChuck Lever __be32 *attrlen_p, status;
3519fce7913bSChuck Lever int attrlen_offset;
3520ae1131d4SChuck Lever u32 attrmask[3];
35211da177e4SLinus Torvalds int err;
3522f488138bSVasily Gorbik struct nfsd4_compoundres *resp = rqstp->rq_resp;
352383ab8678SChuck Lever u32 minorversion = resp->cstate.minorversion;
3524d50e6136SJ. Bruce Fields struct path path = {
35251fcea5b2SJ. Bruce Fields .mnt = exp->ex_path.mnt,
3526b3dbf4e4SChuck Lever .dentry = dentry,
3527082d4bd7SJ. Bruce Fields };
3528f488138bSVasily Gorbik unsigned long bit;
3529b8dd7b9aSAl Viro bool file_modified = false;
35307e705706SAndy Adamson u64 size = 0;
35317e705706SAndy Adamson
3532ebabe9a9SChristoph Hellwig WARN_ON_ONCE(bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1);
3533ebabe9a9SChristoph Hellwig WARN_ON_ONCE(!nfsd_attrs_supported(minorversion, bmval));
3534ebabe9a9SChristoph Hellwig
3535ebabe9a9SChristoph Hellwig args.rqstp = rqstp;
3536fce7913bSChuck Lever args.exp = exp;
3537c5967721SDai Ngo args.dentry = dentry;
3538c5967721SDai Ngo args.ignore_crossmnt = (ignore_crossmnt != 0);
35391da177e4SLinus Torvalds args.acl = NULL;
3540fce7913bSChuck Lever #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
3541fce7913bSChuck Lever args.context = NULL;
35421da177e4SLinus Torvalds #endif
3543c9090e27SChuck Lever
354436ed7e64SChuck Lever /*
3545c9090e27SChuck Lever * Make a local copy of the attribute bitmap that can be modified.
35461b9097e3SChuck Lever */
354718180a45SChuck Lever attrmask[0] = bmval[0];
3548f58bab6fSJeff Layton attrmask[1] = bmval[1];
3549f58bab6fSJeff Layton attrmask[2] = bmval[2];
3550f58bab6fSJeff Layton
3551c9090e27SChuck Lever args.rdattr_err = 0;
3552fce7913bSChuck Lever if (exp->ex_fslocs.migrated) {
3553fce7913bSChuck Lever status = fattr_handle_absent_fs(&attrmask[0], &attrmask[1],
3554fce7913bSChuck Lever &attrmask[2], &args.rdattr_err);
3555f488138bSVasily Gorbik if (status)
3556f488138bSVasily Gorbik goto out;
3557f488138bSVasily Gorbik }
3558fce7913bSChuck Lever args.size = 0;
355983ab8678SChuck Lever if (attrmask[0] & (FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE)) {
356042ca0993SJ.Bruce Fields status = nfsd4_deleg_getattr_conflict(rqstp, dentry,
3561f488138bSVasily Gorbik &file_modified, &size);
3562f488138bSVasily Gorbik if (status)
356342ca0993SJ.Bruce Fields goto out;
356442ca0993SJ.Bruce Fields }
356542ca0993SJ.Bruce Fields
356683ab8678SChuck Lever err = vfs_getattr(&path, &args.stat,
3567f488138bSVasily Gorbik STATX_BASIC_STATS | STATX_BTIME | STATX_CHANGE_COOKIE,
3568*7e8ae848SJeff Layton AT_STATX_SYNC_AS_STAT);
3569c5967721SDai Ngo if (err)
3570fd19ca36SDai Ngo goto out_nfserr;
3571fd19ca36SDai Ngo if (file_modified)
3572fd19ca36SDai Ngo args.size = size;
357342ca0993SJ.Bruce Fields else
357483ab8678SChuck Lever args.size = args.stat.size;
3575638e3e7dSJeff Layton
3576638e3e7dSJeff Layton if (!(args.stat.result_mask & STATX_BTIME))
3577b8dd7b9aSAl Viro /* underlying FS does not offer btime so we can't share it */
35781da177e4SLinus Torvalds attrmask[1] &= ~FATTR4_WORD1_TIME_CREATE;
3579c5967721SDai Ngo if ((attrmask[0] & (FATTR4_WORD0_FILES_AVAIL | FATTR4_WORD0_FILES_FREE |
3580c5967721SDai Ngo FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_MAXNAME)) ||
3581c5967721SDai Ngo (attrmask[1] & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
3582862bee84SChuck Lever FATTR4_WORD1_SPACE_TOTAL))) {
358383ab8678SChuck Lever err = vfs_statfs(&path, &args.statfs);
358483ab8678SChuck Lever if (err)
3585e377a3e6SOndrej Valousek goto out_nfserr;
3586f488138bSVasily Gorbik }
3587f488138bSVasily Gorbik if ((attrmask[0] & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) &&
358812337901SChristoph Hellwig !fhp) {
3589f488138bSVasily Gorbik tempfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL);
35901da177e4SLinus Torvalds status = nfserr_jukebox;
359183ab8678SChuck Lever if (!tempfh)
3592b8dd7b9aSAl Viro goto out;
35931da177e4SLinus Torvalds fh_init(tempfh, NFS4_FHSIZE);
35941da177e4SLinus Torvalds status = fh_compose(tempfh, exp, dentry, NULL);
3595f488138bSVasily Gorbik if (status)
3596fce7913bSChuck Lever goto out;
3597d50e6136SJ. Bruce Fields args.fhp = tempfh;
3598d50e6136SJ. Bruce Fields } else
3599d50e6136SJ. Bruce Fields args.fhp = fhp;
3600d50e6136SJ. Bruce Fields
3601d50e6136SJ. Bruce Fields if (attrmask[0] & FATTR4_WORD0_ACL) {
3602d50e6136SJ. Bruce Fields err = nfsd4_get_nfs4_acl(rqstp, dentry, &args.acl);
36031da177e4SLinus Torvalds if (err == -EOPNOTSUPP)
36041da177e4SLinus Torvalds attrmask[0] &= ~FATTR4_WORD0_ACL;
360583ab8678SChuck Lever else if (err == -EINVAL) {
360683ab8678SChuck Lever status = nfserr_attrnotsupp;
360783ab8678SChuck Lever goto out;
360883ab8678SChuck Lever } else if (err != 0)
3609f488138bSVasily Gorbik goto out_nfserr;
361083ab8678SChuck Lever }
3611b8dd7b9aSAl Viro
3612f488138bSVasily Gorbik args.contextsupport = false;
3613b8dd7b9aSAl Viro
36141da177e4SLinus Torvalds #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
36151da177e4SLinus Torvalds if ((attrmask[2] & FATTR4_WORD2_SECURITY_LABEL) ||
3616b8dd7b9aSAl Viro attrmask[0] & FATTR4_WORD0_SUPPORTED_ATTRS) {
36171da177e4SLinus Torvalds if (exp->ex_flags & NFSEXP_SECURITY_LABEL)
36181da177e4SLinus Torvalds err = security_inode_getsecctx(d_inode(dentry),
36192b44f1baSBenny Halevy &args.context, &args.contextlen);
362083ab8678SChuck Lever else
362183ab8678SChuck Lever err = -EOPNOTSUPP;
362218032ca0SDavid Quigley args.contextsupport = (err == 0);
3623f488138bSVasily Gorbik if (attrmask[2] & FATTR4_WORD2_SECURITY_LABEL) {
3624f488138bSVasily Gorbik if (err == -EOPNOTSUPP)
362532ddd944SJ. Bruce Fields attrmask[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
36262b0143b5SDavid Howells else if (err)
3627f59388a5SChuck Lever goto out_nfserr;
362832ddd944SJ. Bruce Fields }
362932ddd944SJ. Bruce Fields }
363083ab8678SChuck Lever #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
3631f488138bSVasily Gorbik
363218032ca0SDavid Quigley /* attrmask */
3633f488138bSVasily Gorbik status = nfsd4_encode_bitmap4(xdr, attrmask[0], attrmask[1],
363418032ca0SDavid Quigley attrmask[2]);
363518032ca0SDavid Quigley if (status)
363618032ca0SDavid Quigley goto out;
363718032ca0SDavid Quigley
363818032ca0SDavid Quigley /* attr_vals */
363918032ca0SDavid Quigley attrlen_offset = xdr->buf->len;
3640ae1131d4SChuck Lever attrlen_p = xdr_reserve_space(xdr, XDR_UNIT);
3641f488138bSVasily Gorbik if (!attrlen_p)
3642f488138bSVasily Gorbik goto out_resource;
364375976de6SKinglong Mee bitmap_from_arr32(attr_bitmap, attrmask,
364475976de6SKinglong Mee ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
3645082d4bd7SJ. Bruce Fields for_each_set_bit(bit, attr_bitmap,
3646ae1131d4SChuck Lever ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops)) {
3647082d4bd7SJ. Bruce Fields status = nfsd4_enc_fattr4_encode_ops[bit](xdr, &args);
3648ab04de60SChuck Lever if (status != nfs_ok)
3649ab04de60SChuck Lever goto out;
3650ddd1ea56SJ. Bruce Fields }
3651f488138bSVasily Gorbik *attrlen_p = cpu_to_be32(xdr->buf->len - attrlen_offset - XDR_UNIT);
3652f488138bSVasily Gorbik status = nfs_ok;
3653f488138bSVasily Gorbik
3654fce7913bSChuck Lever out:
3655fce7913bSChuck Lever #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
3656c9090e27SChuck Lever if (args.context)
3657c9090e27SChuck Lever security_release_secctx(args.context, args.contextlen);
36581da177e4SLinus Torvalds #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
3659ab04de60SChuck Lever kfree(args.acl);
36601da177e4SLinus Torvalds if (tempfh) {
36611da177e4SLinus Torvalds fh_put(tempfh);
36621da177e4SLinus Torvalds kfree(tempfh);
3663ba4e55bbSJ. Bruce Fields }
3664f59388a5SChuck Lever if (status)
3665f59388a5SChuck Lever xdr_truncate_encode(xdr, starting_len);
3666ba4e55bbSJ. Bruce Fields return status;
366783ab8678SChuck Lever out_nfserr:
366818df11d0SYan, Zheng status = nfserrno(err);
3669d50e6136SJ. Bruce Fields goto out;
367018df11d0SYan, Zheng out_resource:
367118df11d0SYan, Zheng status = nfserr_resource;
36721fcea5b2SJ. Bruce Fields goto out;
36731fcea5b2SJ. Bruce Fields }
36741da177e4SLinus Torvalds
svcxdr_init_encode_from_buffer(struct xdr_stream * xdr,struct xdr_buf * buf,__be32 * p,int bytes)36751da177e4SLinus Torvalds static void svcxdr_init_encode_from_buffer(struct xdr_stream *xdr,
3676b8dd7b9aSAl Viro struct xdr_buf *buf, __be32 *p, int bytes)
36771da177e4SLinus Torvalds {
36781da177e4SLinus Torvalds xdr->scratch.iov_len = 0;
36791da177e4SLinus Torvalds memset(buf, 0, sizeof(struct xdr_buf));
36801da177e4SLinus Torvalds buf->head[0].iov_base = p;
36811da177e4SLinus Torvalds buf->head[0].iov_len = 0;
36821da177e4SLinus Torvalds buf->len = 0;
36832825a7f9SJ. Bruce Fields xdr->buf = buf;
36842825a7f9SJ. Bruce Fields xdr->iov = buf->head;
36852825a7f9SJ. Bruce Fields xdr->p = p;
36862825a7f9SJ. Bruce Fields xdr->end = (void *)p + bytes;
36872825a7f9SJ. Bruce Fields buf->buflen = bytes;
36882825a7f9SJ. Bruce Fields }
36892825a7f9SJ. Bruce Fields
nfsd4_encode_fattr_to_buf(__be32 ** p,int words,struct svc_fh * fhp,struct svc_export * exp,struct dentry * dentry,u32 * bmval,struct svc_rqst * rqstp,int ignore_crossmnt)36902825a7f9SJ. Bruce Fields __be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
36912825a7f9SJ. Bruce Fields struct svc_fh *fhp, struct svc_export *exp,
36922825a7f9SJ. Bruce Fields struct dentry *dentry, u32 *bmval,
36932825a7f9SJ. Bruce Fields struct svc_rqst *rqstp, int ignore_crossmnt)
36942825a7f9SJ. Bruce Fields {
36952825a7f9SJ. Bruce Fields struct xdr_buf dummy;
36962825a7f9SJ. Bruce Fields struct xdr_stream xdr;
36972825a7f9SJ. Bruce Fields __be32 ret;
3698d5184658SJ. Bruce Fields
3699d5184658SJ. Bruce Fields svcxdr_init_encode_from_buffer(&xdr, &dummy, *p, words << 2);
3700d5184658SJ. Bruce Fields ret = nfsd4_encode_fattr4(rqstp, &xdr, fhp, exp, dentry, bmval,
3701d5184658SJ. Bruce Fields ignore_crossmnt);
3702d5184658SJ. Bruce Fields *p = xdr.p;
37032825a7f9SJ. Bruce Fields return ret;
3704d5184658SJ. Bruce Fields }
3705d5184658SJ. Bruce Fields
3706d5184658SJ. Bruce Fields /*
37072825a7f9SJ. Bruce Fields * The buffer space for this field was reserved during a previous
3708ae1131d4SChuck Lever * call to nfsd4_encode_entry4().
3709d5184658SJ. Bruce Fields */
nfsd4_encode_entry4_nfs_cookie4(const struct nfsd4_readdir * readdir,u64 offset)3710d5184658SJ. Bruce Fields static void nfsd4_encode_entry4_nfs_cookie4(const struct nfsd4_readdir *readdir,
3711d5184658SJ. Bruce Fields u64 offset)
3712d5184658SJ. Bruce Fields {
3713d5184658SJ. Bruce Fields __be64 cookie = cpu_to_be64(offset);
37143fc5048cSChuck Lever struct xdr_stream *xdr = readdir->xdr;
37153fc5048cSChuck Lever
37163fc5048cSChuck Lever if (!readdir->cookie_offset)
37173fc5048cSChuck Lever return;
37183fc5048cSChuck Lever write_bytes_to_xdr_buf(xdr->buf, readdir->cookie_offset, &cookie,
37193fc5048cSChuck Lever sizeof(cookie));
37203fc5048cSChuck Lever }
37213fc5048cSChuck Lever
attributes_need_mount(u32 * bmval)37223fc5048cSChuck Lever static inline int attributes_need_mount(u32 *bmval)
37233fc5048cSChuck Lever {
37243fc5048cSChuck Lever if (bmval[0] & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_LEASE_TIME))
37253fc5048cSChuck Lever return 1;
37263fc5048cSChuck Lever if (bmval[1] & ~FATTR4_WORD1_MOUNTED_ON_FILEID)
37273fc5048cSChuck Lever return 1;
37283fc5048cSChuck Lever return 0;
37293fc5048cSChuck Lever }
3730c0ce6ec8SJ. Bruce Fields
3731c0ce6ec8SJ. Bruce Fields static __be32
nfsd4_encode_entry4_fattr(struct nfsd4_readdir * cd,const char * name,int namlen)3732c0ce6ec8SJ. Bruce Fields nfsd4_encode_entry4_fattr(struct nfsd4_readdir *cd, const char *name,
3733c0ce6ec8SJ. Bruce Fields int namlen)
3734c0ce6ec8SJ. Bruce Fields {
3735c0ce6ec8SJ. Bruce Fields struct svc_export *exp = cd->rd_fhp->fh_export;
3736c0ce6ec8SJ. Bruce Fields struct dentry *dentry;
3737c0ce6ec8SJ. Bruce Fields __be32 nfserr;
3738c0ce6ec8SJ. Bruce Fields int ignore_crossmnt = 0;
3739b37ad28bSAl Viro
3740a0f3c835SChuck Lever dentry = lookup_positive_unlocked(name, cd->rd_fhp->fh_dentry, namlen);
3741a0f3c835SChuck Lever if (IS_ERR(dentry))
37421da177e4SLinus Torvalds return nfserrno(PTR_ERR(dentry));
37431da177e4SLinus Torvalds
37441da177e4SLinus Torvalds exp_get(exp);
3745b37ad28bSAl Viro /*
3746406a7ea9SFrank Filz * In the case of a mountpoint, the client may be asking for
37471da177e4SLinus Torvalds * attributes that are only properties of the underlying filesystem
37486c2d4798SAl Viro * as opposed to the cross-mounted file system. In such a case,
37491da177e4SLinus Torvalds * we will not follow the cross mount and will fill the attribtutes
37501da177e4SLinus Torvalds * directly from the mountpoint dentry.
37511da177e4SLinus Torvalds */
37521da177e4SLinus Torvalds if (nfsd_mountpoint(dentry, exp)) {
3753406a7ea9SFrank Filz int err;
3754406a7ea9SFrank Filz
3755406a7ea9SFrank Filz if (!(exp->ex_flags & NFSEXP_V4ROOT)
3756406a7ea9SFrank Filz && !attributes_need_mount(cd->rd_bmval)) {
3757406a7ea9SFrank Filz ignore_crossmnt = 1;
3758406a7ea9SFrank Filz goto out_encode;
3759406a7ea9SFrank Filz }
37603227fa41SJ. Bruce Fields /*
3761021d3a72SJ.Bruce Fields * Why the heck aren't we just using nfsd_lookup??
3762021d3a72SJ.Bruce Fields * Different "."/".." handling? Something else?
37633227fa41SJ. Bruce Fields * At least, add a comment here to explain....
37643227fa41SJ. Bruce Fields */
37653227fa41SJ. Bruce Fields err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp);
37663227fa41SJ. Bruce Fields if (err) {
37673227fa41SJ. Bruce Fields nfserr = nfserrno(err);
3768dcb488a3SAndy Adamson goto out_put;
3769dcb488a3SAndy Adamson }
3770dcb488a3SAndy Adamson nfserr = check_nfsd_access(exp, cd->rd_rqstp);
3771dcb488a3SAndy Adamson if (nfserr)
3772dcb488a3SAndy Adamson goto out_put;
3773021d3a72SJ.Bruce Fields
3774021d3a72SJ.Bruce Fields }
3775021d3a72SJ.Bruce Fields out_encode:
37761da177e4SLinus Torvalds nfserr = nfsd4_encode_fattr4(cd->rd_rqstp, cd->xdr, NULL, exp, dentry,
37771da177e4SLinus Torvalds cd->rd_bmval, ignore_crossmnt);
3778dcb488a3SAndy Adamson out_put:
3779dcb488a3SAndy Adamson dput(dentry);
3780dcb488a3SAndy Adamson exp_put(exp);
37811da177e4SLinus Torvalds return nfserr;
37821da177e4SLinus Torvalds }
37833227fa41SJ. Bruce Fields
3784a0f3c835SChuck Lever static __be32
nfsd4_encode_entry4_rdattr_error(struct xdr_stream * xdr,__be32 nfserr)3785ae1131d4SChuck Lever nfsd4_encode_entry4_rdattr_error(struct xdr_stream *xdr, __be32 nfserr)
37861da177e4SLinus Torvalds {
37871da177e4SLinus Torvalds __be32 status;
37881da177e4SLinus Torvalds
37891da177e4SLinus Torvalds /* attrmask */
37901da177e4SLinus Torvalds status = nfsd4_encode_bitmap4(xdr, FATTR4_WORD0_RDATTR_ERROR, 0, 0);
37911da177e4SLinus Torvalds if (status != nfs_ok)
3792a0d042f8SChuck Lever return status;
3793a0d042f8SChuck Lever /* attr_vals */
37941da177e4SLinus Torvalds if (xdr_stream_encode_u32(xdr, XDR_UNIT) != XDR_UNIT)
3795a0d042f8SChuck Lever return nfserr_resource;
3796561f0ed4SJ. Bruce Fields /* rdattr_error */
3797a0d042f8SChuck Lever if (xdr_stream_encode_be32(xdr, nfserr) != XDR_UNIT)
3798a0d042f8SChuck Lever return nfserr_resource;
3799a0d042f8SChuck Lever return nfs_ok;
3800a0d042f8SChuck Lever }
3801a0d042f8SChuck Lever
3802a0d042f8SChuck Lever static int
nfsd4_encode_entry4(void * ccdv,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)3803a0d042f8SChuck Lever nfsd4_encode_entry4(void *ccdv, const char *name, int namlen,
3804a0d042f8SChuck Lever loff_t offset, u64 ino, unsigned int d_type)
3805a0d042f8SChuck Lever {
3806a0d042f8SChuck Lever struct readdir_cd *ccd = ccdv;
3807a0d042f8SChuck Lever struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
38081da177e4SLinus Torvalds struct xdr_stream *xdr = cd->xdr;
38091da177e4SLinus Torvalds int start_offset = xdr->buf->len;
38101da177e4SLinus Torvalds int cookie_offset;
3811a0f3c835SChuck Lever u32 name_and_cookie;
3812a0ad13efSNeilBrown int entry_bytes;
38131da177e4SLinus Torvalds __be32 nfserr = nfserr_toosmall;
3814a0ad13efSNeilBrown
38151da177e4SLinus Torvalds /* In nfsv4, "." and ".." never make it onto the wire.. */
3816561f0ed4SJ. Bruce Fields if (name && isdotent(name, namlen)) {
3817561f0ed4SJ. Bruce Fields cd->common.err = nfs_ok;
3818561f0ed4SJ. Bruce Fields return 0;
3819aee37764SJ. Bruce Fields }
3820561f0ed4SJ. Bruce Fields
3821b37ad28bSAl Viro /* Encode the previous entry's cookie value */
38221da177e4SLinus Torvalds nfsd4_encode_entry4_nfs_cookie4(cd, offset);
38231da177e4SLinus Torvalds
38241da177e4SLinus Torvalds if (xdr_stream_encode_item_present(xdr) != XDR_UNIT)
38251da177e4SLinus Torvalds goto fail;
38261da177e4SLinus Torvalds
38271da177e4SLinus Torvalds /* Reserve send buffer space for this entry's cookie value. */
38281da177e4SLinus Torvalds cookie_offset = xdr->buf->len;
38293fc5048cSChuck Lever if (nfsd4_encode_nfs_cookie4(xdr, OFFSET_MAX) != nfs_ok)
38303fc5048cSChuck Lever goto fail;
38311da177e4SLinus Torvalds if (nfsd4_encode_component4(xdr, name, namlen) != nfs_ok)
3832a1aee9aaSChuck Lever goto fail;
38331da177e4SLinus Torvalds nfserr = nfsd4_encode_entry4_fattr(cd, name, namlen);
38341da177e4SLinus Torvalds switch (nfserr) {
3835a1aee9aaSChuck Lever case nfs_ok:
3836a1aee9aaSChuck Lever break;
3837a1aee9aaSChuck Lever case nfserr_resource:
3838a1aee9aaSChuck Lever nfserr = nfserr_toosmall;
3839a1aee9aaSChuck Lever goto fail;
3840a1aee9aaSChuck Lever case nfserr_noent:
3841a0f3c835SChuck Lever xdr_truncate_encode(xdr, start_offset);
38421da177e4SLinus Torvalds goto skip_entry;
38431da177e4SLinus Torvalds case nfserr_jukebox:
38441da177e4SLinus Torvalds /*
38451da177e4SLinus Torvalds * The pseudoroot should only display dentries that lead to
38461da177e4SLinus Torvalds * exports. If we get EJUKEBOX here, then we can't tell whether
38471da177e4SLinus Torvalds * this entry should be included. Just fail the whole READDIR
3848b2c0cea6SJ. Bruce Fields * with NFS4ERR_DELAY in that case, and hope that the situation
3849f41c5ad2SKinglong Mee * will resolve itself by the client's next attempt.
3850b2c0cea6SJ. Bruce Fields */
3851cad85337SJeff Layton if (cd->rd_fhp->fh_export->ex_flags & NFSEXP_V4ROOT)
3852cad85337SJeff Layton goto fail;
3853cad85337SJeff Layton fallthrough;
3854cad85337SJeff Layton default:
3855cad85337SJeff Layton /*
3856cad85337SJeff Layton * If the client requested the RDATTR_ERROR attribute,
3857cad85337SJeff Layton * we stuff the error code into this attribute
3858cad85337SJeff Layton * and continue. If this attribute was not requested,
3859cad85337SJeff Layton * then in accordance with the spec, we fail the
3860cad85337SJeff Layton * entire READDIR operation(!)
3861cad85337SJeff Layton */
38621da177e4SLinus Torvalds if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR))
38631da177e4SLinus Torvalds goto fail;
38641da177e4SLinus Torvalds if (nfsd4_encode_entry4_rdattr_error(xdr, nfserr)) {
38651da177e4SLinus Torvalds nfserr = nfserr_toosmall;
38661da177e4SLinus Torvalds goto fail;
38671da177e4SLinus Torvalds }
38681da177e4SLinus Torvalds }
38691da177e4SLinus Torvalds nfserr = nfserr_toosmall;
38701da177e4SLinus Torvalds entry_bytes = xdr->buf->len - start_offset;
38711da177e4SLinus Torvalds if (entry_bytes > cd->rd_maxcount)
3872a0d042f8SChuck Lever goto fail;
387334081efcSFred Isaman cd->rd_maxcount -= entry_bytes;
38741da177e4SLinus Torvalds /*
38751da177e4SLinus Torvalds * RFC 3530 14.2.24 describes rd_dircount as only a "hint", and
387634081efcSFred Isaman * notes that it could be zero. If it is zero, then the server
3877561f0ed4SJ. Bruce Fields * should enforce only the rd_maxcount value.
3878561f0ed4SJ. Bruce Fields */
3879561f0ed4SJ. Bruce Fields if (cd->rd_dircount) {
3880561f0ed4SJ. Bruce Fields name_and_cookie = 4 + 4 * XDR_QUADLEN(namlen) + 8;
3881561f0ed4SJ. Bruce Fields if (name_and_cookie > cd->rd_dircount && cd->cookie_offset)
3882aee37764SJ. Bruce Fields goto fail;
3883f2e717d6STrond Myklebust cd->rd_dircount -= min(cd->rd_dircount, name_and_cookie);
3884f2e717d6STrond Myklebust if (!cd->rd_dircount)
3885f2e717d6STrond Myklebust cd->rd_maxcount = 0;
3886aee37764SJ. Bruce Fields }
3887f2e717d6STrond Myklebust
38880ec016e3SJ. Bruce Fields cd->cookie_offset = cookie_offset;
3889aee37764SJ. Bruce Fields skip_entry:
3890aee37764SJ. Bruce Fields cd->common.err = nfs_ok;
3891aee37764SJ. Bruce Fields return 0;
3892f2e717d6STrond Myklebust fail:
3893f2e717d6STrond Myklebust xdr_truncate_encode(xdr, start_offset);
3894f2e717d6STrond Myklebust cd->common.err = nfserr;
38950ec016e3SJ. Bruce Fields return -EINVAL;
3896561f0ed4SJ. Bruce Fields }
3897b2c0cea6SJ. Bruce Fields
38981da177e4SLinus Torvalds static __be32
nfsd4_encode_verifier4(struct xdr_stream * xdr,const nfs4_verifier * verf)38991da177e4SLinus Torvalds nfsd4_encode_verifier4(struct xdr_stream *xdr, const nfs4_verifier *verf)
39001da177e4SLinus Torvalds {
3901561f0ed4SJ. Bruce Fields __be32 *p;
39021da177e4SLinus Torvalds
39031da177e4SLinus Torvalds p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
39041da177e4SLinus Torvalds if (!p)
39051da177e4SLinus Torvalds return nfserr_resource;
3906d0a381ddSJ. Bruce Fields memcpy(p, verf->data, sizeof(verf->data));
3907adaa7a50SChuck Lever return nfs_ok;
3908adaa7a50SChuck Lever }
3909adaa7a50SChuck Lever
3910adaa7a50SChuck Lever static __be32
nfsd4_encode_clientid4(struct xdr_stream * xdr,const clientid_t * clientid)3911adaa7a50SChuck Lever nfsd4_encode_clientid4(struct xdr_stream *xdr, const clientid_t *clientid)
3912adaa7a50SChuck Lever {
3913adaa7a50SChuck Lever __be32 *p;
3914adaa7a50SChuck Lever
3915adaa7a50SChuck Lever p = xdr_reserve_space(xdr, sizeof(__be64));
3916adaa7a50SChuck Lever if (!p)
3917adaa7a50SChuck Lever return nfserr_resource;
3918adaa7a50SChuck Lever memcpy(p, clientid, sizeof(*clientid));
3919adaa7a50SChuck Lever return nfs_ok;
3920adaa7a50SChuck Lever }
3921adaa7a50SChuck Lever
3922adaa7a50SChuck Lever /* This is a frequently-encoded item; open-coded for speed */
3923adaa7a50SChuck Lever static __be32
nfsd4_encode_stateid4(struct xdr_stream * xdr,const stateid_t * sid)3924adaa7a50SChuck Lever nfsd4_encode_stateid4(struct xdr_stream *xdr, const stateid_t *sid)
3925adaa7a50SChuck Lever {
3926adaa7a50SChuck Lever __be32 *p;
3927adaa7a50SChuck Lever
3928adaa7a50SChuck Lever p = xdr_reserve_space(xdr, NFS4_STATEID_SIZE);
3929adaa7a50SChuck Lever if (!p)
393040bb2baaSChuck Lever return nfserr_resource;
3931adaa7a50SChuck Lever *p++ = cpu_to_be32(sid->si_generation);
393240bb2baaSChuck Lever memcpy(p, &sid->si_opaque, sizeof(sid->si_opaque));
3933e2f282b9SBenny Halevy return nfs_ok;
3934bc749ca4SJ. Bruce Fields }
3935e2f282b9SBenny Halevy
393640bb2baaSChuck Lever static __be32
nfsd4_encode_sessionid4(struct xdr_stream * xdr,const struct nfs4_sessionid * sessionid)3937d0a381ddSJ. Bruce Fields nfsd4_encode_sessionid4(struct xdr_stream *xdr,
3938d0a381ddSJ. Bruce Fields const struct nfs4_sessionid *sessionid)
3939c373b0a4SJ. Bruce Fields {
394040bb2baaSChuck Lever return nfsd4_encode_opaque_fixed(xdr, sessionid->data,
394140bb2baaSChuck Lever NFS4_MAX_SESSIONID_LEN);
3942e2f282b9SBenny Halevy }
3943e2f282b9SBenny Halevy
3944695e12f8SBenny Halevy static __be32
nfsd4_encode_access(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)394565baa609SChuck Lever nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr,
394665baa609SChuck Lever union nfsd4_op_u *u)
394765baa609SChuck Lever {
394865baa609SChuck Lever struct nfsd4_access *access = &u->access;
394965baa609SChuck Lever struct xdr_stream *xdr = resp->xdr;
395065baa609SChuck Lever __be32 status;
395165baa609SChuck Lever
395265baa609SChuck Lever /* supported */
3953e78e274eSKees Cook status = nfsd4_encode_uint32_t(xdr, access->ac_supported);
3954e78e274eSKees Cook if (status != nfs_ok)
39551da177e4SLinus Torvalds return status;
3956e78e274eSKees Cook /* access */
3957bddfdbcdSChuck Lever return nfsd4_encode_uint32_t(xdr, access->ac_resp_access);
3958d38e570fSChuck Lever }
39591da177e4SLinus Torvalds
nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3960d38e570fSChuck Lever static __be32 nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres *resp, __be32 nfserr,
3961d38e570fSChuck Lever union nfsd4_op_u *u)
3962d38e570fSChuck Lever {
3963d38e570fSChuck Lever struct nfsd4_bind_conn_to_session *bcts = &u->bind_conn_to_session;
3964d38e570fSChuck Lever struct xdr_stream *xdr = resp->xdr;
3965d38e570fSChuck Lever
39661da177e4SLinus Torvalds /* bctsr_sessid */
39671da177e4SLinus Torvalds nfserr = nfsd4_encode_sessionid4(xdr, &bcts->sessionid);
3968e78e274eSKees Cook if (nfserr != nfs_ok)
3969e78e274eSKees Cook return nfserr;
39701d1bc8f2SJ. Bruce Fields /* bctsr_dir */
3971e78e274eSKees Cook if (xdr_stream_encode_u32(xdr, bcts->dir) != XDR_UNIT)
3972bddfdbcdSChuck Lever return nfserr_resource;
39731d1bc8f2SJ. Bruce Fields /* bctsr_use_conn_in_rdma_mode */
397465baa609SChuck Lever return nfsd4_encode_bool(xdr, false);
397565baa609SChuck Lever }
397665baa609SChuck Lever
397765baa609SChuck Lever static __be32
nfsd4_encode_close(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)397865baa609SChuck Lever nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr,
397965baa609SChuck Lever union nfsd4_op_u *u)
3980d0a381ddSJ. Bruce Fields {
398165baa609SChuck Lever struct nfsd4_close *close = &u->close;
398265baa609SChuck Lever struct xdr_stream *xdr = resp->xdr;
39831d1bc8f2SJ. Bruce Fields
39841d1bc8f2SJ. Bruce Fields /* open_stateid */
3985695e12f8SBenny Halevy return nfsd4_encode_stateid4(xdr, &close->cl_stateid);
3986e78e274eSKees Cook }
3987e78e274eSKees Cook
39881da177e4SLinus Torvalds
3989e78e274eSKees Cook static __be32
nfsd4_encode_commit(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3990bddfdbcdSChuck Lever nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr,
3991d0a381ddSJ. Bruce Fields union nfsd4_op_u *u)
399240bb2baaSChuck Lever {
399340bb2baaSChuck Lever struct nfsd4_commit *commit = &u->commit;
39941da177e4SLinus Torvalds
39951da177e4SLinus Torvalds return nfsd4_encode_verifier4(resp->xdr, &commit->co_verf);
39961da177e4SLinus Torvalds }
3997695e12f8SBenny Halevy
3998e78e274eSKees Cook static __be32
nfsd4_encode_create(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3999e78e274eSKees Cook nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr,
40001da177e4SLinus Torvalds union nfsd4_op_u *u)
4001e78e274eSKees Cook {
40021da177e4SLinus Torvalds struct nfsd4_create *create = &u->create;
4003adaa7a50SChuck Lever struct xdr_stream *xdr = resp->xdr;
40041da177e4SLinus Torvalds
40051da177e4SLinus Torvalds /* cinfo */
4006695e12f8SBenny Halevy nfserr = nfsd4_encode_change_info4(xdr, &create->cr_cinfo);
4007e78e274eSKees Cook if (nfserr)
4008e78e274eSKees Cook return nfserr;
40091da177e4SLinus Torvalds /* attrset */
4010e78e274eSKees Cook return nfsd4_encode_bitmap4(xdr, create->cr_bmval[0],
4011bddfdbcdSChuck Lever create->cr_bmval[1], create->cr_bmval[2]);
40121da177e4SLinus Torvalds }
4013e64301f5SChuck Lever
401466a21db7SChuck Lever static __be32
nfsd4_encode_getattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)401566a21db7SChuck Lever nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr,
401666a21db7SChuck Lever union nfsd4_op_u *u)
4017e64301f5SChuck Lever {
4018e64301f5SChuck Lever struct nfsd4_getattr *getattr = &u->getattr;
401975976de6SKinglong Mee struct svc_fh *fhp = getattr->ga_fhp;
40201da177e4SLinus Torvalds struct xdr_stream *xdr = resp->xdr;
40211da177e4SLinus Torvalds
4022b37ad28bSAl Viro /* obj_attributes */
4023e78e274eSKees Cook return nfsd4_encode_fattr4(resp->rqstp, xdr, fhp, fhp->fh_export,
4024e78e274eSKees Cook fhp->fh_dentry, getattr->ga_bmval, 0);
40251da177e4SLinus Torvalds }
4026e78e274eSKees Cook
40271da177e4SLinus Torvalds static __be32
nfsd4_encode_getfh(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4028bddfdbcdSChuck Lever nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr,
40291da177e4SLinus Torvalds union nfsd4_op_u *u)
4030ae1131d4SChuck Lever {
4031ae1131d4SChuck Lever struct xdr_stream *xdr = resp->xdr;
4032ae1131d4SChuck Lever struct svc_fh *fhp = u->getfh;
40331da177e4SLinus Torvalds
40341da177e4SLinus Torvalds /* object */
4035695e12f8SBenny Halevy return nfsd4_encode_nfs_fh4(xdr, &fhp->fh_handle);
4036e78e274eSKees Cook }
4037e78e274eSKees Cook
40381da177e4SLinus Torvalds static __be32
nfsd4_encode_lock_owner4(struct xdr_stream * xdr,const clientid_t * clientid,const struct xdr_netobj * owner)4039bddfdbcdSChuck Lever nfsd4_encode_lock_owner4(struct xdr_stream *xdr, const clientid_t *clientid,
40403283bf64SChuck Lever const struct xdr_netobj *owner)
40411da177e4SLinus Torvalds {
40423283bf64SChuck Lever __be32 status;
40433283bf64SChuck Lever
40441da177e4SLinus Torvalds /* clientid */
40451da177e4SLinus Torvalds status = nfsd4_encode_clientid4(xdr, clientid);
4046c4a29c52SChuck Lever if (status != nfs_ok)
4047c4a29c52SChuck Lever return status;
4048c4a29c52SChuck Lever /* owner */
4049c4a29c52SChuck Lever return nfsd4_encode_opaque(xdr, owner->data, owner->len);
4050c4a29c52SChuck Lever }
4051c4a29c52SChuck Lever
4052c4a29c52SChuck Lever static __be32
nfsd4_encode_lock4denied(struct xdr_stream * xdr,const struct nfsd4_lock_denied * ld)4053c4a29c52SChuck Lever nfsd4_encode_lock4denied(struct xdr_stream *xdr,
4054c4a29c52SChuck Lever const struct nfsd4_lock_denied *ld)
4055c4a29c52SChuck Lever {
4056c4a29c52SChuck Lever __be32 status;
4057c4a29c52SChuck Lever
4058c4a29c52SChuck Lever /* offset */
4059c4a29c52SChuck Lever status = nfsd4_encode_offset4(xdr, ld->ld_start);
4060d0a381ddSJ. Bruce Fields if (status != nfs_ok)
4061c5641782SChuck Lever return status;
4062c5641782SChuck Lever /* length */
40631da177e4SLinus Torvalds status = nfsd4_encode_length4(xdr, ld->ld_length);
4064c5641782SChuck Lever if (status != nfs_ok)
40651da177e4SLinus Torvalds return status;
4066c5641782SChuck Lever /* locktype */
4067c5641782SChuck Lever if (xdr_stream_encode_u32(xdr, ld->ld_type) != XDR_UNIT)
4068c4a29c52SChuck Lever return nfserr_resource;
4069c4a29c52SChuck Lever /* owner */
4070c5641782SChuck Lever return nfsd4_encode_lock_owner4(xdr, &ld->ld_clientid,
4071c5641782SChuck Lever &ld->ld_owner);
4072c5641782SChuck Lever }
4073c5641782SChuck Lever
4074c5641782SChuck Lever static __be32
nfsd4_encode_lock(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4075c5641782SChuck Lever nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr,
4076c5641782SChuck Lever union nfsd4_op_u *u)
4077c5641782SChuck Lever {
4078c5641782SChuck Lever struct nfsd4_lock *lock = &u->lock;
4079c5641782SChuck Lever struct xdr_stream *xdr = resp->xdr;
40801da177e4SLinus Torvalds __be32 status;
40811da177e4SLinus Torvalds
4082695e12f8SBenny Halevy switch (nfserr) {
4083e78e274eSKees Cook case nfs_ok:
4084e78e274eSKees Cook /* resok4 */
40851da177e4SLinus Torvalds status = nfsd4_encode_stateid4(xdr, &lock->lk_resp_stateid);
4086e78e274eSKees Cook break;
4087bddfdbcdSChuck Lever case nfserr_denied:
4088c5641782SChuck Lever /* denied */
4089d0a381ddSJ. Bruce Fields status = nfsd4_encode_lock4denied(xdr, &lock->lk_denied);
4090c5641782SChuck Lever break;
4091c5641782SChuck Lever default:
4092c5641782SChuck Lever return nfserr;
4093c5641782SChuck Lever }
4094c5641782SChuck Lever return status != nfs_ok ? status : nfserr;
4095c5641782SChuck Lever }
4096c5641782SChuck Lever
4097c5641782SChuck Lever static __be32
nfsd4_encode_lockt(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4098c5641782SChuck Lever nfsd4_encode_lockt(struct nfsd4_compoundres *resp, __be32 nfserr,
4099c5641782SChuck Lever union nfsd4_op_u *u)
4100695e12f8SBenny Halevy {
41011da177e4SLinus Torvalds struct nfsd4_lockt *lockt = &u->lockt;
4102c5641782SChuck Lever struct xdr_stream *xdr = resp->xdr;
4103c5641782SChuck Lever __be32 status;
41041da177e4SLinus Torvalds
4105695e12f8SBenny Halevy if (nfserr == nfserr_denied) {
4106e78e274eSKees Cook /* denied */
4107e78e274eSKees Cook status = nfsd4_encode_lock4denied(xdr, &lockt->lt_denied);
41081da177e4SLinus Torvalds if (status != nfs_ok)
4109e78e274eSKees Cook return status;
4110bddfdbcdSChuck Lever }
4111c5641782SChuck Lever return nfserr;
4112d0a381ddSJ. Bruce Fields }
4113c5641782SChuck Lever
4114c5641782SChuck Lever static __be32
nfsd4_encode_locku(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4115c5641782SChuck Lever nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr,
4116c5641782SChuck Lever union nfsd4_op_u *u)
4117c5641782SChuck Lever {
4118c5641782SChuck Lever struct nfsd4_locku *locku = &u->locku;
4119695e12f8SBenny Halevy struct xdr_stream *xdr = resp->xdr;
41201da177e4SLinus Torvalds
41211da177e4SLinus Torvalds /* lock_stateid */
4122695e12f8SBenny Halevy return nfsd4_encode_stateid4(xdr, &locku->lu_stateid);
4123e78e274eSKees Cook }
4124e78e274eSKees Cook
41251da177e4SLinus Torvalds
4126e78e274eSKees Cook static __be32
nfsd4_encode_link(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4127bddfdbcdSChuck Lever nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr,
4128d0a381ddSJ. Bruce Fields union nfsd4_op_u *u)
412940bb2baaSChuck Lever {
413040bb2baaSChuck Lever struct nfsd4_link *link = &u->link;
41311da177e4SLinus Torvalds struct xdr_stream *xdr = resp->xdr;
41321da177e4SLinus Torvalds
41331da177e4SLinus Torvalds return nfsd4_encode_change_info4(xdr, &link->li_cinfo);
4134695e12f8SBenny Halevy }
4135e78e274eSKees Cook
4136e78e274eSKees Cook /*
41371da177e4SLinus Torvalds * This implementation does not yet support returning an ACE in an
4138e78e274eSKees Cook * OPEN that offers a delegation.
4139bddfdbcdSChuck Lever */
41401da177e4SLinus Torvalds static __be32
nfsd4_encode_open_nfsace4(struct xdr_stream * xdr)414166a21db7SChuck Lever nfsd4_encode_open_nfsace4(struct xdr_stream *xdr)
41421da177e4SLinus Torvalds {
41431da177e4SLinus Torvalds __be32 status;
4144e4ad7ce7SChuck Lever
4145e4ad7ce7SChuck Lever /* type */
4146e4ad7ce7SChuck Lever status = nfsd4_encode_acetype4(xdr, NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
4147e4ad7ce7SChuck Lever if (status != nfs_ok)
4148e4ad7ce7SChuck Lever return nfserr_resource;
4149e4ad7ce7SChuck Lever /* flag */
4150e4ad7ce7SChuck Lever status = nfsd4_encode_aceflag4(xdr, 0);
4151e4ad7ce7SChuck Lever if (status != nfs_ok)
4152e4ad7ce7SChuck Lever return nfserr_resource;
4153e4ad7ce7SChuck Lever /* access mask */
4154e4ad7ce7SChuck Lever status = nfsd4_encode_acemask4(xdr, 0);
4155e4ad7ce7SChuck Lever if (status != nfs_ok)
4156e4ad7ce7SChuck Lever return nfserr_resource;
4157e4ad7ce7SChuck Lever /* who - empty for now */
4158e4ad7ce7SChuck Lever if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
4159e4ad7ce7SChuck Lever return nfserr_resource;
4160e4ad7ce7SChuck Lever return nfs_ok;
4161e4ad7ce7SChuck Lever }
4162e4ad7ce7SChuck Lever
4163e4ad7ce7SChuck Lever static __be32
nfsd4_encode_open_read_delegation4(struct xdr_stream * xdr,struct nfsd4_open * open)4164e4ad7ce7SChuck Lever nfsd4_encode_open_read_delegation4(struct xdr_stream *xdr, struct nfsd4_open *open)
4165e4ad7ce7SChuck Lever {
4166e4ad7ce7SChuck Lever __be32 status;
4167e4ad7ce7SChuck Lever
4168e4ad7ce7SChuck Lever /* stateid */
4169e4ad7ce7SChuck Lever status = nfsd4_encode_stateid4(xdr, &open->op_delegate_stateid);
4170e4ad7ce7SChuck Lever if (status != nfs_ok)
4171e4ad7ce7SChuck Lever return status;
4172e4ad7ce7SChuck Lever /* recall */
4173e4ad7ce7SChuck Lever status = nfsd4_encode_bool(xdr, open->op_recall);
4174e4ad7ce7SChuck Lever if (status != nfs_ok)
4175e4ad7ce7SChuck Lever return status;
4176e4ad7ce7SChuck Lever /* permissions */
4177e4ad7ce7SChuck Lever return nfsd4_encode_open_nfsace4(xdr);
4178e4ad7ce7SChuck Lever }
4179e4ad7ce7SChuck Lever
4180e4ad7ce7SChuck Lever static __be32
nfsd4_encode_nfs_space_limit4(struct xdr_stream * xdr,u64 filesize)4181e4ad7ce7SChuck Lever nfsd4_encode_nfs_space_limit4(struct xdr_stream *xdr, u64 filesize)
4182e4ad7ce7SChuck Lever {
4183e4ad7ce7SChuck Lever /* limitby */
4184e4ad7ce7SChuck Lever if (xdr_stream_encode_u32(xdr, NFS4_LIMIT_SIZE) != XDR_UNIT)
4185e4ad7ce7SChuck Lever return nfserr_resource;
4186e4ad7ce7SChuck Lever /* filesize */
41871da177e4SLinus Torvalds return nfsd4_encode_uint64_t(xdr, filesize);
4188695e12f8SBenny Halevy }
418932efa674SChuck Lever
419032efa674SChuck Lever static __be32
nfsd4_encode_open_write_delegation4(struct xdr_stream * xdr,struct nfsd4_open * open)419132efa674SChuck Lever nfsd4_encode_open_write_delegation4(struct xdr_stream *xdr,
419232efa674SChuck Lever struct nfsd4_open *open)
419332efa674SChuck Lever {
419432efa674SChuck Lever __be32 status;
419532efa674SChuck Lever
419632efa674SChuck Lever /* stateid */
419732efa674SChuck Lever status = nfsd4_encode_stateid4(xdr, &open->op_delegate_stateid);
419832efa674SChuck Lever if (status != nfs_ok)
419932efa674SChuck Lever return status;
420032efa674SChuck Lever /* recall */
420132efa674SChuck Lever status = nfsd4_encode_bool(xdr, open->op_recall);
420232efa674SChuck Lever if (status != nfs_ok)
420332efa674SChuck Lever return status;
420432efa674SChuck Lever /* space_limit */
420532efa674SChuck Lever status = nfsd4_encode_nfs_space_limit4(xdr, 0);
420632efa674SChuck Lever if (status != nfs_ok)
420732efa674SChuck Lever return status;
420832efa674SChuck Lever return nfsd4_encode_open_nfsace4(xdr);
420932efa674SChuck Lever }
421032efa674SChuck Lever
421132efa674SChuck Lever static __be32
nfsd4_encode_open_none_delegation4(struct xdr_stream * xdr,struct nfsd4_open * open)421232efa674SChuck Lever nfsd4_encode_open_none_delegation4(struct xdr_stream *xdr,
421332efa674SChuck Lever struct nfsd4_open *open)
421432efa674SChuck Lever {
421532efa674SChuck Lever __be32 status = nfs_ok;
421632efa674SChuck Lever
421732efa674SChuck Lever /* ond_why */
421832efa674SChuck Lever if (xdr_stream_encode_u32(xdr, open->op_why_no_deleg) != XDR_UNIT)
421932efa674SChuck Lever return nfserr_resource;
42206dd43c6dSChuck Lever switch (open->op_why_no_deleg) {
42216dd43c6dSChuck Lever case WND4_CONTENTION:
42226dd43c6dSChuck Lever /* ond_server_will_push_deleg */
42236dd43c6dSChuck Lever status = nfsd4_encode_bool(xdr, false);
42246dd43c6dSChuck Lever break;
42256dd43c6dSChuck Lever case WND4_RESOURCE:
42266dd43c6dSChuck Lever /* ond_server_will_signal_avail */
42276dd43c6dSChuck Lever status = nfsd4_encode_bool(xdr, false);
42286dd43c6dSChuck Lever }
42296dd43c6dSChuck Lever return status;
42306dd43c6dSChuck Lever }
42316dd43c6dSChuck Lever
42326dd43c6dSChuck Lever static __be32
nfsd4_encode_open_delegation4(struct xdr_stream * xdr,struct nfsd4_open * open)42336dd43c6dSChuck Lever nfsd4_encode_open_delegation4(struct xdr_stream *xdr, struct nfsd4_open *open)
42346dd43c6dSChuck Lever {
42356dd43c6dSChuck Lever __be32 status;
42366dd43c6dSChuck Lever
42376dd43c6dSChuck Lever /* delegation_type */
42386dd43c6dSChuck Lever if (xdr_stream_encode_u32(xdr, open->op_delegate_type) != XDR_UNIT)
42396dd43c6dSChuck Lever return nfserr_resource;
42406dd43c6dSChuck Lever switch (open->op_delegate_type) {
4241802e1913SChuck Lever case NFS4_OPEN_DELEGATE_NONE:
4242802e1913SChuck Lever status = nfs_ok;
4243802e1913SChuck Lever break;
4244802e1913SChuck Lever case NFS4_OPEN_DELEGATE_READ:
4245802e1913SChuck Lever /* read */
4246802e1913SChuck Lever status = nfsd4_encode_open_read_delegation4(xdr, open);
4247802e1913SChuck Lever break;
4248802e1913SChuck Lever case NFS4_OPEN_DELEGATE_WRITE:
4249802e1913SChuck Lever /* write */
4250802e1913SChuck Lever status = nfsd4_encode_open_write_delegation4(xdr, open);
4251802e1913SChuck Lever break;
4252802e1913SChuck Lever case NFS4_OPEN_DELEGATE_NONE_EXT:
4253802e1913SChuck Lever /* od_whynone */
4254802e1913SChuck Lever status = nfsd4_encode_open_none_delegation4(xdr, open);
4255802e1913SChuck Lever break;
4256802e1913SChuck Lever default:
4257802e1913SChuck Lever status = nfserr_serverfault;
4258802e1913SChuck Lever }
4259802e1913SChuck Lever
4260802e1913SChuck Lever return status;
4261802e1913SChuck Lever }
4262802e1913SChuck Lever
4263802e1913SChuck Lever static __be32
nfsd4_encode_open(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4264802e1913SChuck Lever nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr,
4265802e1913SChuck Lever union nfsd4_op_u *u)
4266802e1913SChuck Lever {
4267802e1913SChuck Lever struct nfsd4_open *open = &u->open;
4268802e1913SChuck Lever struct xdr_stream *xdr = resp->xdr;
4269802e1913SChuck Lever
4270802e1913SChuck Lever /* stateid */
4271802e1913SChuck Lever nfserr = nfsd4_encode_stateid4(xdr, &open->op_stateid);
4272e78e274eSKees Cook if (nfserr != nfs_ok)
4273e78e274eSKees Cook return nfserr;
42741da177e4SLinus Torvalds /* cinfo */
4275e78e274eSKees Cook nfserr = nfsd4_encode_change_info4(xdr, &open->op_cinfo);
4276bddfdbcdSChuck Lever if (nfserr != nfs_ok)
42771da177e4SLinus Torvalds return nfserr;
4278841735b3SChuck Lever /* rflags */
427940bb2baaSChuck Lever nfserr = nfsd4_encode_uint32_t(xdr, open->op_rflags);
4280841735b3SChuck Lever if (nfserr != nfs_ok)
4281bac966d6SJ. Bruce Fields return nfserr;
4282841735b3SChuck Lever /* attrset */
428366a21db7SChuck Lever nfserr = nfsd4_encode_bitmap4(xdr, open->op_bmval[0],
4284841735b3SChuck Lever open->op_bmval[1], open->op_bmval[2]);
428566a21db7SChuck Lever if (nfserr != nfs_ok)
4286841735b3SChuck Lever return nfserr;
4287841735b3SChuck Lever /* delegation */
4288841735b3SChuck Lever return nfsd4_encode_open_delegation4(xdr, open);
4289841735b3SChuck Lever }
4290841735b3SChuck Lever
4291e64301f5SChuck Lever static __be32
nfsd4_encode_open_confirm(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4292e64301f5SChuck Lever nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr,
4293841735b3SChuck Lever union nfsd4_op_u *u)
4294bac966d6SJ. Bruce Fields {
4295802e1913SChuck Lever struct nfsd4_open_confirm *oc = &u->open_confirm;
4296802e1913SChuck Lever struct xdr_stream *xdr = resp->xdr;
42971da177e4SLinus Torvalds
42981da177e4SLinus Torvalds /* open_stateid */
4299695e12f8SBenny Halevy return nfsd4_encode_stateid4(xdr, &oc->oc_resp_stateid);
4300e78e274eSKees Cook }
4301e78e274eSKees Cook
43021da177e4SLinus Torvalds static __be32
nfsd4_encode_open_downgrade(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4303e78e274eSKees Cook nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr,
4304bddfdbcdSChuck Lever union nfsd4_op_u *u)
4305d0a381ddSJ. Bruce Fields {
430640bb2baaSChuck Lever struct nfsd4_open_downgrade *od = &u->open_downgrade;
430740bb2baaSChuck Lever struct xdr_stream *xdr = resp->xdr;
43081da177e4SLinus Torvalds
43091da177e4SLinus Torvalds /* open_stateid */
4310695e12f8SBenny Halevy return nfsd4_encode_stateid4(xdr, &od->od_stateid);
4311e78e274eSKees Cook }
4312e78e274eSKees Cook
43131da177e4SLinus Torvalds /*
4314e78e274eSKees Cook * The operation of this function assumes that this is the only
4315bddfdbcdSChuck Lever * READ operation in the COMPOUND. If there are multiple READs,
4316d0a381ddSJ. Bruce Fields * we use nfsd4_encode_readv().
431740bb2baaSChuck Lever */
nfsd4_encode_splice_read(struct nfsd4_compoundres * resp,struct nfsd4_read * read,struct file * file,unsigned long maxcount)431840bb2baaSChuck Lever static __be32 nfsd4_encode_splice_read(
43191da177e4SLinus Torvalds struct nfsd4_compoundres *resp,
43201da177e4SLinus Torvalds struct nfsd4_read *read,
4321ba21e20bSChuck Lever struct file *file, unsigned long maxcount)
4322ba21e20bSChuck Lever {
4323ba21e20bSChuck Lever struct xdr_stream *xdr = resp->xdr;
4324ba21e20bSChuck Lever struct xdr_buf *buf = xdr->buf;
4325ba21e20bSChuck Lever int status, space_left;
4326dc97618dSJ. Bruce Fields __be32 nfserr;
4327dc97618dSJ. Bruce Fields
4328dc97618dSJ. Bruce Fields /*
4329dc97618dSJ. Bruce Fields * Make sure there is room at the end of buf->head for
43301da177e4SLinus Torvalds * svcxdr_encode_opaque_pages() to create a tail buffer
4331bddfdbcdSChuck Lever * to XDR-pad the payload.
433234a78b48SJ. Bruce Fields */
433376e5492bSChuck Lever if (xdr->iov != xdr->buf->head || xdr->end - xdr->p < 1)
4334dc97618dSJ. Bruce Fields return nfserr_resource;
4335dc97618dSJ. Bruce Fields
4336ba21e20bSChuck Lever nfserr = nfsd_splice_read(read->rd_rqstp, read->rd_fhp,
4337ba21e20bSChuck Lever file, read->rd_offset, &maxcount,
4338ba21e20bSChuck Lever &read->rd_eof);
4339ba21e20bSChuck Lever read->rd_length = maxcount;
4340ba21e20bSChuck Lever if (nfserr)
4341ba21e20bSChuck Lever goto out_err;
4342dc97618dSJ. Bruce Fields svcxdr_encode_opaque_pages(read->rd_rqstp, xdr, buf->pages,
4343dc97618dSJ. Bruce Fields buf->page_base, maxcount);
434487c5942eSChuck Lever status = svc_encode_result_payload(read->rd_rqstp,
434524c7fb85SChuck Lever buf->head[0].iov_len, maxcount);
434624c7fb85SChuck Lever if (status) {
434787c5942eSChuck Lever nfserr = nfserrno(status);
434876e5492bSChuck Lever goto out_err;
434976e5492bSChuck Lever }
4350ba21e20bSChuck Lever
4351ba21e20bSChuck Lever /*
435276e5492bSChuck Lever * Prepare to encode subsequent operations.
435376e5492bSChuck Lever *
435476e5492bSChuck Lever * xdr_truncate_encode() is not safe to use after a successful
435576e5492bSChuck Lever * splice read has been done, so the following stream
435676e5492bSChuck Lever * manipulations are open-coded.
4357dc97618dSJ. Bruce Fields */
4358dc97618dSJ. Bruce Fields space_left = min_t(int, (void *)xdr->end - (void *)xdr->p,
4359ba21e20bSChuck Lever buf->buflen - buf->len);
4360ba21e20bSChuck Lever buf->buflen = buf->len + space_left;
4361ba21e20bSChuck Lever xdr->end = (__be32 *)((void *)xdr->end + space_left);
4362ba21e20bSChuck Lever
4363ba21e20bSChuck Lever return nfs_ok;
4364ba21e20bSChuck Lever
4365ba21e20bSChuck Lever out_err:
4366dc97618dSJ. Bruce Fields /*
436734a78b48SJ. Bruce Fields * nfsd_splice_actor may have already messed with the
436834a78b48SJ. Bruce Fields * page length; reset it so as not to confuse
4369dc97618dSJ. Bruce Fields * xdr_truncate_encode in our caller.
4370dc97618dSJ. Bruce Fields */
4371ba21e20bSChuck Lever buf->page_len = 0;
437276e5492bSChuck Lever return nfserr;
437376e5492bSChuck Lever }
437476e5492bSChuck Lever
nfsd4_encode_readv(struct nfsd4_compoundres * resp,struct nfsd4_read * read,struct file * file,unsigned long maxcount)437576e5492bSChuck Lever static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
437676e5492bSChuck Lever struct nfsd4_read *read,
437776e5492bSChuck Lever struct file *file, unsigned long maxcount)
437876e5492bSChuck Lever {
437976e5492bSChuck Lever struct xdr_stream *xdr = resp->xdr;
438076e5492bSChuck Lever unsigned int base = xdr->buf->page_len & ~PAGE_MASK;
4381dc97618dSJ. Bruce Fields unsigned int starting_len = xdr->buf->len;
4382dc97618dSJ. Bruce Fields __be32 zero = xdr_zero;
4383dc97618dSJ. Bruce Fields __be32 nfserr;
4384dc97618dSJ. Bruce Fields
4385dc97618dSJ. Bruce Fields if (xdr_reserve_space_vec(xdr, maxcount) < 0)
4386dc97618dSJ. Bruce Fields return nfserr_resource;
4387bddfdbcdSChuck Lever
43880d32a6bbSChuck Lever nfserr = nfsd_iter_read(resp->rqstp, read->rd_fhp, file,
4389071ae99fSChuck Lever read->rd_offset, &maxcount, base,
43905e64d85cSChuck Lever &read->rd_eof);
4391dc97618dSJ. Bruce Fields read->rd_length = maxcount;
43921da177e4SLinus Torvalds if (nfserr)
4393703d7521SChuck Lever return nfserr;
4394403217f3SAnna Schumaker if (svc_encode_result_payload(resp->rqstp, starting_len, maxcount))
43951da177e4SLinus Torvalds return nfserr_io;
4396703d7521SChuck Lever xdr_truncate_encode(xdr, starting_len + xdr_align_size(maxcount));
43970d32a6bbSChuck Lever
439824c7fb85SChuck Lever write_bytes_to_xdr_buf(xdr->buf, starting_len + maxcount, &zero,
439987c5942eSChuck Lever xdr_pad_size(maxcount));
4400dc97618dSJ. Bruce Fields return nfs_ok;
44011da177e4SLinus Torvalds }
4402071ae99fSChuck Lever
440341205539SChuck Lever static __be32
nfsd4_encode_read(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4404071ae99fSChuck Lever nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
4405dc97618dSJ. Bruce Fields union nfsd4_op_u *u)
44065e64d85cSChuck Lever {
44075e64d85cSChuck Lever struct nfsd4_compoundargs *argp = resp->rqstp->rq_argp;
44085e64d85cSChuck Lever struct nfsd4_read *read = &u->read;
4409dc97618dSJ. Bruce Fields struct xdr_stream *xdr = resp->xdr;
4410dc97618dSJ. Bruce Fields int starting_len = xdr->buf->len;
4411dc97618dSJ. Bruce Fields bool splice_ok = argp->splice_ok;
4412dc97618dSJ. Bruce Fields unsigned long maxcount;
4413e78e274eSKees Cook struct file *file;
4414dc97618dSJ. Bruce Fields __be32 *p;
4415a2c91753SChuck Lever
4416e78e274eSKees Cook if (nfserr)
4417bddfdbcdSChuck Lever return nfserr;
4418dc97618dSJ. Bruce Fields file = read->rd_nf->nf_file;
4419a2c91753SChuck Lever
4420a2c91753SChuck Lever p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */
4421a2c91753SChuck Lever if (!p) {
4422dc97618dSJ. Bruce Fields WARN_ON_ONCE(splice_ok);
4423dc97618dSJ. Bruce Fields return nfserr_resource;
44245c4583b2SJeff Layton }
44255c4583b2SJeff Layton if (resp->xdr->buf->page_len && splice_ok) {
44265c4583b2SJeff Layton WARN_ON_ONCE(1);
44275c4583b2SJeff Layton return nfserr_serverfault;
4428dc97618dSJ. Bruce Fields }
4429dc97618dSJ. Bruce Fields xdr_commit_encode(xdr);
4430c738b218SChuck Lever
4431bac966d6SJ. Bruce Fields maxcount = min_t(unsigned long, read->rd_length,
4432dc97618dSJ. Bruce Fields (xdr->buf->buflen - xdr->buf->len));
4433c738b218SChuck Lever
4434b0420980SJ. Bruce Fields if (file->f_op->splice_read && splice_ok)
443506981d56SAnna Schumaker nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount);
4436dc97618dSJ. Bruce Fields else
4437dc97618dSJ. Bruce Fields nfserr = nfsd4_encode_readv(resp, read, file, maxcount);
4438dc97618dSJ. Bruce Fields if (nfserr) {
44390cb4d23aSChuck Lever xdr_truncate_encode(xdr, starting_len);
444068e8bb03SChristoph Hellwig return nfserr;
4441dc97618dSJ. Bruce Fields }
4442c738b218SChuck Lever
444396bcad50SChristoph Hellwig p = xdr_encode_bool(p, read->rd_eof);
4444dc97618dSJ. Bruce Fields *p = cpu_to_be32(read->rd_length);
444596bcad50SChristoph Hellwig return nfs_ok;
444628d5bc46SChuck Lever }
4447dc97618dSJ. Bruce Fields
444896bcad50SChristoph Hellwig static __be32
nfsd4_encode_readlink(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)44491da177e4SLinus Torvalds nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr,
44501da177e4SLinus Torvalds union nfsd4_op_u *u)
445128d5bc46SChuck Lever {
445228d5bc46SChuck Lever struct nfsd4_readlink *readlink = &u->readlink;
445328d5bc46SChuck Lever __be32 *p, *maxcount_p, zero = xdr_zero;
445428d5bc46SChuck Lever struct xdr_stream *xdr = resp->xdr;
445528d5bc46SChuck Lever int length_offset = xdr->buf->len;
4456b37ad28bSAl Viro int maxcount, status;
4457e78e274eSKees Cook
4458e78e274eSKees Cook maxcount_p = xdr_reserve_space(xdr, XDR_UNIT);
44591da177e4SLinus Torvalds if (!maxcount_p)
4460e78e274eSKees Cook return nfserr_resource;
446199b002a1SChuck Lever maxcount = PAGE_SIZE;
4462bddfdbcdSChuck Lever
44631fcea5b2SJ. Bruce Fields p = xdr_reserve_space(xdr, maxcount);
446499b002a1SChuck Lever if (!p)
44651da177e4SLinus Torvalds return nfserr_resource;
446699b002a1SChuck Lever /*
446799b002a1SChuck Lever * XXX: By default, vfs_readlink() will truncate symlinks if they
44682825a7f9SJ. Bruce Fields * would overflow the buffer. Is this kosher in NFSv4? If not, one
44691da177e4SLinus Torvalds * easy fix is: if vfs_readlink() precisely fills the buffer, assume
4470d0a381ddSJ. Bruce Fields * that truncation occurred, and return NFS4ERR_RESOURCE.
4471476a7b1fSJ. Bruce Fields */
4472476a7b1fSJ. Bruce Fields nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp,
44734e21ac4bSJ. Bruce Fields (char *)p, &maxcount);
44741da177e4SLinus Torvalds if (nfserr == nfserr_isdir)
4475fd4a0edfSMiklos Szeredi nfserr = nfserr_inval;
4476fd4a0edfSMiklos Szeredi if (nfserr)
4477fd4a0edfSMiklos Szeredi goto out_err;
4478fd4a0edfSMiklos Szeredi status = svc_encode_result_payload(readlink->rl_rqstp, length_offset,
44791da177e4SLinus Torvalds maxcount);
4480476a7b1fSJ. Bruce Fields if (status) {
4481476a7b1fSJ. Bruce Fields nfserr = nfserrno(status);
44821da177e4SLinus Torvalds goto out_err;
4483d3f627c8SJ. Bruce Fields }
448476e5492bSChuck Lever *maxcount_p = cpu_to_be32(maxcount);
448576e5492bSChuck Lever xdr_truncate_encode(xdr, length_offset + 4 + xdr_align_size(maxcount));
448676e5492bSChuck Lever write_bytes_to_xdr_buf(xdr->buf, length_offset + 4 + maxcount, &zero,
448776e5492bSChuck Lever xdr_pad_size(maxcount));
448876e5492bSChuck Lever return nfs_ok;
448976e5492bSChuck Lever
449076e5492bSChuck Lever out_err:
4491d3f627c8SJ. Bruce Fields xdr_truncate_encode(xdr, length_offset);
449299b002a1SChuck Lever return nfserr;
449399b002a1SChuck Lever }
449499b002a1SChuck Lever
nfsd4_encode_dirlist4(struct xdr_stream * xdr,struct nfsd4_readdir * readdir,u32 max_payload)449599b002a1SChuck Lever static __be32 nfsd4_encode_dirlist4(struct xdr_stream *xdr,
449699b002a1SChuck Lever struct nfsd4_readdir *readdir,
449776e5492bSChuck Lever u32 max_payload)
449876e5492bSChuck Lever {
449976e5492bSChuck Lever int bytes_left, maxcount, starting_len = xdr->buf->len;
450076e5492bSChuck Lever loff_t offset;
45011da177e4SLinus Torvalds __be32 status;
45021da177e4SLinus Torvalds
450325c307acSChuck Lever /*
450425c307acSChuck Lever * Number of bytes left for directory entries allowing for the
450525c307acSChuck Lever * final 8 bytes of the readdir and a following failed op.
450625c307acSChuck Lever */
450725c307acSChuck Lever bytes_left = xdr->buf->buflen - xdr->buf->len -
450825c307acSChuck Lever COMPOUND_ERR_SLACK_SPACE - XDR_UNIT * 2;
450925c307acSChuck Lever if (bytes_left < 0)
451025c307acSChuck Lever return nfserr_resource;
451125c307acSChuck Lever maxcount = min_t(u32, readdir->rd_maxcount, max_payload);
451225c307acSChuck Lever
451325c307acSChuck Lever /*
451425c307acSChuck Lever * The RFC defines rd_maxcount as the size of the
451525c307acSChuck Lever * READDIR4resok structure, which includes the verifier
451625c307acSChuck Lever * and the 8 bytes encoded at the end of this function.
451725c307acSChuck Lever */
451825c307acSChuck Lever if (maxcount < XDR_UNIT * 4)
451925c307acSChuck Lever return nfserr_toosmall;
452025c307acSChuck Lever maxcount = min_t(int, maxcount - XDR_UNIT * 4, bytes_left);
452125c307acSChuck Lever
452225c307acSChuck Lever /* RFC 3530 14.2.24 allows us to ignore dircount when it's 0 */
452325c307acSChuck Lever if (!readdir->rd_dircount)
452425c307acSChuck Lever readdir->rd_dircount = max_payload;
452525c307acSChuck Lever
452625c307acSChuck Lever /* *entries */
452725c307acSChuck Lever readdir->xdr = xdr;
452825c307acSChuck Lever readdir->rd_maxcount = maxcount;
452925c307acSChuck Lever readdir->common.err = 0;
453025c307acSChuck Lever readdir->cookie_offset = 0;
453125c307acSChuck Lever offset = readdir->rd_cookie;
453225c307acSChuck Lever status = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp, &offset,
453325c307acSChuck Lever &readdir->common, nfsd4_encode_entry4);
453425c307acSChuck Lever if (status)
453525c307acSChuck Lever return status;
453625c307acSChuck Lever if (readdir->common.err == nfserr_toosmall &&
453725c307acSChuck Lever xdr->buf->len == starting_len) {
453825c307acSChuck Lever /* No entries were encoded. Which limit did we hit? */
453925c307acSChuck Lever if (maxcount - XDR_UNIT * 4 < bytes_left)
454025c307acSChuck Lever /* It was the fault of rd_maxcount */
454125c307acSChuck Lever return nfserr_toosmall;
454225c307acSChuck Lever /* We ran out of buffer space */
454325c307acSChuck Lever return nfserr_resource;
454425c307acSChuck Lever }
454525c307acSChuck Lever /* Encode the final entry's cookie value */
454625c307acSChuck Lever nfsd4_encode_entry4_nfs_cookie4(readdir, offset);
454725c307acSChuck Lever /* No entries follow */
454825c307acSChuck Lever if (xdr_stream_encode_item_absent(xdr) != XDR_UNIT)
454925c307acSChuck Lever return nfserr_resource;
455025c307acSChuck Lever
455125c307acSChuck Lever /* eof */
455225c307acSChuck Lever return nfsd4_encode_bool(xdr, readdir->common.err == nfserr_eof);
455325c307acSChuck Lever }
455425c307acSChuck Lever
455525c307acSChuck Lever static __be32
nfsd4_encode_readdir(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)455625c307acSChuck Lever nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr,
455725c307acSChuck Lever union nfsd4_op_u *u)
455825c307acSChuck Lever {
455925c307acSChuck Lever struct nfsd4_readdir *readdir = &u->readdir;
456025c307acSChuck Lever struct xdr_stream *xdr = resp->xdr;
456125c307acSChuck Lever int starting_len = xdr->buf->len;
456225c307acSChuck Lever
4563b37ad28bSAl Viro /* cookieverf */
4564e78e274eSKees Cook nfserr = nfsd4_encode_verifier4(xdr, &readdir->rd_verf);
4565e78e274eSKees Cook if (nfserr != nfs_ok)
45661da177e4SLinus Torvalds return nfserr;
4567e78e274eSKees Cook
4568bddfdbcdSChuck Lever /* reply */
45691fcea5b2SJ. Bruce Fields nfserr = nfsd4_encode_dirlist4(xdr, readdir, svc_max_payload(resp->rqstp));
45701da177e4SLinus Torvalds if (nfserr != nfs_ok)
457125c307acSChuck Lever xdr_truncate_encode(xdr, starting_len);
4572adaa7a50SChuck Lever return nfserr;
4573adaa7a50SChuck Lever }
4574adaa7a50SChuck Lever
45751da177e4SLinus Torvalds static __be32
nfsd4_encode_remove(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)457625c307acSChuck Lever nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr,
457725c307acSChuck Lever union nfsd4_op_u *u)
457825c307acSChuck Lever {
45791fcea5b2SJ. Bruce Fields struct nfsd4_remove *remove = &u->remove;
45801da177e4SLinus Torvalds struct xdr_stream *xdr = resp->xdr;
45811da177e4SLinus Torvalds
45821da177e4SLinus Torvalds return nfsd4_encode_change_info4(xdr, &remove->rm_cinfo);
4583695e12f8SBenny Halevy }
4584e78e274eSKees Cook
4585e78e274eSKees Cook static __be32
nfsd4_encode_rename(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)45861da177e4SLinus Torvalds nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr,
4587e78e274eSKees Cook union nfsd4_op_u *u)
4588bddfdbcdSChuck Lever {
45891da177e4SLinus Torvalds struct nfsd4_rename *rename = &u->rename;
459066a21db7SChuck Lever struct xdr_stream *xdr = resp->xdr;
45911da177e4SLinus Torvalds
45921da177e4SLinus Torvalds nfserr = nfsd4_encode_change_info4(xdr, &rename->rn_sinfo);
4593695e12f8SBenny Halevy if (nfserr)
4594e78e274eSKees Cook return nfserr;
4595e78e274eSKees Cook return nfsd4_encode_change_info4(xdr, &rename->rn_tinfo);
45961da177e4SLinus Torvalds }
4597e78e274eSKees Cook
4598bddfdbcdSChuck Lever static __be32
nfsd4_encode_rpcsec_gss_info(struct xdr_stream * xdr,struct rpcsec_gss_info * info)45991da177e4SLinus Torvalds nfsd4_encode_rpcsec_gss_info(struct xdr_stream *xdr,
460066a21db7SChuck Lever struct rpcsec_gss_info *info)
460166a21db7SChuck Lever {
460266a21db7SChuck Lever __be32 status;
460366a21db7SChuck Lever
46041da177e4SLinus Torvalds /* oid */
46051da177e4SLinus Torvalds if (xdr_stream_encode_opaque(xdr, info->oid.data, info->oid.len) < 0)
4606695e12f8SBenny Halevy return nfserr_resource;
460791c7a905SChuck Lever /* qop */
460891c7a905SChuck Lever status = nfsd4_encode_qop4(xdr, info->qop);
460991c7a905SChuck Lever if (status != nfs_ok)
461091c7a905SChuck Lever return status;
461191c7a905SChuck Lever /* service */
461291c7a905SChuck Lever if (xdr_stream_encode_u32(xdr, info->service) != XDR_UNIT)
461391c7a905SChuck Lever return nfserr_resource;
461491c7a905SChuck Lever
461591c7a905SChuck Lever return nfs_ok;
461691c7a905SChuck Lever }
461791c7a905SChuck Lever
461891c7a905SChuck Lever static __be32
nfsd4_do_encode_secinfo(struct xdr_stream * xdr,struct svc_export * exp)461991c7a905SChuck Lever nfsd4_do_encode_secinfo(struct xdr_stream *xdr, struct svc_export *exp)
462091c7a905SChuck Lever {
462191c7a905SChuck Lever u32 i, nflavs, supported;
462291c7a905SChuck Lever struct exp_flavor_info *flavs;
462391c7a905SChuck Lever struct exp_flavor_info def_flavs[2];
462491c7a905SChuck Lever static bool report = true;
462591c7a905SChuck Lever __be32 *flavorsp;
462691c7a905SChuck Lever __be32 status;
4627bac966d6SJ. Bruce Fields
4628dcb488a3SAndy Adamson if (exp->ex_nflavors) {
4629676e4ebdSChuck Lever flavs = exp->ex_flavors;
46304796f457SJ. Bruce Fields nflavs = exp->ex_nflavors;
46314796f457SJ. Bruce Fields } else { /* Handling of some defaults in absence of real secinfo: */
4632676e4ebdSChuck Lever flavs = def_flavs;
463391c7a905SChuck Lever if (exp->ex_client->flavour->flavour == RPC_AUTH_UNIX) {
463491c7a905SChuck Lever nflavs = 2;
4635dcb488a3SAndy Adamson flavs[0].pseudoflavor = RPC_AUTH_UNIX;
46364796f457SJ. Bruce Fields flavs[1].pseudoflavor = RPC_AUTH_NULL;
46374796f457SJ. Bruce Fields } else if (exp->ex_client->flavour->flavour == RPC_AUTH_GSS) {
46384796f457SJ. Bruce Fields nflavs = 1;
46394796f457SJ. Bruce Fields flavs[0].pseudoflavor
46404796f457SJ. Bruce Fields = svcauth_gss_flavor(exp->ex_client);
46414796f457SJ. Bruce Fields } else {
46424796f457SJ. Bruce Fields nflavs = 1;
46434796f457SJ. Bruce Fields flavs[0].pseudoflavor
46444796f457SJ. Bruce Fields = exp->ex_client->flavour->flavour;
46454796f457SJ. Bruce Fields }
46464796f457SJ. Bruce Fields }
46474796f457SJ. Bruce Fields
46484796f457SJ. Bruce Fields supported = 0;
46494796f457SJ. Bruce Fields flavorsp = xdr_reserve_space(xdr, XDR_UNIT);
46504796f457SJ. Bruce Fields if (!flavorsp)
46514796f457SJ. Bruce Fields return nfserr_resource;
46524796f457SJ. Bruce Fields
46534796f457SJ. Bruce Fields for (i = 0; i < nflavs; i++) {
46544796f457SJ. Bruce Fields rpc_authflavor_t pf = flavs[i].pseudoflavor;
46554796f457SJ. Bruce Fields struct rpcsec_gss_info info;
4656676e4ebdSChuck Lever
465791c7a905SChuck Lever if (rpcauth_get_gssinfo(pf, &info) == 0) {
465891c7a905SChuck Lever supported++;
4659bac966d6SJ. Bruce Fields
4660676e4ebdSChuck Lever /* flavor */
46614796f457SJ. Bruce Fields status = nfsd4_encode_uint32_t(xdr, RPC_AUTH_GSS);
4662676e4ebdSChuck Lever if (status != nfs_ok)
4663a77c806fSChuck Lever return status;
4664dcb488a3SAndy Adamson /* flavor_info */
4665676e4ebdSChuck Lever status = nfsd4_encode_rpcsec_gss_info(xdr, &info);
4666676e4ebdSChuck Lever if (status != nfs_ok)
466791c7a905SChuck Lever return status;
466891c7a905SChuck Lever } else if (pf < RPC_AUTH_MAXFLAVOR) {
466991c7a905SChuck Lever supported++;
467091c7a905SChuck Lever
467191c7a905SChuck Lever /* flavor */
467291c7a905SChuck Lever status = nfsd4_encode_uint32_t(xdr, pf);
467391c7a905SChuck Lever if (status != nfs_ok)
467491c7a905SChuck Lever return status;
467591c7a905SChuck Lever } else {
4676676e4ebdSChuck Lever if (report)
4677676e4ebdSChuck Lever pr_warn("NFS: SECINFO: security flavor %u "
467891c7a905SChuck Lever "is not supported\n", pf);
467991c7a905SChuck Lever }
468091c7a905SChuck Lever }
468191c7a905SChuck Lever
468291c7a905SChuck Lever if (nflavs != supported)
4683676e4ebdSChuck Lever report = false;
4684676e4ebdSChuck Lever *flavorsp = cpu_to_be32(supported);
4685676e4ebdSChuck Lever return 0;
4686676e4ebdSChuck Lever }
4687dcb488a3SAndy Adamson
4688dcb488a3SAndy Adamson static __be32
nfsd4_encode_secinfo(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4689a77c806fSChuck Lever nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
4690676e4ebdSChuck Lever union nfsd4_op_u *u)
4691676e4ebdSChuck Lever {
469291c7a905SChuck Lever struct nfsd4_secinfo *secinfo = &u->secinfo;
4693bac966d6SJ. Bruce Fields struct xdr_stream *xdr = resp->xdr;
4694dcb488a3SAndy Adamson
4695dcb488a3SAndy Adamson return nfsd4_do_encode_secinfo(xdr, secinfo->si_exp);
469622b6dee8SMi Jinlong }
469722b6dee8SMi Jinlong
4698e78e274eSKees Cook static __be32
nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)469922b6dee8SMi Jinlong nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr,
4700e78e274eSKees Cook union nfsd4_op_u *u)
4701bddfdbcdSChuck Lever {
4702d0a381ddSJ. Bruce Fields struct nfsd4_secinfo_no_name *secinfo = &u->secinfo_no_name;
4703bac966d6SJ. Bruce Fields struct xdr_stream *xdr = resp->xdr;
470422b6dee8SMi Jinlong
470522b6dee8SMi Jinlong return nfsd4_do_encode_secinfo(xdr, secinfo->sin_exp);
470622b6dee8SMi Jinlong }
470722b6dee8SMi Jinlong
4708e78e274eSKees Cook static __be32
nfsd4_encode_setattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)470922b6dee8SMi Jinlong nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr,
4710e78e274eSKees Cook union nfsd4_op_u *u)
4711bddfdbcdSChuck Lever {
4712d0a381ddSJ. Bruce Fields struct nfsd4_setattr *setattr = &u->setattr;
4713bac966d6SJ. Bruce Fields __be32 status;
471422b6dee8SMi Jinlong
471522b6dee8SMi Jinlong switch (nfserr) {
4716695e12f8SBenny Halevy case nfs_ok:
4717e78e274eSKees Cook /* attrsset */
4718e78e274eSKees Cook status = nfsd4_encode_bitmap4(resp->xdr, setattr->sa_bmval[0],
47191da177e4SLinus Torvalds setattr->sa_bmval[1],
4720e78e274eSKees Cook setattr->sa_bmval[2]);
4721c3dcb45bSChuck Lever break;
47221da177e4SLinus Torvalds default:
4723c3dcb45bSChuck Lever /* attrsset */
4724c3dcb45bSChuck Lever status = nfsd4_encode_bitmap4(resp->xdr, 0, 0, 0);
4725c3dcb45bSChuck Lever }
4726c3dcb45bSChuck Lever return status != nfs_ok ? status : nfserr;
4727c3dcb45bSChuck Lever }
4728c3dcb45bSChuck Lever
4729c3dcb45bSChuck Lever static __be32
nfsd4_encode_setclientid(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4730c3dcb45bSChuck Lever nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr,
4731c3dcb45bSChuck Lever union nfsd4_op_u *u)
4732c3dcb45bSChuck Lever {
47331da177e4SLinus Torvalds struct nfsd4_setclientid *scd = &u->setclientid;
4734c3dcb45bSChuck Lever struct xdr_stream *xdr = resp->xdr;
47351da177e4SLinus Torvalds
47361da177e4SLinus Torvalds if (!nfserr) {
4737695e12f8SBenny Halevy nfserr = nfsd4_encode_clientid4(xdr, &scd->se_clientid);
4738e78e274eSKees Cook if (nfserr != nfs_ok)
4739e78e274eSKees Cook goto out;
47401da177e4SLinus Torvalds nfserr = nfsd4_encode_verifier4(xdr, &scd->se_confirm);
4741e78e274eSKees Cook } else if (nfserr == nfserr_clid_inuse) {
4742bddfdbcdSChuck Lever /* empty network id */
47431da177e4SLinus Torvalds if (xdr_stream_encode_u32(xdr, 0) < 0) {
47441da177e4SLinus Torvalds nfserr = nfserr_resource;
4745adaa7a50SChuck Lever goto out;
4746adaa7a50SChuck Lever }
4747adaa7a50SChuck Lever /* empty universal address */
4748adaa7a50SChuck Lever if (xdr_stream_encode_u32(xdr, 0) < 0) {
4749adaa7a50SChuck Lever nfserr = nfserr_resource;
4750adaa7a50SChuck Lever goto out;
4751adaa7a50SChuck Lever }
4752adaa7a50SChuck Lever }
4753adaa7a50SChuck Lever out:
47541da177e4SLinus Torvalds return nfserr;
4755adaa7a50SChuck Lever }
4756adaa7a50SChuck Lever
4757adaa7a50SChuck Lever static __be32
nfsd4_encode_write(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4758adaa7a50SChuck Lever nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr,
47591da177e4SLinus Torvalds union nfsd4_op_u *u)
4760adaa7a50SChuck Lever {
4761adaa7a50SChuck Lever struct nfsd4_write *write = &u->write;
4762695e12f8SBenny Halevy struct xdr_stream *xdr = resp->xdr;
47631da177e4SLinus Torvalds
47641da177e4SLinus Torvalds /* count */
4765695e12f8SBenny Halevy nfserr = nfsd4_encode_count4(xdr, write->wr_bytes_written);
4766e78e274eSKees Cook if (nfserr)
4767e78e274eSKees Cook return nfserr;
47681da177e4SLinus Torvalds /* committed */
4769e78e274eSKees Cook if (xdr_stream_encode_u32(xdr, write->wr_how_written) != XDR_UNIT)
477076bebcc7SChuck Lever return nfserr_resource;
47711da177e4SLinus Torvalds /* writeverf */
477276bebcc7SChuck Lever return nfsd4_encode_verifier4(xdr, &write->wr_verifier);
477376bebcc7SChuck Lever }
477476bebcc7SChuck Lever
477576bebcc7SChuck Lever static __be32
nfsd4_encode_state_protect_ops4(struct xdr_stream * xdr,struct nfsd4_exchange_id * exid)477676bebcc7SChuck Lever nfsd4_encode_state_protect_ops4(struct xdr_stream *xdr,
477776bebcc7SChuck Lever struct nfsd4_exchange_id *exid)
4778d0a381ddSJ. Bruce Fields {
477976bebcc7SChuck Lever __be32 status;
478076bebcc7SChuck Lever
47811da177e4SLinus Torvalds /* spo_must_enforce */
47821da177e4SLinus Torvalds status = nfsd4_encode_bitmap4(xdr, exid->spo_must_enforce[0],
4783695e12f8SBenny Halevy exid->spo_must_enforce[1],
4784abef972cSChuck Lever exid->spo_must_enforce[2]);
4785abef972cSChuck Lever if (status != nfs_ok)
47862db134ebSAndy Adamson return status;
4787abef972cSChuck Lever /* spo_must_allow */
47880733d213SAndy Adamson return nfsd4_encode_bitmap4(xdr, exid->spo_must_allow[0],
4789abef972cSChuck Lever exid->spo_must_allow[1],
4790abef972cSChuck Lever exid->spo_must_allow[2]);
4791abef972cSChuck Lever }
4792abef972cSChuck Lever
4793abef972cSChuck Lever static __be32
nfsd4_encode_state_protect4_r(struct xdr_stream * xdr,struct nfsd4_exchange_id * exid)4794abef972cSChuck Lever nfsd4_encode_state_protect4_r(struct xdr_stream *xdr, struct nfsd4_exchange_id *exid)
4795abef972cSChuck Lever {
4796abef972cSChuck Lever __be32 status;
4797abef972cSChuck Lever
4798abef972cSChuck Lever if (xdr_stream_encode_u32(xdr, exid->spa_how) != XDR_UNIT)
4799abef972cSChuck Lever return nfserr_resource;
48000733d213SAndy Adamson switch (exid->spa_how) {
4801abef972cSChuck Lever case SP4_NONE:
4802abef972cSChuck Lever status = nfs_ok;
4803abef972cSChuck Lever break;
4804abef972cSChuck Lever case SP4_MACH_CRED:
48050733d213SAndy Adamson /* spr_mach_ops */
4806abef972cSChuck Lever status = nfsd4_encode_state_protect_ops4(xdr, exid);
4807adaa7a50SChuck Lever break;
480857266a6eSJ. Bruce Fields default:
480957266a6eSJ. Bruce Fields status = nfserr_serverfault;
4810abef972cSChuck Lever }
481157266a6eSJ. Bruce Fields return status;
481257266a6eSJ. Bruce Fields }
4813abef972cSChuck Lever
4814abef972cSChuck Lever static __be32
nfsd4_encode_server_owner4(struct xdr_stream * xdr,struct svc_rqst * rqstp)481557266a6eSJ. Bruce Fields nfsd4_encode_server_owner4(struct xdr_stream *xdr, struct svc_rqst *rqstp)
481657266a6eSJ. Bruce Fields {
4817abef972cSChuck Lever struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
4818abef972cSChuck Lever __be32 status;
4819abef972cSChuck Lever
482057266a6eSJ. Bruce Fields /* so_minor_id */
48210733d213SAndy Adamson status = nfsd4_encode_uint64_t(xdr, 0);
4822abef972cSChuck Lever if (status != nfs_ok)
4823abef972cSChuck Lever return status;
4824abef972cSChuck Lever /* so_major_id */
4825abef972cSChuck Lever return nfsd4_encode_opaque(xdr, nn->nfsd_name, strlen(nn->nfsd_name));
4826abef972cSChuck Lever }
4827abef972cSChuck Lever
4828abef972cSChuck Lever static __be32
nfsd4_encode_exchange_id(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4829abef972cSChuck Lever nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
4830abef972cSChuck Lever union nfsd4_op_u *u)
4831abef972cSChuck Lever {
4832abef972cSChuck Lever struct nfsd_net *nn = net_generic(SVC_NET(resp->rqstp), nfsd_net_id);
4833abef972cSChuck Lever struct nfsd4_exchange_id *exid = &u->exchange_id;
4834abef972cSChuck Lever struct xdr_stream *xdr = resp->xdr;
4835abef972cSChuck Lever
4836abef972cSChuck Lever /* eir_clientid */
4837abef972cSChuck Lever nfserr = nfsd4_encode_clientid4(xdr, &exid->clientid);
4838abef972cSChuck Lever if (nfserr != nfs_ok)
4839abef972cSChuck Lever return nfserr;
4840abef972cSChuck Lever /* eir_sequenceid */
4841abef972cSChuck Lever nfserr = nfsd4_encode_sequenceid4(xdr, exid->seqid);
4842abef972cSChuck Lever if (nfserr != nfs_ok)
4843abef972cSChuck Lever return nfserr;
4844abef972cSChuck Lever /* eir_flags */
4845abef972cSChuck Lever nfserr = nfsd4_encode_uint32_t(xdr, exid->flags);
4846abef972cSChuck Lever if (nfserr != nfs_ok)
4847abef972cSChuck Lever return nfserr;
4848abef972cSChuck Lever /* eir_state_protect */
4849abef972cSChuck Lever nfserr = nfsd4_encode_state_protect4_r(xdr, exid);
4850abef972cSChuck Lever if (nfserr != nfs_ok)
4851abef972cSChuck Lever return nfserr;
4852abef972cSChuck Lever /* eir_server_owner */
4853abef972cSChuck Lever nfserr = nfsd4_encode_server_owner4(xdr, resp->rqstp);
4854abef972cSChuck Lever if (nfserr != nfs_ok)
4855abef972cSChuck Lever return nfserr;
4856abef972cSChuck Lever /* eir_server_scope */
4857abef972cSChuck Lever nfserr = nfsd4_encode_opaque(xdr, nn->nfsd_name,
4858abef972cSChuck Lever strlen(nn->nfsd_name));
4859abef972cSChuck Lever if (nfserr != nfs_ok)
4860abef972cSChuck Lever return nfserr;
4861abef972cSChuck Lever /* eir_server_impl_id<1> */
4862abef972cSChuck Lever if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
4863abef972cSChuck Lever return nfserr_resource;
4864abef972cSChuck Lever
4865abef972cSChuck Lever return nfs_ok;
4866abef972cSChuck Lever }
4867abef972cSChuck Lever
4868abef972cSChuck Lever static __be32
nfsd4_encode_channel_attrs4(struct xdr_stream * xdr,const struct nfsd4_channel_attrs * attrs)4869abef972cSChuck Lever nfsd4_encode_channel_attrs4(struct xdr_stream *xdr,
4870abef972cSChuck Lever const struct nfsd4_channel_attrs *attrs)
4871d0a381ddSJ. Bruce Fields {
4872a8bb84bcSKinglong Mee __be32 status;
4873abef972cSChuck Lever
48742db134ebSAndy Adamson /* ca_headerpadsize */
48752db134ebSAndy Adamson status = nfsd4_encode_count4(xdr, 0);
48762db134ebSAndy Adamson if (status != nfs_ok)
4877150990f4SChuck Lever return status;
4878150990f4SChuck Lever /* ca_maxrequestsize */
4879150990f4SChuck Lever status = nfsd4_encode_count4(xdr, attrs->maxreq_sz);
4880150990f4SChuck Lever if (status != nfs_ok)
4881150990f4SChuck Lever return status;
4882150990f4SChuck Lever /* ca_maxresponsesize */
4883150990f4SChuck Lever status = nfsd4_encode_count4(xdr, attrs->maxresp_sz);
4884150990f4SChuck Lever if (status != nfs_ok)
4885150990f4SChuck Lever return status;
4886150990f4SChuck Lever /* ca_maxresponsesize_cached */
4887150990f4SChuck Lever status = nfsd4_encode_count4(xdr, attrs->maxresp_cached);
4888150990f4SChuck Lever if (status != nfs_ok)
4889150990f4SChuck Lever return status;
4890150990f4SChuck Lever /* ca_maxoperations */
4891150990f4SChuck Lever status = nfsd4_encode_count4(xdr, attrs->maxops);
4892150990f4SChuck Lever if (status != nfs_ok)
4893150990f4SChuck Lever return status;
4894150990f4SChuck Lever /* ca_maxrequests */
4895150990f4SChuck Lever status = nfsd4_encode_count4(xdr, attrs->maxreqs);
4896150990f4SChuck Lever if (status != nfs_ok)
4897150990f4SChuck Lever return status;
4898150990f4SChuck Lever /* ca_rdma_ird<1> */
4899150990f4SChuck Lever if (xdr_stream_encode_u32(xdr, attrs->nr_rdma_attrs) != XDR_UNIT)
4900150990f4SChuck Lever return nfserr_resource;
4901150990f4SChuck Lever if (attrs->nr_rdma_attrs)
4902150990f4SChuck Lever return nfsd4_encode_uint32_t(xdr, attrs->rdma_attrs);
4903150990f4SChuck Lever return nfs_ok;
4904150990f4SChuck Lever }
4905150990f4SChuck Lever
4906150990f4SChuck Lever static __be32
nfsd4_encode_create_session(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4907150990f4SChuck Lever nfsd4_encode_create_session(struct nfsd4_compoundres *resp, __be32 nfserr,
4908150990f4SChuck Lever union nfsd4_op_u *u)
4909150990f4SChuck Lever {
4910150990f4SChuck Lever struct nfsd4_create_session *sess = &u->create_session;
4911150990f4SChuck Lever struct xdr_stream *xdr = resp->xdr;
4912150990f4SChuck Lever
4913150990f4SChuck Lever /* csr_sessionid */
4914150990f4SChuck Lever nfserr = nfsd4_encode_sessionid4(xdr, &sess->sessionid);
491557b7b43bSJ. Bruce Fields if (nfserr != nfs_ok)
4916e78e274eSKees Cook return nfserr;
49172db134ebSAndy Adamson /* csr_sequence */
4918e78e274eSKees Cook nfserr = nfsd4_encode_sequenceid4(xdr, sess->seqid);
4919bddfdbcdSChuck Lever if (nfserr != nfs_ok)
4920ec6b5d7bSAndy Adamson return nfserr;
4921b0c1b1baSChuck Lever /* csr_flags */
4922b0c1b1baSChuck Lever nfserr = nfsd4_encode_uint32_t(xdr, sess->flags);
4923b0c1b1baSChuck Lever if (nfserr != nfs_ok)
4924b0c1b1baSChuck Lever return nfserr;
4925b0c1b1baSChuck Lever /* csr_fore_chan_attrs */
4926b0c1b1baSChuck Lever nfserr = nfsd4_encode_channel_attrs4(xdr, &sess->fore_channel);
4927b0c1b1baSChuck Lever if (nfserr != nfs_ok)
4928b0c1b1baSChuck Lever return nfserr;
4929b0c1b1baSChuck Lever /* csr_back_chan_attrs */
4930b0c1b1baSChuck Lever return nfsd4_encode_channel_attrs4(xdr, &sess->back_channel);
4931b0c1b1baSChuck Lever }
4932b0c1b1baSChuck Lever
4933150990f4SChuck Lever static __be32
nfsd4_encode_sequence(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4934150990f4SChuck Lever nfsd4_encode_sequence(struct nfsd4_compoundres *resp, __be32 nfserr,
4935150990f4SChuck Lever union nfsd4_op_u *u)
4936150990f4SChuck Lever {
4937150990f4SChuck Lever struct nfsd4_sequence *seq = &u->sequence;
4938150990f4SChuck Lever struct xdr_stream *xdr = resp->xdr;
49392db134ebSAndy Adamson
49402db134ebSAndy Adamson /* sr_sessionid */
49412db134ebSAndy Adamson nfserr = nfsd4_encode_sessionid4(xdr, &seq->sessionid);
494257b7b43bSJ. Bruce Fields if (nfserr != nfs_ok)
4943e78e274eSKees Cook return nfserr;
49442db134ebSAndy Adamson /* sr_sequenceid */
4945e78e274eSKees Cook nfserr = nfsd4_encode_sequenceid4(xdr, seq->seqid);
4946bddfdbcdSChuck Lever if (nfserr != nfs_ok)
4947b85d4c01SBenny Halevy return nfserr;
49486621b88bSChuck Lever /* sr_slotid */
49496621b88bSChuck Lever nfserr = nfsd4_encode_slotid4(xdr, seq->slotid);
49506621b88bSChuck Lever if (nfserr != nfs_ok)
49516621b88bSChuck Lever return nfserr;
49526621b88bSChuck Lever /* Note slotid's are numbered from zero: */
49536621b88bSChuck Lever /* sr_highest_slotid */
49546621b88bSChuck Lever nfserr = nfsd4_encode_slotid4(xdr, seq->maxslots - 1);
49556621b88bSChuck Lever if (nfserr != nfs_ok)
49566621b88bSChuck Lever return nfserr;
49576621b88bSChuck Lever /* sr_target_highest_slotid */
49586621b88bSChuck Lever nfserr = nfsd4_encode_slotid4(xdr, seq->maxslots - 1);
49596621b88bSChuck Lever if (nfserr != nfs_ok)
4960b7d7ca35SJ. Bruce Fields return nfserr;
49616621b88bSChuck Lever /* sr_status_flags */
49626621b88bSChuck Lever nfserr = nfsd4_encode_uint32_t(xdr, seq->status_flags);
49636621b88bSChuck Lever if (nfserr != nfs_ok)
49646621b88bSChuck Lever return nfserr;
49656621b88bSChuck Lever
49666621b88bSChuck Lever resp->cstate.data_offset = xdr->buf->len; /* DRC cache data pointer */
49676621b88bSChuck Lever return nfs_ok;
49686621b88bSChuck Lever }
49696621b88bSChuck Lever
49706621b88bSChuck Lever static __be32
nfsd4_encode_test_stateid(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)49716621b88bSChuck Lever nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, __be32 nfserr,
49726621b88bSChuck Lever union nfsd4_op_u *u)
4973b85d4c01SBenny Halevy {
4974f5236013SJ. Bruce Fields struct nfsd4_test_stateid *test_stateid = &u->test_stateid;
49756621b88bSChuck Lever struct nfsd4_test_stateid_id *stateid, *next;
49762db134ebSAndy Adamson struct xdr_stream *xdr = resp->xdr;
49772db134ebSAndy Adamson
49782355c596SJ. Bruce Fields /* tsr_status_codes<> */
497957b7b43bSJ. Bruce Fields if (xdr_stream_encode_u32(xdr, test_stateid->ts_num_ids) != XDR_UNIT)
4980e78e274eSKees Cook return nfserr_resource;
498117456804SBryan Schumaker list_for_each_entry_safe(stateid, next,
4982e78e274eSKees Cook &test_stateid->ts_stateid_list, ts_id_list) {
498303cfb420SBryan Schumaker if (xdr_stream_encode_be32(xdr, stateid->ts_id_status) != XDR_UNIT)
498408b4436aSChuck Lever return nfserr_resource;
498517456804SBryan Schumaker }
498608b4436aSChuck Lever return nfs_ok;
498708b4436aSChuck Lever }
4988d0a381ddSJ. Bruce Fields
498908b4436aSChuck Lever static __be32
nfsd4_encode_get_dir_delegation(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)499008b4436aSChuck Lever nfsd4_encode_get_dir_delegation(struct nfsd4_compoundres *resp, __be32 nfserr,
499108b4436aSChuck Lever union nfsd4_op_u *u)
499208b4436aSChuck Lever {
499317456804SBryan Schumaker struct nfsd4_get_dir_delegation *gdd = &u->get_dir_delegation;
499408b4436aSChuck Lever struct xdr_stream *xdr = resp->xdr;
499517456804SBryan Schumaker __be32 status = nfserr_resource;
499617456804SBryan Schumaker
499733a1e6eaSJeff Layton switch(gdd->gddrnf_status) {
499833a1e6eaSJeff Layton case GDD4_OK:
499933a1e6eaSJeff Layton if (xdr_stream_encode_u32(xdr, GDD4_OK) != XDR_UNIT)
500033a1e6eaSJeff Layton break;
500133a1e6eaSJeff Layton status = nfsd4_encode_verifier4(xdr, &gdd->gddr_cookieverf);
500233a1e6eaSJeff Layton if (status)
500333a1e6eaSJeff Layton break;
500433a1e6eaSJeff Layton status = nfsd4_encode_stateid4(xdr, &gdd->gddr_stateid);
500533a1e6eaSJeff Layton if (status)
500633a1e6eaSJeff Layton break;
500733a1e6eaSJeff Layton status = nfsd4_encode_bitmap4(xdr, gdd->gddr_notification[0], 0, 0);
500833a1e6eaSJeff Layton if (status)
500933a1e6eaSJeff Layton break;
501033a1e6eaSJeff Layton status = nfsd4_encode_bitmap4(xdr, gdd->gddr_child_attributes[0],
501133a1e6eaSJeff Layton gdd->gddr_child_attributes[1],
501233a1e6eaSJeff Layton gdd->gddr_child_attributes[2]);
501333a1e6eaSJeff Layton if (status)
501433a1e6eaSJeff Layton break;
501533a1e6eaSJeff Layton status = nfsd4_encode_bitmap4(xdr, gdd->gddr_dir_attributes[0],
501633a1e6eaSJeff Layton gdd->gddr_dir_attributes[1],
501733a1e6eaSJeff Layton gdd->gddr_dir_attributes[2]);
501833a1e6eaSJeff Layton break;
501933a1e6eaSJeff Layton default:
502033a1e6eaSJeff Layton pr_warn("nfsd: bad gddrnf_status (%u)\n", gdd->gddrnf_status);
502133a1e6eaSJeff Layton gdd->gddrnf_will_signal_deleg_avail = 0;
502233a1e6eaSJeff Layton fallthrough;
502333a1e6eaSJeff Layton case GDD4_UNAVAIL:
502433a1e6eaSJeff Layton if (xdr_stream_encode_u32(xdr, GDD4_UNAVAIL) != XDR_UNIT)
502533a1e6eaSJeff Layton break;
502633a1e6eaSJeff Layton status = nfsd4_encode_bool(xdr, gdd->gddrnf_will_signal_deleg_avail);
502733a1e6eaSJeff Layton break;
502833a1e6eaSJeff Layton }
502933a1e6eaSJeff Layton return status;
503033a1e6eaSJeff Layton }
503133a1e6eaSJeff Layton
503233a1e6eaSJeff Layton #ifdef CONFIG_NFSD_PNFS
503333a1e6eaSJeff Layton static __be32
nfsd4_encode_device_addr4(struct xdr_stream * xdr,const struct nfsd4_getdeviceinfo * gdev)503433a1e6eaSJeff Layton nfsd4_encode_device_addr4(struct xdr_stream *xdr,
503533a1e6eaSJeff Layton const struct nfsd4_getdeviceinfo *gdev)
503633a1e6eaSJeff Layton {
503733a1e6eaSJeff Layton u32 needed_len, starting_len = xdr->buf->len;
503833a1e6eaSJeff Layton const struct nfsd4_layout_ops *ops;
503933a1e6eaSJeff Layton __be32 status;
50409cf514ccSChristoph Hellwig
50419cf514ccSChristoph Hellwig /* da_layout_type */
50424bbe42e8SChuck Lever if (xdr_stream_encode_u32(xdr, gdev->gd_layout_type) != XDR_UNIT)
50434bbe42e8SChuck Lever return nfserr_resource;
50444bbe42e8SChuck Lever /* da_addr_body */
50454bbe42e8SChuck Lever ops = nfsd4_layout_ops[gdev->gd_layout_type];
50464bbe42e8SChuck Lever status = ops->encode_getdeviceinfo(xdr, gdev);
50474bbe42e8SChuck Lever if (status != nfs_ok) {
50484bbe42e8SChuck Lever /*
50494bbe42e8SChuck Lever * Don't burden the layout drivers with enforcing
50504bbe42e8SChuck Lever * gd_maxcount. Just tell the client to come back
50514bbe42e8SChuck Lever * with a bigger buffer if it's not enough.
50524bbe42e8SChuck Lever */
50534bbe42e8SChuck Lever if (xdr->buf->len + XDR_UNIT > gdev->gd_maxcount)
50544bbe42e8SChuck Lever goto toosmall;
50554bbe42e8SChuck Lever return status;
50564bbe42e8SChuck Lever }
50574bbe42e8SChuck Lever
50584bbe42e8SChuck Lever return nfs_ok;
50594bbe42e8SChuck Lever
50604bbe42e8SChuck Lever toosmall:
50614bbe42e8SChuck Lever needed_len = xdr->buf->len + XDR_UNIT; /* notifications */
50624bbe42e8SChuck Lever xdr_truncate_encode(xdr, starting_len);
50634bbe42e8SChuck Lever
50644bbe42e8SChuck Lever status = nfsd4_encode_count4(xdr, needed_len);
50654bbe42e8SChuck Lever if (status != nfs_ok)
50664bbe42e8SChuck Lever return status;
50674bbe42e8SChuck Lever return nfserr_toosmall;
50684bbe42e8SChuck Lever }
50694bbe42e8SChuck Lever
50704bbe42e8SChuck Lever static __be32
nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)50714bbe42e8SChuck Lever nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
50724bbe42e8SChuck Lever union nfsd4_op_u *u)
50734bbe42e8SChuck Lever {
50744bbe42e8SChuck Lever struct nfsd4_getdeviceinfo *gdev = &u->getdeviceinfo;
50754bbe42e8SChuck Lever struct xdr_stream *xdr = resp->xdr;
50764bbe42e8SChuck Lever
50774bbe42e8SChuck Lever /* gdir_device_addr */
50784bbe42e8SChuck Lever nfserr = nfsd4_encode_device_addr4(xdr, gdev);
50799cf514ccSChristoph Hellwig if (nfserr)
5080e78e274eSKees Cook return nfserr;
50819cf514ccSChristoph Hellwig /* gdir_notification */
5082e78e274eSKees Cook return nfsd4_encode_bitmap4(xdr, gdev->gd_notify_types, 0, 0);
5083bddfdbcdSChuck Lever }
50849cf514ccSChristoph Hellwig
50854bbe42e8SChuck Lever static __be32
nfsd4_encode_layout4(struct xdr_stream * xdr,const struct nfsd4_layoutget * lgp)50864bbe42e8SChuck Lever nfsd4_encode_layout4(struct xdr_stream *xdr, const struct nfsd4_layoutget *lgp)
50874bbe42e8SChuck Lever {
5088bac966d6SJ. Bruce Fields const struct nfsd4_layout_ops *ops = nfsd4_layout_ops[lgp->lg_layout_type];
50894bbe42e8SChuck Lever __be32 status;
50904bbe42e8SChuck Lever
50919cf514ccSChristoph Hellwig /* lo_offset */
50929cf514ccSChristoph Hellwig status = nfsd4_encode_offset4(xdr, lgp->lg_seg.offset);
50939cf514ccSChristoph Hellwig if (status != nfs_ok)
509469f5f019SChuck Lever return status;
509569f5f019SChuck Lever /* lo_length */
509669f5f019SChuck Lever status = nfsd4_encode_length4(xdr, lgp->lg_seg.length);
509769f5f019SChuck Lever if (status != nfs_ok)
509869f5f019SChuck Lever return status;
509969f5f019SChuck Lever /* lo_iomode */
510069f5f019SChuck Lever if (xdr_stream_encode_u32(xdr, lgp->lg_seg.iomode) != XDR_UNIT)
510169f5f019SChuck Lever return nfserr_resource;
510269f5f019SChuck Lever /* lo_content */
510369f5f019SChuck Lever if (xdr_stream_encode_u32(xdr, lgp->lg_layout_type) != XDR_UNIT)
510469f5f019SChuck Lever return nfserr_resource;
510569f5f019SChuck Lever return ops->encode_layoutget(xdr, lgp);
510669f5f019SChuck Lever }
510769f5f019SChuck Lever
510869f5f019SChuck Lever static __be32
nfsd4_encode_layoutget(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)510969f5f019SChuck Lever nfsd4_encode_layoutget(struct nfsd4_compoundres *resp, __be32 nfserr,
511069f5f019SChuck Lever union nfsd4_op_u *u)
511169f5f019SChuck Lever {
511269f5f019SChuck Lever struct nfsd4_layoutget *lgp = &u->layoutget;
511369f5f019SChuck Lever struct xdr_stream *xdr = resp->xdr;
511469f5f019SChuck Lever
511569f5f019SChuck Lever /* logr_return_on_close */
511669f5f019SChuck Lever nfserr = nfsd4_encode_bool(xdr, true);
51179cf514ccSChristoph Hellwig if (nfserr != nfs_ok)
5118e78e274eSKees Cook return nfserr;
51199cf514ccSChristoph Hellwig /* logr_stateid */
5120e78e274eSKees Cook nfserr = nfsd4_encode_stateid4(xdr, &lgp->lg_sid);
5121bddfdbcdSChuck Lever if (nfserr != nfs_ok)
51229cf514ccSChristoph Hellwig return nfserr;
512369f5f019SChuck Lever /* logr_layout<> */
512469f5f019SChuck Lever if (xdr_stream_encode_u32(xdr, 1) != XDR_UNIT)
512569f5f019SChuck Lever return nfserr_resource;
512669f5f019SChuck Lever return nfsd4_encode_layout4(xdr, lgp);
512769f5f019SChuck Lever }
512869f5f019SChuck Lever
512969f5f019SChuck Lever static __be32
nfsd4_encode_layoutcommit(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)513069f5f019SChuck Lever nfsd4_encode_layoutcommit(struct nfsd4_compoundres *resp, __be32 nfserr,
513169f5f019SChuck Lever union nfsd4_op_u *u)
513269f5f019SChuck Lever {
5133bac966d6SJ. Bruce Fields struct nfsd4_layoutcommit *lcp = &u->layoutcommit;
513469f5f019SChuck Lever struct xdr_stream *xdr = resp->xdr;
51359cf514ccSChristoph Hellwig
51369cf514ccSChristoph Hellwig /* ns_sizechanged */
51379cf514ccSChristoph Hellwig nfserr = nfsd4_encode_bool(xdr, lcp->lc_size_chg);
51389cf514ccSChristoph Hellwig if (nfserr != nfs_ok)
5139e78e274eSKees Cook return nfserr;
51409cf514ccSChristoph Hellwig if (lcp->lc_size_chg)
5141e78e274eSKees Cook /* ns_size */
5142bddfdbcdSChuck Lever return nfsd4_encode_length4(xdr, lcp->lc_newsize);
51439cf514ccSChristoph Hellwig return nfs_ok;
5144cc313f80SChuck Lever }
5145cc313f80SChuck Lever
5146cc313f80SChuck Lever static __be32
nfsd4_encode_layoutreturn(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)5147cc313f80SChuck Lever nfsd4_encode_layoutreturn(struct nfsd4_compoundres *resp, __be32 nfserr,
5148cc313f80SChuck Lever union nfsd4_op_u *u)
5149cc313f80SChuck Lever {
5150cc313f80SChuck Lever struct nfsd4_layoutreturn *lrp = &u->layoutreturn;
5151cc313f80SChuck Lever struct xdr_stream *xdr = resp->xdr;
51529cf514ccSChristoph Hellwig
51539cf514ccSChristoph Hellwig /* lrs_present */
51549cf514ccSChristoph Hellwig nfserr = nfsd4_encode_bool(xdr, lrp->lrs_present);
51559cf514ccSChristoph Hellwig if (nfserr != nfs_ok)
5156e78e274eSKees Cook return nfserr;
51579cf514ccSChristoph Hellwig if (lrp->lrs_present)
5158e78e274eSKees Cook /* lrs_stateid */
5159bddfdbcdSChuck Lever return nfsd4_encode_stateid4(xdr, &lrp->lr_sid);
51609cf514ccSChristoph Hellwig return nfs_ok;
516185dbc978SChuck Lever }
516285dbc978SChuck Lever #endif /* CONFIG_NFSD_PNFS */
516385dbc978SChuck Lever
516485dbc978SChuck Lever static __be32
nfsd4_encode_write_response4(struct xdr_stream * xdr,const struct nfsd4_copy * copy)51659cf514ccSChristoph Hellwig nfsd4_encode_write_response4(struct xdr_stream *xdr,
516685dbc978SChuck Lever const struct nfsd4_copy *copy)
516740bb2baaSChuck Lever {
516885dbc978SChuck Lever const struct nfsd42_write_res *write = ©->cp_res;
51699cf514ccSChristoph Hellwig u32 count = nfsd4_copy_is_sync(copy) ? 0 : 1;
51709cf514ccSChristoph Hellwig __be32 status;
51719cf514ccSChristoph Hellwig
51722db134ebSAndy Adamson /* wr_callback_id<1> */
517302e0297fSChuck Lever if (xdr_stream_encode_u32(xdr, count) != XDR_UNIT)
517402e0297fSChuck Lever return nfserr_resource;
517529ae7f9dSAnna Schumaker if (count) {
517602e0297fSChuck Lever status = nfsd4_encode_stateid4(xdr, &write->cb_stateid);
517702e0297fSChuck Lever if (status != nfs_ok)
517802e0297fSChuck Lever return status;
517929ae7f9dSAnna Schumaker }
518002e0297fSChuck Lever
518102e0297fSChuck Lever /* wr_count */
518202e0297fSChuck Lever status = nfsd4_encode_length4(xdr, write->wr_bytes_written);
518302e0297fSChuck Lever if (status != nfs_ok)
518402e0297fSChuck Lever return status;
518502e0297fSChuck Lever /* wr_committed */
518602e0297fSChuck Lever if (xdr_stream_encode_u32(xdr, write->wr_stable_how) != XDR_UNIT)
5187e0639dc5SOlga Kornievskaia return nfserr_resource;
5188e0639dc5SOlga Kornievskaia /* wr_writeverf */
518902e0297fSChuck Lever return nfsd4_encode_verifier4(xdr, &write->wr_verifier);
519002e0297fSChuck Lever }
519102e0297fSChuck Lever
nfsd4_encode_copy_requirements4(struct xdr_stream * xdr,const struct nfsd4_copy * copy)519202e0297fSChuck Lever static __be32 nfsd4_encode_copy_requirements4(struct xdr_stream *xdr,
519302e0297fSChuck Lever const struct nfsd4_copy *copy)
519402e0297fSChuck Lever {
519502e0297fSChuck Lever __be32 status;
519602e0297fSChuck Lever
519702e0297fSChuck Lever /* cr_consecutive */
519802e0297fSChuck Lever status = nfsd4_encode_bool(xdr, true);
519902e0297fSChuck Lever if (status != nfs_ok)
520002e0297fSChuck Lever return status;
520102e0297fSChuck Lever /* cr_synchronous */
520202e0297fSChuck Lever return nfsd4_encode_bool(xdr, nfsd4_copy_is_sync(copy));
520302e0297fSChuck Lever }
520402e0297fSChuck Lever
520502e0297fSChuck Lever static __be32
nfsd4_encode_copy(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)520602e0297fSChuck Lever nfsd4_encode_copy(struct nfsd4_compoundres *resp, __be32 nfserr,
520702e0297fSChuck Lever union nfsd4_op_u *u)
520802e0297fSChuck Lever {
520902e0297fSChuck Lever struct nfsd4_copy *copy = &u->copy;
521002e0297fSChuck Lever
521102e0297fSChuck Lever nfserr = nfsd4_encode_write_response4(resp->xdr, copy);
521202e0297fSChuck Lever if (nfserr != nfs_ok)
521302e0297fSChuck Lever return nfserr;
521402e0297fSChuck Lever return nfsd4_encode_copy_requirements4(resp->xdr, copy);
521502e0297fSChuck Lever }
521602e0297fSChuck Lever
521702e0297fSChuck Lever static __be32
nfsd4_encode_netloc4(struct xdr_stream * xdr,const struct nl4_server * ns)521802e0297fSChuck Lever nfsd4_encode_netloc4(struct xdr_stream *xdr, const struct nl4_server *ns)
521902e0297fSChuck Lever {
522002e0297fSChuck Lever __be32 status;
522102e0297fSChuck Lever
522202e0297fSChuck Lever if (xdr_stream_encode_u32(xdr, ns->nl4_type) != XDR_UNIT)
522329ae7f9dSAnna Schumaker return nfserr_resource;
522429ae7f9dSAnna Schumaker switch (ns->nl4_type) {
522529ae7f9dSAnna Schumaker case NL4_NETADDR:
522621d316a7SChuck Lever /* nl_addr */
522751911868SOlga Kornievskaia status = nfsd4_encode_netaddr4(xdr, &ns->u.nl4_addr);
522821d316a7SChuck Lever break;
522951911868SOlga Kornievskaia default:
523021d316a7SChuck Lever status = nfserr_serverfault;
523121d316a7SChuck Lever }
523251911868SOlga Kornievskaia return status;
523351911868SOlga Kornievskaia }
523421d316a7SChuck Lever
523521d316a7SChuck Lever static __be32
nfsd4_encode_copy_notify(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)523651911868SOlga Kornievskaia nfsd4_encode_copy_notify(struct nfsd4_compoundres *resp, __be32 nfserr,
523751911868SOlga Kornievskaia union nfsd4_op_u *u)
523821d316a7SChuck Lever {
523921d316a7SChuck Lever struct nfsd4_copy_notify *cn = &u->copy_notify;
524021d316a7SChuck Lever struct xdr_stream *xdr = resp->xdr;
524151911868SOlga Kornievskaia
524251911868SOlga Kornievskaia /* cnr_lease_time */
524321d316a7SChuck Lever nfserr = nfsd4_encode_nfstime4(xdr, &cn->cpn_lease_time);
524421d316a7SChuck Lever if (nfserr)
524521d316a7SChuck Lever return nfserr;
524621d316a7SChuck Lever /* cnr_stateid */
524721d316a7SChuck Lever nfserr = nfsd4_encode_stateid4(xdr, &cn->cpn_cnr_stateid);
524821d316a7SChuck Lever if (nfserr)
524921d316a7SChuck Lever return nfserr;
525021d316a7SChuck Lever /* cnr_source_server<> */
525121d316a7SChuck Lever if (xdr_stream_encode_u32(xdr, 1) != XDR_UNIT)
525221d316a7SChuck Lever return nfserr_resource;
525321d316a7SChuck Lever return nfsd4_encode_netloc4(xdr, cn->cpn_src);
525421d316a7SChuck Lever }
525521d316a7SChuck Lever
525621d316a7SChuck Lever static __be32
nfsd4_encode_offload_status(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)525721d316a7SChuck Lever nfsd4_encode_offload_status(struct nfsd4_compoundres *resp, __be32 nfserr,
525821d316a7SChuck Lever union nfsd4_op_u *u)
525921d316a7SChuck Lever {
526021d316a7SChuck Lever struct nfsd4_offload_status *os = &u->offload_status;
526121d316a7SChuck Lever struct xdr_stream *xdr = resp->xdr;
526251911868SOlga Kornievskaia
526351911868SOlga Kornievskaia /* osr_count */
526451911868SOlga Kornievskaia nfserr = nfsd4_encode_length4(xdr, os->count);
52656308bc98SOlga Kornievskaia if (nfserr != nfs_ok)
5266e78e274eSKees Cook return nfserr;
52676308bc98SOlga Kornievskaia /* osr_complete<1> */
5268e78e274eSKees Cook if (os->completed) {
5269bddfdbcdSChuck Lever if (xdr_stream_encode_u32(xdr, 1) != XDR_UNIT)
52706308bc98SOlga Kornievskaia return nfserr_resource;
5271b609ad60SChuck Lever if (xdr_stream_encode_be32(xdr, os->status) != XDR_UNIT)
5272b609ad60SChuck Lever return nfserr_resource;
5273b609ad60SChuck Lever } else if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
5274528b8493SAnna Schumaker return nfserr_resource;
5275b609ad60SChuck Lever return nfs_ok;
5276cc63c216SChuck Lever }
5277cc63c216SChuck Lever
5278cc63c216SChuck Lever static __be32
nfsd4_encode_read_plus_data(struct nfsd4_compoundres * resp,struct nfsd4_read * read)5279cc63c216SChuck Lever nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,
5280cc63c216SChuck Lever struct nfsd4_read *read)
5281cc63c216SChuck Lever {
5282b609ad60SChuck Lever struct nfsd4_compoundargs *argp = resp->rqstp->rq_argp;
5283b609ad60SChuck Lever struct file *file = read->rd_nf->nf_file;
5284528b8493SAnna Schumaker struct xdr_stream *xdr = resp->xdr;
5285528b8493SAnna Schumaker bool splice_ok = argp->splice_ok;
5286528b8493SAnna Schumaker unsigned long maxcount;
5287528b8493SAnna Schumaker __be32 nfserr, *p;
5288eeadcb75SAnna Schumaker
5289528b8493SAnna Schumaker /* Content type, offset, byte count */
5290a2c91753SChuck Lever p = xdr_reserve_space(xdr, 4 + 8 + 4);
5291528b8493SAnna Schumaker if (!p)
5292eeadcb75SAnna Schumaker return nfserr_io;
5293a2c91753SChuck Lever if (resp->xdr->buf->page_len && splice_ok) {
5294eeadcb75SAnna Schumaker WARN_ON_ONCE(splice_ok);
5295eeadcb75SAnna Schumaker return nfserr_serverfault;
5296528b8493SAnna Schumaker }
5297528b8493SAnna Schumaker
5298528b8493SAnna Schumaker maxcount = min_t(unsigned long, read->rd_length,
5299528b8493SAnna Schumaker (xdr->buf->buflen - xdr->buf->len));
5300eeadcb75SAnna Schumaker
5301eeadcb75SAnna Schumaker if (file->f_op->splice_read && splice_ok)
5302eeadcb75SAnna Schumaker nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount);
5303eeadcb75SAnna Schumaker else
5304528b8493SAnna Schumaker nfserr = nfsd4_encode_readv(resp, read, file, maxcount);
5305528b8493SAnna Schumaker if (nfserr)
5306eeadcb75SAnna Schumaker return nfserr;
5307eeadcb75SAnna Schumaker
53082db27992SAnna Schumaker *p++ = cpu_to_be32(NFS4_CONTENT_DATA);
5309eeadcb75SAnna Schumaker p = xdr_encode_hyper(p, read->rd_offset);
5310eeadcb75SAnna Schumaker *p = cpu_to_be32(read->rd_length);
5311eeadcb75SAnna Schumaker
5312eeadcb75SAnna Schumaker return nfs_ok;
5313eeadcb75SAnna Schumaker }
5314eeadcb75SAnna Schumaker
5315278765eaSAnna Schumaker static __be32
nfsd4_encode_read_plus(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)5316eeadcb75SAnna Schumaker nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr,
53172db27992SAnna Schumaker union nfsd4_op_u *u)
5318eeadcb75SAnna Schumaker {
53192db27992SAnna Schumaker struct nfsd4_read *read = &u->read;
53202db27992SAnna Schumaker struct file *file = read->rd_nf->nf_file;
53212db27992SAnna Schumaker struct xdr_stream *xdr = resp->xdr;
53222db27992SAnna Schumaker int starting_len = xdr->buf->len;
53232db27992SAnna Schumaker u32 segments = 0;
5324528b8493SAnna Schumaker __be32 *p;
5325e78e274eSKees Cook
5326528b8493SAnna Schumaker if (nfserr)
5327e78e274eSKees Cook return nfserr;
5328eeadcb75SAnna Schumaker
5329bddfdbcdSChuck Lever /* eof flag, segment count */
5330528b8493SAnna Schumaker p = xdr_reserve_space(xdr, 4 + 4);
5331eeadcb75SAnna Schumaker if (!p)
5332eeadcb75SAnna Schumaker return nfserr_io;
5333528b8493SAnna Schumaker xdr_commit_encode(xdr);
5334528b8493SAnna Schumaker
5335528b8493SAnna Schumaker read->rd_eof = read->rd_offset >= i_size_read(file_inode(file));
5336528b8493SAnna Schumaker if (read->rd_eof)
5337528b8493SAnna Schumaker goto out;
5338528b8493SAnna Schumaker
5339528b8493SAnna Schumaker nfserr = nfsd4_encode_read_plus_data(resp, read);
5340eeadcb75SAnna Schumaker if (nfserr) {
5341528b8493SAnna Schumaker xdr_truncate_encode(xdr, starting_len);
5342528b8493SAnna Schumaker return nfserr;
5343eeadcb75SAnna Schumaker }
5344eeadcb75SAnna Schumaker
53452db27992SAnna Schumaker segments++;
53462db27992SAnna Schumaker
5347eeadcb75SAnna Schumaker out:
5348eeadcb75SAnna Schumaker p = xdr_encode_bool(p, read->rd_eof);
5349eeadcb75SAnna Schumaker *p = cpu_to_be32(segments);
5350eeadcb75SAnna Schumaker return nfserr;
5351528b8493SAnna Schumaker }
5352528b8493SAnna Schumaker
5353eeadcb75SAnna Schumaker static __be32
nfsd4_encode_seek(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)5354eeadcb75SAnna Schumaker nfsd4_encode_seek(struct nfsd4_compoundres *resp, __be32 nfserr,
53552db27992SAnna Schumaker union nfsd4_op_u *u)
5356eeadcb75SAnna Schumaker {
5357eeadcb75SAnna Schumaker struct nfsd4_seek *seek = &u->seek;
53586308bc98SOlga Kornievskaia struct xdr_stream *xdr = resp->xdr;
53596308bc98SOlga Kornievskaia
53606308bc98SOlga Kornievskaia /* sr_eof */
53616308bc98SOlga Kornievskaia nfserr = nfsd4_encode_bool(xdr, seek->seek_eof);
536224bab491SAnna Schumaker if (nfserr != nfs_ok)
5363e78e274eSKees Cook return nfserr;
536424bab491SAnna Schumaker /* sr_offset */
5365e78e274eSKees Cook return nfsd4_encode_offset4(xdr, seek->seek_pos);
53661f121e2dSChuck Lever }
536724bab491SAnna Schumaker
53681f121e2dSChuck Lever static __be32
nfsd4_encode_noop(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * p)53691f121e2dSChuck Lever nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr,
53701f121e2dSChuck Lever union nfsd4_op_u *p)
53711f121e2dSChuck Lever {
53721f121e2dSChuck Lever return nfserr;
53731f121e2dSChuck Lever }
537424bab491SAnna Schumaker
537524bab491SAnna Schumaker /*
537624bab491SAnna Schumaker * Encode kmalloc-ed buffer in to XDR stream.
5377e78e274eSKees Cook */
5378e78e274eSKees Cook static __be32
nfsd4_vbuf_to_stream(struct xdr_stream * xdr,char * buf,u32 buflen)5379695e12f8SBenny Halevy nfsd4_vbuf_to_stream(struct xdr_stream *xdr, char *buf, u32 buflen)
5380695e12f8SBenny Halevy {
5381695e12f8SBenny Halevy u32 cplen;
5382695e12f8SBenny Halevy __be32 *p;
538323e50fe3SFrank van der Linden
538423e50fe3SFrank van der Linden cplen = min_t(unsigned long, buflen,
538523e50fe3SFrank van der Linden ((void *)xdr->end - (void *)xdr->p));
5386b9a49237SChuck Lever p = xdr_reserve_space(xdr, cplen);
538723e50fe3SFrank van der Linden if (!p)
538823e50fe3SFrank van der Linden return nfserr_resource;
538923e50fe3SFrank van der Linden
539023e50fe3SFrank van der Linden memcpy(p, buf, cplen);
539123e50fe3SFrank van der Linden buf += cplen;
539223e50fe3SFrank van der Linden buflen -= cplen;
539323e50fe3SFrank van der Linden
539423e50fe3SFrank van der Linden while (buflen) {
539523e50fe3SFrank van der Linden cplen = min_t(u32, buflen, PAGE_SIZE);
539623e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, cplen);
539723e50fe3SFrank van der Linden if (!p)
539823e50fe3SFrank van der Linden return nfserr_resource;
539923e50fe3SFrank van der Linden
540023e50fe3SFrank van der Linden memcpy(p, buf, cplen);
540123e50fe3SFrank van der Linden
540223e50fe3SFrank van der Linden if (cplen < PAGE_SIZE) {
540323e50fe3SFrank van der Linden /*
540423e50fe3SFrank van der Linden * We're done, with a length that wasn't page
540523e50fe3SFrank van der Linden * aligned, so possibly not word aligned. Pad
540623e50fe3SFrank van der Linden * any trailing bytes with 0.
540723e50fe3SFrank van der Linden */
540823e50fe3SFrank van der Linden xdr_encode_opaque_fixed(p, NULL, cplen);
540923e50fe3SFrank van der Linden break;
541023e50fe3SFrank van der Linden }
541123e50fe3SFrank van der Linden
541223e50fe3SFrank van der Linden buflen -= PAGE_SIZE;
541323e50fe3SFrank van der Linden buf += PAGE_SIZE;
541423e50fe3SFrank van der Linden }
541523e50fe3SFrank van der Linden
541623e50fe3SFrank van der Linden return 0;
541723e50fe3SFrank van der Linden }
541823e50fe3SFrank van der Linden
541923e50fe3SFrank van der Linden static __be32
nfsd4_encode_getxattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)542023e50fe3SFrank van der Linden nfsd4_encode_getxattr(struct nfsd4_compoundres *resp, __be32 nfserr,
542123e50fe3SFrank van der Linden union nfsd4_op_u *u)
542223e50fe3SFrank van der Linden {
542323e50fe3SFrank van der Linden struct nfsd4_getxattr *getxattr = &u->getxattr;
542423e50fe3SFrank van der Linden struct xdr_stream *xdr = resp->xdr;
542523e50fe3SFrank van der Linden __be32 *p, err;
542623e50fe3SFrank van der Linden
542723e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, 4);
542823e50fe3SFrank van der Linden if (!p)
5429e78e274eSKees Cook return nfserr_resource;
543023e50fe3SFrank van der Linden
5431e78e274eSKees Cook *p = cpu_to_be32(getxattr->getxa_len);
5432bddfdbcdSChuck Lever
543323e50fe3SFrank van der Linden if (getxattr->getxa_len == 0)
543423e50fe3SFrank van der Linden return 0;
543523e50fe3SFrank van der Linden
543623e50fe3SFrank van der Linden err = nfsd4_vbuf_to_stream(xdr, getxattr->getxa_buf,
543723e50fe3SFrank van der Linden getxattr->getxa_len);
543823e50fe3SFrank van der Linden
543923e50fe3SFrank van der Linden kvfree(getxattr->getxa_buf);
544023e50fe3SFrank van der Linden
544123e50fe3SFrank van der Linden return err;
544223e50fe3SFrank van der Linden }
544323e50fe3SFrank van der Linden
544423e50fe3SFrank van der Linden static __be32
nfsd4_encode_setxattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)544523e50fe3SFrank van der Linden nfsd4_encode_setxattr(struct nfsd4_compoundres *resp, __be32 nfserr,
544623e50fe3SFrank van der Linden union nfsd4_op_u *u)
544723e50fe3SFrank van der Linden {
544823e50fe3SFrank van der Linden struct nfsd4_setxattr *setxattr = &u->setxattr;
544923e50fe3SFrank van der Linden struct xdr_stream *xdr = resp->xdr;
545023e50fe3SFrank van der Linden
545123e50fe3SFrank van der Linden return nfsd4_encode_change_info4(xdr, &setxattr->setxa_cinfo);
545223e50fe3SFrank van der Linden }
545323e50fe3SFrank van der Linden
5454e78e274eSKees Cook /*
545523e50fe3SFrank van der Linden * See if there are cookie values that can be rejected outright.
5456e78e274eSKees Cook */
5457bddfdbcdSChuck Lever static __be32
nfsd4_listxattr_validate_cookie(struct nfsd4_listxattrs * listxattrs,u32 * offsetp)545823e50fe3SFrank van der Linden nfsd4_listxattr_validate_cookie(struct nfsd4_listxattrs *listxattrs,
545966a21db7SChuck Lever u32 *offsetp)
546023e50fe3SFrank van der Linden {
546123e50fe3SFrank van der Linden u64 cookie = listxattrs->lsxa_cookie;
546223e50fe3SFrank van der Linden
546323e50fe3SFrank van der Linden /*
546423e50fe3SFrank van der Linden * If the cookie is larger than the maximum number we can fit
546523e50fe3SFrank van der Linden * in the buffer we just got back from vfs_listxattr, it's invalid.
546623e50fe3SFrank van der Linden */
546723e50fe3SFrank van der Linden if (cookie > (listxattrs->lsxa_len) / (XATTR_USER_PREFIX_LEN + 2))
546823e50fe3SFrank van der Linden return nfserr_badcookie;
546923e50fe3SFrank van der Linden
547023e50fe3SFrank van der Linden *offsetp = (u32)cookie;
547123e50fe3SFrank van der Linden return 0;
547223e50fe3SFrank van der Linden }
547352a357dbSJorge Mora
547423e50fe3SFrank van der Linden static __be32
nfsd4_encode_listxattrs(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)547523e50fe3SFrank van der Linden nfsd4_encode_listxattrs(struct nfsd4_compoundres *resp, __be32 nfserr,
547623e50fe3SFrank van der Linden union nfsd4_op_u *u)
547723e50fe3SFrank van der Linden {
547823e50fe3SFrank van der Linden struct nfsd4_listxattrs *listxattrs = &u->listxattrs;
547923e50fe3SFrank van der Linden struct xdr_stream *xdr = resp->xdr;
548023e50fe3SFrank van der Linden u32 cookie_offset, count_offset, eof;
548123e50fe3SFrank van der Linden u32 left, xdrleft, slen, count;
548223e50fe3SFrank van der Linden u32 xdrlen, offset;
548323e50fe3SFrank van der Linden u64 cookie;
5484e78e274eSKees Cook char *sp;
548523e50fe3SFrank van der Linden __be32 status, tmp;
5486e78e274eSKees Cook __be64 wire_cookie;
5487bddfdbcdSChuck Lever __be32 *p;
548823e50fe3SFrank van der Linden u32 nuser;
548923e50fe3SFrank van der Linden
549023e50fe3SFrank van der Linden eof = 1;
549123e50fe3SFrank van der Linden
549223e50fe3SFrank van der Linden status = nfsd4_listxattr_validate_cookie(listxattrs, &offset);
5493b9a49237SChuck Lever if (status)
549461ab5e07SJorge Mora goto out;
549523e50fe3SFrank van der Linden
549623e50fe3SFrank van der Linden /*
549723e50fe3SFrank van der Linden * Reserve space for the cookie and the name array count. Record
549823e50fe3SFrank van der Linden * the offsets to save them later.
549923e50fe3SFrank van der Linden */
550023e50fe3SFrank van der Linden cookie_offset = xdr->buf->len;
550123e50fe3SFrank van der Linden count_offset = cookie_offset + 8;
550223e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, XDR_UNIT * 3);
550323e50fe3SFrank van der Linden if (!p) {
550423e50fe3SFrank van der Linden status = nfserr_resource;
550523e50fe3SFrank van der Linden goto out;
550623e50fe3SFrank van der Linden }
550723e50fe3SFrank van der Linden
550823e50fe3SFrank van der Linden count = 0;
550923e50fe3SFrank van der Linden left = listxattrs->lsxa_len;
551031e4bb8fSJorge Mora sp = listxattrs->lsxa_buf;
551123e50fe3SFrank van der Linden nuser = 0;
551223e50fe3SFrank van der Linden
551323e50fe3SFrank van der Linden /* Bytes left is maxcount - 8 (cookie) - 4 (array count) */
551423e50fe3SFrank van der Linden xdrleft = listxattrs->lsxa_maxcount - XDR_UNIT * 3;
551523e50fe3SFrank van der Linden
551623e50fe3SFrank van der Linden while (left > 0 && xdrleft > 0) {
551723e50fe3SFrank van der Linden slen = strlen(sp);
551823e50fe3SFrank van der Linden
551923e50fe3SFrank van der Linden /*
552023e50fe3SFrank van der Linden * Check if this is a "user." attribute, skip it if not.
552131e4bb8fSJorge Mora */
552231e4bb8fSJorge Mora if (strncmp(sp, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
552323e50fe3SFrank van der Linden goto contloop;
552423e50fe3SFrank van der Linden
552523e50fe3SFrank van der Linden slen -= XATTR_USER_PREFIX_LEN;
552623e50fe3SFrank van der Linden xdrlen = 4 + ((slen + 3) & ~3);
552723e50fe3SFrank van der Linden /* Check if both entry and eof can fit in the XDR buffer */
55284cce11faSAlex Dewar if (xdrlen + XDR_UNIT > xdrleft) {
552923e50fe3SFrank van der Linden if (count == 0) {
553023e50fe3SFrank van der Linden /*
553123e50fe3SFrank van der Linden * Can't even fit the first attribute name.
553223e50fe3SFrank van der Linden */
553323e50fe3SFrank van der Linden status = nfserr_toosmall;
553423e50fe3SFrank van der Linden goto out;
55352f73f37dSJorge Mora }
55362f73f37dSJorge Mora eof = 0;
553723e50fe3SFrank van der Linden goto wreof;
553823e50fe3SFrank van der Linden }
553923e50fe3SFrank van der Linden
554023e50fe3SFrank van der Linden left -= XATTR_USER_PREFIX_LEN;
554123e50fe3SFrank van der Linden sp += XATTR_USER_PREFIX_LEN;
554223e50fe3SFrank van der Linden if (nuser++ < offset)
554323e50fe3SFrank van der Linden goto contloop;
554423e50fe3SFrank van der Linden
554523e50fe3SFrank van der Linden
554623e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, xdrlen);
554723e50fe3SFrank van der Linden if (!p) {
554823e50fe3SFrank van der Linden status = nfserr_resource;
554923e50fe3SFrank van der Linden goto out;
555023e50fe3SFrank van der Linden }
555123e50fe3SFrank van der Linden
555223e50fe3SFrank van der Linden xdr_encode_opaque(p, sp, slen);
555323e50fe3SFrank van der Linden
555423e50fe3SFrank van der Linden xdrleft -= xdrlen;
555523e50fe3SFrank van der Linden count++;
555623e50fe3SFrank van der Linden contloop:
555723e50fe3SFrank van der Linden sp += slen + 1;
555823e50fe3SFrank van der Linden left -= slen + 1;
555923e50fe3SFrank van der Linden }
5560e2a1840eSAlex Dewar
556123e50fe3SFrank van der Linden /*
556223e50fe3SFrank van der Linden * If there were user attributes to copy, but we didn't copy
556323e50fe3SFrank van der Linden * any, the offset was too large (e.g. the cookie was invalid).
556423e50fe3SFrank van der Linden */
556523e50fe3SFrank van der Linden if (nuser > 0 && count == 0) {
556623e50fe3SFrank van der Linden status = nfserr_badcookie;
556723e50fe3SFrank van der Linden goto out;
556823e50fe3SFrank van der Linden }
556923e50fe3SFrank van der Linden
557023e50fe3SFrank van der Linden wreof:
557123e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, 4);
557223e50fe3SFrank van der Linden if (!p) {
557323e50fe3SFrank van der Linden status = nfserr_resource;
557423e50fe3SFrank van der Linden goto out;
557523e50fe3SFrank van der Linden }
557623e50fe3SFrank van der Linden *p = cpu_to_be32(eof);
557723e50fe3SFrank van der Linden
557823e50fe3SFrank van der Linden cookie = offset + count;
557923e50fe3SFrank van der Linden
558023e50fe3SFrank van der Linden wire_cookie = cpu_to_be64(cookie);
558123e50fe3SFrank van der Linden write_bytes_to_xdr_buf(xdr->buf, cookie_offset, &wire_cookie, 8);
558223e50fe3SFrank van der Linden tmp = cpu_to_be32(count);
558323e50fe3SFrank van der Linden write_bytes_to_xdr_buf(xdr->buf, count_offset, &tmp, 4);
558423e50fe3SFrank van der Linden out:
558523e50fe3SFrank van der Linden if (listxattrs->lsxa_len)
558623e50fe3SFrank van der Linden kvfree(listxattrs->lsxa_buf);
558723e50fe3SFrank van der Linden return status;
558861ab5e07SJorge Mora }
558961ab5e07SJorge Mora
5590b9a49237SChuck Lever static __be32
nfsd4_encode_removexattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)5591b9a49237SChuck Lever nfsd4_encode_removexattr(struct nfsd4_compoundres *resp, __be32 nfserr,
559223e50fe3SFrank van der Linden union nfsd4_op_u *u)
559323e50fe3SFrank van der Linden {
559423e50fe3SFrank van der Linden struct nfsd4_removexattr *removexattr = &u->removexattr;
559523e50fe3SFrank van der Linden struct xdr_stream *xdr = resp->xdr;
559623e50fe3SFrank van der Linden
559723e50fe3SFrank van der Linden return nfsd4_encode_change_info4(xdr, &removexattr->rmxa_cinfo);
559823e50fe3SFrank van der Linden }
559923e50fe3SFrank van der Linden
5600e78e274eSKees Cook typedef __be32(*nfsd4_enc)(struct nfsd4_compoundres *, __be32, union nfsd4_op_u *u);
560123e50fe3SFrank van der Linden
5602e78e274eSKees Cook /*
5603bddfdbcdSChuck Lever * Note: nfsd4_enc_ops vector is shared for v4.0 and v4.1
560423e50fe3SFrank van der Linden * since we don't need to filter out obsolete ops as this is
560566a21db7SChuck Lever * done in the decoding phase.
560623e50fe3SFrank van der Linden */
560723e50fe3SFrank van der Linden static const nfsd4_enc nfsd4_enc_ops[] = {
5608e78e274eSKees Cook [OP_ACCESS] = nfsd4_encode_access,
5609695e12f8SBenny Halevy [OP_CLOSE] = nfsd4_encode_close,
56102db134ebSAndy Adamson [OP_COMMIT] = nfsd4_encode_commit,
56112db134ebSAndy Adamson [OP_CREATE] = nfsd4_encode_create,
56122db134ebSAndy Adamson [OP_DELEGPURGE] = nfsd4_encode_noop,
56132db134ebSAndy Adamson [OP_DELEGRETURN] = nfsd4_encode_noop,
56142db134ebSAndy Adamson [OP_GETATTR] = nfsd4_encode_getattr,
5615c1df609dSChuck Lever [OP_GETFH] = nfsd4_encode_getfh,
5616e78e274eSKees Cook [OP_LINK] = nfsd4_encode_link,
5617e78e274eSKees Cook [OP_LOCK] = nfsd4_encode_lock,
5618e78e274eSKees Cook [OP_LOCKT] = nfsd4_encode_lockt,
5619e78e274eSKees Cook [OP_LOCKU] = nfsd4_encode_locku,
5620e78e274eSKees Cook [OP_LOOKUP] = nfsd4_encode_noop,
5621e78e274eSKees Cook [OP_LOOKUPP] = nfsd4_encode_noop,
5622e78e274eSKees Cook [OP_NVERIFY] = nfsd4_encode_noop,
5623e78e274eSKees Cook [OP_OPEN] = nfsd4_encode_open,
5624e78e274eSKees Cook [OP_OPENATTR] = nfsd4_encode_noop,
5625e78e274eSKees Cook [OP_OPEN_CONFIRM] = nfsd4_encode_open_confirm,
5626e78e274eSKees Cook [OP_OPEN_DOWNGRADE] = nfsd4_encode_open_downgrade,
5627e78e274eSKees Cook [OP_PUTFH] = nfsd4_encode_noop,
5628e78e274eSKees Cook [OP_PUTPUBFH] = nfsd4_encode_noop,
5629e78e274eSKees Cook [OP_PUTROOTFH] = nfsd4_encode_noop,
5630e78e274eSKees Cook [OP_READ] = nfsd4_encode_read,
5631e78e274eSKees Cook [OP_READDIR] = nfsd4_encode_readdir,
5632e78e274eSKees Cook [OP_READLINK] = nfsd4_encode_readlink,
5633e78e274eSKees Cook [OP_REMOVE] = nfsd4_encode_remove,
5634e78e274eSKees Cook [OP_RENAME] = nfsd4_encode_rename,
5635e78e274eSKees Cook [OP_RENEW] = nfsd4_encode_noop,
5636e78e274eSKees Cook [OP_RESTOREFH] = nfsd4_encode_noop,
5637e78e274eSKees Cook [OP_SAVEFH] = nfsd4_encode_noop,
5638e78e274eSKees Cook [OP_SECINFO] = nfsd4_encode_secinfo,
5639e78e274eSKees Cook [OP_SETATTR] = nfsd4_encode_setattr,
5640e78e274eSKees Cook [OP_SETCLIENTID] = nfsd4_encode_setclientid,
5641e78e274eSKees Cook [OP_SETCLIENTID_CONFIRM] = nfsd4_encode_noop,
5642e78e274eSKees Cook [OP_VERIFY] = nfsd4_encode_noop,
5643e78e274eSKees Cook [OP_WRITE] = nfsd4_encode_write,
5644e78e274eSKees Cook [OP_RELEASE_LOCKOWNER] = nfsd4_encode_noop,
5645e78e274eSKees Cook
5646e78e274eSKees Cook /* NFSv4.1 operations */
5647e78e274eSKees Cook [OP_BACKCHANNEL_CTL] = nfsd4_encode_noop,
5648e78e274eSKees Cook [OP_BIND_CONN_TO_SESSION] = nfsd4_encode_bind_conn_to_session,
5649e78e274eSKees Cook [OP_EXCHANGE_ID] = nfsd4_encode_exchange_id,
5650e78e274eSKees Cook [OP_CREATE_SESSION] = nfsd4_encode_create_session,
5651e78e274eSKees Cook [OP_DESTROY_SESSION] = nfsd4_encode_noop,
5652e78e274eSKees Cook [OP_FREE_STATEID] = nfsd4_encode_noop,
56532db134ebSAndy Adamson [OP_GET_DIR_DELEGATION] = nfsd4_encode_get_dir_delegation,
56542db134ebSAndy Adamson #ifdef CONFIG_NFSD_PNFS
5655e78e274eSKees Cook [OP_GETDEVICEINFO] = nfsd4_encode_getdeviceinfo,
5656e78e274eSKees Cook [OP_GETDEVICELIST] = nfsd4_encode_noop,
5657e78e274eSKees Cook [OP_LAYOUTCOMMIT] = nfsd4_encode_layoutcommit,
5658e78e274eSKees Cook [OP_LAYOUTGET] = nfsd4_encode_layoutget,
5659e78e274eSKees Cook [OP_LAYOUTRETURN] = nfsd4_encode_layoutreturn,
5660e78e274eSKees Cook #else
566133a1e6eaSJeff Layton [OP_GETDEVICEINFO] = nfsd4_encode_noop,
56629cf514ccSChristoph Hellwig [OP_GETDEVICELIST] = nfsd4_encode_noop,
5663e78e274eSKees Cook [OP_LAYOUTCOMMIT] = nfsd4_encode_noop,
5664e78e274eSKees Cook [OP_LAYOUTGET] = nfsd4_encode_noop,
5665e78e274eSKees Cook [OP_LAYOUTRETURN] = nfsd4_encode_noop,
5666e78e274eSKees Cook #endif
5667e78e274eSKees Cook [OP_SECINFO_NO_NAME] = nfsd4_encode_secinfo_no_name,
56689cf514ccSChristoph Hellwig [OP_SEQUENCE] = nfsd4_encode_sequence,
5669e78e274eSKees Cook [OP_SET_SSV] = nfsd4_encode_noop,
5670e78e274eSKees Cook [OP_TEST_STATEID] = nfsd4_encode_test_stateid,
5671e78e274eSKees Cook [OP_WANT_DELEGATION] = nfsd4_encode_noop,
5672e78e274eSKees Cook [OP_DESTROY_CLIENTID] = nfsd4_encode_noop,
5673e78e274eSKees Cook [OP_RECLAIM_COMPLETE] = nfsd4_encode_noop,
56749cf514ccSChristoph Hellwig
5675e78e274eSKees Cook /* NFSv4.2 operations */
5676e78e274eSKees Cook [OP_ALLOCATE] = nfsd4_encode_noop,
5677e78e274eSKees Cook [OP_COPY] = nfsd4_encode_copy,
5678e78e274eSKees Cook [OP_COPY_NOTIFY] = nfsd4_encode_copy_notify,
5679e78e274eSKees Cook [OP_DEALLOCATE] = nfsd4_encode_noop,
5680e78e274eSKees Cook [OP_IO_ADVISE] = nfsd4_encode_noop,
5681e78e274eSKees Cook [OP_LAYOUTERROR] = nfsd4_encode_noop,
568287a15a80SAnna Schumaker [OP_LAYOUTSTATS] = nfsd4_encode_noop,
568387a15a80SAnna Schumaker [OP_OFFLOAD_CANCEL] = nfsd4_encode_noop,
5684e78e274eSKees Cook [OP_OFFLOAD_STATUS] = nfsd4_encode_offload_status,
5685e78e274eSKees Cook [OP_READ_PLUS] = nfsd4_encode_read_plus,
5686e78e274eSKees Cook [OP_SEEK] = nfsd4_encode_seek,
5687e78e274eSKees Cook [OP_WRITE_SAME] = nfsd4_encode_noop,
5688e78e274eSKees Cook [OP_CLONE] = nfsd4_encode_noop,
5689e78e274eSKees Cook
5690e78e274eSKees Cook /* RFC 8276 extended atributes operations */
5691e78e274eSKees Cook [OP_GETXATTR] = nfsd4_encode_getxattr,
5692e78e274eSKees Cook [OP_SETXATTR] = nfsd4_encode_setxattr,
5693e78e274eSKees Cook [OP_LISTXATTRS] = nfsd4_encode_listxattrs,
5694e78e274eSKees Cook [OP_REMOVEXATTR] = nfsd4_encode_removexattr,
5695e78e274eSKees Cook };
5696e78e274eSKees Cook
569723e50fe3SFrank van der Linden /*
569823e50fe3SFrank van der Linden * Calculate whether we still have space to encode repsize bytes.
5699e78e274eSKees Cook * There are two considerations:
5700e78e274eSKees Cook * - For NFS versions >=4.1, the size of the reply must stay within
5701e78e274eSKees Cook * session limits
5702e78e274eSKees Cook * - For all NFS versions, we must stay within limited preallocated
5703695e12f8SBenny Halevy * buffer space.
5704695e12f8SBenny Halevy *
5705496c262cSAndy Adamson * This is called before the operation is processed, so can only provide
5706a8095f7eSJ. Bruce Fields * an upper estimate. For some nonidempotent operations (such as
5707a8095f7eSJ. Bruce Fields * getattr), it's not necessarily a problem if that estimate is wrong,
5708a8095f7eSJ. Bruce Fields * as we can fail it after processing without significant side effects.
5709a8095f7eSJ. Bruce Fields */
nfsd4_check_resp_size(struct nfsd4_compoundres * resp,u32 respsize)5710a8095f7eSJ. Bruce Fields __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 respsize)
5711a8095f7eSJ. Bruce Fields {
5712496c262cSAndy Adamson struct xdr_buf *buf = &resp->rqstp->rq_res;
5713a8095f7eSJ. Bruce Fields struct nfsd4_slot *slot = resp->cstate.slot;
5714a8095f7eSJ. Bruce Fields
5715a8095f7eSJ. Bruce Fields if (buf->len + respsize <= buf->buflen)
5716a8095f7eSJ. Bruce Fields return nfs_ok;
5717496c262cSAndy Adamson if (!nfsd4_has_session(&resp->cstate))
5718a8095f7eSJ. Bruce Fields return nfserr_resource;
5719496c262cSAndy Adamson if (slot->sl_flags & NFSD4_SLOT_CACHETHIS) {
572067492c99SJ. Bruce Fields WARN_ON_ONCE(1);
5721a8095f7eSJ. Bruce Fields return nfserr_rep_too_big_to_cache;
5722496c262cSAndy Adamson }
572347ee5298SJ. Bruce Fields return nfserr_rep_too_big;
572447ee5298SJ. Bruce Fields }
572547ee5298SJ. Bruce Fields
nfsd4_map_status(__be32 status,u32 minor)572647ee5298SJ. Bruce Fields static __be32 nfsd4_map_status(__be32 status, u32 minor)
572747ee5298SJ. Bruce Fields {
572847ee5298SJ. Bruce Fields switch (status) {
5729496c262cSAndy Adamson case nfs_ok:
5730ea8d7720SJ. Bruce Fields break;
573147ee5298SJ. Bruce Fields case nfserr_wrong_type:
5732496c262cSAndy Adamson /* RFC 8881 - 15.1.2.9 */
5733496c262cSAndy Adamson if (minor == 0)
57341da177e4SLinus Torvalds status = nfserr_inval;
57351da177e4SLinus Torvalds break;
57361da177e4SLinus Torvalds case nfserr_symlink_not_dir:
5737bddfdbcdSChuck Lever status = nfserr_symlink;
57389411b1d4SJ. Bruce Fields break;
57395f4ab945SJ. Bruce Fields }
574034b1744cSJ. Bruce Fields return status;
5741082d4bd7SJ. Bruce Fields }
574207d1f802SJ. Bruce Fields
5743bc749ca4SJ. Bruce Fields void
nfsd4_encode_operation(struct nfsd4_compoundres * resp,struct nfsd4_op * op)57441da177e4SLinus Torvalds nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
5745d0a381ddSJ. Bruce Fields {
574615a8b55dSJeff Layton struct xdr_stream *xdr = resp->xdr;
574715a8b55dSJeff Layton struct nfs4_stateowner *so = resp->cstate.replay_owner;
5748c373b0a4SJ. Bruce Fields struct svc_rqst *rqstp = resp->rqstp;
5749082d4bd7SJ. Bruce Fields const struct nfsd4_operation *opdesc = op->opdesc;
57501da177e4SLinus Torvalds int post_err_offset;
5751695e12f8SBenny Halevy nfsd4_enc encoder;
5752695e12f8SBenny Halevy __be32 *p;
5753b7571e4cSJ. Bruce Fields
5754b7571e4cSJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
5755b7571e4cSJ. Bruce Fields if (!p)
57563a237b4aSChuck Lever goto release;
5757695e12f8SBenny Halevy *p++ = cpu_to_be32(op->opnum);
575807d1f802SJ. Bruce Fields post_err_offset = xdr->buf->len;
575907d1f802SJ. Bruce Fields
576008281341SChuck Lever if (op->opnum == OP_ILLEGAL)
576108281341SChuck Lever goto status;
57622825a7f9SJ. Bruce Fields if (op->status && opdesc &&
57632825a7f9SJ. Bruce Fields !(opdesc->op_flags & OP_NONTRIVIAL_ERROR_ENCODE))
5764067e1aceSJ. Bruce Fields goto status;
57655f4ab945SJ. Bruce Fields BUG_ON(op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) ||
57665f4ab945SJ. Bruce Fields !nfsd4_enc_ops[op->opnum]);
57675f4ab945SJ. Bruce Fields encoder = nfsd4_enc_ops[op->opnum];
57685f4ab945SJ. Bruce Fields op->status = encoder(resp, op->status, &op->u);
57695f4ab945SJ. Bruce Fields if (op->status)
57705f4ab945SJ. Bruce Fields trace_nfsd_compound_encode_err(rqstp, op->opnum, op->status);
5771c8f13d97SJ. Bruce Fields xdr_commit_encode(xdr);
5772c8f13d97SJ. Bruce Fields
5773c8f13d97SJ. Bruce Fields /* nfsd4_check_resp_size guarantees enough room for error status */
5774c8f13d97SJ. Bruce Fields if (!op->status) {
5775c8f13d97SJ. Bruce Fields int space_needed = 0;
5776c8f13d97SJ. Bruce Fields if (!nfsd4_last_compound_op(rqstp))
5777c8f13d97SJ. Bruce Fields space_needed = COMPOUND_ERR_SLACK_SPACE;
5778c8f13d97SJ. Bruce Fields op->status = nfsd4_check_resp_size(resp, space_needed);
577907d1f802SJ. Bruce Fields }
578007d1f802SJ. Bruce Fields if (op->status == nfserr_resource && nfsd4_has_session(&resp->cstate)) {
578107d1f802SJ. Bruce Fields struct nfsd4_slot *slot = resp->cstate.slot;
578207d1f802SJ. Bruce Fields
578307d1f802SJ. Bruce Fields if (slot->sl_flags & NFSD4_SLOT_CACHETHIS)
578407d1f802SJ. Bruce Fields op->status = nfserr_rep_too_big_to_cache;
578507d1f802SJ. Bruce Fields else
578607d1f802SJ. Bruce Fields op->status = nfserr_rep_too_big;
578707d1f802SJ. Bruce Fields }
578807d1f802SJ. Bruce Fields if (op->status == nfserr_resource ||
578907d1f802SJ. Bruce Fields op->status == nfserr_rep_too_big ||
5790082d4bd7SJ. Bruce Fields op->status == nfserr_rep_too_big_to_cache) {
579107d1f802SJ. Bruce Fields /*
57929411b1d4SJ. Bruce Fields * The operation may have already been encoded or
5793082d4bd7SJ. Bruce Fields * partially encoded. No op returns anything additional
5794082d4bd7SJ. Bruce Fields * in the case of one of these three errors, so we can
57959411b1d4SJ. Bruce Fields * just truncate back to after the status. But it's a
5796082d4bd7SJ. Bruce Fields * bug if we had to do this on a non-idempotent op:
5797082d4bd7SJ. Bruce Fields */
5798082d4bd7SJ. Bruce Fields warn_on_nonidempotent_op(op);
57999411b1d4SJ. Bruce Fields xdr_truncate_encode(xdr, post_err_offset);
5800695e12f8SBenny Halevy }
5801095a764bSChuck Lever if (so) {
580215a8b55dSJeff Layton int len = xdr->buf->len - post_err_offset;
580315a8b55dSJeff Layton
580415a8b55dSJeff Layton so->so_replay.rp_status = op->status;
5805ed4a567aSChuck Lever so->so_replay.rp_buflen = len;
5806ed4a567aSChuck Lever read_bytes_from_xdr_buf(xdr->buf, post_err_offset,
5807ed4a567aSChuck Lever so->so_replay.rp_buf, len);
5808ed4a567aSChuck Lever }
5809ed4a567aSChuck Lever status:
5810ed4a567aSChuck Lever op->status = nfsd4_map_status(op->status,
58111da177e4SLinus Torvalds resp->cstate.minorversion);
58121da177e4SLinus Torvalds *p = op->status;
58139b350d3eSChuck Lever release:
58149b350d3eSChuck Lever if (opdesc && opdesc->op_release)
58159b350d3eSChuck Lever opdesc->op_release(&op->u);
58169b350d3eSChuck Lever
58171da177e4SLinus Torvalds /*
58189b350d3eSChuck Lever * Account for pages consumed while encoding this operation.
58191da177e4SLinus Torvalds * The xdr_stream primitives don't manage rq_next_page.
58209b350d3eSChuck Lever */
58211da177e4SLinus Torvalds rqstp->rq_next_page = xdr->page_ptr + 1;
58221da177e4SLinus Torvalds }
58231da177e4SLinus Torvalds
58249b350d3eSChuck Lever /**
58251da177e4SLinus Torvalds * nfsd4_encode_replay - encode a result stored in the stateowner reply cache
58269b350d3eSChuck Lever * @xdr: send buffer's XDR stream
58279b350d3eSChuck Lever * @op: operation being replayed
58289b350d3eSChuck Lever *
58299b350d3eSChuck Lever * @op->replay->rp_buf contains the previously-sent already-encoded result.
58309b350d3eSChuck Lever */
nfsd4_encode_replay(struct xdr_stream * xdr,struct nfsd4_op * op)58311da177e4SLinus Torvalds void nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op)
58321da177e4SLinus Torvalds {
58338537488bSChristoph Hellwig struct nfs4_replay *rp = op->replay;
58341da177e4SLinus Torvalds
58353e98abffSJ. Bruce Fields trace_nfsd_stateowner_replay(op->opnum, rp);
58363e98abffSJ. Bruce Fields
58371da177e4SLinus Torvalds if (xdr_stream_encode_u32(xdr, op->opnum) != XDR_UNIT)
583880e591ceSChuck Lever return;
58391da177e4SLinus Torvalds if (xdr_stream_encode_be32(xdr, rp->rp_status) != XDR_UNIT)
58401da177e4SLinus Torvalds return;
58411da177e4SLinus Torvalds xdr_stream_encode_opaque_fixed(xdr, rp->rp_buf, rp->rp_buflen);
5842d5e23383SJ. Bruce Fields }
58431da177e4SLinus Torvalds
nfsd4_release_compoundargs(struct svc_rqst * rqstp)58441da177e4SLinus Torvalds void nfsd4_release_compoundargs(struct svc_rqst *rqstp)
58451da177e4SLinus Torvalds {
58461da177e4SLinus Torvalds struct nfsd4_compoundargs *args = rqstp->rq_argp;
58471da177e4SLinus Torvalds
5848c44b31c2SChuck Lever if (args->ops != args->iops) {
584916c66364SChuck Lever vfree(args->ops);
58501da177e4SLinus Torvalds args->ops = args->iops;
5851026fec7eSChristoph Hellwig }
5852026fec7eSChristoph Hellwig while (args->to_free) {
5853c1346a12SChuck Lever struct svcxdr_tmpbuf *tb = args->to_free;
58541da177e4SLinus Torvalds args->to_free = tb->next;
5855c1346a12SChuck Lever kfree(tb);
585616c66364SChuck Lever }
58571da177e4SLinus Torvalds }
58581da177e4SLinus Torvalds
58591da177e4SLinus Torvalds bool
nfs4svc_decode_compoundargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)5860d9b74bdaSChuck Lever nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
58611da177e4SLinus Torvalds {
58621da177e4SLinus Torvalds struct nfsd4_compoundargs *args = rqstp->rq_argp;
5863130e2054SChuck Lever
5864fda49441SChuck Lever /* svcxdr_tmp_alloc */
58651da177e4SLinus Torvalds args->to_free = NULL;
586663f8de37SChristoph Hellwig
5867fda49441SChuck Lever args->xdr = xdr;
58686ac90391SJ. Bruce Fields args->ops = args->iops;
58693b0ebb25SChuck Lever args->rqstp = rqstp;
58703b0ebb25SChuck Lever
58713b0ebb25SChuck Lever return nfsd4_decode_compound(args);
58723b0ebb25SChuck Lever }
58733b0ebb25SChuck Lever
5874cc028a10SChuck Lever bool
nfs4svc_encode_compoundres(struct svc_rqst * rqstp,struct xdr_stream * xdr)58753b0ebb25SChuck Lever nfs4svc_encode_compoundres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
58761da177e4SLinus Torvalds {
58771da177e4SLinus Torvalds struct nfsd4_compoundres *resp = rqstp->rq_resp;
58781da177e4SLinus Torvalds __be32 *p;
58791da177e4SLinus Torvalds
58801da177e4SLinus Torvalds /*
5881b607664eSTrond Myklebust * Send buffer space for the following items is reserved
5882130e2054SChuck Lever * at the top of nfsd4_proc_compound().
58831da177e4SLinus Torvalds */
5884 p = resp->statusp;
5885
5886 *p++ = resp->cstate.status;
5887 *p++ = htonl(resp->taglen);
5888 memcpy(p, resp->tag, resp->taglen);
5889 p += XDR_QUADLEN(resp->taglen);
5890 *p++ = htonl(resp->opcnt);
5891
5892 nfsd4_sequence_done(resp);
5893 return true;
5894 }
5895