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,u32 len)121d5e23383SJ. Bruce Fields svcxdr_tmpalloc(struct nfsd4_compoundargs *argp, u32 len)
1221da177e4SLinus Torvalds {
123d5e23383SJ. Bruce Fields struct svcxdr_tmpbuf *tb;
1241da177e4SLinus Torvalds
125d5e23383SJ. Bruce Fields tb = kmalloc(sizeof(*tb) + 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,u32 len)14129c353b3SJ. Bruce Fields svcxdr_dupstr(struct nfsd4_compoundargs *argp, void *buf, u32 len)
14229c353b3SJ. Bruce Fields {
143d5e23383SJ. Bruce Fields char *p = svcxdr_tmpalloc(argp, 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,u32 len)1537b723008SChuck Lever svcxdr_savemem(struct nfsd4_compoundargs *argp, __be32 *p, u32 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)
12501da177e4SLinus Torvalds {
1251e78e274eSKees Cook struct nfsd4_read *read = &u->read;
12523909c3bcSChuck Lever __be32 status;
12531da177e4SLinus Torvalds
12543fdc5464SChuck Lever memset(read, 0, sizeof(*read));
12553909c3bcSChuck Lever status = nfsd4_decode_stateid4(argp, &read->rd_stateid);
1256e31a1b66SBenny Halevy if (status)
1257e31a1b66SBenny Halevy return status;
12583909c3bcSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &read->rd_offset) < 0)
12593909c3bcSChuck Lever return nfserr_bad_xdr;
12603909c3bcSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &read->rd_length) < 0)
12613909c3bcSChuck Lever return nfserr_bad_xdr;
12621da177e4SLinus Torvalds
12633909c3bcSChuck Lever return nfs_ok;
12641da177e4SLinus Torvalds }
12651da177e4SLinus Torvalds
1266b37ad28bSAl Viro static __be32
nfsd4_decode_readdir(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1267e78e274eSKees Cook nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
12681da177e4SLinus Torvalds {
1269e78e274eSKees Cook struct nfsd4_readdir *readdir = &u->readdir;
12700dfaf2a3SChuck Lever __be32 status;
12711da177e4SLinus Torvalds
12723fdc5464SChuck Lever memset(readdir, 0, sizeof(*readdir));
12730dfaf2a3SChuck Lever if (xdr_stream_decode_u64(argp->xdr, &readdir->rd_cookie) < 0)
12740dfaf2a3SChuck Lever return nfserr_bad_xdr;
12750dfaf2a3SChuck Lever status = nfsd4_decode_verifier4(argp, &readdir->rd_verf);
12760dfaf2a3SChuck Lever if (status)
12770dfaf2a3SChuck Lever return status;
12780dfaf2a3SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &readdir->rd_dircount) < 0)
12790dfaf2a3SChuck Lever return nfserr_bad_xdr;
12800dfaf2a3SChuck 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;
12851da177e4SLinus Torvalds
12860dfaf2a3SChuck Lever return nfs_ok;
12871da177e4SLinus Torvalds }
12881da177e4SLinus Torvalds
1289b37ad28bSAl Viro static __be32
nfsd4_decode_remove(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1290e78e274eSKees Cook nfsd4_decode_remove(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
12911da177e4SLinus Torvalds {
1292e78e274eSKees Cook struct nfsd4_remove *remove = &u->remove;
12933fdc5464SChuck Lever memset(&remove->rm_cinfo, 0, sizeof(remove->rm_cinfo));
1294b7f5fbf2SChuck 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;
1301ba881a0aSChuck Lever __be32 status;
13021da177e4SLinus Torvalds
13033fdc5464SChuck Lever memset(rename, 0, sizeof(*rename));
1304ba881a0aSChuck Lever status = nfsd4_decode_component4(argp, &rename->rn_sname, &rename->rn_snamelen);
1305ba881a0aSChuck Lever if (status)
13061da177e4SLinus Torvalds return status;
1307ba881a0aSChuck Lever return nfsd4_decode_component4(argp, &rename->rn_tname, &rename->rn_tnamelen);
13081da177e4SLinus Torvalds }
13091da177e4SLinus Torvalds
1310b37ad28bSAl Viro static __be32
nfsd4_decode_renew(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1311e78e274eSKees Cook nfsd4_decode_renew(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
13121da177e4SLinus Torvalds {
1313e78e274eSKees Cook clientid_t *clientid = &u->renew;
1314d12f9045SChuck Lever return nfsd4_decode_clientid4(argp, clientid);
13151da177e4SLinus Torvalds }
13161da177e4SLinus Torvalds
1317b37ad28bSAl Viro static __be32
nfsd4_decode_secinfo(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1318dcb488a3SAndy Adamson nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp,
1319e78e274eSKees Cook union nfsd4_op_u *u)
1320dcb488a3SAndy Adamson {
1321e78e274eSKees Cook struct nfsd4_secinfo *secinfo = &u->secinfo;
13223fdc5464SChuck Lever secinfo->si_exp = NULL;
1323d0abdae5SChuck Lever return nfsd4_decode_component4(argp, &secinfo->si_name, &secinfo->si_namelen);
1324dcb488a3SAndy Adamson }
1325dcb488a3SAndy Adamson
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)
13281da177e4SLinus Torvalds {
1329e78e274eSKees Cook struct nfsd4_setattr *setattr = &u->setattr;
1330e31a1b66SBenny Halevy __be32 status;
13311da177e4SLinus Torvalds
13323fdc5464SChuck Lever memset(setattr, 0, sizeof(*setattr));
133344592fe9SChuck Lever status = nfsd4_decode_stateid4(argp, &setattr->sa_stateid);
1334e31a1b66SBenny Halevy if (status)
1335e31a1b66SBenny Halevy return status;
1336d1c263a0SChuck Lever return nfsd4_decode_fattr4(argp, setattr->sa_bmval,
1337d1c263a0SChuck Lever ARRAY_SIZE(setattr->sa_bmval),
1338d1c263a0SChuck Lever &setattr->sa_iattr, &setattr->sa_acl,
1339d1c263a0SChuck Lever &setattr->sa_label, NULL);
13401da177e4SLinus Torvalds }
13411da177e4SLinus Torvalds
1342b37ad28bSAl Viro static __be32
nfsd4_decode_setclientid(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1343e78e274eSKees Cook nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
13441da177e4SLinus Torvalds {
1345e78e274eSKees Cook struct nfsd4_setclientid *setclientid = &u->setclientid;
134692fa6c08SChuck Lever __be32 *p, status;
13471da177e4SLinus Torvalds
13483fdc5464SChuck Lever memset(setclientid, 0, sizeof(*setclientid));
13493fdc5464SChuck Lever
1350e1a90ebdSAnna Schumaker if (argp->minorversion >= 1)
1351e1a90ebdSAnna Schumaker return nfserr_notsupp;
1352e1a90ebdSAnna Schumaker
135392fa6c08SChuck Lever status = nfsd4_decode_verifier4(argp, &setclientid->se_verf);
135492fa6c08SChuck Lever if (status)
135592fa6c08SChuck Lever return status;
1356a084daf5SJ. Bruce Fields status = nfsd4_decode_opaque(argp, &setclientid->se_name);
1357a084daf5SJ. Bruce Fields if (status)
135892fa6c08SChuck Lever return status;
135992fa6c08SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &setclientid->se_callback_prog) < 0)
1360a084daf5SJ. Bruce Fields 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);
136492fa6c08SChuck Lever if (!p)
136592fa6c08SChuck Lever return nfserr_bad_xdr;
13667b723008SChuck Lever setclientid->se_callback_netid_val = svcxdr_savemem(argp, p,
136792fa6c08SChuck Lever setclientid->se_callback_netid_len);
136892fa6c08SChuck Lever if (!setclientid->se_callback_netid_val)
136992fa6c08SChuck Lever return nfserr_jukebox;
13701da177e4SLinus Torvalds
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);
137492fa6c08SChuck Lever if (!p)
137592fa6c08SChuck Lever return nfserr_bad_xdr;
13767b723008SChuck Lever setclientid->se_callback_addr_val = svcxdr_savemem(argp, p,
137792fa6c08SChuck Lever setclientid->se_callback_addr_len);
137892fa6c08SChuck Lever 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;
13821da177e4SLinus Torvalds
138392fa6c08SChuck Lever return nfs_ok;
13841da177e4SLinus Torvalds }
13851da177e4SLinus Torvalds
1386b37ad28bSAl Viro static __be32
nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1387e78e274eSKees Cook nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp,
1388e78e274eSKees Cook union nfsd4_op_u *u)
13891da177e4SLinus Torvalds {
1390e78e274eSKees Cook struct nfsd4_setclientid_confirm *scd_c = &u->setclientid_confirm;
1391d1ca5514SChuck Lever __be32 status;
13921da177e4SLinus Torvalds
1393e1a90ebdSAnna Schumaker if (argp->minorversion >= 1)
1394e1a90ebdSAnna Schumaker return nfserr_notsupp;
1395e1a90ebdSAnna Schumaker
1396d1ca5514SChuck Lever status = nfsd4_decode_clientid4(argp, &scd_c->sc_clientid);
1397d1ca5514SChuck Lever if (status)
1398d1ca5514SChuck Lever return status;
1399d1ca5514SChuck Lever return nfsd4_decode_verifier4(argp, &scd_c->sc_confirm);
14001da177e4SLinus Torvalds }
14011da177e4SLinus Torvalds
14021da177e4SLinus Torvalds /* Also used for NVERIFY */
1403b37ad28bSAl Viro static __be32
nfsd4_decode_verify(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1404e78e274eSKees Cook nfsd4_decode_verify(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
14051da177e4SLinus Torvalds {
1406e78e274eSKees Cook struct nfsd4_verify *verify = &u->verify;
140767cd453eSChuck Lever __be32 *p, status;
14081da177e4SLinus Torvalds
14093fdc5464SChuck Lever memset(verify, 0, sizeof(*verify));
14103fdc5464SChuck Lever
141167cd453eSChuck Lever status = nfsd4_decode_bitmap4(argp, verify->ve_bmval,
141267cd453eSChuck Lever ARRAY_SIZE(verify->ve_bmval));
141367cd453eSChuck Lever if (status)
141467cd453eSChuck Lever return status;
14151da177e4SLinus Torvalds
14161da177e4SLinus Torvalds /* For convenience's sake, we compare raw xdr'd attributes in
1417e5f95703SJ. Bruce Fields * nfsd4_proc_verify */
1418e5f95703SJ. Bruce Fields
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)
142367cd453eSChuck Lever return nfserr_bad_xdr;
14247b723008SChuck Lever verify->ve_attrval = svcxdr_savemem(argp, p, verify->ve_attrlen);
142567cd453eSChuck Lever if (!verify->ve_attrval)
142667cd453eSChuck Lever return nfserr_jukebox;
14271da177e4SLinus Torvalds
142867cd453eSChuck Lever return nfs_ok;
14291da177e4SLinus Torvalds }
14301da177e4SLinus Torvalds
1431b37ad28bSAl Viro static __be32
nfsd4_decode_write(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1432e78e274eSKees Cook nfsd4_decode_write(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
14331da177e4SLinus Torvalds {
1434e78e274eSKees Cook struct nfsd4_write *write = &u->write;
1435244e2befSChuck Lever __be32 status;
14361da177e4SLinus Torvalds
1437244e2befSChuck Lever status = nfsd4_decode_stateid4(argp, &write->wr_stateid);
1438e31a1b66SBenny Halevy if (status)
1439e31a1b66SBenny Halevy return status;
1440244e2befSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &write->wr_offset) < 0)
1441244e2befSChuck Lever return nfserr_bad_xdr;
1442244e2befSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &write->wr_stable_how) < 0)
1443244e2befSChuck Lever return nfserr_bad_xdr;
144454bbb7d2SKinglong Mee if (write->wr_stable_how > NFS_FILE_SYNC)
1445244e2befSChuck Lever return nfserr_bad_xdr;
1446244e2befSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &write->wr_buflen) < 0)
1447244e2befSChuck Lever return nfserr_bad_xdr;
1448c1346a12SChuck Lever if (!xdr_stream_subsegment(argp->xdr, &write->wr_payload, write->wr_buflen))
1449244e2befSChuck Lever return nfserr_bad_xdr;
14501da177e4SLinus Torvalds
14513fdc5464SChuck Lever write->wr_bytes_written = 0;
14523fdc5464SChuck Lever write->wr_how_written = 0;
14533fdc5464SChuck Lever memset(&write->wr_verifier, 0, sizeof(write->wr_verifier));
1454244e2befSChuck Lever return nfs_ok;
14551da177e4SLinus Torvalds }
14561da177e4SLinus Torvalds
1457b37ad28bSAl Viro static __be32
nfsd4_decode_release_lockowner(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1458e78e274eSKees Cook nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp,
1459e78e274eSKees Cook union nfsd4_op_u *u)
14601da177e4SLinus Torvalds {
1461e78e274eSKees Cook struct nfsd4_release_lockowner *rlockowner = &u->release_lockowner;
1462a4a80c15SChuck Lever __be32 status;
14631da177e4SLinus Torvalds
1464e1a90ebdSAnna Schumaker if (argp->minorversion >= 1)
1465e1a90ebdSAnna Schumaker return nfserr_notsupp;
1466e1a90ebdSAnna Schumaker
1467a4a80c15SChuck Lever status = nfsd4_decode_state_owner4(argp, &rlockowner->rl_clientid,
1468a4a80c15SChuck Lever &rlockowner->rl_owner);
1469a4a80c15SChuck Lever if (status)
1470a4a80c15SChuck Lever return status;
14711da177e4SLinus Torvalds
147260adfc50SAndy Adamson if (argp->minorversion && !zero_clientid(&rlockowner->rl_clientid))
147360adfc50SAndy Adamson return nfserr_inval;
1474a4a80c15SChuck Lever
1475a4a80c15SChuck Lever return nfs_ok;
14761da177e4SLinus Torvalds }
14771da177e4SLinus Torvalds
nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1478e78e274eSKees Cook static __be32 nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs *argp,
1479e78e274eSKees Cook union nfsd4_op_u *u)
14800f81d960SChuck Lever {
1481e78e274eSKees Cook struct nfsd4_backchannel_ctl *bc = &u->backchannel_ctl;
14823fdc5464SChuck Lever memset(bc, 0, sizeof(*bc));
14830f81d960SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &bc->bc_cb_program) < 0)
14840f81d960SChuck Lever return nfserr_bad_xdr;
14850f81d960SChuck Lever return nfsd4_decode_cb_sec(argp, &bc->bc_cb_sec);
14860f81d960SChuck Lever }
14870f81d960SChuck Lever
nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1488e78e274eSKees Cook static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp,
1489e78e274eSKees Cook union nfsd4_op_u *u)
1490571e0451SChuck Lever {
1491e78e274eSKees Cook struct nfsd4_bind_conn_to_session *bcts = &u->bind_conn_to_session;
1492571e0451SChuck Lever u32 use_conn_in_rdma_mode;
1493571e0451SChuck Lever __be32 status;
1494571e0451SChuck Lever
14953fdc5464SChuck Lever memset(bcts, 0, sizeof(*bcts));
1496571e0451SChuck Lever status = nfsd4_decode_sessionid4(argp, &bcts->sessionid);
1497571e0451SChuck Lever if (status)
1498571e0451SChuck Lever return status;
1499571e0451SChuck Lever 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;
1503571e0451SChuck Lever
1504571e0451SChuck Lever return nfs_ok;
1505571e0451SChuck Lever }
1506571e0451SChuck Lever
1507b37ad28bSAl Viro static __be32
nfsd4_decode_state_protect_ops(struct nfsd4_compoundargs * argp,struct nfsd4_exchange_id * exid)15082548aa78SChuck Lever nfsd4_decode_state_protect_ops(struct nfsd4_compoundargs *argp,
15092548aa78SChuck Lever struct nfsd4_exchange_id *exid)
15102548aa78SChuck Lever {
15112548aa78SChuck Lever __be32 status;
15122548aa78SChuck Lever
15132548aa78SChuck Lever status = nfsd4_decode_bitmap4(argp, exid->spo_must_enforce,
15142548aa78SChuck Lever ARRAY_SIZE(exid->spo_must_enforce));
15152548aa78SChuck Lever 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
1525547bfeb4SChuck Lever /*
1526547bfeb4SChuck Lever * This implementation currently does not support SP4_SSV.
1527547bfeb4SChuck Lever * This decoder simply skips over these arguments.
1528547bfeb4SChuck Lever */
1529547bfeb4SChuck Lever static noinline __be32
nfsd4_decode_ssv_sp_parms(struct nfsd4_compoundargs * argp,struct nfsd4_exchange_id * exid)1530547bfeb4SChuck Lever nfsd4_decode_ssv_sp_parms(struct nfsd4_compoundargs *argp,
1531547bfeb4SChuck Lever struct nfsd4_exchange_id *exid)
1532547bfeb4SChuck 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
15672548aa78SChuck Lever static __be32
nfsd4_decode_state_protect4_a(struct nfsd4_compoundargs * argp,struct nfsd4_exchange_id * exid)1568523ec6edSChuck Lever nfsd4_decode_state_protect4_a(struct nfsd4_compoundargs *argp,
15690733d213SAndy Adamson struct nfsd4_exchange_id *exid)
15702db134ebSAndy Adamson {
1571523ec6edSChuck Lever __be32 status;
15720733d213SAndy Adamson
1573523ec6edSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &exid->spa_how) < 0)
1574a084daf5SJ. Bruce Fields return nfserr_bad_xdr;
15750733d213SAndy Adamson switch (exid->spa_how) {
15760733d213SAndy Adamson case SP4_NONE:
15770733d213SAndy Adamson break;
15780733d213SAndy Adamson case SP4_MACH_CRED:
15792548aa78SChuck Lever status = nfsd4_decode_state_protect_ops(argp, exid);
1580ed941643SAndrew Elble if (status)
15812548aa78SChuck Lever return status;
15820733d213SAndy Adamson break;
15830733d213SAndy Adamson case SP4_SSV:
1584547bfeb4SChuck Lever status = nfsd4_decode_ssv_sp_parms(argp, exid);
15852548aa78SChuck Lever if (status)
15862548aa78SChuck Lever return status;
15870733d213SAndy Adamson break;
15880733d213SAndy Adamson default:
1589523ec6edSChuck Lever return nfserr_bad_xdr;
15900733d213SAndy Adamson }
15910733d213SAndy Adamson
1592523ec6edSChuck Lever return nfs_ok;
1593523ec6edSChuck Lever }
1594523ec6edSChuck Lever
1595523ec6edSChuck Lever static __be32
nfsd4_decode_nfs_impl_id4(struct nfsd4_compoundargs * argp,struct nfsd4_exchange_id * exid)159610ff8422SChuck Lever nfsd4_decode_nfs_impl_id4(struct nfsd4_compoundargs *argp,
159710ff8422SChuck Lever struct nfsd4_exchange_id *exid)
159810ff8422SChuck Lever {
159910ff8422SChuck Lever __be32 status;
160010ff8422SChuck Lever u32 count;
160110ff8422SChuck Lever
160210ff8422SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
160310ff8422SChuck 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)1632523ec6edSChuck Lever nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
1633e78e274eSKees Cook union nfsd4_op_u *u)
1634523ec6edSChuck Lever {
1635e78e274eSKees Cook struct nfsd4_exchange_id *exid = &u->exchange_id;
163610ff8422SChuck Lever __be32 status;
1637523ec6edSChuck Lever
16383fdc5464SChuck Lever memset(exid, 0, sizeof(*exid));
1639523ec6edSChuck Lever status = nfsd4_decode_verifier4(argp, &exid->verifier);
1640523ec6edSChuck Lever if (status)
1641523ec6edSChuck Lever return status;
1642523ec6edSChuck Lever status = nfsd4_decode_opaque(argp, &exid->clname);
1643523ec6edSChuck Lever if (status)
1644523ec6edSChuck Lever return status;
1645523ec6edSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &exid->flags) < 0)
1646523ec6edSChuck Lever return nfserr_bad_xdr;
1647523ec6edSChuck Lever status = nfsd4_decode_state_protect4_a(argp, exid);
1648523ec6edSChuck Lever if (status)
1649523ec6edSChuck Lever return status;
165010ff8422SChuck Lever return nfsd4_decode_nfs_impl_id4(argp, exid);
16512db134ebSAndy Adamson }
16522db134ebSAndy Adamson
16532db134ebSAndy Adamson static __be32
nfsd4_decode_channel_attrs4(struct nfsd4_compoundargs * argp,struct nfsd4_channel_attrs * ca)16543a3f1fbaSChuck Lever nfsd4_decode_channel_attrs4(struct nfsd4_compoundargs *argp,
16553a3f1fbaSChuck Lever struct nfsd4_channel_attrs *ca)
16563a3f1fbaSChuck Lever {
16573a3f1fbaSChuck Lever __be32 *p;
16583a3f1fbaSChuck Lever
16593a3f1fbaSChuck Lever p = xdr_inline_decode(argp->xdr, XDR_UNIT * 7);
16603a3f1fbaSChuck Lever if (!p)
16613a3f1fbaSChuck Lever 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)16862db134ebSAndy Adamson nfsd4_decode_create_session(struct nfsd4_compoundargs *argp,
1687e78e274eSKees Cook union nfsd4_op_u *u)
16882db134ebSAndy Adamson {
1689e78e274eSKees Cook struct nfsd4_create_session *sess = &u->create_session;
169081243e3fSChuck Lever __be32 status;
1691ec6b5d7bSAndy Adamson
16923fdc5464SChuck Lever memset(sess, 0, sizeof(*sess));
169381243e3fSChuck Lever status = nfsd4_decode_clientid4(argp, &sess->clientid);
169481243e3fSChuck Lever if (status)
169581243e3fSChuck Lever return status;
169681243e3fSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &sess->seqid) < 0)
169781243e3fSChuck Lever return nfserr_bad_xdr;
169881243e3fSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &sess->flags) < 0)
169981243e3fSChuck Lever return nfserr_bad_xdr;
17003a3f1fbaSChuck Lever status = nfsd4_decode_channel_attrs4(argp, &sess->fore_channel);
17013a3f1fbaSChuck Lever if (status)
17023a3f1fbaSChuck Lever return status;
17033a3f1fbaSChuck Lever status = nfsd4_decode_channel_attrs4(argp, &sess->back_channel);
17043a3f1fbaSChuck Lever if (status)
17053a3f1fbaSChuck Lever return status;
170681243e3fSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &sess->callback_prog) < 0)
170781243e3fSChuck Lever return nfserr_bad_xdr;
17083fdc5464SChuck Lever return nfsd4_decode_cb_sec(argp, &sess->cb_sec);
17092db134ebSAndy Adamson }
17102db134ebSAndy Adamson
17112db134ebSAndy Adamson static __be32
nfsd4_decode_destroy_session(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)17122db134ebSAndy Adamson nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp,
1713e78e274eSKees Cook union nfsd4_op_u *u)
17142db134ebSAndy Adamson {
1715e78e274eSKees Cook struct nfsd4_destroy_session *destroy_session = &u->destroy_session;
171694e254afSChuck 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)1720e1ca12dfSBryan Schumaker nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp,
1721e78e274eSKees Cook union nfsd4_op_u *u)
1722e1ca12dfSBryan Schumaker {
1723e78e274eSKees Cook struct nfsd4_free_stateid *free_stateid = &u->free_stateid;
1724aec387d5SChuck Lever return nfsd4_decode_stateid4(argp, &free_stateid->fr_stateid);
1725e1ca12dfSBryan Schumaker }
1726e1ca12dfSBryan Schumaker
17279cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
17289cf514ccSChristoph Hellwig static __be32
nfsd4_decode_getdeviceinfo(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)17299cf514ccSChristoph Hellwig nfsd4_decode_getdeviceinfo(struct nfsd4_compoundargs *argp,
1730e78e274eSKees Cook union nfsd4_op_u *u)
17319cf514ccSChristoph Hellwig {
1732e78e274eSKees Cook struct nfsd4_getdeviceinfo *gdev = &u->getdeviceinfo;
173304495971SChuck Lever __be32 status;
17349cf514ccSChristoph Hellwig
17353fdc5464SChuck Lever memset(gdev, 0, sizeof(*gdev));
173604495971SChuck Lever status = nfsd4_decode_deviceid4(argp, &gdev->gd_devid);
173704495971SChuck Lever if (status)
173804495971SChuck Lever return status;
173904495971SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &gdev->gd_layout_type) < 0)
174004495971SChuck Lever return nfserr_bad_xdr;
174104495971SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &gdev->gd_maxcount) < 0)
174204495971SChuck Lever return nfserr_bad_xdr;
174304495971SChuck Lever if (xdr_stream_decode_uint32_array(argp->xdr,
174404495971SChuck Lever &gdev->gd_notify_types, 1) < 0)
174504495971SChuck Lever return nfserr_bad_xdr;
174604495971SChuck Lever
174704495971SChuck Lever return nfs_ok;
17489cf514ccSChristoph Hellwig }
17499cf514ccSChristoph Hellwig
17509cf514ccSChristoph Hellwig static __be32
nfsd4_decode_layoutcommit(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)17515185980dSChuck Lever nfsd4_decode_layoutcommit(struct nfsd4_compoundargs *argp,
1752e78e274eSKees Cook union nfsd4_op_u *u)
17535185980dSChuck Lever {
1754e78e274eSKees Cook struct nfsd4_layoutcommit *lcp = &u->layoutcommit;
17555185980dSChuck Lever __be32 *p, status;
17565185980dSChuck Lever
17573fdc5464SChuck Lever memset(lcp, 0, sizeof(*lcp));
17585185980dSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &lcp->lc_seg.offset) < 0)
17595185980dSChuck Lever return nfserr_bad_xdr;
17605185980dSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &lcp->lc_seg.length) < 0)
17615185980dSChuck Lever return nfserr_bad_xdr;
17625185980dSChuck Lever if (xdr_stream_decode_bool(argp->xdr, &lcp->lc_reclaim) < 0)
17635185980dSChuck Lever return nfserr_bad_xdr;
17645185980dSChuck Lever status = nfsd4_decode_stateid4(argp, &lcp->lc_sid);
17655185980dSChuck Lever if (status)
17665185980dSChuck Lever return status;
17675185980dSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lcp->lc_newoffset) < 0)
17685185980dSChuck Lever return nfserr_bad_xdr;
17695185980dSChuck Lever if (lcp->lc_newoffset) {
17705185980dSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &lcp->lc_last_wr) < 0)
17715185980dSChuck Lever return nfserr_bad_xdr;
17725185980dSChuck Lever } else
17735185980dSChuck Lever lcp->lc_last_wr = 0;
17745185980dSChuck Lever p = xdr_inline_decode(argp->xdr, XDR_UNIT);
17755185980dSChuck Lever if (!p)
17765185980dSChuck Lever return nfserr_bad_xdr;
17775185980dSChuck Lever if (xdr_item_is_present(p)) {
17785185980dSChuck Lever status = nfsd4_decode_nfstime4(argp, &lcp->lc_mtime);
17795185980dSChuck Lever if (status)
17805185980dSChuck Lever return status;
17815185980dSChuck Lever } else {
17825185980dSChuck Lever lcp->lc_mtime.tv_nsec = UTIME_NOW;
17835185980dSChuck Lever }
17845185980dSChuck Lever return nfsd4_decode_layoutupdate4(argp, lcp);
17855185980dSChuck Lever }
17865185980dSChuck Lever
17875185980dSChuck Lever static __be32
nfsd4_decode_layoutget(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)17889cf514ccSChristoph Hellwig nfsd4_decode_layoutget(struct nfsd4_compoundargs *argp,
1789e78e274eSKees Cook union nfsd4_op_u *u)
17909cf514ccSChristoph Hellwig {
1791e78e274eSKees Cook struct nfsd4_layoutget *lgp = &u->layoutget;
1792c8e88e3aSChuck Lever __be32 status;
17939cf514ccSChristoph Hellwig
17943fdc5464SChuck Lever memset(lgp, 0, sizeof(*lgp));
1795c8e88e3aSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_signal) < 0)
1796c8e88e3aSChuck Lever return nfserr_bad_xdr;
1797c8e88e3aSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_layout_type) < 0)
1798c8e88e3aSChuck Lever return nfserr_bad_xdr;
1799c8e88e3aSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_seg.iomode) < 0)
1800c8e88e3aSChuck Lever return nfserr_bad_xdr;
1801c8e88e3aSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &lgp->lg_seg.offset) < 0)
1802c8e88e3aSChuck Lever return nfserr_bad_xdr;
1803c8e88e3aSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &lgp->lg_seg.length) < 0)
1804c8e88e3aSChuck Lever return nfserr_bad_xdr;
1805c8e88e3aSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &lgp->lg_minlength) < 0)
1806c8e88e3aSChuck Lever return nfserr_bad_xdr;
1807c8e88e3aSChuck Lever status = nfsd4_decode_stateid4(argp, &lgp->lg_sid);
1808db59c0efSKinglong Mee if (status)
1809db59c0efSKinglong Mee return status;
1810c8e88e3aSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_maxcount) < 0)
1811c8e88e3aSChuck Lever return nfserr_bad_xdr;
1812db59c0efSKinglong Mee
1813c8e88e3aSChuck Lever return nfs_ok;
18149cf514ccSChristoph Hellwig }
18159cf514ccSChristoph Hellwig
18169cf514ccSChristoph Hellwig static __be32
nfsd4_decode_layoutreturn(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)18179cf514ccSChristoph Hellwig nfsd4_decode_layoutreturn(struct nfsd4_compoundargs *argp,
1818e78e274eSKees Cook union nfsd4_op_u *u)
18199cf514ccSChristoph Hellwig {
1820e78e274eSKees Cook struct nfsd4_layoutreturn *lrp = &u->layoutreturn;
18213fdc5464SChuck Lever memset(lrp, 0, sizeof(*lrp));
1822645fcad3SChuck Lever if (xdr_stream_decode_bool(argp->xdr, &lrp->lr_reclaim) < 0)
1823645fcad3SChuck Lever return nfserr_bad_xdr;
1824645fcad3SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lrp->lr_layout_type) < 0)
1825645fcad3SChuck Lever return nfserr_bad_xdr;
1826645fcad3SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &lrp->lr_seg.iomode) < 0)
1827645fcad3SChuck Lever return nfserr_bad_xdr;
1828645fcad3SChuck Lever return nfsd4_decode_layoutreturn4(argp, lrp);
18299cf514ccSChristoph Hellwig }
18309cf514ccSChristoph Hellwig #endif /* CONFIG_NFSD_PNFS */
18319cf514ccSChristoph Hellwig
nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)183253d70873SChuck Lever static __be32 nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp,
1833e78e274eSKees Cook union nfsd4_op_u *u)
183453d70873SChuck Lever {
1835e78e274eSKees Cook struct nfsd4_secinfo_no_name *sin = &u->secinfo_no_name;
183653d70873SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &sin->sin_style) < 0)
183753d70873SChuck Lever return nfserr_bad_xdr;
18383fdc5464SChuck Lever
18393fdc5464SChuck Lever sin->sin_exp = NULL;
184053d70873SChuck Lever return nfs_ok;
184153d70873SChuck Lever }
184253d70873SChuck Lever
18432db134ebSAndy Adamson static __be32
nfsd4_decode_sequence(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1844cf907b11SChuck Lever nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
1845e78e274eSKees Cook union nfsd4_op_u *u)
1846cf907b11SChuck Lever {
1847e78e274eSKees Cook struct nfsd4_sequence *seq = &u->sequence;
1848cf907b11SChuck Lever __be32 *p, status;
1849cf907b11SChuck Lever
1850cf907b11SChuck Lever status = nfsd4_decode_sessionid4(argp, &seq->sessionid);
1851cf907b11SChuck Lever if (status)
1852cf907b11SChuck Lever return status;
1853cf907b11SChuck Lever p = xdr_inline_decode(argp->xdr, XDR_UNIT * 4);
1854cf907b11SChuck Lever if (!p)
1855cf907b11SChuck Lever return nfserr_bad_xdr;
1856cf907b11SChuck Lever seq->seqid = be32_to_cpup(p++);
1857cf907b11SChuck Lever seq->slotid = be32_to_cpup(p++);
1858cf907b11SChuck Lever seq->maxslots = be32_to_cpup(p++);
1859cf907b11SChuck Lever seq->cachethis = be32_to_cpup(p);
1860cf907b11SChuck Lever
18613fdc5464SChuck Lever seq->status_flags = 0;
1862cf907b11SChuck Lever return nfs_ok;
1863cf907b11SChuck Lever }
1864cf907b11SChuck Lever
1865cf907b11SChuck Lever static __be32
nfsd4_decode_test_stateid(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1866e78e274eSKees Cook nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp,
1867e78e274eSKees Cook union nfsd4_op_u *u)
1868b7a0c8f6SChuck Lever {
1869e78e274eSKees Cook struct nfsd4_test_stateid *test_stateid = &u->test_stateid;
1870b7a0c8f6SChuck Lever struct nfsd4_test_stateid_id *stateid;
1871b7a0c8f6SChuck Lever __be32 status;
1872b7a0c8f6SChuck Lever u32 i;
1873b7a0c8f6SChuck Lever
18743fdc5464SChuck Lever memset(test_stateid, 0, sizeof(*test_stateid));
1875b7a0c8f6SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &test_stateid->ts_num_ids) < 0)
1876b7a0c8f6SChuck Lever return nfserr_bad_xdr;
1877b7a0c8f6SChuck Lever
1878b7a0c8f6SChuck Lever INIT_LIST_HEAD(&test_stateid->ts_stateid_list);
1879b7a0c8f6SChuck Lever for (i = 0; i < test_stateid->ts_num_ids; i++) {
1880b7a0c8f6SChuck Lever stateid = svcxdr_tmpalloc(argp, sizeof(*stateid));
1881b7a0c8f6SChuck Lever if (!stateid)
1882bb4d8427SChuck Lever return nfserr_jukebox;
1883b7a0c8f6SChuck Lever INIT_LIST_HEAD(&stateid->ts_id_list);
1884b7a0c8f6SChuck Lever list_add_tail(&stateid->ts_id_list, &test_stateid->ts_stateid_list);
1885b7a0c8f6SChuck Lever status = nfsd4_decode_stateid4(argp, &stateid->ts_id_stateid);
1886b7a0c8f6SChuck Lever if (status)
1887b7a0c8f6SChuck Lever return status;
1888b7a0c8f6SChuck Lever }
1889b7a0c8f6SChuck Lever
1890b7a0c8f6SChuck Lever return nfs_ok;
1891b7a0c8f6SChuck Lever }
1892b7a0c8f6SChuck Lever
nfsd4_decode_destroy_clientid(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1893c95f2ec3SChuck Lever static __be32 nfsd4_decode_destroy_clientid(struct nfsd4_compoundargs *argp,
1894e78e274eSKees Cook union nfsd4_op_u *u)
1895c95f2ec3SChuck Lever {
1896e78e274eSKees Cook struct nfsd4_destroy_clientid *dc = &u->destroy_clientid;
1897c95f2ec3SChuck Lever return nfsd4_decode_clientid4(argp, &dc->clientid);
1898c95f2ec3SChuck Lever }
1899c95f2ec3SChuck Lever
nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)19000d646784SChuck Lever static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp,
1901e78e274eSKees Cook union nfsd4_op_u *u)
19020d646784SChuck Lever {
1903e78e274eSKees Cook struct nfsd4_reclaim_complete *rc = &u->reclaim_complete;
19040d646784SChuck Lever if (xdr_stream_decode_bool(argp->xdr, &rc->rca_one_fs) < 0)
19050d646784SChuck Lever return nfserr_bad_xdr;
19060d646784SChuck Lever return nfs_ok;
19070d646784SChuck Lever }
19080d646784SChuck Lever
1909b7a0c8f6SChuck Lever static __be32
nfsd4_decode_fallocate(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)191095d871f0SAnna Schumaker nfsd4_decode_fallocate(struct nfsd4_compoundargs *argp,
1911e78e274eSKees Cook union nfsd4_op_u *u)
191295d871f0SAnna Schumaker {
1913e78e274eSKees Cook struct nfsd4_fallocate *fallocate = &u->allocate;
19146aef27aaSChuck Lever __be32 status;
191595d871f0SAnna Schumaker
19166aef27aaSChuck Lever status = nfsd4_decode_stateid4(argp, &fallocate->falloc_stateid);
191795d871f0SAnna Schumaker if (status)
191895d871f0SAnna Schumaker return status;
19196aef27aaSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &fallocate->falloc_offset) < 0)
19206aef27aaSChuck Lever return nfserr_bad_xdr;
19216aef27aaSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &fallocate->falloc_length) < 0)
19226aef27aaSChuck Lever return nfserr_bad_xdr;
192395d871f0SAnna Schumaker
19246aef27aaSChuck Lever return nfs_ok;
192595d871f0SAnna Schumaker }
192695d871f0SAnna Schumaker
nfsd4_decode_nl4_server(struct nfsd4_compoundargs * argp,struct nl4_server * ns)192784e1b21dSOlga Kornievskaia static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
192884e1b21dSOlga Kornievskaia struct nl4_server *ns)
192984e1b21dSOlga Kornievskaia {
193084e1b21dSOlga Kornievskaia struct nfs42_netaddr *naddr;
1931f49e4b4dSChuck Lever __be32 *p;
193284e1b21dSOlga Kornievskaia
1933f49e4b4dSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &ns->nl4_type) < 0)
1934f49e4b4dSChuck Lever return nfserr_bad_xdr;
193584e1b21dSOlga Kornievskaia
193684e1b21dSOlga Kornievskaia /* currently support for 1 inter-server source server */
193784e1b21dSOlga Kornievskaia switch (ns->nl4_type) {
193884e1b21dSOlga Kornievskaia case NL4_NETADDR:
193984e1b21dSOlga Kornievskaia naddr = &ns->u.nl4_addr;
194084e1b21dSOlga Kornievskaia
1941f49e4b4dSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &naddr->netid_len) < 0)
1942f49e4b4dSChuck Lever return nfserr_bad_xdr;
194384e1b21dSOlga Kornievskaia if (naddr->netid_len > RPCBIND_MAXNETIDLEN)
1944f49e4b4dSChuck Lever return nfserr_bad_xdr;
194584e1b21dSOlga Kornievskaia
1946f49e4b4dSChuck Lever p = xdr_inline_decode(argp->xdr, naddr->netid_len);
1947f49e4b4dSChuck Lever if (!p)
1948f49e4b4dSChuck Lever return nfserr_bad_xdr;
1949f49e4b4dSChuck Lever memcpy(naddr->netid, p, naddr->netid_len);
195084e1b21dSOlga Kornievskaia
1951f49e4b4dSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &naddr->addr_len) < 0)
1952f49e4b4dSChuck Lever return nfserr_bad_xdr;
195384e1b21dSOlga Kornievskaia if (naddr->addr_len > RPCBIND_MAXUADDRLEN)
1954f49e4b4dSChuck Lever return nfserr_bad_xdr;
195584e1b21dSOlga Kornievskaia
1956f49e4b4dSChuck Lever p = xdr_inline_decode(argp->xdr, naddr->addr_len);
1957f49e4b4dSChuck Lever if (!p)
1958f49e4b4dSChuck Lever return nfserr_bad_xdr;
1959f49e4b4dSChuck Lever memcpy(naddr->addr, p, naddr->addr_len);
196084e1b21dSOlga Kornievskaia break;
196184e1b21dSOlga Kornievskaia default:
1962f49e4b4dSChuck Lever return nfserr_bad_xdr;
196384e1b21dSOlga Kornievskaia }
1964f49e4b4dSChuck Lever
1965f49e4b4dSChuck Lever return nfs_ok;
196684e1b21dSOlga Kornievskaia }
196784e1b21dSOlga Kornievskaia
1968ffa0160aSChristoph Hellwig static __be32
nfsd4_decode_copy(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1969e78e274eSKees Cook nfsd4_decode_copy(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
197029ae7f9dSAnna Schumaker {
1971e78e274eSKees Cook struct nfsd4_copy *copy = &u->copy;
19721913cdf5SChuck Lever u32 consecutive, i, count, sync;
197384e1b21dSOlga Kornievskaia struct nl4_server *ns_dummy;
1974e8febea7SChuck Lever __be32 status;
197529ae7f9dSAnna Schumaker
19763fdc5464SChuck Lever memset(copy, 0, sizeof(*copy));
1977e8febea7SChuck Lever status = nfsd4_decode_stateid4(argp, ©->cp_src_stateid);
197829ae7f9dSAnna Schumaker if (status)
197929ae7f9dSAnna Schumaker return status;
1980e8febea7SChuck Lever status = nfsd4_decode_stateid4(argp, ©->cp_dst_stateid);
198129ae7f9dSAnna Schumaker if (status)
198229ae7f9dSAnna Schumaker return status;
1983e8febea7SChuck Lever if (xdr_stream_decode_u64(argp->xdr, ©->cp_src_pos) < 0)
1984e8febea7SChuck Lever return nfserr_bad_xdr;
1985e8febea7SChuck Lever if (xdr_stream_decode_u64(argp->xdr, ©->cp_dst_pos) < 0)
1986e8febea7SChuck Lever return nfserr_bad_xdr;
1987e8febea7SChuck Lever if (xdr_stream_decode_u64(argp->xdr, ©->cp_count) < 0)
1988e8febea7SChuck Lever return nfserr_bad_xdr;
1989e8febea7SChuck Lever /* ca_consecutive: we always do consecutive copies */
1990e8febea7SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &consecutive) < 0)
1991e8febea7SChuck Lever return nfserr_bad_xdr;
19921913cdf5SChuck Lever if (xdr_stream_decode_bool(argp->xdr, &sync) < 0)
1993e8febea7SChuck Lever return nfserr_bad_xdr;
19941913cdf5SChuck Lever nfsd4_copy_set_sync(copy, sync);
199529ae7f9dSAnna Schumaker
1996e8febea7SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
1997e8febea7SChuck Lever return nfserr_bad_xdr;
199887689df6SChuck Lever copy->cp_src = svcxdr_tmpalloc(argp, sizeof(*copy->cp_src));
199987689df6SChuck Lever if (copy->cp_src == NULL)
200087689df6SChuck Lever return nfserr_jukebox;
200184e1b21dSOlga Kornievskaia if (count == 0) { /* intra-server copy */
20021913cdf5SChuck Lever __set_bit(NFSD4_COPY_F_INTRA, ©->cp_flags);
2003e8febea7SChuck Lever return nfs_ok;
200484e1b21dSOlga Kornievskaia }
200584e1b21dSOlga Kornievskaia
2006e8febea7SChuck Lever /* decode all the supplied server addresses but use only the first */
200787689df6SChuck Lever status = nfsd4_decode_nl4_server(argp, copy->cp_src);
200884e1b21dSOlga Kornievskaia if (status)
200984e1b21dSOlga Kornievskaia return status;
201084e1b21dSOlga Kornievskaia
201184e1b21dSOlga Kornievskaia ns_dummy = kmalloc(sizeof(struct nl4_server), GFP_KERNEL);
201284e1b21dSOlga Kornievskaia if (ns_dummy == NULL)
2013bb4d8427SChuck Lever return nfserr_jukebox;
201484e1b21dSOlga Kornievskaia for (i = 0; i < count - 1; i++) {
201584e1b21dSOlga Kornievskaia status = nfsd4_decode_nl4_server(argp, ns_dummy);
201684e1b21dSOlga Kornievskaia if (status) {
201784e1b21dSOlga Kornievskaia kfree(ns_dummy);
201884e1b21dSOlga Kornievskaia return status;
201984e1b21dSOlga Kornievskaia }
202084e1b21dSOlga Kornievskaia }
202184e1b21dSOlga Kornievskaia kfree(ns_dummy);
202229ae7f9dSAnna Schumaker
2023e8febea7SChuck Lever return nfs_ok;
202429ae7f9dSAnna Schumaker }
202529ae7f9dSAnna Schumaker
202629ae7f9dSAnna Schumaker static __be32
nfsd4_decode_copy_notify(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)202751911868SOlga Kornievskaia nfsd4_decode_copy_notify(struct nfsd4_compoundargs *argp,
2028e78e274eSKees Cook union nfsd4_op_u *u)
202951911868SOlga Kornievskaia {
2030e78e274eSKees Cook struct nfsd4_copy_notify *cn = &u->copy_notify;
20315aff7d08SChuck Lever __be32 status;
203251911868SOlga Kornievskaia
20333fdc5464SChuck Lever memset(cn, 0, sizeof(*cn));
203409426ef2SChuck Lever cn->cpn_src = svcxdr_tmpalloc(argp, sizeof(*cn->cpn_src));
203509426ef2SChuck Lever if (cn->cpn_src == NULL)
203609426ef2SChuck Lever return nfserr_jukebox;
203709426ef2SChuck Lever cn->cpn_dst = svcxdr_tmpalloc(argp, sizeof(*cn->cpn_dst));
203809426ef2SChuck Lever if (cn->cpn_dst == NULL)
203909426ef2SChuck Lever return nfserr_jukebox;
204009426ef2SChuck Lever
2041f9a953fbSChuck Lever status = nfsd4_decode_stateid4(argp, &cn->cpn_src_stateid);
204251911868SOlga Kornievskaia if (status)
204351911868SOlga Kornievskaia return status;
204409426ef2SChuck Lever return nfsd4_decode_nl4_server(argp, cn->cpn_dst);
204551911868SOlga Kornievskaia }
204651911868SOlga Kornievskaia
204751911868SOlga Kornievskaia static __be32
nfsd4_decode_offload_status(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)2048f9a953fbSChuck Lever nfsd4_decode_offload_status(struct nfsd4_compoundargs *argp,
2049e78e274eSKees Cook union nfsd4_op_u *u)
2050f9a953fbSChuck Lever {
2051e78e274eSKees Cook struct nfsd4_offload_status *os = &u->offload_status;
20523fdc5464SChuck Lever os->count = 0;
20533fdc5464SChuck Lever os->status = 0;
20542846bb05SChuck Lever return nfsd4_decode_stateid4(argp, &os->stateid);
2055f9a953fbSChuck Lever }
2056f9a953fbSChuck Lever
2057f9a953fbSChuck Lever static __be32
nfsd4_decode_seek(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)2058e78e274eSKees Cook nfsd4_decode_seek(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
205924bab491SAnna Schumaker {
2060e78e274eSKees Cook struct nfsd4_seek *seek = &u->seek;
20619d32b412SChuck Lever __be32 status;
206224bab491SAnna Schumaker
20639d32b412SChuck Lever status = nfsd4_decode_stateid4(argp, &seek->seek_stateid);
206424bab491SAnna Schumaker if (status)
206524bab491SAnna Schumaker return status;
20669d32b412SChuck Lever if (xdr_stream_decode_u64(argp->xdr, &seek->seek_offset) < 0)
20679d32b412SChuck Lever return nfserr_bad_xdr;
20689d32b412SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &seek->seek_whence) < 0)
20699d32b412SChuck Lever return nfserr_bad_xdr;
207024bab491SAnna Schumaker
20713fdc5464SChuck Lever seek->seek_eof = 0;
20723fdc5464SChuck Lever seek->seek_pos = 0;
20739d32b412SChuck Lever return nfs_ok;
207424bab491SAnna Schumaker }
207524bab491SAnna Schumaker
20763dfd0b0eSChuck Lever static __be32
nfsd4_decode_clone(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)2077e78e274eSKees Cook nfsd4_decode_clone(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
20783dfd0b0eSChuck Lever {
2079e78e274eSKees Cook struct nfsd4_clone *clone = &u->clone;
20803dfd0b0eSChuck Lever __be32 status;
20813dfd0b0eSChuck Lever
20823dfd0b0eSChuck Lever status = nfsd4_decode_stateid4(argp, &clone->cl_src_stateid);
20833dfd0b0eSChuck Lever if (status)
20843dfd0b0eSChuck Lever return status;
20853dfd0b0eSChuck Lever status = nfsd4_decode_stateid4(argp, &clone->cl_dst_stateid);
20863dfd0b0eSChuck Lever if (status)
20873dfd0b0eSChuck Lever return status;
20883dfd0b0eSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &clone->cl_src_pos) < 0)
20893dfd0b0eSChuck Lever return nfserr_bad_xdr;
20903dfd0b0eSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &clone->cl_dst_pos) < 0)
20913dfd0b0eSChuck Lever return nfserr_bad_xdr;
20923dfd0b0eSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &clone->cl_count) < 0)
20933dfd0b0eSChuck Lever return nfserr_bad_xdr;
20943dfd0b0eSChuck Lever
20953dfd0b0eSChuck Lever return nfs_ok;
20963dfd0b0eSChuck Lever }
20973dfd0b0eSChuck Lever
209823e50fe3SFrank van der Linden /*
209923e50fe3SFrank van der Linden * XDR data that is more than PAGE_SIZE in size is normally part of a
210023e50fe3SFrank van der Linden * read or write. However, the size of extended attributes is limited
210123e50fe3SFrank van der Linden * by the maximum request size, and then further limited by the underlying
210223e50fe3SFrank van der Linden * filesystem limits. This can exceed PAGE_SIZE (currently, XATTR_SIZE_MAX
210323e50fe3SFrank van der Linden * is 64k). Since there is no kvec- or page-based interface to xattrs,
210423e50fe3SFrank van der Linden * and we're not dealing with contiguous pages, we need to do some copying.
210523e50fe3SFrank van der Linden */
210623e50fe3SFrank van der Linden
210723e50fe3SFrank van der Linden /*
2108c1346a12SChuck Lever * Decode data into buffer.
210923e50fe3SFrank van der Linden */
211023e50fe3SFrank van der Linden static __be32
nfsd4_vbuf_from_vector(struct nfsd4_compoundargs * argp,struct xdr_buf * xdr,char ** bufp,u32 buflen)2111c1346a12SChuck Lever nfsd4_vbuf_from_vector(struct nfsd4_compoundargs *argp, struct xdr_buf *xdr,
2112c1346a12SChuck Lever char **bufp, u32 buflen)
211323e50fe3SFrank van der Linden {
2114c1346a12SChuck Lever struct page **pages = xdr->pages;
2115c1346a12SChuck Lever struct kvec *head = xdr->head;
211623e50fe3SFrank van der Linden char *tmp, *dp;
211723e50fe3SFrank van der Linden u32 len;
211823e50fe3SFrank van der Linden
211923e50fe3SFrank van der Linden if (buflen <= head->iov_len) {
212023e50fe3SFrank van der Linden /*
212123e50fe3SFrank van der Linden * We're in luck, the head has enough space. Just return
212223e50fe3SFrank van der Linden * the head, no need for copying.
212323e50fe3SFrank van der Linden */
212423e50fe3SFrank van der Linden *bufp = head->iov_base;
212523e50fe3SFrank van der Linden return 0;
212623e50fe3SFrank van der Linden }
212723e50fe3SFrank van der Linden
212823e50fe3SFrank van der Linden tmp = svcxdr_tmpalloc(argp, buflen);
212923e50fe3SFrank van der Linden if (tmp == NULL)
213023e50fe3SFrank van der Linden return nfserr_jukebox;
213123e50fe3SFrank van der Linden
213223e50fe3SFrank van der Linden dp = tmp;
213323e50fe3SFrank van der Linden memcpy(dp, head->iov_base, head->iov_len);
213423e50fe3SFrank van der Linden buflen -= head->iov_len;
213523e50fe3SFrank van der Linden dp += head->iov_len;
213623e50fe3SFrank van der Linden
213723e50fe3SFrank van der Linden while (buflen > 0) {
213823e50fe3SFrank van der Linden len = min_t(u32, buflen, PAGE_SIZE);
213923e50fe3SFrank van der Linden memcpy(dp, page_address(*pages), len);
214023e50fe3SFrank van der Linden
214123e50fe3SFrank van der Linden buflen -= len;
214223e50fe3SFrank van der Linden dp += len;
214323e50fe3SFrank van der Linden pages++;
214423e50fe3SFrank van der Linden }
214523e50fe3SFrank van der Linden
214623e50fe3SFrank van der Linden *bufp = tmp;
214723e50fe3SFrank van der Linden return 0;
214823e50fe3SFrank van der Linden }
214923e50fe3SFrank van der Linden
215023e50fe3SFrank van der Linden /*
215123e50fe3SFrank van der Linden * Get a user extended attribute name from the XDR buffer.
215223e50fe3SFrank van der Linden * It will not have the "user." prefix, so prepend it.
215323e50fe3SFrank van der Linden * Lastly, check for nul characters in the name.
215423e50fe3SFrank van der Linden */
215523e50fe3SFrank van der Linden static __be32
nfsd4_decode_xattr_name(struct nfsd4_compoundargs * argp,char ** namep)215623e50fe3SFrank van der Linden nfsd4_decode_xattr_name(struct nfsd4_compoundargs *argp, char **namep)
215723e50fe3SFrank van der Linden {
215823e50fe3SFrank van der Linden char *name, *sp, *dp;
215923e50fe3SFrank van der Linden u32 namelen, cnt;
2160830c7150SChuck Lever __be32 *p;
216123e50fe3SFrank van der Linden
2162830c7150SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &namelen) < 0)
2163830c7150SChuck Lever return nfserr_bad_xdr;
216423e50fe3SFrank van der Linden if (namelen > (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN))
216523e50fe3SFrank van der Linden return nfserr_nametoolong;
216623e50fe3SFrank van der Linden if (namelen == 0)
2167830c7150SChuck Lever return nfserr_bad_xdr;
2168830c7150SChuck Lever p = xdr_inline_decode(argp->xdr, namelen);
2169830c7150SChuck Lever if (!p)
2170830c7150SChuck Lever return nfserr_bad_xdr;
217123e50fe3SFrank van der Linden name = svcxdr_tmpalloc(argp, namelen + XATTR_USER_PREFIX_LEN + 1);
217223e50fe3SFrank van der Linden if (!name)
217323e50fe3SFrank van der Linden return nfserr_jukebox;
217423e50fe3SFrank van der Linden memcpy(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
217523e50fe3SFrank van der Linden
217623e50fe3SFrank van der Linden /*
217723e50fe3SFrank van der Linden * Copy the extended attribute name over while checking for 0
217823e50fe3SFrank van der Linden * characters.
217923e50fe3SFrank van der Linden */
218023e50fe3SFrank van der Linden sp = (char *)p;
218123e50fe3SFrank van der Linden dp = name + XATTR_USER_PREFIX_LEN;
218223e50fe3SFrank van der Linden cnt = namelen;
218323e50fe3SFrank van der Linden
218423e50fe3SFrank van der Linden while (cnt-- > 0) {
218523e50fe3SFrank van der Linden if (*sp == '\0')
2186830c7150SChuck Lever return nfserr_bad_xdr;
218723e50fe3SFrank van der Linden *dp++ = *sp++;
218823e50fe3SFrank van der Linden }
218923e50fe3SFrank van der Linden *dp = '\0';
219023e50fe3SFrank van der Linden
219123e50fe3SFrank van der Linden *namep = name;
219223e50fe3SFrank van der Linden
2193830c7150SChuck Lever return nfs_ok;
219423e50fe3SFrank van der Linden }
219523e50fe3SFrank van der Linden
219623e50fe3SFrank van der Linden /*
219723e50fe3SFrank van der Linden * A GETXATTR op request comes without a length specifier. We just set the
219823e50fe3SFrank van der Linden * maximum length for the reply based on XATTR_SIZE_MAX and the maximum
219923e50fe3SFrank van der Linden * channel reply size. nfsd_getxattr will probe the length of the xattr,
220023e50fe3SFrank van der Linden * check it against getxa_len, and allocate + return the value.
220123e50fe3SFrank van der Linden */
220223e50fe3SFrank van der Linden static __be32
nfsd4_decode_getxattr(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)220323e50fe3SFrank van der Linden nfsd4_decode_getxattr(struct nfsd4_compoundargs *argp,
2204e78e274eSKees Cook union nfsd4_op_u *u)
220523e50fe3SFrank van der Linden {
2206e78e274eSKees Cook struct nfsd4_getxattr *getxattr = &u->getxattr;
220723e50fe3SFrank van der Linden __be32 status;
220823e50fe3SFrank van der Linden u32 maxcount;
220923e50fe3SFrank van der Linden
22103fdc5464SChuck Lever memset(getxattr, 0, sizeof(*getxattr));
221123e50fe3SFrank van der Linden status = nfsd4_decode_xattr_name(argp, &getxattr->getxa_name);
221223e50fe3SFrank van der Linden if (status)
221323e50fe3SFrank van der Linden return status;
221423e50fe3SFrank van der Linden
221523e50fe3SFrank van der Linden maxcount = svc_max_payload(argp->rqstp);
221623e50fe3SFrank van der Linden maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount);
221723e50fe3SFrank van der Linden
221823e50fe3SFrank van der Linden getxattr->getxa_len = maxcount;
22193fdc5464SChuck Lever return nfs_ok;
222023e50fe3SFrank van der Linden }
222123e50fe3SFrank van der Linden
222223e50fe3SFrank van der Linden static __be32
nfsd4_decode_setxattr(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)222323e50fe3SFrank van der Linden nfsd4_decode_setxattr(struct nfsd4_compoundargs *argp,
2224e78e274eSKees Cook union nfsd4_op_u *u)
222523e50fe3SFrank van der Linden {
2226e78e274eSKees Cook struct nfsd4_setxattr *setxattr = &u->setxattr;
222723e50fe3SFrank van der Linden u32 flags, maxcount, size;
2228403366a7SChuck Lever __be32 status;
222923e50fe3SFrank van der Linden
22303fdc5464SChuck Lever memset(setxattr, 0, sizeof(*setxattr));
22313fdc5464SChuck Lever
2232403366a7SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &flags) < 0)
2233403366a7SChuck Lever return nfserr_bad_xdr;
223423e50fe3SFrank van der Linden
223523e50fe3SFrank van der Linden if (flags > SETXATTR4_REPLACE)
223623e50fe3SFrank van der Linden return nfserr_inval;
223723e50fe3SFrank van der Linden setxattr->setxa_flags = flags;
223823e50fe3SFrank van der Linden
223923e50fe3SFrank van der Linden status = nfsd4_decode_xattr_name(argp, &setxattr->setxa_name);
224023e50fe3SFrank van der Linden if (status)
224123e50fe3SFrank van der Linden return status;
224223e50fe3SFrank van der Linden
224323e50fe3SFrank van der Linden maxcount = svc_max_payload(argp->rqstp);
224423e50fe3SFrank van der Linden maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount);
224523e50fe3SFrank van der Linden
2246403366a7SChuck Lever if (xdr_stream_decode_u32(argp->xdr, &size) < 0)
2247403366a7SChuck Lever return nfserr_bad_xdr;
224823e50fe3SFrank van der Linden if (size > maxcount)
224923e50fe3SFrank van der Linden return nfserr_xattr2big;
225023e50fe3SFrank van der Linden
225123e50fe3SFrank van der Linden setxattr->setxa_len = size;
225223e50fe3SFrank van der Linden if (size > 0) {
2253c1346a12SChuck Lever struct xdr_buf payload;
225423e50fe3SFrank van der Linden
2255c1346a12SChuck Lever if (!xdr_stream_subsegment(argp->xdr, &payload, size))
2256403366a7SChuck Lever return nfserr_bad_xdr;
2257c1346a12SChuck Lever status = nfsd4_vbuf_from_vector(argp, &payload,
225823e50fe3SFrank van der Linden &setxattr->setxa_buf, size);
225923e50fe3SFrank van der Linden }
226023e50fe3SFrank van der Linden
2261403366a7SChuck Lever return nfs_ok;
226223e50fe3SFrank van der Linden }
226323e50fe3SFrank van der Linden
226423e50fe3SFrank van der Linden static __be32
nfsd4_decode_listxattrs(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)226523e50fe3SFrank van der Linden nfsd4_decode_listxattrs(struct nfsd4_compoundargs *argp,
2266e78e274eSKees Cook union nfsd4_op_u *u)
226723e50fe3SFrank van der Linden {
2268e78e274eSKees Cook struct nfsd4_listxattrs *listxattrs = &u->listxattrs;
226923e50fe3SFrank van der Linden u32 maxcount;
227023e50fe3SFrank van der Linden
22713fdc5464SChuck Lever memset(listxattrs, 0, sizeof(*listxattrs));
22723fdc5464SChuck Lever
22732212036cSChuck Lever if (xdr_stream_decode_u64(argp->xdr, &listxattrs->lsxa_cookie) < 0)
22742212036cSChuck Lever return nfserr_bad_xdr;
227523e50fe3SFrank van der Linden
227623e50fe3SFrank van der Linden /*
227723e50fe3SFrank van der Linden * If the cookie is too large to have even one user.x attribute
227823e50fe3SFrank van der Linden * plus trailing '\0' left in a maximum size buffer, it's invalid.
227923e50fe3SFrank van der Linden */
228023e50fe3SFrank van der Linden if (listxattrs->lsxa_cookie >=
228123e50fe3SFrank van der Linden (XATTR_LIST_MAX / (XATTR_USER_PREFIX_LEN + 2)))
228223e50fe3SFrank van der Linden return nfserr_badcookie;
228323e50fe3SFrank van der Linden
22842212036cSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &maxcount) < 0)
22852212036cSChuck Lever return nfserr_bad_xdr;
228623e50fe3SFrank van der Linden if (maxcount < 8)
228723e50fe3SFrank van der Linden /* Always need at least 2 words (length and one character) */
228823e50fe3SFrank van der Linden return nfserr_inval;
228923e50fe3SFrank van der Linden
229023e50fe3SFrank van der Linden maxcount = min(maxcount, svc_max_payload(argp->rqstp));
229123e50fe3SFrank van der Linden listxattrs->lsxa_maxcount = maxcount;
229223e50fe3SFrank van der Linden
22932212036cSChuck Lever return nfs_ok;
229423e50fe3SFrank van der Linden }
229523e50fe3SFrank van der Linden
229623e50fe3SFrank van der Linden static __be32
nfsd4_decode_removexattr(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)229723e50fe3SFrank van der Linden nfsd4_decode_removexattr(struct nfsd4_compoundargs *argp,
2298e78e274eSKees Cook union nfsd4_op_u *u)
229923e50fe3SFrank van der Linden {
2300e78e274eSKees Cook struct nfsd4_removexattr *removexattr = &u->removexattr;
23013fdc5464SChuck Lever memset(removexattr, 0, sizeof(*removexattr));
230223e50fe3SFrank van der Linden return nfsd4_decode_xattr_name(argp, &removexattr->rmxa_name);
230323e50fe3SFrank van der Linden }
230423e50fe3SFrank van der Linden
230524bab491SAnna Schumaker static __be32
nfsd4_decode_noop(struct nfsd4_compoundargs * argp,union nfsd4_op_u * p)2306e78e274eSKees Cook nfsd4_decode_noop(struct nfsd4_compoundargs *argp, union nfsd4_op_u *p)
2307347e0ad9SBenny Halevy {
2308347e0ad9SBenny Halevy return nfs_ok;
2309347e0ad9SBenny Halevy }
2310347e0ad9SBenny Halevy
23113c375c6fSBenny Halevy static __be32
nfsd4_decode_notsupp(struct nfsd4_compoundargs * argp,union nfsd4_op_u * p)2312e78e274eSKees Cook nfsd4_decode_notsupp(struct nfsd4_compoundargs *argp, union nfsd4_op_u *p)
23133c375c6fSBenny Halevy {
23141e685ec2SBenny Halevy return nfserr_notsupp;
23153c375c6fSBenny Halevy }
23163c375c6fSBenny Halevy
2317e78e274eSKees Cook typedef __be32(*nfsd4_dec)(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u);
2318347e0ad9SBenny Halevy
2319c1df609dSChuck Lever static const nfsd4_dec nfsd4_dec_ops[] = {
2320e78e274eSKees Cook [OP_ACCESS] = nfsd4_decode_access,
2321e78e274eSKees Cook [OP_CLOSE] = nfsd4_decode_close,
2322e78e274eSKees Cook [OP_COMMIT] = nfsd4_decode_commit,
2323e78e274eSKees Cook [OP_CREATE] = nfsd4_decode_create,
2324e78e274eSKees Cook [OP_DELEGPURGE] = nfsd4_decode_notsupp,
2325e78e274eSKees Cook [OP_DELEGRETURN] = nfsd4_decode_delegreturn,
2326e78e274eSKees Cook [OP_GETATTR] = nfsd4_decode_getattr,
2327e78e274eSKees Cook [OP_GETFH] = nfsd4_decode_noop,
2328e78e274eSKees Cook [OP_LINK] = nfsd4_decode_link,
2329e78e274eSKees Cook [OP_LOCK] = nfsd4_decode_lock,
2330e78e274eSKees Cook [OP_LOCKT] = nfsd4_decode_lockt,
2331e78e274eSKees Cook [OP_LOCKU] = nfsd4_decode_locku,
2332e78e274eSKees Cook [OP_LOOKUP] = nfsd4_decode_lookup,
2333e78e274eSKees Cook [OP_LOOKUPP] = nfsd4_decode_noop,
2334e78e274eSKees Cook [OP_NVERIFY] = nfsd4_decode_verify,
2335e78e274eSKees Cook [OP_OPEN] = nfsd4_decode_open,
2336e78e274eSKees Cook [OP_OPENATTR] = nfsd4_decode_notsupp,
2337e78e274eSKees Cook [OP_OPEN_CONFIRM] = nfsd4_decode_open_confirm,
2338e78e274eSKees Cook [OP_OPEN_DOWNGRADE] = nfsd4_decode_open_downgrade,
2339e78e274eSKees Cook [OP_PUTFH] = nfsd4_decode_putfh,
2340*6b17072cSChuck Lever [OP_PUTPUBFH] = nfsd4_decode_noop,
2341e78e274eSKees Cook [OP_PUTROOTFH] = nfsd4_decode_noop,
2342e78e274eSKees Cook [OP_READ] = nfsd4_decode_read,
2343e78e274eSKees Cook [OP_READDIR] = nfsd4_decode_readdir,
2344e78e274eSKees Cook [OP_READLINK] = nfsd4_decode_noop,
2345e78e274eSKees Cook [OP_REMOVE] = nfsd4_decode_remove,
2346e78e274eSKees Cook [OP_RENAME] = nfsd4_decode_rename,
2347e78e274eSKees Cook [OP_RENEW] = nfsd4_decode_renew,
2348e78e274eSKees Cook [OP_RESTOREFH] = nfsd4_decode_noop,
2349e78e274eSKees Cook [OP_SAVEFH] = nfsd4_decode_noop,
2350e78e274eSKees Cook [OP_SECINFO] = nfsd4_decode_secinfo,
2351e78e274eSKees Cook [OP_SETATTR] = nfsd4_decode_setattr,
2352e78e274eSKees Cook [OP_SETCLIENTID] = nfsd4_decode_setclientid,
2353e78e274eSKees Cook [OP_SETCLIENTID_CONFIRM] = nfsd4_decode_setclientid_confirm,
2354e78e274eSKees Cook [OP_VERIFY] = nfsd4_decode_verify,
2355e78e274eSKees Cook [OP_WRITE] = nfsd4_decode_write,
2356e78e274eSKees Cook [OP_RELEASE_LOCKOWNER] = nfsd4_decode_release_lockowner,
23572db134ebSAndy Adamson
23582db134ebSAndy Adamson /* new operations for NFSv4.1 */
2359e78e274eSKees Cook [OP_BACKCHANNEL_CTL] = nfsd4_decode_backchannel_ctl,
2360e78e274eSKees Cook [OP_BIND_CONN_TO_SESSION] = nfsd4_decode_bind_conn_to_session,
2361e78e274eSKees Cook [OP_EXCHANGE_ID] = nfsd4_decode_exchange_id,
2362e78e274eSKees Cook [OP_CREATE_SESSION] = nfsd4_decode_create_session,
2363e78e274eSKees Cook [OP_DESTROY_SESSION] = nfsd4_decode_destroy_session,
2364e78e274eSKees Cook [OP_FREE_STATEID] = nfsd4_decode_free_stateid,
2365e78e274eSKees Cook [OP_GET_DIR_DELEGATION] = nfsd4_decode_notsupp,
23669cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
2367e78e274eSKees Cook [OP_GETDEVICEINFO] = nfsd4_decode_getdeviceinfo,
2368e78e274eSKees Cook [OP_GETDEVICELIST] = nfsd4_decode_notsupp,
2369e78e274eSKees Cook [OP_LAYOUTCOMMIT] = nfsd4_decode_layoutcommit,
2370e78e274eSKees Cook [OP_LAYOUTGET] = nfsd4_decode_layoutget,
2371e78e274eSKees Cook [OP_LAYOUTRETURN] = nfsd4_decode_layoutreturn,
23729cf514ccSChristoph Hellwig #else
2373e78e274eSKees Cook [OP_GETDEVICEINFO] = nfsd4_decode_notsupp,
2374e78e274eSKees Cook [OP_GETDEVICELIST] = nfsd4_decode_notsupp,
2375e78e274eSKees Cook [OP_LAYOUTCOMMIT] = nfsd4_decode_notsupp,
2376e78e274eSKees Cook [OP_LAYOUTGET] = nfsd4_decode_notsupp,
2377e78e274eSKees Cook [OP_LAYOUTRETURN] = nfsd4_decode_notsupp,
23789cf514ccSChristoph Hellwig #endif
2379e78e274eSKees Cook [OP_SECINFO_NO_NAME] = nfsd4_decode_secinfo_no_name,
2380e78e274eSKees Cook [OP_SEQUENCE] = nfsd4_decode_sequence,
2381e78e274eSKees Cook [OP_SET_SSV] = nfsd4_decode_notsupp,
2382e78e274eSKees Cook [OP_TEST_STATEID] = nfsd4_decode_test_stateid,
2383e78e274eSKees Cook [OP_WANT_DELEGATION] = nfsd4_decode_notsupp,
2384e78e274eSKees Cook [OP_DESTROY_CLIENTID] = nfsd4_decode_destroy_clientid,
2385e78e274eSKees Cook [OP_RECLAIM_COMPLETE] = nfsd4_decode_reclaim_complete,
238687a15a80SAnna Schumaker
238787a15a80SAnna Schumaker /* new operations for NFSv4.2 */
2388e78e274eSKees Cook [OP_ALLOCATE] = nfsd4_decode_fallocate,
2389e78e274eSKees Cook [OP_COPY] = nfsd4_decode_copy,
2390e78e274eSKees Cook [OP_COPY_NOTIFY] = nfsd4_decode_copy_notify,
2391e78e274eSKees Cook [OP_DEALLOCATE] = nfsd4_decode_fallocate,
2392e78e274eSKees Cook [OP_IO_ADVISE] = nfsd4_decode_notsupp,
2393e78e274eSKees Cook [OP_LAYOUTERROR] = nfsd4_decode_notsupp,
2394e78e274eSKees Cook [OP_LAYOUTSTATS] = nfsd4_decode_notsupp,
2395e78e274eSKees Cook [OP_OFFLOAD_CANCEL] = nfsd4_decode_offload_status,
2396e78e274eSKees Cook [OP_OFFLOAD_STATUS] = nfsd4_decode_offload_status,
2397e78e274eSKees Cook [OP_READ_PLUS] = nfsd4_decode_read,
2398e78e274eSKees Cook [OP_SEEK] = nfsd4_decode_seek,
2399e78e274eSKees Cook [OP_WRITE_SAME] = nfsd4_decode_notsupp,
2400e78e274eSKees Cook [OP_CLONE] = nfsd4_decode_clone,
240123e50fe3SFrank van der Linden /* RFC 8276 extended atributes operations */
2402e78e274eSKees Cook [OP_GETXATTR] = nfsd4_decode_getxattr,
2403e78e274eSKees Cook [OP_SETXATTR] = nfsd4_decode_setxattr,
2404e78e274eSKees Cook [OP_LISTXATTRS] = nfsd4_decode_listxattrs,
2405e78e274eSKees Cook [OP_REMOVEXATTR] = nfsd4_decode_removexattr,
24062db134ebSAndy Adamson };
24072db134ebSAndy Adamson
2408e1a90ebdSAnna Schumaker static inline bool
nfsd4_opnum_in_range(struct nfsd4_compoundargs * argp,struct nfsd4_op * op)2409e1a90ebdSAnna Schumaker nfsd4_opnum_in_range(struct nfsd4_compoundargs *argp, struct nfsd4_op *op)
2410e1a90ebdSAnna Schumaker {
24118217d146SAnna Schumaker if (op->opnum < FIRST_NFS4_OP)
2412e1a90ebdSAnna Schumaker return false;
24138217d146SAnna Schumaker else if (argp->minorversion == 0 && op->opnum > LAST_NFS40_OP)
2414e1a90ebdSAnna Schumaker return false;
24158217d146SAnna Schumaker else if (argp->minorversion == 1 && op->opnum > LAST_NFS41_OP)
24168217d146SAnna Schumaker return false;
24178217d146SAnna Schumaker else if (argp->minorversion == 2 && op->opnum > LAST_NFS42_OP)
2418e1a90ebdSAnna Schumaker return false;
2419e1a90ebdSAnna Schumaker return true;
2420e1a90ebdSAnna Schumaker }
2421f2feb96bSBenny Halevy
2422c44b31c2SChuck Lever static bool
nfsd4_decode_compound(struct nfsd4_compoundargs * argp)24231da177e4SLinus Torvalds nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
24241da177e4SLinus Torvalds {
24251da177e4SLinus Torvalds struct nfsd4_op *op;
24261091006cSJ. Bruce Fields bool cachethis = false;
2427a5cddc88SJ. Bruce Fields int auth_slack= argp->rqstp->rq_auth_slack;
2428a5cddc88SJ. Bruce Fields int max_reply = auth_slack + 8; /* opcnt, status */
2429b0e35fdaSJ. Bruce Fields int readcount = 0;
2430b0e35fdaSJ. Bruce Fields int readbytes = 0;
2431d9b74bdaSChuck Lever __be32 *p;
24321da177e4SLinus Torvalds int i;
24331da177e4SLinus Torvalds
2434d9b74bdaSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &argp->taglen) < 0)
2435c44b31c2SChuck Lever return false;
2436d9b74bdaSChuck Lever max_reply += XDR_UNIT;
2437d9b74bdaSChuck Lever argp->tag = NULL;
2438d9b74bdaSChuck Lever if (unlikely(argp->taglen)) {
24391da177e4SLinus Torvalds if (argp->taglen > NFSD4_MAX_TAGLEN)
2440c44b31c2SChuck Lever return false;
2441d9b74bdaSChuck Lever p = xdr_inline_decode(argp->xdr, argp->taglen);
2442d9b74bdaSChuck Lever if (!p)
2443c44b31c2SChuck Lever return false;
24447b723008SChuck Lever argp->tag = svcxdr_savemem(argp, p, argp->taglen);
2445d9b74bdaSChuck Lever if (!argp->tag)
2446c44b31c2SChuck Lever return false;
2447d9b74bdaSChuck Lever max_reply += xdr_align_size(argp->taglen);
2448d9b74bdaSChuck Lever }
2449d9b74bdaSChuck Lever
2450d9b74bdaSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &argp->minorversion) < 0)
2451c44b31c2SChuck Lever return false;
24527518a3dcSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &argp->client_opcnt) < 0)
2453c44b31c2SChuck Lever return false;
24547518a3dcSChuck Lever argp->opcnt = min_t(u32, argp->client_opcnt,
24557518a3dcSChuck Lever NFSD_MAX_OPS_PER_COMPOUND);
24561da177e4SLinus Torvalds
2457e8c96f8cSTobias Klauser if (argp->opcnt > ARRAY_SIZE(argp->iops)) {
245880e591ceSChuck Lever argp->ops = vcalloc(argp->opcnt, sizeof(*argp->ops));
24591da177e4SLinus Torvalds if (!argp->ops) {
24601da177e4SLinus Torvalds argp->ops = argp->iops;
2461c44b31c2SChuck Lever return false;
24621da177e4SLinus Torvalds }
24631da177e4SLinus Torvalds }
24641da177e4SLinus Torvalds
2465e1a90ebdSAnna Schumaker if (argp->minorversion > NFSD_SUPPORTED_MINOR_VERSION)
246630cff1ffSBenny Halevy argp->opcnt = 0;
246730cff1ffSBenny Halevy
24681da177e4SLinus Torvalds for (i = 0; i < argp->opcnt; i++) {
24691da177e4SLinus Torvalds op = &argp->ops[i];
24701da177e4SLinus Torvalds op->replay = NULL;
2471804d8e0aSChuck Lever op->opdesc = NULL;
24721da177e4SLinus Torvalds
24733a237b4aSChuck Lever if (xdr_stream_decode_u32(argp->xdr, &op->opnum) < 0)
2474c44b31c2SChuck Lever return false;
247508281341SChuck Lever if (nfsd4_opnum_in_range(argp, op)) {
2476804d8e0aSChuck Lever op->opdesc = OPDESC(op);
2477e1a90ebdSAnna Schumaker op->status = nfsd4_dec_ops[op->opnum](argp, &op->u);
247808281341SChuck Lever if (op->status != nfs_ok)
247908281341SChuck Lever trace_nfsd_compound_decode_err(argp->rqstp,
248008281341SChuck Lever argp->opcnt, i,
248108281341SChuck Lever op->opnum,
248208281341SChuck Lever op->status);
248308281341SChuck Lever } else {
24841da177e4SLinus Torvalds op->opnum = OP_ILLEGAL;
24851da177e4SLinus Torvalds op->status = nfserr_op_illegal;
24861da177e4SLinus Torvalds }
2487804d8e0aSChuck Lever
24881091006cSJ. Bruce Fields /*
24891091006cSJ. Bruce Fields * We'll try to cache the result in the DRC if any one
24901091006cSJ. Bruce Fields * op in the compound wants to be cached:
24911091006cSJ. Bruce Fields */
24921091006cSJ. Bruce Fields cachethis |= nfsd4_cache_this_op(op);
24936ff40decSJ. Bruce Fields
2494528b8493SAnna Schumaker if (op->opnum == OP_READ || op->opnum == OP_READ_PLUS) {
2495b0e35fdaSJ. Bruce Fields readcount++;
2496b0e35fdaSJ. Bruce Fields readbytes += nfsd4_max_reply(argp->rqstp, op);
2497b0e35fdaSJ. Bruce Fields } else
24984f0cefbfSJ. Bruce Fields max_reply += nfsd4_max_reply(argp->rqstp, op);
2499f7b43d0cSJ. Bruce Fields /*
25007323f0d2SKinglong Mee * OP_LOCK and OP_LOCKT may return a conflicting lock.
25017323f0d2SKinglong Mee * (Special case because it will just skip encoding this
25027323f0d2SKinglong Mee * if it runs out of xdr buffer space, and it is the only
25037323f0d2SKinglong Mee * operation that behaves this way.)
2504f7b43d0cSJ. Bruce Fields */
25057323f0d2SKinglong Mee if (op->opnum == OP_LOCK || op->opnum == OP_LOCKT)
2506f7b43d0cSJ. Bruce Fields max_reply += NFS4_OPAQUE_LIMIT;
2507e372ba60SJ. Bruce Fields
2508e372ba60SJ. Bruce Fields if (op->status) {
2509e372ba60SJ. Bruce Fields argp->opcnt = i+1;
2510e372ba60SJ. Bruce Fields break;
2511e372ba60SJ. Bruce Fields }
25121da177e4SLinus Torvalds }
25131091006cSJ. Bruce Fields /* Sessions make the DRC unnecessary: */
25141091006cSJ. Bruce Fields if (argp->minorversion)
25151091006cSJ. Bruce Fields cachethis = false;
2516b0e35fdaSJ. Bruce Fields svc_reserve(argp->rqstp, max_reply + readbytes);
25171091006cSJ. Bruce Fields argp->rqstp->rq_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE;
25181da177e4SLinus Torvalds
2519a5cddc88SJ. Bruce Fields if (readcount > 1 || max_reply > PAGE_SIZE - auth_slack)
25207827c81fSChuck Lever clear_bit(RQ_SPLICE_OK, &argp->rqstp->rq_flags);
2521b0e35fdaSJ. Bruce Fields
2522c44b31c2SChuck Lever return true;
25231da177e4SLinus Torvalds }
25241da177e4SLinus Torvalds
encode_change(__be32 * p,struct kstat * stat,struct inode * inode,struct svc_export * exp)2525b8800921SNeilBrown static __be32 *encode_change(__be32 *p, struct kstat *stat, struct inode *inode,
2526b8800921SNeilBrown struct svc_export *exp)
2527c654b8a9SJ. Bruce Fields {
2528b8800921SNeilBrown if (exp->ex_flags & NFSEXP_V4ROOT) {
2529b8800921SNeilBrown *p++ = cpu_to_be32(convert_to_wallclock(exp->cd->flush_time));
2530b8800921SNeilBrown *p++ = 0;
253170b87f77SJ. Bruce Fields } else
253239ca1bf6SAmir Goldstein p = xdr_encode_hyper(p, nfsd4_change_attribute(stat, inode));
2533d05d5744SJ. Bruce Fields return p;
2534c654b8a9SJ. Bruce Fields }
2535c654b8a9SJ. Bruce Fields
nfsd4_encode_nfstime4(struct xdr_stream * xdr,struct timespec64 * tv)253626217679SChuck Lever static __be32 nfsd4_encode_nfstime4(struct xdr_stream *xdr,
253726217679SChuck Lever struct timespec64 *tv)
253826217679SChuck Lever {
253926217679SChuck Lever __be32 *p;
254026217679SChuck Lever
254126217679SChuck Lever p = xdr_reserve_space(xdr, XDR_UNIT * 3);
254226217679SChuck Lever if (!p)
254326217679SChuck Lever return nfserr_resource;
254426217679SChuck Lever
254526217679SChuck Lever p = xdr_encode_hyper(p, (s64)tv->tv_sec);
254626217679SChuck Lever *p = cpu_to_be32(tv->tv_nsec);
254726217679SChuck Lever return nfs_ok;
254826217679SChuck Lever }
254926217679SChuck Lever
255016945141SJ. Bruce Fields /*
255116945141SJ. Bruce Fields * ctime (in NFSv4, time_metadata) is not writeable, and the client
255216945141SJ. Bruce Fields * doesn't really care what resolution could theoretically be stored by
255316945141SJ. Bruce Fields * the filesystem.
255416945141SJ. Bruce Fields *
255516945141SJ. Bruce Fields * The client cares how close together changes can be while still
255616945141SJ. Bruce Fields * guaranteeing ctime changes. For most filesystems (which have
255716945141SJ. Bruce Fields * timestamps with nanosecond fields) that is limited by the resolution
255816945141SJ. Bruce Fields * of the time returned from current_time() (which I'm assuming to be
255916945141SJ. Bruce Fields * 1/HZ).
256016945141SJ. Bruce Fields */
encode_time_delta(__be32 * p,struct inode * inode)256116945141SJ. Bruce Fields static __be32 *encode_time_delta(__be32 *p, struct inode *inode)
256216945141SJ. Bruce Fields {
2563e4598e38SArnd Bergmann struct timespec64 ts;
256416945141SJ. Bruce Fields u32 ns;
256516945141SJ. Bruce Fields
256616945141SJ. Bruce Fields ns = max_t(u32, NSEC_PER_SEC/HZ, inode->i_sb->s_time_gran);
2567e4598e38SArnd Bergmann ts = ns_to_timespec64(ns);
256816945141SJ. Bruce Fields
256916945141SJ. Bruce Fields p = xdr_encode_hyper(p, ts.tv_sec);
257016945141SJ. Bruce Fields *p++ = cpu_to_be32(ts.tv_nsec);
257116945141SJ. Bruce Fields
257216945141SJ. Bruce Fields return p;
257316945141SJ. Bruce Fields }
257416945141SJ. Bruce Fields
257566a21db7SChuck Lever static __be32
nfsd4_encode_change_info4(struct xdr_stream * xdr,struct nfsd4_change_info * c)257666a21db7SChuck Lever nfsd4_encode_change_info4(struct xdr_stream *xdr, struct nfsd4_change_info *c)
2577c654b8a9SJ. Bruce Fields {
257866a21db7SChuck Lever if (xdr_stream_encode_bool(xdr, c->atomic) < 0)
257966a21db7SChuck Lever return nfserr_resource;
258066a21db7SChuck Lever if (xdr_stream_encode_u64(xdr, c->before_change) < 0)
258166a21db7SChuck Lever return nfserr_resource;
258266a21db7SChuck Lever if (xdr_stream_encode_u64(xdr, c->after_change) < 0)
258366a21db7SChuck Lever return nfserr_resource;
258466a21db7SChuck Lever return nfs_ok;
2585c654b8a9SJ. Bruce Fields }
25861da177e4SLinus Torvalds
258781c3f413SJ.Bruce Fields /* Encode as an array of strings the string given with components
2588e7a0444aSWeston Andros Adamson * separated @sep, escaped with esc_enter and esc_exit.
258981c3f413SJ.Bruce Fields */
nfsd4_encode_components_esc(struct xdr_stream * xdr,char sep,char * components,char esc_enter,char esc_exit)2590ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_components_esc(struct xdr_stream *xdr, char sep,
2591ddd1ea56SJ. Bruce Fields char *components, char esc_enter,
2592ddd1ea56SJ. Bruce Fields char esc_exit)
259381c3f413SJ.Bruce Fields {
2594ddd1ea56SJ. Bruce Fields __be32 *p;
2595082d4bd7SJ. Bruce Fields __be32 pathlen;
2596082d4bd7SJ. Bruce Fields int pathlen_offset;
259781c3f413SJ.Bruce Fields int strlen, count=0;
2598e7a0444aSWeston Andros Adamson char *str, *end, *next;
259981c3f413SJ.Bruce Fields
260081c3f413SJ.Bruce Fields dprintk("nfsd4_encode_components(%s)\n", components);
2601082d4bd7SJ. Bruce Fields
2602082d4bd7SJ. Bruce Fields pathlen_offset = xdr->buf->len;
2603ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
2604ddd1ea56SJ. Bruce Fields if (!p)
260581c3f413SJ.Bruce Fields return nfserr_resource;
2606082d4bd7SJ. Bruce Fields p++; /* We will fill this in with @count later */
2607082d4bd7SJ. Bruce Fields
260881c3f413SJ.Bruce Fields end = str = components;
260981c3f413SJ.Bruce Fields while (*end) {
2610e7a0444aSWeston Andros Adamson bool found_esc = false;
2611e7a0444aSWeston Andros Adamson
2612e7a0444aSWeston Andros Adamson /* try to parse as esc_start, ..., esc_end, sep */
2613e7a0444aSWeston Andros Adamson if (*str == esc_enter) {
2614e7a0444aSWeston Andros Adamson for (; *end && (*end != esc_exit); end++)
2615e7a0444aSWeston Andros Adamson /* find esc_exit or end of string */;
2616e7a0444aSWeston Andros Adamson next = end + 1;
2617e7a0444aSWeston Andros Adamson if (*end && (!*next || *next == sep)) {
2618e7a0444aSWeston Andros Adamson str++;
2619e7a0444aSWeston Andros Adamson found_esc = true;
2620e7a0444aSWeston Andros Adamson }
2621e7a0444aSWeston Andros Adamson }
2622e7a0444aSWeston Andros Adamson
2623e7a0444aSWeston Andros Adamson if (!found_esc)
262481c3f413SJ.Bruce Fields for (; *end && (*end != sep); end++)
2625e7a0444aSWeston Andros Adamson /* find sep or end of string */;
2626e7a0444aSWeston Andros Adamson
262781c3f413SJ.Bruce Fields strlen = end - str;
262881c3f413SJ.Bruce Fields if (strlen) {
2629ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, strlen + 4);
2630ddd1ea56SJ. Bruce Fields if (!p)
263181c3f413SJ.Bruce Fields return nfserr_resource;
26320c0c267bSJ. Bruce Fields p = xdr_encode_opaque(p, str, strlen);
263381c3f413SJ.Bruce Fields count++;
263481c3f413SJ.Bruce Fields }
263581c3f413SJ.Bruce Fields else
263681c3f413SJ.Bruce Fields end++;
26375a64e569SBenjamin Coddington if (found_esc)
26385a64e569SBenjamin Coddington end = next;
26395a64e569SBenjamin Coddington
264081c3f413SJ.Bruce Fields str = end;
264181c3f413SJ.Bruce Fields }
2642bf7491f1SBenjamin Coddington pathlen = htonl(count);
2643082d4bd7SJ. Bruce Fields write_bytes_to_xdr_buf(xdr->buf, pathlen_offset, &pathlen, 4);
264481c3f413SJ.Bruce Fields return 0;
264581c3f413SJ.Bruce Fields }
264681c3f413SJ.Bruce Fields
2647e7a0444aSWeston Andros Adamson /* Encode as an array of strings the string given with components
2648e7a0444aSWeston Andros Adamson * separated @sep.
2649e7a0444aSWeston Andros Adamson */
nfsd4_encode_components(struct xdr_stream * xdr,char sep,char * components)2650ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_components(struct xdr_stream *xdr, char sep,
2651ddd1ea56SJ. Bruce Fields char *components)
2652e7a0444aSWeston Andros Adamson {
2653ddd1ea56SJ. Bruce Fields return nfsd4_encode_components_esc(xdr, sep, components, 0, 0);
2654e7a0444aSWeston Andros Adamson }
2655e7a0444aSWeston Andros Adamson
265681c3f413SJ.Bruce Fields /*
265781c3f413SJ.Bruce Fields * encode a location element of a fs_locations structure
265881c3f413SJ.Bruce Fields */
nfsd4_encode_fs_location4(struct xdr_stream * xdr,struct nfsd4_fs_location * location)2659ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_fs_location4(struct xdr_stream *xdr,
2660ddd1ea56SJ. Bruce Fields struct nfsd4_fs_location *location)
266181c3f413SJ.Bruce Fields {
2662b37ad28bSAl Viro __be32 status;
266381c3f413SJ.Bruce Fields
2664ddd1ea56SJ. Bruce Fields status = nfsd4_encode_components_esc(xdr, ':', location->hosts,
2665e7a0444aSWeston Andros Adamson '[', ']');
266681c3f413SJ.Bruce Fields if (status)
266781c3f413SJ.Bruce Fields return status;
2668ddd1ea56SJ. Bruce Fields status = nfsd4_encode_components(xdr, '/', location->path);
266981c3f413SJ.Bruce Fields if (status)
267081c3f413SJ.Bruce Fields return status;
267181c3f413SJ.Bruce Fields return 0;
267281c3f413SJ.Bruce Fields }
267381c3f413SJ.Bruce Fields
267481c3f413SJ.Bruce Fields /*
2675ed748aacSTrond Myklebust * Encode a path in RFC3530 'pathname4' format
267681c3f413SJ.Bruce Fields */
nfsd4_encode_path(struct xdr_stream * xdr,const struct path * root,const struct path * path)2677ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_path(struct xdr_stream *xdr,
2678ddd1ea56SJ. Bruce Fields const struct path *root,
2679ddd1ea56SJ. Bruce Fields const struct path *path)
268081c3f413SJ.Bruce Fields {
2681301f0268SAl Viro struct path cur = *path;
2682ddd1ea56SJ. Bruce Fields __be32 *p;
2683ed748aacSTrond Myklebust struct dentry **components = NULL;
2684ed748aacSTrond Myklebust unsigned int ncomponents = 0;
2685ed748aacSTrond Myklebust __be32 err = nfserr_jukebox;
268681c3f413SJ.Bruce Fields
2687ed748aacSTrond Myklebust dprintk("nfsd4_encode_components(");
268881c3f413SJ.Bruce Fields
2689ed748aacSTrond Myklebust path_get(&cur);
2690ed748aacSTrond Myklebust /* First walk the path up to the nfsd root, and store the
2691ed748aacSTrond Myklebust * dentries/path components in an array.
2692ed748aacSTrond Myklebust */
2693ed748aacSTrond Myklebust for (;;) {
2694b77a4b2eSKinglong Mee if (path_equal(&cur, root))
2695ed748aacSTrond Myklebust break;
2696ed748aacSTrond Myklebust if (cur.dentry == cur.mnt->mnt_root) {
2697ed748aacSTrond Myklebust if (follow_up(&cur))
2698ed748aacSTrond Myklebust continue;
2699ed748aacSTrond Myklebust goto out_free;
270081c3f413SJ.Bruce Fields }
2701ed748aacSTrond Myklebust if ((ncomponents & 15) == 0) {
2702ed748aacSTrond Myklebust struct dentry **new;
2703ed748aacSTrond Myklebust new = krealloc(components,
2704ed748aacSTrond Myklebust sizeof(*new) * (ncomponents + 16),
2705ed748aacSTrond Myklebust GFP_KERNEL);
2706ed748aacSTrond Myklebust if (!new)
2707ed748aacSTrond Myklebust goto out_free;
2708ed748aacSTrond Myklebust components = new;
2709ed748aacSTrond Myklebust }
2710ed748aacSTrond Myklebust components[ncomponents++] = cur.dentry;
2711ed748aacSTrond Myklebust cur.dentry = dget_parent(cur.dentry);
2712ed748aacSTrond Myklebust }
2713ddd1ea56SJ. Bruce Fields err = nfserr_resource;
2714ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
2715ddd1ea56SJ. Bruce Fields if (!p)
2716ed748aacSTrond Myklebust goto out_free;
2717c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(ncomponents);
2718ed748aacSTrond Myklebust
2719ed748aacSTrond Myklebust while (ncomponents) {
2720ed748aacSTrond Myklebust struct dentry *dentry = components[ncomponents - 1];
2721301f0268SAl Viro unsigned int len;
2722ed748aacSTrond Myklebust
2723301f0268SAl Viro spin_lock(&dentry->d_lock);
2724301f0268SAl Viro len = dentry->d_name.len;
2725ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, len + 4);
2726ddd1ea56SJ. Bruce Fields if (!p) {
2727301f0268SAl Viro spin_unlock(&dentry->d_lock);
2728ed748aacSTrond Myklebust goto out_free;
2729301f0268SAl Viro }
27300c0c267bSJ. Bruce Fields p = xdr_encode_opaque(p, dentry->d_name.name, len);
2731a455589fSAl Viro dprintk("/%pd", dentry);
2732301f0268SAl Viro spin_unlock(&dentry->d_lock);
2733ed748aacSTrond Myklebust dput(dentry);
2734ed748aacSTrond Myklebust ncomponents--;
2735ed748aacSTrond Myklebust }
2736ed748aacSTrond Myklebust
2737ed748aacSTrond Myklebust err = 0;
2738ed748aacSTrond Myklebust out_free:
2739ed748aacSTrond Myklebust dprintk(")\n");
2740ed748aacSTrond Myklebust while (ncomponents)
2741ed748aacSTrond Myklebust dput(components[--ncomponents]);
2742ed748aacSTrond Myklebust kfree(components);
2743ed748aacSTrond Myklebust path_put(&cur);
2744ed748aacSTrond Myklebust return err;
2745ed748aacSTrond Myklebust }
2746ed748aacSTrond Myklebust
nfsd4_encode_fsloc_fsroot(struct xdr_stream * xdr,struct svc_rqst * rqstp,const struct path * path)2747ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_fsloc_fsroot(struct xdr_stream *xdr,
2748ddd1ea56SJ. Bruce Fields struct svc_rqst *rqstp, const struct path *path)
2749ed748aacSTrond Myklebust {
2750ed748aacSTrond Myklebust struct svc_export *exp_ps;
2751ed748aacSTrond Myklebust __be32 res;
2752ed748aacSTrond Myklebust
2753ed748aacSTrond Myklebust exp_ps = rqst_find_fsidzero_export(rqstp);
2754ed748aacSTrond Myklebust if (IS_ERR(exp_ps))
2755ed748aacSTrond Myklebust return nfserrno(PTR_ERR(exp_ps));
2756ddd1ea56SJ. Bruce Fields res = nfsd4_encode_path(xdr, &exp_ps->ex_path, path);
2757ed748aacSTrond Myklebust exp_put(exp_ps);
2758ed748aacSTrond Myklebust return res;
275981c3f413SJ.Bruce Fields }
276081c3f413SJ.Bruce Fields
276181c3f413SJ.Bruce Fields /*
276281c3f413SJ.Bruce Fields * encode a fs_locations structure
276381c3f413SJ.Bruce Fields */
nfsd4_encode_fs_locations(struct xdr_stream * xdr,struct svc_rqst * rqstp,struct svc_export * exp)2764ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_fs_locations(struct xdr_stream *xdr,
2765ddd1ea56SJ. Bruce Fields struct svc_rqst *rqstp, struct svc_export *exp)
276681c3f413SJ.Bruce Fields {
2767b37ad28bSAl Viro __be32 status;
2768cc45f017SAl Viro int i;
2769ddd1ea56SJ. Bruce Fields __be32 *p;
277081c3f413SJ.Bruce Fields struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs;
277181c3f413SJ.Bruce Fields
2772ddd1ea56SJ. Bruce Fields status = nfsd4_encode_fsloc_fsroot(xdr, rqstp, &exp->ex_path);
277381c3f413SJ.Bruce Fields if (status)
277481c3f413SJ.Bruce Fields return status;
2775ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
2776ddd1ea56SJ. Bruce Fields if (!p)
277781c3f413SJ.Bruce Fields return nfserr_resource;
2778c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(fslocs->locations_count);
277981c3f413SJ.Bruce Fields for (i=0; i<fslocs->locations_count; i++) {
2780ddd1ea56SJ. Bruce Fields status = nfsd4_encode_fs_location4(xdr, &fslocs->locations[i]);
278181c3f413SJ.Bruce Fields if (status)
278281c3f413SJ.Bruce Fields return status;
278381c3f413SJ.Bruce Fields }
278481c3f413SJ.Bruce Fields return 0;
278581c3f413SJ.Bruce Fields }
27861da177e4SLinus Torvalds
nfs4_file_type(umode_t mode)27873d2544b1SJ. Bruce Fields static u32 nfs4_file_type(umode_t mode)
27883d2544b1SJ. Bruce Fields {
27893d2544b1SJ. Bruce Fields switch (mode & S_IFMT) {
27903d2544b1SJ. Bruce Fields case S_IFIFO: return NF4FIFO;
27913d2544b1SJ. Bruce Fields case S_IFCHR: return NF4CHR;
27923d2544b1SJ. Bruce Fields case S_IFDIR: return NF4DIR;
27933d2544b1SJ. Bruce Fields case S_IFBLK: return NF4BLK;
27943d2544b1SJ. Bruce Fields case S_IFLNK: return NF4LNK;
27953d2544b1SJ. Bruce Fields case S_IFREG: return NF4REG;
27963d2544b1SJ. Bruce Fields case S_IFSOCK: return NF4SOCK;
27973d2544b1SJ. Bruce Fields default: return NF4BAD;
279825fef48bSTom Rix }
27993d2544b1SJ. Bruce Fields }
28001da177e4SLinus Torvalds
2801b37ad28bSAl Viro static inline __be32
nfsd4_encode_aclname(struct xdr_stream * xdr,struct svc_rqst * rqstp,struct nfs4_ace * ace)2802ddd1ea56SJ. Bruce Fields nfsd4_encode_aclname(struct xdr_stream *xdr, struct svc_rqst *rqstp,
2803ddd1ea56SJ. Bruce Fields struct nfs4_ace *ace)
28041da177e4SLinus Torvalds {
28053554116dSJ. Bruce Fields if (ace->whotype != NFS4_ACL_WHO_NAMED)
2806ddd1ea56SJ. Bruce Fields return nfs4_acl_write_who(xdr, ace->whotype);
28073554116dSJ. Bruce Fields else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
2808ddd1ea56SJ. Bruce Fields return nfsd4_encode_group(xdr, rqstp, ace->who_gid);
2809ab8e4aeeSEric W. Biederman else
2810ddd1ea56SJ. Bruce Fields return nfsd4_encode_user(xdr, rqstp, ace->who_uid);
28111da177e4SLinus Torvalds }
28121da177e4SLinus Torvalds
28136896f15aSKinglong Mee static inline __be32
nfsd4_encode_layout_types(struct xdr_stream * xdr,u32 layout_types)28148a4c3926SJeff Layton nfsd4_encode_layout_types(struct xdr_stream *xdr, u32 layout_types)
28156896f15aSKinglong Mee {
28166896f15aSKinglong Mee __be32 *p;
28178a4c3926SJeff Layton unsigned long i = hweight_long(layout_types);
28186896f15aSKinglong Mee
28198a4c3926SJeff Layton p = xdr_reserve_space(xdr, 4 + 4 * i);
28206896f15aSKinglong Mee if (!p)
28216896f15aSKinglong Mee return nfserr_resource;
28228a4c3926SJeff Layton
28238a4c3926SJeff Layton *p++ = cpu_to_be32(i);
28248a4c3926SJeff Layton
28258a4c3926SJeff Layton for (i = LAYOUT_NFSV4_1_FILES; i < LAYOUT_TYPE_MAX; ++i)
28268a4c3926SJeff Layton if (layout_types & (1 << i))
28278a4c3926SJeff Layton *p++ = cpu_to_be32(i);
28286896f15aSKinglong Mee
28296896f15aSKinglong Mee return 0;
28306896f15aSKinglong Mee }
28316896f15aSKinglong Mee
283242ca0993SJ.Bruce Fields #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
283342ca0993SJ.Bruce Fields FATTR4_WORD0_RDATTR_ERROR)
283442ca0993SJ.Bruce Fields #define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
2835c2227a39SKinglong Mee #define WORD2_ABSENT_FS_ATTRS 0
283642ca0993SJ.Bruce Fields
283718032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
283818032ca0SDavid Quigley static inline __be32
nfsd4_encode_security_label(struct xdr_stream * xdr,struct svc_rqst * rqstp,void * context,int len)2839ddd1ea56SJ. Bruce Fields nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
2840ddd1ea56SJ. Bruce Fields void *context, int len)
284118032ca0SDavid Quigley {
2842ddd1ea56SJ. Bruce Fields __be32 *p;
284318032ca0SDavid Quigley
2844ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, len + 4 + 4 + 4);
2845ddd1ea56SJ. Bruce Fields if (!p)
284618032ca0SDavid Quigley return nfserr_resource;
284718032ca0SDavid Quigley
284818032ca0SDavid Quigley /*
284918032ca0SDavid Quigley * For now we use a 0 here to indicate the null translation; in
285018032ca0SDavid Quigley * the future we may place a call to translation code here.
285118032ca0SDavid Quigley */
2852c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); /* lfs */
2853c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); /* pi */
285418032ca0SDavid Quigley p = xdr_encode_opaque(p, context, len);
285518032ca0SDavid Quigley return 0;
285618032ca0SDavid Quigley }
285718032ca0SDavid Quigley #else
285818032ca0SDavid Quigley static inline __be32
nfsd4_encode_security_label(struct xdr_stream * xdr,struct svc_rqst * rqstp,void * context,int len)2859ddd1ea56SJ. Bruce Fields nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
2860ddd1ea56SJ. Bruce Fields void *context, int len)
286118032ca0SDavid Quigley { return 0; }
286218032ca0SDavid Quigley #endif
286318032ca0SDavid Quigley
fattr_handle_absent_fs(u32 * bmval0,u32 * bmval1,u32 * bmval2,u32 * rdattr_err)2864c2227a39SKinglong Mee static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *bmval2, u32 *rdattr_err)
286542ca0993SJ.Bruce Fields {
286642ca0993SJ.Bruce Fields /* As per referral draft: */
286742ca0993SJ.Bruce Fields if (*bmval0 & ~WORD0_ABSENT_FS_ATTRS ||
286842ca0993SJ.Bruce Fields *bmval1 & ~WORD1_ABSENT_FS_ATTRS) {
286942ca0993SJ.Bruce Fields if (*bmval0 & FATTR4_WORD0_RDATTR_ERROR ||
287042ca0993SJ.Bruce Fields *bmval0 & FATTR4_WORD0_FS_LOCATIONS)
287142ca0993SJ.Bruce Fields *rdattr_err = NFSERR_MOVED;
287242ca0993SJ.Bruce Fields else
287342ca0993SJ.Bruce Fields return nfserr_moved;
287442ca0993SJ.Bruce Fields }
287542ca0993SJ.Bruce Fields *bmval0 &= WORD0_ABSENT_FS_ATTRS;
287642ca0993SJ.Bruce Fields *bmval1 &= WORD1_ABSENT_FS_ATTRS;
2877c2227a39SKinglong Mee *bmval2 &= WORD2_ABSENT_FS_ATTRS;
287842ca0993SJ.Bruce Fields return 0;
287942ca0993SJ.Bruce Fields }
28801da177e4SLinus Torvalds
2881ae7095a7SJ. Bruce Fields
nfsd4_get_mounted_on_ino(struct svc_export * exp,u64 * pino)28826106d911SJeff Layton static int nfsd4_get_mounted_on_ino(struct svc_export *exp, u64 *pino)
2883ae7095a7SJ. Bruce Fields {
2884ae7095a7SJ. Bruce Fields struct path path = exp->ex_path;
28856106d911SJeff Layton struct kstat stat;
2886ae7095a7SJ. Bruce Fields int err;
2887ae7095a7SJ. Bruce Fields
2888ae7095a7SJ. Bruce Fields path_get(&path);
2889ae7095a7SJ. Bruce Fields while (follow_up(&path)) {
2890ae7095a7SJ. Bruce Fields if (path.dentry != path.mnt->mnt_root)
2891ae7095a7SJ. Bruce Fields break;
2892ae7095a7SJ. Bruce Fields }
28936106d911SJeff Layton err = vfs_getattr(&path, &stat, STATX_INO, AT_STATX_SYNC_AS_STAT);
2894ae7095a7SJ. Bruce Fields path_put(&path);
28956106d911SJeff Layton if (!err)
28966106d911SJeff Layton *pino = stat.ino;
2897ae7095a7SJ. Bruce Fields return err;
2898ae7095a7SJ. Bruce Fields }
2899ae7095a7SJ. Bruce Fields
290075976de6SKinglong Mee static __be32
nfsd4_encode_bitmap(struct xdr_stream * xdr,u32 bmval0,u32 bmval1,u32 bmval2)290175976de6SKinglong Mee nfsd4_encode_bitmap(struct xdr_stream *xdr, u32 bmval0, u32 bmval1, u32 bmval2)
290275976de6SKinglong Mee {
290375976de6SKinglong Mee __be32 *p;
290475976de6SKinglong Mee
290575976de6SKinglong Mee if (bmval2) {
290675976de6SKinglong Mee p = xdr_reserve_space(xdr, 16);
290775976de6SKinglong Mee if (!p)
290875976de6SKinglong Mee goto out_resource;
290975976de6SKinglong Mee *p++ = cpu_to_be32(3);
291075976de6SKinglong Mee *p++ = cpu_to_be32(bmval0);
291175976de6SKinglong Mee *p++ = cpu_to_be32(bmval1);
291275976de6SKinglong Mee *p++ = cpu_to_be32(bmval2);
291375976de6SKinglong Mee } else if (bmval1) {
291475976de6SKinglong Mee p = xdr_reserve_space(xdr, 12);
291575976de6SKinglong Mee if (!p)
291675976de6SKinglong Mee goto out_resource;
291775976de6SKinglong Mee *p++ = cpu_to_be32(2);
291875976de6SKinglong Mee *p++ = cpu_to_be32(bmval0);
291975976de6SKinglong Mee *p++ = cpu_to_be32(bmval1);
292075976de6SKinglong Mee } else {
292175976de6SKinglong Mee p = xdr_reserve_space(xdr, 8);
292275976de6SKinglong Mee if (!p)
292375976de6SKinglong Mee goto out_resource;
292475976de6SKinglong Mee *p++ = cpu_to_be32(1);
292575976de6SKinglong Mee *p++ = cpu_to_be32(bmval0);
292675976de6SKinglong Mee }
292775976de6SKinglong Mee
292875976de6SKinglong Mee return 0;
292975976de6SKinglong Mee out_resource:
293075976de6SKinglong Mee return nfserr_resource;
293175976de6SKinglong Mee }
293275976de6SKinglong Mee
29331da177e4SLinus Torvalds /*
29341da177e4SLinus Torvalds * Note: @fhp can be NULL; in this case, we might have to compose the filehandle
29351da177e4SLinus Torvalds * ourselves.
29361da177e4SLinus Torvalds */
2937da2ebce6SJeff Layton static __be32
nfsd4_encode_fattr(struct xdr_stream * xdr,struct svc_fh * fhp,struct svc_export * exp,struct dentry * dentry,u32 * bmval,struct svc_rqst * rqstp,int ignore_crossmnt)2938d5184658SJ. Bruce Fields nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
2939d5184658SJ. Bruce Fields struct svc_export *exp,
2940d5184658SJ. Bruce Fields struct dentry *dentry, u32 *bmval,
2941406a7ea9SFrank Filz struct svc_rqst *rqstp, int ignore_crossmnt)
29421da177e4SLinus Torvalds {
29431da177e4SLinus Torvalds u32 bmval0 = bmval[0];
29441da177e4SLinus Torvalds u32 bmval1 = bmval[1];
29457e705706SAndy Adamson u32 bmval2 = bmval[2];
29461da177e4SLinus Torvalds struct kstat stat;
2947d50e6136SJ. Bruce Fields struct svc_fh *tempfh = NULL;
29481da177e4SLinus Torvalds struct kstatfs statfs;
2949ab04de60SChuck Lever __be32 *p, *attrlen_p;
29501fcea5b2SJ. Bruce Fields int starting_len = xdr->buf->len;
2951082d4bd7SJ. Bruce Fields int attrlen_offset;
29521da177e4SLinus Torvalds u32 dummy;
29531da177e4SLinus Torvalds u64 dummy64;
295442ca0993SJ.Bruce Fields u32 rdattr_err = 0;
2955b37ad28bSAl Viro __be32 status;
2956b8dd7b9aSAl Viro int err;
29571da177e4SLinus Torvalds struct nfs4_acl *acl = NULL;
29580ab88ca4SArnd Bergmann #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
295918032ca0SDavid Quigley void *context = NULL;
296018032ca0SDavid Quigley int contextlen;
29610ab88ca4SArnd Bergmann #endif
296218032ca0SDavid Quigley bool contextsupport = false;
29637e705706SAndy Adamson struct nfsd4_compoundres *resp = rqstp->rq_resp;
29647e705706SAndy Adamson u32 minorversion = resp->cstate.minorversion;
2965ebabe9a9SChristoph Hellwig struct path path = {
2966ebabe9a9SChristoph Hellwig .mnt = exp->ex_path.mnt,
2967ebabe9a9SChristoph Hellwig .dentry = dentry,
2968ebabe9a9SChristoph Hellwig };
29693d733711SStanislav Kinsbursky struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
29701da177e4SLinus Torvalds
29711da177e4SLinus Torvalds BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1);
2972916d2d84SJ. Bruce Fields BUG_ON(!nfsd_attrs_supported(minorversion, bmval));
29731da177e4SLinus Torvalds
297442ca0993SJ.Bruce Fields if (exp->ex_fslocs.migrated) {
2975c2227a39SKinglong Mee status = fattr_handle_absent_fs(&bmval0, &bmval1, &bmval2, &rdattr_err);
297642ca0993SJ.Bruce Fields if (status)
297742ca0993SJ.Bruce Fields goto out;
297842ca0993SJ.Bruce Fields }
2979fd19ca36SDai Ngo if (bmval0 & (FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE)) {
2980fd19ca36SDai Ngo status = nfsd4_deleg_getattr_conflict(rqstp, d_inode(dentry));
2981fd19ca36SDai Ngo if (status)
2982fd19ca36SDai Ngo goto out;
2983fd19ca36SDai Ngo }
298442ca0993SJ.Bruce Fields
2985638e3e7dSJeff Layton err = vfs_getattr(&path, &stat,
2986638e3e7dSJeff Layton STATX_BASIC_STATS | STATX_BTIME | STATX_CHANGE_COOKIE,
2987638e3e7dSJeff Layton AT_STATX_SYNC_AS_STAT);
2988b8dd7b9aSAl Viro if (err)
29891da177e4SLinus Torvalds goto out_nfserr;
2990e377a3e6SOndrej Valousek if (!(stat.result_mask & STATX_BTIME))
2991e377a3e6SOndrej Valousek /* underlying FS does not offer btime so we can't share it */
2992e377a3e6SOndrej Valousek bmval1 &= ~FATTR4_WORD1_TIME_CREATE;
299312337901SChristoph Hellwig if ((bmval0 & (FATTR4_WORD0_FILES_AVAIL | FATTR4_WORD0_FILES_FREE |
299412337901SChristoph Hellwig FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_MAXNAME)) ||
29951da177e4SLinus Torvalds (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
29961da177e4SLinus Torvalds FATTR4_WORD1_SPACE_TOTAL))) {
2997ebabe9a9SChristoph Hellwig err = vfs_statfs(&path, &statfs);
2998b8dd7b9aSAl Viro if (err)
29991da177e4SLinus Torvalds goto out_nfserr;
30001da177e4SLinus Torvalds }
30011da177e4SLinus Torvalds if ((bmval0 & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) && !fhp) {
3002d50e6136SJ. Bruce Fields tempfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL);
3003d50e6136SJ. Bruce Fields status = nfserr_jukebox;
3004d50e6136SJ. Bruce Fields if (!tempfh)
3005d50e6136SJ. Bruce Fields goto out;
3006d50e6136SJ. Bruce Fields fh_init(tempfh, NFS4_FHSIZE);
3007d50e6136SJ. Bruce Fields status = fh_compose(tempfh, exp, dentry, NULL);
30081da177e4SLinus Torvalds if (status)
30091da177e4SLinus Torvalds goto out;
3010d50e6136SJ. Bruce Fields fhp = tempfh;
30111da177e4SLinus Torvalds }
30121da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_ACL) {
30130c9d65e7SAndreas Gruenbacher err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
3014b8dd7b9aSAl Viro if (err == -EOPNOTSUPP)
30151da177e4SLinus Torvalds bmval0 &= ~FATTR4_WORD0_ACL;
3016b8dd7b9aSAl Viro else if (err == -EINVAL) {
30171da177e4SLinus Torvalds status = nfserr_attrnotsupp;
30181da177e4SLinus Torvalds goto out;
3019b8dd7b9aSAl Viro } else if (err != 0)
30201da177e4SLinus Torvalds goto out_nfserr;
30211da177e4SLinus Torvalds }
30222b44f1baSBenny Halevy
302318032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
3024c2227a39SKinglong Mee if ((bmval2 & FATTR4_WORD2_SECURITY_LABEL) ||
3025c2227a39SKinglong Mee bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) {
302632ddd944SJ. Bruce Fields if (exp->ex_flags & NFSEXP_SECURITY_LABEL)
30272b0143b5SDavid Howells err = security_inode_getsecctx(d_inode(dentry),
302818032ca0SDavid Quigley &context, &contextlen);
302932ddd944SJ. Bruce Fields else
303032ddd944SJ. Bruce Fields err = -EOPNOTSUPP;
303118032ca0SDavid Quigley contextsupport = (err == 0);
303218032ca0SDavid Quigley if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
303318032ca0SDavid Quigley if (err == -EOPNOTSUPP)
303418032ca0SDavid Quigley bmval2 &= ~FATTR4_WORD2_SECURITY_LABEL;
303518032ca0SDavid Quigley else if (err)
303618032ca0SDavid Quigley goto out_nfserr;
303718032ca0SDavid Quigley }
303818032ca0SDavid Quigley }
303918032ca0SDavid Quigley #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
304018032ca0SDavid Quigley
304175976de6SKinglong Mee status = nfsd4_encode_bitmap(xdr, bmval0, bmval1, bmval2);
304275976de6SKinglong Mee if (status)
304375976de6SKinglong Mee goto out;
3044082d4bd7SJ. Bruce Fields
3045082d4bd7SJ. Bruce Fields attrlen_offset = xdr->buf->len;
3046ab04de60SChuck Lever attrlen_p = xdr_reserve_space(xdr, XDR_UNIT);
3047ab04de60SChuck Lever if (!attrlen_p)
3048ddd1ea56SJ. Bruce Fields goto out_resource;
30491da177e4SLinus Torvalds
30501da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) {
3051dcd20869SJ. Bruce Fields u32 supp[3];
3052dcd20869SJ. Bruce Fields
3053dcd20869SJ. Bruce Fields memcpy(supp, nfsd_suppattrs[minorversion], sizeof(supp));
30547e705706SAndy Adamson
30550c9d65e7SAndreas Gruenbacher if (!IS_POSIXACL(dentry->d_inode))
3056916d2d84SJ. Bruce Fields supp[0] &= ~FATTR4_WORD0_ACL;
305718032ca0SDavid Quigley if (!contextsupport)
3058916d2d84SJ. Bruce Fields supp[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
3059916d2d84SJ. Bruce Fields if (!supp[2]) {
3060ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 12);
3061ddd1ea56SJ. Bruce Fields if (!p)
30622b44f1baSBenny Halevy goto out_resource;
3063c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(2);
3064916d2d84SJ. Bruce Fields *p++ = cpu_to_be32(supp[0]);
3065916d2d84SJ. Bruce Fields *p++ = cpu_to_be32(supp[1]);
30667e705706SAndy Adamson } else {
3067ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 16);
3068ddd1ea56SJ. Bruce Fields if (!p)
30692b44f1baSBenny Halevy goto out_resource;
3070c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(3);
3071916d2d84SJ. Bruce Fields *p++ = cpu_to_be32(supp[0]);
3072916d2d84SJ. Bruce Fields *p++ = cpu_to_be32(supp[1]);
3073916d2d84SJ. Bruce Fields *p++ = cpu_to_be32(supp[2]);
30747e705706SAndy Adamson }
30751da177e4SLinus Torvalds }
30761da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_TYPE) {
3077ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3078ddd1ea56SJ. Bruce Fields if (!p)
30791da177e4SLinus Torvalds goto out_resource;
30803d2544b1SJ. Bruce Fields dummy = nfs4_file_type(stat.mode);
30816b6d8137SJ. Bruce Fields if (dummy == NF4BAD) {
30826b6d8137SJ. Bruce Fields status = nfserr_serverfault;
30836b6d8137SJ. Bruce Fields goto out;
30846b6d8137SJ. Bruce Fields }
3085c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(dummy);
30861da177e4SLinus Torvalds }
30871da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) {
3088ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3089ddd1ea56SJ. Bruce Fields if (!p)
30901da177e4SLinus Torvalds goto out_resource;
309149640001SNeilBrown if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
3092c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(NFS4_FH_PERSISTENT);
309349640001SNeilBrown else
3094c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(NFS4_FH_PERSISTENT|
3095c373b0a4SJ. Bruce Fields NFS4_FH_VOL_RENAME);
30961da177e4SLinus Torvalds }
30971da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_CHANGE) {
3098ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
3099ddd1ea56SJ. Bruce Fields if (!p)
31001da177e4SLinus Torvalds goto out_resource;
3101b8800921SNeilBrown p = encode_change(p, &stat, d_inode(dentry), exp);
31021da177e4SLinus Torvalds }
31031da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_SIZE) {
3104ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
3105ddd1ea56SJ. Bruce Fields if (!p)
31061da177e4SLinus Torvalds goto out_resource;
3107b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, stat.size);
31081da177e4SLinus Torvalds }
31091da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) {
3110ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3111ddd1ea56SJ. Bruce Fields if (!p)
31121da177e4SLinus Torvalds goto out_resource;
3113c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(1);
31141da177e4SLinus Torvalds }
31151da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_SYMLINK_SUPPORT) {
3116ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3117ddd1ea56SJ. Bruce Fields if (!p)
31181da177e4SLinus Torvalds goto out_resource;
3119c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(1);
31201da177e4SLinus Torvalds }
31211da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_NAMED_ATTR) {
3122ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3123ddd1ea56SJ. Bruce Fields if (!p)
31241da177e4SLinus Torvalds goto out_resource;
3125c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0);
31261da177e4SLinus Torvalds }
31271da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FSID) {
3128ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 16);
3129ddd1ea56SJ. Bruce Fields if (!p)
31301da177e4SLinus Torvalds goto out_resource;
313142ca0993SJ.Bruce Fields if (exp->ex_fslocs.migrated) {
3132b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MAJOR);
3133b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MINOR);
3134af6a4e28SNeilBrown } else switch(fsid_source(fhp)) {
3135af6a4e28SNeilBrown case FSIDSOURCE_FSID:
3136b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (u64)exp->ex_fsid);
3137b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (u64)0);
3138af6a4e28SNeilBrown break;
3139af6a4e28SNeilBrown case FSIDSOURCE_DEV:
3140c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0);
3141c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(MAJOR(stat.dev));
3142c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0);
3143c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(MINOR(stat.dev));
3144af6a4e28SNeilBrown break;
3145af6a4e28SNeilBrown case FSIDSOURCE_UUID:
314694eb3689SKinglong Mee p = xdr_encode_opaque_fixed(p, exp->ex_uuid,
314794eb3689SKinglong Mee EX_UUID_LEN);
3148af6a4e28SNeilBrown break;
31491da177e4SLinus Torvalds }
31501da177e4SLinus Torvalds }
31511da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) {
3152ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3153ddd1ea56SJ. Bruce Fields if (!p)
31541da177e4SLinus Torvalds goto out_resource;
3155c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0);
31561da177e4SLinus Torvalds }
31571da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_LEASE_TIME) {
3158ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3159ddd1ea56SJ. Bruce Fields if (!p)
31601da177e4SLinus Torvalds goto out_resource;
3161c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(nn->nfsd4_lease);
31621da177e4SLinus Torvalds }
31631da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) {
3164ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3165ddd1ea56SJ. Bruce Fields if (!p)
31661da177e4SLinus Torvalds goto out_resource;
3167c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(rdattr_err);
31681da177e4SLinus Torvalds }
31691da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_ACL) {
31701da177e4SLinus Torvalds struct nfs4_ace *ace;
31711da177e4SLinus Torvalds
31721da177e4SLinus Torvalds if (acl == NULL) {
3173ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3174ddd1ea56SJ. Bruce Fields if (!p)
31751da177e4SLinus Torvalds goto out_resource;
31761da177e4SLinus Torvalds
3177c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0);
31781da177e4SLinus Torvalds goto out_acl;
31791da177e4SLinus Torvalds }
3180ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3181ddd1ea56SJ. Bruce Fields if (!p)
31821da177e4SLinus Torvalds goto out_resource;
3183c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(acl->naces);
31841da177e4SLinus Torvalds
318528e05dd8SJ. Bruce Fields for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
3186ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4*3);
3187ddd1ea56SJ. Bruce Fields if (!p)
31881da177e4SLinus Torvalds goto out_resource;
3189c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(ace->type);
3190c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(ace->flag);
3191c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(ace->access_mask &
3192c373b0a4SJ. Bruce Fields NFS4_ACE_MASK_ALL);
3193ddd1ea56SJ. Bruce Fields status = nfsd4_encode_aclname(xdr, rqstp, ace);
31941da177e4SLinus Torvalds if (status)
31951da177e4SLinus Torvalds goto out;
31961da177e4SLinus Torvalds }
31971da177e4SLinus Torvalds }
31981da177e4SLinus Torvalds out_acl:
31991da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_ACLSUPPORT) {
3200ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3201ddd1ea56SJ. Bruce Fields if (!p)
32021da177e4SLinus Torvalds goto out_resource;
32030c9d65e7SAndreas Gruenbacher *p++ = cpu_to_be32(IS_POSIXACL(dentry->d_inode) ?
32041da177e4SLinus Torvalds ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0);
32051da177e4SLinus Torvalds }
32061da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_CANSETTIME) {
3207ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3208ddd1ea56SJ. Bruce Fields if (!p)
32091da177e4SLinus Torvalds goto out_resource;
3210c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(1);
32111da177e4SLinus Torvalds }
32121da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) {
3213ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3214ddd1ea56SJ. Bruce Fields if (!p)
32151da177e4SLinus Torvalds goto out_resource;
3216c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0);
32171da177e4SLinus Torvalds }
32181da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) {
3219ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3220ddd1ea56SJ. Bruce Fields if (!p)
32211da177e4SLinus Torvalds goto out_resource;
3222c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(1);
32231da177e4SLinus Torvalds }
32241da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_CHOWN_RESTRICTED) {
3225ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3226ddd1ea56SJ. Bruce Fields if (!p)
32271da177e4SLinus Torvalds goto out_resource;
3228c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(1);
32291da177e4SLinus Torvalds }
32301da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FILEHANDLE) {
3231ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, fhp->fh_handle.fh_size + 4);
3232ddd1ea56SJ. Bruce Fields if (!p)
32331da177e4SLinus Torvalds goto out_resource;
3234d8b26071SNeilBrown p = xdr_encode_opaque(p, &fhp->fh_handle.fh_raw,
32350c0c267bSJ. Bruce Fields fhp->fh_handle.fh_size);
32361da177e4SLinus Torvalds }
32371da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FILEID) {
3238ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
3239ddd1ea56SJ. Bruce Fields if (!p)
32401da177e4SLinus Torvalds goto out_resource;
3241b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, stat.ino);
32421da177e4SLinus Torvalds }
32431da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FILES_AVAIL) {
3244ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
3245ddd1ea56SJ. Bruce Fields if (!p)
32461da177e4SLinus Torvalds goto out_resource;
3247b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (u64) statfs.f_ffree);
32481da177e4SLinus Torvalds }
32491da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FILES_FREE) {
3250ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
3251ddd1ea56SJ. Bruce Fields if (!p)
32521da177e4SLinus Torvalds goto out_resource;
3253b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (u64) statfs.f_ffree);
32541da177e4SLinus Torvalds }
32551da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FILES_TOTAL) {
3256ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
3257ddd1ea56SJ. Bruce Fields if (!p)
32581da177e4SLinus Torvalds goto out_resource;
3259b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (u64) statfs.f_files);
32601da177e4SLinus Torvalds }
326181c3f413SJ.Bruce Fields if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
3262ddd1ea56SJ. Bruce Fields status = nfsd4_encode_fs_locations(xdr, rqstp, exp);
326381c3f413SJ.Bruce Fields if (status)
326481c3f413SJ.Bruce Fields goto out;
326581c3f413SJ.Bruce Fields }
32661da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) {
3267ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3268ddd1ea56SJ. Bruce Fields if (!p)
32691da177e4SLinus Torvalds goto out_resource;
3270c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(1);
32711da177e4SLinus Torvalds }
32721da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_MAXFILESIZE) {
3273ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
3274ddd1ea56SJ. Bruce Fields if (!p)
32751da177e4SLinus Torvalds goto out_resource;
3276b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, exp->ex_path.mnt->mnt_sb->s_maxbytes);
32771da177e4SLinus Torvalds }
32781da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_MAXLINK) {
3279ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3280ddd1ea56SJ. Bruce Fields if (!p)
32811da177e4SLinus Torvalds goto out_resource;
3282c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(255);
32831da177e4SLinus Torvalds }
32841da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_MAXNAME) {
3285ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3286ddd1ea56SJ. Bruce Fields if (!p)
32871da177e4SLinus Torvalds goto out_resource;
3288c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(statfs.f_namelen);
32891da177e4SLinus Torvalds }
32901da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_MAXREAD) {
3291ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
3292ddd1ea56SJ. Bruce Fields if (!p)
32931da177e4SLinus Torvalds goto out_resource;
3294b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (u64) svc_max_payload(rqstp));
32951da177e4SLinus Torvalds }
32961da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_MAXWRITE) {
3297ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
3298ddd1ea56SJ. Bruce Fields if (!p)
32991da177e4SLinus Torvalds goto out_resource;
3300b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (u64) svc_max_payload(rqstp));
33011da177e4SLinus Torvalds }
33021da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_MODE) {
3303ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3304ddd1ea56SJ. Bruce Fields if (!p)
33051da177e4SLinus Torvalds goto out_resource;
3306c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(stat.mode & S_IALLUGO);
33071da177e4SLinus Torvalds }
33081da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_NO_TRUNC) {
3309ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3310ddd1ea56SJ. Bruce Fields if (!p)
33111da177e4SLinus Torvalds goto out_resource;
3312c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(1);
33131da177e4SLinus Torvalds }
33141da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_NUMLINKS) {
3315ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3316ddd1ea56SJ. Bruce Fields if (!p)
33171da177e4SLinus Torvalds goto out_resource;
3318c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(stat.nlink);
33191da177e4SLinus Torvalds }
33201da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_OWNER) {
3321ddd1ea56SJ. Bruce Fields status = nfsd4_encode_user(xdr, rqstp, stat.uid);
33221da177e4SLinus Torvalds if (status)
33231da177e4SLinus Torvalds goto out;
33241da177e4SLinus Torvalds }
33251da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
3326ddd1ea56SJ. Bruce Fields status = nfsd4_encode_group(xdr, rqstp, stat.gid);
33271da177e4SLinus Torvalds if (status)
33281da177e4SLinus Torvalds goto out;
33291da177e4SLinus Torvalds }
33301da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_RAWDEV) {
3331ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
3332ddd1ea56SJ. Bruce Fields if (!p)
33331da177e4SLinus Torvalds goto out_resource;
3334c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32((u32) MAJOR(stat.rdev));
3335c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32((u32) MINOR(stat.rdev));
33361da177e4SLinus Torvalds }
33371da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) {
3338ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
3339ddd1ea56SJ. Bruce Fields if (!p)
33401da177e4SLinus Torvalds goto out_resource;
33411da177e4SLinus Torvalds dummy64 = (u64)statfs.f_bavail * (u64)statfs.f_bsize;
3342b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, dummy64);
33431da177e4SLinus Torvalds }
33441da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_SPACE_FREE) {
3345ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
3346ddd1ea56SJ. Bruce Fields if (!p)
33471da177e4SLinus Torvalds goto out_resource;
33481da177e4SLinus Torvalds dummy64 = (u64)statfs.f_bfree * (u64)statfs.f_bsize;
3349b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, dummy64);
33501da177e4SLinus Torvalds }
33511da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) {
3352ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
3353ddd1ea56SJ. Bruce Fields if (!p)
33541da177e4SLinus Torvalds goto out_resource;
33551da177e4SLinus Torvalds dummy64 = (u64)statfs.f_blocks * (u64)statfs.f_bsize;
3356b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, dummy64);
33571da177e4SLinus Torvalds }
33581da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_SPACE_USED) {
3359ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
3360ddd1ea56SJ. Bruce Fields if (!p)
33611da177e4SLinus Torvalds goto out_resource;
33621da177e4SLinus Torvalds dummy64 = (u64)stat.blocks << 9;
3363b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, dummy64);
33641da177e4SLinus Torvalds }
33651da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_TIME_ACCESS) {
336626217679SChuck Lever status = nfsd4_encode_nfstime4(xdr, &stat.atime);
336726217679SChuck Lever if (status)
336826217679SChuck Lever goto out;
33691da177e4SLinus Torvalds }
3370d7dbed45STavian Barnes if (bmval1 & FATTR4_WORD1_TIME_CREATE) {
3371d7dbed45STavian Barnes status = nfsd4_encode_nfstime4(xdr, &stat.btime);
3372d7dbed45STavian Barnes if (status)
3373d7dbed45STavian Barnes goto out;
3374d7dbed45STavian Barnes }
33751da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_TIME_DELTA) {
3376ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 12);
3377ddd1ea56SJ. Bruce Fields if (!p)
33781da177e4SLinus Torvalds goto out_resource;
337916945141SJ. Bruce Fields p = encode_time_delta(p, d_inode(dentry));
33801da177e4SLinus Torvalds }
33811da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_TIME_METADATA) {
338226217679SChuck Lever status = nfsd4_encode_nfstime4(xdr, &stat.ctime);
338326217679SChuck Lever if (status)
338426217679SChuck Lever goto out;
33851da177e4SLinus Torvalds }
33861da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_TIME_MODIFY) {
338726217679SChuck Lever status = nfsd4_encode_nfstime4(xdr, &stat.mtime);
338826217679SChuck Lever if (status)
338926217679SChuck Lever goto out;
33901da177e4SLinus Torvalds }
33911da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
33920a2050d7SKinglong Mee u64 ino = stat.ino;
33930a2050d7SKinglong Mee
3394ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
3395ddd1ea56SJ. Bruce Fields if (!p)
33961da177e4SLinus Torvalds goto out_resource;
3397406a7ea9SFrank Filz /*
33986106d911SJeff Layton * Get ino of mountpoint in parent filesystem, if not ignoring
33996106d911SJeff Layton * crossmount and this is the root of a cross-mounted
34006106d911SJeff Layton * filesystem.
3401406a7ea9SFrank Filz */
3402406a7ea9SFrank Filz if (ignore_crossmnt == 0 &&
34030a2050d7SKinglong Mee dentry == exp->ex_path.mnt->mnt_root) {
34046106d911SJeff Layton err = nfsd4_get_mounted_on_ino(exp, &ino);
34050a2050d7SKinglong Mee if (err)
34060a2050d7SKinglong Mee goto out_nfserr;
34070a2050d7SKinglong Mee }
34080a2050d7SKinglong Mee p = xdr_encode_hyper(p, ino);
34091da177e4SLinus Torvalds }
34109cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
34116896f15aSKinglong Mee if (bmval1 & FATTR4_WORD1_FS_LAYOUT_TYPES) {
34128a4c3926SJeff Layton status = nfsd4_encode_layout_types(xdr, exp->ex_layout_types);
34136896f15aSKinglong Mee if (status)
34146896f15aSKinglong Mee goto out;
34159cf514ccSChristoph Hellwig }
34166896f15aSKinglong Mee
34176896f15aSKinglong Mee if (bmval2 & FATTR4_WORD2_LAYOUT_TYPES) {
34188a4c3926SJeff Layton status = nfsd4_encode_layout_types(xdr, exp->ex_layout_types);
34196896f15aSKinglong Mee if (status)
34206896f15aSKinglong Mee goto out;
34219cf514ccSChristoph Hellwig }
34229cf514ccSChristoph Hellwig
34239cf514ccSChristoph Hellwig if (bmval2 & FATTR4_WORD2_LAYOUT_BLKSIZE) {
34249cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 4);
34259cf514ccSChristoph Hellwig if (!p)
34269cf514ccSChristoph Hellwig goto out_resource;
34279cf514ccSChristoph Hellwig *p++ = cpu_to_be32(stat.blksize);
34289cf514ccSChristoph Hellwig }
34299cf514ccSChristoph Hellwig #endif /* CONFIG_NFSD_PNFS */
34308c18f205SBenny Halevy if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) {
3431b26b78cbSTrond Myklebust u32 supp[3];
3432b26b78cbSTrond Myklebust
3433b26b78cbSTrond Myklebust memcpy(supp, nfsd_suppattrs[minorversion], sizeof(supp));
3434b26b78cbSTrond Myklebust supp[0] &= NFSD_SUPPATTR_EXCLCREAT_WORD0;
3435b26b78cbSTrond Myklebust supp[1] &= NFSD_SUPPATTR_EXCLCREAT_WORD1;
3436b26b78cbSTrond Myklebust supp[2] &= NFSD_SUPPATTR_EXCLCREAT_WORD2;
3437b26b78cbSTrond Myklebust
3438b26b78cbSTrond Myklebust status = nfsd4_encode_bitmap(xdr, supp[0], supp[1], supp[2]);
343975976de6SKinglong Mee if (status)
344075976de6SKinglong Mee goto out;
34418c18f205SBenny Halevy }
34427e705706SAndy Adamson
34430ab88ca4SArnd Bergmann #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
34447d580722SKinglong Mee if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
34457d580722SKinglong Mee status = nfsd4_encode_security_label(xdr, rqstp, context,
34467d580722SKinglong Mee contextlen);
34477d580722SKinglong Mee if (status)
34487d580722SKinglong Mee goto out;
34497d580722SKinglong Mee }
34500ab88ca4SArnd Bergmann #endif
34517d580722SKinglong Mee
34520e885e84SFrank van der Linden if (bmval2 & FATTR4_WORD2_XATTR_SUPPORT) {
34530e885e84SFrank van der Linden p = xdr_reserve_space(xdr, 4);
34540e885e84SFrank van der Linden if (!p)
34550e885e84SFrank van der Linden goto out_resource;
3456831be973SChristian Brauner err = xattr_supports_user_prefix(d_inode(dentry));
34570e885e84SFrank van der Linden *p++ = cpu_to_be32(err == 0);
34580e885e84SFrank van der Linden }
34590e885e84SFrank van der Linden
3460ab04de60SChuck Lever *attrlen_p = cpu_to_be32(xdr->buf->len - attrlen_offset - XDR_UNIT);
34611da177e4SLinus Torvalds status = nfs_ok;
34621da177e4SLinus Torvalds
34631da177e4SLinus Torvalds out:
3464ba4e55bbSJ. Bruce Fields #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
346518032ca0SDavid Quigley if (context)
346618032ca0SDavid Quigley security_release_secctx(context, contextlen);
3467ba4e55bbSJ. Bruce Fields #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
346828e05dd8SJ. Bruce Fields kfree(acl);
346918df11d0SYan, Zheng if (tempfh) {
3470d50e6136SJ. Bruce Fields fh_put(tempfh);
347118df11d0SYan, Zheng kfree(tempfh);
347218df11d0SYan, Zheng }
34731fcea5b2SJ. Bruce Fields if (status)
34741fcea5b2SJ. Bruce Fields xdr_truncate_encode(xdr, starting_len);
34751da177e4SLinus Torvalds return status;
34761da177e4SLinus Torvalds out_nfserr:
3477b8dd7b9aSAl Viro status = nfserrno(err);
34781da177e4SLinus Torvalds goto out;
34791da177e4SLinus Torvalds out_resource:
34801da177e4SLinus Torvalds status = nfserr_resource;
34811da177e4SLinus Torvalds goto out;
34821da177e4SLinus Torvalds }
34831da177e4SLinus Torvalds
svcxdr_init_encode_from_buffer(struct xdr_stream * xdr,struct xdr_buf * buf,__be32 * p,int bytes)34842825a7f9SJ. Bruce Fields static void svcxdr_init_encode_from_buffer(struct xdr_stream *xdr,
34852825a7f9SJ. Bruce Fields struct xdr_buf *buf, __be32 *p, int bytes)
34862825a7f9SJ. Bruce Fields {
34872825a7f9SJ. Bruce Fields xdr->scratch.iov_len = 0;
34882825a7f9SJ. Bruce Fields memset(buf, 0, sizeof(struct xdr_buf));
34892825a7f9SJ. Bruce Fields buf->head[0].iov_base = p;
34902825a7f9SJ. Bruce Fields buf->head[0].iov_len = 0;
34912825a7f9SJ. Bruce Fields buf->len = 0;
34922825a7f9SJ. Bruce Fields xdr->buf = buf;
34932825a7f9SJ. Bruce Fields xdr->iov = buf->head;
34942825a7f9SJ. Bruce Fields xdr->p = p;
34952825a7f9SJ. Bruce Fields xdr->end = (void *)p + bytes;
34962825a7f9SJ. Bruce Fields buf->buflen = bytes;
34972825a7f9SJ. Bruce Fields }
34982825a7f9SJ. 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)3499d5184658SJ. Bruce Fields __be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
3500d5184658SJ. Bruce Fields struct svc_fh *fhp, struct svc_export *exp,
3501d5184658SJ. Bruce Fields struct dentry *dentry, u32 *bmval,
3502d5184658SJ. Bruce Fields struct svc_rqst *rqstp, int ignore_crossmnt)
3503d5184658SJ. Bruce Fields {
35042825a7f9SJ. Bruce Fields struct xdr_buf dummy;
3505d5184658SJ. Bruce Fields struct xdr_stream xdr;
3506d5184658SJ. Bruce Fields __be32 ret;
3507d5184658SJ. Bruce Fields
35082825a7f9SJ. Bruce Fields svcxdr_init_encode_from_buffer(&xdr, &dummy, *p, words << 2);
3509d5184658SJ. Bruce Fields ret = nfsd4_encode_fattr(&xdr, fhp, exp, dentry, bmval, rqstp,
3510d5184658SJ. Bruce Fields ignore_crossmnt);
3511d5184658SJ. Bruce Fields *p = xdr.p;
3512d5184658SJ. Bruce Fields return ret;
3513d5184658SJ. Bruce Fields }
3514d5184658SJ. Bruce Fields
attributes_need_mount(u32 * bmval)3515c0ce6ec8SJ. Bruce Fields static inline int attributes_need_mount(u32 *bmval)
3516c0ce6ec8SJ. Bruce Fields {
3517c0ce6ec8SJ. Bruce Fields if (bmval[0] & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_LEASE_TIME))
3518c0ce6ec8SJ. Bruce Fields return 1;
3519c0ce6ec8SJ. Bruce Fields if (bmval[1] & ~FATTR4_WORD1_MOUNTED_ON_FILEID)
3520c0ce6ec8SJ. Bruce Fields return 1;
3521c0ce6ec8SJ. Bruce Fields return 0;
3522c0ce6ec8SJ. Bruce Fields }
3523c0ce6ec8SJ. Bruce Fields
3524b37ad28bSAl Viro static __be32
nfsd4_encode_dirent_fattr(struct xdr_stream * xdr,struct nfsd4_readdir * cd,const char * name,int namlen)3525561f0ed4SJ. Bruce Fields nfsd4_encode_dirent_fattr(struct xdr_stream *xdr, struct nfsd4_readdir *cd,
3526561f0ed4SJ. Bruce Fields const char *name, int namlen)
35271da177e4SLinus Torvalds {
35281da177e4SLinus Torvalds struct svc_export *exp = cd->rd_fhp->fh_export;
35291da177e4SLinus Torvalds struct dentry *dentry;
3530b37ad28bSAl Viro __be32 nfserr;
3531406a7ea9SFrank Filz int ignore_crossmnt = 0;
35321da177e4SLinus Torvalds
35336c2d4798SAl Viro dentry = lookup_positive_unlocked(name, cd->rd_fhp->fh_dentry, namlen);
35341da177e4SLinus Torvalds if (IS_ERR(dentry))
35351da177e4SLinus Torvalds return nfserrno(PTR_ERR(dentry));
35361da177e4SLinus Torvalds
35371da177e4SLinus Torvalds exp_get(exp);
3538406a7ea9SFrank Filz /*
3539406a7ea9SFrank Filz * In the case of a mountpoint, the client may be asking for
3540406a7ea9SFrank Filz * attributes that are only properties of the underlying filesystem
3541406a7ea9SFrank Filz * as opposed to the cross-mounted file system. In such a case,
3542406a7ea9SFrank Filz * we will not follow the cross mount and will fill the attribtutes
3543406a7ea9SFrank Filz * directly from the mountpoint dentry.
3544406a7ea9SFrank Filz */
35453227fa41SJ. Bruce Fields if (nfsd_mountpoint(dentry, exp)) {
3546021d3a72SJ.Bruce Fields int err;
3547021d3a72SJ.Bruce Fields
35483227fa41SJ. Bruce Fields if (!(exp->ex_flags & NFSEXP_V4ROOT)
35493227fa41SJ. Bruce Fields && !attributes_need_mount(cd->rd_bmval)) {
35503227fa41SJ. Bruce Fields ignore_crossmnt = 1;
35513227fa41SJ. Bruce Fields goto out_encode;
35523227fa41SJ. Bruce Fields }
3553dcb488a3SAndy Adamson /*
3554dcb488a3SAndy Adamson * Why the heck aren't we just using nfsd_lookup??
3555dcb488a3SAndy Adamson * Different "."/".." handling? Something else?
3556dcb488a3SAndy Adamson * At least, add a comment here to explain....
3557dcb488a3SAndy Adamson */
3558021d3a72SJ.Bruce Fields err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp);
3559021d3a72SJ.Bruce Fields if (err) {
3560021d3a72SJ.Bruce Fields nfserr = nfserrno(err);
35611da177e4SLinus Torvalds goto out_put;
35621da177e4SLinus Torvalds }
3563dcb488a3SAndy Adamson nfserr = check_nfsd_access(exp, cd->rd_rqstp);
3564dcb488a3SAndy Adamson if (nfserr)
3565dcb488a3SAndy Adamson goto out_put;
35661da177e4SLinus Torvalds
35671da177e4SLinus Torvalds }
35683227fa41SJ. Bruce Fields out_encode:
3569561f0ed4SJ. Bruce Fields nfserr = nfsd4_encode_fattr(xdr, NULL, exp, dentry, cd->rd_bmval,
3570406a7ea9SFrank Filz cd->rd_rqstp, ignore_crossmnt);
35711da177e4SLinus Torvalds out_put:
35721da177e4SLinus Torvalds dput(dentry);
35731da177e4SLinus Torvalds exp_put(exp);
35741da177e4SLinus Torvalds return nfserr;
35751da177e4SLinus Torvalds }
35761da177e4SLinus Torvalds
35772ebbc012SAl Viro static __be32 *
nfsd4_encode_rdattr_error(struct xdr_stream * xdr,__be32 nfserr)3578561f0ed4SJ. Bruce Fields nfsd4_encode_rdattr_error(struct xdr_stream *xdr, __be32 nfserr)
35791da177e4SLinus Torvalds {
3580561f0ed4SJ. Bruce Fields __be32 *p;
3581561f0ed4SJ. Bruce Fields
3582c3a45617SKinglong Mee p = xdr_reserve_space(xdr, 20);
3583561f0ed4SJ. Bruce Fields if (!p)
35841da177e4SLinus Torvalds return NULL;
35851da177e4SLinus Torvalds *p++ = htonl(2);
35861da177e4SLinus Torvalds *p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */
35871da177e4SLinus Torvalds *p++ = htonl(0); /* bmval1 */
35881da177e4SLinus Torvalds
358987915c64SJ. Bruce Fields *p++ = htonl(4); /* attribute length */
35901da177e4SLinus Torvalds *p++ = nfserr; /* no htonl */
35911da177e4SLinus Torvalds return p;
35921da177e4SLinus Torvalds }
35931da177e4SLinus Torvalds
35941da177e4SLinus Torvalds static int
nfsd4_encode_dirent(void * ccdv,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)3595a0ad13efSNeilBrown nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
3596a0ad13efSNeilBrown loff_t offset, u64 ino, unsigned int d_type)
35971da177e4SLinus Torvalds {
3598a0ad13efSNeilBrown struct readdir_cd *ccd = ccdv;
35991da177e4SLinus Torvalds struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
3600561f0ed4SJ. Bruce Fields struct xdr_stream *xdr = cd->xdr;
3601561f0ed4SJ. Bruce Fields int start_offset = xdr->buf->len;
3602561f0ed4SJ. Bruce Fields int cookie_offset;
3603aee37764SJ. Bruce Fields u32 name_and_cookie;
3604561f0ed4SJ. Bruce Fields int entry_bytes;
3605b37ad28bSAl Viro __be32 nfserr = nfserr_toosmall;
3606561f0ed4SJ. Bruce Fields __be64 wire_offset;
3607561f0ed4SJ. Bruce Fields __be32 *p;
36081da177e4SLinus Torvalds
36091da177e4SLinus Torvalds /* In nfsv4, "." and ".." never make it onto the wire.. */
36101da177e4SLinus Torvalds if (name && isdotent(name, namlen)) {
36111da177e4SLinus Torvalds cd->common.err = nfs_ok;
36121da177e4SLinus Torvalds return 0;
36131da177e4SLinus Torvalds }
36141da177e4SLinus Torvalds
3615561f0ed4SJ. Bruce Fields if (cd->cookie_offset) {
3616561f0ed4SJ. Bruce Fields wire_offset = cpu_to_be64(offset);
3617561f0ed4SJ. Bruce Fields write_bytes_to_xdr_buf(xdr->buf, cd->cookie_offset,
3618561f0ed4SJ. Bruce Fields &wire_offset, 8);
3619561f0ed4SJ. Bruce Fields }
36201da177e4SLinus Torvalds
3621561f0ed4SJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
3622561f0ed4SJ. Bruce Fields if (!p)
36231da177e4SLinus Torvalds goto fail;
36241da177e4SLinus Torvalds *p++ = xdr_one; /* mark entry present */
3625561f0ed4SJ. Bruce Fields cookie_offset = xdr->buf->len;
3626561f0ed4SJ. Bruce Fields p = xdr_reserve_space(xdr, 3*4 + namlen);
3627561f0ed4SJ. Bruce Fields if (!p)
3628561f0ed4SJ. Bruce Fields goto fail;
3629c306d737SChuck Lever p = xdr_encode_hyper(p, OFFSET_MAX); /* offset of next entry */
36301da177e4SLinus Torvalds p = xdr_encode_array(p, name, namlen); /* name length & name */
36311da177e4SLinus Torvalds
3632561f0ed4SJ. Bruce Fields nfserr = nfsd4_encode_dirent_fattr(xdr, cd, name, namlen);
36331da177e4SLinus Torvalds switch (nfserr) {
36341da177e4SLinus Torvalds case nfs_ok:
36351da177e4SLinus Torvalds break;
36361da177e4SLinus Torvalds case nfserr_resource:
36371da177e4SLinus Torvalds nfserr = nfserr_toosmall;
36381da177e4SLinus Torvalds goto fail;
3639b2c0cea6SJ. Bruce Fields case nfserr_noent:
3640f41c5ad2SKinglong Mee xdr_truncate_encode(xdr, start_offset);
3641b2c0cea6SJ. Bruce Fields goto skip_entry;
3642cad85337SJeff Layton case nfserr_jukebox:
3643cad85337SJeff Layton /*
3644cad85337SJeff Layton * The pseudoroot should only display dentries that lead to
3645cad85337SJeff Layton * exports. If we get EJUKEBOX here, then we can't tell whether
3646cad85337SJeff Layton * this entry should be included. Just fail the whole READDIR
3647cad85337SJeff Layton * with NFS4ERR_DELAY in that case, and hope that the situation
3648cad85337SJeff Layton * will resolve itself by the client's next attempt.
3649cad85337SJeff Layton */
3650cad85337SJeff Layton if (cd->rd_fhp->fh_export->ex_flags & NFSEXP_V4ROOT)
3651cad85337SJeff Layton goto fail;
3652cad85337SJeff Layton fallthrough;
36531da177e4SLinus Torvalds default:
36541da177e4SLinus Torvalds /*
36551da177e4SLinus Torvalds * If the client requested the RDATTR_ERROR attribute,
36561da177e4SLinus Torvalds * we stuff the error code into this attribute
36571da177e4SLinus Torvalds * and continue. If this attribute was not requested,
36581da177e4SLinus Torvalds * then in accordance with the spec, we fail the
36591da177e4SLinus Torvalds * entire READDIR operation(!)
36601da177e4SLinus Torvalds */
36611da177e4SLinus Torvalds if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR))
36621da177e4SLinus Torvalds goto fail;
3663561f0ed4SJ. Bruce Fields p = nfsd4_encode_rdattr_error(xdr, nfserr);
366434081efcSFred Isaman if (p == NULL) {
366534081efcSFred Isaman nfserr = nfserr_toosmall;
36661da177e4SLinus Torvalds goto fail;
36671da177e4SLinus Torvalds }
366834081efcSFred Isaman }
3669561f0ed4SJ. Bruce Fields nfserr = nfserr_toosmall;
3670561f0ed4SJ. Bruce Fields entry_bytes = xdr->buf->len - start_offset;
3671561f0ed4SJ. Bruce Fields if (entry_bytes > cd->rd_maxcount)
3672561f0ed4SJ. Bruce Fields goto fail;
3673561f0ed4SJ. Bruce Fields cd->rd_maxcount -= entry_bytes;
3674aee37764SJ. Bruce Fields /*
3675f2e717d6STrond Myklebust * RFC 3530 14.2.24 describes rd_dircount as only a "hint", and
3676f2e717d6STrond Myklebust * notes that it could be zero. If it is zero, then the server
3677f2e717d6STrond Myklebust * should enforce only the rd_maxcount value.
3678aee37764SJ. Bruce Fields */
3679f2e717d6STrond Myklebust if (cd->rd_dircount) {
36800ec016e3SJ. Bruce Fields name_and_cookie = 4 + 4 * XDR_QUADLEN(namlen) + 8;
3681aee37764SJ. Bruce Fields if (name_and_cookie > cd->rd_dircount && cd->cookie_offset)
3682aee37764SJ. Bruce Fields goto fail;
3683aee37764SJ. Bruce Fields cd->rd_dircount -= min(cd->rd_dircount, name_and_cookie);
3684f2e717d6STrond Myklebust if (!cd->rd_dircount)
3685f2e717d6STrond Myklebust cd->rd_maxcount = 0;
3686f2e717d6STrond Myklebust }
36870ec016e3SJ. Bruce Fields
3688561f0ed4SJ. Bruce Fields cd->cookie_offset = cookie_offset;
3689b2c0cea6SJ. Bruce Fields skip_entry:
36901da177e4SLinus Torvalds cd->common.err = nfs_ok;
36911da177e4SLinus Torvalds return 0;
36921da177e4SLinus Torvalds fail:
3693561f0ed4SJ. Bruce Fields xdr_truncate_encode(xdr, start_offset);
36941da177e4SLinus Torvalds cd->common.err = nfserr;
36951da177e4SLinus Torvalds return -EINVAL;
36961da177e4SLinus Torvalds }
36971da177e4SLinus Torvalds
3698d0a381ddSJ. Bruce Fields static __be32
nfsd4_encode_verifier4(struct xdr_stream * xdr,const nfs4_verifier * verf)3699adaa7a50SChuck Lever nfsd4_encode_verifier4(struct xdr_stream *xdr, const nfs4_verifier *verf)
3700adaa7a50SChuck Lever {
3701adaa7a50SChuck Lever __be32 *p;
3702adaa7a50SChuck Lever
3703adaa7a50SChuck Lever p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
3704adaa7a50SChuck Lever if (!p)
3705adaa7a50SChuck Lever return nfserr_resource;
3706adaa7a50SChuck Lever memcpy(p, verf->data, sizeof(verf->data));
3707adaa7a50SChuck Lever return nfs_ok;
3708adaa7a50SChuck Lever }
3709adaa7a50SChuck Lever
3710adaa7a50SChuck Lever static __be32
nfsd4_encode_clientid4(struct xdr_stream * xdr,const clientid_t * clientid)3711adaa7a50SChuck Lever nfsd4_encode_clientid4(struct xdr_stream *xdr, const clientid_t *clientid)
3712adaa7a50SChuck Lever {
3713adaa7a50SChuck Lever __be32 *p;
3714adaa7a50SChuck Lever
3715adaa7a50SChuck Lever p = xdr_reserve_space(xdr, sizeof(__be64));
3716adaa7a50SChuck Lever if (!p)
3717adaa7a50SChuck Lever return nfserr_resource;
3718adaa7a50SChuck Lever memcpy(p, clientid, sizeof(*clientid));
3719adaa7a50SChuck Lever return nfs_ok;
3720adaa7a50SChuck Lever }
3721adaa7a50SChuck Lever
3722adaa7a50SChuck Lever static __be32
nfsd4_encode_stateid(struct xdr_stream * xdr,stateid_t * sid)3723d0a381ddSJ. Bruce Fields nfsd4_encode_stateid(struct xdr_stream *xdr, stateid_t *sid)
3724e2f282b9SBenny Halevy {
3725bc749ca4SJ. Bruce Fields __be32 *p;
3726e2f282b9SBenny Halevy
3727d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, sizeof(stateid_t));
3728d0a381ddSJ. Bruce Fields if (!p)
3729d0a381ddSJ. Bruce Fields return nfserr_resource;
3730c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sid->si_generation);
37310c0c267bSJ. Bruce Fields p = xdr_encode_opaque_fixed(p, &sid->si_opaque,
37320c0c267bSJ. Bruce Fields sizeof(stateid_opaque_t));
3733d0a381ddSJ. Bruce Fields return 0;
3734e2f282b9SBenny Halevy }
3735e2f282b9SBenny Halevy
3736695e12f8SBenny Halevy static __be32
nfsd4_encode_access(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3737e78e274eSKees Cook nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr,
3738e78e274eSKees Cook union nfsd4_op_u *u)
37391da177e4SLinus Torvalds {
3740e78e274eSKees Cook struct nfsd4_access *access = &u->access;
3741bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
3742bc749ca4SJ. Bruce Fields __be32 *p;
37431da177e4SLinus Torvalds
3744d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
3745d0a381ddSJ. Bruce Fields if (!p)
3746d0a381ddSJ. Bruce Fields return nfserr_resource;
3747c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(access->ac_supported);
3748c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(access->ac_resp_access);
3749bac966d6SJ. Bruce Fields return 0;
37501da177e4SLinus Torvalds }
37511da177e4SLinus Torvalds
nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3752e78e274eSKees Cook static __be32 nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres *resp, __be32 nfserr,
3753e78e274eSKees Cook union nfsd4_op_u *u)
37541d1bc8f2SJ. Bruce Fields {
3755e78e274eSKees Cook struct nfsd4_bind_conn_to_session *bcts = &u->bind_conn_to_session;
3756bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
37571d1bc8f2SJ. Bruce Fields __be32 *p;
37581d1bc8f2SJ. Bruce Fields
3759d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 8);
3760d0a381ddSJ. Bruce Fields if (!p)
3761d0a381ddSJ. Bruce Fields return nfserr_resource;
37620c0c267bSJ. Bruce Fields p = xdr_encode_opaque_fixed(p, bcts->sessionid.data,
37630c0c267bSJ. Bruce Fields NFS4_MAX_SESSIONID_LEN);
3764c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(bcts->dir);
37654ce85c8cSChuck Lever /* Upshifting from TCP to RDMA is not supported */
3766c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0);
3767bac966d6SJ. Bruce Fields return 0;
37681d1bc8f2SJ. Bruce Fields }
37691d1bc8f2SJ. Bruce Fields
3770695e12f8SBenny Halevy static __be32
nfsd4_encode_close(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3771e78e274eSKees Cook nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr,
3772e78e274eSKees Cook union nfsd4_op_u *u)
37731da177e4SLinus Torvalds {
3774e78e274eSKees Cook struct nfsd4_close *close = &u->close;
3775bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
3776d0a381ddSJ. Bruce Fields
3777bac966d6SJ. Bruce Fields return nfsd4_encode_stateid(xdr, &close->cl_stateid);
37781da177e4SLinus Torvalds }
37791da177e4SLinus Torvalds
37801da177e4SLinus Torvalds
3781695e12f8SBenny Halevy static __be32
nfsd4_encode_commit(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3782e78e274eSKees Cook nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr,
3783e78e274eSKees Cook union nfsd4_op_u *u)
37841da177e4SLinus Torvalds {
3785e78e274eSKees Cook struct nfsd4_commit *commit = &u->commit;
37861da177e4SLinus Torvalds
3787adaa7a50SChuck Lever return nfsd4_encode_verifier4(resp->xdr, &commit->co_verf);
37881da177e4SLinus Torvalds }
37891da177e4SLinus Torvalds
3790695e12f8SBenny Halevy static __be32
nfsd4_encode_create(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3791e78e274eSKees Cook nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr,
3792e78e274eSKees Cook union nfsd4_op_u *u)
37931da177e4SLinus Torvalds {
3794e78e274eSKees Cook struct nfsd4_create *create = &u->create;
3795bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
37961da177e4SLinus Torvalds
379766a21db7SChuck Lever nfserr = nfsd4_encode_change_info4(xdr, &create->cr_cinfo);
379866a21db7SChuck Lever if (nfserr)
379966a21db7SChuck Lever return nfserr;
3800b96811cdSTrond Myklebust return nfsd4_encode_bitmap(xdr, create->cr_bmval[0],
380175976de6SKinglong Mee create->cr_bmval[1], create->cr_bmval[2]);
38021da177e4SLinus Torvalds }
38031da177e4SLinus Torvalds
3804b37ad28bSAl Viro static __be32
nfsd4_encode_getattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3805e78e274eSKees Cook nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr,
3806e78e274eSKees Cook union nfsd4_op_u *u)
38071da177e4SLinus Torvalds {
3808e78e274eSKees Cook struct nfsd4_getattr *getattr = &u->getattr;
38091da177e4SLinus Torvalds struct svc_fh *fhp = getattr->ga_fhp;
3810bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
38111da177e4SLinus Torvalds
3812bac966d6SJ. Bruce Fields return nfsd4_encode_fattr(xdr, fhp, fhp->fh_export, fhp->fh_dentry,
3813bac966d6SJ. Bruce Fields getattr->ga_bmval, resp->rqstp, 0);
38141da177e4SLinus Torvalds }
38151da177e4SLinus Torvalds
3816695e12f8SBenny Halevy static __be32
nfsd4_encode_getfh(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3817e78e274eSKees Cook nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr,
3818e78e274eSKees Cook union nfsd4_op_u *u)
38191da177e4SLinus Torvalds {
3820e78e274eSKees Cook struct svc_fh **fhpp = &u->getfh;
3821bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
3822695e12f8SBenny Halevy struct svc_fh *fhp = *fhpp;
38231da177e4SLinus Torvalds unsigned int len;
3824bc749ca4SJ. Bruce Fields __be32 *p;
38251da177e4SLinus Torvalds
38261da177e4SLinus Torvalds len = fhp->fh_handle.fh_size;
3827d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, len + 4);
3828d0a381ddSJ. Bruce Fields if (!p)
3829d0a381ddSJ. Bruce Fields return nfserr_resource;
3830d8b26071SNeilBrown p = xdr_encode_opaque(p, &fhp->fh_handle.fh_raw, len);
3831bac966d6SJ. Bruce Fields return 0;
38321da177e4SLinus Torvalds }
38331da177e4SLinus Torvalds
38341da177e4SLinus Torvalds /*
38351da177e4SLinus Torvalds * Including all fields other than the name, a LOCK4denied structure requires
38361da177e4SLinus Torvalds * 8(clientid) + 4(namelen) + 8(offset) + 8(length) + 4(type) = 32 bytes.
38371da177e4SLinus Torvalds */
3838d0a381ddSJ. Bruce Fields static __be32
nfsd4_encode_lock_denied(struct xdr_stream * xdr,struct nfsd4_lock_denied * ld)3839d0a381ddSJ. Bruce Fields nfsd4_encode_lock_denied(struct xdr_stream *xdr, struct nfsd4_lock_denied *ld)
38401da177e4SLinus Torvalds {
38417c13f344SJ. Bruce Fields struct xdr_netobj *conf = &ld->ld_owner;
3842bc749ca4SJ. Bruce Fields __be32 *p;
38431da177e4SLinus Torvalds
38448c7424cfSJ. Bruce Fields again:
3845d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 32 + XDR_LEN(conf->len));
38468c7424cfSJ. Bruce Fields if (!p) {
38478c7424cfSJ. Bruce Fields /*
38488c7424cfSJ. Bruce Fields * Don't fail to return the result just because we can't
38498c7424cfSJ. Bruce Fields * return the conflicting open:
38508c7424cfSJ. Bruce Fields */
38518c7424cfSJ. Bruce Fields if (conf->len) {
3852f98bac5aSKinglong Mee kfree(conf->data);
38538c7424cfSJ. Bruce Fields conf->len = 0;
38548c7424cfSJ. Bruce Fields conf->data = NULL;
38558c7424cfSJ. Bruce Fields goto again;
38568c7424cfSJ. Bruce Fields }
3857d0a381ddSJ. Bruce Fields return nfserr_resource;
38588c7424cfSJ. Bruce Fields }
3859b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, ld->ld_start);
3860b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, ld->ld_length);
3861c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(ld->ld_type);
38627c13f344SJ. Bruce Fields if (conf->len) {
38630c0c267bSJ. Bruce Fields p = xdr_encode_opaque_fixed(p, &ld->ld_clientid, 8);
38640c0c267bSJ. Bruce Fields p = xdr_encode_opaque(p, conf->data, conf->len);
3865f98bac5aSKinglong Mee kfree(conf->data);
38661da177e4SLinus Torvalds } else { /* non - nfsv4 lock in conflict, no clientid nor owner */
3867b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (u64)0); /* clientid */
3868c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); /* length of owner name */
38691da177e4SLinus Torvalds }
3870d0a381ddSJ. Bruce Fields return nfserr_denied;
38711da177e4SLinus Torvalds }
38721da177e4SLinus Torvalds
3873695e12f8SBenny Halevy static __be32
nfsd4_encode_lock(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3874e78e274eSKees Cook nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr,
3875e78e274eSKees Cook union nfsd4_op_u *u)
38761da177e4SLinus Torvalds {
3877e78e274eSKees Cook struct nfsd4_lock *lock = &u->lock;
3878bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
3879d0a381ddSJ. Bruce Fields
3880e2f282b9SBenny Halevy if (!nfserr)
3881d0a381ddSJ. Bruce Fields nfserr = nfsd4_encode_stateid(xdr, &lock->lk_resp_stateid);
3882e2f282b9SBenny Halevy else if (nfserr == nfserr_denied)
3883d0a381ddSJ. Bruce Fields nfserr = nfsd4_encode_lock_denied(xdr, &lock->lk_denied);
3884f98bac5aSKinglong Mee
3885695e12f8SBenny Halevy return nfserr;
38861da177e4SLinus Torvalds }
38871da177e4SLinus Torvalds
3888695e12f8SBenny Halevy static __be32
nfsd4_encode_lockt(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3889e78e274eSKees Cook nfsd4_encode_lockt(struct nfsd4_compoundres *resp, __be32 nfserr,
3890e78e274eSKees Cook union nfsd4_op_u *u)
38911da177e4SLinus Torvalds {
3892e78e274eSKees Cook struct nfsd4_lockt *lockt = &u->lockt;
3893bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
3894d0a381ddSJ. Bruce Fields
38951da177e4SLinus Torvalds if (nfserr == nfserr_denied)
3896d0a381ddSJ. Bruce Fields nfsd4_encode_lock_denied(xdr, &lockt->lt_denied);
3897695e12f8SBenny Halevy return nfserr;
38981da177e4SLinus Torvalds }
38991da177e4SLinus Torvalds
3900695e12f8SBenny Halevy static __be32
nfsd4_encode_locku(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3901e78e274eSKees Cook nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr,
3902e78e274eSKees Cook union nfsd4_op_u *u)
39031da177e4SLinus Torvalds {
3904e78e274eSKees Cook struct nfsd4_locku *locku = &u->locku;
3905bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
3906d0a381ddSJ. Bruce Fields
3907bac966d6SJ. Bruce Fields return nfsd4_encode_stateid(xdr, &locku->lu_stateid);
39081da177e4SLinus Torvalds }
39091da177e4SLinus Torvalds
39101da177e4SLinus Torvalds
3911695e12f8SBenny Halevy static __be32
nfsd4_encode_link(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3912e78e274eSKees Cook nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr,
3913e78e274eSKees Cook union nfsd4_op_u *u)
39141da177e4SLinus Torvalds {
3915e78e274eSKees Cook struct nfsd4_link *link = &u->link;
3916bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
39171da177e4SLinus Torvalds
391866a21db7SChuck Lever return nfsd4_encode_change_info4(xdr, &link->li_cinfo);
39191da177e4SLinus Torvalds }
39201da177e4SLinus Torvalds
39211da177e4SLinus Torvalds
3922695e12f8SBenny Halevy static __be32
nfsd4_encode_open(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3923e78e274eSKees Cook nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr,
3924e78e274eSKees Cook union nfsd4_op_u *u)
39251da177e4SLinus Torvalds {
3926e78e274eSKees Cook struct nfsd4_open *open = &u->open;
3927bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
3928bc749ca4SJ. Bruce Fields __be32 *p;
39291da177e4SLinus Torvalds
3930d0a381ddSJ. Bruce Fields nfserr = nfsd4_encode_stateid(xdr, &open->op_stateid);
3931d0a381ddSJ. Bruce Fields if (nfserr)
3932bac966d6SJ. Bruce Fields return nfserr;
393366a21db7SChuck Lever nfserr = nfsd4_encode_change_info4(xdr, &open->op_cinfo);
393466a21db7SChuck Lever if (nfserr)
393566a21db7SChuck Lever return nfserr;
393666a21db7SChuck Lever if (xdr_stream_encode_u32(xdr, open->op_rflags) < 0)
3937d0a381ddSJ. Bruce Fields return nfserr_resource;
39381da177e4SLinus Torvalds
393975976de6SKinglong Mee nfserr = nfsd4_encode_bitmap(xdr, open->op_bmval[0], open->op_bmval[1],
394075976de6SKinglong Mee open->op_bmval[2]);
394175976de6SKinglong Mee if (nfserr)
3942bac966d6SJ. Bruce Fields return nfserr;
394375976de6SKinglong Mee
394475976de6SKinglong Mee p = xdr_reserve_space(xdr, 4);
394575976de6SKinglong Mee if (!p)
394675976de6SKinglong Mee return nfserr_resource;
394775976de6SKinglong Mee
394875976de6SKinglong Mee *p++ = cpu_to_be32(open->op_delegate_type);
39491da177e4SLinus Torvalds switch (open->op_delegate_type) {
39501da177e4SLinus Torvalds case NFS4_OPEN_DELEGATE_NONE:
39511da177e4SLinus Torvalds break;
39521da177e4SLinus Torvalds case NFS4_OPEN_DELEGATE_READ:
3953d0a381ddSJ. Bruce Fields nfserr = nfsd4_encode_stateid(xdr, &open->op_delegate_stateid);
3954d0a381ddSJ. Bruce Fields if (nfserr)
3955d0a381ddSJ. Bruce Fields return nfserr;
3956d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 20);
3957d0a381ddSJ. Bruce Fields if (!p)
3958d0a381ddSJ. Bruce Fields return nfserr_resource;
3959c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(open->op_recall);
39601da177e4SLinus Torvalds
39611da177e4SLinus Torvalds /*
39621da177e4SLinus Torvalds * TODO: ACE's in delegations
39631da177e4SLinus Torvalds */
3964c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
3965c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0);
3966c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0);
3967c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); /* XXX: is NULL principal ok? */
39681da177e4SLinus Torvalds break;
39691da177e4SLinus Torvalds case NFS4_OPEN_DELEGATE_WRITE:
3970d0a381ddSJ. Bruce Fields nfserr = nfsd4_encode_stateid(xdr, &open->op_delegate_stateid);
3971d0a381ddSJ. Bruce Fields if (nfserr)
3972d0a381ddSJ. Bruce Fields return nfserr;
397350bce06fSChuck Lever
397450bce06fSChuck Lever p = xdr_reserve_space(xdr, XDR_UNIT * 8);
3975d0a381ddSJ. Bruce Fields if (!p)
3976d0a381ddSJ. Bruce Fields return nfserr_resource;
397758f5d894SDai Ngo *p++ = cpu_to_be32(open->op_recall);
39781da177e4SLinus Torvalds
39791da177e4SLinus Torvalds /*
398050bce06fSChuck Lever * Always flush on close
398150bce06fSChuck Lever *
39821da177e4SLinus Torvalds * TODO: space_limit's in delegations
39831da177e4SLinus Torvalds */
3984c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(NFS4_LIMIT_SIZE);
398550bce06fSChuck Lever *p++ = xdr_zero;
398650bce06fSChuck Lever *p++ = xdr_zero;
39871da177e4SLinus Torvalds
39881da177e4SLinus Torvalds /*
39891da177e4SLinus Torvalds * TODO: ACE's in delegations
39901da177e4SLinus Torvalds */
3991c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
3992c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0);
3993c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0);
3994c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); /* XXX: is NULL principal ok? */
39951da177e4SLinus Torvalds break;
3996d24433cdSBenny Halevy case NFS4_OPEN_DELEGATE_NONE_EXT: /* 4.1 */
3997d24433cdSBenny Halevy switch (open->op_why_no_deleg) {
3998d24433cdSBenny Halevy case WND4_CONTENTION:
3999d24433cdSBenny Halevy case WND4_RESOURCE:
4000d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
4001d0a381ddSJ. Bruce Fields if (!p)
4002d0a381ddSJ. Bruce Fields return nfserr_resource;
4003c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(open->op_why_no_deleg);
4004c373b0a4SJ. Bruce Fields /* deleg signaling not supported yet: */
4005c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0);
4006d24433cdSBenny Halevy break;
4007d24433cdSBenny Halevy default:
4008d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
4009d0a381ddSJ. Bruce Fields if (!p)
4010d0a381ddSJ. Bruce Fields return nfserr_resource;
4011c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(open->op_why_no_deleg);
4012d24433cdSBenny Halevy }
4013d24433cdSBenny Halevy break;
40141da177e4SLinus Torvalds default:
40151da177e4SLinus Torvalds BUG();
40161da177e4SLinus Torvalds }
40171da177e4SLinus Torvalds /* XXX save filehandle here */
4018bac966d6SJ. Bruce Fields return 0;
40191da177e4SLinus Torvalds }
40201da177e4SLinus Torvalds
4021695e12f8SBenny Halevy static __be32
nfsd4_encode_open_confirm(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4022e78e274eSKees Cook nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr,
4023e78e274eSKees Cook union nfsd4_op_u *u)
40241da177e4SLinus Torvalds {
4025e78e274eSKees Cook struct nfsd4_open_confirm *oc = &u->open_confirm;
4026bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
4027d0a381ddSJ. Bruce Fields
4028bac966d6SJ. Bruce Fields return nfsd4_encode_stateid(xdr, &oc->oc_resp_stateid);
40291da177e4SLinus Torvalds }
40301da177e4SLinus Torvalds
4031695e12f8SBenny Halevy static __be32
nfsd4_encode_open_downgrade(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4032e78e274eSKees Cook nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr,
4033e78e274eSKees Cook union nfsd4_op_u *u)
40341da177e4SLinus Torvalds {
4035e78e274eSKees Cook struct nfsd4_open_downgrade *od = &u->open_downgrade;
4036bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
4037d0a381ddSJ. Bruce Fields
4038bac966d6SJ. Bruce Fields return nfsd4_encode_stateid(xdr, &od->od_stateid);
40391da177e4SLinus Torvalds }
40401da177e4SLinus Torvalds
4041ba21e20bSChuck Lever /*
4042ba21e20bSChuck Lever * The operation of this function assumes that this is the only
4043ba21e20bSChuck Lever * READ operation in the COMPOUND. If there are multiple READs,
4044ba21e20bSChuck Lever * we use nfsd4_encode_readv().
4045ba21e20bSChuck Lever */
nfsd4_encode_splice_read(struct nfsd4_compoundres * resp,struct nfsd4_read * read,struct file * file,unsigned long maxcount)4046dc97618dSJ. Bruce Fields static __be32 nfsd4_encode_splice_read(
4047dc97618dSJ. Bruce Fields struct nfsd4_compoundres *resp,
4048dc97618dSJ. Bruce Fields struct nfsd4_read *read,
4049dc97618dSJ. Bruce Fields struct file *file, unsigned long maxcount)
40501da177e4SLinus Torvalds {
4051bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
405234a78b48SJ. Bruce Fields struct xdr_buf *buf = xdr->buf;
405376e5492bSChuck Lever int status, space_left;
4054dc97618dSJ. Bruce Fields __be32 nfserr;
4055dc97618dSJ. Bruce Fields
4056ba21e20bSChuck Lever /*
4057ba21e20bSChuck Lever * Make sure there is room at the end of buf->head for
4058ba21e20bSChuck Lever * svcxdr_encode_opaque_pages() to create a tail buffer
4059ba21e20bSChuck Lever * to XDR-pad the payload.
4060ba21e20bSChuck Lever */
4061ba21e20bSChuck Lever if (xdr->iov != xdr->buf->head || xdr->end - xdr->p < 1)
4062dc97618dSJ. Bruce Fields return nfserr_resource;
4063dc97618dSJ. Bruce Fields
406487c5942eSChuck Lever nfserr = nfsd_splice_read(read->rd_rqstp, read->rd_fhp,
406524c7fb85SChuck Lever file, read->rd_offset, &maxcount,
406624c7fb85SChuck Lever &read->rd_eof);
406787c5942eSChuck Lever read->rd_length = maxcount;
406876e5492bSChuck Lever if (nfserr)
406976e5492bSChuck Lever goto out_err;
4070ba21e20bSChuck Lever svcxdr_encode_opaque_pages(read->rd_rqstp, xdr, buf->pages,
4071ba21e20bSChuck Lever buf->page_base, maxcount);
407276e5492bSChuck Lever status = svc_encode_result_payload(read->rd_rqstp,
407376e5492bSChuck Lever buf->head[0].iov_len, maxcount);
407476e5492bSChuck Lever if (status) {
407576e5492bSChuck Lever nfserr = nfserrno(status);
407676e5492bSChuck Lever goto out_err;
4077dc97618dSJ. Bruce Fields }
4078dc97618dSJ. Bruce Fields
4079ba21e20bSChuck Lever /*
4080ba21e20bSChuck Lever * Prepare to encode subsequent operations.
4081ba21e20bSChuck Lever *
4082ba21e20bSChuck Lever * xdr_truncate_encode() is not safe to use after a successful
4083ba21e20bSChuck Lever * splice read has been done, so the following stream
4084ba21e20bSChuck Lever * manipulations are open-coded.
4085ba21e20bSChuck Lever */
4086dc97618dSJ. Bruce Fields space_left = min_t(int, (void *)xdr->end - (void *)xdr->p,
408734a78b48SJ. Bruce Fields buf->buflen - buf->len);
408834a78b48SJ. Bruce Fields buf->buflen = buf->len + space_left;
4089dc97618dSJ. Bruce Fields xdr->end = (__be32 *)((void *)xdr->end + space_left);
4090dc97618dSJ. Bruce Fields
4091ba21e20bSChuck Lever return nfs_ok;
409276e5492bSChuck Lever
409376e5492bSChuck Lever out_err:
409476e5492bSChuck Lever /*
409576e5492bSChuck Lever * nfsd_splice_actor may have already messed with the
409676e5492bSChuck Lever * page length; reset it so as not to confuse
409776e5492bSChuck Lever * xdr_truncate_encode in our caller.
409876e5492bSChuck Lever */
409976e5492bSChuck Lever buf->page_len = 0;
410076e5492bSChuck Lever return nfserr;
4101dc97618dSJ. Bruce Fields }
4102dc97618dSJ. Bruce Fields
nfsd4_encode_readv(struct nfsd4_compoundres * resp,struct nfsd4_read * read,struct file * file,unsigned long maxcount)4103dc97618dSJ. Bruce Fields static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
4104dc97618dSJ. Bruce Fields struct nfsd4_read *read,
4105dc97618dSJ. Bruce Fields struct file *file, unsigned long maxcount)
4106dc97618dSJ. Bruce Fields {
4107bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
41080d32a6bbSChuck Lever unsigned int base = xdr->buf->page_len & ~PAGE_MASK;
4109071ae99fSChuck Lever unsigned int starting_len = xdr->buf->len;
41105e64d85cSChuck Lever __be32 zero = xdr_zero;
4111dc97618dSJ. Bruce Fields __be32 nfserr;
41121da177e4SLinus Torvalds
4113703d7521SChuck Lever if (xdr_reserve_space_vec(xdr, maxcount) < 0)
4114403217f3SAnna Schumaker return nfserr_resource;
41151da177e4SLinus Torvalds
4116703d7521SChuck Lever nfserr = nfsd_iter_read(resp->rqstp, read->rd_fhp, file,
41170d32a6bbSChuck Lever read->rd_offset, &maxcount, base,
411824c7fb85SChuck Lever &read->rd_eof);
411987c5942eSChuck Lever read->rd_length = maxcount;
4120dc97618dSJ. Bruce Fields if (nfserr)
41211da177e4SLinus Torvalds return nfserr;
4122071ae99fSChuck Lever if (svc_encode_result_payload(resp->rqstp, starting_len, maxcount))
412341205539SChuck Lever return nfserr_io;
4124071ae99fSChuck Lever xdr_truncate_encode(xdr, starting_len + xdr_align_size(maxcount));
4125dc97618dSJ. Bruce Fields
41265e64d85cSChuck Lever write_bytes_to_xdr_buf(xdr->buf, starting_len + maxcount, &zero,
41275e64d85cSChuck Lever xdr_pad_size(maxcount));
41285e64d85cSChuck Lever return nfs_ok;
4129dc97618dSJ. Bruce Fields }
4130dc97618dSJ. Bruce Fields
4131dc97618dSJ. Bruce Fields static __be32
nfsd4_encode_read(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4132dc97618dSJ. Bruce Fields nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
4133e78e274eSKees Cook union nfsd4_op_u *u)
4134dc97618dSJ. Bruce Fields {
4135e78e274eSKees Cook struct nfsd4_read *read = &u->read;
4136c738b218SChuck Lever bool splice_ok = test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags);
4137dc97618dSJ. Bruce Fields unsigned long maxcount;
4138bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
41395c4583b2SJeff Layton struct file *file;
4140dc97618dSJ. Bruce Fields int starting_len = xdr->buf->len;
4141dc97618dSJ. Bruce Fields __be32 *p;
4142dc97618dSJ. Bruce Fields
41435c4583b2SJeff Layton if (nfserr)
41445c4583b2SJeff Layton return nfserr;
41455c4583b2SJeff Layton file = read->rd_nf->nf_file;
41465c4583b2SJeff Layton
4147dc97618dSJ. Bruce Fields p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */
4148dc97618dSJ. Bruce Fields if (!p) {
4149c738b218SChuck Lever WARN_ON_ONCE(splice_ok);
4150bac966d6SJ. Bruce Fields return nfserr_resource;
4151dc97618dSJ. Bruce Fields }
4152c738b218SChuck Lever if (resp->xdr->buf->page_len && splice_ok) {
4153b0420980SJ. Bruce Fields WARN_ON_ONCE(1);
415406981d56SAnna Schumaker return nfserr_serverfault;
4155dc97618dSJ. Bruce Fields }
4156dc97618dSJ. Bruce Fields xdr_commit_encode(xdr);
4157dc97618dSJ. Bruce Fields
41580cb4d23aSChuck Lever maxcount = min_t(unsigned long, read->rd_length,
415968e8bb03SChristoph Hellwig (xdr->buf->buflen - xdr->buf->len));
4160dc97618dSJ. Bruce Fields
4161c738b218SChuck Lever if (file->f_op->splice_read && splice_ok)
416296bcad50SChristoph Hellwig nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount);
4163dc97618dSJ. Bruce Fields else
416496bcad50SChristoph Hellwig nfserr = nfsd4_encode_readv(resp, read, file, maxcount);
416528d5bc46SChuck Lever if (nfserr) {
4166dc97618dSJ. Bruce Fields xdr_truncate_encode(xdr, starting_len);
416796bcad50SChristoph Hellwig return nfserr;
41681da177e4SLinus Torvalds }
41691da177e4SLinus Torvalds
417028d5bc46SChuck Lever p = xdr_encode_bool(p, read->rd_eof);
417128d5bc46SChuck Lever *p = cpu_to_be32(read->rd_length);
417228d5bc46SChuck Lever return nfs_ok;
417328d5bc46SChuck Lever }
417428d5bc46SChuck Lever
4175b37ad28bSAl Viro static __be32
nfsd4_encode_readlink(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4176e78e274eSKees Cook nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr,
4177e78e274eSKees Cook union nfsd4_op_u *u)
41781da177e4SLinus Torvalds {
4179e78e274eSKees Cook struct nfsd4_readlink *readlink = &u->readlink;
418099b002a1SChuck Lever __be32 *p, *maxcount_p, zero = xdr_zero;
4181bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
41821fcea5b2SJ. Bruce Fields int length_offset = xdr->buf->len;
418399b002a1SChuck Lever int maxcount, status;
41841da177e4SLinus Torvalds
418599b002a1SChuck Lever maxcount_p = xdr_reserve_space(xdr, XDR_UNIT);
418699b002a1SChuck Lever if (!maxcount_p)
41872825a7f9SJ. Bruce Fields return nfserr_resource;
41881da177e4SLinus Torvalds maxcount = PAGE_SIZE;
4189d0a381ddSJ. Bruce Fields
4190476a7b1fSJ. Bruce Fields p = xdr_reserve_space(xdr, maxcount);
4191476a7b1fSJ. Bruce Fields if (!p)
41924e21ac4bSJ. Bruce Fields return nfserr_resource;
41931da177e4SLinus Torvalds /*
4194fd4a0edfSMiklos Szeredi * XXX: By default, vfs_readlink() will truncate symlinks if they
4195fd4a0edfSMiklos Szeredi * would overflow the buffer. Is this kosher in NFSv4? If not, one
4196fd4a0edfSMiklos Szeredi * easy fix is: if vfs_readlink() precisely fills the buffer, assume
4197fd4a0edfSMiklos Szeredi * that truncation occurred, and return NFS4ERR_RESOURCE.
41981da177e4SLinus Torvalds */
4199476a7b1fSJ. Bruce Fields nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp,
4200476a7b1fSJ. Bruce Fields (char *)p, &maxcount);
42011da177e4SLinus Torvalds if (nfserr == nfserr_isdir)
4202d3f627c8SJ. Bruce Fields nfserr = nfserr_inval;
420376e5492bSChuck Lever if (nfserr)
420476e5492bSChuck Lever goto out_err;
420576e5492bSChuck Lever status = svc_encode_result_payload(readlink->rl_rqstp, length_offset,
420676e5492bSChuck Lever maxcount);
420776e5492bSChuck Lever if (status) {
420876e5492bSChuck Lever nfserr = nfserrno(status);
420976e5492bSChuck Lever goto out_err;
4210d3f627c8SJ. Bruce Fields }
421199b002a1SChuck Lever *maxcount_p = cpu_to_be32(maxcount);
421299b002a1SChuck Lever xdr_truncate_encode(xdr, length_offset + 4 + xdr_align_size(maxcount));
421399b002a1SChuck Lever write_bytes_to_xdr_buf(xdr->buf, length_offset + 4 + maxcount, &zero,
421499b002a1SChuck Lever xdr_pad_size(maxcount));
421599b002a1SChuck Lever return nfs_ok;
421676e5492bSChuck Lever
421776e5492bSChuck Lever out_err:
421876e5492bSChuck Lever xdr_truncate_encode(xdr, length_offset);
421976e5492bSChuck Lever return nfserr;
42201da177e4SLinus Torvalds }
42211da177e4SLinus Torvalds
4222b37ad28bSAl Viro static __be32
nfsd4_encode_readdir(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4223e78e274eSKees Cook nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr,
4224e78e274eSKees Cook union nfsd4_op_u *u)
42251da177e4SLinus Torvalds {
4226e78e274eSKees Cook struct nfsd4_readdir *readdir = &u->readdir;
42271da177e4SLinus Torvalds int maxcount;
4228561f0ed4SJ. Bruce Fields int bytes_left;
42291da177e4SLinus Torvalds loff_t offset;
4230561f0ed4SJ. Bruce Fields __be64 wire_offset;
4231bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
42321fcea5b2SJ. Bruce Fields int starting_len = xdr->buf->len;
4233bc749ca4SJ. Bruce Fields __be32 *p;
42341da177e4SLinus Torvalds
4235adaa7a50SChuck Lever nfserr = nfsd4_encode_verifier4(xdr, &readdir->rd_verf);
4236adaa7a50SChuck Lever if (nfserr != nfs_ok)
4237adaa7a50SChuck Lever return nfserr;
42381da177e4SLinus Torvalds
42391da177e4SLinus Torvalds /*
4240561f0ed4SJ. Bruce Fields * Number of bytes left for directory entries allowing for the
4241561f0ed4SJ. Bruce Fields * final 8 bytes of the readdir and a following failed op:
42421da177e4SLinus Torvalds */
4243561f0ed4SJ. Bruce Fields bytes_left = xdr->buf->buflen - xdr->buf->len
4244561f0ed4SJ. Bruce Fields - COMPOUND_ERR_SLACK_SPACE - 8;
4245561f0ed4SJ. Bruce Fields if (bytes_left < 0) {
4246561f0ed4SJ. Bruce Fields nfserr = nfserr_resource;
4247561f0ed4SJ. Bruce Fields goto err_no_verf;
4248561f0ed4SJ. Bruce Fields }
42499c2ece6eSScott Mayhew maxcount = svc_max_payload(resp->rqstp);
42509c2ece6eSScott Mayhew maxcount = min_t(u32, readdir->rd_maxcount, maxcount);
4251561f0ed4SJ. Bruce Fields /*
4252561f0ed4SJ. Bruce Fields * Note the rfc defines rd_maxcount as the size of the
4253561f0ed4SJ. Bruce Fields * READDIR4resok structure, which includes the verifier above
4254561f0ed4SJ. Bruce Fields * and the 8 bytes encoded at the end of this function:
4255561f0ed4SJ. Bruce Fields */
4256561f0ed4SJ. Bruce Fields if (maxcount < 16) {
42571da177e4SLinus Torvalds nfserr = nfserr_toosmall;
42581da177e4SLinus Torvalds goto err_no_verf;
42591da177e4SLinus Torvalds }
4260561f0ed4SJ. Bruce Fields maxcount = min_t(int, maxcount-16, bytes_left);
42611da177e4SLinus Torvalds
4262aee37764SJ. Bruce Fields /* RFC 3530 14.2.24 allows us to ignore dircount when it's 0: */
4263aee37764SJ. Bruce Fields if (!readdir->rd_dircount)
42649c2ece6eSScott Mayhew readdir->rd_dircount = svc_max_payload(resp->rqstp);
4265aee37764SJ. Bruce Fields
4266561f0ed4SJ. Bruce Fields readdir->xdr = xdr;
4267561f0ed4SJ. Bruce Fields readdir->rd_maxcount = maxcount;
42681da177e4SLinus Torvalds readdir->common.err = 0;
4269561f0ed4SJ. Bruce Fields readdir->cookie_offset = 0;
42701da177e4SLinus Torvalds
42711da177e4SLinus Torvalds offset = readdir->rd_cookie;
42721da177e4SLinus Torvalds nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp,
42731da177e4SLinus Torvalds &offset,
42741da177e4SLinus Torvalds &readdir->common, nfsd4_encode_dirent);
42751da177e4SLinus Torvalds if (nfserr == nfs_ok &&
42761da177e4SLinus Torvalds readdir->common.err == nfserr_toosmall &&
4277561f0ed4SJ. Bruce Fields xdr->buf->len == starting_len + 8) {
4278561f0ed4SJ. Bruce Fields /* nothing encoded; which limit did we hit?: */
4279561f0ed4SJ. Bruce Fields if (maxcount - 16 < bytes_left)
4280561f0ed4SJ. Bruce Fields /* It was the fault of rd_maxcount: */
42811da177e4SLinus Torvalds nfserr = nfserr_toosmall;
4282561f0ed4SJ. Bruce Fields else
4283561f0ed4SJ. Bruce Fields /* We ran out of buffer space: */
4284561f0ed4SJ. Bruce Fields nfserr = nfserr_resource;
4285561f0ed4SJ. Bruce Fields }
42861da177e4SLinus Torvalds if (nfserr)
42871da177e4SLinus Torvalds goto err_no_verf;
42881da177e4SLinus Torvalds
4289561f0ed4SJ. Bruce Fields if (readdir->cookie_offset) {
4290561f0ed4SJ. Bruce Fields wire_offset = cpu_to_be64(offset);
4291561f0ed4SJ. Bruce Fields write_bytes_to_xdr_buf(xdr->buf, readdir->cookie_offset,
4292561f0ed4SJ. Bruce Fields &wire_offset, 8);
4293561f0ed4SJ. Bruce Fields }
42941da177e4SLinus Torvalds
4295561f0ed4SJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
4296561f0ed4SJ. Bruce Fields if (!p) {
4297561f0ed4SJ. Bruce Fields WARN_ON_ONCE(1);
4298561f0ed4SJ. Bruce Fields goto err_no_verf;
4299561f0ed4SJ. Bruce Fields }
43001da177e4SLinus Torvalds *p++ = 0; /* no more entries */
43011da177e4SLinus Torvalds *p++ = htonl(readdir->common.err == nfserr_eof);
43021da177e4SLinus Torvalds
43031da177e4SLinus Torvalds return 0;
43041da177e4SLinus Torvalds err_no_verf:
43051fcea5b2SJ. Bruce Fields xdr_truncate_encode(xdr, starting_len);
43061da177e4SLinus Torvalds return nfserr;
43071da177e4SLinus Torvalds }
43081da177e4SLinus Torvalds
4309695e12f8SBenny Halevy static __be32
nfsd4_encode_remove(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4310e78e274eSKees Cook nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr,
4311e78e274eSKees Cook union nfsd4_op_u *u)
43121da177e4SLinus Torvalds {
4313e78e274eSKees Cook struct nfsd4_remove *remove = &u->remove;
4314bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
43151da177e4SLinus Torvalds
431666a21db7SChuck Lever return nfsd4_encode_change_info4(xdr, &remove->rm_cinfo);
43171da177e4SLinus Torvalds }
43181da177e4SLinus Torvalds
4319695e12f8SBenny Halevy static __be32
nfsd4_encode_rename(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4320e78e274eSKees Cook nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr,
4321e78e274eSKees Cook union nfsd4_op_u *u)
43221da177e4SLinus Torvalds {
4323e78e274eSKees Cook struct nfsd4_rename *rename = &u->rename;
4324bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
43251da177e4SLinus Torvalds
432666a21db7SChuck Lever nfserr = nfsd4_encode_change_info4(xdr, &rename->rn_sinfo);
432766a21db7SChuck Lever if (nfserr)
432866a21db7SChuck Lever return nfserr;
432966a21db7SChuck Lever return nfsd4_encode_change_info4(xdr, &rename->rn_tinfo);
43301da177e4SLinus Torvalds }
43311da177e4SLinus Torvalds
4332695e12f8SBenny Halevy static __be32
nfsd4_do_encode_secinfo(struct xdr_stream * xdr,struct svc_export * exp)4333bac966d6SJ. Bruce Fields nfsd4_do_encode_secinfo(struct xdr_stream *xdr, struct svc_export *exp)
4334dcb488a3SAndy Adamson {
4335676e4ebdSChuck Lever u32 i, nflavs, supported;
43364796f457SJ. Bruce Fields struct exp_flavor_info *flavs;
43374796f457SJ. Bruce Fields struct exp_flavor_info def_flavs[2];
4338676e4ebdSChuck Lever __be32 *p, *flavorsp;
4339676e4ebdSChuck Lever static bool report = true;
4340dcb488a3SAndy Adamson
43414796f457SJ. Bruce Fields if (exp->ex_nflavors) {
43424796f457SJ. Bruce Fields flavs = exp->ex_flavors;
43434796f457SJ. Bruce Fields nflavs = exp->ex_nflavors;
43444796f457SJ. Bruce Fields } else { /* Handling of some defaults in absence of real secinfo: */
43454796f457SJ. Bruce Fields flavs = def_flavs;
43464796f457SJ. Bruce Fields if (exp->ex_client->flavour->flavour == RPC_AUTH_UNIX) {
43474796f457SJ. Bruce Fields nflavs = 2;
43484796f457SJ. Bruce Fields flavs[0].pseudoflavor = RPC_AUTH_UNIX;
43494796f457SJ. Bruce Fields flavs[1].pseudoflavor = RPC_AUTH_NULL;
43504796f457SJ. Bruce Fields } else if (exp->ex_client->flavour->flavour == RPC_AUTH_GSS) {
43514796f457SJ. Bruce Fields nflavs = 1;
43524796f457SJ. Bruce Fields flavs[0].pseudoflavor
43534796f457SJ. Bruce Fields = svcauth_gss_flavor(exp->ex_client);
43544796f457SJ. Bruce Fields } else {
43554796f457SJ. Bruce Fields nflavs = 1;
43564796f457SJ. Bruce Fields flavs[0].pseudoflavor
43574796f457SJ. Bruce Fields = exp->ex_client->flavour->flavour;
43584796f457SJ. Bruce Fields }
43594796f457SJ. Bruce Fields }
43604796f457SJ. Bruce Fields
4361676e4ebdSChuck Lever supported = 0;
4362d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
4363d0a381ddSJ. Bruce Fields if (!p)
4364bac966d6SJ. Bruce Fields return nfserr_resource;
4365676e4ebdSChuck Lever flavorsp = p++; /* to be backfilled later */
4366676e4ebdSChuck Lever
43674796f457SJ. Bruce Fields for (i = 0; i < nflavs; i++) {
4368676e4ebdSChuck Lever rpc_authflavor_t pf = flavs[i].pseudoflavor;
4369a77c806fSChuck Lever struct rpcsec_gss_info info;
4370dcb488a3SAndy Adamson
4371676e4ebdSChuck Lever if (rpcauth_get_gssinfo(pf, &info) == 0) {
4372676e4ebdSChuck Lever supported++;
4373d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 4 + 4 +
4374d0a381ddSJ. Bruce Fields XDR_LEN(info.oid.len) + 4 + 4);
4375d0a381ddSJ. Bruce Fields if (!p)
4376bac966d6SJ. Bruce Fields return nfserr_resource;
4377c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(RPC_AUTH_GSS);
43780c0c267bSJ. Bruce Fields p = xdr_encode_opaque(p, info.oid.data, info.oid.len);
4379c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(info.qop);
4380c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(info.service);
4381676e4ebdSChuck Lever } else if (pf < RPC_AUTH_MAXFLAVOR) {
4382676e4ebdSChuck Lever supported++;
4383d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
4384d0a381ddSJ. Bruce Fields if (!p)
4385bac966d6SJ. Bruce Fields return nfserr_resource;
4386c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(pf);
4387676e4ebdSChuck Lever } else {
4388676e4ebdSChuck Lever if (report)
4389676e4ebdSChuck Lever pr_warn("NFS: SECINFO: security flavor %u "
4390676e4ebdSChuck Lever "is not supported\n", pf);
4391dcb488a3SAndy Adamson }
4392dcb488a3SAndy Adamson }
4393a77c806fSChuck Lever
4394676e4ebdSChuck Lever if (nflavs != supported)
4395676e4ebdSChuck Lever report = false;
4396676e4ebdSChuck Lever *flavorsp = htonl(supported);
4397bac966d6SJ. Bruce Fields return 0;
4398dcb488a3SAndy Adamson }
4399dcb488a3SAndy Adamson
440022b6dee8SMi Jinlong static __be32
nfsd4_encode_secinfo(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)440122b6dee8SMi Jinlong nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
4402e78e274eSKees Cook union nfsd4_op_u *u)
440322b6dee8SMi Jinlong {
4404e78e274eSKees Cook struct nfsd4_secinfo *secinfo = &u->secinfo;
4405bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
4406d0a381ddSJ. Bruce Fields
4407bac966d6SJ. Bruce Fields return nfsd4_do_encode_secinfo(xdr, secinfo->si_exp);
440822b6dee8SMi Jinlong }
440922b6dee8SMi Jinlong
441022b6dee8SMi Jinlong static __be32
nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)441122b6dee8SMi Jinlong nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr,
4412e78e274eSKees Cook union nfsd4_op_u *u)
441322b6dee8SMi Jinlong {
4414e78e274eSKees Cook struct nfsd4_secinfo_no_name *secinfo = &u->secinfo_no_name;
4415bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
4416d0a381ddSJ. Bruce Fields
4417bac966d6SJ. Bruce Fields return nfsd4_do_encode_secinfo(xdr, secinfo->sin_exp);
441822b6dee8SMi Jinlong }
441922b6dee8SMi Jinlong
44201da177e4SLinus Torvalds /*
44211da177e4SLinus Torvalds * The SETATTR encode routine is special -- it always encodes a bitmap,
44221da177e4SLinus Torvalds * regardless of the error status.
44231da177e4SLinus Torvalds */
4424695e12f8SBenny Halevy static __be32
nfsd4_encode_setattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4425e78e274eSKees Cook nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr,
4426e78e274eSKees Cook union nfsd4_op_u *u)
44271da177e4SLinus Torvalds {
4428e78e274eSKees Cook struct nfsd4_setattr *setattr = &u->setattr;
4429bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
4430bc749ca4SJ. Bruce Fields __be32 *p;
44311da177e4SLinus Torvalds
4432d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 16);
4433d0a381ddSJ. Bruce Fields if (!p)
4434d0a381ddSJ. Bruce Fields return nfserr_resource;
44351da177e4SLinus Torvalds if (nfserr) {
4436c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(3);
4437c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0);
4438c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0);
4439c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0);
44401da177e4SLinus Torvalds }
44411da177e4SLinus Torvalds else {
4442c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(3);
4443c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(setattr->sa_bmval[0]);
4444c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(setattr->sa_bmval[1]);
4445c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(setattr->sa_bmval[2]);
44461da177e4SLinus Torvalds }
4447695e12f8SBenny Halevy return nfserr;
44481da177e4SLinus Torvalds }
44491da177e4SLinus Torvalds
4450695e12f8SBenny Halevy static __be32
nfsd4_encode_setclientid(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4451e78e274eSKees Cook nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr,
4452e78e274eSKees Cook union nfsd4_op_u *u)
44531da177e4SLinus Torvalds {
4454e78e274eSKees Cook struct nfsd4_setclientid *scd = &u->setclientid;
4455bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
44561da177e4SLinus Torvalds
44571da177e4SLinus Torvalds if (!nfserr) {
4458adaa7a50SChuck Lever nfserr = nfsd4_encode_clientid4(xdr, &scd->se_clientid);
4459adaa7a50SChuck Lever if (nfserr != nfs_ok)
4460adaa7a50SChuck Lever goto out;
4461adaa7a50SChuck Lever nfserr = nfsd4_encode_verifier4(xdr, &scd->se_confirm);
4462adaa7a50SChuck Lever } else if (nfserr == nfserr_clid_inuse) {
4463adaa7a50SChuck Lever /* empty network id */
4464adaa7a50SChuck Lever if (xdr_stream_encode_u32(xdr, 0) < 0) {
4465adaa7a50SChuck Lever nfserr = nfserr_resource;
4466adaa7a50SChuck Lever goto out;
44671da177e4SLinus Torvalds }
4468adaa7a50SChuck Lever /* empty universal address */
4469adaa7a50SChuck Lever if (xdr_stream_encode_u32(xdr, 0) < 0) {
4470adaa7a50SChuck Lever nfserr = nfserr_resource;
4471adaa7a50SChuck Lever goto out;
44721da177e4SLinus Torvalds }
4473adaa7a50SChuck Lever }
4474adaa7a50SChuck Lever out:
4475695e12f8SBenny Halevy return nfserr;
44761da177e4SLinus Torvalds }
44771da177e4SLinus Torvalds
4478695e12f8SBenny Halevy static __be32
nfsd4_encode_write(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4479e78e274eSKees Cook nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr,
4480e78e274eSKees Cook union nfsd4_op_u *u)
44811da177e4SLinus Torvalds {
4482e78e274eSKees Cook struct nfsd4_write *write = &u->write;
44831da177e4SLinus Torvalds
4484adaa7a50SChuck Lever if (xdr_stream_encode_u32(resp->xdr, write->wr_bytes_written) < 0)
4485d0a381ddSJ. Bruce Fields return nfserr_resource;
4486adaa7a50SChuck Lever if (xdr_stream_encode_u32(resp->xdr, write->wr_how_written) < 0)
4487adaa7a50SChuck Lever return nfserr_resource;
4488adaa7a50SChuck Lever return nfsd4_encode_verifier4(resp->xdr, &write->wr_verifier);
44891da177e4SLinus Torvalds }
44901da177e4SLinus Torvalds
4491695e12f8SBenny Halevy static __be32
nfsd4_encode_exchange_id(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)449257b7b43bSJ. Bruce Fields nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
4493e78e274eSKees Cook union nfsd4_op_u *u)
44942db134ebSAndy Adamson {
4495e78e274eSKees Cook struct nfsd4_exchange_id *exid = &u->exchange_id;
4496bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
4497bc749ca4SJ. Bruce Fields __be32 *p;
44980733d213SAndy Adamson char *major_id;
44990733d213SAndy Adamson char *server_scope;
45000733d213SAndy Adamson int major_id_sz;
45010733d213SAndy Adamson int server_scope_sz;
45020733d213SAndy Adamson uint64_t minor_id = 0;
45037627d7dcSScott Mayhew struct nfsd_net *nn = net_generic(SVC_NET(resp->rqstp), nfsd_net_id);
45040733d213SAndy Adamson
45057627d7dcSScott Mayhew major_id = nn->nfsd_name;
45067627d7dcSScott Mayhew major_id_sz = strlen(nn->nfsd_name);
45077627d7dcSScott Mayhew server_scope = nn->nfsd_name;
45087627d7dcSScott Mayhew server_scope_sz = strlen(nn->nfsd_name);
45090733d213SAndy Adamson
4510adaa7a50SChuck Lever if (nfsd4_encode_clientid4(xdr, &exid->clientid) != nfs_ok)
4511adaa7a50SChuck Lever return nfserr_resource;
4512adaa7a50SChuck Lever if (xdr_stream_encode_u32(xdr, exid->seqid) < 0)
4513adaa7a50SChuck Lever return nfserr_resource;
4514adaa7a50SChuck Lever if (xdr_stream_encode_u32(xdr, exid->flags) < 0)
4515d0a381ddSJ. Bruce Fields return nfserr_resource;
45160733d213SAndy Adamson
4517adaa7a50SChuck Lever if (xdr_stream_encode_u32(xdr, exid->spa_how) < 0)
4518adaa7a50SChuck Lever return nfserr_resource;
451957266a6eSJ. Bruce Fields switch (exid->spa_how) {
452057266a6eSJ. Bruce Fields case SP4_NONE:
452157266a6eSJ. Bruce Fields break;
452257266a6eSJ. Bruce Fields case SP4_MACH_CRED:
452357266a6eSJ. Bruce Fields /* spo_must_enforce bitmap: */
4524bac966d6SJ. Bruce Fields nfserr = nfsd4_encode_bitmap(xdr,
4525ed941643SAndrew Elble exid->spo_must_enforce[0],
4526ed941643SAndrew Elble exid->spo_must_enforce[1],
4527ed941643SAndrew Elble exid->spo_must_enforce[2]);
4528bac966d6SJ. Bruce Fields if (nfserr)
4529bac966d6SJ. Bruce Fields return nfserr;
4530ed941643SAndrew Elble /* spo_must_allow bitmap: */
4531bac966d6SJ. Bruce Fields nfserr = nfsd4_encode_bitmap(xdr,
4532ed941643SAndrew Elble exid->spo_must_allow[0],
4533ed941643SAndrew Elble exid->spo_must_allow[1],
4534ed941643SAndrew Elble exid->spo_must_allow[2]);
4535bac966d6SJ. Bruce Fields if (nfserr)
4536bac966d6SJ. Bruce Fields return nfserr;
453757266a6eSJ. Bruce Fields break;
453857266a6eSJ. Bruce Fields default:
453957266a6eSJ. Bruce Fields WARN_ON_ONCE(1);
454057266a6eSJ. Bruce Fields }
45410733d213SAndy Adamson
4542d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr,
4543a8bb84bcSKinglong Mee 8 /* so_minor_id */ +
4544a8bb84bcSKinglong Mee 4 /* so_major_id.len */ +
4545a8bb84bcSKinglong Mee (XDR_QUADLEN(major_id_sz) * 4) +
4546a8bb84bcSKinglong Mee 4 /* eir_server_scope.len */ +
4547a8bb84bcSKinglong Mee (XDR_QUADLEN(server_scope_sz) * 4) +
4548a8bb84bcSKinglong Mee 4 /* eir_server_impl_id.count (0) */);
4549d0a381ddSJ. Bruce Fields if (!p)
4550d0a381ddSJ. Bruce Fields return nfserr_resource;
4551a8bb84bcSKinglong Mee
45520733d213SAndy Adamson /* The server_owner struct */
4553b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, minor_id); /* Minor id */
45540733d213SAndy Adamson /* major id */
45550c0c267bSJ. Bruce Fields p = xdr_encode_opaque(p, major_id, major_id_sz);
45560733d213SAndy Adamson
45570733d213SAndy Adamson /* Server scope */
45580c0c267bSJ. Bruce Fields p = xdr_encode_opaque(p, server_scope, server_scope_sz);
45590733d213SAndy Adamson
45600733d213SAndy Adamson /* Implementation id */
4561c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); /* zero length nfs_impl_id4 array */
45620733d213SAndy Adamson return 0;
45632db134ebSAndy Adamson }
45642db134ebSAndy Adamson
45652db134ebSAndy Adamson static __be32
nfsd4_encode_create_session(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)456657b7b43bSJ. Bruce Fields nfsd4_encode_create_session(struct nfsd4_compoundres *resp, __be32 nfserr,
4567e78e274eSKees Cook union nfsd4_op_u *u)
45682db134ebSAndy Adamson {
4569e78e274eSKees Cook struct nfsd4_create_session *sess = &u->create_session;
4570bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
4571bc749ca4SJ. Bruce Fields __be32 *p;
4572ec6b5d7bSAndy Adamson
4573d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 24);
4574d0a381ddSJ. Bruce Fields if (!p)
4575d0a381ddSJ. Bruce Fields return nfserr_resource;
45760c0c267bSJ. Bruce Fields p = xdr_encode_opaque_fixed(p, sess->sessionid.data,
45770c0c267bSJ. Bruce Fields NFS4_MAX_SESSIONID_LEN);
4578c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->seqid);
4579c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->flags);
4580ec6b5d7bSAndy Adamson
4581d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 28);
4582d0a381ddSJ. Bruce Fields if (!p)
4583d0a381ddSJ. Bruce Fields return nfserr_resource;
4584c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); /* headerpadsz */
4585c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->fore_channel.maxreq_sz);
4586c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->fore_channel.maxresp_sz);
4587c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->fore_channel.maxresp_cached);
4588c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->fore_channel.maxops);
4589c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->fore_channel.maxreqs);
4590c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->fore_channel.nr_rdma_attrs);
4591ec6b5d7bSAndy Adamson
4592ec6b5d7bSAndy Adamson if (sess->fore_channel.nr_rdma_attrs) {
4593d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
4594d0a381ddSJ. Bruce Fields if (!p)
4595d0a381ddSJ. Bruce Fields return nfserr_resource;
4596c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->fore_channel.rdma_attrs);
4597ec6b5d7bSAndy Adamson }
4598ec6b5d7bSAndy Adamson
4599d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 28);
4600d0a381ddSJ. Bruce Fields if (!p)
4601d0a381ddSJ. Bruce Fields return nfserr_resource;
4602c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); /* headerpadsz */
4603c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->back_channel.maxreq_sz);
4604c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->back_channel.maxresp_sz);
4605c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->back_channel.maxresp_cached);
4606c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->back_channel.maxops);
4607c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->back_channel.maxreqs);
4608c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->back_channel.nr_rdma_attrs);
4609ec6b5d7bSAndy Adamson
4610ec6b5d7bSAndy Adamson if (sess->back_channel.nr_rdma_attrs) {
4611d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 4);
4612d0a381ddSJ. Bruce Fields if (!p)
4613d0a381ddSJ. Bruce Fields return nfserr_resource;
4614c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->back_channel.rdma_attrs);
4615ec6b5d7bSAndy Adamson }
4616ec6b5d7bSAndy Adamson return 0;
46172db134ebSAndy Adamson }
46182db134ebSAndy Adamson
46192db134ebSAndy Adamson static __be32
nfsd4_encode_sequence(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)462057b7b43bSJ. Bruce Fields nfsd4_encode_sequence(struct nfsd4_compoundres *resp, __be32 nfserr,
4621e78e274eSKees Cook union nfsd4_op_u *u)
46222db134ebSAndy Adamson {
4623e78e274eSKees Cook struct nfsd4_sequence *seq = &u->sequence;
4624bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
4625bc749ca4SJ. Bruce Fields __be32 *p;
4626b85d4c01SBenny Halevy
4627d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 20);
4628d0a381ddSJ. Bruce Fields if (!p)
4629d0a381ddSJ. Bruce Fields return nfserr_resource;
46300c0c267bSJ. Bruce Fields p = xdr_encode_opaque_fixed(p, seq->sessionid.data,
46310c0c267bSJ. Bruce Fields NFS4_MAX_SESSIONID_LEN);
4632c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(seq->seqid);
4633c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(seq->slotid);
4634b7d7ca35SJ. Bruce Fields /* Note slotid's are numbered from zero: */
4635c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(seq->maxslots - 1); /* sr_highest_slotid */
4636c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(seq->maxslots - 1); /* sr_target_highest_slotid */
4637c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(seq->status_flags);
4638b85d4c01SBenny Halevy
4639f5236013SJ. Bruce Fields resp->cstate.data_offset = xdr->buf->len; /* DRC cache data pointer */
4640b85d4c01SBenny Halevy return 0;
46412db134ebSAndy Adamson }
46422db134ebSAndy Adamson
46432355c596SJ. Bruce Fields static __be32
nfsd4_encode_test_stateid(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)464457b7b43bSJ. Bruce Fields nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, __be32 nfserr,
4645e78e274eSKees Cook union nfsd4_op_u *u)
464617456804SBryan Schumaker {
4647e78e274eSKees Cook struct nfsd4_test_stateid *test_stateid = &u->test_stateid;
4648bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
464903cfb420SBryan Schumaker struct nfsd4_test_stateid_id *stateid, *next;
465017456804SBryan Schumaker __be32 *p;
465117456804SBryan Schumaker
4652d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 4 + (4 * test_stateid->ts_num_ids));
4653d0a381ddSJ. Bruce Fields if (!p)
4654d0a381ddSJ. Bruce Fields return nfserr_resource;
465517456804SBryan Schumaker *p++ = htonl(test_stateid->ts_num_ids);
465617456804SBryan Schumaker
465703cfb420SBryan Schumaker list_for_each_entry_safe(stateid, next, &test_stateid->ts_stateid_list, ts_id_list) {
465802f5fde5SAl Viro *p++ = stateid->ts_id_status;
465917456804SBryan Schumaker }
466017456804SBryan Schumaker
4661bac966d6SJ. Bruce Fields return 0;
466217456804SBryan Schumaker }
466317456804SBryan Schumaker
46649cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
46659cf514ccSChristoph Hellwig static __be32
nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)46669cf514ccSChristoph Hellwig nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
4667e78e274eSKees Cook union nfsd4_op_u *u)
46689cf514ccSChristoph Hellwig {
4669e78e274eSKees Cook struct nfsd4_getdeviceinfo *gdev = &u->getdeviceinfo;
4670bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
4671f961e3f2SJ. Bruce Fields const struct nfsd4_layout_ops *ops;
46729cf514ccSChristoph Hellwig u32 starting_len = xdr->buf->len, needed_len;
46739cf514ccSChristoph Hellwig __be32 *p;
46749cf514ccSChristoph Hellwig
46759cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 4);
46769cf514ccSChristoph Hellwig if (!p)
4677bac966d6SJ. Bruce Fields return nfserr_resource;
46789cf514ccSChristoph Hellwig
46799cf514ccSChristoph Hellwig *p++ = cpu_to_be32(gdev->gd_layout_type);
46809cf514ccSChristoph Hellwig
4681f961e3f2SJ. Bruce Fields ops = nfsd4_layout_ops[gdev->gd_layout_type];
46829cf514ccSChristoph Hellwig nfserr = ops->encode_getdeviceinfo(xdr, gdev);
46839cf514ccSChristoph Hellwig if (nfserr) {
46849cf514ccSChristoph Hellwig /*
46859cf514ccSChristoph Hellwig * We don't bother to burden the layout drivers with
46869cf514ccSChristoph Hellwig * enforcing gd_maxcount, just tell the client to
46879cf514ccSChristoph Hellwig * come back with a bigger buffer if it's not enough.
46889cf514ccSChristoph Hellwig */
46899cf514ccSChristoph Hellwig if (xdr->buf->len + 4 > gdev->gd_maxcount)
46909cf514ccSChristoph Hellwig goto toosmall;
4691bac966d6SJ. Bruce Fields return nfserr;
46929cf514ccSChristoph Hellwig }
46939cf514ccSChristoph Hellwig
46949cf514ccSChristoph Hellwig if (gdev->gd_notify_types) {
46959cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 4 + 4);
46969cf514ccSChristoph Hellwig if (!p)
4697bac966d6SJ. Bruce Fields return nfserr_resource;
46989cf514ccSChristoph Hellwig *p++ = cpu_to_be32(1); /* bitmap length */
46999cf514ccSChristoph Hellwig *p++ = cpu_to_be32(gdev->gd_notify_types);
47009cf514ccSChristoph Hellwig } else {
47019cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 4);
47029cf514ccSChristoph Hellwig if (!p)
4703bac966d6SJ. Bruce Fields return nfserr_resource;
47049cf514ccSChristoph Hellwig *p++ = 0;
47059cf514ccSChristoph Hellwig }
47069cf514ccSChristoph Hellwig
4707bac966d6SJ. Bruce Fields return 0;
47089cf514ccSChristoph Hellwig toosmall:
47099cf514ccSChristoph Hellwig dprintk("%s: maxcount too small\n", __func__);
47109cf514ccSChristoph Hellwig needed_len = xdr->buf->len + 4 /* notifications */;
47119cf514ccSChristoph Hellwig xdr_truncate_encode(xdr, starting_len);
47129cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 4);
4713bac966d6SJ. Bruce Fields if (!p)
4714bac966d6SJ. Bruce Fields return nfserr_resource;
47159cf514ccSChristoph Hellwig *p++ = cpu_to_be32(needed_len);
4716bac966d6SJ. Bruce Fields return nfserr_toosmall;
47179cf514ccSChristoph Hellwig }
47189cf514ccSChristoph Hellwig
47199cf514ccSChristoph Hellwig static __be32
nfsd4_encode_layoutget(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)47209cf514ccSChristoph Hellwig nfsd4_encode_layoutget(struct nfsd4_compoundres *resp, __be32 nfserr,
4721e78e274eSKees Cook union nfsd4_op_u *u)
47229cf514ccSChristoph Hellwig {
4723e78e274eSKees Cook struct nfsd4_layoutget *lgp = &u->layoutget;
4724bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
4725f961e3f2SJ. Bruce Fields const struct nfsd4_layout_ops *ops;
47269cf514ccSChristoph Hellwig __be32 *p;
47279cf514ccSChristoph Hellwig
47289cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 36 + sizeof(stateid_opaque_t));
47299cf514ccSChristoph Hellwig if (!p)
4730bac966d6SJ. Bruce Fields return nfserr_resource;
47319cf514ccSChristoph Hellwig
47329cf514ccSChristoph Hellwig *p++ = cpu_to_be32(1); /* we always set return-on-close */
47339cf514ccSChristoph Hellwig *p++ = cpu_to_be32(lgp->lg_sid.si_generation);
47349cf514ccSChristoph Hellwig p = xdr_encode_opaque_fixed(p, &lgp->lg_sid.si_opaque,
47359cf514ccSChristoph Hellwig sizeof(stateid_opaque_t));
47369cf514ccSChristoph Hellwig
47379cf514ccSChristoph Hellwig *p++ = cpu_to_be32(1); /* we always return a single layout */
47389cf514ccSChristoph Hellwig p = xdr_encode_hyper(p, lgp->lg_seg.offset);
47399cf514ccSChristoph Hellwig p = xdr_encode_hyper(p, lgp->lg_seg.length);
47409cf514ccSChristoph Hellwig *p++ = cpu_to_be32(lgp->lg_seg.iomode);
47419cf514ccSChristoph Hellwig *p++ = cpu_to_be32(lgp->lg_layout_type);
47429cf514ccSChristoph Hellwig
4743f961e3f2SJ. Bruce Fields ops = nfsd4_layout_ops[lgp->lg_layout_type];
4744bac966d6SJ. Bruce Fields return ops->encode_layoutget(xdr, lgp);
47459cf514ccSChristoph Hellwig }
47469cf514ccSChristoph Hellwig
47479cf514ccSChristoph Hellwig static __be32
nfsd4_encode_layoutcommit(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)47489cf514ccSChristoph Hellwig nfsd4_encode_layoutcommit(struct nfsd4_compoundres *resp, __be32 nfserr,
4749e78e274eSKees Cook union nfsd4_op_u *u)
47509cf514ccSChristoph Hellwig {
4751e78e274eSKees Cook struct nfsd4_layoutcommit *lcp = &u->layoutcommit;
4752bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
47539cf514ccSChristoph Hellwig __be32 *p;
47549cf514ccSChristoph Hellwig
47559cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 4);
47569cf514ccSChristoph Hellwig if (!p)
47579cf514ccSChristoph Hellwig return nfserr_resource;
47589cf514ccSChristoph Hellwig *p++ = cpu_to_be32(lcp->lc_size_chg);
47599cf514ccSChristoph Hellwig if (lcp->lc_size_chg) {
47609cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 8);
47619cf514ccSChristoph Hellwig if (!p)
47629cf514ccSChristoph Hellwig return nfserr_resource;
47639cf514ccSChristoph Hellwig p = xdr_encode_hyper(p, lcp->lc_newsize);
47649cf514ccSChristoph Hellwig }
47659cf514ccSChristoph Hellwig
4766bac966d6SJ. Bruce Fields return 0;
47679cf514ccSChristoph Hellwig }
47689cf514ccSChristoph Hellwig
47699cf514ccSChristoph Hellwig static __be32
nfsd4_encode_layoutreturn(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)47709cf514ccSChristoph Hellwig nfsd4_encode_layoutreturn(struct nfsd4_compoundres *resp, __be32 nfserr,
4771e78e274eSKees Cook union nfsd4_op_u *u)
47729cf514ccSChristoph Hellwig {
4773e78e274eSKees Cook struct nfsd4_layoutreturn *lrp = &u->layoutreturn;
4774bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
47759cf514ccSChristoph Hellwig __be32 *p;
47769cf514ccSChristoph Hellwig
47779cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 4);
47789cf514ccSChristoph Hellwig if (!p)
47799cf514ccSChristoph Hellwig return nfserr_resource;
47809cf514ccSChristoph Hellwig *p++ = cpu_to_be32(lrp->lrs_present);
47819cf514ccSChristoph Hellwig if (lrp->lrs_present)
4782376675daSKinglong Mee return nfsd4_encode_stateid(xdr, &lrp->lr_sid);
4783bac966d6SJ. Bruce Fields return 0;
47849cf514ccSChristoph Hellwig }
47859cf514ccSChristoph Hellwig #endif /* CONFIG_NFSD_PNFS */
47869cf514ccSChristoph Hellwig
47872db134ebSAndy Adamson static __be32
nfsd42_encode_write_res(struct nfsd4_compoundres * resp,struct nfsd42_write_res * write,bool sync)4788e0639dc5SOlga Kornievskaia nfsd42_encode_write_res(struct nfsd4_compoundres *resp,
4789e0639dc5SOlga Kornievskaia struct nfsd42_write_res *write, bool sync)
479029ae7f9dSAnna Schumaker {
479129ae7f9dSAnna Schumaker __be32 *p;
4792bddfdbcdSChuck Lever p = xdr_reserve_space(resp->xdr, 4);
479329ae7f9dSAnna Schumaker if (!p)
479429ae7f9dSAnna Schumaker return nfserr_resource;
479529ae7f9dSAnna Schumaker
4796e0639dc5SOlga Kornievskaia if (sync)
479729ae7f9dSAnna Schumaker *p++ = cpu_to_be32(0);
4798e0639dc5SOlga Kornievskaia else {
4799e0639dc5SOlga Kornievskaia __be32 nfserr;
4800e0639dc5SOlga Kornievskaia *p++ = cpu_to_be32(1);
4801bddfdbcdSChuck Lever nfserr = nfsd4_encode_stateid(resp->xdr, &write->cb_stateid);
4802e0639dc5SOlga Kornievskaia if (nfserr)
4803e0639dc5SOlga Kornievskaia return nfserr;
4804e0639dc5SOlga Kornievskaia }
4805bddfdbcdSChuck Lever p = xdr_reserve_space(resp->xdr, 8 + 4 + NFS4_VERIFIER_SIZE);
4806e0639dc5SOlga Kornievskaia if (!p)
4807e0639dc5SOlga Kornievskaia return nfserr_resource;
4808e0639dc5SOlga Kornievskaia
480929ae7f9dSAnna Schumaker p = xdr_encode_hyper(p, write->wr_bytes_written);
481029ae7f9dSAnna Schumaker *p++ = cpu_to_be32(write->wr_stable_how);
481129ae7f9dSAnna Schumaker p = xdr_encode_opaque_fixed(p, write->wr_verifier.data,
481229ae7f9dSAnna Schumaker NFS4_VERIFIER_SIZE);
481329ae7f9dSAnna Schumaker return nfs_ok;
481429ae7f9dSAnna Schumaker }
481529ae7f9dSAnna Schumaker
481629ae7f9dSAnna Schumaker static __be32
nfsd42_encode_nl4_server(struct nfsd4_compoundres * resp,struct nl4_server * ns)481751911868SOlga Kornievskaia nfsd42_encode_nl4_server(struct nfsd4_compoundres *resp, struct nl4_server *ns)
481851911868SOlga Kornievskaia {
4819bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
482051911868SOlga Kornievskaia struct nfs42_netaddr *addr;
482151911868SOlga Kornievskaia __be32 *p;
482251911868SOlga Kornievskaia
482351911868SOlga Kornievskaia p = xdr_reserve_space(xdr, 4);
482451911868SOlga Kornievskaia *p++ = cpu_to_be32(ns->nl4_type);
482551911868SOlga Kornievskaia
482651911868SOlga Kornievskaia switch (ns->nl4_type) {
482751911868SOlga Kornievskaia case NL4_NETADDR:
482851911868SOlga Kornievskaia addr = &ns->u.nl4_addr;
482951911868SOlga Kornievskaia
483051911868SOlga Kornievskaia /* netid_len, netid, uaddr_len, uaddr (port included
483151911868SOlga Kornievskaia * in RPCBIND_MAXUADDRLEN)
483251911868SOlga Kornievskaia */
483351911868SOlga Kornievskaia p = xdr_reserve_space(xdr,
483451911868SOlga Kornievskaia 4 /* netid len */ +
483551911868SOlga Kornievskaia (XDR_QUADLEN(addr->netid_len) * 4) +
483651911868SOlga Kornievskaia 4 /* uaddr len */ +
483751911868SOlga Kornievskaia (XDR_QUADLEN(addr->addr_len) * 4));
483851911868SOlga Kornievskaia if (!p)
483951911868SOlga Kornievskaia return nfserr_resource;
484051911868SOlga Kornievskaia
484151911868SOlga Kornievskaia *p++ = cpu_to_be32(addr->netid_len);
484251911868SOlga Kornievskaia p = xdr_encode_opaque_fixed(p, addr->netid,
484351911868SOlga Kornievskaia addr->netid_len);
484451911868SOlga Kornievskaia *p++ = cpu_to_be32(addr->addr_len);
484551911868SOlga Kornievskaia p = xdr_encode_opaque_fixed(p, addr->addr,
484651911868SOlga Kornievskaia addr->addr_len);
484751911868SOlga Kornievskaia break;
484851911868SOlga Kornievskaia default:
484951911868SOlga Kornievskaia WARN_ON_ONCE(ns->nl4_type != NL4_NETADDR);
485051911868SOlga Kornievskaia return nfserr_inval;
485151911868SOlga Kornievskaia }
485251911868SOlga Kornievskaia
485351911868SOlga Kornievskaia return 0;
485451911868SOlga Kornievskaia }
485551911868SOlga Kornievskaia
485651911868SOlga Kornievskaia static __be32
nfsd4_encode_copy(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)485729ae7f9dSAnna Schumaker nfsd4_encode_copy(struct nfsd4_compoundres *resp, __be32 nfserr,
4858e78e274eSKees Cook union nfsd4_op_u *u)
485929ae7f9dSAnna Schumaker {
4860e78e274eSKees Cook struct nfsd4_copy *copy = &u->copy;
486129ae7f9dSAnna Schumaker __be32 *p;
486229ae7f9dSAnna Schumaker
4863e0639dc5SOlga Kornievskaia nfserr = nfsd42_encode_write_res(resp, ©->cp_res,
48641913cdf5SChuck Lever nfsd4_copy_is_sync(copy));
486529ae7f9dSAnna Schumaker if (nfserr)
486629ae7f9dSAnna Schumaker return nfserr;
486729ae7f9dSAnna Schumaker
4868bddfdbcdSChuck Lever p = xdr_reserve_space(resp->xdr, 4 + 4);
4869edcc8452SJ. Bruce Fields *p++ = xdr_one; /* cr_consecutive */
48701913cdf5SChuck Lever *p = nfsd4_copy_is_sync(copy) ? xdr_one : xdr_zero;
4871bac966d6SJ. Bruce Fields return 0;
487229ae7f9dSAnna Schumaker }
487329ae7f9dSAnna Schumaker
487429ae7f9dSAnna Schumaker static __be32
nfsd4_encode_offload_status(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)48756308bc98SOlga Kornievskaia nfsd4_encode_offload_status(struct nfsd4_compoundres *resp, __be32 nfserr,
4876e78e274eSKees Cook union nfsd4_op_u *u)
48776308bc98SOlga Kornievskaia {
4878e78e274eSKees Cook struct nfsd4_offload_status *os = &u->offload_status;
4879bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
48806308bc98SOlga Kornievskaia __be32 *p;
48816308bc98SOlga Kornievskaia
48826308bc98SOlga Kornievskaia p = xdr_reserve_space(xdr, 8 + 4);
48836308bc98SOlga Kornievskaia if (!p)
48846308bc98SOlga Kornievskaia return nfserr_resource;
48856308bc98SOlga Kornievskaia p = xdr_encode_hyper(p, os->count);
48866308bc98SOlga Kornievskaia *p++ = cpu_to_be32(0);
4887528b8493SAnna Schumaker return nfserr;
4888528b8493SAnna Schumaker }
4889528b8493SAnna Schumaker
4890528b8493SAnna Schumaker static __be32
nfsd4_encode_read_plus_data(struct nfsd4_compoundres * resp,struct nfsd4_read * read)4891528b8493SAnna Schumaker nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,
4892eeadcb75SAnna Schumaker struct nfsd4_read *read)
4893528b8493SAnna Schumaker {
4894eeadcb75SAnna Schumaker bool splice_ok = test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags);
4895528b8493SAnna Schumaker struct file *file = read->rd_nf->nf_file;
4896eeadcb75SAnna Schumaker struct xdr_stream *xdr = resp->xdr;
4897eeadcb75SAnna Schumaker unsigned long maxcount;
4898eeadcb75SAnna Schumaker __be32 nfserr, *p;
4899528b8493SAnna Schumaker
4900528b8493SAnna Schumaker /* Content type, offset, byte count */
4901528b8493SAnna Schumaker p = xdr_reserve_space(xdr, 4 + 8 + 4);
4902528b8493SAnna Schumaker if (!p)
4903eeadcb75SAnna Schumaker return nfserr_io;
4904eeadcb75SAnna Schumaker if (resp->xdr->buf->page_len && splice_ok) {
4905eeadcb75SAnna Schumaker WARN_ON_ONCE(splice_ok);
4906eeadcb75SAnna Schumaker return nfserr_serverfault;
4907528b8493SAnna Schumaker }
4908528b8493SAnna Schumaker
4909eeadcb75SAnna Schumaker maxcount = min_t(unsigned long, read->rd_length,
4910eeadcb75SAnna Schumaker (xdr->buf->buflen - xdr->buf->len));
49112db27992SAnna Schumaker
4912eeadcb75SAnna Schumaker if (file->f_op->splice_read && splice_ok)
4913eeadcb75SAnna Schumaker nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount);
4914eeadcb75SAnna Schumaker else
4915eeadcb75SAnna Schumaker nfserr = nfsd4_encode_readv(resp, read, file, maxcount);
4916eeadcb75SAnna Schumaker if (nfserr)
4917eeadcb75SAnna Schumaker return nfserr;
4918278765eaSAnna Schumaker
4919eeadcb75SAnna Schumaker *p++ = cpu_to_be32(NFS4_CONTENT_DATA);
49202db27992SAnna Schumaker p = xdr_encode_hyper(p, read->rd_offset);
4921eeadcb75SAnna Schumaker *p = cpu_to_be32(read->rd_length);
49222db27992SAnna Schumaker
49232db27992SAnna Schumaker return nfs_ok;
49242db27992SAnna Schumaker }
49252db27992SAnna Schumaker
49262db27992SAnna Schumaker static __be32
nfsd4_encode_read_plus(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4927528b8493SAnna Schumaker nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr,
4928e78e274eSKees Cook union nfsd4_op_u *u)
4929528b8493SAnna Schumaker {
4930e78e274eSKees Cook struct nfsd4_read *read = &u->read;
4931eeadcb75SAnna Schumaker struct file *file = read->rd_nf->nf_file;
4932bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
4933528b8493SAnna Schumaker int starting_len = xdr->buf->len;
4934eeadcb75SAnna Schumaker u32 segments = 0;
4935eeadcb75SAnna Schumaker __be32 *p;
4936528b8493SAnna Schumaker
4937528b8493SAnna Schumaker if (nfserr)
4938528b8493SAnna Schumaker return nfserr;
4939528b8493SAnna Schumaker
4940528b8493SAnna Schumaker /* eof flag, segment count */
4941528b8493SAnna Schumaker p = xdr_reserve_space(xdr, 4 + 4);
4942528b8493SAnna Schumaker if (!p)
4943eeadcb75SAnna Schumaker return nfserr_io;
4944528b8493SAnna Schumaker xdr_commit_encode(xdr);
4945528b8493SAnna Schumaker
4946eeadcb75SAnna Schumaker read->rd_eof = read->rd_offset >= i_size_read(file_inode(file));
4947eeadcb75SAnna Schumaker if (read->rd_eof)
49482db27992SAnna Schumaker goto out;
49492db27992SAnna Schumaker
4950eeadcb75SAnna Schumaker nfserr = nfsd4_encode_read_plus_data(resp, read);
4951eeadcb75SAnna Schumaker if (nfserr) {
4952eeadcb75SAnna Schumaker xdr_truncate_encode(xdr, starting_len);
4953eeadcb75SAnna Schumaker return nfserr;
4954528b8493SAnna Schumaker }
4955528b8493SAnna Schumaker
4956eeadcb75SAnna Schumaker segments++;
4957eeadcb75SAnna Schumaker
49582db27992SAnna Schumaker out:
4959eeadcb75SAnna Schumaker p = xdr_encode_bool(p, read->rd_eof);
4960eeadcb75SAnna Schumaker *p = cpu_to_be32(segments);
49616308bc98SOlga Kornievskaia return nfserr;
49626308bc98SOlga Kornievskaia }
49636308bc98SOlga Kornievskaia
49646308bc98SOlga Kornievskaia static __be32
nfsd4_encode_copy_notify(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)496551911868SOlga Kornievskaia nfsd4_encode_copy_notify(struct nfsd4_compoundres *resp, __be32 nfserr,
4966e78e274eSKees Cook union nfsd4_op_u *u)
496751911868SOlga Kornievskaia {
4968e78e274eSKees Cook struct nfsd4_copy_notify *cn = &u->copy_notify;
4969bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
497051911868SOlga Kornievskaia __be32 *p;
497151911868SOlga Kornievskaia
497251911868SOlga Kornievskaia if (nfserr)
497351911868SOlga Kornievskaia return nfserr;
497451911868SOlga Kornievskaia
497551911868SOlga Kornievskaia /* 8 sec, 4 nsec */
497651911868SOlga Kornievskaia p = xdr_reserve_space(xdr, 12);
497751911868SOlga Kornievskaia if (!p)
497851911868SOlga Kornievskaia return nfserr_resource;
497951911868SOlga Kornievskaia
498051911868SOlga Kornievskaia /* cnr_lease_time */
498151911868SOlga Kornievskaia p = xdr_encode_hyper(p, cn->cpn_sec);
498251911868SOlga Kornievskaia *p++ = cpu_to_be32(cn->cpn_nsec);
498351911868SOlga Kornievskaia
498451911868SOlga Kornievskaia /* cnr_stateid */
498551911868SOlga Kornievskaia nfserr = nfsd4_encode_stateid(xdr, &cn->cpn_cnr_stateid);
498651911868SOlga Kornievskaia if (nfserr)
498751911868SOlga Kornievskaia return nfserr;
498851911868SOlga Kornievskaia
498951911868SOlga Kornievskaia /* cnr_src.nl_nsvr */
499051911868SOlga Kornievskaia p = xdr_reserve_space(xdr, 4);
499151911868SOlga Kornievskaia if (!p)
499251911868SOlga Kornievskaia return nfserr_resource;
499351911868SOlga Kornievskaia
499451911868SOlga Kornievskaia *p++ = cpu_to_be32(1);
499551911868SOlga Kornievskaia
499609426ef2SChuck Lever nfserr = nfsd42_encode_nl4_server(resp, cn->cpn_src);
499709426ef2SChuck Lever return nfserr;
499851911868SOlga Kornievskaia }
499951911868SOlga Kornievskaia
500051911868SOlga Kornievskaia static __be32
nfsd4_encode_seek(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)500124bab491SAnna Schumaker nfsd4_encode_seek(struct nfsd4_compoundres *resp, __be32 nfserr,
5002e78e274eSKees Cook union nfsd4_op_u *u)
500324bab491SAnna Schumaker {
5004e78e274eSKees Cook struct nfsd4_seek *seek = &u->seek;
500524bab491SAnna Schumaker __be32 *p;
500624bab491SAnna Schumaker
5007bddfdbcdSChuck Lever p = xdr_reserve_space(resp->xdr, 4 + 8);
500824bab491SAnna Schumaker *p++ = cpu_to_be32(seek->seek_eof);
500924bab491SAnna Schumaker p = xdr_encode_hyper(p, seek->seek_pos);
501024bab491SAnna Schumaker
5011bac966d6SJ. Bruce Fields return 0;
501224bab491SAnna Schumaker }
501324bab491SAnna Schumaker
501424bab491SAnna Schumaker static __be32
nfsd4_encode_noop(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * p)5015e78e274eSKees Cook nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr,
5016e78e274eSKees Cook union nfsd4_op_u *p)
5017695e12f8SBenny Halevy {
5018695e12f8SBenny Halevy return nfserr;
5019695e12f8SBenny Halevy }
5020695e12f8SBenny Halevy
502123e50fe3SFrank van der Linden /*
502223e50fe3SFrank van der Linden * Encode kmalloc-ed buffer in to XDR stream.
502323e50fe3SFrank van der Linden */
5024b9a49237SChuck Lever static __be32
nfsd4_vbuf_to_stream(struct xdr_stream * xdr,char * buf,u32 buflen)502523e50fe3SFrank van der Linden nfsd4_vbuf_to_stream(struct xdr_stream *xdr, char *buf, u32 buflen)
502623e50fe3SFrank van der Linden {
502723e50fe3SFrank van der Linden u32 cplen;
502823e50fe3SFrank van der Linden __be32 *p;
502923e50fe3SFrank van der Linden
503023e50fe3SFrank van der Linden cplen = min_t(unsigned long, buflen,
503123e50fe3SFrank van der Linden ((void *)xdr->end - (void *)xdr->p));
503223e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, cplen);
503323e50fe3SFrank van der Linden if (!p)
503423e50fe3SFrank van der Linden return nfserr_resource;
503523e50fe3SFrank van der Linden
503623e50fe3SFrank van der Linden memcpy(p, buf, cplen);
503723e50fe3SFrank van der Linden buf += cplen;
503823e50fe3SFrank van der Linden buflen -= cplen;
503923e50fe3SFrank van der Linden
504023e50fe3SFrank van der Linden while (buflen) {
504123e50fe3SFrank van der Linden cplen = min_t(u32, buflen, PAGE_SIZE);
504223e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, cplen);
504323e50fe3SFrank van der Linden if (!p)
504423e50fe3SFrank van der Linden return nfserr_resource;
504523e50fe3SFrank van der Linden
504623e50fe3SFrank van der Linden memcpy(p, buf, cplen);
504723e50fe3SFrank van der Linden
504823e50fe3SFrank van der Linden if (cplen < PAGE_SIZE) {
504923e50fe3SFrank van der Linden /*
505023e50fe3SFrank van der Linden * We're done, with a length that wasn't page
505123e50fe3SFrank van der Linden * aligned, so possibly not word aligned. Pad
505223e50fe3SFrank van der Linden * any trailing bytes with 0.
505323e50fe3SFrank van der Linden */
505423e50fe3SFrank van der Linden xdr_encode_opaque_fixed(p, NULL, cplen);
505523e50fe3SFrank van der Linden break;
505623e50fe3SFrank van der Linden }
505723e50fe3SFrank van der Linden
505823e50fe3SFrank van der Linden buflen -= PAGE_SIZE;
505923e50fe3SFrank van der Linden buf += PAGE_SIZE;
506023e50fe3SFrank van der Linden }
506123e50fe3SFrank van der Linden
506223e50fe3SFrank van der Linden return 0;
506323e50fe3SFrank van der Linden }
506423e50fe3SFrank van der Linden
506523e50fe3SFrank van der Linden static __be32
nfsd4_encode_getxattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)506623e50fe3SFrank van der Linden nfsd4_encode_getxattr(struct nfsd4_compoundres *resp, __be32 nfserr,
5067e78e274eSKees Cook union nfsd4_op_u *u)
506823e50fe3SFrank van der Linden {
5069e78e274eSKees Cook struct nfsd4_getxattr *getxattr = &u->getxattr;
5070bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
507123e50fe3SFrank van der Linden __be32 *p, err;
507223e50fe3SFrank van der Linden
507323e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, 4);
507423e50fe3SFrank van der Linden if (!p)
507523e50fe3SFrank van der Linden return nfserr_resource;
507623e50fe3SFrank van der Linden
507723e50fe3SFrank van der Linden *p = cpu_to_be32(getxattr->getxa_len);
507823e50fe3SFrank van der Linden
507923e50fe3SFrank van der Linden if (getxattr->getxa_len == 0)
508023e50fe3SFrank van der Linden return 0;
508123e50fe3SFrank van der Linden
508223e50fe3SFrank van der Linden err = nfsd4_vbuf_to_stream(xdr, getxattr->getxa_buf,
508323e50fe3SFrank van der Linden getxattr->getxa_len);
508423e50fe3SFrank van der Linden
508523e50fe3SFrank van der Linden kvfree(getxattr->getxa_buf);
508623e50fe3SFrank van der Linden
508723e50fe3SFrank van der Linden return err;
508823e50fe3SFrank van der Linden }
508923e50fe3SFrank van der Linden
509023e50fe3SFrank van der Linden static __be32
nfsd4_encode_setxattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)509123e50fe3SFrank van der Linden nfsd4_encode_setxattr(struct nfsd4_compoundres *resp, __be32 nfserr,
5092e78e274eSKees Cook union nfsd4_op_u *u)
509323e50fe3SFrank van der Linden {
5094e78e274eSKees Cook struct nfsd4_setxattr *setxattr = &u->setxattr;
5095bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
509623e50fe3SFrank van der Linden
509766a21db7SChuck Lever return nfsd4_encode_change_info4(xdr, &setxattr->setxa_cinfo);
509823e50fe3SFrank van der Linden }
509923e50fe3SFrank van der Linden
510023e50fe3SFrank van der Linden /*
510123e50fe3SFrank van der Linden * See if there are cookie values that can be rejected outright.
510223e50fe3SFrank van der Linden */
510323e50fe3SFrank van der Linden static __be32
nfsd4_listxattr_validate_cookie(struct nfsd4_listxattrs * listxattrs,u32 * offsetp)510423e50fe3SFrank van der Linden nfsd4_listxattr_validate_cookie(struct nfsd4_listxattrs *listxattrs,
510523e50fe3SFrank van der Linden u32 *offsetp)
510623e50fe3SFrank van der Linden {
510723e50fe3SFrank van der Linden u64 cookie = listxattrs->lsxa_cookie;
510823e50fe3SFrank van der Linden
510923e50fe3SFrank van der Linden /*
511023e50fe3SFrank van der Linden * If the cookie is larger than the maximum number we can fit
511123e50fe3SFrank van der Linden * in either the buffer we just got back from vfs_listxattr, or,
511223e50fe3SFrank van der Linden * XDR-encoded, in the return buffer, it's invalid.
511323e50fe3SFrank van der Linden */
511423e50fe3SFrank van der Linden if (cookie > (listxattrs->lsxa_len) / (XATTR_USER_PREFIX_LEN + 2))
511523e50fe3SFrank van der Linden return nfserr_badcookie;
511623e50fe3SFrank van der Linden
511723e50fe3SFrank van der Linden if (cookie > (listxattrs->lsxa_maxcount /
511823e50fe3SFrank van der Linden (XDR_QUADLEN(XATTR_USER_PREFIX_LEN + 2) + 4)))
511923e50fe3SFrank van der Linden return nfserr_badcookie;
512023e50fe3SFrank van der Linden
512123e50fe3SFrank van der Linden *offsetp = (u32)cookie;
512223e50fe3SFrank van der Linden return 0;
512323e50fe3SFrank van der Linden }
512423e50fe3SFrank van der Linden
512523e50fe3SFrank van der Linden static __be32
nfsd4_encode_listxattrs(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)512623e50fe3SFrank van der Linden nfsd4_encode_listxattrs(struct nfsd4_compoundres *resp, __be32 nfserr,
5127e78e274eSKees Cook union nfsd4_op_u *u)
512823e50fe3SFrank van der Linden {
5129e78e274eSKees Cook struct nfsd4_listxattrs *listxattrs = &u->listxattrs;
5130bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
513123e50fe3SFrank van der Linden u32 cookie_offset, count_offset, eof;
513223e50fe3SFrank van der Linden u32 left, xdrleft, slen, count;
513323e50fe3SFrank van der Linden u32 xdrlen, offset;
513423e50fe3SFrank van der Linden u64 cookie;
513523e50fe3SFrank van der Linden char *sp;
5136b9a49237SChuck Lever __be32 status, tmp;
513723e50fe3SFrank van der Linden __be32 *p;
513823e50fe3SFrank van der Linden u32 nuser;
513923e50fe3SFrank van der Linden
514023e50fe3SFrank van der Linden eof = 1;
514123e50fe3SFrank van der Linden
514223e50fe3SFrank van der Linden status = nfsd4_listxattr_validate_cookie(listxattrs, &offset);
514323e50fe3SFrank van der Linden if (status)
514423e50fe3SFrank van der Linden goto out;
514523e50fe3SFrank van der Linden
514623e50fe3SFrank van der Linden /*
514723e50fe3SFrank van der Linden * Reserve space for the cookie and the name array count. Record
514823e50fe3SFrank van der Linden * the offsets to save them later.
514923e50fe3SFrank van der Linden */
515023e50fe3SFrank van der Linden cookie_offset = xdr->buf->len;
515123e50fe3SFrank van der Linden count_offset = cookie_offset + 8;
515223e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, 12);
515323e50fe3SFrank van der Linden if (!p) {
515423e50fe3SFrank van der Linden status = nfserr_resource;
515523e50fe3SFrank van der Linden goto out;
515623e50fe3SFrank van der Linden }
515723e50fe3SFrank van der Linden
515823e50fe3SFrank van der Linden count = 0;
515923e50fe3SFrank van der Linden left = listxattrs->lsxa_len;
516023e50fe3SFrank van der Linden sp = listxattrs->lsxa_buf;
516123e50fe3SFrank van der Linden nuser = 0;
516223e50fe3SFrank van der Linden
516323e50fe3SFrank van der Linden xdrleft = listxattrs->lsxa_maxcount;
516423e50fe3SFrank van der Linden
516523e50fe3SFrank van der Linden while (left > 0 && xdrleft > 0) {
516623e50fe3SFrank van der Linden slen = strlen(sp);
516723e50fe3SFrank van der Linden
516823e50fe3SFrank van der Linden /*
51694cce11faSAlex Dewar * Check if this is a "user." attribute, skip it if not.
517023e50fe3SFrank van der Linden */
517123e50fe3SFrank van der Linden if (strncmp(sp, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
517223e50fe3SFrank van der Linden goto contloop;
517323e50fe3SFrank van der Linden
517423e50fe3SFrank van der Linden slen -= XATTR_USER_PREFIX_LEN;
517523e50fe3SFrank van der Linden xdrlen = 4 + ((slen + 3) & ~3);
517623e50fe3SFrank van der Linden if (xdrlen > xdrleft) {
517723e50fe3SFrank van der Linden if (count == 0) {
517823e50fe3SFrank van der Linden /*
517923e50fe3SFrank van der Linden * Can't even fit the first attribute name.
518023e50fe3SFrank van der Linden */
518123e50fe3SFrank van der Linden status = nfserr_toosmall;
518223e50fe3SFrank van der Linden goto out;
518323e50fe3SFrank van der Linden }
518423e50fe3SFrank van der Linden eof = 0;
518523e50fe3SFrank van der Linden goto wreof;
518623e50fe3SFrank van der Linden }
518723e50fe3SFrank van der Linden
518823e50fe3SFrank van der Linden left -= XATTR_USER_PREFIX_LEN;
518923e50fe3SFrank van der Linden sp += XATTR_USER_PREFIX_LEN;
519023e50fe3SFrank van der Linden if (nuser++ < offset)
519123e50fe3SFrank van der Linden goto contloop;
519223e50fe3SFrank van der Linden
519323e50fe3SFrank van der Linden
519423e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, xdrlen);
519523e50fe3SFrank van der Linden if (!p) {
519623e50fe3SFrank van der Linden status = nfserr_resource;
519723e50fe3SFrank van der Linden goto out;
519823e50fe3SFrank van der Linden }
519923e50fe3SFrank van der Linden
5200e2a1840eSAlex Dewar xdr_encode_opaque(p, sp, slen);
520123e50fe3SFrank van der Linden
520223e50fe3SFrank van der Linden xdrleft -= xdrlen;
520323e50fe3SFrank van der Linden count++;
520423e50fe3SFrank van der Linden contloop:
520523e50fe3SFrank van der Linden sp += slen + 1;
520623e50fe3SFrank van der Linden left -= slen + 1;
520723e50fe3SFrank van der Linden }
520823e50fe3SFrank van der Linden
520923e50fe3SFrank van der Linden /*
521023e50fe3SFrank van der Linden * If there were user attributes to copy, but we didn't copy
521123e50fe3SFrank van der Linden * any, the offset was too large (e.g. the cookie was invalid).
521223e50fe3SFrank van der Linden */
521323e50fe3SFrank van der Linden if (nuser > 0 && count == 0) {
521423e50fe3SFrank van der Linden status = nfserr_badcookie;
521523e50fe3SFrank van der Linden goto out;
521623e50fe3SFrank van der Linden }
521723e50fe3SFrank van der Linden
521823e50fe3SFrank van der Linden wreof:
521923e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, 4);
522023e50fe3SFrank van der Linden if (!p) {
522123e50fe3SFrank van der Linden status = nfserr_resource;
522223e50fe3SFrank van der Linden goto out;
522323e50fe3SFrank van der Linden }
522423e50fe3SFrank van der Linden *p = cpu_to_be32(eof);
522523e50fe3SFrank van der Linden
522623e50fe3SFrank van der Linden cookie = offset + count;
522723e50fe3SFrank van der Linden
522823e50fe3SFrank van der Linden write_bytes_to_xdr_buf(xdr->buf, cookie_offset, &cookie, 8);
5229b9a49237SChuck Lever tmp = cpu_to_be32(count);
5230b9a49237SChuck Lever write_bytes_to_xdr_buf(xdr->buf, count_offset, &tmp, 4);
523123e50fe3SFrank van der Linden out:
523223e50fe3SFrank van der Linden if (listxattrs->lsxa_len)
523323e50fe3SFrank van der Linden kvfree(listxattrs->lsxa_buf);
523423e50fe3SFrank van der Linden return status;
523523e50fe3SFrank van der Linden }
523623e50fe3SFrank van der Linden
523723e50fe3SFrank van der Linden static __be32
nfsd4_encode_removexattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)523823e50fe3SFrank van der Linden nfsd4_encode_removexattr(struct nfsd4_compoundres *resp, __be32 nfserr,
5239e78e274eSKees Cook union nfsd4_op_u *u)
524023e50fe3SFrank van der Linden {
5241e78e274eSKees Cook struct nfsd4_removexattr *removexattr = &u->removexattr;
5242bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
524323e50fe3SFrank van der Linden
524466a21db7SChuck Lever return nfsd4_encode_change_info4(xdr, &removexattr->rmxa_cinfo);
524523e50fe3SFrank van der Linden }
524623e50fe3SFrank van der Linden
5247e78e274eSKees Cook typedef __be32(*nfsd4_enc)(struct nfsd4_compoundres *, __be32, union nfsd4_op_u *u);
5248695e12f8SBenny Halevy
52492db134ebSAndy Adamson /*
52502db134ebSAndy Adamson * Note: nfsd4_enc_ops vector is shared for v4.0 and v4.1
52512db134ebSAndy Adamson * since we don't need to filter out obsolete ops as this is
52522db134ebSAndy Adamson * done in the decoding phase.
52532db134ebSAndy Adamson */
5254c1df609dSChuck Lever static const nfsd4_enc nfsd4_enc_ops[] = {
5255e78e274eSKees Cook [OP_ACCESS] = nfsd4_encode_access,
5256e78e274eSKees Cook [OP_CLOSE] = nfsd4_encode_close,
5257e78e274eSKees Cook [OP_COMMIT] = nfsd4_encode_commit,
5258e78e274eSKees Cook [OP_CREATE] = nfsd4_encode_create,
5259e78e274eSKees Cook [OP_DELEGPURGE] = nfsd4_encode_noop,
5260e78e274eSKees Cook [OP_DELEGRETURN] = nfsd4_encode_noop,
5261e78e274eSKees Cook [OP_GETATTR] = nfsd4_encode_getattr,
5262e78e274eSKees Cook [OP_GETFH] = nfsd4_encode_getfh,
5263e78e274eSKees Cook [OP_LINK] = nfsd4_encode_link,
5264e78e274eSKees Cook [OP_LOCK] = nfsd4_encode_lock,
5265e78e274eSKees Cook [OP_LOCKT] = nfsd4_encode_lockt,
5266e78e274eSKees Cook [OP_LOCKU] = nfsd4_encode_locku,
5267e78e274eSKees Cook [OP_LOOKUP] = nfsd4_encode_noop,
5268e78e274eSKees Cook [OP_LOOKUPP] = nfsd4_encode_noop,
5269e78e274eSKees Cook [OP_NVERIFY] = nfsd4_encode_noop,
5270e78e274eSKees Cook [OP_OPEN] = nfsd4_encode_open,
5271e78e274eSKees Cook [OP_OPENATTR] = nfsd4_encode_noop,
5272e78e274eSKees Cook [OP_OPEN_CONFIRM] = nfsd4_encode_open_confirm,
5273e78e274eSKees Cook [OP_OPEN_DOWNGRADE] = nfsd4_encode_open_downgrade,
5274e78e274eSKees Cook [OP_PUTFH] = nfsd4_encode_noop,
5275e78e274eSKees Cook [OP_PUTPUBFH] = nfsd4_encode_noop,
5276e78e274eSKees Cook [OP_PUTROOTFH] = nfsd4_encode_noop,
5277e78e274eSKees Cook [OP_READ] = nfsd4_encode_read,
5278e78e274eSKees Cook [OP_READDIR] = nfsd4_encode_readdir,
5279e78e274eSKees Cook [OP_READLINK] = nfsd4_encode_readlink,
5280e78e274eSKees Cook [OP_REMOVE] = nfsd4_encode_remove,
5281e78e274eSKees Cook [OP_RENAME] = nfsd4_encode_rename,
5282e78e274eSKees Cook [OP_RENEW] = nfsd4_encode_noop,
5283e78e274eSKees Cook [OP_RESTOREFH] = nfsd4_encode_noop,
5284e78e274eSKees Cook [OP_SAVEFH] = nfsd4_encode_noop,
5285e78e274eSKees Cook [OP_SECINFO] = nfsd4_encode_secinfo,
5286e78e274eSKees Cook [OP_SETATTR] = nfsd4_encode_setattr,
5287e78e274eSKees Cook [OP_SETCLIENTID] = nfsd4_encode_setclientid,
5288e78e274eSKees Cook [OP_SETCLIENTID_CONFIRM] = nfsd4_encode_noop,
5289e78e274eSKees Cook [OP_VERIFY] = nfsd4_encode_noop,
5290e78e274eSKees Cook [OP_WRITE] = nfsd4_encode_write,
5291e78e274eSKees Cook [OP_RELEASE_LOCKOWNER] = nfsd4_encode_noop,
52922db134ebSAndy Adamson
52932db134ebSAndy Adamson /* NFSv4.1 operations */
5294e78e274eSKees Cook [OP_BACKCHANNEL_CTL] = nfsd4_encode_noop,
5295e78e274eSKees Cook [OP_BIND_CONN_TO_SESSION] = nfsd4_encode_bind_conn_to_session,
5296e78e274eSKees Cook [OP_EXCHANGE_ID] = nfsd4_encode_exchange_id,
5297e78e274eSKees Cook [OP_CREATE_SESSION] = nfsd4_encode_create_session,
5298e78e274eSKees Cook [OP_DESTROY_SESSION] = nfsd4_encode_noop,
5299e78e274eSKees Cook [OP_FREE_STATEID] = nfsd4_encode_noop,
5300e78e274eSKees Cook [OP_GET_DIR_DELEGATION] = nfsd4_encode_noop,
53019cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
5302e78e274eSKees Cook [OP_GETDEVICEINFO] = nfsd4_encode_getdeviceinfo,
5303e78e274eSKees Cook [OP_GETDEVICELIST] = nfsd4_encode_noop,
5304e78e274eSKees Cook [OP_LAYOUTCOMMIT] = nfsd4_encode_layoutcommit,
5305e78e274eSKees Cook [OP_LAYOUTGET] = nfsd4_encode_layoutget,
5306e78e274eSKees Cook [OP_LAYOUTRETURN] = nfsd4_encode_layoutreturn,
53079cf514ccSChristoph Hellwig #else
5308e78e274eSKees Cook [OP_GETDEVICEINFO] = nfsd4_encode_noop,
5309e78e274eSKees Cook [OP_GETDEVICELIST] = nfsd4_encode_noop,
5310e78e274eSKees Cook [OP_LAYOUTCOMMIT] = nfsd4_encode_noop,
5311e78e274eSKees Cook [OP_LAYOUTGET] = nfsd4_encode_noop,
5312e78e274eSKees Cook [OP_LAYOUTRETURN] = nfsd4_encode_noop,
53139cf514ccSChristoph Hellwig #endif
5314e78e274eSKees Cook [OP_SECINFO_NO_NAME] = nfsd4_encode_secinfo_no_name,
5315e78e274eSKees Cook [OP_SEQUENCE] = nfsd4_encode_sequence,
5316e78e274eSKees Cook [OP_SET_SSV] = nfsd4_encode_noop,
5317e78e274eSKees Cook [OP_TEST_STATEID] = nfsd4_encode_test_stateid,
5318e78e274eSKees Cook [OP_WANT_DELEGATION] = nfsd4_encode_noop,
5319e78e274eSKees Cook [OP_DESTROY_CLIENTID] = nfsd4_encode_noop,
5320e78e274eSKees Cook [OP_RECLAIM_COMPLETE] = nfsd4_encode_noop,
532187a15a80SAnna Schumaker
532287a15a80SAnna Schumaker /* NFSv4.2 operations */
5323e78e274eSKees Cook [OP_ALLOCATE] = nfsd4_encode_noop,
5324e78e274eSKees Cook [OP_COPY] = nfsd4_encode_copy,
5325e78e274eSKees Cook [OP_COPY_NOTIFY] = nfsd4_encode_copy_notify,
5326e78e274eSKees Cook [OP_DEALLOCATE] = nfsd4_encode_noop,
5327e78e274eSKees Cook [OP_IO_ADVISE] = nfsd4_encode_noop,
5328e78e274eSKees Cook [OP_LAYOUTERROR] = nfsd4_encode_noop,
5329e78e274eSKees Cook [OP_LAYOUTSTATS] = nfsd4_encode_noop,
5330e78e274eSKees Cook [OP_OFFLOAD_CANCEL] = nfsd4_encode_noop,
5331e78e274eSKees Cook [OP_OFFLOAD_STATUS] = nfsd4_encode_offload_status,
5332e78e274eSKees Cook [OP_READ_PLUS] = nfsd4_encode_read_plus,
5333e78e274eSKees Cook [OP_SEEK] = nfsd4_encode_seek,
5334e78e274eSKees Cook [OP_WRITE_SAME] = nfsd4_encode_noop,
5335e78e274eSKees Cook [OP_CLONE] = nfsd4_encode_noop,
533623e50fe3SFrank van der Linden
533723e50fe3SFrank van der Linden /* RFC 8276 extended atributes operations */
5338e78e274eSKees Cook [OP_GETXATTR] = nfsd4_encode_getxattr,
5339e78e274eSKees Cook [OP_SETXATTR] = nfsd4_encode_setxattr,
5340e78e274eSKees Cook [OP_LISTXATTRS] = nfsd4_encode_listxattrs,
5341e78e274eSKees Cook [OP_REMOVEXATTR] = nfsd4_encode_removexattr,
5342695e12f8SBenny Halevy };
5343695e12f8SBenny Halevy
5344496c262cSAndy Adamson /*
5345a8095f7eSJ. Bruce Fields * Calculate whether we still have space to encode repsize bytes.
5346a8095f7eSJ. Bruce Fields * There are two considerations:
5347a8095f7eSJ. Bruce Fields * - For NFS versions >=4.1, the size of the reply must stay within
5348a8095f7eSJ. Bruce Fields * session limits
5349a8095f7eSJ. Bruce Fields * - For all NFS versions, we must stay within limited preallocated
5350a8095f7eSJ. Bruce Fields * buffer space.
5351496c262cSAndy Adamson *
5352a8095f7eSJ. Bruce Fields * This is called before the operation is processed, so can only provide
5353a8095f7eSJ. Bruce Fields * an upper estimate. For some nonidempotent operations (such as
5354a8095f7eSJ. Bruce Fields * getattr), it's not necessarily a problem if that estimate is wrong,
5355a8095f7eSJ. Bruce Fields * as we can fail it after processing without significant side effects.
5356496c262cSAndy Adamson */
nfsd4_check_resp_size(struct nfsd4_compoundres * resp,u32 respsize)5357a8095f7eSJ. Bruce Fields __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 respsize)
5358496c262cSAndy Adamson {
535967492c99SJ. Bruce Fields struct xdr_buf *buf = &resp->rqstp->rq_res;
5360a8095f7eSJ. Bruce Fields struct nfsd4_slot *slot = resp->cstate.slot;
5361496c262cSAndy Adamson
536247ee5298SJ. Bruce Fields if (buf->len + respsize <= buf->buflen)
536347ee5298SJ. Bruce Fields return nfs_ok;
536447ee5298SJ. Bruce Fields if (!nfsd4_has_session(&resp->cstate))
536547ee5298SJ. Bruce Fields return nfserr_resource;
536647ee5298SJ. Bruce Fields if (slot->sl_flags & NFSD4_SLOT_CACHETHIS) {
536747ee5298SJ. Bruce Fields WARN_ON_ONCE(1);
5368496c262cSAndy Adamson return nfserr_rep_too_big_to_cache;
5369ea8d7720SJ. Bruce Fields }
537047ee5298SJ. Bruce Fields return nfserr_rep_too_big;
5371496c262cSAndy Adamson }
5372496c262cSAndy Adamson
53731da177e4SLinus Torvalds void
nfsd4_encode_operation(struct nfsd4_compoundres * resp,struct nfsd4_op * op)53741da177e4SLinus Torvalds nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
53751da177e4SLinus Torvalds {
5376bddfdbcdSChuck Lever struct xdr_stream *xdr = resp->xdr;
53779411b1d4SJ. Bruce Fields struct nfs4_stateowner *so = resp->cstate.replay_owner;
53785f4ab945SJ. Bruce Fields struct svc_rqst *rqstp = resp->rqstp;
537934b1744cSJ. Bruce Fields const struct nfsd4_operation *opdesc = op->opdesc;
5380082d4bd7SJ. Bruce Fields int post_err_offset;
538107d1f802SJ. Bruce Fields nfsd4_enc encoder;
5382bc749ca4SJ. Bruce Fields __be32 *p;
53831da177e4SLinus Torvalds
5384d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 8);
538515a8b55dSJeff Layton if (!p)
538615a8b55dSJeff Layton goto release;
5387c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(op->opnum);
5388082d4bd7SJ. Bruce Fields post_err_offset = xdr->buf->len;
53891da177e4SLinus Torvalds
5390695e12f8SBenny Halevy if (op->opnum == OP_ILLEGAL)
5391695e12f8SBenny Halevy goto status;
5392b7571e4cSJ. Bruce Fields if (op->status && opdesc &&
5393b7571e4cSJ. Bruce Fields !(opdesc->op_flags & OP_NONTRIVIAL_ERROR_ENCODE))
5394b7571e4cSJ. Bruce Fields goto status;
53953a237b4aSChuck Lever BUG_ON(op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) ||
5396695e12f8SBenny Halevy !nfsd4_enc_ops[op->opnum]);
539707d1f802SJ. Bruce Fields encoder = nfsd4_enc_ops[op->opnum];
539807d1f802SJ. Bruce Fields op->status = encoder(resp, op->status, &op->u);
539908281341SChuck Lever if (op->status)
540008281341SChuck Lever trace_nfsd_compound_encode_err(rqstp, op->opnum, op->status);
54012825a7f9SJ. Bruce Fields xdr_commit_encode(xdr);
54022825a7f9SJ. Bruce Fields
5403067e1aceSJ. Bruce Fields /* nfsd4_check_resp_size guarantees enough room for error status */
54045f4ab945SJ. Bruce Fields if (!op->status) {
54055f4ab945SJ. Bruce Fields int space_needed = 0;
54065f4ab945SJ. Bruce Fields if (!nfsd4_last_compound_op(rqstp))
54075f4ab945SJ. Bruce Fields space_needed = COMPOUND_ERR_SLACK_SPACE;
54085f4ab945SJ. Bruce Fields op->status = nfsd4_check_resp_size(resp, space_needed);
54095f4ab945SJ. Bruce Fields }
5410c8f13d97SJ. Bruce Fields if (op->status == nfserr_resource && nfsd4_has_session(&resp->cstate)) {
5411c8f13d97SJ. Bruce Fields struct nfsd4_slot *slot = resp->cstate.slot;
5412c8f13d97SJ. Bruce Fields
5413c8f13d97SJ. Bruce Fields if (slot->sl_flags & NFSD4_SLOT_CACHETHIS)
5414c8f13d97SJ. Bruce Fields op->status = nfserr_rep_too_big_to_cache;
5415c8f13d97SJ. Bruce Fields else
5416c8f13d97SJ. Bruce Fields op->status = nfserr_rep_too_big;
5417c8f13d97SJ. Bruce Fields }
541807d1f802SJ. Bruce Fields if (op->status == nfserr_resource ||
541907d1f802SJ. Bruce Fields op->status == nfserr_rep_too_big ||
542007d1f802SJ. Bruce Fields op->status == nfserr_rep_too_big_to_cache) {
542107d1f802SJ. Bruce Fields /*
542207d1f802SJ. Bruce Fields * The operation may have already been encoded or
542307d1f802SJ. Bruce Fields * partially encoded. No op returns anything additional
542407d1f802SJ. Bruce Fields * in the case of one of these three errors, so we can
542507d1f802SJ. Bruce Fields * just truncate back to after the status. But it's a
542607d1f802SJ. Bruce Fields * bug if we had to do this on a non-idempotent op:
542707d1f802SJ. Bruce Fields */
542807d1f802SJ. Bruce Fields warn_on_nonidempotent_op(op);
5429082d4bd7SJ. Bruce Fields xdr_truncate_encode(xdr, post_err_offset);
543007d1f802SJ. Bruce Fields }
54319411b1d4SJ. Bruce Fields if (so) {
5432082d4bd7SJ. Bruce Fields int len = xdr->buf->len - post_err_offset;
5433082d4bd7SJ. Bruce Fields
54349411b1d4SJ. Bruce Fields so->so_replay.rp_status = op->status;
5435082d4bd7SJ. Bruce Fields so->so_replay.rp_buflen = len;
5436082d4bd7SJ. Bruce Fields read_bytes_from_xdr_buf(xdr->buf, post_err_offset,
5437082d4bd7SJ. Bruce Fields so->so_replay.rp_buf, len);
54389411b1d4SJ. Bruce Fields }
5439695e12f8SBenny Halevy status:
5440095a764bSChuck Lever *p = op->status;
544115a8b55dSJeff Layton release:
544215a8b55dSJeff Layton if (opdesc && opdesc->op_release)
544315a8b55dSJeff Layton opdesc->op_release(&op->u);
5444ed4a567aSChuck Lever
5445ed4a567aSChuck Lever /*
5446ed4a567aSChuck Lever * Account for pages consumed while encoding this operation.
5447ed4a567aSChuck Lever * The xdr_stream primitives don't manage rq_next_page.
5448ed4a567aSChuck Lever */
5449ed4a567aSChuck Lever rqstp->rq_next_page = xdr->page_ptr + 1;
54501da177e4SLinus Torvalds }
54511da177e4SLinus Torvalds
54521da177e4SLinus Torvalds /*
54531da177e4SLinus Torvalds * Encode the reply stored in the stateowner reply cache
54541da177e4SLinus Torvalds *
54551da177e4SLinus Torvalds * XDR note: do not encode rp->rp_buflen: the buffer contains the
54561da177e4SLinus Torvalds * previously sent already encoded operation.
54571da177e4SLinus Torvalds */
54581da177e4SLinus Torvalds void
nfsd4_encode_replay(struct xdr_stream * xdr,struct nfsd4_op * op)5459d0a381ddSJ. Bruce Fields nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op)
54601da177e4SLinus Torvalds {
5461bc749ca4SJ. Bruce Fields __be32 *p;
54621da177e4SLinus Torvalds struct nfs4_replay *rp = op->replay;
54631da177e4SLinus Torvalds
5464d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 8 + rp->rp_buflen);
5465d0a381ddSJ. Bruce Fields if (!p) {
5466d0a381ddSJ. Bruce Fields WARN_ON_ONCE(1);
5467d0a381ddSJ. Bruce Fields return;
5468d0a381ddSJ. Bruce Fields }
5469c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(op->opnum);
54701da177e4SLinus Torvalds *p++ = rp->rp_status; /* already xdr'ed */
54711da177e4SLinus Torvalds
54720c0c267bSJ. Bruce Fields p = xdr_encode_opaque_fixed(p, rp->rp_buf, rp->rp_buflen);
54731da177e4SLinus Torvalds }
54741da177e4SLinus Torvalds
nfsd4_release_compoundargs(struct svc_rqst * rqstp)54758537488bSChristoph Hellwig void nfsd4_release_compoundargs(struct svc_rqst *rqstp)
54761da177e4SLinus Torvalds {
54773e98abffSJ. Bruce Fields struct nfsd4_compoundargs *args = rqstp->rq_argp;
54783e98abffSJ. Bruce Fields
54791da177e4SLinus Torvalds if (args->ops != args->iops) {
548080e591ceSChuck Lever vfree(args->ops);
54811da177e4SLinus Torvalds args->ops = args->iops;
54821da177e4SLinus Torvalds }
54831da177e4SLinus Torvalds while (args->to_free) {
5484d5e23383SJ. Bruce Fields struct svcxdr_tmpbuf *tb = args->to_free;
54851da177e4SLinus Torvalds args->to_free = tb->next;
54861da177e4SLinus Torvalds kfree(tb);
54871da177e4SLinus Torvalds }
54881da177e4SLinus Torvalds }
54891da177e4SLinus Torvalds
5490c44b31c2SChuck Lever bool
nfs4svc_decode_compoundargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)549116c66364SChuck Lever nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
54921da177e4SLinus Torvalds {
5493026fec7eSChristoph Hellwig struct nfsd4_compoundargs *args = rqstp->rq_argp;
5494026fec7eSChristoph Hellwig
5495c1346a12SChuck Lever /* svcxdr_tmp_alloc */
54961da177e4SLinus Torvalds args->to_free = NULL;
5497c1346a12SChuck Lever
549816c66364SChuck Lever args->xdr = xdr;
54991da177e4SLinus Torvalds args->ops = args->iops;
55001da177e4SLinus Torvalds args->rqstp = rqstp;
55011da177e4SLinus Torvalds
5502d9b74bdaSChuck Lever return nfsd4_decode_compound(args);
55031da177e4SLinus Torvalds }
55041da177e4SLinus Torvalds
5505130e2054SChuck Lever bool
nfs4svc_encode_compoundres(struct svc_rqst * rqstp,struct xdr_stream * xdr)5506fda49441SChuck Lever nfs4svc_encode_compoundres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
55071da177e4SLinus Torvalds {
550863f8de37SChristoph Hellwig struct nfsd4_compoundres *resp = rqstp->rq_resp;
5509fda49441SChuck Lever __be32 *p;
55106ac90391SJ. Bruce Fields
55113b0ebb25SChuck Lever /*
55123b0ebb25SChuck Lever * Send buffer space for the following items is reserved
55133b0ebb25SChuck Lever * at the top of nfsd4_proc_compound().
55143b0ebb25SChuck Lever */
55153b0ebb25SChuck Lever p = resp->statusp;
5516cc028a10SChuck Lever
55173b0ebb25SChuck Lever *p++ = resp->cstate.status;
55181da177e4SLinus Torvalds *p++ = htonl(resp->taglen);
55191da177e4SLinus Torvalds memcpy(p, resp->tag, resp->taglen);
55201da177e4SLinus Torvalds p += XDR_QUADLEN(resp->taglen);
55211da177e4SLinus Torvalds *p++ = htonl(resp->opcnt);
55221da177e4SLinus Torvalds
5523b607664eSTrond Myklebust nfsd4_sequence_done(resp);
5524130e2054SChuck Lever return true;
55251da177e4SLinus Torvalds }
5526