162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Server-side XDR for NFSv4 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2002 The Regents of the University of Michigan. 562306a36Sopenharmony_ci * All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Kendrick Smith <kmsmith@umich.edu> 862306a36Sopenharmony_ci * Andy Adamson <andros@umich.edu> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 1162306a36Sopenharmony_ci * modification, are permitted provided that the following conditions 1262306a36Sopenharmony_ci * are met: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 1562306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 1662306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 1762306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 1862306a36Sopenharmony_ci * documentation and/or other materials provided with the distribution. 1962306a36Sopenharmony_ci * 3. Neither the name of the University nor the names of its 2062306a36Sopenharmony_ci * contributors may be used to endorse or promote products derived 2162306a36Sopenharmony_ci * from this software without specific prior written permission. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 2462306a36Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 2562306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 2662306a36Sopenharmony_ci * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2762306a36Sopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2862306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2962306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 3062306a36Sopenharmony_ci * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 3162306a36Sopenharmony_ci * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 3262306a36Sopenharmony_ci * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 3362306a36Sopenharmony_ci * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include <linux/file.h> 3762306a36Sopenharmony_ci#include <linux/slab.h> 3862306a36Sopenharmony_ci#include <linux/namei.h> 3962306a36Sopenharmony_ci#include <linux/statfs.h> 4062306a36Sopenharmony_ci#include <linux/utsname.h> 4162306a36Sopenharmony_ci#include <linux/pagemap.h> 4262306a36Sopenharmony_ci#include <linux/sunrpc/svcauth_gss.h> 4362306a36Sopenharmony_ci#include <linux/sunrpc/addr.h> 4462306a36Sopenharmony_ci#include <linux/xattr.h> 4562306a36Sopenharmony_ci#include <linux/vmalloc.h> 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#include <uapi/linux/xattr.h> 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#include "idmap.h" 5062306a36Sopenharmony_ci#include "acl.h" 5162306a36Sopenharmony_ci#include "xdr4.h" 5262306a36Sopenharmony_ci#include "vfs.h" 5362306a36Sopenharmony_ci#include "state.h" 5462306a36Sopenharmony_ci#include "cache.h" 5562306a36Sopenharmony_ci#include "netns.h" 5662306a36Sopenharmony_ci#include "pnfs.h" 5762306a36Sopenharmony_ci#include "filecache.h" 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#include "trace.h" 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#ifdef CONFIG_NFSD_V4_SECURITY_LABEL 6262306a36Sopenharmony_ci#include <linux/security.h> 6362306a36Sopenharmony_ci#endif 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define NFSDDBG_FACILITY NFSDDBG_XDR 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ciconst u32 nfsd_suppattrs[3][3] = { 6962306a36Sopenharmony_ci {NFSD4_SUPPORTED_ATTRS_WORD0, 7062306a36Sopenharmony_ci NFSD4_SUPPORTED_ATTRS_WORD1, 7162306a36Sopenharmony_ci NFSD4_SUPPORTED_ATTRS_WORD2}, 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci {NFSD4_1_SUPPORTED_ATTRS_WORD0, 7462306a36Sopenharmony_ci NFSD4_1_SUPPORTED_ATTRS_WORD1, 7562306a36Sopenharmony_ci NFSD4_1_SUPPORTED_ATTRS_WORD2}, 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci {NFSD4_1_SUPPORTED_ATTRS_WORD0, 7862306a36Sopenharmony_ci NFSD4_1_SUPPORTED_ATTRS_WORD1, 7962306a36Sopenharmony_ci NFSD4_2_SUPPORTED_ATTRS_WORD2}, 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* 8362306a36Sopenharmony_ci * As per referral draft, the fsid for a referral MUST be different from the fsid of the containing 8462306a36Sopenharmony_ci * directory in order to indicate to the client that a filesystem boundary is present 8562306a36Sopenharmony_ci * We use a fixed fsid for a referral 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci#define NFS4_REFERRAL_FSID_MAJOR 0x8000000ULL 8862306a36Sopenharmony_ci#define NFS4_REFERRAL_FSID_MINOR 0x8000000ULL 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic __be32 9162306a36Sopenharmony_cicheck_filename(char *str, int len) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci int i; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (len == 0) 9662306a36Sopenharmony_ci return nfserr_inval; 9762306a36Sopenharmony_ci if (len > NFS4_MAXNAMLEN) 9862306a36Sopenharmony_ci return nfserr_nametoolong; 9962306a36Sopenharmony_ci if (isdotent(str, len)) 10062306a36Sopenharmony_ci return nfserr_badname; 10162306a36Sopenharmony_ci for (i = 0; i < len; i++) 10262306a36Sopenharmony_ci if (str[i] == '/') 10362306a36Sopenharmony_ci return nfserr_badname; 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int zero_clientid(clientid_t *clid) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci return (clid->cl_boot == 0) && (clid->cl_id == 0); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/** 11362306a36Sopenharmony_ci * svcxdr_tmpalloc - allocate memory to be freed after compound processing 11462306a36Sopenharmony_ci * @argp: NFSv4 compound argument structure 11562306a36Sopenharmony_ci * @len: length of buffer to allocate 11662306a36Sopenharmony_ci * 11762306a36Sopenharmony_ci * Allocates a buffer of size @len to be freed when processing the compound 11862306a36Sopenharmony_ci * operation described in @argp finishes. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_cistatic void * 12162306a36Sopenharmony_cisvcxdr_tmpalloc(struct nfsd4_compoundargs *argp, u32 len) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct svcxdr_tmpbuf *tb; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci tb = kmalloc(sizeof(*tb) + len, GFP_KERNEL); 12662306a36Sopenharmony_ci if (!tb) 12762306a36Sopenharmony_ci return NULL; 12862306a36Sopenharmony_ci tb->next = argp->to_free; 12962306a36Sopenharmony_ci argp->to_free = tb; 13062306a36Sopenharmony_ci return tb->buf; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* 13462306a36Sopenharmony_ci * For xdr strings that need to be passed to other kernel api's 13562306a36Sopenharmony_ci * as null-terminated strings. 13662306a36Sopenharmony_ci * 13762306a36Sopenharmony_ci * Note null-terminating in place usually isn't safe since the 13862306a36Sopenharmony_ci * buffer might end on a page boundary. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_cistatic char * 14162306a36Sopenharmony_cisvcxdr_dupstr(struct nfsd4_compoundargs *argp, void *buf, u32 len) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci char *p = svcxdr_tmpalloc(argp, len + 1); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (!p) 14662306a36Sopenharmony_ci return NULL; 14762306a36Sopenharmony_ci memcpy(p, buf, len); 14862306a36Sopenharmony_ci p[len] = '\0'; 14962306a36Sopenharmony_ci return p; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void * 15362306a36Sopenharmony_cisvcxdr_savemem(struct nfsd4_compoundargs *argp, __be32 *p, u32 len) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci __be32 *tmp; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* 15862306a36Sopenharmony_ci * The location of the decoded data item is stable, 15962306a36Sopenharmony_ci * so @p is OK to use. This is the common case. 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci if (p != argp->xdr->scratch.iov_base) 16262306a36Sopenharmony_ci return p; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci tmp = svcxdr_tmpalloc(argp, len); 16562306a36Sopenharmony_ci if (!tmp) 16662306a36Sopenharmony_ci return NULL; 16762306a36Sopenharmony_ci memcpy(tmp, p, len); 16862306a36Sopenharmony_ci return tmp; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/* 17262306a36Sopenharmony_ci * NFSv4 basic data type decoders 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/* 17662306a36Sopenharmony_ci * This helper handles variable-length opaques which belong to protocol 17762306a36Sopenharmony_ci * elements that this implementation does not support. 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_cistatic __be32 18062306a36Sopenharmony_cinfsd4_decode_ignored_string(struct nfsd4_compoundargs *argp, u32 maxlen) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci u32 len; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &len) < 0) 18562306a36Sopenharmony_ci return nfserr_bad_xdr; 18662306a36Sopenharmony_ci if (maxlen && len > maxlen) 18762306a36Sopenharmony_ci return nfserr_bad_xdr; 18862306a36Sopenharmony_ci if (!xdr_inline_decode(argp->xdr, len)) 18962306a36Sopenharmony_ci return nfserr_bad_xdr; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci return nfs_ok; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic __be32 19562306a36Sopenharmony_cinfsd4_decode_opaque(struct nfsd4_compoundargs *argp, struct xdr_netobj *o) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci __be32 *p; 19862306a36Sopenharmony_ci u32 len; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &len) < 0) 20162306a36Sopenharmony_ci return nfserr_bad_xdr; 20262306a36Sopenharmony_ci if (len == 0 || len > NFS4_OPAQUE_LIMIT) 20362306a36Sopenharmony_ci return nfserr_bad_xdr; 20462306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, len); 20562306a36Sopenharmony_ci if (!p) 20662306a36Sopenharmony_ci return nfserr_bad_xdr; 20762306a36Sopenharmony_ci o->data = svcxdr_savemem(argp, p, len); 20862306a36Sopenharmony_ci if (!o->data) 20962306a36Sopenharmony_ci return nfserr_jukebox; 21062306a36Sopenharmony_ci o->len = len; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return nfs_ok; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic __be32 21662306a36Sopenharmony_cinfsd4_decode_component4(struct nfsd4_compoundargs *argp, char **namp, u32 *lenp) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci __be32 *p, status; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, lenp) < 0) 22162306a36Sopenharmony_ci return nfserr_bad_xdr; 22262306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, *lenp); 22362306a36Sopenharmony_ci if (!p) 22462306a36Sopenharmony_ci return nfserr_bad_xdr; 22562306a36Sopenharmony_ci status = check_filename((char *)p, *lenp); 22662306a36Sopenharmony_ci if (status) 22762306a36Sopenharmony_ci return status; 22862306a36Sopenharmony_ci *namp = svcxdr_savemem(argp, p, *lenp); 22962306a36Sopenharmony_ci if (!*namp) 23062306a36Sopenharmony_ci return nfserr_jukebox; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return nfs_ok; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic __be32 23662306a36Sopenharmony_cinfsd4_decode_nfstime4(struct nfsd4_compoundargs *argp, struct timespec64 *tv) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci __be32 *p; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, XDR_UNIT * 3); 24162306a36Sopenharmony_ci if (!p) 24262306a36Sopenharmony_ci return nfserr_bad_xdr; 24362306a36Sopenharmony_ci p = xdr_decode_hyper(p, &tv->tv_sec); 24462306a36Sopenharmony_ci tv->tv_nsec = be32_to_cpup(p++); 24562306a36Sopenharmony_ci if (tv->tv_nsec >= (u32)1000000000) 24662306a36Sopenharmony_ci return nfserr_inval; 24762306a36Sopenharmony_ci return nfs_ok; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic __be32 25162306a36Sopenharmony_cinfsd4_decode_verifier4(struct nfsd4_compoundargs *argp, nfs4_verifier *verf) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci __be32 *p; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, NFS4_VERIFIER_SIZE); 25662306a36Sopenharmony_ci if (!p) 25762306a36Sopenharmony_ci return nfserr_bad_xdr; 25862306a36Sopenharmony_ci memcpy(verf->data, p, sizeof(verf->data)); 25962306a36Sopenharmony_ci return nfs_ok; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/** 26362306a36Sopenharmony_ci * nfsd4_decode_bitmap4 - Decode an NFSv4 bitmap4 26462306a36Sopenharmony_ci * @argp: NFSv4 compound argument structure 26562306a36Sopenharmony_ci * @bmval: pointer to an array of u32's to decode into 26662306a36Sopenharmony_ci * @bmlen: size of the @bmval array 26762306a36Sopenharmony_ci * 26862306a36Sopenharmony_ci * The server needs to return nfs_ok rather than nfserr_bad_xdr when 26962306a36Sopenharmony_ci * encountering bitmaps containing bits it does not recognize. This 27062306a36Sopenharmony_ci * includes bits in bitmap words past WORDn, where WORDn is the last 27162306a36Sopenharmony_ci * bitmap WORD the implementation currently supports. Thus we are 27262306a36Sopenharmony_ci * careful here to simply ignore bits in bitmap words that this 27362306a36Sopenharmony_ci * implementation has yet to support explicitly. 27462306a36Sopenharmony_ci * 27562306a36Sopenharmony_ci * Return values: 27662306a36Sopenharmony_ci * %nfs_ok: @bmval populated successfully 27762306a36Sopenharmony_ci * %nfserr_bad_xdr: the encoded bitmap was invalid 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_cistatic __be32 28062306a36Sopenharmony_cinfsd4_decode_bitmap4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci ssize_t status; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci status = xdr_stream_decode_uint32_array(argp->xdr, bmval, bmlen); 28562306a36Sopenharmony_ci return status == -EBADMSG ? nfserr_bad_xdr : nfs_ok; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic __be32 28962306a36Sopenharmony_cinfsd4_decode_nfsace4(struct nfsd4_compoundargs *argp, struct nfs4_ace *ace) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci __be32 *p, status; 29262306a36Sopenharmony_ci u32 length; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &ace->type) < 0) 29562306a36Sopenharmony_ci return nfserr_bad_xdr; 29662306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &ace->flag) < 0) 29762306a36Sopenharmony_ci return nfserr_bad_xdr; 29862306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &ace->access_mask) < 0) 29962306a36Sopenharmony_ci return nfserr_bad_xdr; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &length) < 0) 30262306a36Sopenharmony_ci return nfserr_bad_xdr; 30362306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, length); 30462306a36Sopenharmony_ci if (!p) 30562306a36Sopenharmony_ci return nfserr_bad_xdr; 30662306a36Sopenharmony_ci ace->whotype = nfs4_acl_get_whotype((char *)p, length); 30762306a36Sopenharmony_ci if (ace->whotype != NFS4_ACL_WHO_NAMED) 30862306a36Sopenharmony_ci status = nfs_ok; 30962306a36Sopenharmony_ci else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP) 31062306a36Sopenharmony_ci status = nfsd_map_name_to_gid(argp->rqstp, 31162306a36Sopenharmony_ci (char *)p, length, &ace->who_gid); 31262306a36Sopenharmony_ci else 31362306a36Sopenharmony_ci status = nfsd_map_name_to_uid(argp->rqstp, 31462306a36Sopenharmony_ci (char *)p, length, &ace->who_uid); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return status; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/* A counted array of nfsace4's */ 32062306a36Sopenharmony_cistatic noinline __be32 32162306a36Sopenharmony_cinfsd4_decode_acl(struct nfsd4_compoundargs *argp, struct nfs4_acl **acl) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct nfs4_ace *ace; 32462306a36Sopenharmony_ci __be32 status; 32562306a36Sopenharmony_ci u32 count; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &count) < 0) 32862306a36Sopenharmony_ci return nfserr_bad_xdr; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (count > xdr_stream_remaining(argp->xdr) / 20) 33162306a36Sopenharmony_ci /* 33262306a36Sopenharmony_ci * Even with 4-byte names there wouldn't be 33362306a36Sopenharmony_ci * space for that many aces; something fishy is 33462306a36Sopenharmony_ci * going on: 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci return nfserr_fbig; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci *acl = svcxdr_tmpalloc(argp, nfs4_acl_bytes(count)); 33962306a36Sopenharmony_ci if (*acl == NULL) 34062306a36Sopenharmony_ci return nfserr_jukebox; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci (*acl)->naces = count; 34362306a36Sopenharmony_ci for (ace = (*acl)->aces; ace < (*acl)->aces + count; ace++) { 34462306a36Sopenharmony_ci status = nfsd4_decode_nfsace4(argp, ace); 34562306a36Sopenharmony_ci if (status) 34662306a36Sopenharmony_ci return status; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return nfs_ok; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic noinline __be32 35362306a36Sopenharmony_cinfsd4_decode_security_label(struct nfsd4_compoundargs *argp, 35462306a36Sopenharmony_ci struct xdr_netobj *label) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci u32 lfs, pi, length; 35762306a36Sopenharmony_ci __be32 *p; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &lfs) < 0) 36062306a36Sopenharmony_ci return nfserr_bad_xdr; 36162306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &pi) < 0) 36262306a36Sopenharmony_ci return nfserr_bad_xdr; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &length) < 0) 36562306a36Sopenharmony_ci return nfserr_bad_xdr; 36662306a36Sopenharmony_ci if (length > NFS4_MAXLABELLEN) 36762306a36Sopenharmony_ci return nfserr_badlabel; 36862306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, length); 36962306a36Sopenharmony_ci if (!p) 37062306a36Sopenharmony_ci return nfserr_bad_xdr; 37162306a36Sopenharmony_ci label->len = length; 37262306a36Sopenharmony_ci label->data = svcxdr_dupstr(argp, p, length); 37362306a36Sopenharmony_ci if (!label->data) 37462306a36Sopenharmony_ci return nfserr_jukebox; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return nfs_ok; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic __be32 38062306a36Sopenharmony_cinfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen, 38162306a36Sopenharmony_ci struct iattr *iattr, struct nfs4_acl **acl, 38262306a36Sopenharmony_ci struct xdr_netobj *label, int *umask) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci unsigned int starting_pos; 38562306a36Sopenharmony_ci u32 attrlist4_count; 38662306a36Sopenharmony_ci __be32 *p, status; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci iattr->ia_valid = 0; 38962306a36Sopenharmony_ci status = nfsd4_decode_bitmap4(argp, bmval, bmlen); 39062306a36Sopenharmony_ci if (status) 39162306a36Sopenharmony_ci return nfserr_bad_xdr; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0 39462306a36Sopenharmony_ci || bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1 39562306a36Sopenharmony_ci || bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2) { 39662306a36Sopenharmony_ci if (nfsd_attrs_supported(argp->minorversion, bmval)) 39762306a36Sopenharmony_ci return nfserr_inval; 39862306a36Sopenharmony_ci return nfserr_attrnotsupp; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &attrlist4_count) < 0) 40262306a36Sopenharmony_ci return nfserr_bad_xdr; 40362306a36Sopenharmony_ci starting_pos = xdr_stream_pos(argp->xdr); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (bmval[0] & FATTR4_WORD0_SIZE) { 40662306a36Sopenharmony_ci u64 size; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &size) < 0) 40962306a36Sopenharmony_ci return nfserr_bad_xdr; 41062306a36Sopenharmony_ci iattr->ia_size = size; 41162306a36Sopenharmony_ci iattr->ia_valid |= ATTR_SIZE; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci if (bmval[0] & FATTR4_WORD0_ACL) { 41462306a36Sopenharmony_ci status = nfsd4_decode_acl(argp, acl); 41562306a36Sopenharmony_ci if (status) 41662306a36Sopenharmony_ci return status; 41762306a36Sopenharmony_ci } else 41862306a36Sopenharmony_ci *acl = NULL; 41962306a36Sopenharmony_ci if (bmval[1] & FATTR4_WORD1_MODE) { 42062306a36Sopenharmony_ci u32 mode; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &mode) < 0) 42362306a36Sopenharmony_ci return nfserr_bad_xdr; 42462306a36Sopenharmony_ci iattr->ia_mode = mode; 42562306a36Sopenharmony_ci iattr->ia_mode &= (S_IFMT | S_IALLUGO); 42662306a36Sopenharmony_ci iattr->ia_valid |= ATTR_MODE; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci if (bmval[1] & FATTR4_WORD1_OWNER) { 42962306a36Sopenharmony_ci u32 length; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &length) < 0) 43262306a36Sopenharmony_ci return nfserr_bad_xdr; 43362306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, length); 43462306a36Sopenharmony_ci if (!p) 43562306a36Sopenharmony_ci return nfserr_bad_xdr; 43662306a36Sopenharmony_ci status = nfsd_map_name_to_uid(argp->rqstp, (char *)p, length, 43762306a36Sopenharmony_ci &iattr->ia_uid); 43862306a36Sopenharmony_ci if (status) 43962306a36Sopenharmony_ci return status; 44062306a36Sopenharmony_ci iattr->ia_valid |= ATTR_UID; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) { 44362306a36Sopenharmony_ci u32 length; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &length) < 0) 44662306a36Sopenharmony_ci return nfserr_bad_xdr; 44762306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, length); 44862306a36Sopenharmony_ci if (!p) 44962306a36Sopenharmony_ci return nfserr_bad_xdr; 45062306a36Sopenharmony_ci status = nfsd_map_name_to_gid(argp->rqstp, (char *)p, length, 45162306a36Sopenharmony_ci &iattr->ia_gid); 45262306a36Sopenharmony_ci if (status) 45362306a36Sopenharmony_ci return status; 45462306a36Sopenharmony_ci iattr->ia_valid |= ATTR_GID; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) { 45762306a36Sopenharmony_ci u32 set_it; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &set_it) < 0) 46062306a36Sopenharmony_ci return nfserr_bad_xdr; 46162306a36Sopenharmony_ci switch (set_it) { 46262306a36Sopenharmony_ci case NFS4_SET_TO_CLIENT_TIME: 46362306a36Sopenharmony_ci status = nfsd4_decode_nfstime4(argp, &iattr->ia_atime); 46462306a36Sopenharmony_ci if (status) 46562306a36Sopenharmony_ci return status; 46662306a36Sopenharmony_ci iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); 46762306a36Sopenharmony_ci break; 46862306a36Sopenharmony_ci case NFS4_SET_TO_SERVER_TIME: 46962306a36Sopenharmony_ci iattr->ia_valid |= ATTR_ATIME; 47062306a36Sopenharmony_ci break; 47162306a36Sopenharmony_ci default: 47262306a36Sopenharmony_ci return nfserr_bad_xdr; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci if (bmval[1] & FATTR4_WORD1_TIME_CREATE) { 47662306a36Sopenharmony_ci struct timespec64 ts; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* No Linux filesystem supports setting this attribute. */ 47962306a36Sopenharmony_ci bmval[1] &= ~FATTR4_WORD1_TIME_CREATE; 48062306a36Sopenharmony_ci status = nfsd4_decode_nfstime4(argp, &ts); 48162306a36Sopenharmony_ci if (status) 48262306a36Sopenharmony_ci return status; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) { 48562306a36Sopenharmony_ci u32 set_it; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &set_it) < 0) 48862306a36Sopenharmony_ci return nfserr_bad_xdr; 48962306a36Sopenharmony_ci switch (set_it) { 49062306a36Sopenharmony_ci case NFS4_SET_TO_CLIENT_TIME: 49162306a36Sopenharmony_ci status = nfsd4_decode_nfstime4(argp, &iattr->ia_mtime); 49262306a36Sopenharmony_ci if (status) 49362306a36Sopenharmony_ci return status; 49462306a36Sopenharmony_ci iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); 49562306a36Sopenharmony_ci break; 49662306a36Sopenharmony_ci case NFS4_SET_TO_SERVER_TIME: 49762306a36Sopenharmony_ci iattr->ia_valid |= ATTR_MTIME; 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci default: 50062306a36Sopenharmony_ci return nfserr_bad_xdr; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci label->len = 0; 50462306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_NFSD_V4_SECURITY_LABEL) && 50562306a36Sopenharmony_ci bmval[2] & FATTR4_WORD2_SECURITY_LABEL) { 50662306a36Sopenharmony_ci status = nfsd4_decode_security_label(argp, label); 50762306a36Sopenharmony_ci if (status) 50862306a36Sopenharmony_ci return status; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci if (bmval[2] & FATTR4_WORD2_MODE_UMASK) { 51162306a36Sopenharmony_ci u32 mode, mask; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (!umask) 51462306a36Sopenharmony_ci return nfserr_bad_xdr; 51562306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &mode) < 0) 51662306a36Sopenharmony_ci return nfserr_bad_xdr; 51762306a36Sopenharmony_ci iattr->ia_mode = mode & (S_IFMT | S_IALLUGO); 51862306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &mask) < 0) 51962306a36Sopenharmony_ci return nfserr_bad_xdr; 52062306a36Sopenharmony_ci *umask = mask & S_IRWXUGO; 52162306a36Sopenharmony_ci iattr->ia_valid |= ATTR_MODE; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* request sanity: did attrlist4 contain the expected number of words? */ 52562306a36Sopenharmony_ci if (attrlist4_count != xdr_stream_pos(argp->xdr) - starting_pos) 52662306a36Sopenharmony_ci return nfserr_bad_xdr; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci return nfs_ok; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic __be32 53262306a36Sopenharmony_cinfsd4_decode_stateid4(struct nfsd4_compoundargs *argp, stateid_t *sid) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci __be32 *p; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, NFS4_STATEID_SIZE); 53762306a36Sopenharmony_ci if (!p) 53862306a36Sopenharmony_ci return nfserr_bad_xdr; 53962306a36Sopenharmony_ci sid->si_generation = be32_to_cpup(p++); 54062306a36Sopenharmony_ci memcpy(&sid->si_opaque, p, sizeof(sid->si_opaque)); 54162306a36Sopenharmony_ci return nfs_ok; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic __be32 54562306a36Sopenharmony_cinfsd4_decode_clientid4(struct nfsd4_compoundargs *argp, clientid_t *clientid) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci __be32 *p; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, sizeof(__be64)); 55062306a36Sopenharmony_ci if (!p) 55162306a36Sopenharmony_ci return nfserr_bad_xdr; 55262306a36Sopenharmony_ci memcpy(clientid, p, sizeof(*clientid)); 55362306a36Sopenharmony_ci return nfs_ok; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic __be32 55762306a36Sopenharmony_cinfsd4_decode_state_owner4(struct nfsd4_compoundargs *argp, 55862306a36Sopenharmony_ci clientid_t *clientid, struct xdr_netobj *owner) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci __be32 status; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci status = nfsd4_decode_clientid4(argp, clientid); 56362306a36Sopenharmony_ci if (status) 56462306a36Sopenharmony_ci return status; 56562306a36Sopenharmony_ci return nfsd4_decode_opaque(argp, owner); 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci#ifdef CONFIG_NFSD_PNFS 56962306a36Sopenharmony_cistatic __be32 57062306a36Sopenharmony_cinfsd4_decode_deviceid4(struct nfsd4_compoundargs *argp, 57162306a36Sopenharmony_ci struct nfsd4_deviceid *devid) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci __be32 *p; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, NFS4_DEVICEID4_SIZE); 57662306a36Sopenharmony_ci if (!p) 57762306a36Sopenharmony_ci return nfserr_bad_xdr; 57862306a36Sopenharmony_ci memcpy(devid, p, sizeof(*devid)); 57962306a36Sopenharmony_ci return nfs_ok; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic __be32 58362306a36Sopenharmony_cinfsd4_decode_layoutupdate4(struct nfsd4_compoundargs *argp, 58462306a36Sopenharmony_ci struct nfsd4_layoutcommit *lcp) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &lcp->lc_layout_type) < 0) 58762306a36Sopenharmony_ci return nfserr_bad_xdr; 58862306a36Sopenharmony_ci if (lcp->lc_layout_type < LAYOUT_NFSV4_1_FILES) 58962306a36Sopenharmony_ci return nfserr_bad_xdr; 59062306a36Sopenharmony_ci if (lcp->lc_layout_type >= LAYOUT_TYPE_MAX) 59162306a36Sopenharmony_ci return nfserr_bad_xdr; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &lcp->lc_up_len) < 0) 59462306a36Sopenharmony_ci return nfserr_bad_xdr; 59562306a36Sopenharmony_ci if (lcp->lc_up_len > 0) { 59662306a36Sopenharmony_ci lcp->lc_up_layout = xdr_inline_decode(argp->xdr, lcp->lc_up_len); 59762306a36Sopenharmony_ci if (!lcp->lc_up_layout) 59862306a36Sopenharmony_ci return nfserr_bad_xdr; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci return nfs_ok; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic __be32 60562306a36Sopenharmony_cinfsd4_decode_layoutreturn4(struct nfsd4_compoundargs *argp, 60662306a36Sopenharmony_ci struct nfsd4_layoutreturn *lrp) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci __be32 status; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &lrp->lr_return_type) < 0) 61162306a36Sopenharmony_ci return nfserr_bad_xdr; 61262306a36Sopenharmony_ci switch (lrp->lr_return_type) { 61362306a36Sopenharmony_ci case RETURN_FILE: 61462306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &lrp->lr_seg.offset) < 0) 61562306a36Sopenharmony_ci return nfserr_bad_xdr; 61662306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &lrp->lr_seg.length) < 0) 61762306a36Sopenharmony_ci return nfserr_bad_xdr; 61862306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &lrp->lr_sid); 61962306a36Sopenharmony_ci if (status) 62062306a36Sopenharmony_ci return status; 62162306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &lrp->lrf_body_len) < 0) 62262306a36Sopenharmony_ci return nfserr_bad_xdr; 62362306a36Sopenharmony_ci if (lrp->lrf_body_len > 0) { 62462306a36Sopenharmony_ci lrp->lrf_body = xdr_inline_decode(argp->xdr, lrp->lrf_body_len); 62562306a36Sopenharmony_ci if (!lrp->lrf_body) 62662306a36Sopenharmony_ci return nfserr_bad_xdr; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci break; 62962306a36Sopenharmony_ci case RETURN_FSID: 63062306a36Sopenharmony_ci case RETURN_ALL: 63162306a36Sopenharmony_ci lrp->lr_seg.offset = 0; 63262306a36Sopenharmony_ci lrp->lr_seg.length = NFS4_MAX_UINT64; 63362306a36Sopenharmony_ci break; 63462306a36Sopenharmony_ci default: 63562306a36Sopenharmony_ci return nfserr_bad_xdr; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci return nfs_ok; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci#endif /* CONFIG_NFSD_PNFS */ 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic __be32 64462306a36Sopenharmony_cinfsd4_decode_sessionid4(struct nfsd4_compoundargs *argp, 64562306a36Sopenharmony_ci struct nfs4_sessionid *sessionid) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci __be32 *p; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, NFS4_MAX_SESSIONID_LEN); 65062306a36Sopenharmony_ci if (!p) 65162306a36Sopenharmony_ci return nfserr_bad_xdr; 65262306a36Sopenharmony_ci memcpy(sessionid->data, p, sizeof(sessionid->data)); 65362306a36Sopenharmony_ci return nfs_ok; 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci/* Defined in Appendix A of RFC 5531 */ 65762306a36Sopenharmony_cistatic __be32 65862306a36Sopenharmony_cinfsd4_decode_authsys_parms(struct nfsd4_compoundargs *argp, 65962306a36Sopenharmony_ci struct nfsd4_cb_sec *cbs) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci u32 stamp, gidcount, uid, gid; 66262306a36Sopenharmony_ci __be32 *p, status; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &stamp) < 0) 66562306a36Sopenharmony_ci return nfserr_bad_xdr; 66662306a36Sopenharmony_ci /* machine name */ 66762306a36Sopenharmony_ci status = nfsd4_decode_ignored_string(argp, 255); 66862306a36Sopenharmony_ci if (status) 66962306a36Sopenharmony_ci return status; 67062306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &uid) < 0) 67162306a36Sopenharmony_ci return nfserr_bad_xdr; 67262306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &gid) < 0) 67362306a36Sopenharmony_ci return nfserr_bad_xdr; 67462306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &gidcount) < 0) 67562306a36Sopenharmony_ci return nfserr_bad_xdr; 67662306a36Sopenharmony_ci if (gidcount > 16) 67762306a36Sopenharmony_ci return nfserr_bad_xdr; 67862306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, gidcount << 2); 67962306a36Sopenharmony_ci if (!p) 68062306a36Sopenharmony_ci return nfserr_bad_xdr; 68162306a36Sopenharmony_ci if (cbs->flavor == (u32)(-1)) { 68262306a36Sopenharmony_ci struct user_namespace *userns = nfsd_user_namespace(argp->rqstp); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci kuid_t kuid = make_kuid(userns, uid); 68562306a36Sopenharmony_ci kgid_t kgid = make_kgid(userns, gid); 68662306a36Sopenharmony_ci if (uid_valid(kuid) && gid_valid(kgid)) { 68762306a36Sopenharmony_ci cbs->uid = kuid; 68862306a36Sopenharmony_ci cbs->gid = kgid; 68962306a36Sopenharmony_ci cbs->flavor = RPC_AUTH_UNIX; 69062306a36Sopenharmony_ci } else { 69162306a36Sopenharmony_ci dprintk("RPC_AUTH_UNIX with invalid uid or gid, ignoring!\n"); 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci return nfs_ok; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic __be32 69962306a36Sopenharmony_cinfsd4_decode_gss_cb_handles4(struct nfsd4_compoundargs *argp, 70062306a36Sopenharmony_ci struct nfsd4_cb_sec *cbs) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci __be32 status; 70362306a36Sopenharmony_ci u32 service; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci dprintk("RPC_AUTH_GSS callback secflavor not supported!\n"); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &service) < 0) 70862306a36Sopenharmony_ci return nfserr_bad_xdr; 70962306a36Sopenharmony_ci if (service < RPC_GSS_SVC_NONE || service > RPC_GSS_SVC_PRIVACY) 71062306a36Sopenharmony_ci return nfserr_bad_xdr; 71162306a36Sopenharmony_ci /* gcbp_handle_from_server */ 71262306a36Sopenharmony_ci status = nfsd4_decode_ignored_string(argp, 0); 71362306a36Sopenharmony_ci if (status) 71462306a36Sopenharmony_ci return status; 71562306a36Sopenharmony_ci /* gcbp_handle_from_client */ 71662306a36Sopenharmony_ci status = nfsd4_decode_ignored_string(argp, 0); 71762306a36Sopenharmony_ci if (status) 71862306a36Sopenharmony_ci return status; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci return nfs_ok; 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci/* a counted array of callback_sec_parms4 items */ 72462306a36Sopenharmony_cistatic __be32 72562306a36Sopenharmony_cinfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_cb_sec *cbs) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci u32 i, secflavor, nr_secflavs; 72862306a36Sopenharmony_ci __be32 status; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* callback_sec_params4 */ 73162306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &nr_secflavs) < 0) 73262306a36Sopenharmony_ci return nfserr_bad_xdr; 73362306a36Sopenharmony_ci if (nr_secflavs) 73462306a36Sopenharmony_ci cbs->flavor = (u32)(-1); 73562306a36Sopenharmony_ci else 73662306a36Sopenharmony_ci /* Is this legal? Be generous, take it to mean AUTH_NONE: */ 73762306a36Sopenharmony_ci cbs->flavor = 0; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci for (i = 0; i < nr_secflavs; ++i) { 74062306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &secflavor) < 0) 74162306a36Sopenharmony_ci return nfserr_bad_xdr; 74262306a36Sopenharmony_ci switch (secflavor) { 74362306a36Sopenharmony_ci case RPC_AUTH_NULL: 74462306a36Sopenharmony_ci /* void */ 74562306a36Sopenharmony_ci if (cbs->flavor == (u32)(-1)) 74662306a36Sopenharmony_ci cbs->flavor = RPC_AUTH_NULL; 74762306a36Sopenharmony_ci break; 74862306a36Sopenharmony_ci case RPC_AUTH_UNIX: 74962306a36Sopenharmony_ci status = nfsd4_decode_authsys_parms(argp, cbs); 75062306a36Sopenharmony_ci if (status) 75162306a36Sopenharmony_ci return status; 75262306a36Sopenharmony_ci break; 75362306a36Sopenharmony_ci case RPC_AUTH_GSS: 75462306a36Sopenharmony_ci status = nfsd4_decode_gss_cb_handles4(argp, cbs); 75562306a36Sopenharmony_ci if (status) 75662306a36Sopenharmony_ci return status; 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci default: 75962306a36Sopenharmony_ci return nfserr_inval; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci return nfs_ok; 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci/* 76862306a36Sopenharmony_ci * NFSv4 operation argument decoders 76962306a36Sopenharmony_ci */ 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic __be32 77262306a36Sopenharmony_cinfsd4_decode_access(struct nfsd4_compoundargs *argp, 77362306a36Sopenharmony_ci union nfsd4_op_u *u) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci struct nfsd4_access *access = &u->access; 77662306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &access->ac_req_access) < 0) 77762306a36Sopenharmony_ci return nfserr_bad_xdr; 77862306a36Sopenharmony_ci return nfs_ok; 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic __be32 78262306a36Sopenharmony_cinfsd4_decode_close(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci struct nfsd4_close *close = &u->close; 78562306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &close->cl_seqid) < 0) 78662306a36Sopenharmony_ci return nfserr_bad_xdr; 78762306a36Sopenharmony_ci return nfsd4_decode_stateid4(argp, &close->cl_stateid); 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic __be32 79262306a36Sopenharmony_cinfsd4_decode_commit(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci struct nfsd4_commit *commit = &u->commit; 79562306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &commit->co_offset) < 0) 79662306a36Sopenharmony_ci return nfserr_bad_xdr; 79762306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &commit->co_count) < 0) 79862306a36Sopenharmony_ci return nfserr_bad_xdr; 79962306a36Sopenharmony_ci memset(&commit->co_verf, 0, sizeof(commit->co_verf)); 80062306a36Sopenharmony_ci return nfs_ok; 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic __be32 80462306a36Sopenharmony_cinfsd4_decode_create(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci struct nfsd4_create *create = &u->create; 80762306a36Sopenharmony_ci __be32 *p, status; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci memset(create, 0, sizeof(*create)); 81062306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &create->cr_type) < 0) 81162306a36Sopenharmony_ci return nfserr_bad_xdr; 81262306a36Sopenharmony_ci switch (create->cr_type) { 81362306a36Sopenharmony_ci case NF4LNK: 81462306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &create->cr_datalen) < 0) 81562306a36Sopenharmony_ci return nfserr_bad_xdr; 81662306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, create->cr_datalen); 81762306a36Sopenharmony_ci if (!p) 81862306a36Sopenharmony_ci return nfserr_bad_xdr; 81962306a36Sopenharmony_ci create->cr_data = svcxdr_dupstr(argp, p, create->cr_datalen); 82062306a36Sopenharmony_ci if (!create->cr_data) 82162306a36Sopenharmony_ci return nfserr_jukebox; 82262306a36Sopenharmony_ci break; 82362306a36Sopenharmony_ci case NF4BLK: 82462306a36Sopenharmony_ci case NF4CHR: 82562306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &create->cr_specdata1) < 0) 82662306a36Sopenharmony_ci return nfserr_bad_xdr; 82762306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &create->cr_specdata2) < 0) 82862306a36Sopenharmony_ci return nfserr_bad_xdr; 82962306a36Sopenharmony_ci break; 83062306a36Sopenharmony_ci case NF4SOCK: 83162306a36Sopenharmony_ci case NF4FIFO: 83262306a36Sopenharmony_ci case NF4DIR: 83362306a36Sopenharmony_ci default: 83462306a36Sopenharmony_ci break; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci status = nfsd4_decode_component4(argp, &create->cr_name, 83762306a36Sopenharmony_ci &create->cr_namelen); 83862306a36Sopenharmony_ci if (status) 83962306a36Sopenharmony_ci return status; 84062306a36Sopenharmony_ci status = nfsd4_decode_fattr4(argp, create->cr_bmval, 84162306a36Sopenharmony_ci ARRAY_SIZE(create->cr_bmval), 84262306a36Sopenharmony_ci &create->cr_iattr, &create->cr_acl, 84362306a36Sopenharmony_ci &create->cr_label, &create->cr_umask); 84462306a36Sopenharmony_ci if (status) 84562306a36Sopenharmony_ci return status; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return nfs_ok; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic inline __be32 85162306a36Sopenharmony_cinfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci struct nfsd4_delegreturn *dr = &u->delegreturn; 85462306a36Sopenharmony_ci return nfsd4_decode_stateid4(argp, &dr->dr_stateid); 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic inline __be32 85862306a36Sopenharmony_cinfsd4_decode_getattr(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci struct nfsd4_getattr *getattr = &u->getattr; 86162306a36Sopenharmony_ci memset(getattr, 0, sizeof(*getattr)); 86262306a36Sopenharmony_ci return nfsd4_decode_bitmap4(argp, getattr->ga_bmval, 86362306a36Sopenharmony_ci ARRAY_SIZE(getattr->ga_bmval)); 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic __be32 86762306a36Sopenharmony_cinfsd4_decode_link(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci struct nfsd4_link *link = &u->link; 87062306a36Sopenharmony_ci memset(link, 0, sizeof(*link)); 87162306a36Sopenharmony_ci return nfsd4_decode_component4(argp, &link->li_name, &link->li_namelen); 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic __be32 87562306a36Sopenharmony_cinfsd4_decode_open_to_lock_owner4(struct nfsd4_compoundargs *argp, 87662306a36Sopenharmony_ci struct nfsd4_lock *lock) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci __be32 status; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &lock->lk_new_open_seqid) < 0) 88162306a36Sopenharmony_ci return nfserr_bad_xdr; 88262306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &lock->lk_new_open_stateid); 88362306a36Sopenharmony_ci if (status) 88462306a36Sopenharmony_ci return status; 88562306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &lock->lk_new_lock_seqid) < 0) 88662306a36Sopenharmony_ci return nfserr_bad_xdr; 88762306a36Sopenharmony_ci return nfsd4_decode_state_owner4(argp, &lock->lk_new_clientid, 88862306a36Sopenharmony_ci &lock->lk_new_owner); 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic __be32 89262306a36Sopenharmony_cinfsd4_decode_exist_lock_owner4(struct nfsd4_compoundargs *argp, 89362306a36Sopenharmony_ci struct nfsd4_lock *lock) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci __be32 status; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &lock->lk_old_lock_stateid); 89862306a36Sopenharmony_ci if (status) 89962306a36Sopenharmony_ci return status; 90062306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &lock->lk_old_lock_seqid) < 0) 90162306a36Sopenharmony_ci return nfserr_bad_xdr; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci return nfs_ok; 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_cistatic __be32 90762306a36Sopenharmony_cinfsd4_decode_locker4(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci if (xdr_stream_decode_bool(argp->xdr, &lock->lk_is_new) < 0) 91062306a36Sopenharmony_ci return nfserr_bad_xdr; 91162306a36Sopenharmony_ci if (lock->lk_is_new) 91262306a36Sopenharmony_ci return nfsd4_decode_open_to_lock_owner4(argp, lock); 91362306a36Sopenharmony_ci return nfsd4_decode_exist_lock_owner4(argp, lock); 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic __be32 91762306a36Sopenharmony_cinfsd4_decode_lock(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 91862306a36Sopenharmony_ci{ 91962306a36Sopenharmony_ci struct nfsd4_lock *lock = &u->lock; 92062306a36Sopenharmony_ci memset(lock, 0, sizeof(*lock)); 92162306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &lock->lk_type) < 0) 92262306a36Sopenharmony_ci return nfserr_bad_xdr; 92362306a36Sopenharmony_ci if ((lock->lk_type < NFS4_READ_LT) || (lock->lk_type > NFS4_WRITEW_LT)) 92462306a36Sopenharmony_ci return nfserr_bad_xdr; 92562306a36Sopenharmony_ci if (xdr_stream_decode_bool(argp->xdr, &lock->lk_reclaim) < 0) 92662306a36Sopenharmony_ci return nfserr_bad_xdr; 92762306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &lock->lk_offset) < 0) 92862306a36Sopenharmony_ci return nfserr_bad_xdr; 92962306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &lock->lk_length) < 0) 93062306a36Sopenharmony_ci return nfserr_bad_xdr; 93162306a36Sopenharmony_ci return nfsd4_decode_locker4(argp, lock); 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic __be32 93562306a36Sopenharmony_cinfsd4_decode_lockt(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci struct nfsd4_lockt *lockt = &u->lockt; 93862306a36Sopenharmony_ci memset(lockt, 0, sizeof(*lockt)); 93962306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &lockt->lt_type) < 0) 94062306a36Sopenharmony_ci return nfserr_bad_xdr; 94162306a36Sopenharmony_ci if ((lockt->lt_type < NFS4_READ_LT) || (lockt->lt_type > NFS4_WRITEW_LT)) 94262306a36Sopenharmony_ci return nfserr_bad_xdr; 94362306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &lockt->lt_offset) < 0) 94462306a36Sopenharmony_ci return nfserr_bad_xdr; 94562306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &lockt->lt_length) < 0) 94662306a36Sopenharmony_ci return nfserr_bad_xdr; 94762306a36Sopenharmony_ci return nfsd4_decode_state_owner4(argp, &lockt->lt_clientid, 94862306a36Sopenharmony_ci &lockt->lt_owner); 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic __be32 95262306a36Sopenharmony_cinfsd4_decode_locku(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci struct nfsd4_locku *locku = &u->locku; 95562306a36Sopenharmony_ci __be32 status; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &locku->lu_type) < 0) 95862306a36Sopenharmony_ci return nfserr_bad_xdr; 95962306a36Sopenharmony_ci if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT)) 96062306a36Sopenharmony_ci return nfserr_bad_xdr; 96162306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &locku->lu_seqid) < 0) 96262306a36Sopenharmony_ci return nfserr_bad_xdr; 96362306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &locku->lu_stateid); 96462306a36Sopenharmony_ci if (status) 96562306a36Sopenharmony_ci return status; 96662306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &locku->lu_offset) < 0) 96762306a36Sopenharmony_ci return nfserr_bad_xdr; 96862306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &locku->lu_length) < 0) 96962306a36Sopenharmony_ci return nfserr_bad_xdr; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci return nfs_ok; 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_cistatic __be32 97562306a36Sopenharmony_cinfsd4_decode_lookup(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci struct nfsd4_lookup *lookup = &u->lookup; 97862306a36Sopenharmony_ci return nfsd4_decode_component4(argp, &lookup->lo_name, &lookup->lo_len); 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cistatic __be32 98262306a36Sopenharmony_cinfsd4_decode_createhow4(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci __be32 status; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &open->op_createmode) < 0) 98762306a36Sopenharmony_ci return nfserr_bad_xdr; 98862306a36Sopenharmony_ci switch (open->op_createmode) { 98962306a36Sopenharmony_ci case NFS4_CREATE_UNCHECKED: 99062306a36Sopenharmony_ci case NFS4_CREATE_GUARDED: 99162306a36Sopenharmony_ci status = nfsd4_decode_fattr4(argp, open->op_bmval, 99262306a36Sopenharmony_ci ARRAY_SIZE(open->op_bmval), 99362306a36Sopenharmony_ci &open->op_iattr, &open->op_acl, 99462306a36Sopenharmony_ci &open->op_label, &open->op_umask); 99562306a36Sopenharmony_ci if (status) 99662306a36Sopenharmony_ci return status; 99762306a36Sopenharmony_ci break; 99862306a36Sopenharmony_ci case NFS4_CREATE_EXCLUSIVE: 99962306a36Sopenharmony_ci status = nfsd4_decode_verifier4(argp, &open->op_verf); 100062306a36Sopenharmony_ci if (status) 100162306a36Sopenharmony_ci return status; 100262306a36Sopenharmony_ci break; 100362306a36Sopenharmony_ci case NFS4_CREATE_EXCLUSIVE4_1: 100462306a36Sopenharmony_ci if (argp->minorversion < 1) 100562306a36Sopenharmony_ci return nfserr_bad_xdr; 100662306a36Sopenharmony_ci status = nfsd4_decode_verifier4(argp, &open->op_verf); 100762306a36Sopenharmony_ci if (status) 100862306a36Sopenharmony_ci return status; 100962306a36Sopenharmony_ci status = nfsd4_decode_fattr4(argp, open->op_bmval, 101062306a36Sopenharmony_ci ARRAY_SIZE(open->op_bmval), 101162306a36Sopenharmony_ci &open->op_iattr, &open->op_acl, 101262306a36Sopenharmony_ci &open->op_label, &open->op_umask); 101362306a36Sopenharmony_ci if (status) 101462306a36Sopenharmony_ci return status; 101562306a36Sopenharmony_ci break; 101662306a36Sopenharmony_ci default: 101762306a36Sopenharmony_ci return nfserr_bad_xdr; 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci return nfs_ok; 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_cistatic __be32 102462306a36Sopenharmony_cinfsd4_decode_openflag4(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci __be32 status; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &open->op_create) < 0) 102962306a36Sopenharmony_ci return nfserr_bad_xdr; 103062306a36Sopenharmony_ci switch (open->op_create) { 103162306a36Sopenharmony_ci case NFS4_OPEN_NOCREATE: 103262306a36Sopenharmony_ci break; 103362306a36Sopenharmony_ci case NFS4_OPEN_CREATE: 103462306a36Sopenharmony_ci status = nfsd4_decode_createhow4(argp, open); 103562306a36Sopenharmony_ci if (status) 103662306a36Sopenharmony_ci return status; 103762306a36Sopenharmony_ci break; 103862306a36Sopenharmony_ci default: 103962306a36Sopenharmony_ci return nfserr_bad_xdr; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci return nfs_ok; 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *share_access, u32 *deleg_want, u32 *deleg_when) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci u32 w; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &w) < 0) 105062306a36Sopenharmony_ci return nfserr_bad_xdr; 105162306a36Sopenharmony_ci *share_access = w & NFS4_SHARE_ACCESS_MASK; 105262306a36Sopenharmony_ci *deleg_want = w & NFS4_SHARE_WANT_MASK; 105362306a36Sopenharmony_ci if (deleg_when) 105462306a36Sopenharmony_ci *deleg_when = w & NFS4_SHARE_WHEN_MASK; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci switch (w & NFS4_SHARE_ACCESS_MASK) { 105762306a36Sopenharmony_ci case NFS4_SHARE_ACCESS_READ: 105862306a36Sopenharmony_ci case NFS4_SHARE_ACCESS_WRITE: 105962306a36Sopenharmony_ci case NFS4_SHARE_ACCESS_BOTH: 106062306a36Sopenharmony_ci break; 106162306a36Sopenharmony_ci default: 106262306a36Sopenharmony_ci return nfserr_bad_xdr; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci w &= ~NFS4_SHARE_ACCESS_MASK; 106562306a36Sopenharmony_ci if (!w) 106662306a36Sopenharmony_ci return nfs_ok; 106762306a36Sopenharmony_ci if (!argp->minorversion) 106862306a36Sopenharmony_ci return nfserr_bad_xdr; 106962306a36Sopenharmony_ci switch (w & NFS4_SHARE_WANT_MASK) { 107062306a36Sopenharmony_ci case NFS4_SHARE_WANT_NO_PREFERENCE: 107162306a36Sopenharmony_ci case NFS4_SHARE_WANT_READ_DELEG: 107262306a36Sopenharmony_ci case NFS4_SHARE_WANT_WRITE_DELEG: 107362306a36Sopenharmony_ci case NFS4_SHARE_WANT_ANY_DELEG: 107462306a36Sopenharmony_ci case NFS4_SHARE_WANT_NO_DELEG: 107562306a36Sopenharmony_ci case NFS4_SHARE_WANT_CANCEL: 107662306a36Sopenharmony_ci break; 107762306a36Sopenharmony_ci default: 107862306a36Sopenharmony_ci return nfserr_bad_xdr; 107962306a36Sopenharmony_ci } 108062306a36Sopenharmony_ci w &= ~NFS4_SHARE_WANT_MASK; 108162306a36Sopenharmony_ci if (!w) 108262306a36Sopenharmony_ci return nfs_ok; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci if (!deleg_when) /* open_downgrade */ 108562306a36Sopenharmony_ci return nfserr_inval; 108662306a36Sopenharmony_ci switch (w) { 108762306a36Sopenharmony_ci case NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL: 108862306a36Sopenharmony_ci case NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED: 108962306a36Sopenharmony_ci case (NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL | 109062306a36Sopenharmony_ci NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED): 109162306a36Sopenharmony_ci return nfs_ok; 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci return nfserr_bad_xdr; 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_cistatic __be32 nfsd4_decode_share_deny(struct nfsd4_compoundargs *argp, u32 *x) 109762306a36Sopenharmony_ci{ 109862306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, x) < 0) 109962306a36Sopenharmony_ci return nfserr_bad_xdr; 110062306a36Sopenharmony_ci /* Note: unlike access bits, deny bits may be zero. */ 110162306a36Sopenharmony_ci if (*x & ~NFS4_SHARE_DENY_BOTH) 110262306a36Sopenharmony_ci return nfserr_bad_xdr; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci return nfs_ok; 110562306a36Sopenharmony_ci} 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_cistatic __be32 110862306a36Sopenharmony_cinfsd4_decode_open_claim4(struct nfsd4_compoundargs *argp, 110962306a36Sopenharmony_ci struct nfsd4_open *open) 111062306a36Sopenharmony_ci{ 111162306a36Sopenharmony_ci __be32 status; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &open->op_claim_type) < 0) 111462306a36Sopenharmony_ci return nfserr_bad_xdr; 111562306a36Sopenharmony_ci switch (open->op_claim_type) { 111662306a36Sopenharmony_ci case NFS4_OPEN_CLAIM_NULL: 111762306a36Sopenharmony_ci case NFS4_OPEN_CLAIM_DELEGATE_PREV: 111862306a36Sopenharmony_ci status = nfsd4_decode_component4(argp, &open->op_fname, 111962306a36Sopenharmony_ci &open->op_fnamelen); 112062306a36Sopenharmony_ci if (status) 112162306a36Sopenharmony_ci return status; 112262306a36Sopenharmony_ci break; 112362306a36Sopenharmony_ci case NFS4_OPEN_CLAIM_PREVIOUS: 112462306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &open->op_delegate_type) < 0) 112562306a36Sopenharmony_ci return nfserr_bad_xdr; 112662306a36Sopenharmony_ci break; 112762306a36Sopenharmony_ci case NFS4_OPEN_CLAIM_DELEGATE_CUR: 112862306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &open->op_delegate_stateid); 112962306a36Sopenharmony_ci if (status) 113062306a36Sopenharmony_ci return status; 113162306a36Sopenharmony_ci status = nfsd4_decode_component4(argp, &open->op_fname, 113262306a36Sopenharmony_ci &open->op_fnamelen); 113362306a36Sopenharmony_ci if (status) 113462306a36Sopenharmony_ci return status; 113562306a36Sopenharmony_ci break; 113662306a36Sopenharmony_ci case NFS4_OPEN_CLAIM_FH: 113762306a36Sopenharmony_ci case NFS4_OPEN_CLAIM_DELEG_PREV_FH: 113862306a36Sopenharmony_ci if (argp->minorversion < 1) 113962306a36Sopenharmony_ci return nfserr_bad_xdr; 114062306a36Sopenharmony_ci /* void */ 114162306a36Sopenharmony_ci break; 114262306a36Sopenharmony_ci case NFS4_OPEN_CLAIM_DELEG_CUR_FH: 114362306a36Sopenharmony_ci if (argp->minorversion < 1) 114462306a36Sopenharmony_ci return nfserr_bad_xdr; 114562306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &open->op_delegate_stateid); 114662306a36Sopenharmony_ci if (status) 114762306a36Sopenharmony_ci return status; 114862306a36Sopenharmony_ci break; 114962306a36Sopenharmony_ci default: 115062306a36Sopenharmony_ci return nfserr_bad_xdr; 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci return nfs_ok; 115462306a36Sopenharmony_ci} 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_cistatic __be32 115762306a36Sopenharmony_cinfsd4_decode_open(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci struct nfsd4_open *open = &u->open; 116062306a36Sopenharmony_ci __be32 status; 116162306a36Sopenharmony_ci u32 dummy; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci memset(open, 0, sizeof(*open)); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &open->op_seqid) < 0) 116662306a36Sopenharmony_ci return nfserr_bad_xdr; 116762306a36Sopenharmony_ci /* deleg_want is ignored */ 116862306a36Sopenharmony_ci status = nfsd4_decode_share_access(argp, &open->op_share_access, 116962306a36Sopenharmony_ci &open->op_deleg_want, &dummy); 117062306a36Sopenharmony_ci if (status) 117162306a36Sopenharmony_ci return status; 117262306a36Sopenharmony_ci status = nfsd4_decode_share_deny(argp, &open->op_share_deny); 117362306a36Sopenharmony_ci if (status) 117462306a36Sopenharmony_ci return status; 117562306a36Sopenharmony_ci status = nfsd4_decode_state_owner4(argp, &open->op_clientid, 117662306a36Sopenharmony_ci &open->op_owner); 117762306a36Sopenharmony_ci if (status) 117862306a36Sopenharmony_ci return status; 117962306a36Sopenharmony_ci status = nfsd4_decode_openflag4(argp, open); 118062306a36Sopenharmony_ci if (status) 118162306a36Sopenharmony_ci return status; 118262306a36Sopenharmony_ci return nfsd4_decode_open_claim4(argp, open); 118362306a36Sopenharmony_ci} 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_cistatic __be32 118662306a36Sopenharmony_cinfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, 118762306a36Sopenharmony_ci union nfsd4_op_u *u) 118862306a36Sopenharmony_ci{ 118962306a36Sopenharmony_ci struct nfsd4_open_confirm *open_conf = &u->open_confirm; 119062306a36Sopenharmony_ci __be32 status; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci if (argp->minorversion >= 1) 119362306a36Sopenharmony_ci return nfserr_notsupp; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &open_conf->oc_req_stateid); 119662306a36Sopenharmony_ci if (status) 119762306a36Sopenharmony_ci return status; 119862306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &open_conf->oc_seqid) < 0) 119962306a36Sopenharmony_ci return nfserr_bad_xdr; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci memset(&open_conf->oc_resp_stateid, 0, 120262306a36Sopenharmony_ci sizeof(open_conf->oc_resp_stateid)); 120362306a36Sopenharmony_ci return nfs_ok; 120462306a36Sopenharmony_ci} 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_cistatic __be32 120762306a36Sopenharmony_cinfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, 120862306a36Sopenharmony_ci union nfsd4_op_u *u) 120962306a36Sopenharmony_ci{ 121062306a36Sopenharmony_ci struct nfsd4_open_downgrade *open_down = &u->open_downgrade; 121162306a36Sopenharmony_ci __be32 status; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci memset(open_down, 0, sizeof(*open_down)); 121462306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &open_down->od_stateid); 121562306a36Sopenharmony_ci if (status) 121662306a36Sopenharmony_ci return status; 121762306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &open_down->od_seqid) < 0) 121862306a36Sopenharmony_ci return nfserr_bad_xdr; 121962306a36Sopenharmony_ci /* deleg_want is ignored */ 122062306a36Sopenharmony_ci status = nfsd4_decode_share_access(argp, &open_down->od_share_access, 122162306a36Sopenharmony_ci &open_down->od_deleg_want, NULL); 122262306a36Sopenharmony_ci if (status) 122362306a36Sopenharmony_ci return status; 122462306a36Sopenharmony_ci return nfsd4_decode_share_deny(argp, &open_down->od_share_deny); 122562306a36Sopenharmony_ci} 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_cistatic __be32 122862306a36Sopenharmony_cinfsd4_decode_putfh(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 122962306a36Sopenharmony_ci{ 123062306a36Sopenharmony_ci struct nfsd4_putfh *putfh = &u->putfh; 123162306a36Sopenharmony_ci __be32 *p; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &putfh->pf_fhlen) < 0) 123462306a36Sopenharmony_ci return nfserr_bad_xdr; 123562306a36Sopenharmony_ci if (putfh->pf_fhlen > NFS4_FHSIZE) 123662306a36Sopenharmony_ci return nfserr_bad_xdr; 123762306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, putfh->pf_fhlen); 123862306a36Sopenharmony_ci if (!p) 123962306a36Sopenharmony_ci return nfserr_bad_xdr; 124062306a36Sopenharmony_ci putfh->pf_fhval = svcxdr_savemem(argp, p, putfh->pf_fhlen); 124162306a36Sopenharmony_ci if (!putfh->pf_fhval) 124262306a36Sopenharmony_ci return nfserr_jukebox; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci putfh->no_verify = false; 124562306a36Sopenharmony_ci return nfs_ok; 124662306a36Sopenharmony_ci} 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_cistatic __be32 124962306a36Sopenharmony_cinfsd4_decode_putpubfh(struct nfsd4_compoundargs *argp, union nfsd4_op_u *p) 125062306a36Sopenharmony_ci{ 125162306a36Sopenharmony_ci if (argp->minorversion == 0) 125262306a36Sopenharmony_ci return nfs_ok; 125362306a36Sopenharmony_ci return nfserr_notsupp; 125462306a36Sopenharmony_ci} 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_cistatic __be32 125762306a36Sopenharmony_cinfsd4_decode_read(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 125862306a36Sopenharmony_ci{ 125962306a36Sopenharmony_ci struct nfsd4_read *read = &u->read; 126062306a36Sopenharmony_ci __be32 status; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci memset(read, 0, sizeof(*read)); 126362306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &read->rd_stateid); 126462306a36Sopenharmony_ci if (status) 126562306a36Sopenharmony_ci return status; 126662306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &read->rd_offset) < 0) 126762306a36Sopenharmony_ci return nfserr_bad_xdr; 126862306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &read->rd_length) < 0) 126962306a36Sopenharmony_ci return nfserr_bad_xdr; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci return nfs_ok; 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic __be32 127562306a36Sopenharmony_cinfsd4_decode_readdir(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 127662306a36Sopenharmony_ci{ 127762306a36Sopenharmony_ci struct nfsd4_readdir *readdir = &u->readdir; 127862306a36Sopenharmony_ci __be32 status; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci memset(readdir, 0, sizeof(*readdir)); 128162306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &readdir->rd_cookie) < 0) 128262306a36Sopenharmony_ci return nfserr_bad_xdr; 128362306a36Sopenharmony_ci status = nfsd4_decode_verifier4(argp, &readdir->rd_verf); 128462306a36Sopenharmony_ci if (status) 128562306a36Sopenharmony_ci return status; 128662306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &readdir->rd_dircount) < 0) 128762306a36Sopenharmony_ci return nfserr_bad_xdr; 128862306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &readdir->rd_maxcount) < 0) 128962306a36Sopenharmony_ci return nfserr_bad_xdr; 129062306a36Sopenharmony_ci if (xdr_stream_decode_uint32_array(argp->xdr, readdir->rd_bmval, 129162306a36Sopenharmony_ci ARRAY_SIZE(readdir->rd_bmval)) < 0) 129262306a36Sopenharmony_ci return nfserr_bad_xdr; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci return nfs_ok; 129562306a36Sopenharmony_ci} 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_cistatic __be32 129862306a36Sopenharmony_cinfsd4_decode_remove(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci struct nfsd4_remove *remove = &u->remove; 130162306a36Sopenharmony_ci memset(&remove->rm_cinfo, 0, sizeof(remove->rm_cinfo)); 130262306a36Sopenharmony_ci return nfsd4_decode_component4(argp, &remove->rm_name, &remove->rm_namelen); 130362306a36Sopenharmony_ci} 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_cistatic __be32 130662306a36Sopenharmony_cinfsd4_decode_rename(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 130762306a36Sopenharmony_ci{ 130862306a36Sopenharmony_ci struct nfsd4_rename *rename = &u->rename; 130962306a36Sopenharmony_ci __be32 status; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci memset(rename, 0, sizeof(*rename)); 131262306a36Sopenharmony_ci status = nfsd4_decode_component4(argp, &rename->rn_sname, &rename->rn_snamelen); 131362306a36Sopenharmony_ci if (status) 131462306a36Sopenharmony_ci return status; 131562306a36Sopenharmony_ci return nfsd4_decode_component4(argp, &rename->rn_tname, &rename->rn_tnamelen); 131662306a36Sopenharmony_ci} 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_cistatic __be32 131962306a36Sopenharmony_cinfsd4_decode_renew(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 132062306a36Sopenharmony_ci{ 132162306a36Sopenharmony_ci clientid_t *clientid = &u->renew; 132262306a36Sopenharmony_ci return nfsd4_decode_clientid4(argp, clientid); 132362306a36Sopenharmony_ci} 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_cistatic __be32 132662306a36Sopenharmony_cinfsd4_decode_secinfo(struct nfsd4_compoundargs *argp, 132762306a36Sopenharmony_ci union nfsd4_op_u *u) 132862306a36Sopenharmony_ci{ 132962306a36Sopenharmony_ci struct nfsd4_secinfo *secinfo = &u->secinfo; 133062306a36Sopenharmony_ci secinfo->si_exp = NULL; 133162306a36Sopenharmony_ci return nfsd4_decode_component4(argp, &secinfo->si_name, &secinfo->si_namelen); 133262306a36Sopenharmony_ci} 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_cistatic __be32 133562306a36Sopenharmony_cinfsd4_decode_setattr(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 133662306a36Sopenharmony_ci{ 133762306a36Sopenharmony_ci struct nfsd4_setattr *setattr = &u->setattr; 133862306a36Sopenharmony_ci __be32 status; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci memset(setattr, 0, sizeof(*setattr)); 134162306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &setattr->sa_stateid); 134262306a36Sopenharmony_ci if (status) 134362306a36Sopenharmony_ci return status; 134462306a36Sopenharmony_ci return nfsd4_decode_fattr4(argp, setattr->sa_bmval, 134562306a36Sopenharmony_ci ARRAY_SIZE(setattr->sa_bmval), 134662306a36Sopenharmony_ci &setattr->sa_iattr, &setattr->sa_acl, 134762306a36Sopenharmony_ci &setattr->sa_label, NULL); 134862306a36Sopenharmony_ci} 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_cistatic __be32 135162306a36Sopenharmony_cinfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 135262306a36Sopenharmony_ci{ 135362306a36Sopenharmony_ci struct nfsd4_setclientid *setclientid = &u->setclientid; 135462306a36Sopenharmony_ci __be32 *p, status; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci memset(setclientid, 0, sizeof(*setclientid)); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci if (argp->minorversion >= 1) 135962306a36Sopenharmony_ci return nfserr_notsupp; 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci status = nfsd4_decode_verifier4(argp, &setclientid->se_verf); 136262306a36Sopenharmony_ci if (status) 136362306a36Sopenharmony_ci return status; 136462306a36Sopenharmony_ci status = nfsd4_decode_opaque(argp, &setclientid->se_name); 136562306a36Sopenharmony_ci if (status) 136662306a36Sopenharmony_ci return status; 136762306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &setclientid->se_callback_prog) < 0) 136862306a36Sopenharmony_ci return nfserr_bad_xdr; 136962306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &setclientid->se_callback_netid_len) < 0) 137062306a36Sopenharmony_ci return nfserr_bad_xdr; 137162306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, setclientid->se_callback_netid_len); 137262306a36Sopenharmony_ci if (!p) 137362306a36Sopenharmony_ci return nfserr_bad_xdr; 137462306a36Sopenharmony_ci setclientid->se_callback_netid_val = svcxdr_savemem(argp, p, 137562306a36Sopenharmony_ci setclientid->se_callback_netid_len); 137662306a36Sopenharmony_ci if (!setclientid->se_callback_netid_val) 137762306a36Sopenharmony_ci return nfserr_jukebox; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &setclientid->se_callback_addr_len) < 0) 138062306a36Sopenharmony_ci return nfserr_bad_xdr; 138162306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, setclientid->se_callback_addr_len); 138262306a36Sopenharmony_ci if (!p) 138362306a36Sopenharmony_ci return nfserr_bad_xdr; 138462306a36Sopenharmony_ci setclientid->se_callback_addr_val = svcxdr_savemem(argp, p, 138562306a36Sopenharmony_ci setclientid->se_callback_addr_len); 138662306a36Sopenharmony_ci if (!setclientid->se_callback_addr_val) 138762306a36Sopenharmony_ci return nfserr_jukebox; 138862306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &setclientid->se_callback_ident) < 0) 138962306a36Sopenharmony_ci return nfserr_bad_xdr; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci return nfs_ok; 139262306a36Sopenharmony_ci} 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_cistatic __be32 139562306a36Sopenharmony_cinfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, 139662306a36Sopenharmony_ci union nfsd4_op_u *u) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci struct nfsd4_setclientid_confirm *scd_c = &u->setclientid_confirm; 139962306a36Sopenharmony_ci __be32 status; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci if (argp->minorversion >= 1) 140262306a36Sopenharmony_ci return nfserr_notsupp; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci status = nfsd4_decode_clientid4(argp, &scd_c->sc_clientid); 140562306a36Sopenharmony_ci if (status) 140662306a36Sopenharmony_ci return status; 140762306a36Sopenharmony_ci return nfsd4_decode_verifier4(argp, &scd_c->sc_confirm); 140862306a36Sopenharmony_ci} 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci/* Also used for NVERIFY */ 141162306a36Sopenharmony_cistatic __be32 141262306a36Sopenharmony_cinfsd4_decode_verify(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 141362306a36Sopenharmony_ci{ 141462306a36Sopenharmony_ci struct nfsd4_verify *verify = &u->verify; 141562306a36Sopenharmony_ci __be32 *p, status; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci memset(verify, 0, sizeof(*verify)); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci status = nfsd4_decode_bitmap4(argp, verify->ve_bmval, 142062306a36Sopenharmony_ci ARRAY_SIZE(verify->ve_bmval)); 142162306a36Sopenharmony_ci if (status) 142262306a36Sopenharmony_ci return status; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci /* For convenience's sake, we compare raw xdr'd attributes in 142562306a36Sopenharmony_ci * nfsd4_proc_verify */ 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &verify->ve_attrlen) < 0) 142862306a36Sopenharmony_ci return nfserr_bad_xdr; 142962306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, verify->ve_attrlen); 143062306a36Sopenharmony_ci if (!p) 143162306a36Sopenharmony_ci return nfserr_bad_xdr; 143262306a36Sopenharmony_ci verify->ve_attrval = svcxdr_savemem(argp, p, verify->ve_attrlen); 143362306a36Sopenharmony_ci if (!verify->ve_attrval) 143462306a36Sopenharmony_ci return nfserr_jukebox; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci return nfs_ok; 143762306a36Sopenharmony_ci} 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_cistatic __be32 144062306a36Sopenharmony_cinfsd4_decode_write(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 144162306a36Sopenharmony_ci{ 144262306a36Sopenharmony_ci struct nfsd4_write *write = &u->write; 144362306a36Sopenharmony_ci __be32 status; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &write->wr_stateid); 144662306a36Sopenharmony_ci if (status) 144762306a36Sopenharmony_ci return status; 144862306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &write->wr_offset) < 0) 144962306a36Sopenharmony_ci return nfserr_bad_xdr; 145062306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &write->wr_stable_how) < 0) 145162306a36Sopenharmony_ci return nfserr_bad_xdr; 145262306a36Sopenharmony_ci if (write->wr_stable_how > NFS_FILE_SYNC) 145362306a36Sopenharmony_ci return nfserr_bad_xdr; 145462306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &write->wr_buflen) < 0) 145562306a36Sopenharmony_ci return nfserr_bad_xdr; 145662306a36Sopenharmony_ci if (!xdr_stream_subsegment(argp->xdr, &write->wr_payload, write->wr_buflen)) 145762306a36Sopenharmony_ci return nfserr_bad_xdr; 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci write->wr_bytes_written = 0; 146062306a36Sopenharmony_ci write->wr_how_written = 0; 146162306a36Sopenharmony_ci memset(&write->wr_verifier, 0, sizeof(write->wr_verifier)); 146262306a36Sopenharmony_ci return nfs_ok; 146362306a36Sopenharmony_ci} 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_cistatic __be32 146662306a36Sopenharmony_cinfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, 146762306a36Sopenharmony_ci union nfsd4_op_u *u) 146862306a36Sopenharmony_ci{ 146962306a36Sopenharmony_ci struct nfsd4_release_lockowner *rlockowner = &u->release_lockowner; 147062306a36Sopenharmony_ci __be32 status; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci if (argp->minorversion >= 1) 147362306a36Sopenharmony_ci return nfserr_notsupp; 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci status = nfsd4_decode_state_owner4(argp, &rlockowner->rl_clientid, 147662306a36Sopenharmony_ci &rlockowner->rl_owner); 147762306a36Sopenharmony_ci if (status) 147862306a36Sopenharmony_ci return status; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci if (argp->minorversion && !zero_clientid(&rlockowner->rl_clientid)) 148162306a36Sopenharmony_ci return nfserr_inval; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci return nfs_ok; 148462306a36Sopenharmony_ci} 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_cistatic __be32 nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs *argp, 148762306a36Sopenharmony_ci union nfsd4_op_u *u) 148862306a36Sopenharmony_ci{ 148962306a36Sopenharmony_ci struct nfsd4_backchannel_ctl *bc = &u->backchannel_ctl; 149062306a36Sopenharmony_ci memset(bc, 0, sizeof(*bc)); 149162306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &bc->bc_cb_program) < 0) 149262306a36Sopenharmony_ci return nfserr_bad_xdr; 149362306a36Sopenharmony_ci return nfsd4_decode_cb_sec(argp, &bc->bc_cb_sec); 149462306a36Sopenharmony_ci} 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_cistatic __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp, 149762306a36Sopenharmony_ci union nfsd4_op_u *u) 149862306a36Sopenharmony_ci{ 149962306a36Sopenharmony_ci struct nfsd4_bind_conn_to_session *bcts = &u->bind_conn_to_session; 150062306a36Sopenharmony_ci u32 use_conn_in_rdma_mode; 150162306a36Sopenharmony_ci __be32 status; 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci memset(bcts, 0, sizeof(*bcts)); 150462306a36Sopenharmony_ci status = nfsd4_decode_sessionid4(argp, &bcts->sessionid); 150562306a36Sopenharmony_ci if (status) 150662306a36Sopenharmony_ci return status; 150762306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &bcts->dir) < 0) 150862306a36Sopenharmony_ci return nfserr_bad_xdr; 150962306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &use_conn_in_rdma_mode) < 0) 151062306a36Sopenharmony_ci return nfserr_bad_xdr; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci return nfs_ok; 151362306a36Sopenharmony_ci} 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_cistatic __be32 151662306a36Sopenharmony_cinfsd4_decode_state_protect_ops(struct nfsd4_compoundargs *argp, 151762306a36Sopenharmony_ci struct nfsd4_exchange_id *exid) 151862306a36Sopenharmony_ci{ 151962306a36Sopenharmony_ci __be32 status; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci status = nfsd4_decode_bitmap4(argp, exid->spo_must_enforce, 152262306a36Sopenharmony_ci ARRAY_SIZE(exid->spo_must_enforce)); 152362306a36Sopenharmony_ci if (status) 152462306a36Sopenharmony_ci return nfserr_bad_xdr; 152562306a36Sopenharmony_ci status = nfsd4_decode_bitmap4(argp, exid->spo_must_allow, 152662306a36Sopenharmony_ci ARRAY_SIZE(exid->spo_must_allow)); 152762306a36Sopenharmony_ci if (status) 152862306a36Sopenharmony_ci return nfserr_bad_xdr; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci return nfs_ok; 153162306a36Sopenharmony_ci} 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci/* 153462306a36Sopenharmony_ci * This implementation currently does not support SP4_SSV. 153562306a36Sopenharmony_ci * This decoder simply skips over these arguments. 153662306a36Sopenharmony_ci */ 153762306a36Sopenharmony_cistatic noinline __be32 153862306a36Sopenharmony_cinfsd4_decode_ssv_sp_parms(struct nfsd4_compoundargs *argp, 153962306a36Sopenharmony_ci struct nfsd4_exchange_id *exid) 154062306a36Sopenharmony_ci{ 154162306a36Sopenharmony_ci u32 count, window, num_gss_handles; 154262306a36Sopenharmony_ci __be32 status; 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci /* ssp_ops */ 154562306a36Sopenharmony_ci status = nfsd4_decode_state_protect_ops(argp, exid); 154662306a36Sopenharmony_ci if (status) 154762306a36Sopenharmony_ci return status; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci /* ssp_hash_algs<> */ 155062306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &count) < 0) 155162306a36Sopenharmony_ci return nfserr_bad_xdr; 155262306a36Sopenharmony_ci while (count--) { 155362306a36Sopenharmony_ci status = nfsd4_decode_ignored_string(argp, 0); 155462306a36Sopenharmony_ci if (status) 155562306a36Sopenharmony_ci return status; 155662306a36Sopenharmony_ci } 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci /* ssp_encr_algs<> */ 155962306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &count) < 0) 156062306a36Sopenharmony_ci return nfserr_bad_xdr; 156162306a36Sopenharmony_ci while (count--) { 156262306a36Sopenharmony_ci status = nfsd4_decode_ignored_string(argp, 0); 156362306a36Sopenharmony_ci if (status) 156462306a36Sopenharmony_ci return status; 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &window) < 0) 156862306a36Sopenharmony_ci return nfserr_bad_xdr; 156962306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &num_gss_handles) < 0) 157062306a36Sopenharmony_ci return nfserr_bad_xdr; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci return nfs_ok; 157362306a36Sopenharmony_ci} 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_cistatic __be32 157662306a36Sopenharmony_cinfsd4_decode_state_protect4_a(struct nfsd4_compoundargs *argp, 157762306a36Sopenharmony_ci struct nfsd4_exchange_id *exid) 157862306a36Sopenharmony_ci{ 157962306a36Sopenharmony_ci __be32 status; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &exid->spa_how) < 0) 158262306a36Sopenharmony_ci return nfserr_bad_xdr; 158362306a36Sopenharmony_ci switch (exid->spa_how) { 158462306a36Sopenharmony_ci case SP4_NONE: 158562306a36Sopenharmony_ci break; 158662306a36Sopenharmony_ci case SP4_MACH_CRED: 158762306a36Sopenharmony_ci status = nfsd4_decode_state_protect_ops(argp, exid); 158862306a36Sopenharmony_ci if (status) 158962306a36Sopenharmony_ci return status; 159062306a36Sopenharmony_ci break; 159162306a36Sopenharmony_ci case SP4_SSV: 159262306a36Sopenharmony_ci status = nfsd4_decode_ssv_sp_parms(argp, exid); 159362306a36Sopenharmony_ci if (status) 159462306a36Sopenharmony_ci return status; 159562306a36Sopenharmony_ci break; 159662306a36Sopenharmony_ci default: 159762306a36Sopenharmony_ci return nfserr_bad_xdr; 159862306a36Sopenharmony_ci } 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci return nfs_ok; 160162306a36Sopenharmony_ci} 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_cistatic __be32 160462306a36Sopenharmony_cinfsd4_decode_nfs_impl_id4(struct nfsd4_compoundargs *argp, 160562306a36Sopenharmony_ci struct nfsd4_exchange_id *exid) 160662306a36Sopenharmony_ci{ 160762306a36Sopenharmony_ci __be32 status; 160862306a36Sopenharmony_ci u32 count; 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &count) < 0) 161162306a36Sopenharmony_ci return nfserr_bad_xdr; 161262306a36Sopenharmony_ci switch (count) { 161362306a36Sopenharmony_ci case 0: 161462306a36Sopenharmony_ci break; 161562306a36Sopenharmony_ci case 1: 161662306a36Sopenharmony_ci /* Note that RFC 8881 places no length limit on 161762306a36Sopenharmony_ci * nii_domain, but this implementation permits no 161862306a36Sopenharmony_ci * more than NFS4_OPAQUE_LIMIT bytes */ 161962306a36Sopenharmony_ci status = nfsd4_decode_opaque(argp, &exid->nii_domain); 162062306a36Sopenharmony_ci if (status) 162162306a36Sopenharmony_ci return status; 162262306a36Sopenharmony_ci /* Note that RFC 8881 places no length limit on 162362306a36Sopenharmony_ci * nii_name, but this implementation permits no 162462306a36Sopenharmony_ci * more than NFS4_OPAQUE_LIMIT bytes */ 162562306a36Sopenharmony_ci status = nfsd4_decode_opaque(argp, &exid->nii_name); 162662306a36Sopenharmony_ci if (status) 162762306a36Sopenharmony_ci return status; 162862306a36Sopenharmony_ci status = nfsd4_decode_nfstime4(argp, &exid->nii_time); 162962306a36Sopenharmony_ci if (status) 163062306a36Sopenharmony_ci return status; 163162306a36Sopenharmony_ci break; 163262306a36Sopenharmony_ci default: 163362306a36Sopenharmony_ci return nfserr_bad_xdr; 163462306a36Sopenharmony_ci } 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci return nfs_ok; 163762306a36Sopenharmony_ci} 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_cistatic __be32 164062306a36Sopenharmony_cinfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp, 164162306a36Sopenharmony_ci union nfsd4_op_u *u) 164262306a36Sopenharmony_ci{ 164362306a36Sopenharmony_ci struct nfsd4_exchange_id *exid = &u->exchange_id; 164462306a36Sopenharmony_ci __be32 status; 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci memset(exid, 0, sizeof(*exid)); 164762306a36Sopenharmony_ci status = nfsd4_decode_verifier4(argp, &exid->verifier); 164862306a36Sopenharmony_ci if (status) 164962306a36Sopenharmony_ci return status; 165062306a36Sopenharmony_ci status = nfsd4_decode_opaque(argp, &exid->clname); 165162306a36Sopenharmony_ci if (status) 165262306a36Sopenharmony_ci return status; 165362306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &exid->flags) < 0) 165462306a36Sopenharmony_ci return nfserr_bad_xdr; 165562306a36Sopenharmony_ci status = nfsd4_decode_state_protect4_a(argp, exid); 165662306a36Sopenharmony_ci if (status) 165762306a36Sopenharmony_ci return status; 165862306a36Sopenharmony_ci return nfsd4_decode_nfs_impl_id4(argp, exid); 165962306a36Sopenharmony_ci} 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_cistatic __be32 166262306a36Sopenharmony_cinfsd4_decode_channel_attrs4(struct nfsd4_compoundargs *argp, 166362306a36Sopenharmony_ci struct nfsd4_channel_attrs *ca) 166462306a36Sopenharmony_ci{ 166562306a36Sopenharmony_ci __be32 *p; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, XDR_UNIT * 7); 166862306a36Sopenharmony_ci if (!p) 166962306a36Sopenharmony_ci return nfserr_bad_xdr; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci /* headerpadsz is ignored */ 167262306a36Sopenharmony_ci p++; 167362306a36Sopenharmony_ci ca->maxreq_sz = be32_to_cpup(p++); 167462306a36Sopenharmony_ci ca->maxresp_sz = be32_to_cpup(p++); 167562306a36Sopenharmony_ci ca->maxresp_cached = be32_to_cpup(p++); 167662306a36Sopenharmony_ci ca->maxops = be32_to_cpup(p++); 167762306a36Sopenharmony_ci ca->maxreqs = be32_to_cpup(p++); 167862306a36Sopenharmony_ci ca->nr_rdma_attrs = be32_to_cpup(p); 167962306a36Sopenharmony_ci switch (ca->nr_rdma_attrs) { 168062306a36Sopenharmony_ci case 0: 168162306a36Sopenharmony_ci break; 168262306a36Sopenharmony_ci case 1: 168362306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &ca->rdma_attrs) < 0) 168462306a36Sopenharmony_ci return nfserr_bad_xdr; 168562306a36Sopenharmony_ci break; 168662306a36Sopenharmony_ci default: 168762306a36Sopenharmony_ci return nfserr_bad_xdr; 168862306a36Sopenharmony_ci } 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci return nfs_ok; 169162306a36Sopenharmony_ci} 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_cistatic __be32 169462306a36Sopenharmony_cinfsd4_decode_create_session(struct nfsd4_compoundargs *argp, 169562306a36Sopenharmony_ci union nfsd4_op_u *u) 169662306a36Sopenharmony_ci{ 169762306a36Sopenharmony_ci struct nfsd4_create_session *sess = &u->create_session; 169862306a36Sopenharmony_ci __be32 status; 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci memset(sess, 0, sizeof(*sess)); 170162306a36Sopenharmony_ci status = nfsd4_decode_clientid4(argp, &sess->clientid); 170262306a36Sopenharmony_ci if (status) 170362306a36Sopenharmony_ci return status; 170462306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &sess->seqid) < 0) 170562306a36Sopenharmony_ci return nfserr_bad_xdr; 170662306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &sess->flags) < 0) 170762306a36Sopenharmony_ci return nfserr_bad_xdr; 170862306a36Sopenharmony_ci status = nfsd4_decode_channel_attrs4(argp, &sess->fore_channel); 170962306a36Sopenharmony_ci if (status) 171062306a36Sopenharmony_ci return status; 171162306a36Sopenharmony_ci status = nfsd4_decode_channel_attrs4(argp, &sess->back_channel); 171262306a36Sopenharmony_ci if (status) 171362306a36Sopenharmony_ci return status; 171462306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &sess->callback_prog) < 0) 171562306a36Sopenharmony_ci return nfserr_bad_xdr; 171662306a36Sopenharmony_ci return nfsd4_decode_cb_sec(argp, &sess->cb_sec); 171762306a36Sopenharmony_ci} 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_cistatic __be32 172062306a36Sopenharmony_cinfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp, 172162306a36Sopenharmony_ci union nfsd4_op_u *u) 172262306a36Sopenharmony_ci{ 172362306a36Sopenharmony_ci struct nfsd4_destroy_session *destroy_session = &u->destroy_session; 172462306a36Sopenharmony_ci return nfsd4_decode_sessionid4(argp, &destroy_session->sessionid); 172562306a36Sopenharmony_ci} 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_cistatic __be32 172862306a36Sopenharmony_cinfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp, 172962306a36Sopenharmony_ci union nfsd4_op_u *u) 173062306a36Sopenharmony_ci{ 173162306a36Sopenharmony_ci struct nfsd4_free_stateid *free_stateid = &u->free_stateid; 173262306a36Sopenharmony_ci return nfsd4_decode_stateid4(argp, &free_stateid->fr_stateid); 173362306a36Sopenharmony_ci} 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci#ifdef CONFIG_NFSD_PNFS 173662306a36Sopenharmony_cistatic __be32 173762306a36Sopenharmony_cinfsd4_decode_getdeviceinfo(struct nfsd4_compoundargs *argp, 173862306a36Sopenharmony_ci union nfsd4_op_u *u) 173962306a36Sopenharmony_ci{ 174062306a36Sopenharmony_ci struct nfsd4_getdeviceinfo *gdev = &u->getdeviceinfo; 174162306a36Sopenharmony_ci __be32 status; 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci memset(gdev, 0, sizeof(*gdev)); 174462306a36Sopenharmony_ci status = nfsd4_decode_deviceid4(argp, &gdev->gd_devid); 174562306a36Sopenharmony_ci if (status) 174662306a36Sopenharmony_ci return status; 174762306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &gdev->gd_layout_type) < 0) 174862306a36Sopenharmony_ci return nfserr_bad_xdr; 174962306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &gdev->gd_maxcount) < 0) 175062306a36Sopenharmony_ci return nfserr_bad_xdr; 175162306a36Sopenharmony_ci if (xdr_stream_decode_uint32_array(argp->xdr, 175262306a36Sopenharmony_ci &gdev->gd_notify_types, 1) < 0) 175362306a36Sopenharmony_ci return nfserr_bad_xdr; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci return nfs_ok; 175662306a36Sopenharmony_ci} 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_cistatic __be32 175962306a36Sopenharmony_cinfsd4_decode_layoutcommit(struct nfsd4_compoundargs *argp, 176062306a36Sopenharmony_ci union nfsd4_op_u *u) 176162306a36Sopenharmony_ci{ 176262306a36Sopenharmony_ci struct nfsd4_layoutcommit *lcp = &u->layoutcommit; 176362306a36Sopenharmony_ci __be32 *p, status; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci memset(lcp, 0, sizeof(*lcp)); 176662306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &lcp->lc_seg.offset) < 0) 176762306a36Sopenharmony_ci return nfserr_bad_xdr; 176862306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &lcp->lc_seg.length) < 0) 176962306a36Sopenharmony_ci return nfserr_bad_xdr; 177062306a36Sopenharmony_ci if (xdr_stream_decode_bool(argp->xdr, &lcp->lc_reclaim) < 0) 177162306a36Sopenharmony_ci return nfserr_bad_xdr; 177262306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &lcp->lc_sid); 177362306a36Sopenharmony_ci if (status) 177462306a36Sopenharmony_ci return status; 177562306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &lcp->lc_newoffset) < 0) 177662306a36Sopenharmony_ci return nfserr_bad_xdr; 177762306a36Sopenharmony_ci if (lcp->lc_newoffset) { 177862306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &lcp->lc_last_wr) < 0) 177962306a36Sopenharmony_ci return nfserr_bad_xdr; 178062306a36Sopenharmony_ci } else 178162306a36Sopenharmony_ci lcp->lc_last_wr = 0; 178262306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, XDR_UNIT); 178362306a36Sopenharmony_ci if (!p) 178462306a36Sopenharmony_ci return nfserr_bad_xdr; 178562306a36Sopenharmony_ci if (xdr_item_is_present(p)) { 178662306a36Sopenharmony_ci status = nfsd4_decode_nfstime4(argp, &lcp->lc_mtime); 178762306a36Sopenharmony_ci if (status) 178862306a36Sopenharmony_ci return status; 178962306a36Sopenharmony_ci } else { 179062306a36Sopenharmony_ci lcp->lc_mtime.tv_nsec = UTIME_NOW; 179162306a36Sopenharmony_ci } 179262306a36Sopenharmony_ci return nfsd4_decode_layoutupdate4(argp, lcp); 179362306a36Sopenharmony_ci} 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_cistatic __be32 179662306a36Sopenharmony_cinfsd4_decode_layoutget(struct nfsd4_compoundargs *argp, 179762306a36Sopenharmony_ci union nfsd4_op_u *u) 179862306a36Sopenharmony_ci{ 179962306a36Sopenharmony_ci struct nfsd4_layoutget *lgp = &u->layoutget; 180062306a36Sopenharmony_ci __be32 status; 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci memset(lgp, 0, sizeof(*lgp)); 180362306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_signal) < 0) 180462306a36Sopenharmony_ci return nfserr_bad_xdr; 180562306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_layout_type) < 0) 180662306a36Sopenharmony_ci return nfserr_bad_xdr; 180762306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_seg.iomode) < 0) 180862306a36Sopenharmony_ci return nfserr_bad_xdr; 180962306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &lgp->lg_seg.offset) < 0) 181062306a36Sopenharmony_ci return nfserr_bad_xdr; 181162306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &lgp->lg_seg.length) < 0) 181262306a36Sopenharmony_ci return nfserr_bad_xdr; 181362306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &lgp->lg_minlength) < 0) 181462306a36Sopenharmony_ci return nfserr_bad_xdr; 181562306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &lgp->lg_sid); 181662306a36Sopenharmony_ci if (status) 181762306a36Sopenharmony_ci return status; 181862306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_maxcount) < 0) 181962306a36Sopenharmony_ci return nfserr_bad_xdr; 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci return nfs_ok; 182262306a36Sopenharmony_ci} 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_cistatic __be32 182562306a36Sopenharmony_cinfsd4_decode_layoutreturn(struct nfsd4_compoundargs *argp, 182662306a36Sopenharmony_ci union nfsd4_op_u *u) 182762306a36Sopenharmony_ci{ 182862306a36Sopenharmony_ci struct nfsd4_layoutreturn *lrp = &u->layoutreturn; 182962306a36Sopenharmony_ci memset(lrp, 0, sizeof(*lrp)); 183062306a36Sopenharmony_ci if (xdr_stream_decode_bool(argp->xdr, &lrp->lr_reclaim) < 0) 183162306a36Sopenharmony_ci return nfserr_bad_xdr; 183262306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &lrp->lr_layout_type) < 0) 183362306a36Sopenharmony_ci return nfserr_bad_xdr; 183462306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &lrp->lr_seg.iomode) < 0) 183562306a36Sopenharmony_ci return nfserr_bad_xdr; 183662306a36Sopenharmony_ci return nfsd4_decode_layoutreturn4(argp, lrp); 183762306a36Sopenharmony_ci} 183862306a36Sopenharmony_ci#endif /* CONFIG_NFSD_PNFS */ 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_cistatic __be32 nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp, 184162306a36Sopenharmony_ci union nfsd4_op_u *u) 184262306a36Sopenharmony_ci{ 184362306a36Sopenharmony_ci struct nfsd4_secinfo_no_name *sin = &u->secinfo_no_name; 184462306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &sin->sin_style) < 0) 184562306a36Sopenharmony_ci return nfserr_bad_xdr; 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci sin->sin_exp = NULL; 184862306a36Sopenharmony_ci return nfs_ok; 184962306a36Sopenharmony_ci} 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_cistatic __be32 185262306a36Sopenharmony_cinfsd4_decode_sequence(struct nfsd4_compoundargs *argp, 185362306a36Sopenharmony_ci union nfsd4_op_u *u) 185462306a36Sopenharmony_ci{ 185562306a36Sopenharmony_ci struct nfsd4_sequence *seq = &u->sequence; 185662306a36Sopenharmony_ci __be32 *p, status; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci status = nfsd4_decode_sessionid4(argp, &seq->sessionid); 185962306a36Sopenharmony_ci if (status) 186062306a36Sopenharmony_ci return status; 186162306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, XDR_UNIT * 4); 186262306a36Sopenharmony_ci if (!p) 186362306a36Sopenharmony_ci return nfserr_bad_xdr; 186462306a36Sopenharmony_ci seq->seqid = be32_to_cpup(p++); 186562306a36Sopenharmony_ci seq->slotid = be32_to_cpup(p++); 186662306a36Sopenharmony_ci seq->maxslots = be32_to_cpup(p++); 186762306a36Sopenharmony_ci seq->cachethis = be32_to_cpup(p); 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci seq->status_flags = 0; 187062306a36Sopenharmony_ci return nfs_ok; 187162306a36Sopenharmony_ci} 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_cistatic __be32 187462306a36Sopenharmony_cinfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, 187562306a36Sopenharmony_ci union nfsd4_op_u *u) 187662306a36Sopenharmony_ci{ 187762306a36Sopenharmony_ci struct nfsd4_test_stateid *test_stateid = &u->test_stateid; 187862306a36Sopenharmony_ci struct nfsd4_test_stateid_id *stateid; 187962306a36Sopenharmony_ci __be32 status; 188062306a36Sopenharmony_ci u32 i; 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci memset(test_stateid, 0, sizeof(*test_stateid)); 188362306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &test_stateid->ts_num_ids) < 0) 188462306a36Sopenharmony_ci return nfserr_bad_xdr; 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci INIT_LIST_HEAD(&test_stateid->ts_stateid_list); 188762306a36Sopenharmony_ci for (i = 0; i < test_stateid->ts_num_ids; i++) { 188862306a36Sopenharmony_ci stateid = svcxdr_tmpalloc(argp, sizeof(*stateid)); 188962306a36Sopenharmony_ci if (!stateid) 189062306a36Sopenharmony_ci return nfserr_jukebox; 189162306a36Sopenharmony_ci INIT_LIST_HEAD(&stateid->ts_id_list); 189262306a36Sopenharmony_ci list_add_tail(&stateid->ts_id_list, &test_stateid->ts_stateid_list); 189362306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &stateid->ts_id_stateid); 189462306a36Sopenharmony_ci if (status) 189562306a36Sopenharmony_ci return status; 189662306a36Sopenharmony_ci } 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci return nfs_ok; 189962306a36Sopenharmony_ci} 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_cistatic __be32 nfsd4_decode_destroy_clientid(struct nfsd4_compoundargs *argp, 190262306a36Sopenharmony_ci union nfsd4_op_u *u) 190362306a36Sopenharmony_ci{ 190462306a36Sopenharmony_ci struct nfsd4_destroy_clientid *dc = &u->destroy_clientid; 190562306a36Sopenharmony_ci return nfsd4_decode_clientid4(argp, &dc->clientid); 190662306a36Sopenharmony_ci} 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_cistatic __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, 190962306a36Sopenharmony_ci union nfsd4_op_u *u) 191062306a36Sopenharmony_ci{ 191162306a36Sopenharmony_ci struct nfsd4_reclaim_complete *rc = &u->reclaim_complete; 191262306a36Sopenharmony_ci if (xdr_stream_decode_bool(argp->xdr, &rc->rca_one_fs) < 0) 191362306a36Sopenharmony_ci return nfserr_bad_xdr; 191462306a36Sopenharmony_ci return nfs_ok; 191562306a36Sopenharmony_ci} 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_cistatic __be32 191862306a36Sopenharmony_cinfsd4_decode_fallocate(struct nfsd4_compoundargs *argp, 191962306a36Sopenharmony_ci union nfsd4_op_u *u) 192062306a36Sopenharmony_ci{ 192162306a36Sopenharmony_ci struct nfsd4_fallocate *fallocate = &u->allocate; 192262306a36Sopenharmony_ci __be32 status; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &fallocate->falloc_stateid); 192562306a36Sopenharmony_ci if (status) 192662306a36Sopenharmony_ci return status; 192762306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &fallocate->falloc_offset) < 0) 192862306a36Sopenharmony_ci return nfserr_bad_xdr; 192962306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &fallocate->falloc_length) < 0) 193062306a36Sopenharmony_ci return nfserr_bad_xdr; 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci return nfs_ok; 193362306a36Sopenharmony_ci} 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_cistatic __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp, 193662306a36Sopenharmony_ci struct nl4_server *ns) 193762306a36Sopenharmony_ci{ 193862306a36Sopenharmony_ci struct nfs42_netaddr *naddr; 193962306a36Sopenharmony_ci __be32 *p; 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &ns->nl4_type) < 0) 194262306a36Sopenharmony_ci return nfserr_bad_xdr; 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci /* currently support for 1 inter-server source server */ 194562306a36Sopenharmony_ci switch (ns->nl4_type) { 194662306a36Sopenharmony_ci case NL4_NETADDR: 194762306a36Sopenharmony_ci naddr = &ns->u.nl4_addr; 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &naddr->netid_len) < 0) 195062306a36Sopenharmony_ci return nfserr_bad_xdr; 195162306a36Sopenharmony_ci if (naddr->netid_len > RPCBIND_MAXNETIDLEN) 195262306a36Sopenharmony_ci return nfserr_bad_xdr; 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, naddr->netid_len); 195562306a36Sopenharmony_ci if (!p) 195662306a36Sopenharmony_ci return nfserr_bad_xdr; 195762306a36Sopenharmony_ci memcpy(naddr->netid, p, naddr->netid_len); 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &naddr->addr_len) < 0) 196062306a36Sopenharmony_ci return nfserr_bad_xdr; 196162306a36Sopenharmony_ci if (naddr->addr_len > RPCBIND_MAXUADDRLEN) 196262306a36Sopenharmony_ci return nfserr_bad_xdr; 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, naddr->addr_len); 196562306a36Sopenharmony_ci if (!p) 196662306a36Sopenharmony_ci return nfserr_bad_xdr; 196762306a36Sopenharmony_ci memcpy(naddr->addr, p, naddr->addr_len); 196862306a36Sopenharmony_ci break; 196962306a36Sopenharmony_ci default: 197062306a36Sopenharmony_ci return nfserr_bad_xdr; 197162306a36Sopenharmony_ci } 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_ci return nfs_ok; 197462306a36Sopenharmony_ci} 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_cistatic __be32 197762306a36Sopenharmony_cinfsd4_decode_copy(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 197862306a36Sopenharmony_ci{ 197962306a36Sopenharmony_ci struct nfsd4_copy *copy = &u->copy; 198062306a36Sopenharmony_ci u32 consecutive, i, count, sync; 198162306a36Sopenharmony_ci struct nl4_server *ns_dummy; 198262306a36Sopenharmony_ci __be32 status; 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci memset(copy, 0, sizeof(*copy)); 198562306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, ©->cp_src_stateid); 198662306a36Sopenharmony_ci if (status) 198762306a36Sopenharmony_ci return status; 198862306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, ©->cp_dst_stateid); 198962306a36Sopenharmony_ci if (status) 199062306a36Sopenharmony_ci return status; 199162306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, ©->cp_src_pos) < 0) 199262306a36Sopenharmony_ci return nfserr_bad_xdr; 199362306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, ©->cp_dst_pos) < 0) 199462306a36Sopenharmony_ci return nfserr_bad_xdr; 199562306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, ©->cp_count) < 0) 199662306a36Sopenharmony_ci return nfserr_bad_xdr; 199762306a36Sopenharmony_ci /* ca_consecutive: we always do consecutive copies */ 199862306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &consecutive) < 0) 199962306a36Sopenharmony_ci return nfserr_bad_xdr; 200062306a36Sopenharmony_ci if (xdr_stream_decode_bool(argp->xdr, &sync) < 0) 200162306a36Sopenharmony_ci return nfserr_bad_xdr; 200262306a36Sopenharmony_ci nfsd4_copy_set_sync(copy, sync); 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &count) < 0) 200562306a36Sopenharmony_ci return nfserr_bad_xdr; 200662306a36Sopenharmony_ci copy->cp_src = svcxdr_tmpalloc(argp, sizeof(*copy->cp_src)); 200762306a36Sopenharmony_ci if (copy->cp_src == NULL) 200862306a36Sopenharmony_ci return nfserr_jukebox; 200962306a36Sopenharmony_ci if (count == 0) { /* intra-server copy */ 201062306a36Sopenharmony_ci __set_bit(NFSD4_COPY_F_INTRA, ©->cp_flags); 201162306a36Sopenharmony_ci return nfs_ok; 201262306a36Sopenharmony_ci } 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci /* decode all the supplied server addresses but use only the first */ 201562306a36Sopenharmony_ci status = nfsd4_decode_nl4_server(argp, copy->cp_src); 201662306a36Sopenharmony_ci if (status) 201762306a36Sopenharmony_ci return status; 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci ns_dummy = kmalloc(sizeof(struct nl4_server), GFP_KERNEL); 202062306a36Sopenharmony_ci if (ns_dummy == NULL) 202162306a36Sopenharmony_ci return nfserr_jukebox; 202262306a36Sopenharmony_ci for (i = 0; i < count - 1; i++) { 202362306a36Sopenharmony_ci status = nfsd4_decode_nl4_server(argp, ns_dummy); 202462306a36Sopenharmony_ci if (status) { 202562306a36Sopenharmony_ci kfree(ns_dummy); 202662306a36Sopenharmony_ci return status; 202762306a36Sopenharmony_ci } 202862306a36Sopenharmony_ci } 202962306a36Sopenharmony_ci kfree(ns_dummy); 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci return nfs_ok; 203262306a36Sopenharmony_ci} 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_cistatic __be32 203562306a36Sopenharmony_cinfsd4_decode_copy_notify(struct nfsd4_compoundargs *argp, 203662306a36Sopenharmony_ci union nfsd4_op_u *u) 203762306a36Sopenharmony_ci{ 203862306a36Sopenharmony_ci struct nfsd4_copy_notify *cn = &u->copy_notify; 203962306a36Sopenharmony_ci __be32 status; 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci memset(cn, 0, sizeof(*cn)); 204262306a36Sopenharmony_ci cn->cpn_src = svcxdr_tmpalloc(argp, sizeof(*cn->cpn_src)); 204362306a36Sopenharmony_ci if (cn->cpn_src == NULL) 204462306a36Sopenharmony_ci return nfserr_jukebox; 204562306a36Sopenharmony_ci cn->cpn_dst = svcxdr_tmpalloc(argp, sizeof(*cn->cpn_dst)); 204662306a36Sopenharmony_ci if (cn->cpn_dst == NULL) 204762306a36Sopenharmony_ci return nfserr_jukebox; 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &cn->cpn_src_stateid); 205062306a36Sopenharmony_ci if (status) 205162306a36Sopenharmony_ci return status; 205262306a36Sopenharmony_ci return nfsd4_decode_nl4_server(argp, cn->cpn_dst); 205362306a36Sopenharmony_ci} 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_cistatic __be32 205662306a36Sopenharmony_cinfsd4_decode_offload_status(struct nfsd4_compoundargs *argp, 205762306a36Sopenharmony_ci union nfsd4_op_u *u) 205862306a36Sopenharmony_ci{ 205962306a36Sopenharmony_ci struct nfsd4_offload_status *os = &u->offload_status; 206062306a36Sopenharmony_ci os->count = 0; 206162306a36Sopenharmony_ci os->status = 0; 206262306a36Sopenharmony_ci return nfsd4_decode_stateid4(argp, &os->stateid); 206362306a36Sopenharmony_ci} 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_cistatic __be32 206662306a36Sopenharmony_cinfsd4_decode_seek(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 206762306a36Sopenharmony_ci{ 206862306a36Sopenharmony_ci struct nfsd4_seek *seek = &u->seek; 206962306a36Sopenharmony_ci __be32 status; 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &seek->seek_stateid); 207262306a36Sopenharmony_ci if (status) 207362306a36Sopenharmony_ci return status; 207462306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &seek->seek_offset) < 0) 207562306a36Sopenharmony_ci return nfserr_bad_xdr; 207662306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &seek->seek_whence) < 0) 207762306a36Sopenharmony_ci return nfserr_bad_xdr; 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci seek->seek_eof = 0; 208062306a36Sopenharmony_ci seek->seek_pos = 0; 208162306a36Sopenharmony_ci return nfs_ok; 208262306a36Sopenharmony_ci} 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_cistatic __be32 208562306a36Sopenharmony_cinfsd4_decode_clone(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) 208662306a36Sopenharmony_ci{ 208762306a36Sopenharmony_ci struct nfsd4_clone *clone = &u->clone; 208862306a36Sopenharmony_ci __be32 status; 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &clone->cl_src_stateid); 209162306a36Sopenharmony_ci if (status) 209262306a36Sopenharmony_ci return status; 209362306a36Sopenharmony_ci status = nfsd4_decode_stateid4(argp, &clone->cl_dst_stateid); 209462306a36Sopenharmony_ci if (status) 209562306a36Sopenharmony_ci return status; 209662306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &clone->cl_src_pos) < 0) 209762306a36Sopenharmony_ci return nfserr_bad_xdr; 209862306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &clone->cl_dst_pos) < 0) 209962306a36Sopenharmony_ci return nfserr_bad_xdr; 210062306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &clone->cl_count) < 0) 210162306a36Sopenharmony_ci return nfserr_bad_xdr; 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci return nfs_ok; 210462306a36Sopenharmony_ci} 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci/* 210762306a36Sopenharmony_ci * XDR data that is more than PAGE_SIZE in size is normally part of a 210862306a36Sopenharmony_ci * read or write. However, the size of extended attributes is limited 210962306a36Sopenharmony_ci * by the maximum request size, and then further limited by the underlying 211062306a36Sopenharmony_ci * filesystem limits. This can exceed PAGE_SIZE (currently, XATTR_SIZE_MAX 211162306a36Sopenharmony_ci * is 64k). Since there is no kvec- or page-based interface to xattrs, 211262306a36Sopenharmony_ci * and we're not dealing with contiguous pages, we need to do some copying. 211362306a36Sopenharmony_ci */ 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci/* 211662306a36Sopenharmony_ci * Decode data into buffer. 211762306a36Sopenharmony_ci */ 211862306a36Sopenharmony_cistatic __be32 211962306a36Sopenharmony_cinfsd4_vbuf_from_vector(struct nfsd4_compoundargs *argp, struct xdr_buf *xdr, 212062306a36Sopenharmony_ci char **bufp, u32 buflen) 212162306a36Sopenharmony_ci{ 212262306a36Sopenharmony_ci struct page **pages = xdr->pages; 212362306a36Sopenharmony_ci struct kvec *head = xdr->head; 212462306a36Sopenharmony_ci char *tmp, *dp; 212562306a36Sopenharmony_ci u32 len; 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci if (buflen <= head->iov_len) { 212862306a36Sopenharmony_ci /* 212962306a36Sopenharmony_ci * We're in luck, the head has enough space. Just return 213062306a36Sopenharmony_ci * the head, no need for copying. 213162306a36Sopenharmony_ci */ 213262306a36Sopenharmony_ci *bufp = head->iov_base; 213362306a36Sopenharmony_ci return 0; 213462306a36Sopenharmony_ci } 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci tmp = svcxdr_tmpalloc(argp, buflen); 213762306a36Sopenharmony_ci if (tmp == NULL) 213862306a36Sopenharmony_ci return nfserr_jukebox; 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci dp = tmp; 214162306a36Sopenharmony_ci memcpy(dp, head->iov_base, head->iov_len); 214262306a36Sopenharmony_ci buflen -= head->iov_len; 214362306a36Sopenharmony_ci dp += head->iov_len; 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci while (buflen > 0) { 214662306a36Sopenharmony_ci len = min_t(u32, buflen, PAGE_SIZE); 214762306a36Sopenharmony_ci memcpy(dp, page_address(*pages), len); 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci buflen -= len; 215062306a36Sopenharmony_ci dp += len; 215162306a36Sopenharmony_ci pages++; 215262306a36Sopenharmony_ci } 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci *bufp = tmp; 215562306a36Sopenharmony_ci return 0; 215662306a36Sopenharmony_ci} 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci/* 215962306a36Sopenharmony_ci * Get a user extended attribute name from the XDR buffer. 216062306a36Sopenharmony_ci * It will not have the "user." prefix, so prepend it. 216162306a36Sopenharmony_ci * Lastly, check for nul characters in the name. 216262306a36Sopenharmony_ci */ 216362306a36Sopenharmony_cistatic __be32 216462306a36Sopenharmony_cinfsd4_decode_xattr_name(struct nfsd4_compoundargs *argp, char **namep) 216562306a36Sopenharmony_ci{ 216662306a36Sopenharmony_ci char *name, *sp, *dp; 216762306a36Sopenharmony_ci u32 namelen, cnt; 216862306a36Sopenharmony_ci __be32 *p; 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &namelen) < 0) 217162306a36Sopenharmony_ci return nfserr_bad_xdr; 217262306a36Sopenharmony_ci if (namelen > (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)) 217362306a36Sopenharmony_ci return nfserr_nametoolong; 217462306a36Sopenharmony_ci if (namelen == 0) 217562306a36Sopenharmony_ci return nfserr_bad_xdr; 217662306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, namelen); 217762306a36Sopenharmony_ci if (!p) 217862306a36Sopenharmony_ci return nfserr_bad_xdr; 217962306a36Sopenharmony_ci name = svcxdr_tmpalloc(argp, namelen + XATTR_USER_PREFIX_LEN + 1); 218062306a36Sopenharmony_ci if (!name) 218162306a36Sopenharmony_ci return nfserr_jukebox; 218262306a36Sopenharmony_ci memcpy(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci /* 218562306a36Sopenharmony_ci * Copy the extended attribute name over while checking for 0 218662306a36Sopenharmony_ci * characters. 218762306a36Sopenharmony_ci */ 218862306a36Sopenharmony_ci sp = (char *)p; 218962306a36Sopenharmony_ci dp = name + XATTR_USER_PREFIX_LEN; 219062306a36Sopenharmony_ci cnt = namelen; 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci while (cnt-- > 0) { 219362306a36Sopenharmony_ci if (*sp == '\0') 219462306a36Sopenharmony_ci return nfserr_bad_xdr; 219562306a36Sopenharmony_ci *dp++ = *sp++; 219662306a36Sopenharmony_ci } 219762306a36Sopenharmony_ci *dp = '\0'; 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci *namep = name; 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci return nfs_ok; 220262306a36Sopenharmony_ci} 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci/* 220562306a36Sopenharmony_ci * A GETXATTR op request comes without a length specifier. We just set the 220662306a36Sopenharmony_ci * maximum length for the reply based on XATTR_SIZE_MAX and the maximum 220762306a36Sopenharmony_ci * channel reply size. nfsd_getxattr will probe the length of the xattr, 220862306a36Sopenharmony_ci * check it against getxa_len, and allocate + return the value. 220962306a36Sopenharmony_ci */ 221062306a36Sopenharmony_cistatic __be32 221162306a36Sopenharmony_cinfsd4_decode_getxattr(struct nfsd4_compoundargs *argp, 221262306a36Sopenharmony_ci union nfsd4_op_u *u) 221362306a36Sopenharmony_ci{ 221462306a36Sopenharmony_ci struct nfsd4_getxattr *getxattr = &u->getxattr; 221562306a36Sopenharmony_ci __be32 status; 221662306a36Sopenharmony_ci u32 maxcount; 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci memset(getxattr, 0, sizeof(*getxattr)); 221962306a36Sopenharmony_ci status = nfsd4_decode_xattr_name(argp, &getxattr->getxa_name); 222062306a36Sopenharmony_ci if (status) 222162306a36Sopenharmony_ci return status; 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci maxcount = svc_max_payload(argp->rqstp); 222462306a36Sopenharmony_ci maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount); 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci getxattr->getxa_len = maxcount; 222762306a36Sopenharmony_ci return nfs_ok; 222862306a36Sopenharmony_ci} 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_cistatic __be32 223162306a36Sopenharmony_cinfsd4_decode_setxattr(struct nfsd4_compoundargs *argp, 223262306a36Sopenharmony_ci union nfsd4_op_u *u) 223362306a36Sopenharmony_ci{ 223462306a36Sopenharmony_ci struct nfsd4_setxattr *setxattr = &u->setxattr; 223562306a36Sopenharmony_ci u32 flags, maxcount, size; 223662306a36Sopenharmony_ci __be32 status; 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci memset(setxattr, 0, sizeof(*setxattr)); 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &flags) < 0) 224162306a36Sopenharmony_ci return nfserr_bad_xdr; 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci if (flags > SETXATTR4_REPLACE) 224462306a36Sopenharmony_ci return nfserr_inval; 224562306a36Sopenharmony_ci setxattr->setxa_flags = flags; 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci status = nfsd4_decode_xattr_name(argp, &setxattr->setxa_name); 224862306a36Sopenharmony_ci if (status) 224962306a36Sopenharmony_ci return status; 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci maxcount = svc_max_payload(argp->rqstp); 225262306a36Sopenharmony_ci maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount); 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &size) < 0) 225562306a36Sopenharmony_ci return nfserr_bad_xdr; 225662306a36Sopenharmony_ci if (size > maxcount) 225762306a36Sopenharmony_ci return nfserr_xattr2big; 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci setxattr->setxa_len = size; 226062306a36Sopenharmony_ci if (size > 0) { 226162306a36Sopenharmony_ci struct xdr_buf payload; 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci if (!xdr_stream_subsegment(argp->xdr, &payload, size)) 226462306a36Sopenharmony_ci return nfserr_bad_xdr; 226562306a36Sopenharmony_ci status = nfsd4_vbuf_from_vector(argp, &payload, 226662306a36Sopenharmony_ci &setxattr->setxa_buf, size); 226762306a36Sopenharmony_ci } 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_ci return nfs_ok; 227062306a36Sopenharmony_ci} 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_cistatic __be32 227362306a36Sopenharmony_cinfsd4_decode_listxattrs(struct nfsd4_compoundargs *argp, 227462306a36Sopenharmony_ci union nfsd4_op_u *u) 227562306a36Sopenharmony_ci{ 227662306a36Sopenharmony_ci struct nfsd4_listxattrs *listxattrs = &u->listxattrs; 227762306a36Sopenharmony_ci u32 maxcount; 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_ci memset(listxattrs, 0, sizeof(*listxattrs)); 228062306a36Sopenharmony_ci 228162306a36Sopenharmony_ci if (xdr_stream_decode_u64(argp->xdr, &listxattrs->lsxa_cookie) < 0) 228262306a36Sopenharmony_ci return nfserr_bad_xdr; 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_ci /* 228562306a36Sopenharmony_ci * If the cookie is too large to have even one user.x attribute 228662306a36Sopenharmony_ci * plus trailing '\0' left in a maximum size buffer, it's invalid. 228762306a36Sopenharmony_ci */ 228862306a36Sopenharmony_ci if (listxattrs->lsxa_cookie >= 228962306a36Sopenharmony_ci (XATTR_LIST_MAX / (XATTR_USER_PREFIX_LEN + 2))) 229062306a36Sopenharmony_ci return nfserr_badcookie; 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &maxcount) < 0) 229362306a36Sopenharmony_ci return nfserr_bad_xdr; 229462306a36Sopenharmony_ci if (maxcount < 8) 229562306a36Sopenharmony_ci /* Always need at least 2 words (length and one character) */ 229662306a36Sopenharmony_ci return nfserr_inval; 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci maxcount = min(maxcount, svc_max_payload(argp->rqstp)); 229962306a36Sopenharmony_ci listxattrs->lsxa_maxcount = maxcount; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci return nfs_ok; 230262306a36Sopenharmony_ci} 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_cistatic __be32 230562306a36Sopenharmony_cinfsd4_decode_removexattr(struct nfsd4_compoundargs *argp, 230662306a36Sopenharmony_ci union nfsd4_op_u *u) 230762306a36Sopenharmony_ci{ 230862306a36Sopenharmony_ci struct nfsd4_removexattr *removexattr = &u->removexattr; 230962306a36Sopenharmony_ci memset(removexattr, 0, sizeof(*removexattr)); 231062306a36Sopenharmony_ci return nfsd4_decode_xattr_name(argp, &removexattr->rmxa_name); 231162306a36Sopenharmony_ci} 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_cistatic __be32 231462306a36Sopenharmony_cinfsd4_decode_noop(struct nfsd4_compoundargs *argp, union nfsd4_op_u *p) 231562306a36Sopenharmony_ci{ 231662306a36Sopenharmony_ci return nfs_ok; 231762306a36Sopenharmony_ci} 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_cistatic __be32 232062306a36Sopenharmony_cinfsd4_decode_notsupp(struct nfsd4_compoundargs *argp, union nfsd4_op_u *p) 232162306a36Sopenharmony_ci{ 232262306a36Sopenharmony_ci return nfserr_notsupp; 232362306a36Sopenharmony_ci} 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_citypedef __be32(*nfsd4_dec)(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u); 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_cistatic const nfsd4_dec nfsd4_dec_ops[] = { 232862306a36Sopenharmony_ci [OP_ACCESS] = nfsd4_decode_access, 232962306a36Sopenharmony_ci [OP_CLOSE] = nfsd4_decode_close, 233062306a36Sopenharmony_ci [OP_COMMIT] = nfsd4_decode_commit, 233162306a36Sopenharmony_ci [OP_CREATE] = nfsd4_decode_create, 233262306a36Sopenharmony_ci [OP_DELEGPURGE] = nfsd4_decode_notsupp, 233362306a36Sopenharmony_ci [OP_DELEGRETURN] = nfsd4_decode_delegreturn, 233462306a36Sopenharmony_ci [OP_GETATTR] = nfsd4_decode_getattr, 233562306a36Sopenharmony_ci [OP_GETFH] = nfsd4_decode_noop, 233662306a36Sopenharmony_ci [OP_LINK] = nfsd4_decode_link, 233762306a36Sopenharmony_ci [OP_LOCK] = nfsd4_decode_lock, 233862306a36Sopenharmony_ci [OP_LOCKT] = nfsd4_decode_lockt, 233962306a36Sopenharmony_ci [OP_LOCKU] = nfsd4_decode_locku, 234062306a36Sopenharmony_ci [OP_LOOKUP] = nfsd4_decode_lookup, 234162306a36Sopenharmony_ci [OP_LOOKUPP] = nfsd4_decode_noop, 234262306a36Sopenharmony_ci [OP_NVERIFY] = nfsd4_decode_verify, 234362306a36Sopenharmony_ci [OP_OPEN] = nfsd4_decode_open, 234462306a36Sopenharmony_ci [OP_OPENATTR] = nfsd4_decode_notsupp, 234562306a36Sopenharmony_ci [OP_OPEN_CONFIRM] = nfsd4_decode_open_confirm, 234662306a36Sopenharmony_ci [OP_OPEN_DOWNGRADE] = nfsd4_decode_open_downgrade, 234762306a36Sopenharmony_ci [OP_PUTFH] = nfsd4_decode_putfh, 234862306a36Sopenharmony_ci [OP_PUTPUBFH] = nfsd4_decode_putpubfh, 234962306a36Sopenharmony_ci [OP_PUTROOTFH] = nfsd4_decode_noop, 235062306a36Sopenharmony_ci [OP_READ] = nfsd4_decode_read, 235162306a36Sopenharmony_ci [OP_READDIR] = nfsd4_decode_readdir, 235262306a36Sopenharmony_ci [OP_READLINK] = nfsd4_decode_noop, 235362306a36Sopenharmony_ci [OP_REMOVE] = nfsd4_decode_remove, 235462306a36Sopenharmony_ci [OP_RENAME] = nfsd4_decode_rename, 235562306a36Sopenharmony_ci [OP_RENEW] = nfsd4_decode_renew, 235662306a36Sopenharmony_ci [OP_RESTOREFH] = nfsd4_decode_noop, 235762306a36Sopenharmony_ci [OP_SAVEFH] = nfsd4_decode_noop, 235862306a36Sopenharmony_ci [OP_SECINFO] = nfsd4_decode_secinfo, 235962306a36Sopenharmony_ci [OP_SETATTR] = nfsd4_decode_setattr, 236062306a36Sopenharmony_ci [OP_SETCLIENTID] = nfsd4_decode_setclientid, 236162306a36Sopenharmony_ci [OP_SETCLIENTID_CONFIRM] = nfsd4_decode_setclientid_confirm, 236262306a36Sopenharmony_ci [OP_VERIFY] = nfsd4_decode_verify, 236362306a36Sopenharmony_ci [OP_WRITE] = nfsd4_decode_write, 236462306a36Sopenharmony_ci [OP_RELEASE_LOCKOWNER] = nfsd4_decode_release_lockowner, 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci /* new operations for NFSv4.1 */ 236762306a36Sopenharmony_ci [OP_BACKCHANNEL_CTL] = nfsd4_decode_backchannel_ctl, 236862306a36Sopenharmony_ci [OP_BIND_CONN_TO_SESSION] = nfsd4_decode_bind_conn_to_session, 236962306a36Sopenharmony_ci [OP_EXCHANGE_ID] = nfsd4_decode_exchange_id, 237062306a36Sopenharmony_ci [OP_CREATE_SESSION] = nfsd4_decode_create_session, 237162306a36Sopenharmony_ci [OP_DESTROY_SESSION] = nfsd4_decode_destroy_session, 237262306a36Sopenharmony_ci [OP_FREE_STATEID] = nfsd4_decode_free_stateid, 237362306a36Sopenharmony_ci [OP_GET_DIR_DELEGATION] = nfsd4_decode_notsupp, 237462306a36Sopenharmony_ci#ifdef CONFIG_NFSD_PNFS 237562306a36Sopenharmony_ci [OP_GETDEVICEINFO] = nfsd4_decode_getdeviceinfo, 237662306a36Sopenharmony_ci [OP_GETDEVICELIST] = nfsd4_decode_notsupp, 237762306a36Sopenharmony_ci [OP_LAYOUTCOMMIT] = nfsd4_decode_layoutcommit, 237862306a36Sopenharmony_ci [OP_LAYOUTGET] = nfsd4_decode_layoutget, 237962306a36Sopenharmony_ci [OP_LAYOUTRETURN] = nfsd4_decode_layoutreturn, 238062306a36Sopenharmony_ci#else 238162306a36Sopenharmony_ci [OP_GETDEVICEINFO] = nfsd4_decode_notsupp, 238262306a36Sopenharmony_ci [OP_GETDEVICELIST] = nfsd4_decode_notsupp, 238362306a36Sopenharmony_ci [OP_LAYOUTCOMMIT] = nfsd4_decode_notsupp, 238462306a36Sopenharmony_ci [OP_LAYOUTGET] = nfsd4_decode_notsupp, 238562306a36Sopenharmony_ci [OP_LAYOUTRETURN] = nfsd4_decode_notsupp, 238662306a36Sopenharmony_ci#endif 238762306a36Sopenharmony_ci [OP_SECINFO_NO_NAME] = nfsd4_decode_secinfo_no_name, 238862306a36Sopenharmony_ci [OP_SEQUENCE] = nfsd4_decode_sequence, 238962306a36Sopenharmony_ci [OP_SET_SSV] = nfsd4_decode_notsupp, 239062306a36Sopenharmony_ci [OP_TEST_STATEID] = nfsd4_decode_test_stateid, 239162306a36Sopenharmony_ci [OP_WANT_DELEGATION] = nfsd4_decode_notsupp, 239262306a36Sopenharmony_ci [OP_DESTROY_CLIENTID] = nfsd4_decode_destroy_clientid, 239362306a36Sopenharmony_ci [OP_RECLAIM_COMPLETE] = nfsd4_decode_reclaim_complete, 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci /* new operations for NFSv4.2 */ 239662306a36Sopenharmony_ci [OP_ALLOCATE] = nfsd4_decode_fallocate, 239762306a36Sopenharmony_ci [OP_COPY] = nfsd4_decode_copy, 239862306a36Sopenharmony_ci [OP_COPY_NOTIFY] = nfsd4_decode_copy_notify, 239962306a36Sopenharmony_ci [OP_DEALLOCATE] = nfsd4_decode_fallocate, 240062306a36Sopenharmony_ci [OP_IO_ADVISE] = nfsd4_decode_notsupp, 240162306a36Sopenharmony_ci [OP_LAYOUTERROR] = nfsd4_decode_notsupp, 240262306a36Sopenharmony_ci [OP_LAYOUTSTATS] = nfsd4_decode_notsupp, 240362306a36Sopenharmony_ci [OP_OFFLOAD_CANCEL] = nfsd4_decode_offload_status, 240462306a36Sopenharmony_ci [OP_OFFLOAD_STATUS] = nfsd4_decode_offload_status, 240562306a36Sopenharmony_ci [OP_READ_PLUS] = nfsd4_decode_read, 240662306a36Sopenharmony_ci [OP_SEEK] = nfsd4_decode_seek, 240762306a36Sopenharmony_ci [OP_WRITE_SAME] = nfsd4_decode_notsupp, 240862306a36Sopenharmony_ci [OP_CLONE] = nfsd4_decode_clone, 240962306a36Sopenharmony_ci /* RFC 8276 extended atributes operations */ 241062306a36Sopenharmony_ci [OP_GETXATTR] = nfsd4_decode_getxattr, 241162306a36Sopenharmony_ci [OP_SETXATTR] = nfsd4_decode_setxattr, 241262306a36Sopenharmony_ci [OP_LISTXATTRS] = nfsd4_decode_listxattrs, 241362306a36Sopenharmony_ci [OP_REMOVEXATTR] = nfsd4_decode_removexattr, 241462306a36Sopenharmony_ci}; 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_cistatic inline bool 241762306a36Sopenharmony_cinfsd4_opnum_in_range(struct nfsd4_compoundargs *argp, struct nfsd4_op *op) 241862306a36Sopenharmony_ci{ 241962306a36Sopenharmony_ci if (op->opnum < FIRST_NFS4_OP) 242062306a36Sopenharmony_ci return false; 242162306a36Sopenharmony_ci else if (argp->minorversion == 0 && op->opnum > LAST_NFS40_OP) 242262306a36Sopenharmony_ci return false; 242362306a36Sopenharmony_ci else if (argp->minorversion == 1 && op->opnum > LAST_NFS41_OP) 242462306a36Sopenharmony_ci return false; 242562306a36Sopenharmony_ci else if (argp->minorversion == 2 && op->opnum > LAST_NFS42_OP) 242662306a36Sopenharmony_ci return false; 242762306a36Sopenharmony_ci return true; 242862306a36Sopenharmony_ci} 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_cistatic bool 243162306a36Sopenharmony_cinfsd4_decode_compound(struct nfsd4_compoundargs *argp) 243262306a36Sopenharmony_ci{ 243362306a36Sopenharmony_ci struct nfsd4_op *op; 243462306a36Sopenharmony_ci bool cachethis = false; 243562306a36Sopenharmony_ci int auth_slack= argp->rqstp->rq_auth_slack; 243662306a36Sopenharmony_ci int max_reply = auth_slack + 8; /* opcnt, status */ 243762306a36Sopenharmony_ci int readcount = 0; 243862306a36Sopenharmony_ci int readbytes = 0; 243962306a36Sopenharmony_ci __be32 *p; 244062306a36Sopenharmony_ci int i; 244162306a36Sopenharmony_ci 244262306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &argp->taglen) < 0) 244362306a36Sopenharmony_ci return false; 244462306a36Sopenharmony_ci max_reply += XDR_UNIT; 244562306a36Sopenharmony_ci argp->tag = NULL; 244662306a36Sopenharmony_ci if (unlikely(argp->taglen)) { 244762306a36Sopenharmony_ci if (argp->taglen > NFSD4_MAX_TAGLEN) 244862306a36Sopenharmony_ci return false; 244962306a36Sopenharmony_ci p = xdr_inline_decode(argp->xdr, argp->taglen); 245062306a36Sopenharmony_ci if (!p) 245162306a36Sopenharmony_ci return false; 245262306a36Sopenharmony_ci argp->tag = svcxdr_savemem(argp, p, argp->taglen); 245362306a36Sopenharmony_ci if (!argp->tag) 245462306a36Sopenharmony_ci return false; 245562306a36Sopenharmony_ci max_reply += xdr_align_size(argp->taglen); 245662306a36Sopenharmony_ci } 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &argp->minorversion) < 0) 245962306a36Sopenharmony_ci return false; 246062306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &argp->client_opcnt) < 0) 246162306a36Sopenharmony_ci return false; 246262306a36Sopenharmony_ci argp->opcnt = min_t(u32, argp->client_opcnt, 246362306a36Sopenharmony_ci NFSD_MAX_OPS_PER_COMPOUND); 246462306a36Sopenharmony_ci 246562306a36Sopenharmony_ci if (argp->opcnt > ARRAY_SIZE(argp->iops)) { 246662306a36Sopenharmony_ci argp->ops = vcalloc(argp->opcnt, sizeof(*argp->ops)); 246762306a36Sopenharmony_ci if (!argp->ops) { 246862306a36Sopenharmony_ci argp->ops = argp->iops; 246962306a36Sopenharmony_ci return false; 247062306a36Sopenharmony_ci } 247162306a36Sopenharmony_ci } 247262306a36Sopenharmony_ci 247362306a36Sopenharmony_ci if (argp->minorversion > NFSD_SUPPORTED_MINOR_VERSION) 247462306a36Sopenharmony_ci argp->opcnt = 0; 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci for (i = 0; i < argp->opcnt; i++) { 247762306a36Sopenharmony_ci op = &argp->ops[i]; 247862306a36Sopenharmony_ci op->replay = NULL; 247962306a36Sopenharmony_ci op->opdesc = NULL; 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci if (xdr_stream_decode_u32(argp->xdr, &op->opnum) < 0) 248262306a36Sopenharmony_ci return false; 248362306a36Sopenharmony_ci if (nfsd4_opnum_in_range(argp, op)) { 248462306a36Sopenharmony_ci op->opdesc = OPDESC(op); 248562306a36Sopenharmony_ci op->status = nfsd4_dec_ops[op->opnum](argp, &op->u); 248662306a36Sopenharmony_ci if (op->status != nfs_ok) 248762306a36Sopenharmony_ci trace_nfsd_compound_decode_err(argp->rqstp, 248862306a36Sopenharmony_ci argp->opcnt, i, 248962306a36Sopenharmony_ci op->opnum, 249062306a36Sopenharmony_ci op->status); 249162306a36Sopenharmony_ci } else { 249262306a36Sopenharmony_ci op->opnum = OP_ILLEGAL; 249362306a36Sopenharmony_ci op->status = nfserr_op_illegal; 249462306a36Sopenharmony_ci } 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci /* 249762306a36Sopenharmony_ci * We'll try to cache the result in the DRC if any one 249862306a36Sopenharmony_ci * op in the compound wants to be cached: 249962306a36Sopenharmony_ci */ 250062306a36Sopenharmony_ci cachethis |= nfsd4_cache_this_op(op); 250162306a36Sopenharmony_ci 250262306a36Sopenharmony_ci if (op->opnum == OP_READ || op->opnum == OP_READ_PLUS) { 250362306a36Sopenharmony_ci readcount++; 250462306a36Sopenharmony_ci readbytes += nfsd4_max_reply(argp->rqstp, op); 250562306a36Sopenharmony_ci } else 250662306a36Sopenharmony_ci max_reply += nfsd4_max_reply(argp->rqstp, op); 250762306a36Sopenharmony_ci /* 250862306a36Sopenharmony_ci * OP_LOCK and OP_LOCKT may return a conflicting lock. 250962306a36Sopenharmony_ci * (Special case because it will just skip encoding this 251062306a36Sopenharmony_ci * if it runs out of xdr buffer space, and it is the only 251162306a36Sopenharmony_ci * operation that behaves this way.) 251262306a36Sopenharmony_ci */ 251362306a36Sopenharmony_ci if (op->opnum == OP_LOCK || op->opnum == OP_LOCKT) 251462306a36Sopenharmony_ci max_reply += NFS4_OPAQUE_LIMIT; 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci if (op->status) { 251762306a36Sopenharmony_ci argp->opcnt = i+1; 251862306a36Sopenharmony_ci break; 251962306a36Sopenharmony_ci } 252062306a36Sopenharmony_ci } 252162306a36Sopenharmony_ci /* Sessions make the DRC unnecessary: */ 252262306a36Sopenharmony_ci if (argp->minorversion) 252362306a36Sopenharmony_ci cachethis = false; 252462306a36Sopenharmony_ci svc_reserve(argp->rqstp, max_reply + readbytes); 252562306a36Sopenharmony_ci argp->rqstp->rq_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE; 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci if (readcount > 1 || max_reply > PAGE_SIZE - auth_slack) 252862306a36Sopenharmony_ci clear_bit(RQ_SPLICE_OK, &argp->rqstp->rq_flags); 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_ci return true; 253162306a36Sopenharmony_ci} 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_cistatic __be32 *encode_change(__be32 *p, struct kstat *stat, struct inode *inode, 253462306a36Sopenharmony_ci struct svc_export *exp) 253562306a36Sopenharmony_ci{ 253662306a36Sopenharmony_ci if (exp->ex_flags & NFSEXP_V4ROOT) { 253762306a36Sopenharmony_ci *p++ = cpu_to_be32(convert_to_wallclock(exp->cd->flush_time)); 253862306a36Sopenharmony_ci *p++ = 0; 253962306a36Sopenharmony_ci } else 254062306a36Sopenharmony_ci p = xdr_encode_hyper(p, nfsd4_change_attribute(stat, inode)); 254162306a36Sopenharmony_ci return p; 254262306a36Sopenharmony_ci} 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_cistatic __be32 nfsd4_encode_nfstime4(struct xdr_stream *xdr, 254562306a36Sopenharmony_ci struct timespec64 *tv) 254662306a36Sopenharmony_ci{ 254762306a36Sopenharmony_ci __be32 *p; 254862306a36Sopenharmony_ci 254962306a36Sopenharmony_ci p = xdr_reserve_space(xdr, XDR_UNIT * 3); 255062306a36Sopenharmony_ci if (!p) 255162306a36Sopenharmony_ci return nfserr_resource; 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_ci p = xdr_encode_hyper(p, (s64)tv->tv_sec); 255462306a36Sopenharmony_ci *p = cpu_to_be32(tv->tv_nsec); 255562306a36Sopenharmony_ci return nfs_ok; 255662306a36Sopenharmony_ci} 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci/* 255962306a36Sopenharmony_ci * ctime (in NFSv4, time_metadata) is not writeable, and the client 256062306a36Sopenharmony_ci * doesn't really care what resolution could theoretically be stored by 256162306a36Sopenharmony_ci * the filesystem. 256262306a36Sopenharmony_ci * 256362306a36Sopenharmony_ci * The client cares how close together changes can be while still 256462306a36Sopenharmony_ci * guaranteeing ctime changes. For most filesystems (which have 256562306a36Sopenharmony_ci * timestamps with nanosecond fields) that is limited by the resolution 256662306a36Sopenharmony_ci * of the time returned from current_time() (which I'm assuming to be 256762306a36Sopenharmony_ci * 1/HZ). 256862306a36Sopenharmony_ci */ 256962306a36Sopenharmony_cistatic __be32 *encode_time_delta(__be32 *p, struct inode *inode) 257062306a36Sopenharmony_ci{ 257162306a36Sopenharmony_ci struct timespec64 ts; 257262306a36Sopenharmony_ci u32 ns; 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_ci ns = max_t(u32, NSEC_PER_SEC/HZ, inode->i_sb->s_time_gran); 257562306a36Sopenharmony_ci ts = ns_to_timespec64(ns); 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_ci p = xdr_encode_hyper(p, ts.tv_sec); 257862306a36Sopenharmony_ci *p++ = cpu_to_be32(ts.tv_nsec); 257962306a36Sopenharmony_ci 258062306a36Sopenharmony_ci return p; 258162306a36Sopenharmony_ci} 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_cistatic __be32 258462306a36Sopenharmony_cinfsd4_encode_change_info4(struct xdr_stream *xdr, struct nfsd4_change_info *c) 258562306a36Sopenharmony_ci{ 258662306a36Sopenharmony_ci if (xdr_stream_encode_bool(xdr, c->atomic) < 0) 258762306a36Sopenharmony_ci return nfserr_resource; 258862306a36Sopenharmony_ci if (xdr_stream_encode_u64(xdr, c->before_change) < 0) 258962306a36Sopenharmony_ci return nfserr_resource; 259062306a36Sopenharmony_ci if (xdr_stream_encode_u64(xdr, c->after_change) < 0) 259162306a36Sopenharmony_ci return nfserr_resource; 259262306a36Sopenharmony_ci return nfs_ok; 259362306a36Sopenharmony_ci} 259462306a36Sopenharmony_ci 259562306a36Sopenharmony_ci/* Encode as an array of strings the string given with components 259662306a36Sopenharmony_ci * separated @sep, escaped with esc_enter and esc_exit. 259762306a36Sopenharmony_ci */ 259862306a36Sopenharmony_cistatic __be32 nfsd4_encode_components_esc(struct xdr_stream *xdr, char sep, 259962306a36Sopenharmony_ci char *components, char esc_enter, 260062306a36Sopenharmony_ci char esc_exit) 260162306a36Sopenharmony_ci{ 260262306a36Sopenharmony_ci __be32 *p; 260362306a36Sopenharmony_ci __be32 pathlen; 260462306a36Sopenharmony_ci int pathlen_offset; 260562306a36Sopenharmony_ci int strlen, count=0; 260662306a36Sopenharmony_ci char *str, *end, *next; 260762306a36Sopenharmony_ci 260862306a36Sopenharmony_ci dprintk("nfsd4_encode_components(%s)\n", components); 260962306a36Sopenharmony_ci 261062306a36Sopenharmony_ci pathlen_offset = xdr->buf->len; 261162306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 261262306a36Sopenharmony_ci if (!p) 261362306a36Sopenharmony_ci return nfserr_resource; 261462306a36Sopenharmony_ci p++; /* We will fill this in with @count later */ 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_ci end = str = components; 261762306a36Sopenharmony_ci while (*end) { 261862306a36Sopenharmony_ci bool found_esc = false; 261962306a36Sopenharmony_ci 262062306a36Sopenharmony_ci /* try to parse as esc_start, ..., esc_end, sep */ 262162306a36Sopenharmony_ci if (*str == esc_enter) { 262262306a36Sopenharmony_ci for (; *end && (*end != esc_exit); end++) 262362306a36Sopenharmony_ci /* find esc_exit or end of string */; 262462306a36Sopenharmony_ci next = end + 1; 262562306a36Sopenharmony_ci if (*end && (!*next || *next == sep)) { 262662306a36Sopenharmony_ci str++; 262762306a36Sopenharmony_ci found_esc = true; 262862306a36Sopenharmony_ci } 262962306a36Sopenharmony_ci } 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci if (!found_esc) 263262306a36Sopenharmony_ci for (; *end && (*end != sep); end++) 263362306a36Sopenharmony_ci /* find sep or end of string */; 263462306a36Sopenharmony_ci 263562306a36Sopenharmony_ci strlen = end - str; 263662306a36Sopenharmony_ci if (strlen) { 263762306a36Sopenharmony_ci p = xdr_reserve_space(xdr, strlen + 4); 263862306a36Sopenharmony_ci if (!p) 263962306a36Sopenharmony_ci return nfserr_resource; 264062306a36Sopenharmony_ci p = xdr_encode_opaque(p, str, strlen); 264162306a36Sopenharmony_ci count++; 264262306a36Sopenharmony_ci } 264362306a36Sopenharmony_ci else 264462306a36Sopenharmony_ci end++; 264562306a36Sopenharmony_ci if (found_esc) 264662306a36Sopenharmony_ci end = next; 264762306a36Sopenharmony_ci 264862306a36Sopenharmony_ci str = end; 264962306a36Sopenharmony_ci } 265062306a36Sopenharmony_ci pathlen = htonl(count); 265162306a36Sopenharmony_ci write_bytes_to_xdr_buf(xdr->buf, pathlen_offset, &pathlen, 4); 265262306a36Sopenharmony_ci return 0; 265362306a36Sopenharmony_ci} 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_ci/* Encode as an array of strings the string given with components 265662306a36Sopenharmony_ci * separated @sep. 265762306a36Sopenharmony_ci */ 265862306a36Sopenharmony_cistatic __be32 nfsd4_encode_components(struct xdr_stream *xdr, char sep, 265962306a36Sopenharmony_ci char *components) 266062306a36Sopenharmony_ci{ 266162306a36Sopenharmony_ci return nfsd4_encode_components_esc(xdr, sep, components, 0, 0); 266262306a36Sopenharmony_ci} 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci/* 266562306a36Sopenharmony_ci * encode a location element of a fs_locations structure 266662306a36Sopenharmony_ci */ 266762306a36Sopenharmony_cistatic __be32 nfsd4_encode_fs_location4(struct xdr_stream *xdr, 266862306a36Sopenharmony_ci struct nfsd4_fs_location *location) 266962306a36Sopenharmony_ci{ 267062306a36Sopenharmony_ci __be32 status; 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_ci status = nfsd4_encode_components_esc(xdr, ':', location->hosts, 267362306a36Sopenharmony_ci '[', ']'); 267462306a36Sopenharmony_ci if (status) 267562306a36Sopenharmony_ci return status; 267662306a36Sopenharmony_ci status = nfsd4_encode_components(xdr, '/', location->path); 267762306a36Sopenharmony_ci if (status) 267862306a36Sopenharmony_ci return status; 267962306a36Sopenharmony_ci return 0; 268062306a36Sopenharmony_ci} 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_ci/* 268362306a36Sopenharmony_ci * Encode a path in RFC3530 'pathname4' format 268462306a36Sopenharmony_ci */ 268562306a36Sopenharmony_cistatic __be32 nfsd4_encode_path(struct xdr_stream *xdr, 268662306a36Sopenharmony_ci const struct path *root, 268762306a36Sopenharmony_ci const struct path *path) 268862306a36Sopenharmony_ci{ 268962306a36Sopenharmony_ci struct path cur = *path; 269062306a36Sopenharmony_ci __be32 *p; 269162306a36Sopenharmony_ci struct dentry **components = NULL; 269262306a36Sopenharmony_ci unsigned int ncomponents = 0; 269362306a36Sopenharmony_ci __be32 err = nfserr_jukebox; 269462306a36Sopenharmony_ci 269562306a36Sopenharmony_ci dprintk("nfsd4_encode_components("); 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_ci path_get(&cur); 269862306a36Sopenharmony_ci /* First walk the path up to the nfsd root, and store the 269962306a36Sopenharmony_ci * dentries/path components in an array. 270062306a36Sopenharmony_ci */ 270162306a36Sopenharmony_ci for (;;) { 270262306a36Sopenharmony_ci if (path_equal(&cur, root)) 270362306a36Sopenharmony_ci break; 270462306a36Sopenharmony_ci if (cur.dentry == cur.mnt->mnt_root) { 270562306a36Sopenharmony_ci if (follow_up(&cur)) 270662306a36Sopenharmony_ci continue; 270762306a36Sopenharmony_ci goto out_free; 270862306a36Sopenharmony_ci } 270962306a36Sopenharmony_ci if ((ncomponents & 15) == 0) { 271062306a36Sopenharmony_ci struct dentry **new; 271162306a36Sopenharmony_ci new = krealloc(components, 271262306a36Sopenharmony_ci sizeof(*new) * (ncomponents + 16), 271362306a36Sopenharmony_ci GFP_KERNEL); 271462306a36Sopenharmony_ci if (!new) 271562306a36Sopenharmony_ci goto out_free; 271662306a36Sopenharmony_ci components = new; 271762306a36Sopenharmony_ci } 271862306a36Sopenharmony_ci components[ncomponents++] = cur.dentry; 271962306a36Sopenharmony_ci cur.dentry = dget_parent(cur.dentry); 272062306a36Sopenharmony_ci } 272162306a36Sopenharmony_ci err = nfserr_resource; 272262306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 272362306a36Sopenharmony_ci if (!p) 272462306a36Sopenharmony_ci goto out_free; 272562306a36Sopenharmony_ci *p++ = cpu_to_be32(ncomponents); 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci while (ncomponents) { 272862306a36Sopenharmony_ci struct dentry *dentry = components[ncomponents - 1]; 272962306a36Sopenharmony_ci unsigned int len; 273062306a36Sopenharmony_ci 273162306a36Sopenharmony_ci spin_lock(&dentry->d_lock); 273262306a36Sopenharmony_ci len = dentry->d_name.len; 273362306a36Sopenharmony_ci p = xdr_reserve_space(xdr, len + 4); 273462306a36Sopenharmony_ci if (!p) { 273562306a36Sopenharmony_ci spin_unlock(&dentry->d_lock); 273662306a36Sopenharmony_ci goto out_free; 273762306a36Sopenharmony_ci } 273862306a36Sopenharmony_ci p = xdr_encode_opaque(p, dentry->d_name.name, len); 273962306a36Sopenharmony_ci dprintk("/%pd", dentry); 274062306a36Sopenharmony_ci spin_unlock(&dentry->d_lock); 274162306a36Sopenharmony_ci dput(dentry); 274262306a36Sopenharmony_ci ncomponents--; 274362306a36Sopenharmony_ci } 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci err = 0; 274662306a36Sopenharmony_ciout_free: 274762306a36Sopenharmony_ci dprintk(")\n"); 274862306a36Sopenharmony_ci while (ncomponents) 274962306a36Sopenharmony_ci dput(components[--ncomponents]); 275062306a36Sopenharmony_ci kfree(components); 275162306a36Sopenharmony_ci path_put(&cur); 275262306a36Sopenharmony_ci return err; 275362306a36Sopenharmony_ci} 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_cistatic __be32 nfsd4_encode_fsloc_fsroot(struct xdr_stream *xdr, 275662306a36Sopenharmony_ci struct svc_rqst *rqstp, const struct path *path) 275762306a36Sopenharmony_ci{ 275862306a36Sopenharmony_ci struct svc_export *exp_ps; 275962306a36Sopenharmony_ci __be32 res; 276062306a36Sopenharmony_ci 276162306a36Sopenharmony_ci exp_ps = rqst_find_fsidzero_export(rqstp); 276262306a36Sopenharmony_ci if (IS_ERR(exp_ps)) 276362306a36Sopenharmony_ci return nfserrno(PTR_ERR(exp_ps)); 276462306a36Sopenharmony_ci res = nfsd4_encode_path(xdr, &exp_ps->ex_path, path); 276562306a36Sopenharmony_ci exp_put(exp_ps); 276662306a36Sopenharmony_ci return res; 276762306a36Sopenharmony_ci} 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci/* 277062306a36Sopenharmony_ci * encode a fs_locations structure 277162306a36Sopenharmony_ci */ 277262306a36Sopenharmony_cistatic __be32 nfsd4_encode_fs_locations(struct xdr_stream *xdr, 277362306a36Sopenharmony_ci struct svc_rqst *rqstp, struct svc_export *exp) 277462306a36Sopenharmony_ci{ 277562306a36Sopenharmony_ci __be32 status; 277662306a36Sopenharmony_ci int i; 277762306a36Sopenharmony_ci __be32 *p; 277862306a36Sopenharmony_ci struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs; 277962306a36Sopenharmony_ci 278062306a36Sopenharmony_ci status = nfsd4_encode_fsloc_fsroot(xdr, rqstp, &exp->ex_path); 278162306a36Sopenharmony_ci if (status) 278262306a36Sopenharmony_ci return status; 278362306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 278462306a36Sopenharmony_ci if (!p) 278562306a36Sopenharmony_ci return nfserr_resource; 278662306a36Sopenharmony_ci *p++ = cpu_to_be32(fslocs->locations_count); 278762306a36Sopenharmony_ci for (i=0; i<fslocs->locations_count; i++) { 278862306a36Sopenharmony_ci status = nfsd4_encode_fs_location4(xdr, &fslocs->locations[i]); 278962306a36Sopenharmony_ci if (status) 279062306a36Sopenharmony_ci return status; 279162306a36Sopenharmony_ci } 279262306a36Sopenharmony_ci return 0; 279362306a36Sopenharmony_ci} 279462306a36Sopenharmony_ci 279562306a36Sopenharmony_cistatic u32 nfs4_file_type(umode_t mode) 279662306a36Sopenharmony_ci{ 279762306a36Sopenharmony_ci switch (mode & S_IFMT) { 279862306a36Sopenharmony_ci case S_IFIFO: return NF4FIFO; 279962306a36Sopenharmony_ci case S_IFCHR: return NF4CHR; 280062306a36Sopenharmony_ci case S_IFDIR: return NF4DIR; 280162306a36Sopenharmony_ci case S_IFBLK: return NF4BLK; 280262306a36Sopenharmony_ci case S_IFLNK: return NF4LNK; 280362306a36Sopenharmony_ci case S_IFREG: return NF4REG; 280462306a36Sopenharmony_ci case S_IFSOCK: return NF4SOCK; 280562306a36Sopenharmony_ci default: return NF4BAD; 280662306a36Sopenharmony_ci } 280762306a36Sopenharmony_ci} 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_cistatic inline __be32 281062306a36Sopenharmony_cinfsd4_encode_aclname(struct xdr_stream *xdr, struct svc_rqst *rqstp, 281162306a36Sopenharmony_ci struct nfs4_ace *ace) 281262306a36Sopenharmony_ci{ 281362306a36Sopenharmony_ci if (ace->whotype != NFS4_ACL_WHO_NAMED) 281462306a36Sopenharmony_ci return nfs4_acl_write_who(xdr, ace->whotype); 281562306a36Sopenharmony_ci else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP) 281662306a36Sopenharmony_ci return nfsd4_encode_group(xdr, rqstp, ace->who_gid); 281762306a36Sopenharmony_ci else 281862306a36Sopenharmony_ci return nfsd4_encode_user(xdr, rqstp, ace->who_uid); 281962306a36Sopenharmony_ci} 282062306a36Sopenharmony_ci 282162306a36Sopenharmony_cistatic inline __be32 282262306a36Sopenharmony_cinfsd4_encode_layout_types(struct xdr_stream *xdr, u32 layout_types) 282362306a36Sopenharmony_ci{ 282462306a36Sopenharmony_ci __be32 *p; 282562306a36Sopenharmony_ci unsigned long i = hweight_long(layout_types); 282662306a36Sopenharmony_ci 282762306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4 + 4 * i); 282862306a36Sopenharmony_ci if (!p) 282962306a36Sopenharmony_ci return nfserr_resource; 283062306a36Sopenharmony_ci 283162306a36Sopenharmony_ci *p++ = cpu_to_be32(i); 283262306a36Sopenharmony_ci 283362306a36Sopenharmony_ci for (i = LAYOUT_NFSV4_1_FILES; i < LAYOUT_TYPE_MAX; ++i) 283462306a36Sopenharmony_ci if (layout_types & (1 << i)) 283562306a36Sopenharmony_ci *p++ = cpu_to_be32(i); 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci return 0; 283862306a36Sopenharmony_ci} 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_ci#define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \ 284162306a36Sopenharmony_ci FATTR4_WORD0_RDATTR_ERROR) 284262306a36Sopenharmony_ci#define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID 284362306a36Sopenharmony_ci#define WORD2_ABSENT_FS_ATTRS 0 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_ci#ifdef CONFIG_NFSD_V4_SECURITY_LABEL 284662306a36Sopenharmony_cistatic inline __be32 284762306a36Sopenharmony_cinfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp, 284862306a36Sopenharmony_ci void *context, int len) 284962306a36Sopenharmony_ci{ 285062306a36Sopenharmony_ci __be32 *p; 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_ci p = xdr_reserve_space(xdr, len + 4 + 4 + 4); 285362306a36Sopenharmony_ci if (!p) 285462306a36Sopenharmony_ci return nfserr_resource; 285562306a36Sopenharmony_ci 285662306a36Sopenharmony_ci /* 285762306a36Sopenharmony_ci * For now we use a 0 here to indicate the null translation; in 285862306a36Sopenharmony_ci * the future we may place a call to translation code here. 285962306a36Sopenharmony_ci */ 286062306a36Sopenharmony_ci *p++ = cpu_to_be32(0); /* lfs */ 286162306a36Sopenharmony_ci *p++ = cpu_to_be32(0); /* pi */ 286262306a36Sopenharmony_ci p = xdr_encode_opaque(p, context, len); 286362306a36Sopenharmony_ci return 0; 286462306a36Sopenharmony_ci} 286562306a36Sopenharmony_ci#else 286662306a36Sopenharmony_cistatic inline __be32 286762306a36Sopenharmony_cinfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp, 286862306a36Sopenharmony_ci void *context, int len) 286962306a36Sopenharmony_ci{ return 0; } 287062306a36Sopenharmony_ci#endif 287162306a36Sopenharmony_ci 287262306a36Sopenharmony_cistatic __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *bmval2, u32 *rdattr_err) 287362306a36Sopenharmony_ci{ 287462306a36Sopenharmony_ci /* As per referral draft: */ 287562306a36Sopenharmony_ci if (*bmval0 & ~WORD0_ABSENT_FS_ATTRS || 287662306a36Sopenharmony_ci *bmval1 & ~WORD1_ABSENT_FS_ATTRS) { 287762306a36Sopenharmony_ci if (*bmval0 & FATTR4_WORD0_RDATTR_ERROR || 287862306a36Sopenharmony_ci *bmval0 & FATTR4_WORD0_FS_LOCATIONS) 287962306a36Sopenharmony_ci *rdattr_err = NFSERR_MOVED; 288062306a36Sopenharmony_ci else 288162306a36Sopenharmony_ci return nfserr_moved; 288262306a36Sopenharmony_ci } 288362306a36Sopenharmony_ci *bmval0 &= WORD0_ABSENT_FS_ATTRS; 288462306a36Sopenharmony_ci *bmval1 &= WORD1_ABSENT_FS_ATTRS; 288562306a36Sopenharmony_ci *bmval2 &= WORD2_ABSENT_FS_ATTRS; 288662306a36Sopenharmony_ci return 0; 288762306a36Sopenharmony_ci} 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_ci 289062306a36Sopenharmony_cistatic int nfsd4_get_mounted_on_ino(struct svc_export *exp, u64 *pino) 289162306a36Sopenharmony_ci{ 289262306a36Sopenharmony_ci struct path path = exp->ex_path; 289362306a36Sopenharmony_ci struct kstat stat; 289462306a36Sopenharmony_ci int err; 289562306a36Sopenharmony_ci 289662306a36Sopenharmony_ci path_get(&path); 289762306a36Sopenharmony_ci while (follow_up(&path)) { 289862306a36Sopenharmony_ci if (path.dentry != path.mnt->mnt_root) 289962306a36Sopenharmony_ci break; 290062306a36Sopenharmony_ci } 290162306a36Sopenharmony_ci err = vfs_getattr(&path, &stat, STATX_INO, AT_STATX_SYNC_AS_STAT); 290262306a36Sopenharmony_ci path_put(&path); 290362306a36Sopenharmony_ci if (!err) 290462306a36Sopenharmony_ci *pino = stat.ino; 290562306a36Sopenharmony_ci return err; 290662306a36Sopenharmony_ci} 290762306a36Sopenharmony_ci 290862306a36Sopenharmony_cistatic __be32 290962306a36Sopenharmony_cinfsd4_encode_bitmap(struct xdr_stream *xdr, u32 bmval0, u32 bmval1, u32 bmval2) 291062306a36Sopenharmony_ci{ 291162306a36Sopenharmony_ci __be32 *p; 291262306a36Sopenharmony_ci 291362306a36Sopenharmony_ci if (bmval2) { 291462306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 16); 291562306a36Sopenharmony_ci if (!p) 291662306a36Sopenharmony_ci goto out_resource; 291762306a36Sopenharmony_ci *p++ = cpu_to_be32(3); 291862306a36Sopenharmony_ci *p++ = cpu_to_be32(bmval0); 291962306a36Sopenharmony_ci *p++ = cpu_to_be32(bmval1); 292062306a36Sopenharmony_ci *p++ = cpu_to_be32(bmval2); 292162306a36Sopenharmony_ci } else if (bmval1) { 292262306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 12); 292362306a36Sopenharmony_ci if (!p) 292462306a36Sopenharmony_ci goto out_resource; 292562306a36Sopenharmony_ci *p++ = cpu_to_be32(2); 292662306a36Sopenharmony_ci *p++ = cpu_to_be32(bmval0); 292762306a36Sopenharmony_ci *p++ = cpu_to_be32(bmval1); 292862306a36Sopenharmony_ci } else { 292962306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 293062306a36Sopenharmony_ci if (!p) 293162306a36Sopenharmony_ci goto out_resource; 293262306a36Sopenharmony_ci *p++ = cpu_to_be32(1); 293362306a36Sopenharmony_ci *p++ = cpu_to_be32(bmval0); 293462306a36Sopenharmony_ci } 293562306a36Sopenharmony_ci 293662306a36Sopenharmony_ci return 0; 293762306a36Sopenharmony_ciout_resource: 293862306a36Sopenharmony_ci return nfserr_resource; 293962306a36Sopenharmony_ci} 294062306a36Sopenharmony_ci 294162306a36Sopenharmony_ci/* 294262306a36Sopenharmony_ci * Note: @fhp can be NULL; in this case, we might have to compose the filehandle 294362306a36Sopenharmony_ci * ourselves. 294462306a36Sopenharmony_ci */ 294562306a36Sopenharmony_cistatic __be32 294662306a36Sopenharmony_cinfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp, 294762306a36Sopenharmony_ci struct svc_export *exp, 294862306a36Sopenharmony_ci struct dentry *dentry, u32 *bmval, 294962306a36Sopenharmony_ci struct svc_rqst *rqstp, int ignore_crossmnt) 295062306a36Sopenharmony_ci{ 295162306a36Sopenharmony_ci u32 bmval0 = bmval[0]; 295262306a36Sopenharmony_ci u32 bmval1 = bmval[1]; 295362306a36Sopenharmony_ci u32 bmval2 = bmval[2]; 295462306a36Sopenharmony_ci struct kstat stat; 295562306a36Sopenharmony_ci struct svc_fh *tempfh = NULL; 295662306a36Sopenharmony_ci struct kstatfs statfs; 295762306a36Sopenharmony_ci __be32 *p, *attrlen_p; 295862306a36Sopenharmony_ci int starting_len = xdr->buf->len; 295962306a36Sopenharmony_ci int attrlen_offset; 296062306a36Sopenharmony_ci u32 dummy; 296162306a36Sopenharmony_ci u64 dummy64; 296262306a36Sopenharmony_ci u32 rdattr_err = 0; 296362306a36Sopenharmony_ci __be32 status; 296462306a36Sopenharmony_ci int err; 296562306a36Sopenharmony_ci struct nfs4_acl *acl = NULL; 296662306a36Sopenharmony_ci#ifdef CONFIG_NFSD_V4_SECURITY_LABEL 296762306a36Sopenharmony_ci void *context = NULL; 296862306a36Sopenharmony_ci int contextlen; 296962306a36Sopenharmony_ci#endif 297062306a36Sopenharmony_ci bool contextsupport = false; 297162306a36Sopenharmony_ci struct nfsd4_compoundres *resp = rqstp->rq_resp; 297262306a36Sopenharmony_ci u32 minorversion = resp->cstate.minorversion; 297362306a36Sopenharmony_ci struct path path = { 297462306a36Sopenharmony_ci .mnt = exp->ex_path.mnt, 297562306a36Sopenharmony_ci .dentry = dentry, 297662306a36Sopenharmony_ci }; 297762306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); 297862306a36Sopenharmony_ci 297962306a36Sopenharmony_ci BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1); 298062306a36Sopenharmony_ci BUG_ON(!nfsd_attrs_supported(minorversion, bmval)); 298162306a36Sopenharmony_ci 298262306a36Sopenharmony_ci if (exp->ex_fslocs.migrated) { 298362306a36Sopenharmony_ci status = fattr_handle_absent_fs(&bmval0, &bmval1, &bmval2, &rdattr_err); 298462306a36Sopenharmony_ci if (status) 298562306a36Sopenharmony_ci goto out; 298662306a36Sopenharmony_ci } 298762306a36Sopenharmony_ci if (bmval0 & (FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE)) { 298862306a36Sopenharmony_ci status = nfsd4_deleg_getattr_conflict(rqstp, d_inode(dentry)); 298962306a36Sopenharmony_ci if (status) 299062306a36Sopenharmony_ci goto out; 299162306a36Sopenharmony_ci } 299262306a36Sopenharmony_ci 299362306a36Sopenharmony_ci err = vfs_getattr(&path, &stat, 299462306a36Sopenharmony_ci STATX_BASIC_STATS | STATX_BTIME | STATX_CHANGE_COOKIE, 299562306a36Sopenharmony_ci AT_STATX_SYNC_AS_STAT); 299662306a36Sopenharmony_ci if (err) 299762306a36Sopenharmony_ci goto out_nfserr; 299862306a36Sopenharmony_ci if (!(stat.result_mask & STATX_BTIME)) 299962306a36Sopenharmony_ci /* underlying FS does not offer btime so we can't share it */ 300062306a36Sopenharmony_ci bmval1 &= ~FATTR4_WORD1_TIME_CREATE; 300162306a36Sopenharmony_ci if ((bmval0 & (FATTR4_WORD0_FILES_AVAIL | FATTR4_WORD0_FILES_FREE | 300262306a36Sopenharmony_ci FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_MAXNAME)) || 300362306a36Sopenharmony_ci (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | 300462306a36Sopenharmony_ci FATTR4_WORD1_SPACE_TOTAL))) { 300562306a36Sopenharmony_ci err = vfs_statfs(&path, &statfs); 300662306a36Sopenharmony_ci if (err) 300762306a36Sopenharmony_ci goto out_nfserr; 300862306a36Sopenharmony_ci } 300962306a36Sopenharmony_ci if ((bmval0 & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) && !fhp) { 301062306a36Sopenharmony_ci tempfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL); 301162306a36Sopenharmony_ci status = nfserr_jukebox; 301262306a36Sopenharmony_ci if (!tempfh) 301362306a36Sopenharmony_ci goto out; 301462306a36Sopenharmony_ci fh_init(tempfh, NFS4_FHSIZE); 301562306a36Sopenharmony_ci status = fh_compose(tempfh, exp, dentry, NULL); 301662306a36Sopenharmony_ci if (status) 301762306a36Sopenharmony_ci goto out; 301862306a36Sopenharmony_ci fhp = tempfh; 301962306a36Sopenharmony_ci } 302062306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_ACL) { 302162306a36Sopenharmony_ci err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl); 302262306a36Sopenharmony_ci if (err == -EOPNOTSUPP) 302362306a36Sopenharmony_ci bmval0 &= ~FATTR4_WORD0_ACL; 302462306a36Sopenharmony_ci else if (err == -EINVAL) { 302562306a36Sopenharmony_ci status = nfserr_attrnotsupp; 302662306a36Sopenharmony_ci goto out; 302762306a36Sopenharmony_ci } else if (err != 0) 302862306a36Sopenharmony_ci goto out_nfserr; 302962306a36Sopenharmony_ci } 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_ci#ifdef CONFIG_NFSD_V4_SECURITY_LABEL 303262306a36Sopenharmony_ci if ((bmval2 & FATTR4_WORD2_SECURITY_LABEL) || 303362306a36Sopenharmony_ci bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) { 303462306a36Sopenharmony_ci if (exp->ex_flags & NFSEXP_SECURITY_LABEL) 303562306a36Sopenharmony_ci err = security_inode_getsecctx(d_inode(dentry), 303662306a36Sopenharmony_ci &context, &contextlen); 303762306a36Sopenharmony_ci else 303862306a36Sopenharmony_ci err = -EOPNOTSUPP; 303962306a36Sopenharmony_ci contextsupport = (err == 0); 304062306a36Sopenharmony_ci if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) { 304162306a36Sopenharmony_ci if (err == -EOPNOTSUPP) 304262306a36Sopenharmony_ci bmval2 &= ~FATTR4_WORD2_SECURITY_LABEL; 304362306a36Sopenharmony_ci else if (err) 304462306a36Sopenharmony_ci goto out_nfserr; 304562306a36Sopenharmony_ci } 304662306a36Sopenharmony_ci } 304762306a36Sopenharmony_ci#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */ 304862306a36Sopenharmony_ci 304962306a36Sopenharmony_ci status = nfsd4_encode_bitmap(xdr, bmval0, bmval1, bmval2); 305062306a36Sopenharmony_ci if (status) 305162306a36Sopenharmony_ci goto out; 305262306a36Sopenharmony_ci 305362306a36Sopenharmony_ci attrlen_offset = xdr->buf->len; 305462306a36Sopenharmony_ci attrlen_p = xdr_reserve_space(xdr, XDR_UNIT); 305562306a36Sopenharmony_ci if (!attrlen_p) 305662306a36Sopenharmony_ci goto out_resource; 305762306a36Sopenharmony_ci 305862306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) { 305962306a36Sopenharmony_ci u32 supp[3]; 306062306a36Sopenharmony_ci 306162306a36Sopenharmony_ci memcpy(supp, nfsd_suppattrs[minorversion], sizeof(supp)); 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_ci if (!IS_POSIXACL(dentry->d_inode)) 306462306a36Sopenharmony_ci supp[0] &= ~FATTR4_WORD0_ACL; 306562306a36Sopenharmony_ci if (!contextsupport) 306662306a36Sopenharmony_ci supp[2] &= ~FATTR4_WORD2_SECURITY_LABEL; 306762306a36Sopenharmony_ci if (!supp[2]) { 306862306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 12); 306962306a36Sopenharmony_ci if (!p) 307062306a36Sopenharmony_ci goto out_resource; 307162306a36Sopenharmony_ci *p++ = cpu_to_be32(2); 307262306a36Sopenharmony_ci *p++ = cpu_to_be32(supp[0]); 307362306a36Sopenharmony_ci *p++ = cpu_to_be32(supp[1]); 307462306a36Sopenharmony_ci } else { 307562306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 16); 307662306a36Sopenharmony_ci if (!p) 307762306a36Sopenharmony_ci goto out_resource; 307862306a36Sopenharmony_ci *p++ = cpu_to_be32(3); 307962306a36Sopenharmony_ci *p++ = cpu_to_be32(supp[0]); 308062306a36Sopenharmony_ci *p++ = cpu_to_be32(supp[1]); 308162306a36Sopenharmony_ci *p++ = cpu_to_be32(supp[2]); 308262306a36Sopenharmony_ci } 308362306a36Sopenharmony_ci } 308462306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_TYPE) { 308562306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 308662306a36Sopenharmony_ci if (!p) 308762306a36Sopenharmony_ci goto out_resource; 308862306a36Sopenharmony_ci dummy = nfs4_file_type(stat.mode); 308962306a36Sopenharmony_ci if (dummy == NF4BAD) { 309062306a36Sopenharmony_ci status = nfserr_serverfault; 309162306a36Sopenharmony_ci goto out; 309262306a36Sopenharmony_ci } 309362306a36Sopenharmony_ci *p++ = cpu_to_be32(dummy); 309462306a36Sopenharmony_ci } 309562306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) { 309662306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 309762306a36Sopenharmony_ci if (!p) 309862306a36Sopenharmony_ci goto out_resource; 309962306a36Sopenharmony_ci if (exp->ex_flags & NFSEXP_NOSUBTREECHECK) 310062306a36Sopenharmony_ci *p++ = cpu_to_be32(NFS4_FH_PERSISTENT); 310162306a36Sopenharmony_ci else 310262306a36Sopenharmony_ci *p++ = cpu_to_be32(NFS4_FH_PERSISTENT| 310362306a36Sopenharmony_ci NFS4_FH_VOL_RENAME); 310462306a36Sopenharmony_ci } 310562306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_CHANGE) { 310662306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 310762306a36Sopenharmony_ci if (!p) 310862306a36Sopenharmony_ci goto out_resource; 310962306a36Sopenharmony_ci p = encode_change(p, &stat, d_inode(dentry), exp); 311062306a36Sopenharmony_ci } 311162306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_SIZE) { 311262306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 311362306a36Sopenharmony_ci if (!p) 311462306a36Sopenharmony_ci goto out_resource; 311562306a36Sopenharmony_ci p = xdr_encode_hyper(p, stat.size); 311662306a36Sopenharmony_ci } 311762306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) { 311862306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 311962306a36Sopenharmony_ci if (!p) 312062306a36Sopenharmony_ci goto out_resource; 312162306a36Sopenharmony_ci *p++ = cpu_to_be32(1); 312262306a36Sopenharmony_ci } 312362306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_SYMLINK_SUPPORT) { 312462306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 312562306a36Sopenharmony_ci if (!p) 312662306a36Sopenharmony_ci goto out_resource; 312762306a36Sopenharmony_ci *p++ = cpu_to_be32(1); 312862306a36Sopenharmony_ci } 312962306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_NAMED_ATTR) { 313062306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 313162306a36Sopenharmony_ci if (!p) 313262306a36Sopenharmony_ci goto out_resource; 313362306a36Sopenharmony_ci *p++ = cpu_to_be32(0); 313462306a36Sopenharmony_ci } 313562306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_FSID) { 313662306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 16); 313762306a36Sopenharmony_ci if (!p) 313862306a36Sopenharmony_ci goto out_resource; 313962306a36Sopenharmony_ci if (exp->ex_fslocs.migrated) { 314062306a36Sopenharmony_ci p = xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MAJOR); 314162306a36Sopenharmony_ci p = xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MINOR); 314262306a36Sopenharmony_ci } else switch(fsid_source(fhp)) { 314362306a36Sopenharmony_ci case FSIDSOURCE_FSID: 314462306a36Sopenharmony_ci p = xdr_encode_hyper(p, (u64)exp->ex_fsid); 314562306a36Sopenharmony_ci p = xdr_encode_hyper(p, (u64)0); 314662306a36Sopenharmony_ci break; 314762306a36Sopenharmony_ci case FSIDSOURCE_DEV: 314862306a36Sopenharmony_ci *p++ = cpu_to_be32(0); 314962306a36Sopenharmony_ci *p++ = cpu_to_be32(MAJOR(stat.dev)); 315062306a36Sopenharmony_ci *p++ = cpu_to_be32(0); 315162306a36Sopenharmony_ci *p++ = cpu_to_be32(MINOR(stat.dev)); 315262306a36Sopenharmony_ci break; 315362306a36Sopenharmony_ci case FSIDSOURCE_UUID: 315462306a36Sopenharmony_ci p = xdr_encode_opaque_fixed(p, exp->ex_uuid, 315562306a36Sopenharmony_ci EX_UUID_LEN); 315662306a36Sopenharmony_ci break; 315762306a36Sopenharmony_ci } 315862306a36Sopenharmony_ci } 315962306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) { 316062306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 316162306a36Sopenharmony_ci if (!p) 316262306a36Sopenharmony_ci goto out_resource; 316362306a36Sopenharmony_ci *p++ = cpu_to_be32(0); 316462306a36Sopenharmony_ci } 316562306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_LEASE_TIME) { 316662306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 316762306a36Sopenharmony_ci if (!p) 316862306a36Sopenharmony_ci goto out_resource; 316962306a36Sopenharmony_ci *p++ = cpu_to_be32(nn->nfsd4_lease); 317062306a36Sopenharmony_ci } 317162306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) { 317262306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 317362306a36Sopenharmony_ci if (!p) 317462306a36Sopenharmony_ci goto out_resource; 317562306a36Sopenharmony_ci *p++ = cpu_to_be32(rdattr_err); 317662306a36Sopenharmony_ci } 317762306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_ACL) { 317862306a36Sopenharmony_ci struct nfs4_ace *ace; 317962306a36Sopenharmony_ci 318062306a36Sopenharmony_ci if (acl == NULL) { 318162306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 318262306a36Sopenharmony_ci if (!p) 318362306a36Sopenharmony_ci goto out_resource; 318462306a36Sopenharmony_ci 318562306a36Sopenharmony_ci *p++ = cpu_to_be32(0); 318662306a36Sopenharmony_ci goto out_acl; 318762306a36Sopenharmony_ci } 318862306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 318962306a36Sopenharmony_ci if (!p) 319062306a36Sopenharmony_ci goto out_resource; 319162306a36Sopenharmony_ci *p++ = cpu_to_be32(acl->naces); 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) { 319462306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4*3); 319562306a36Sopenharmony_ci if (!p) 319662306a36Sopenharmony_ci goto out_resource; 319762306a36Sopenharmony_ci *p++ = cpu_to_be32(ace->type); 319862306a36Sopenharmony_ci *p++ = cpu_to_be32(ace->flag); 319962306a36Sopenharmony_ci *p++ = cpu_to_be32(ace->access_mask & 320062306a36Sopenharmony_ci NFS4_ACE_MASK_ALL); 320162306a36Sopenharmony_ci status = nfsd4_encode_aclname(xdr, rqstp, ace); 320262306a36Sopenharmony_ci if (status) 320362306a36Sopenharmony_ci goto out; 320462306a36Sopenharmony_ci } 320562306a36Sopenharmony_ci } 320662306a36Sopenharmony_ciout_acl: 320762306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_ACLSUPPORT) { 320862306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 320962306a36Sopenharmony_ci if (!p) 321062306a36Sopenharmony_ci goto out_resource; 321162306a36Sopenharmony_ci *p++ = cpu_to_be32(IS_POSIXACL(dentry->d_inode) ? 321262306a36Sopenharmony_ci ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0); 321362306a36Sopenharmony_ci } 321462306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_CANSETTIME) { 321562306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 321662306a36Sopenharmony_ci if (!p) 321762306a36Sopenharmony_ci goto out_resource; 321862306a36Sopenharmony_ci *p++ = cpu_to_be32(1); 321962306a36Sopenharmony_ci } 322062306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) { 322162306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 322262306a36Sopenharmony_ci if (!p) 322362306a36Sopenharmony_ci goto out_resource; 322462306a36Sopenharmony_ci *p++ = cpu_to_be32(0); 322562306a36Sopenharmony_ci } 322662306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) { 322762306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 322862306a36Sopenharmony_ci if (!p) 322962306a36Sopenharmony_ci goto out_resource; 323062306a36Sopenharmony_ci *p++ = cpu_to_be32(1); 323162306a36Sopenharmony_ci } 323262306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_CHOWN_RESTRICTED) { 323362306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 323462306a36Sopenharmony_ci if (!p) 323562306a36Sopenharmony_ci goto out_resource; 323662306a36Sopenharmony_ci *p++ = cpu_to_be32(1); 323762306a36Sopenharmony_ci } 323862306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_FILEHANDLE) { 323962306a36Sopenharmony_ci p = xdr_reserve_space(xdr, fhp->fh_handle.fh_size + 4); 324062306a36Sopenharmony_ci if (!p) 324162306a36Sopenharmony_ci goto out_resource; 324262306a36Sopenharmony_ci p = xdr_encode_opaque(p, &fhp->fh_handle.fh_raw, 324362306a36Sopenharmony_ci fhp->fh_handle.fh_size); 324462306a36Sopenharmony_ci } 324562306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_FILEID) { 324662306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 324762306a36Sopenharmony_ci if (!p) 324862306a36Sopenharmony_ci goto out_resource; 324962306a36Sopenharmony_ci p = xdr_encode_hyper(p, stat.ino); 325062306a36Sopenharmony_ci } 325162306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_FILES_AVAIL) { 325262306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 325362306a36Sopenharmony_ci if (!p) 325462306a36Sopenharmony_ci goto out_resource; 325562306a36Sopenharmony_ci p = xdr_encode_hyper(p, (u64) statfs.f_ffree); 325662306a36Sopenharmony_ci } 325762306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_FILES_FREE) { 325862306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 325962306a36Sopenharmony_ci if (!p) 326062306a36Sopenharmony_ci goto out_resource; 326162306a36Sopenharmony_ci p = xdr_encode_hyper(p, (u64) statfs.f_ffree); 326262306a36Sopenharmony_ci } 326362306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_FILES_TOTAL) { 326462306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 326562306a36Sopenharmony_ci if (!p) 326662306a36Sopenharmony_ci goto out_resource; 326762306a36Sopenharmony_ci p = xdr_encode_hyper(p, (u64) statfs.f_files); 326862306a36Sopenharmony_ci } 326962306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) { 327062306a36Sopenharmony_ci status = nfsd4_encode_fs_locations(xdr, rqstp, exp); 327162306a36Sopenharmony_ci if (status) 327262306a36Sopenharmony_ci goto out; 327362306a36Sopenharmony_ci } 327462306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) { 327562306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 327662306a36Sopenharmony_ci if (!p) 327762306a36Sopenharmony_ci goto out_resource; 327862306a36Sopenharmony_ci *p++ = cpu_to_be32(1); 327962306a36Sopenharmony_ci } 328062306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_MAXFILESIZE) { 328162306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 328262306a36Sopenharmony_ci if (!p) 328362306a36Sopenharmony_ci goto out_resource; 328462306a36Sopenharmony_ci p = xdr_encode_hyper(p, exp->ex_path.mnt->mnt_sb->s_maxbytes); 328562306a36Sopenharmony_ci } 328662306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_MAXLINK) { 328762306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 328862306a36Sopenharmony_ci if (!p) 328962306a36Sopenharmony_ci goto out_resource; 329062306a36Sopenharmony_ci *p++ = cpu_to_be32(255); 329162306a36Sopenharmony_ci } 329262306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_MAXNAME) { 329362306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 329462306a36Sopenharmony_ci if (!p) 329562306a36Sopenharmony_ci goto out_resource; 329662306a36Sopenharmony_ci *p++ = cpu_to_be32(statfs.f_namelen); 329762306a36Sopenharmony_ci } 329862306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_MAXREAD) { 329962306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 330062306a36Sopenharmony_ci if (!p) 330162306a36Sopenharmony_ci goto out_resource; 330262306a36Sopenharmony_ci p = xdr_encode_hyper(p, (u64) svc_max_payload(rqstp)); 330362306a36Sopenharmony_ci } 330462306a36Sopenharmony_ci if (bmval0 & FATTR4_WORD0_MAXWRITE) { 330562306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 330662306a36Sopenharmony_ci if (!p) 330762306a36Sopenharmony_ci goto out_resource; 330862306a36Sopenharmony_ci p = xdr_encode_hyper(p, (u64) svc_max_payload(rqstp)); 330962306a36Sopenharmony_ci } 331062306a36Sopenharmony_ci if (bmval1 & FATTR4_WORD1_MODE) { 331162306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 331262306a36Sopenharmony_ci if (!p) 331362306a36Sopenharmony_ci goto out_resource; 331462306a36Sopenharmony_ci *p++ = cpu_to_be32(stat.mode & S_IALLUGO); 331562306a36Sopenharmony_ci } 331662306a36Sopenharmony_ci if (bmval1 & FATTR4_WORD1_NO_TRUNC) { 331762306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 331862306a36Sopenharmony_ci if (!p) 331962306a36Sopenharmony_ci goto out_resource; 332062306a36Sopenharmony_ci *p++ = cpu_to_be32(1); 332162306a36Sopenharmony_ci } 332262306a36Sopenharmony_ci if (bmval1 & FATTR4_WORD1_NUMLINKS) { 332362306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 332462306a36Sopenharmony_ci if (!p) 332562306a36Sopenharmony_ci goto out_resource; 332662306a36Sopenharmony_ci *p++ = cpu_to_be32(stat.nlink); 332762306a36Sopenharmony_ci } 332862306a36Sopenharmony_ci if (bmval1 & FATTR4_WORD1_OWNER) { 332962306a36Sopenharmony_ci status = nfsd4_encode_user(xdr, rqstp, stat.uid); 333062306a36Sopenharmony_ci if (status) 333162306a36Sopenharmony_ci goto out; 333262306a36Sopenharmony_ci } 333362306a36Sopenharmony_ci if (bmval1 & FATTR4_WORD1_OWNER_GROUP) { 333462306a36Sopenharmony_ci status = nfsd4_encode_group(xdr, rqstp, stat.gid); 333562306a36Sopenharmony_ci if (status) 333662306a36Sopenharmony_ci goto out; 333762306a36Sopenharmony_ci } 333862306a36Sopenharmony_ci if (bmval1 & FATTR4_WORD1_RAWDEV) { 333962306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 334062306a36Sopenharmony_ci if (!p) 334162306a36Sopenharmony_ci goto out_resource; 334262306a36Sopenharmony_ci *p++ = cpu_to_be32((u32) MAJOR(stat.rdev)); 334362306a36Sopenharmony_ci *p++ = cpu_to_be32((u32) MINOR(stat.rdev)); 334462306a36Sopenharmony_ci } 334562306a36Sopenharmony_ci if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) { 334662306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 334762306a36Sopenharmony_ci if (!p) 334862306a36Sopenharmony_ci goto out_resource; 334962306a36Sopenharmony_ci dummy64 = (u64)statfs.f_bavail * (u64)statfs.f_bsize; 335062306a36Sopenharmony_ci p = xdr_encode_hyper(p, dummy64); 335162306a36Sopenharmony_ci } 335262306a36Sopenharmony_ci if (bmval1 & FATTR4_WORD1_SPACE_FREE) { 335362306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 335462306a36Sopenharmony_ci if (!p) 335562306a36Sopenharmony_ci goto out_resource; 335662306a36Sopenharmony_ci dummy64 = (u64)statfs.f_bfree * (u64)statfs.f_bsize; 335762306a36Sopenharmony_ci p = xdr_encode_hyper(p, dummy64); 335862306a36Sopenharmony_ci } 335962306a36Sopenharmony_ci if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) { 336062306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 336162306a36Sopenharmony_ci if (!p) 336262306a36Sopenharmony_ci goto out_resource; 336362306a36Sopenharmony_ci dummy64 = (u64)statfs.f_blocks * (u64)statfs.f_bsize; 336462306a36Sopenharmony_ci p = xdr_encode_hyper(p, dummy64); 336562306a36Sopenharmony_ci } 336662306a36Sopenharmony_ci if (bmval1 & FATTR4_WORD1_SPACE_USED) { 336762306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 336862306a36Sopenharmony_ci if (!p) 336962306a36Sopenharmony_ci goto out_resource; 337062306a36Sopenharmony_ci dummy64 = (u64)stat.blocks << 9; 337162306a36Sopenharmony_ci p = xdr_encode_hyper(p, dummy64); 337262306a36Sopenharmony_ci } 337362306a36Sopenharmony_ci if (bmval1 & FATTR4_WORD1_TIME_ACCESS) { 337462306a36Sopenharmony_ci status = nfsd4_encode_nfstime4(xdr, &stat.atime); 337562306a36Sopenharmony_ci if (status) 337662306a36Sopenharmony_ci goto out; 337762306a36Sopenharmony_ci } 337862306a36Sopenharmony_ci if (bmval1 & FATTR4_WORD1_TIME_CREATE) { 337962306a36Sopenharmony_ci status = nfsd4_encode_nfstime4(xdr, &stat.btime); 338062306a36Sopenharmony_ci if (status) 338162306a36Sopenharmony_ci goto out; 338262306a36Sopenharmony_ci } 338362306a36Sopenharmony_ci if (bmval1 & FATTR4_WORD1_TIME_DELTA) { 338462306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 12); 338562306a36Sopenharmony_ci if (!p) 338662306a36Sopenharmony_ci goto out_resource; 338762306a36Sopenharmony_ci p = encode_time_delta(p, d_inode(dentry)); 338862306a36Sopenharmony_ci } 338962306a36Sopenharmony_ci if (bmval1 & FATTR4_WORD1_TIME_METADATA) { 339062306a36Sopenharmony_ci status = nfsd4_encode_nfstime4(xdr, &stat.ctime); 339162306a36Sopenharmony_ci if (status) 339262306a36Sopenharmony_ci goto out; 339362306a36Sopenharmony_ci } 339462306a36Sopenharmony_ci if (bmval1 & FATTR4_WORD1_TIME_MODIFY) { 339562306a36Sopenharmony_ci status = nfsd4_encode_nfstime4(xdr, &stat.mtime); 339662306a36Sopenharmony_ci if (status) 339762306a36Sopenharmony_ci goto out; 339862306a36Sopenharmony_ci } 339962306a36Sopenharmony_ci if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) { 340062306a36Sopenharmony_ci u64 ino = stat.ino; 340162306a36Sopenharmony_ci 340262306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 340362306a36Sopenharmony_ci if (!p) 340462306a36Sopenharmony_ci goto out_resource; 340562306a36Sopenharmony_ci /* 340662306a36Sopenharmony_ci * Get ino of mountpoint in parent filesystem, if not ignoring 340762306a36Sopenharmony_ci * crossmount and this is the root of a cross-mounted 340862306a36Sopenharmony_ci * filesystem. 340962306a36Sopenharmony_ci */ 341062306a36Sopenharmony_ci if (ignore_crossmnt == 0 && 341162306a36Sopenharmony_ci dentry == exp->ex_path.mnt->mnt_root) { 341262306a36Sopenharmony_ci err = nfsd4_get_mounted_on_ino(exp, &ino); 341362306a36Sopenharmony_ci if (err) 341462306a36Sopenharmony_ci goto out_nfserr; 341562306a36Sopenharmony_ci } 341662306a36Sopenharmony_ci p = xdr_encode_hyper(p, ino); 341762306a36Sopenharmony_ci } 341862306a36Sopenharmony_ci#ifdef CONFIG_NFSD_PNFS 341962306a36Sopenharmony_ci if (bmval1 & FATTR4_WORD1_FS_LAYOUT_TYPES) { 342062306a36Sopenharmony_ci status = nfsd4_encode_layout_types(xdr, exp->ex_layout_types); 342162306a36Sopenharmony_ci if (status) 342262306a36Sopenharmony_ci goto out; 342362306a36Sopenharmony_ci } 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_ci if (bmval2 & FATTR4_WORD2_LAYOUT_TYPES) { 342662306a36Sopenharmony_ci status = nfsd4_encode_layout_types(xdr, exp->ex_layout_types); 342762306a36Sopenharmony_ci if (status) 342862306a36Sopenharmony_ci goto out; 342962306a36Sopenharmony_ci } 343062306a36Sopenharmony_ci 343162306a36Sopenharmony_ci if (bmval2 & FATTR4_WORD2_LAYOUT_BLKSIZE) { 343262306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 343362306a36Sopenharmony_ci if (!p) 343462306a36Sopenharmony_ci goto out_resource; 343562306a36Sopenharmony_ci *p++ = cpu_to_be32(stat.blksize); 343662306a36Sopenharmony_ci } 343762306a36Sopenharmony_ci#endif /* CONFIG_NFSD_PNFS */ 343862306a36Sopenharmony_ci if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) { 343962306a36Sopenharmony_ci u32 supp[3]; 344062306a36Sopenharmony_ci 344162306a36Sopenharmony_ci memcpy(supp, nfsd_suppattrs[minorversion], sizeof(supp)); 344262306a36Sopenharmony_ci supp[0] &= NFSD_SUPPATTR_EXCLCREAT_WORD0; 344362306a36Sopenharmony_ci supp[1] &= NFSD_SUPPATTR_EXCLCREAT_WORD1; 344462306a36Sopenharmony_ci supp[2] &= NFSD_SUPPATTR_EXCLCREAT_WORD2; 344562306a36Sopenharmony_ci 344662306a36Sopenharmony_ci status = nfsd4_encode_bitmap(xdr, supp[0], supp[1], supp[2]); 344762306a36Sopenharmony_ci if (status) 344862306a36Sopenharmony_ci goto out; 344962306a36Sopenharmony_ci } 345062306a36Sopenharmony_ci 345162306a36Sopenharmony_ci#ifdef CONFIG_NFSD_V4_SECURITY_LABEL 345262306a36Sopenharmony_ci if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) { 345362306a36Sopenharmony_ci status = nfsd4_encode_security_label(xdr, rqstp, context, 345462306a36Sopenharmony_ci contextlen); 345562306a36Sopenharmony_ci if (status) 345662306a36Sopenharmony_ci goto out; 345762306a36Sopenharmony_ci } 345862306a36Sopenharmony_ci#endif 345962306a36Sopenharmony_ci 346062306a36Sopenharmony_ci if (bmval2 & FATTR4_WORD2_XATTR_SUPPORT) { 346162306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 346262306a36Sopenharmony_ci if (!p) 346362306a36Sopenharmony_ci goto out_resource; 346462306a36Sopenharmony_ci err = xattr_supports_user_prefix(d_inode(dentry)); 346562306a36Sopenharmony_ci *p++ = cpu_to_be32(err == 0); 346662306a36Sopenharmony_ci } 346762306a36Sopenharmony_ci 346862306a36Sopenharmony_ci *attrlen_p = cpu_to_be32(xdr->buf->len - attrlen_offset - XDR_UNIT); 346962306a36Sopenharmony_ci status = nfs_ok; 347062306a36Sopenharmony_ci 347162306a36Sopenharmony_ciout: 347262306a36Sopenharmony_ci#ifdef CONFIG_NFSD_V4_SECURITY_LABEL 347362306a36Sopenharmony_ci if (context) 347462306a36Sopenharmony_ci security_release_secctx(context, contextlen); 347562306a36Sopenharmony_ci#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */ 347662306a36Sopenharmony_ci kfree(acl); 347762306a36Sopenharmony_ci if (tempfh) { 347862306a36Sopenharmony_ci fh_put(tempfh); 347962306a36Sopenharmony_ci kfree(tempfh); 348062306a36Sopenharmony_ci } 348162306a36Sopenharmony_ci if (status) 348262306a36Sopenharmony_ci xdr_truncate_encode(xdr, starting_len); 348362306a36Sopenharmony_ci return status; 348462306a36Sopenharmony_ciout_nfserr: 348562306a36Sopenharmony_ci status = nfserrno(err); 348662306a36Sopenharmony_ci goto out; 348762306a36Sopenharmony_ciout_resource: 348862306a36Sopenharmony_ci status = nfserr_resource; 348962306a36Sopenharmony_ci goto out; 349062306a36Sopenharmony_ci} 349162306a36Sopenharmony_ci 349262306a36Sopenharmony_cistatic void svcxdr_init_encode_from_buffer(struct xdr_stream *xdr, 349362306a36Sopenharmony_ci struct xdr_buf *buf, __be32 *p, int bytes) 349462306a36Sopenharmony_ci{ 349562306a36Sopenharmony_ci xdr->scratch.iov_len = 0; 349662306a36Sopenharmony_ci memset(buf, 0, sizeof(struct xdr_buf)); 349762306a36Sopenharmony_ci buf->head[0].iov_base = p; 349862306a36Sopenharmony_ci buf->head[0].iov_len = 0; 349962306a36Sopenharmony_ci buf->len = 0; 350062306a36Sopenharmony_ci xdr->buf = buf; 350162306a36Sopenharmony_ci xdr->iov = buf->head; 350262306a36Sopenharmony_ci xdr->p = p; 350362306a36Sopenharmony_ci xdr->end = (void *)p + bytes; 350462306a36Sopenharmony_ci buf->buflen = bytes; 350562306a36Sopenharmony_ci} 350662306a36Sopenharmony_ci 350762306a36Sopenharmony_ci__be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words, 350862306a36Sopenharmony_ci struct svc_fh *fhp, struct svc_export *exp, 350962306a36Sopenharmony_ci struct dentry *dentry, u32 *bmval, 351062306a36Sopenharmony_ci struct svc_rqst *rqstp, int ignore_crossmnt) 351162306a36Sopenharmony_ci{ 351262306a36Sopenharmony_ci struct xdr_buf dummy; 351362306a36Sopenharmony_ci struct xdr_stream xdr; 351462306a36Sopenharmony_ci __be32 ret; 351562306a36Sopenharmony_ci 351662306a36Sopenharmony_ci svcxdr_init_encode_from_buffer(&xdr, &dummy, *p, words << 2); 351762306a36Sopenharmony_ci ret = nfsd4_encode_fattr(&xdr, fhp, exp, dentry, bmval, rqstp, 351862306a36Sopenharmony_ci ignore_crossmnt); 351962306a36Sopenharmony_ci *p = xdr.p; 352062306a36Sopenharmony_ci return ret; 352162306a36Sopenharmony_ci} 352262306a36Sopenharmony_ci 352362306a36Sopenharmony_cistatic inline int attributes_need_mount(u32 *bmval) 352462306a36Sopenharmony_ci{ 352562306a36Sopenharmony_ci if (bmval[0] & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_LEASE_TIME)) 352662306a36Sopenharmony_ci return 1; 352762306a36Sopenharmony_ci if (bmval[1] & ~FATTR4_WORD1_MOUNTED_ON_FILEID) 352862306a36Sopenharmony_ci return 1; 352962306a36Sopenharmony_ci return 0; 353062306a36Sopenharmony_ci} 353162306a36Sopenharmony_ci 353262306a36Sopenharmony_cistatic __be32 353362306a36Sopenharmony_cinfsd4_encode_dirent_fattr(struct xdr_stream *xdr, struct nfsd4_readdir *cd, 353462306a36Sopenharmony_ci const char *name, int namlen) 353562306a36Sopenharmony_ci{ 353662306a36Sopenharmony_ci struct svc_export *exp = cd->rd_fhp->fh_export; 353762306a36Sopenharmony_ci struct dentry *dentry; 353862306a36Sopenharmony_ci __be32 nfserr; 353962306a36Sopenharmony_ci int ignore_crossmnt = 0; 354062306a36Sopenharmony_ci 354162306a36Sopenharmony_ci dentry = lookup_positive_unlocked(name, cd->rd_fhp->fh_dentry, namlen); 354262306a36Sopenharmony_ci if (IS_ERR(dentry)) 354362306a36Sopenharmony_ci return nfserrno(PTR_ERR(dentry)); 354462306a36Sopenharmony_ci 354562306a36Sopenharmony_ci exp_get(exp); 354662306a36Sopenharmony_ci /* 354762306a36Sopenharmony_ci * In the case of a mountpoint, the client may be asking for 354862306a36Sopenharmony_ci * attributes that are only properties of the underlying filesystem 354962306a36Sopenharmony_ci * as opposed to the cross-mounted file system. In such a case, 355062306a36Sopenharmony_ci * we will not follow the cross mount and will fill the attribtutes 355162306a36Sopenharmony_ci * directly from the mountpoint dentry. 355262306a36Sopenharmony_ci */ 355362306a36Sopenharmony_ci if (nfsd_mountpoint(dentry, exp)) { 355462306a36Sopenharmony_ci int err; 355562306a36Sopenharmony_ci 355662306a36Sopenharmony_ci if (!(exp->ex_flags & NFSEXP_V4ROOT) 355762306a36Sopenharmony_ci && !attributes_need_mount(cd->rd_bmval)) { 355862306a36Sopenharmony_ci ignore_crossmnt = 1; 355962306a36Sopenharmony_ci goto out_encode; 356062306a36Sopenharmony_ci } 356162306a36Sopenharmony_ci /* 356262306a36Sopenharmony_ci * Why the heck aren't we just using nfsd_lookup?? 356362306a36Sopenharmony_ci * Different "."/".." handling? Something else? 356462306a36Sopenharmony_ci * At least, add a comment here to explain.... 356562306a36Sopenharmony_ci */ 356662306a36Sopenharmony_ci err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp); 356762306a36Sopenharmony_ci if (err) { 356862306a36Sopenharmony_ci nfserr = nfserrno(err); 356962306a36Sopenharmony_ci goto out_put; 357062306a36Sopenharmony_ci } 357162306a36Sopenharmony_ci nfserr = check_nfsd_access(exp, cd->rd_rqstp); 357262306a36Sopenharmony_ci if (nfserr) 357362306a36Sopenharmony_ci goto out_put; 357462306a36Sopenharmony_ci 357562306a36Sopenharmony_ci } 357662306a36Sopenharmony_ciout_encode: 357762306a36Sopenharmony_ci nfserr = nfsd4_encode_fattr(xdr, NULL, exp, dentry, cd->rd_bmval, 357862306a36Sopenharmony_ci cd->rd_rqstp, ignore_crossmnt); 357962306a36Sopenharmony_ciout_put: 358062306a36Sopenharmony_ci dput(dentry); 358162306a36Sopenharmony_ci exp_put(exp); 358262306a36Sopenharmony_ci return nfserr; 358362306a36Sopenharmony_ci} 358462306a36Sopenharmony_ci 358562306a36Sopenharmony_cistatic __be32 * 358662306a36Sopenharmony_cinfsd4_encode_rdattr_error(struct xdr_stream *xdr, __be32 nfserr) 358762306a36Sopenharmony_ci{ 358862306a36Sopenharmony_ci __be32 *p; 358962306a36Sopenharmony_ci 359062306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 20); 359162306a36Sopenharmony_ci if (!p) 359262306a36Sopenharmony_ci return NULL; 359362306a36Sopenharmony_ci *p++ = htonl(2); 359462306a36Sopenharmony_ci *p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */ 359562306a36Sopenharmony_ci *p++ = htonl(0); /* bmval1 */ 359662306a36Sopenharmony_ci 359762306a36Sopenharmony_ci *p++ = htonl(4); /* attribute length */ 359862306a36Sopenharmony_ci *p++ = nfserr; /* no htonl */ 359962306a36Sopenharmony_ci return p; 360062306a36Sopenharmony_ci} 360162306a36Sopenharmony_ci 360262306a36Sopenharmony_cistatic int 360362306a36Sopenharmony_cinfsd4_encode_dirent(void *ccdv, const char *name, int namlen, 360462306a36Sopenharmony_ci loff_t offset, u64 ino, unsigned int d_type) 360562306a36Sopenharmony_ci{ 360662306a36Sopenharmony_ci struct readdir_cd *ccd = ccdv; 360762306a36Sopenharmony_ci struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common); 360862306a36Sopenharmony_ci struct xdr_stream *xdr = cd->xdr; 360962306a36Sopenharmony_ci int start_offset = xdr->buf->len; 361062306a36Sopenharmony_ci int cookie_offset; 361162306a36Sopenharmony_ci u32 name_and_cookie; 361262306a36Sopenharmony_ci int entry_bytes; 361362306a36Sopenharmony_ci __be32 nfserr = nfserr_toosmall; 361462306a36Sopenharmony_ci __be64 wire_offset; 361562306a36Sopenharmony_ci __be32 *p; 361662306a36Sopenharmony_ci 361762306a36Sopenharmony_ci /* In nfsv4, "." and ".." never make it onto the wire.. */ 361862306a36Sopenharmony_ci if (name && isdotent(name, namlen)) { 361962306a36Sopenharmony_ci cd->common.err = nfs_ok; 362062306a36Sopenharmony_ci return 0; 362162306a36Sopenharmony_ci } 362262306a36Sopenharmony_ci 362362306a36Sopenharmony_ci if (cd->cookie_offset) { 362462306a36Sopenharmony_ci wire_offset = cpu_to_be64(offset); 362562306a36Sopenharmony_ci write_bytes_to_xdr_buf(xdr->buf, cd->cookie_offset, 362662306a36Sopenharmony_ci &wire_offset, 8); 362762306a36Sopenharmony_ci } 362862306a36Sopenharmony_ci 362962306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 363062306a36Sopenharmony_ci if (!p) 363162306a36Sopenharmony_ci goto fail; 363262306a36Sopenharmony_ci *p++ = xdr_one; /* mark entry present */ 363362306a36Sopenharmony_ci cookie_offset = xdr->buf->len; 363462306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 3*4 + namlen); 363562306a36Sopenharmony_ci if (!p) 363662306a36Sopenharmony_ci goto fail; 363762306a36Sopenharmony_ci p = xdr_encode_hyper(p, OFFSET_MAX); /* offset of next entry */ 363862306a36Sopenharmony_ci p = xdr_encode_array(p, name, namlen); /* name length & name */ 363962306a36Sopenharmony_ci 364062306a36Sopenharmony_ci nfserr = nfsd4_encode_dirent_fattr(xdr, cd, name, namlen); 364162306a36Sopenharmony_ci switch (nfserr) { 364262306a36Sopenharmony_ci case nfs_ok: 364362306a36Sopenharmony_ci break; 364462306a36Sopenharmony_ci case nfserr_resource: 364562306a36Sopenharmony_ci nfserr = nfserr_toosmall; 364662306a36Sopenharmony_ci goto fail; 364762306a36Sopenharmony_ci case nfserr_noent: 364862306a36Sopenharmony_ci xdr_truncate_encode(xdr, start_offset); 364962306a36Sopenharmony_ci goto skip_entry; 365062306a36Sopenharmony_ci case nfserr_jukebox: 365162306a36Sopenharmony_ci /* 365262306a36Sopenharmony_ci * The pseudoroot should only display dentries that lead to 365362306a36Sopenharmony_ci * exports. If we get EJUKEBOX here, then we can't tell whether 365462306a36Sopenharmony_ci * this entry should be included. Just fail the whole READDIR 365562306a36Sopenharmony_ci * with NFS4ERR_DELAY in that case, and hope that the situation 365662306a36Sopenharmony_ci * will resolve itself by the client's next attempt. 365762306a36Sopenharmony_ci */ 365862306a36Sopenharmony_ci if (cd->rd_fhp->fh_export->ex_flags & NFSEXP_V4ROOT) 365962306a36Sopenharmony_ci goto fail; 366062306a36Sopenharmony_ci fallthrough; 366162306a36Sopenharmony_ci default: 366262306a36Sopenharmony_ci /* 366362306a36Sopenharmony_ci * If the client requested the RDATTR_ERROR attribute, 366462306a36Sopenharmony_ci * we stuff the error code into this attribute 366562306a36Sopenharmony_ci * and continue. If this attribute was not requested, 366662306a36Sopenharmony_ci * then in accordance with the spec, we fail the 366762306a36Sopenharmony_ci * entire READDIR operation(!) 366862306a36Sopenharmony_ci */ 366962306a36Sopenharmony_ci if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR)) 367062306a36Sopenharmony_ci goto fail; 367162306a36Sopenharmony_ci p = nfsd4_encode_rdattr_error(xdr, nfserr); 367262306a36Sopenharmony_ci if (p == NULL) { 367362306a36Sopenharmony_ci nfserr = nfserr_toosmall; 367462306a36Sopenharmony_ci goto fail; 367562306a36Sopenharmony_ci } 367662306a36Sopenharmony_ci } 367762306a36Sopenharmony_ci nfserr = nfserr_toosmall; 367862306a36Sopenharmony_ci entry_bytes = xdr->buf->len - start_offset; 367962306a36Sopenharmony_ci if (entry_bytes > cd->rd_maxcount) 368062306a36Sopenharmony_ci goto fail; 368162306a36Sopenharmony_ci cd->rd_maxcount -= entry_bytes; 368262306a36Sopenharmony_ci /* 368362306a36Sopenharmony_ci * RFC 3530 14.2.24 describes rd_dircount as only a "hint", and 368462306a36Sopenharmony_ci * notes that it could be zero. If it is zero, then the server 368562306a36Sopenharmony_ci * should enforce only the rd_maxcount value. 368662306a36Sopenharmony_ci */ 368762306a36Sopenharmony_ci if (cd->rd_dircount) { 368862306a36Sopenharmony_ci name_and_cookie = 4 + 4 * XDR_QUADLEN(namlen) + 8; 368962306a36Sopenharmony_ci if (name_and_cookie > cd->rd_dircount && cd->cookie_offset) 369062306a36Sopenharmony_ci goto fail; 369162306a36Sopenharmony_ci cd->rd_dircount -= min(cd->rd_dircount, name_and_cookie); 369262306a36Sopenharmony_ci if (!cd->rd_dircount) 369362306a36Sopenharmony_ci cd->rd_maxcount = 0; 369462306a36Sopenharmony_ci } 369562306a36Sopenharmony_ci 369662306a36Sopenharmony_ci cd->cookie_offset = cookie_offset; 369762306a36Sopenharmony_ciskip_entry: 369862306a36Sopenharmony_ci cd->common.err = nfs_ok; 369962306a36Sopenharmony_ci return 0; 370062306a36Sopenharmony_cifail: 370162306a36Sopenharmony_ci xdr_truncate_encode(xdr, start_offset); 370262306a36Sopenharmony_ci cd->common.err = nfserr; 370362306a36Sopenharmony_ci return -EINVAL; 370462306a36Sopenharmony_ci} 370562306a36Sopenharmony_ci 370662306a36Sopenharmony_cistatic __be32 370762306a36Sopenharmony_cinfsd4_encode_verifier4(struct xdr_stream *xdr, const nfs4_verifier *verf) 370862306a36Sopenharmony_ci{ 370962306a36Sopenharmony_ci __be32 *p; 371062306a36Sopenharmony_ci 371162306a36Sopenharmony_ci p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE); 371262306a36Sopenharmony_ci if (!p) 371362306a36Sopenharmony_ci return nfserr_resource; 371462306a36Sopenharmony_ci memcpy(p, verf->data, sizeof(verf->data)); 371562306a36Sopenharmony_ci return nfs_ok; 371662306a36Sopenharmony_ci} 371762306a36Sopenharmony_ci 371862306a36Sopenharmony_cistatic __be32 371962306a36Sopenharmony_cinfsd4_encode_clientid4(struct xdr_stream *xdr, const clientid_t *clientid) 372062306a36Sopenharmony_ci{ 372162306a36Sopenharmony_ci __be32 *p; 372262306a36Sopenharmony_ci 372362306a36Sopenharmony_ci p = xdr_reserve_space(xdr, sizeof(__be64)); 372462306a36Sopenharmony_ci if (!p) 372562306a36Sopenharmony_ci return nfserr_resource; 372662306a36Sopenharmony_ci memcpy(p, clientid, sizeof(*clientid)); 372762306a36Sopenharmony_ci return nfs_ok; 372862306a36Sopenharmony_ci} 372962306a36Sopenharmony_ci 373062306a36Sopenharmony_cistatic __be32 373162306a36Sopenharmony_cinfsd4_encode_stateid(struct xdr_stream *xdr, stateid_t *sid) 373262306a36Sopenharmony_ci{ 373362306a36Sopenharmony_ci __be32 *p; 373462306a36Sopenharmony_ci 373562306a36Sopenharmony_ci p = xdr_reserve_space(xdr, sizeof(stateid_t)); 373662306a36Sopenharmony_ci if (!p) 373762306a36Sopenharmony_ci return nfserr_resource; 373862306a36Sopenharmony_ci *p++ = cpu_to_be32(sid->si_generation); 373962306a36Sopenharmony_ci p = xdr_encode_opaque_fixed(p, &sid->si_opaque, 374062306a36Sopenharmony_ci sizeof(stateid_opaque_t)); 374162306a36Sopenharmony_ci return 0; 374262306a36Sopenharmony_ci} 374362306a36Sopenharmony_ci 374462306a36Sopenharmony_cistatic __be32 374562306a36Sopenharmony_cinfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, 374662306a36Sopenharmony_ci union nfsd4_op_u *u) 374762306a36Sopenharmony_ci{ 374862306a36Sopenharmony_ci struct nfsd4_access *access = &u->access; 374962306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 375062306a36Sopenharmony_ci __be32 *p; 375162306a36Sopenharmony_ci 375262306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 375362306a36Sopenharmony_ci if (!p) 375462306a36Sopenharmony_ci return nfserr_resource; 375562306a36Sopenharmony_ci *p++ = cpu_to_be32(access->ac_supported); 375662306a36Sopenharmony_ci *p++ = cpu_to_be32(access->ac_resp_access); 375762306a36Sopenharmony_ci return 0; 375862306a36Sopenharmony_ci} 375962306a36Sopenharmony_ci 376062306a36Sopenharmony_cistatic __be32 nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres *resp, __be32 nfserr, 376162306a36Sopenharmony_ci union nfsd4_op_u *u) 376262306a36Sopenharmony_ci{ 376362306a36Sopenharmony_ci struct nfsd4_bind_conn_to_session *bcts = &u->bind_conn_to_session; 376462306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 376562306a36Sopenharmony_ci __be32 *p; 376662306a36Sopenharmony_ci 376762306a36Sopenharmony_ci p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 8); 376862306a36Sopenharmony_ci if (!p) 376962306a36Sopenharmony_ci return nfserr_resource; 377062306a36Sopenharmony_ci p = xdr_encode_opaque_fixed(p, bcts->sessionid.data, 377162306a36Sopenharmony_ci NFS4_MAX_SESSIONID_LEN); 377262306a36Sopenharmony_ci *p++ = cpu_to_be32(bcts->dir); 377362306a36Sopenharmony_ci /* Upshifting from TCP to RDMA is not supported */ 377462306a36Sopenharmony_ci *p++ = cpu_to_be32(0); 377562306a36Sopenharmony_ci return 0; 377662306a36Sopenharmony_ci} 377762306a36Sopenharmony_ci 377862306a36Sopenharmony_cistatic __be32 377962306a36Sopenharmony_cinfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, 378062306a36Sopenharmony_ci union nfsd4_op_u *u) 378162306a36Sopenharmony_ci{ 378262306a36Sopenharmony_ci struct nfsd4_close *close = &u->close; 378362306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 378462306a36Sopenharmony_ci 378562306a36Sopenharmony_ci return nfsd4_encode_stateid(xdr, &close->cl_stateid); 378662306a36Sopenharmony_ci} 378762306a36Sopenharmony_ci 378862306a36Sopenharmony_ci 378962306a36Sopenharmony_cistatic __be32 379062306a36Sopenharmony_cinfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, 379162306a36Sopenharmony_ci union nfsd4_op_u *u) 379262306a36Sopenharmony_ci{ 379362306a36Sopenharmony_ci struct nfsd4_commit *commit = &u->commit; 379462306a36Sopenharmony_ci 379562306a36Sopenharmony_ci return nfsd4_encode_verifier4(resp->xdr, &commit->co_verf); 379662306a36Sopenharmony_ci} 379762306a36Sopenharmony_ci 379862306a36Sopenharmony_cistatic __be32 379962306a36Sopenharmony_cinfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr, 380062306a36Sopenharmony_ci union nfsd4_op_u *u) 380162306a36Sopenharmony_ci{ 380262306a36Sopenharmony_ci struct nfsd4_create *create = &u->create; 380362306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 380462306a36Sopenharmony_ci 380562306a36Sopenharmony_ci nfserr = nfsd4_encode_change_info4(xdr, &create->cr_cinfo); 380662306a36Sopenharmony_ci if (nfserr) 380762306a36Sopenharmony_ci return nfserr; 380862306a36Sopenharmony_ci return nfsd4_encode_bitmap(xdr, create->cr_bmval[0], 380962306a36Sopenharmony_ci create->cr_bmval[1], create->cr_bmval[2]); 381062306a36Sopenharmony_ci} 381162306a36Sopenharmony_ci 381262306a36Sopenharmony_cistatic __be32 381362306a36Sopenharmony_cinfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, 381462306a36Sopenharmony_ci union nfsd4_op_u *u) 381562306a36Sopenharmony_ci{ 381662306a36Sopenharmony_ci struct nfsd4_getattr *getattr = &u->getattr; 381762306a36Sopenharmony_ci struct svc_fh *fhp = getattr->ga_fhp; 381862306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 381962306a36Sopenharmony_ci 382062306a36Sopenharmony_ci return nfsd4_encode_fattr(xdr, fhp, fhp->fh_export, fhp->fh_dentry, 382162306a36Sopenharmony_ci getattr->ga_bmval, resp->rqstp, 0); 382262306a36Sopenharmony_ci} 382362306a36Sopenharmony_ci 382462306a36Sopenharmony_cistatic __be32 382562306a36Sopenharmony_cinfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, 382662306a36Sopenharmony_ci union nfsd4_op_u *u) 382762306a36Sopenharmony_ci{ 382862306a36Sopenharmony_ci struct svc_fh **fhpp = &u->getfh; 382962306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 383062306a36Sopenharmony_ci struct svc_fh *fhp = *fhpp; 383162306a36Sopenharmony_ci unsigned int len; 383262306a36Sopenharmony_ci __be32 *p; 383362306a36Sopenharmony_ci 383462306a36Sopenharmony_ci len = fhp->fh_handle.fh_size; 383562306a36Sopenharmony_ci p = xdr_reserve_space(xdr, len + 4); 383662306a36Sopenharmony_ci if (!p) 383762306a36Sopenharmony_ci return nfserr_resource; 383862306a36Sopenharmony_ci p = xdr_encode_opaque(p, &fhp->fh_handle.fh_raw, len); 383962306a36Sopenharmony_ci return 0; 384062306a36Sopenharmony_ci} 384162306a36Sopenharmony_ci 384262306a36Sopenharmony_ci/* 384362306a36Sopenharmony_ci* Including all fields other than the name, a LOCK4denied structure requires 384462306a36Sopenharmony_ci* 8(clientid) + 4(namelen) + 8(offset) + 8(length) + 4(type) = 32 bytes. 384562306a36Sopenharmony_ci*/ 384662306a36Sopenharmony_cistatic __be32 384762306a36Sopenharmony_cinfsd4_encode_lock_denied(struct xdr_stream *xdr, struct nfsd4_lock_denied *ld) 384862306a36Sopenharmony_ci{ 384962306a36Sopenharmony_ci struct xdr_netobj *conf = &ld->ld_owner; 385062306a36Sopenharmony_ci __be32 *p; 385162306a36Sopenharmony_ci 385262306a36Sopenharmony_ciagain: 385362306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 32 + XDR_LEN(conf->len)); 385462306a36Sopenharmony_ci if (!p) { 385562306a36Sopenharmony_ci /* 385662306a36Sopenharmony_ci * Don't fail to return the result just because we can't 385762306a36Sopenharmony_ci * return the conflicting open: 385862306a36Sopenharmony_ci */ 385962306a36Sopenharmony_ci if (conf->len) { 386062306a36Sopenharmony_ci kfree(conf->data); 386162306a36Sopenharmony_ci conf->len = 0; 386262306a36Sopenharmony_ci conf->data = NULL; 386362306a36Sopenharmony_ci goto again; 386462306a36Sopenharmony_ci } 386562306a36Sopenharmony_ci return nfserr_resource; 386662306a36Sopenharmony_ci } 386762306a36Sopenharmony_ci p = xdr_encode_hyper(p, ld->ld_start); 386862306a36Sopenharmony_ci p = xdr_encode_hyper(p, ld->ld_length); 386962306a36Sopenharmony_ci *p++ = cpu_to_be32(ld->ld_type); 387062306a36Sopenharmony_ci if (conf->len) { 387162306a36Sopenharmony_ci p = xdr_encode_opaque_fixed(p, &ld->ld_clientid, 8); 387262306a36Sopenharmony_ci p = xdr_encode_opaque(p, conf->data, conf->len); 387362306a36Sopenharmony_ci kfree(conf->data); 387462306a36Sopenharmony_ci } else { /* non - nfsv4 lock in conflict, no clientid nor owner */ 387562306a36Sopenharmony_ci p = xdr_encode_hyper(p, (u64)0); /* clientid */ 387662306a36Sopenharmony_ci *p++ = cpu_to_be32(0); /* length of owner name */ 387762306a36Sopenharmony_ci } 387862306a36Sopenharmony_ci return nfserr_denied; 387962306a36Sopenharmony_ci} 388062306a36Sopenharmony_ci 388162306a36Sopenharmony_cistatic __be32 388262306a36Sopenharmony_cinfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, 388362306a36Sopenharmony_ci union nfsd4_op_u *u) 388462306a36Sopenharmony_ci{ 388562306a36Sopenharmony_ci struct nfsd4_lock *lock = &u->lock; 388662306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 388762306a36Sopenharmony_ci 388862306a36Sopenharmony_ci if (!nfserr) 388962306a36Sopenharmony_ci nfserr = nfsd4_encode_stateid(xdr, &lock->lk_resp_stateid); 389062306a36Sopenharmony_ci else if (nfserr == nfserr_denied) 389162306a36Sopenharmony_ci nfserr = nfsd4_encode_lock_denied(xdr, &lock->lk_denied); 389262306a36Sopenharmony_ci 389362306a36Sopenharmony_ci return nfserr; 389462306a36Sopenharmony_ci} 389562306a36Sopenharmony_ci 389662306a36Sopenharmony_cistatic __be32 389762306a36Sopenharmony_cinfsd4_encode_lockt(struct nfsd4_compoundres *resp, __be32 nfserr, 389862306a36Sopenharmony_ci union nfsd4_op_u *u) 389962306a36Sopenharmony_ci{ 390062306a36Sopenharmony_ci struct nfsd4_lockt *lockt = &u->lockt; 390162306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 390262306a36Sopenharmony_ci 390362306a36Sopenharmony_ci if (nfserr == nfserr_denied) 390462306a36Sopenharmony_ci nfsd4_encode_lock_denied(xdr, &lockt->lt_denied); 390562306a36Sopenharmony_ci return nfserr; 390662306a36Sopenharmony_ci} 390762306a36Sopenharmony_ci 390862306a36Sopenharmony_cistatic __be32 390962306a36Sopenharmony_cinfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, 391062306a36Sopenharmony_ci union nfsd4_op_u *u) 391162306a36Sopenharmony_ci{ 391262306a36Sopenharmony_ci struct nfsd4_locku *locku = &u->locku; 391362306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 391462306a36Sopenharmony_ci 391562306a36Sopenharmony_ci return nfsd4_encode_stateid(xdr, &locku->lu_stateid); 391662306a36Sopenharmony_ci} 391762306a36Sopenharmony_ci 391862306a36Sopenharmony_ci 391962306a36Sopenharmony_cistatic __be32 392062306a36Sopenharmony_cinfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, 392162306a36Sopenharmony_ci union nfsd4_op_u *u) 392262306a36Sopenharmony_ci{ 392362306a36Sopenharmony_ci struct nfsd4_link *link = &u->link; 392462306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 392562306a36Sopenharmony_ci 392662306a36Sopenharmony_ci return nfsd4_encode_change_info4(xdr, &link->li_cinfo); 392762306a36Sopenharmony_ci} 392862306a36Sopenharmony_ci 392962306a36Sopenharmony_ci 393062306a36Sopenharmony_cistatic __be32 393162306a36Sopenharmony_cinfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, 393262306a36Sopenharmony_ci union nfsd4_op_u *u) 393362306a36Sopenharmony_ci{ 393462306a36Sopenharmony_ci struct nfsd4_open *open = &u->open; 393562306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 393662306a36Sopenharmony_ci __be32 *p; 393762306a36Sopenharmony_ci 393862306a36Sopenharmony_ci nfserr = nfsd4_encode_stateid(xdr, &open->op_stateid); 393962306a36Sopenharmony_ci if (nfserr) 394062306a36Sopenharmony_ci return nfserr; 394162306a36Sopenharmony_ci nfserr = nfsd4_encode_change_info4(xdr, &open->op_cinfo); 394262306a36Sopenharmony_ci if (nfserr) 394362306a36Sopenharmony_ci return nfserr; 394462306a36Sopenharmony_ci if (xdr_stream_encode_u32(xdr, open->op_rflags) < 0) 394562306a36Sopenharmony_ci return nfserr_resource; 394662306a36Sopenharmony_ci 394762306a36Sopenharmony_ci nfserr = nfsd4_encode_bitmap(xdr, open->op_bmval[0], open->op_bmval[1], 394862306a36Sopenharmony_ci open->op_bmval[2]); 394962306a36Sopenharmony_ci if (nfserr) 395062306a36Sopenharmony_ci return nfserr; 395162306a36Sopenharmony_ci 395262306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 395362306a36Sopenharmony_ci if (!p) 395462306a36Sopenharmony_ci return nfserr_resource; 395562306a36Sopenharmony_ci 395662306a36Sopenharmony_ci *p++ = cpu_to_be32(open->op_delegate_type); 395762306a36Sopenharmony_ci switch (open->op_delegate_type) { 395862306a36Sopenharmony_ci case NFS4_OPEN_DELEGATE_NONE: 395962306a36Sopenharmony_ci break; 396062306a36Sopenharmony_ci case NFS4_OPEN_DELEGATE_READ: 396162306a36Sopenharmony_ci nfserr = nfsd4_encode_stateid(xdr, &open->op_delegate_stateid); 396262306a36Sopenharmony_ci if (nfserr) 396362306a36Sopenharmony_ci return nfserr; 396462306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 20); 396562306a36Sopenharmony_ci if (!p) 396662306a36Sopenharmony_ci return nfserr_resource; 396762306a36Sopenharmony_ci *p++ = cpu_to_be32(open->op_recall); 396862306a36Sopenharmony_ci 396962306a36Sopenharmony_ci /* 397062306a36Sopenharmony_ci * TODO: ACE's in delegations 397162306a36Sopenharmony_ci */ 397262306a36Sopenharmony_ci *p++ = cpu_to_be32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE); 397362306a36Sopenharmony_ci *p++ = cpu_to_be32(0); 397462306a36Sopenharmony_ci *p++ = cpu_to_be32(0); 397562306a36Sopenharmony_ci *p++ = cpu_to_be32(0); /* XXX: is NULL principal ok? */ 397662306a36Sopenharmony_ci break; 397762306a36Sopenharmony_ci case NFS4_OPEN_DELEGATE_WRITE: 397862306a36Sopenharmony_ci nfserr = nfsd4_encode_stateid(xdr, &open->op_delegate_stateid); 397962306a36Sopenharmony_ci if (nfserr) 398062306a36Sopenharmony_ci return nfserr; 398162306a36Sopenharmony_ci 398262306a36Sopenharmony_ci p = xdr_reserve_space(xdr, XDR_UNIT * 8); 398362306a36Sopenharmony_ci if (!p) 398462306a36Sopenharmony_ci return nfserr_resource; 398562306a36Sopenharmony_ci *p++ = cpu_to_be32(open->op_recall); 398662306a36Sopenharmony_ci 398762306a36Sopenharmony_ci /* 398862306a36Sopenharmony_ci * Always flush on close 398962306a36Sopenharmony_ci * 399062306a36Sopenharmony_ci * TODO: space_limit's in delegations 399162306a36Sopenharmony_ci */ 399262306a36Sopenharmony_ci *p++ = cpu_to_be32(NFS4_LIMIT_SIZE); 399362306a36Sopenharmony_ci *p++ = xdr_zero; 399462306a36Sopenharmony_ci *p++ = xdr_zero; 399562306a36Sopenharmony_ci 399662306a36Sopenharmony_ci /* 399762306a36Sopenharmony_ci * TODO: ACE's in delegations 399862306a36Sopenharmony_ci */ 399962306a36Sopenharmony_ci *p++ = cpu_to_be32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE); 400062306a36Sopenharmony_ci *p++ = cpu_to_be32(0); 400162306a36Sopenharmony_ci *p++ = cpu_to_be32(0); 400262306a36Sopenharmony_ci *p++ = cpu_to_be32(0); /* XXX: is NULL principal ok? */ 400362306a36Sopenharmony_ci break; 400462306a36Sopenharmony_ci case NFS4_OPEN_DELEGATE_NONE_EXT: /* 4.1 */ 400562306a36Sopenharmony_ci switch (open->op_why_no_deleg) { 400662306a36Sopenharmony_ci case WND4_CONTENTION: 400762306a36Sopenharmony_ci case WND4_RESOURCE: 400862306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 400962306a36Sopenharmony_ci if (!p) 401062306a36Sopenharmony_ci return nfserr_resource; 401162306a36Sopenharmony_ci *p++ = cpu_to_be32(open->op_why_no_deleg); 401262306a36Sopenharmony_ci /* deleg signaling not supported yet: */ 401362306a36Sopenharmony_ci *p++ = cpu_to_be32(0); 401462306a36Sopenharmony_ci break; 401562306a36Sopenharmony_ci default: 401662306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 401762306a36Sopenharmony_ci if (!p) 401862306a36Sopenharmony_ci return nfserr_resource; 401962306a36Sopenharmony_ci *p++ = cpu_to_be32(open->op_why_no_deleg); 402062306a36Sopenharmony_ci } 402162306a36Sopenharmony_ci break; 402262306a36Sopenharmony_ci default: 402362306a36Sopenharmony_ci BUG(); 402462306a36Sopenharmony_ci } 402562306a36Sopenharmony_ci /* XXX save filehandle here */ 402662306a36Sopenharmony_ci return 0; 402762306a36Sopenharmony_ci} 402862306a36Sopenharmony_ci 402962306a36Sopenharmony_cistatic __be32 403062306a36Sopenharmony_cinfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, 403162306a36Sopenharmony_ci union nfsd4_op_u *u) 403262306a36Sopenharmony_ci{ 403362306a36Sopenharmony_ci struct nfsd4_open_confirm *oc = &u->open_confirm; 403462306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 403562306a36Sopenharmony_ci 403662306a36Sopenharmony_ci return nfsd4_encode_stateid(xdr, &oc->oc_resp_stateid); 403762306a36Sopenharmony_ci} 403862306a36Sopenharmony_ci 403962306a36Sopenharmony_cistatic __be32 404062306a36Sopenharmony_cinfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, 404162306a36Sopenharmony_ci union nfsd4_op_u *u) 404262306a36Sopenharmony_ci{ 404362306a36Sopenharmony_ci struct nfsd4_open_downgrade *od = &u->open_downgrade; 404462306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 404562306a36Sopenharmony_ci 404662306a36Sopenharmony_ci return nfsd4_encode_stateid(xdr, &od->od_stateid); 404762306a36Sopenharmony_ci} 404862306a36Sopenharmony_ci 404962306a36Sopenharmony_ci/* 405062306a36Sopenharmony_ci * The operation of this function assumes that this is the only 405162306a36Sopenharmony_ci * READ operation in the COMPOUND. If there are multiple READs, 405262306a36Sopenharmony_ci * we use nfsd4_encode_readv(). 405362306a36Sopenharmony_ci */ 405462306a36Sopenharmony_cistatic __be32 nfsd4_encode_splice_read( 405562306a36Sopenharmony_ci struct nfsd4_compoundres *resp, 405662306a36Sopenharmony_ci struct nfsd4_read *read, 405762306a36Sopenharmony_ci struct file *file, unsigned long maxcount) 405862306a36Sopenharmony_ci{ 405962306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 406062306a36Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 406162306a36Sopenharmony_ci int status, space_left; 406262306a36Sopenharmony_ci __be32 nfserr; 406362306a36Sopenharmony_ci 406462306a36Sopenharmony_ci /* 406562306a36Sopenharmony_ci * Make sure there is room at the end of buf->head for 406662306a36Sopenharmony_ci * svcxdr_encode_opaque_pages() to create a tail buffer 406762306a36Sopenharmony_ci * to XDR-pad the payload. 406862306a36Sopenharmony_ci */ 406962306a36Sopenharmony_ci if (xdr->iov != xdr->buf->head || xdr->end - xdr->p < 1) 407062306a36Sopenharmony_ci return nfserr_resource; 407162306a36Sopenharmony_ci 407262306a36Sopenharmony_ci nfserr = nfsd_splice_read(read->rd_rqstp, read->rd_fhp, 407362306a36Sopenharmony_ci file, read->rd_offset, &maxcount, 407462306a36Sopenharmony_ci &read->rd_eof); 407562306a36Sopenharmony_ci read->rd_length = maxcount; 407662306a36Sopenharmony_ci if (nfserr) 407762306a36Sopenharmony_ci goto out_err; 407862306a36Sopenharmony_ci svcxdr_encode_opaque_pages(read->rd_rqstp, xdr, buf->pages, 407962306a36Sopenharmony_ci buf->page_base, maxcount); 408062306a36Sopenharmony_ci status = svc_encode_result_payload(read->rd_rqstp, 408162306a36Sopenharmony_ci buf->head[0].iov_len, maxcount); 408262306a36Sopenharmony_ci if (status) { 408362306a36Sopenharmony_ci nfserr = nfserrno(status); 408462306a36Sopenharmony_ci goto out_err; 408562306a36Sopenharmony_ci } 408662306a36Sopenharmony_ci 408762306a36Sopenharmony_ci /* 408862306a36Sopenharmony_ci * Prepare to encode subsequent operations. 408962306a36Sopenharmony_ci * 409062306a36Sopenharmony_ci * xdr_truncate_encode() is not safe to use after a successful 409162306a36Sopenharmony_ci * splice read has been done, so the following stream 409262306a36Sopenharmony_ci * manipulations are open-coded. 409362306a36Sopenharmony_ci */ 409462306a36Sopenharmony_ci space_left = min_t(int, (void *)xdr->end - (void *)xdr->p, 409562306a36Sopenharmony_ci buf->buflen - buf->len); 409662306a36Sopenharmony_ci buf->buflen = buf->len + space_left; 409762306a36Sopenharmony_ci xdr->end = (__be32 *)((void *)xdr->end + space_left); 409862306a36Sopenharmony_ci 409962306a36Sopenharmony_ci return nfs_ok; 410062306a36Sopenharmony_ci 410162306a36Sopenharmony_ciout_err: 410262306a36Sopenharmony_ci /* 410362306a36Sopenharmony_ci * nfsd_splice_actor may have already messed with the 410462306a36Sopenharmony_ci * page length; reset it so as not to confuse 410562306a36Sopenharmony_ci * xdr_truncate_encode in our caller. 410662306a36Sopenharmony_ci */ 410762306a36Sopenharmony_ci buf->page_len = 0; 410862306a36Sopenharmony_ci return nfserr; 410962306a36Sopenharmony_ci} 411062306a36Sopenharmony_ci 411162306a36Sopenharmony_cistatic __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp, 411262306a36Sopenharmony_ci struct nfsd4_read *read, 411362306a36Sopenharmony_ci struct file *file, unsigned long maxcount) 411462306a36Sopenharmony_ci{ 411562306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 411662306a36Sopenharmony_ci unsigned int base = xdr->buf->page_len & ~PAGE_MASK; 411762306a36Sopenharmony_ci unsigned int starting_len = xdr->buf->len; 411862306a36Sopenharmony_ci __be32 zero = xdr_zero; 411962306a36Sopenharmony_ci __be32 nfserr; 412062306a36Sopenharmony_ci 412162306a36Sopenharmony_ci if (xdr_reserve_space_vec(xdr, maxcount) < 0) 412262306a36Sopenharmony_ci return nfserr_resource; 412362306a36Sopenharmony_ci 412462306a36Sopenharmony_ci nfserr = nfsd_iter_read(resp->rqstp, read->rd_fhp, file, 412562306a36Sopenharmony_ci read->rd_offset, &maxcount, base, 412662306a36Sopenharmony_ci &read->rd_eof); 412762306a36Sopenharmony_ci read->rd_length = maxcount; 412862306a36Sopenharmony_ci if (nfserr) 412962306a36Sopenharmony_ci return nfserr; 413062306a36Sopenharmony_ci if (svc_encode_result_payload(resp->rqstp, starting_len, maxcount)) 413162306a36Sopenharmony_ci return nfserr_io; 413262306a36Sopenharmony_ci xdr_truncate_encode(xdr, starting_len + xdr_align_size(maxcount)); 413362306a36Sopenharmony_ci 413462306a36Sopenharmony_ci write_bytes_to_xdr_buf(xdr->buf, starting_len + maxcount, &zero, 413562306a36Sopenharmony_ci xdr_pad_size(maxcount)); 413662306a36Sopenharmony_ci return nfs_ok; 413762306a36Sopenharmony_ci} 413862306a36Sopenharmony_ci 413962306a36Sopenharmony_cistatic __be32 414062306a36Sopenharmony_cinfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr, 414162306a36Sopenharmony_ci union nfsd4_op_u *u) 414262306a36Sopenharmony_ci{ 414362306a36Sopenharmony_ci struct nfsd4_read *read = &u->read; 414462306a36Sopenharmony_ci bool splice_ok = test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags); 414562306a36Sopenharmony_ci unsigned long maxcount; 414662306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 414762306a36Sopenharmony_ci struct file *file; 414862306a36Sopenharmony_ci int starting_len = xdr->buf->len; 414962306a36Sopenharmony_ci __be32 *p; 415062306a36Sopenharmony_ci 415162306a36Sopenharmony_ci if (nfserr) 415262306a36Sopenharmony_ci return nfserr; 415362306a36Sopenharmony_ci file = read->rd_nf->nf_file; 415462306a36Sopenharmony_ci 415562306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */ 415662306a36Sopenharmony_ci if (!p) { 415762306a36Sopenharmony_ci WARN_ON_ONCE(splice_ok); 415862306a36Sopenharmony_ci return nfserr_resource; 415962306a36Sopenharmony_ci } 416062306a36Sopenharmony_ci if (resp->xdr->buf->page_len && splice_ok) { 416162306a36Sopenharmony_ci WARN_ON_ONCE(1); 416262306a36Sopenharmony_ci return nfserr_serverfault; 416362306a36Sopenharmony_ci } 416462306a36Sopenharmony_ci xdr_commit_encode(xdr); 416562306a36Sopenharmony_ci 416662306a36Sopenharmony_ci maxcount = min_t(unsigned long, read->rd_length, 416762306a36Sopenharmony_ci (xdr->buf->buflen - xdr->buf->len)); 416862306a36Sopenharmony_ci 416962306a36Sopenharmony_ci if (file->f_op->splice_read && splice_ok) 417062306a36Sopenharmony_ci nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount); 417162306a36Sopenharmony_ci else 417262306a36Sopenharmony_ci nfserr = nfsd4_encode_readv(resp, read, file, maxcount); 417362306a36Sopenharmony_ci if (nfserr) { 417462306a36Sopenharmony_ci xdr_truncate_encode(xdr, starting_len); 417562306a36Sopenharmony_ci return nfserr; 417662306a36Sopenharmony_ci } 417762306a36Sopenharmony_ci 417862306a36Sopenharmony_ci p = xdr_encode_bool(p, read->rd_eof); 417962306a36Sopenharmony_ci *p = cpu_to_be32(read->rd_length); 418062306a36Sopenharmony_ci return nfs_ok; 418162306a36Sopenharmony_ci} 418262306a36Sopenharmony_ci 418362306a36Sopenharmony_cistatic __be32 418462306a36Sopenharmony_cinfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, 418562306a36Sopenharmony_ci union nfsd4_op_u *u) 418662306a36Sopenharmony_ci{ 418762306a36Sopenharmony_ci struct nfsd4_readlink *readlink = &u->readlink; 418862306a36Sopenharmony_ci __be32 *p, *maxcount_p, zero = xdr_zero; 418962306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 419062306a36Sopenharmony_ci int length_offset = xdr->buf->len; 419162306a36Sopenharmony_ci int maxcount, status; 419262306a36Sopenharmony_ci 419362306a36Sopenharmony_ci maxcount_p = xdr_reserve_space(xdr, XDR_UNIT); 419462306a36Sopenharmony_ci if (!maxcount_p) 419562306a36Sopenharmony_ci return nfserr_resource; 419662306a36Sopenharmony_ci maxcount = PAGE_SIZE; 419762306a36Sopenharmony_ci 419862306a36Sopenharmony_ci p = xdr_reserve_space(xdr, maxcount); 419962306a36Sopenharmony_ci if (!p) 420062306a36Sopenharmony_ci return nfserr_resource; 420162306a36Sopenharmony_ci /* 420262306a36Sopenharmony_ci * XXX: By default, vfs_readlink() will truncate symlinks if they 420362306a36Sopenharmony_ci * would overflow the buffer. Is this kosher in NFSv4? If not, one 420462306a36Sopenharmony_ci * easy fix is: if vfs_readlink() precisely fills the buffer, assume 420562306a36Sopenharmony_ci * that truncation occurred, and return NFS4ERR_RESOURCE. 420662306a36Sopenharmony_ci */ 420762306a36Sopenharmony_ci nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, 420862306a36Sopenharmony_ci (char *)p, &maxcount); 420962306a36Sopenharmony_ci if (nfserr == nfserr_isdir) 421062306a36Sopenharmony_ci nfserr = nfserr_inval; 421162306a36Sopenharmony_ci if (nfserr) 421262306a36Sopenharmony_ci goto out_err; 421362306a36Sopenharmony_ci status = svc_encode_result_payload(readlink->rl_rqstp, length_offset, 421462306a36Sopenharmony_ci maxcount); 421562306a36Sopenharmony_ci if (status) { 421662306a36Sopenharmony_ci nfserr = nfserrno(status); 421762306a36Sopenharmony_ci goto out_err; 421862306a36Sopenharmony_ci } 421962306a36Sopenharmony_ci *maxcount_p = cpu_to_be32(maxcount); 422062306a36Sopenharmony_ci xdr_truncate_encode(xdr, length_offset + 4 + xdr_align_size(maxcount)); 422162306a36Sopenharmony_ci write_bytes_to_xdr_buf(xdr->buf, length_offset + 4 + maxcount, &zero, 422262306a36Sopenharmony_ci xdr_pad_size(maxcount)); 422362306a36Sopenharmony_ci return nfs_ok; 422462306a36Sopenharmony_ci 422562306a36Sopenharmony_ciout_err: 422662306a36Sopenharmony_ci xdr_truncate_encode(xdr, length_offset); 422762306a36Sopenharmony_ci return nfserr; 422862306a36Sopenharmony_ci} 422962306a36Sopenharmony_ci 423062306a36Sopenharmony_cistatic __be32 423162306a36Sopenharmony_cinfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, 423262306a36Sopenharmony_ci union nfsd4_op_u *u) 423362306a36Sopenharmony_ci{ 423462306a36Sopenharmony_ci struct nfsd4_readdir *readdir = &u->readdir; 423562306a36Sopenharmony_ci int maxcount; 423662306a36Sopenharmony_ci int bytes_left; 423762306a36Sopenharmony_ci loff_t offset; 423862306a36Sopenharmony_ci __be64 wire_offset; 423962306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 424062306a36Sopenharmony_ci int starting_len = xdr->buf->len; 424162306a36Sopenharmony_ci __be32 *p; 424262306a36Sopenharmony_ci 424362306a36Sopenharmony_ci nfserr = nfsd4_encode_verifier4(xdr, &readdir->rd_verf); 424462306a36Sopenharmony_ci if (nfserr != nfs_ok) 424562306a36Sopenharmony_ci return nfserr; 424662306a36Sopenharmony_ci 424762306a36Sopenharmony_ci /* 424862306a36Sopenharmony_ci * Number of bytes left for directory entries allowing for the 424962306a36Sopenharmony_ci * final 8 bytes of the readdir and a following failed op: 425062306a36Sopenharmony_ci */ 425162306a36Sopenharmony_ci bytes_left = xdr->buf->buflen - xdr->buf->len 425262306a36Sopenharmony_ci - COMPOUND_ERR_SLACK_SPACE - 8; 425362306a36Sopenharmony_ci if (bytes_left < 0) { 425462306a36Sopenharmony_ci nfserr = nfserr_resource; 425562306a36Sopenharmony_ci goto err_no_verf; 425662306a36Sopenharmony_ci } 425762306a36Sopenharmony_ci maxcount = svc_max_payload(resp->rqstp); 425862306a36Sopenharmony_ci maxcount = min_t(u32, readdir->rd_maxcount, maxcount); 425962306a36Sopenharmony_ci /* 426062306a36Sopenharmony_ci * Note the rfc defines rd_maxcount as the size of the 426162306a36Sopenharmony_ci * READDIR4resok structure, which includes the verifier above 426262306a36Sopenharmony_ci * and the 8 bytes encoded at the end of this function: 426362306a36Sopenharmony_ci */ 426462306a36Sopenharmony_ci if (maxcount < 16) { 426562306a36Sopenharmony_ci nfserr = nfserr_toosmall; 426662306a36Sopenharmony_ci goto err_no_verf; 426762306a36Sopenharmony_ci } 426862306a36Sopenharmony_ci maxcount = min_t(int, maxcount-16, bytes_left); 426962306a36Sopenharmony_ci 427062306a36Sopenharmony_ci /* RFC 3530 14.2.24 allows us to ignore dircount when it's 0: */ 427162306a36Sopenharmony_ci if (!readdir->rd_dircount) 427262306a36Sopenharmony_ci readdir->rd_dircount = svc_max_payload(resp->rqstp); 427362306a36Sopenharmony_ci 427462306a36Sopenharmony_ci readdir->xdr = xdr; 427562306a36Sopenharmony_ci readdir->rd_maxcount = maxcount; 427662306a36Sopenharmony_ci readdir->common.err = 0; 427762306a36Sopenharmony_ci readdir->cookie_offset = 0; 427862306a36Sopenharmony_ci 427962306a36Sopenharmony_ci offset = readdir->rd_cookie; 428062306a36Sopenharmony_ci nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp, 428162306a36Sopenharmony_ci &offset, 428262306a36Sopenharmony_ci &readdir->common, nfsd4_encode_dirent); 428362306a36Sopenharmony_ci if (nfserr == nfs_ok && 428462306a36Sopenharmony_ci readdir->common.err == nfserr_toosmall && 428562306a36Sopenharmony_ci xdr->buf->len == starting_len + 8) { 428662306a36Sopenharmony_ci /* nothing encoded; which limit did we hit?: */ 428762306a36Sopenharmony_ci if (maxcount - 16 < bytes_left) 428862306a36Sopenharmony_ci /* It was the fault of rd_maxcount: */ 428962306a36Sopenharmony_ci nfserr = nfserr_toosmall; 429062306a36Sopenharmony_ci else 429162306a36Sopenharmony_ci /* We ran out of buffer space: */ 429262306a36Sopenharmony_ci nfserr = nfserr_resource; 429362306a36Sopenharmony_ci } 429462306a36Sopenharmony_ci if (nfserr) 429562306a36Sopenharmony_ci goto err_no_verf; 429662306a36Sopenharmony_ci 429762306a36Sopenharmony_ci if (readdir->cookie_offset) { 429862306a36Sopenharmony_ci wire_offset = cpu_to_be64(offset); 429962306a36Sopenharmony_ci write_bytes_to_xdr_buf(xdr->buf, readdir->cookie_offset, 430062306a36Sopenharmony_ci &wire_offset, 8); 430162306a36Sopenharmony_ci } 430262306a36Sopenharmony_ci 430362306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 430462306a36Sopenharmony_ci if (!p) { 430562306a36Sopenharmony_ci WARN_ON_ONCE(1); 430662306a36Sopenharmony_ci goto err_no_verf; 430762306a36Sopenharmony_ci } 430862306a36Sopenharmony_ci *p++ = 0; /* no more entries */ 430962306a36Sopenharmony_ci *p++ = htonl(readdir->common.err == nfserr_eof); 431062306a36Sopenharmony_ci 431162306a36Sopenharmony_ci return 0; 431262306a36Sopenharmony_cierr_no_verf: 431362306a36Sopenharmony_ci xdr_truncate_encode(xdr, starting_len); 431462306a36Sopenharmony_ci return nfserr; 431562306a36Sopenharmony_ci} 431662306a36Sopenharmony_ci 431762306a36Sopenharmony_cistatic __be32 431862306a36Sopenharmony_cinfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, 431962306a36Sopenharmony_ci union nfsd4_op_u *u) 432062306a36Sopenharmony_ci{ 432162306a36Sopenharmony_ci struct nfsd4_remove *remove = &u->remove; 432262306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 432362306a36Sopenharmony_ci 432462306a36Sopenharmony_ci return nfsd4_encode_change_info4(xdr, &remove->rm_cinfo); 432562306a36Sopenharmony_ci} 432662306a36Sopenharmony_ci 432762306a36Sopenharmony_cistatic __be32 432862306a36Sopenharmony_cinfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, 432962306a36Sopenharmony_ci union nfsd4_op_u *u) 433062306a36Sopenharmony_ci{ 433162306a36Sopenharmony_ci struct nfsd4_rename *rename = &u->rename; 433262306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 433362306a36Sopenharmony_ci 433462306a36Sopenharmony_ci nfserr = nfsd4_encode_change_info4(xdr, &rename->rn_sinfo); 433562306a36Sopenharmony_ci if (nfserr) 433662306a36Sopenharmony_ci return nfserr; 433762306a36Sopenharmony_ci return nfsd4_encode_change_info4(xdr, &rename->rn_tinfo); 433862306a36Sopenharmony_ci} 433962306a36Sopenharmony_ci 434062306a36Sopenharmony_cistatic __be32 434162306a36Sopenharmony_cinfsd4_do_encode_secinfo(struct xdr_stream *xdr, struct svc_export *exp) 434262306a36Sopenharmony_ci{ 434362306a36Sopenharmony_ci u32 i, nflavs, supported; 434462306a36Sopenharmony_ci struct exp_flavor_info *flavs; 434562306a36Sopenharmony_ci struct exp_flavor_info def_flavs[2]; 434662306a36Sopenharmony_ci __be32 *p, *flavorsp; 434762306a36Sopenharmony_ci static bool report = true; 434862306a36Sopenharmony_ci 434962306a36Sopenharmony_ci if (exp->ex_nflavors) { 435062306a36Sopenharmony_ci flavs = exp->ex_flavors; 435162306a36Sopenharmony_ci nflavs = exp->ex_nflavors; 435262306a36Sopenharmony_ci } else { /* Handling of some defaults in absence of real secinfo: */ 435362306a36Sopenharmony_ci flavs = def_flavs; 435462306a36Sopenharmony_ci if (exp->ex_client->flavour->flavour == RPC_AUTH_UNIX) { 435562306a36Sopenharmony_ci nflavs = 2; 435662306a36Sopenharmony_ci flavs[0].pseudoflavor = RPC_AUTH_UNIX; 435762306a36Sopenharmony_ci flavs[1].pseudoflavor = RPC_AUTH_NULL; 435862306a36Sopenharmony_ci } else if (exp->ex_client->flavour->flavour == RPC_AUTH_GSS) { 435962306a36Sopenharmony_ci nflavs = 1; 436062306a36Sopenharmony_ci flavs[0].pseudoflavor 436162306a36Sopenharmony_ci = svcauth_gss_flavor(exp->ex_client); 436262306a36Sopenharmony_ci } else { 436362306a36Sopenharmony_ci nflavs = 1; 436462306a36Sopenharmony_ci flavs[0].pseudoflavor 436562306a36Sopenharmony_ci = exp->ex_client->flavour->flavour; 436662306a36Sopenharmony_ci } 436762306a36Sopenharmony_ci } 436862306a36Sopenharmony_ci 436962306a36Sopenharmony_ci supported = 0; 437062306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 437162306a36Sopenharmony_ci if (!p) 437262306a36Sopenharmony_ci return nfserr_resource; 437362306a36Sopenharmony_ci flavorsp = p++; /* to be backfilled later */ 437462306a36Sopenharmony_ci 437562306a36Sopenharmony_ci for (i = 0; i < nflavs; i++) { 437662306a36Sopenharmony_ci rpc_authflavor_t pf = flavs[i].pseudoflavor; 437762306a36Sopenharmony_ci struct rpcsec_gss_info info; 437862306a36Sopenharmony_ci 437962306a36Sopenharmony_ci if (rpcauth_get_gssinfo(pf, &info) == 0) { 438062306a36Sopenharmony_ci supported++; 438162306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4 + 4 + 438262306a36Sopenharmony_ci XDR_LEN(info.oid.len) + 4 + 4); 438362306a36Sopenharmony_ci if (!p) 438462306a36Sopenharmony_ci return nfserr_resource; 438562306a36Sopenharmony_ci *p++ = cpu_to_be32(RPC_AUTH_GSS); 438662306a36Sopenharmony_ci p = xdr_encode_opaque(p, info.oid.data, info.oid.len); 438762306a36Sopenharmony_ci *p++ = cpu_to_be32(info.qop); 438862306a36Sopenharmony_ci *p++ = cpu_to_be32(info.service); 438962306a36Sopenharmony_ci } else if (pf < RPC_AUTH_MAXFLAVOR) { 439062306a36Sopenharmony_ci supported++; 439162306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 439262306a36Sopenharmony_ci if (!p) 439362306a36Sopenharmony_ci return nfserr_resource; 439462306a36Sopenharmony_ci *p++ = cpu_to_be32(pf); 439562306a36Sopenharmony_ci } else { 439662306a36Sopenharmony_ci if (report) 439762306a36Sopenharmony_ci pr_warn("NFS: SECINFO: security flavor %u " 439862306a36Sopenharmony_ci "is not supported\n", pf); 439962306a36Sopenharmony_ci } 440062306a36Sopenharmony_ci } 440162306a36Sopenharmony_ci 440262306a36Sopenharmony_ci if (nflavs != supported) 440362306a36Sopenharmony_ci report = false; 440462306a36Sopenharmony_ci *flavorsp = htonl(supported); 440562306a36Sopenharmony_ci return 0; 440662306a36Sopenharmony_ci} 440762306a36Sopenharmony_ci 440862306a36Sopenharmony_cistatic __be32 440962306a36Sopenharmony_cinfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr, 441062306a36Sopenharmony_ci union nfsd4_op_u *u) 441162306a36Sopenharmony_ci{ 441262306a36Sopenharmony_ci struct nfsd4_secinfo *secinfo = &u->secinfo; 441362306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 441462306a36Sopenharmony_ci 441562306a36Sopenharmony_ci return nfsd4_do_encode_secinfo(xdr, secinfo->si_exp); 441662306a36Sopenharmony_ci} 441762306a36Sopenharmony_ci 441862306a36Sopenharmony_cistatic __be32 441962306a36Sopenharmony_cinfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr, 442062306a36Sopenharmony_ci union nfsd4_op_u *u) 442162306a36Sopenharmony_ci{ 442262306a36Sopenharmony_ci struct nfsd4_secinfo_no_name *secinfo = &u->secinfo_no_name; 442362306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 442462306a36Sopenharmony_ci 442562306a36Sopenharmony_ci return nfsd4_do_encode_secinfo(xdr, secinfo->sin_exp); 442662306a36Sopenharmony_ci} 442762306a36Sopenharmony_ci 442862306a36Sopenharmony_ci/* 442962306a36Sopenharmony_ci * The SETATTR encode routine is special -- it always encodes a bitmap, 443062306a36Sopenharmony_ci * regardless of the error status. 443162306a36Sopenharmony_ci */ 443262306a36Sopenharmony_cistatic __be32 443362306a36Sopenharmony_cinfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, 443462306a36Sopenharmony_ci union nfsd4_op_u *u) 443562306a36Sopenharmony_ci{ 443662306a36Sopenharmony_ci struct nfsd4_setattr *setattr = &u->setattr; 443762306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 443862306a36Sopenharmony_ci __be32 *p; 443962306a36Sopenharmony_ci 444062306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 16); 444162306a36Sopenharmony_ci if (!p) 444262306a36Sopenharmony_ci return nfserr_resource; 444362306a36Sopenharmony_ci if (nfserr) { 444462306a36Sopenharmony_ci *p++ = cpu_to_be32(3); 444562306a36Sopenharmony_ci *p++ = cpu_to_be32(0); 444662306a36Sopenharmony_ci *p++ = cpu_to_be32(0); 444762306a36Sopenharmony_ci *p++ = cpu_to_be32(0); 444862306a36Sopenharmony_ci } 444962306a36Sopenharmony_ci else { 445062306a36Sopenharmony_ci *p++ = cpu_to_be32(3); 445162306a36Sopenharmony_ci *p++ = cpu_to_be32(setattr->sa_bmval[0]); 445262306a36Sopenharmony_ci *p++ = cpu_to_be32(setattr->sa_bmval[1]); 445362306a36Sopenharmony_ci *p++ = cpu_to_be32(setattr->sa_bmval[2]); 445462306a36Sopenharmony_ci } 445562306a36Sopenharmony_ci return nfserr; 445662306a36Sopenharmony_ci} 445762306a36Sopenharmony_ci 445862306a36Sopenharmony_cistatic __be32 445962306a36Sopenharmony_cinfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, 446062306a36Sopenharmony_ci union nfsd4_op_u *u) 446162306a36Sopenharmony_ci{ 446262306a36Sopenharmony_ci struct nfsd4_setclientid *scd = &u->setclientid; 446362306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 446462306a36Sopenharmony_ci 446562306a36Sopenharmony_ci if (!nfserr) { 446662306a36Sopenharmony_ci nfserr = nfsd4_encode_clientid4(xdr, &scd->se_clientid); 446762306a36Sopenharmony_ci if (nfserr != nfs_ok) 446862306a36Sopenharmony_ci goto out; 446962306a36Sopenharmony_ci nfserr = nfsd4_encode_verifier4(xdr, &scd->se_confirm); 447062306a36Sopenharmony_ci } else if (nfserr == nfserr_clid_inuse) { 447162306a36Sopenharmony_ci /* empty network id */ 447262306a36Sopenharmony_ci if (xdr_stream_encode_u32(xdr, 0) < 0) { 447362306a36Sopenharmony_ci nfserr = nfserr_resource; 447462306a36Sopenharmony_ci goto out; 447562306a36Sopenharmony_ci } 447662306a36Sopenharmony_ci /* empty universal address */ 447762306a36Sopenharmony_ci if (xdr_stream_encode_u32(xdr, 0) < 0) { 447862306a36Sopenharmony_ci nfserr = nfserr_resource; 447962306a36Sopenharmony_ci goto out; 448062306a36Sopenharmony_ci } 448162306a36Sopenharmony_ci } 448262306a36Sopenharmony_ciout: 448362306a36Sopenharmony_ci return nfserr; 448462306a36Sopenharmony_ci} 448562306a36Sopenharmony_ci 448662306a36Sopenharmony_cistatic __be32 448762306a36Sopenharmony_cinfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, 448862306a36Sopenharmony_ci union nfsd4_op_u *u) 448962306a36Sopenharmony_ci{ 449062306a36Sopenharmony_ci struct nfsd4_write *write = &u->write; 449162306a36Sopenharmony_ci 449262306a36Sopenharmony_ci if (xdr_stream_encode_u32(resp->xdr, write->wr_bytes_written) < 0) 449362306a36Sopenharmony_ci return nfserr_resource; 449462306a36Sopenharmony_ci if (xdr_stream_encode_u32(resp->xdr, write->wr_how_written) < 0) 449562306a36Sopenharmony_ci return nfserr_resource; 449662306a36Sopenharmony_ci return nfsd4_encode_verifier4(resp->xdr, &write->wr_verifier); 449762306a36Sopenharmony_ci} 449862306a36Sopenharmony_ci 449962306a36Sopenharmony_cistatic __be32 450062306a36Sopenharmony_cinfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr, 450162306a36Sopenharmony_ci union nfsd4_op_u *u) 450262306a36Sopenharmony_ci{ 450362306a36Sopenharmony_ci struct nfsd4_exchange_id *exid = &u->exchange_id; 450462306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 450562306a36Sopenharmony_ci __be32 *p; 450662306a36Sopenharmony_ci char *major_id; 450762306a36Sopenharmony_ci char *server_scope; 450862306a36Sopenharmony_ci int major_id_sz; 450962306a36Sopenharmony_ci int server_scope_sz; 451062306a36Sopenharmony_ci uint64_t minor_id = 0; 451162306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(SVC_NET(resp->rqstp), nfsd_net_id); 451262306a36Sopenharmony_ci 451362306a36Sopenharmony_ci major_id = nn->nfsd_name; 451462306a36Sopenharmony_ci major_id_sz = strlen(nn->nfsd_name); 451562306a36Sopenharmony_ci server_scope = nn->nfsd_name; 451662306a36Sopenharmony_ci server_scope_sz = strlen(nn->nfsd_name); 451762306a36Sopenharmony_ci 451862306a36Sopenharmony_ci if (nfsd4_encode_clientid4(xdr, &exid->clientid) != nfs_ok) 451962306a36Sopenharmony_ci return nfserr_resource; 452062306a36Sopenharmony_ci if (xdr_stream_encode_u32(xdr, exid->seqid) < 0) 452162306a36Sopenharmony_ci return nfserr_resource; 452262306a36Sopenharmony_ci if (xdr_stream_encode_u32(xdr, exid->flags) < 0) 452362306a36Sopenharmony_ci return nfserr_resource; 452462306a36Sopenharmony_ci 452562306a36Sopenharmony_ci if (xdr_stream_encode_u32(xdr, exid->spa_how) < 0) 452662306a36Sopenharmony_ci return nfserr_resource; 452762306a36Sopenharmony_ci switch (exid->spa_how) { 452862306a36Sopenharmony_ci case SP4_NONE: 452962306a36Sopenharmony_ci break; 453062306a36Sopenharmony_ci case SP4_MACH_CRED: 453162306a36Sopenharmony_ci /* spo_must_enforce bitmap: */ 453262306a36Sopenharmony_ci nfserr = nfsd4_encode_bitmap(xdr, 453362306a36Sopenharmony_ci exid->spo_must_enforce[0], 453462306a36Sopenharmony_ci exid->spo_must_enforce[1], 453562306a36Sopenharmony_ci exid->spo_must_enforce[2]); 453662306a36Sopenharmony_ci if (nfserr) 453762306a36Sopenharmony_ci return nfserr; 453862306a36Sopenharmony_ci /* spo_must_allow bitmap: */ 453962306a36Sopenharmony_ci nfserr = nfsd4_encode_bitmap(xdr, 454062306a36Sopenharmony_ci exid->spo_must_allow[0], 454162306a36Sopenharmony_ci exid->spo_must_allow[1], 454262306a36Sopenharmony_ci exid->spo_must_allow[2]); 454362306a36Sopenharmony_ci if (nfserr) 454462306a36Sopenharmony_ci return nfserr; 454562306a36Sopenharmony_ci break; 454662306a36Sopenharmony_ci default: 454762306a36Sopenharmony_ci WARN_ON_ONCE(1); 454862306a36Sopenharmony_ci } 454962306a36Sopenharmony_ci 455062306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 455162306a36Sopenharmony_ci 8 /* so_minor_id */ + 455262306a36Sopenharmony_ci 4 /* so_major_id.len */ + 455362306a36Sopenharmony_ci (XDR_QUADLEN(major_id_sz) * 4) + 455462306a36Sopenharmony_ci 4 /* eir_server_scope.len */ + 455562306a36Sopenharmony_ci (XDR_QUADLEN(server_scope_sz) * 4) + 455662306a36Sopenharmony_ci 4 /* eir_server_impl_id.count (0) */); 455762306a36Sopenharmony_ci if (!p) 455862306a36Sopenharmony_ci return nfserr_resource; 455962306a36Sopenharmony_ci 456062306a36Sopenharmony_ci /* The server_owner struct */ 456162306a36Sopenharmony_ci p = xdr_encode_hyper(p, minor_id); /* Minor id */ 456262306a36Sopenharmony_ci /* major id */ 456362306a36Sopenharmony_ci p = xdr_encode_opaque(p, major_id, major_id_sz); 456462306a36Sopenharmony_ci 456562306a36Sopenharmony_ci /* Server scope */ 456662306a36Sopenharmony_ci p = xdr_encode_opaque(p, server_scope, server_scope_sz); 456762306a36Sopenharmony_ci 456862306a36Sopenharmony_ci /* Implementation id */ 456962306a36Sopenharmony_ci *p++ = cpu_to_be32(0); /* zero length nfs_impl_id4 array */ 457062306a36Sopenharmony_ci return 0; 457162306a36Sopenharmony_ci} 457262306a36Sopenharmony_ci 457362306a36Sopenharmony_cistatic __be32 457462306a36Sopenharmony_cinfsd4_encode_create_session(struct nfsd4_compoundres *resp, __be32 nfserr, 457562306a36Sopenharmony_ci union nfsd4_op_u *u) 457662306a36Sopenharmony_ci{ 457762306a36Sopenharmony_ci struct nfsd4_create_session *sess = &u->create_session; 457862306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 457962306a36Sopenharmony_ci __be32 *p; 458062306a36Sopenharmony_ci 458162306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 24); 458262306a36Sopenharmony_ci if (!p) 458362306a36Sopenharmony_ci return nfserr_resource; 458462306a36Sopenharmony_ci p = xdr_encode_opaque_fixed(p, sess->sessionid.data, 458562306a36Sopenharmony_ci NFS4_MAX_SESSIONID_LEN); 458662306a36Sopenharmony_ci *p++ = cpu_to_be32(sess->seqid); 458762306a36Sopenharmony_ci *p++ = cpu_to_be32(sess->flags); 458862306a36Sopenharmony_ci 458962306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 28); 459062306a36Sopenharmony_ci if (!p) 459162306a36Sopenharmony_ci return nfserr_resource; 459262306a36Sopenharmony_ci *p++ = cpu_to_be32(0); /* headerpadsz */ 459362306a36Sopenharmony_ci *p++ = cpu_to_be32(sess->fore_channel.maxreq_sz); 459462306a36Sopenharmony_ci *p++ = cpu_to_be32(sess->fore_channel.maxresp_sz); 459562306a36Sopenharmony_ci *p++ = cpu_to_be32(sess->fore_channel.maxresp_cached); 459662306a36Sopenharmony_ci *p++ = cpu_to_be32(sess->fore_channel.maxops); 459762306a36Sopenharmony_ci *p++ = cpu_to_be32(sess->fore_channel.maxreqs); 459862306a36Sopenharmony_ci *p++ = cpu_to_be32(sess->fore_channel.nr_rdma_attrs); 459962306a36Sopenharmony_ci 460062306a36Sopenharmony_ci if (sess->fore_channel.nr_rdma_attrs) { 460162306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 460262306a36Sopenharmony_ci if (!p) 460362306a36Sopenharmony_ci return nfserr_resource; 460462306a36Sopenharmony_ci *p++ = cpu_to_be32(sess->fore_channel.rdma_attrs); 460562306a36Sopenharmony_ci } 460662306a36Sopenharmony_ci 460762306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 28); 460862306a36Sopenharmony_ci if (!p) 460962306a36Sopenharmony_ci return nfserr_resource; 461062306a36Sopenharmony_ci *p++ = cpu_to_be32(0); /* headerpadsz */ 461162306a36Sopenharmony_ci *p++ = cpu_to_be32(sess->back_channel.maxreq_sz); 461262306a36Sopenharmony_ci *p++ = cpu_to_be32(sess->back_channel.maxresp_sz); 461362306a36Sopenharmony_ci *p++ = cpu_to_be32(sess->back_channel.maxresp_cached); 461462306a36Sopenharmony_ci *p++ = cpu_to_be32(sess->back_channel.maxops); 461562306a36Sopenharmony_ci *p++ = cpu_to_be32(sess->back_channel.maxreqs); 461662306a36Sopenharmony_ci *p++ = cpu_to_be32(sess->back_channel.nr_rdma_attrs); 461762306a36Sopenharmony_ci 461862306a36Sopenharmony_ci if (sess->back_channel.nr_rdma_attrs) { 461962306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 462062306a36Sopenharmony_ci if (!p) 462162306a36Sopenharmony_ci return nfserr_resource; 462262306a36Sopenharmony_ci *p++ = cpu_to_be32(sess->back_channel.rdma_attrs); 462362306a36Sopenharmony_ci } 462462306a36Sopenharmony_ci return 0; 462562306a36Sopenharmony_ci} 462662306a36Sopenharmony_ci 462762306a36Sopenharmony_cistatic __be32 462862306a36Sopenharmony_cinfsd4_encode_sequence(struct nfsd4_compoundres *resp, __be32 nfserr, 462962306a36Sopenharmony_ci union nfsd4_op_u *u) 463062306a36Sopenharmony_ci{ 463162306a36Sopenharmony_ci struct nfsd4_sequence *seq = &u->sequence; 463262306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 463362306a36Sopenharmony_ci __be32 *p; 463462306a36Sopenharmony_ci 463562306a36Sopenharmony_ci p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 20); 463662306a36Sopenharmony_ci if (!p) 463762306a36Sopenharmony_ci return nfserr_resource; 463862306a36Sopenharmony_ci p = xdr_encode_opaque_fixed(p, seq->sessionid.data, 463962306a36Sopenharmony_ci NFS4_MAX_SESSIONID_LEN); 464062306a36Sopenharmony_ci *p++ = cpu_to_be32(seq->seqid); 464162306a36Sopenharmony_ci *p++ = cpu_to_be32(seq->slotid); 464262306a36Sopenharmony_ci /* Note slotid's are numbered from zero: */ 464362306a36Sopenharmony_ci *p++ = cpu_to_be32(seq->maxslots - 1); /* sr_highest_slotid */ 464462306a36Sopenharmony_ci *p++ = cpu_to_be32(seq->maxslots - 1); /* sr_target_highest_slotid */ 464562306a36Sopenharmony_ci *p++ = cpu_to_be32(seq->status_flags); 464662306a36Sopenharmony_ci 464762306a36Sopenharmony_ci resp->cstate.data_offset = xdr->buf->len; /* DRC cache data pointer */ 464862306a36Sopenharmony_ci return 0; 464962306a36Sopenharmony_ci} 465062306a36Sopenharmony_ci 465162306a36Sopenharmony_cistatic __be32 465262306a36Sopenharmony_cinfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, __be32 nfserr, 465362306a36Sopenharmony_ci union nfsd4_op_u *u) 465462306a36Sopenharmony_ci{ 465562306a36Sopenharmony_ci struct nfsd4_test_stateid *test_stateid = &u->test_stateid; 465662306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 465762306a36Sopenharmony_ci struct nfsd4_test_stateid_id *stateid, *next; 465862306a36Sopenharmony_ci __be32 *p; 465962306a36Sopenharmony_ci 466062306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4 + (4 * test_stateid->ts_num_ids)); 466162306a36Sopenharmony_ci if (!p) 466262306a36Sopenharmony_ci return nfserr_resource; 466362306a36Sopenharmony_ci *p++ = htonl(test_stateid->ts_num_ids); 466462306a36Sopenharmony_ci 466562306a36Sopenharmony_ci list_for_each_entry_safe(stateid, next, &test_stateid->ts_stateid_list, ts_id_list) { 466662306a36Sopenharmony_ci *p++ = stateid->ts_id_status; 466762306a36Sopenharmony_ci } 466862306a36Sopenharmony_ci 466962306a36Sopenharmony_ci return 0; 467062306a36Sopenharmony_ci} 467162306a36Sopenharmony_ci 467262306a36Sopenharmony_ci#ifdef CONFIG_NFSD_PNFS 467362306a36Sopenharmony_cistatic __be32 467462306a36Sopenharmony_cinfsd4_encode_getdeviceinfo(struct nfsd4_compoundres *resp, __be32 nfserr, 467562306a36Sopenharmony_ci union nfsd4_op_u *u) 467662306a36Sopenharmony_ci{ 467762306a36Sopenharmony_ci struct nfsd4_getdeviceinfo *gdev = &u->getdeviceinfo; 467862306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 467962306a36Sopenharmony_ci const struct nfsd4_layout_ops *ops; 468062306a36Sopenharmony_ci u32 starting_len = xdr->buf->len, needed_len; 468162306a36Sopenharmony_ci __be32 *p; 468262306a36Sopenharmony_ci 468362306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 468462306a36Sopenharmony_ci if (!p) 468562306a36Sopenharmony_ci return nfserr_resource; 468662306a36Sopenharmony_ci 468762306a36Sopenharmony_ci *p++ = cpu_to_be32(gdev->gd_layout_type); 468862306a36Sopenharmony_ci 468962306a36Sopenharmony_ci ops = nfsd4_layout_ops[gdev->gd_layout_type]; 469062306a36Sopenharmony_ci nfserr = ops->encode_getdeviceinfo(xdr, gdev); 469162306a36Sopenharmony_ci if (nfserr) { 469262306a36Sopenharmony_ci /* 469362306a36Sopenharmony_ci * We don't bother to burden the layout drivers with 469462306a36Sopenharmony_ci * enforcing gd_maxcount, just tell the client to 469562306a36Sopenharmony_ci * come back with a bigger buffer if it's not enough. 469662306a36Sopenharmony_ci */ 469762306a36Sopenharmony_ci if (xdr->buf->len + 4 > gdev->gd_maxcount) 469862306a36Sopenharmony_ci goto toosmall; 469962306a36Sopenharmony_ci return nfserr; 470062306a36Sopenharmony_ci } 470162306a36Sopenharmony_ci 470262306a36Sopenharmony_ci if (gdev->gd_notify_types) { 470362306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4 + 4); 470462306a36Sopenharmony_ci if (!p) 470562306a36Sopenharmony_ci return nfserr_resource; 470662306a36Sopenharmony_ci *p++ = cpu_to_be32(1); /* bitmap length */ 470762306a36Sopenharmony_ci *p++ = cpu_to_be32(gdev->gd_notify_types); 470862306a36Sopenharmony_ci } else { 470962306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 471062306a36Sopenharmony_ci if (!p) 471162306a36Sopenharmony_ci return nfserr_resource; 471262306a36Sopenharmony_ci *p++ = 0; 471362306a36Sopenharmony_ci } 471462306a36Sopenharmony_ci 471562306a36Sopenharmony_ci return 0; 471662306a36Sopenharmony_citoosmall: 471762306a36Sopenharmony_ci dprintk("%s: maxcount too small\n", __func__); 471862306a36Sopenharmony_ci needed_len = xdr->buf->len + 4 /* notifications */; 471962306a36Sopenharmony_ci xdr_truncate_encode(xdr, starting_len); 472062306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 472162306a36Sopenharmony_ci if (!p) 472262306a36Sopenharmony_ci return nfserr_resource; 472362306a36Sopenharmony_ci *p++ = cpu_to_be32(needed_len); 472462306a36Sopenharmony_ci return nfserr_toosmall; 472562306a36Sopenharmony_ci} 472662306a36Sopenharmony_ci 472762306a36Sopenharmony_cistatic __be32 472862306a36Sopenharmony_cinfsd4_encode_layoutget(struct nfsd4_compoundres *resp, __be32 nfserr, 472962306a36Sopenharmony_ci union nfsd4_op_u *u) 473062306a36Sopenharmony_ci{ 473162306a36Sopenharmony_ci struct nfsd4_layoutget *lgp = &u->layoutget; 473262306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 473362306a36Sopenharmony_ci const struct nfsd4_layout_ops *ops; 473462306a36Sopenharmony_ci __be32 *p; 473562306a36Sopenharmony_ci 473662306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 36 + sizeof(stateid_opaque_t)); 473762306a36Sopenharmony_ci if (!p) 473862306a36Sopenharmony_ci return nfserr_resource; 473962306a36Sopenharmony_ci 474062306a36Sopenharmony_ci *p++ = cpu_to_be32(1); /* we always set return-on-close */ 474162306a36Sopenharmony_ci *p++ = cpu_to_be32(lgp->lg_sid.si_generation); 474262306a36Sopenharmony_ci p = xdr_encode_opaque_fixed(p, &lgp->lg_sid.si_opaque, 474362306a36Sopenharmony_ci sizeof(stateid_opaque_t)); 474462306a36Sopenharmony_ci 474562306a36Sopenharmony_ci *p++ = cpu_to_be32(1); /* we always return a single layout */ 474662306a36Sopenharmony_ci p = xdr_encode_hyper(p, lgp->lg_seg.offset); 474762306a36Sopenharmony_ci p = xdr_encode_hyper(p, lgp->lg_seg.length); 474862306a36Sopenharmony_ci *p++ = cpu_to_be32(lgp->lg_seg.iomode); 474962306a36Sopenharmony_ci *p++ = cpu_to_be32(lgp->lg_layout_type); 475062306a36Sopenharmony_ci 475162306a36Sopenharmony_ci ops = nfsd4_layout_ops[lgp->lg_layout_type]; 475262306a36Sopenharmony_ci return ops->encode_layoutget(xdr, lgp); 475362306a36Sopenharmony_ci} 475462306a36Sopenharmony_ci 475562306a36Sopenharmony_cistatic __be32 475662306a36Sopenharmony_cinfsd4_encode_layoutcommit(struct nfsd4_compoundres *resp, __be32 nfserr, 475762306a36Sopenharmony_ci union nfsd4_op_u *u) 475862306a36Sopenharmony_ci{ 475962306a36Sopenharmony_ci struct nfsd4_layoutcommit *lcp = &u->layoutcommit; 476062306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 476162306a36Sopenharmony_ci __be32 *p; 476262306a36Sopenharmony_ci 476362306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 476462306a36Sopenharmony_ci if (!p) 476562306a36Sopenharmony_ci return nfserr_resource; 476662306a36Sopenharmony_ci *p++ = cpu_to_be32(lcp->lc_size_chg); 476762306a36Sopenharmony_ci if (lcp->lc_size_chg) { 476862306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 476962306a36Sopenharmony_ci if (!p) 477062306a36Sopenharmony_ci return nfserr_resource; 477162306a36Sopenharmony_ci p = xdr_encode_hyper(p, lcp->lc_newsize); 477262306a36Sopenharmony_ci } 477362306a36Sopenharmony_ci 477462306a36Sopenharmony_ci return 0; 477562306a36Sopenharmony_ci} 477662306a36Sopenharmony_ci 477762306a36Sopenharmony_cistatic __be32 477862306a36Sopenharmony_cinfsd4_encode_layoutreturn(struct nfsd4_compoundres *resp, __be32 nfserr, 477962306a36Sopenharmony_ci union nfsd4_op_u *u) 478062306a36Sopenharmony_ci{ 478162306a36Sopenharmony_ci struct nfsd4_layoutreturn *lrp = &u->layoutreturn; 478262306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 478362306a36Sopenharmony_ci __be32 *p; 478462306a36Sopenharmony_ci 478562306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 478662306a36Sopenharmony_ci if (!p) 478762306a36Sopenharmony_ci return nfserr_resource; 478862306a36Sopenharmony_ci *p++ = cpu_to_be32(lrp->lrs_present); 478962306a36Sopenharmony_ci if (lrp->lrs_present) 479062306a36Sopenharmony_ci return nfsd4_encode_stateid(xdr, &lrp->lr_sid); 479162306a36Sopenharmony_ci return 0; 479262306a36Sopenharmony_ci} 479362306a36Sopenharmony_ci#endif /* CONFIG_NFSD_PNFS */ 479462306a36Sopenharmony_ci 479562306a36Sopenharmony_cistatic __be32 479662306a36Sopenharmony_cinfsd42_encode_write_res(struct nfsd4_compoundres *resp, 479762306a36Sopenharmony_ci struct nfsd42_write_res *write, bool sync) 479862306a36Sopenharmony_ci{ 479962306a36Sopenharmony_ci __be32 *p; 480062306a36Sopenharmony_ci p = xdr_reserve_space(resp->xdr, 4); 480162306a36Sopenharmony_ci if (!p) 480262306a36Sopenharmony_ci return nfserr_resource; 480362306a36Sopenharmony_ci 480462306a36Sopenharmony_ci if (sync) 480562306a36Sopenharmony_ci *p++ = cpu_to_be32(0); 480662306a36Sopenharmony_ci else { 480762306a36Sopenharmony_ci __be32 nfserr; 480862306a36Sopenharmony_ci *p++ = cpu_to_be32(1); 480962306a36Sopenharmony_ci nfserr = nfsd4_encode_stateid(resp->xdr, &write->cb_stateid); 481062306a36Sopenharmony_ci if (nfserr) 481162306a36Sopenharmony_ci return nfserr; 481262306a36Sopenharmony_ci } 481362306a36Sopenharmony_ci p = xdr_reserve_space(resp->xdr, 8 + 4 + NFS4_VERIFIER_SIZE); 481462306a36Sopenharmony_ci if (!p) 481562306a36Sopenharmony_ci return nfserr_resource; 481662306a36Sopenharmony_ci 481762306a36Sopenharmony_ci p = xdr_encode_hyper(p, write->wr_bytes_written); 481862306a36Sopenharmony_ci *p++ = cpu_to_be32(write->wr_stable_how); 481962306a36Sopenharmony_ci p = xdr_encode_opaque_fixed(p, write->wr_verifier.data, 482062306a36Sopenharmony_ci NFS4_VERIFIER_SIZE); 482162306a36Sopenharmony_ci return nfs_ok; 482262306a36Sopenharmony_ci} 482362306a36Sopenharmony_ci 482462306a36Sopenharmony_cistatic __be32 482562306a36Sopenharmony_cinfsd42_encode_nl4_server(struct nfsd4_compoundres *resp, struct nl4_server *ns) 482662306a36Sopenharmony_ci{ 482762306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 482862306a36Sopenharmony_ci struct nfs42_netaddr *addr; 482962306a36Sopenharmony_ci __be32 *p; 483062306a36Sopenharmony_ci 483162306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 483262306a36Sopenharmony_ci *p++ = cpu_to_be32(ns->nl4_type); 483362306a36Sopenharmony_ci 483462306a36Sopenharmony_ci switch (ns->nl4_type) { 483562306a36Sopenharmony_ci case NL4_NETADDR: 483662306a36Sopenharmony_ci addr = &ns->u.nl4_addr; 483762306a36Sopenharmony_ci 483862306a36Sopenharmony_ci /* netid_len, netid, uaddr_len, uaddr (port included 483962306a36Sopenharmony_ci * in RPCBIND_MAXUADDRLEN) 484062306a36Sopenharmony_ci */ 484162306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 484262306a36Sopenharmony_ci 4 /* netid len */ + 484362306a36Sopenharmony_ci (XDR_QUADLEN(addr->netid_len) * 4) + 484462306a36Sopenharmony_ci 4 /* uaddr len */ + 484562306a36Sopenharmony_ci (XDR_QUADLEN(addr->addr_len) * 4)); 484662306a36Sopenharmony_ci if (!p) 484762306a36Sopenharmony_ci return nfserr_resource; 484862306a36Sopenharmony_ci 484962306a36Sopenharmony_ci *p++ = cpu_to_be32(addr->netid_len); 485062306a36Sopenharmony_ci p = xdr_encode_opaque_fixed(p, addr->netid, 485162306a36Sopenharmony_ci addr->netid_len); 485262306a36Sopenharmony_ci *p++ = cpu_to_be32(addr->addr_len); 485362306a36Sopenharmony_ci p = xdr_encode_opaque_fixed(p, addr->addr, 485462306a36Sopenharmony_ci addr->addr_len); 485562306a36Sopenharmony_ci break; 485662306a36Sopenharmony_ci default: 485762306a36Sopenharmony_ci WARN_ON_ONCE(ns->nl4_type != NL4_NETADDR); 485862306a36Sopenharmony_ci return nfserr_inval; 485962306a36Sopenharmony_ci } 486062306a36Sopenharmony_ci 486162306a36Sopenharmony_ci return 0; 486262306a36Sopenharmony_ci} 486362306a36Sopenharmony_ci 486462306a36Sopenharmony_cistatic __be32 486562306a36Sopenharmony_cinfsd4_encode_copy(struct nfsd4_compoundres *resp, __be32 nfserr, 486662306a36Sopenharmony_ci union nfsd4_op_u *u) 486762306a36Sopenharmony_ci{ 486862306a36Sopenharmony_ci struct nfsd4_copy *copy = &u->copy; 486962306a36Sopenharmony_ci __be32 *p; 487062306a36Sopenharmony_ci 487162306a36Sopenharmony_ci nfserr = nfsd42_encode_write_res(resp, ©->cp_res, 487262306a36Sopenharmony_ci nfsd4_copy_is_sync(copy)); 487362306a36Sopenharmony_ci if (nfserr) 487462306a36Sopenharmony_ci return nfserr; 487562306a36Sopenharmony_ci 487662306a36Sopenharmony_ci p = xdr_reserve_space(resp->xdr, 4 + 4); 487762306a36Sopenharmony_ci *p++ = xdr_one; /* cr_consecutive */ 487862306a36Sopenharmony_ci *p = nfsd4_copy_is_sync(copy) ? xdr_one : xdr_zero; 487962306a36Sopenharmony_ci return 0; 488062306a36Sopenharmony_ci} 488162306a36Sopenharmony_ci 488262306a36Sopenharmony_cistatic __be32 488362306a36Sopenharmony_cinfsd4_encode_offload_status(struct nfsd4_compoundres *resp, __be32 nfserr, 488462306a36Sopenharmony_ci union nfsd4_op_u *u) 488562306a36Sopenharmony_ci{ 488662306a36Sopenharmony_ci struct nfsd4_offload_status *os = &u->offload_status; 488762306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 488862306a36Sopenharmony_ci __be32 *p; 488962306a36Sopenharmony_ci 489062306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8 + 4); 489162306a36Sopenharmony_ci if (!p) 489262306a36Sopenharmony_ci return nfserr_resource; 489362306a36Sopenharmony_ci p = xdr_encode_hyper(p, os->count); 489462306a36Sopenharmony_ci *p++ = cpu_to_be32(0); 489562306a36Sopenharmony_ci return nfserr; 489662306a36Sopenharmony_ci} 489762306a36Sopenharmony_ci 489862306a36Sopenharmony_cistatic __be32 489962306a36Sopenharmony_cinfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp, 490062306a36Sopenharmony_ci struct nfsd4_read *read) 490162306a36Sopenharmony_ci{ 490262306a36Sopenharmony_ci bool splice_ok = test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags); 490362306a36Sopenharmony_ci struct file *file = read->rd_nf->nf_file; 490462306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 490562306a36Sopenharmony_ci unsigned long maxcount; 490662306a36Sopenharmony_ci __be32 nfserr, *p; 490762306a36Sopenharmony_ci 490862306a36Sopenharmony_ci /* Content type, offset, byte count */ 490962306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4 + 8 + 4); 491062306a36Sopenharmony_ci if (!p) 491162306a36Sopenharmony_ci return nfserr_io; 491262306a36Sopenharmony_ci if (resp->xdr->buf->page_len && splice_ok) { 491362306a36Sopenharmony_ci WARN_ON_ONCE(splice_ok); 491462306a36Sopenharmony_ci return nfserr_serverfault; 491562306a36Sopenharmony_ci } 491662306a36Sopenharmony_ci 491762306a36Sopenharmony_ci maxcount = min_t(unsigned long, read->rd_length, 491862306a36Sopenharmony_ci (xdr->buf->buflen - xdr->buf->len)); 491962306a36Sopenharmony_ci 492062306a36Sopenharmony_ci if (file->f_op->splice_read && splice_ok) 492162306a36Sopenharmony_ci nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount); 492262306a36Sopenharmony_ci else 492362306a36Sopenharmony_ci nfserr = nfsd4_encode_readv(resp, read, file, maxcount); 492462306a36Sopenharmony_ci if (nfserr) 492562306a36Sopenharmony_ci return nfserr; 492662306a36Sopenharmony_ci 492762306a36Sopenharmony_ci *p++ = cpu_to_be32(NFS4_CONTENT_DATA); 492862306a36Sopenharmony_ci p = xdr_encode_hyper(p, read->rd_offset); 492962306a36Sopenharmony_ci *p = cpu_to_be32(read->rd_length); 493062306a36Sopenharmony_ci 493162306a36Sopenharmony_ci return nfs_ok; 493262306a36Sopenharmony_ci} 493362306a36Sopenharmony_ci 493462306a36Sopenharmony_cistatic __be32 493562306a36Sopenharmony_cinfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr, 493662306a36Sopenharmony_ci union nfsd4_op_u *u) 493762306a36Sopenharmony_ci{ 493862306a36Sopenharmony_ci struct nfsd4_read *read = &u->read; 493962306a36Sopenharmony_ci struct file *file = read->rd_nf->nf_file; 494062306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 494162306a36Sopenharmony_ci int starting_len = xdr->buf->len; 494262306a36Sopenharmony_ci u32 segments = 0; 494362306a36Sopenharmony_ci __be32 *p; 494462306a36Sopenharmony_ci 494562306a36Sopenharmony_ci if (nfserr) 494662306a36Sopenharmony_ci return nfserr; 494762306a36Sopenharmony_ci 494862306a36Sopenharmony_ci /* eof flag, segment count */ 494962306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4 + 4); 495062306a36Sopenharmony_ci if (!p) 495162306a36Sopenharmony_ci return nfserr_io; 495262306a36Sopenharmony_ci xdr_commit_encode(xdr); 495362306a36Sopenharmony_ci 495462306a36Sopenharmony_ci read->rd_eof = read->rd_offset >= i_size_read(file_inode(file)); 495562306a36Sopenharmony_ci if (read->rd_eof) 495662306a36Sopenharmony_ci goto out; 495762306a36Sopenharmony_ci 495862306a36Sopenharmony_ci nfserr = nfsd4_encode_read_plus_data(resp, read); 495962306a36Sopenharmony_ci if (nfserr) { 496062306a36Sopenharmony_ci xdr_truncate_encode(xdr, starting_len); 496162306a36Sopenharmony_ci return nfserr; 496262306a36Sopenharmony_ci } 496362306a36Sopenharmony_ci 496462306a36Sopenharmony_ci segments++; 496562306a36Sopenharmony_ci 496662306a36Sopenharmony_ciout: 496762306a36Sopenharmony_ci p = xdr_encode_bool(p, read->rd_eof); 496862306a36Sopenharmony_ci *p = cpu_to_be32(segments); 496962306a36Sopenharmony_ci return nfserr; 497062306a36Sopenharmony_ci} 497162306a36Sopenharmony_ci 497262306a36Sopenharmony_cistatic __be32 497362306a36Sopenharmony_cinfsd4_encode_copy_notify(struct nfsd4_compoundres *resp, __be32 nfserr, 497462306a36Sopenharmony_ci union nfsd4_op_u *u) 497562306a36Sopenharmony_ci{ 497662306a36Sopenharmony_ci struct nfsd4_copy_notify *cn = &u->copy_notify; 497762306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 497862306a36Sopenharmony_ci __be32 *p; 497962306a36Sopenharmony_ci 498062306a36Sopenharmony_ci if (nfserr) 498162306a36Sopenharmony_ci return nfserr; 498262306a36Sopenharmony_ci 498362306a36Sopenharmony_ci /* 8 sec, 4 nsec */ 498462306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 12); 498562306a36Sopenharmony_ci if (!p) 498662306a36Sopenharmony_ci return nfserr_resource; 498762306a36Sopenharmony_ci 498862306a36Sopenharmony_ci /* cnr_lease_time */ 498962306a36Sopenharmony_ci p = xdr_encode_hyper(p, cn->cpn_sec); 499062306a36Sopenharmony_ci *p++ = cpu_to_be32(cn->cpn_nsec); 499162306a36Sopenharmony_ci 499262306a36Sopenharmony_ci /* cnr_stateid */ 499362306a36Sopenharmony_ci nfserr = nfsd4_encode_stateid(xdr, &cn->cpn_cnr_stateid); 499462306a36Sopenharmony_ci if (nfserr) 499562306a36Sopenharmony_ci return nfserr; 499662306a36Sopenharmony_ci 499762306a36Sopenharmony_ci /* cnr_src.nl_nsvr */ 499862306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 499962306a36Sopenharmony_ci if (!p) 500062306a36Sopenharmony_ci return nfserr_resource; 500162306a36Sopenharmony_ci 500262306a36Sopenharmony_ci *p++ = cpu_to_be32(1); 500362306a36Sopenharmony_ci 500462306a36Sopenharmony_ci nfserr = nfsd42_encode_nl4_server(resp, cn->cpn_src); 500562306a36Sopenharmony_ci return nfserr; 500662306a36Sopenharmony_ci} 500762306a36Sopenharmony_ci 500862306a36Sopenharmony_cistatic __be32 500962306a36Sopenharmony_cinfsd4_encode_seek(struct nfsd4_compoundres *resp, __be32 nfserr, 501062306a36Sopenharmony_ci union nfsd4_op_u *u) 501162306a36Sopenharmony_ci{ 501262306a36Sopenharmony_ci struct nfsd4_seek *seek = &u->seek; 501362306a36Sopenharmony_ci __be32 *p; 501462306a36Sopenharmony_ci 501562306a36Sopenharmony_ci p = xdr_reserve_space(resp->xdr, 4 + 8); 501662306a36Sopenharmony_ci *p++ = cpu_to_be32(seek->seek_eof); 501762306a36Sopenharmony_ci p = xdr_encode_hyper(p, seek->seek_pos); 501862306a36Sopenharmony_ci 501962306a36Sopenharmony_ci return 0; 502062306a36Sopenharmony_ci} 502162306a36Sopenharmony_ci 502262306a36Sopenharmony_cistatic __be32 502362306a36Sopenharmony_cinfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, 502462306a36Sopenharmony_ci union nfsd4_op_u *p) 502562306a36Sopenharmony_ci{ 502662306a36Sopenharmony_ci return nfserr; 502762306a36Sopenharmony_ci} 502862306a36Sopenharmony_ci 502962306a36Sopenharmony_ci/* 503062306a36Sopenharmony_ci * Encode kmalloc-ed buffer in to XDR stream. 503162306a36Sopenharmony_ci */ 503262306a36Sopenharmony_cistatic __be32 503362306a36Sopenharmony_cinfsd4_vbuf_to_stream(struct xdr_stream *xdr, char *buf, u32 buflen) 503462306a36Sopenharmony_ci{ 503562306a36Sopenharmony_ci u32 cplen; 503662306a36Sopenharmony_ci __be32 *p; 503762306a36Sopenharmony_ci 503862306a36Sopenharmony_ci cplen = min_t(unsigned long, buflen, 503962306a36Sopenharmony_ci ((void *)xdr->end - (void *)xdr->p)); 504062306a36Sopenharmony_ci p = xdr_reserve_space(xdr, cplen); 504162306a36Sopenharmony_ci if (!p) 504262306a36Sopenharmony_ci return nfserr_resource; 504362306a36Sopenharmony_ci 504462306a36Sopenharmony_ci memcpy(p, buf, cplen); 504562306a36Sopenharmony_ci buf += cplen; 504662306a36Sopenharmony_ci buflen -= cplen; 504762306a36Sopenharmony_ci 504862306a36Sopenharmony_ci while (buflen) { 504962306a36Sopenharmony_ci cplen = min_t(u32, buflen, PAGE_SIZE); 505062306a36Sopenharmony_ci p = xdr_reserve_space(xdr, cplen); 505162306a36Sopenharmony_ci if (!p) 505262306a36Sopenharmony_ci return nfserr_resource; 505362306a36Sopenharmony_ci 505462306a36Sopenharmony_ci memcpy(p, buf, cplen); 505562306a36Sopenharmony_ci 505662306a36Sopenharmony_ci if (cplen < PAGE_SIZE) { 505762306a36Sopenharmony_ci /* 505862306a36Sopenharmony_ci * We're done, with a length that wasn't page 505962306a36Sopenharmony_ci * aligned, so possibly not word aligned. Pad 506062306a36Sopenharmony_ci * any trailing bytes with 0. 506162306a36Sopenharmony_ci */ 506262306a36Sopenharmony_ci xdr_encode_opaque_fixed(p, NULL, cplen); 506362306a36Sopenharmony_ci break; 506462306a36Sopenharmony_ci } 506562306a36Sopenharmony_ci 506662306a36Sopenharmony_ci buflen -= PAGE_SIZE; 506762306a36Sopenharmony_ci buf += PAGE_SIZE; 506862306a36Sopenharmony_ci } 506962306a36Sopenharmony_ci 507062306a36Sopenharmony_ci return 0; 507162306a36Sopenharmony_ci} 507262306a36Sopenharmony_ci 507362306a36Sopenharmony_cistatic __be32 507462306a36Sopenharmony_cinfsd4_encode_getxattr(struct nfsd4_compoundres *resp, __be32 nfserr, 507562306a36Sopenharmony_ci union nfsd4_op_u *u) 507662306a36Sopenharmony_ci{ 507762306a36Sopenharmony_ci struct nfsd4_getxattr *getxattr = &u->getxattr; 507862306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 507962306a36Sopenharmony_ci __be32 *p, err; 508062306a36Sopenharmony_ci 508162306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 508262306a36Sopenharmony_ci if (!p) 508362306a36Sopenharmony_ci return nfserr_resource; 508462306a36Sopenharmony_ci 508562306a36Sopenharmony_ci *p = cpu_to_be32(getxattr->getxa_len); 508662306a36Sopenharmony_ci 508762306a36Sopenharmony_ci if (getxattr->getxa_len == 0) 508862306a36Sopenharmony_ci return 0; 508962306a36Sopenharmony_ci 509062306a36Sopenharmony_ci err = nfsd4_vbuf_to_stream(xdr, getxattr->getxa_buf, 509162306a36Sopenharmony_ci getxattr->getxa_len); 509262306a36Sopenharmony_ci 509362306a36Sopenharmony_ci kvfree(getxattr->getxa_buf); 509462306a36Sopenharmony_ci 509562306a36Sopenharmony_ci return err; 509662306a36Sopenharmony_ci} 509762306a36Sopenharmony_ci 509862306a36Sopenharmony_cistatic __be32 509962306a36Sopenharmony_cinfsd4_encode_setxattr(struct nfsd4_compoundres *resp, __be32 nfserr, 510062306a36Sopenharmony_ci union nfsd4_op_u *u) 510162306a36Sopenharmony_ci{ 510262306a36Sopenharmony_ci struct nfsd4_setxattr *setxattr = &u->setxattr; 510362306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 510462306a36Sopenharmony_ci 510562306a36Sopenharmony_ci return nfsd4_encode_change_info4(xdr, &setxattr->setxa_cinfo); 510662306a36Sopenharmony_ci} 510762306a36Sopenharmony_ci 510862306a36Sopenharmony_ci/* 510962306a36Sopenharmony_ci * See if there are cookie values that can be rejected outright. 511062306a36Sopenharmony_ci */ 511162306a36Sopenharmony_cistatic __be32 511262306a36Sopenharmony_cinfsd4_listxattr_validate_cookie(struct nfsd4_listxattrs *listxattrs, 511362306a36Sopenharmony_ci u32 *offsetp) 511462306a36Sopenharmony_ci{ 511562306a36Sopenharmony_ci u64 cookie = listxattrs->lsxa_cookie; 511662306a36Sopenharmony_ci 511762306a36Sopenharmony_ci /* 511862306a36Sopenharmony_ci * If the cookie is larger than the maximum number we can fit 511962306a36Sopenharmony_ci * in either the buffer we just got back from vfs_listxattr, or, 512062306a36Sopenharmony_ci * XDR-encoded, in the return buffer, it's invalid. 512162306a36Sopenharmony_ci */ 512262306a36Sopenharmony_ci if (cookie > (listxattrs->lsxa_len) / (XATTR_USER_PREFIX_LEN + 2)) 512362306a36Sopenharmony_ci return nfserr_badcookie; 512462306a36Sopenharmony_ci 512562306a36Sopenharmony_ci if (cookie > (listxattrs->lsxa_maxcount / 512662306a36Sopenharmony_ci (XDR_QUADLEN(XATTR_USER_PREFIX_LEN + 2) + 4))) 512762306a36Sopenharmony_ci return nfserr_badcookie; 512862306a36Sopenharmony_ci 512962306a36Sopenharmony_ci *offsetp = (u32)cookie; 513062306a36Sopenharmony_ci return 0; 513162306a36Sopenharmony_ci} 513262306a36Sopenharmony_ci 513362306a36Sopenharmony_cistatic __be32 513462306a36Sopenharmony_cinfsd4_encode_listxattrs(struct nfsd4_compoundres *resp, __be32 nfserr, 513562306a36Sopenharmony_ci union nfsd4_op_u *u) 513662306a36Sopenharmony_ci{ 513762306a36Sopenharmony_ci struct nfsd4_listxattrs *listxattrs = &u->listxattrs; 513862306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 513962306a36Sopenharmony_ci u32 cookie_offset, count_offset, eof; 514062306a36Sopenharmony_ci u32 left, xdrleft, slen, count; 514162306a36Sopenharmony_ci u32 xdrlen, offset; 514262306a36Sopenharmony_ci u64 cookie; 514362306a36Sopenharmony_ci char *sp; 514462306a36Sopenharmony_ci __be32 status, tmp; 514562306a36Sopenharmony_ci __be32 *p; 514662306a36Sopenharmony_ci u32 nuser; 514762306a36Sopenharmony_ci 514862306a36Sopenharmony_ci eof = 1; 514962306a36Sopenharmony_ci 515062306a36Sopenharmony_ci status = nfsd4_listxattr_validate_cookie(listxattrs, &offset); 515162306a36Sopenharmony_ci if (status) 515262306a36Sopenharmony_ci goto out; 515362306a36Sopenharmony_ci 515462306a36Sopenharmony_ci /* 515562306a36Sopenharmony_ci * Reserve space for the cookie and the name array count. Record 515662306a36Sopenharmony_ci * the offsets to save them later. 515762306a36Sopenharmony_ci */ 515862306a36Sopenharmony_ci cookie_offset = xdr->buf->len; 515962306a36Sopenharmony_ci count_offset = cookie_offset + 8; 516062306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 12); 516162306a36Sopenharmony_ci if (!p) { 516262306a36Sopenharmony_ci status = nfserr_resource; 516362306a36Sopenharmony_ci goto out; 516462306a36Sopenharmony_ci } 516562306a36Sopenharmony_ci 516662306a36Sopenharmony_ci count = 0; 516762306a36Sopenharmony_ci left = listxattrs->lsxa_len; 516862306a36Sopenharmony_ci sp = listxattrs->lsxa_buf; 516962306a36Sopenharmony_ci nuser = 0; 517062306a36Sopenharmony_ci 517162306a36Sopenharmony_ci xdrleft = listxattrs->lsxa_maxcount; 517262306a36Sopenharmony_ci 517362306a36Sopenharmony_ci while (left > 0 && xdrleft > 0) { 517462306a36Sopenharmony_ci slen = strlen(sp); 517562306a36Sopenharmony_ci 517662306a36Sopenharmony_ci /* 517762306a36Sopenharmony_ci * Check if this is a "user." attribute, skip it if not. 517862306a36Sopenharmony_ci */ 517962306a36Sopenharmony_ci if (strncmp(sp, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) 518062306a36Sopenharmony_ci goto contloop; 518162306a36Sopenharmony_ci 518262306a36Sopenharmony_ci slen -= XATTR_USER_PREFIX_LEN; 518362306a36Sopenharmony_ci xdrlen = 4 + ((slen + 3) & ~3); 518462306a36Sopenharmony_ci if (xdrlen > xdrleft) { 518562306a36Sopenharmony_ci if (count == 0) { 518662306a36Sopenharmony_ci /* 518762306a36Sopenharmony_ci * Can't even fit the first attribute name. 518862306a36Sopenharmony_ci */ 518962306a36Sopenharmony_ci status = nfserr_toosmall; 519062306a36Sopenharmony_ci goto out; 519162306a36Sopenharmony_ci } 519262306a36Sopenharmony_ci eof = 0; 519362306a36Sopenharmony_ci goto wreof; 519462306a36Sopenharmony_ci } 519562306a36Sopenharmony_ci 519662306a36Sopenharmony_ci left -= XATTR_USER_PREFIX_LEN; 519762306a36Sopenharmony_ci sp += XATTR_USER_PREFIX_LEN; 519862306a36Sopenharmony_ci if (nuser++ < offset) 519962306a36Sopenharmony_ci goto contloop; 520062306a36Sopenharmony_ci 520162306a36Sopenharmony_ci 520262306a36Sopenharmony_ci p = xdr_reserve_space(xdr, xdrlen); 520362306a36Sopenharmony_ci if (!p) { 520462306a36Sopenharmony_ci status = nfserr_resource; 520562306a36Sopenharmony_ci goto out; 520662306a36Sopenharmony_ci } 520762306a36Sopenharmony_ci 520862306a36Sopenharmony_ci xdr_encode_opaque(p, sp, slen); 520962306a36Sopenharmony_ci 521062306a36Sopenharmony_ci xdrleft -= xdrlen; 521162306a36Sopenharmony_ci count++; 521262306a36Sopenharmony_cicontloop: 521362306a36Sopenharmony_ci sp += slen + 1; 521462306a36Sopenharmony_ci left -= slen + 1; 521562306a36Sopenharmony_ci } 521662306a36Sopenharmony_ci 521762306a36Sopenharmony_ci /* 521862306a36Sopenharmony_ci * If there were user attributes to copy, but we didn't copy 521962306a36Sopenharmony_ci * any, the offset was too large (e.g. the cookie was invalid). 522062306a36Sopenharmony_ci */ 522162306a36Sopenharmony_ci if (nuser > 0 && count == 0) { 522262306a36Sopenharmony_ci status = nfserr_badcookie; 522362306a36Sopenharmony_ci goto out; 522462306a36Sopenharmony_ci } 522562306a36Sopenharmony_ci 522662306a36Sopenharmony_ciwreof: 522762306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 4); 522862306a36Sopenharmony_ci if (!p) { 522962306a36Sopenharmony_ci status = nfserr_resource; 523062306a36Sopenharmony_ci goto out; 523162306a36Sopenharmony_ci } 523262306a36Sopenharmony_ci *p = cpu_to_be32(eof); 523362306a36Sopenharmony_ci 523462306a36Sopenharmony_ci cookie = offset + count; 523562306a36Sopenharmony_ci 523662306a36Sopenharmony_ci write_bytes_to_xdr_buf(xdr->buf, cookie_offset, &cookie, 8); 523762306a36Sopenharmony_ci tmp = cpu_to_be32(count); 523862306a36Sopenharmony_ci write_bytes_to_xdr_buf(xdr->buf, count_offset, &tmp, 4); 523962306a36Sopenharmony_ciout: 524062306a36Sopenharmony_ci if (listxattrs->lsxa_len) 524162306a36Sopenharmony_ci kvfree(listxattrs->lsxa_buf); 524262306a36Sopenharmony_ci return status; 524362306a36Sopenharmony_ci} 524462306a36Sopenharmony_ci 524562306a36Sopenharmony_cistatic __be32 524662306a36Sopenharmony_cinfsd4_encode_removexattr(struct nfsd4_compoundres *resp, __be32 nfserr, 524762306a36Sopenharmony_ci union nfsd4_op_u *u) 524862306a36Sopenharmony_ci{ 524962306a36Sopenharmony_ci struct nfsd4_removexattr *removexattr = &u->removexattr; 525062306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 525162306a36Sopenharmony_ci 525262306a36Sopenharmony_ci return nfsd4_encode_change_info4(xdr, &removexattr->rmxa_cinfo); 525362306a36Sopenharmony_ci} 525462306a36Sopenharmony_ci 525562306a36Sopenharmony_citypedef __be32(*nfsd4_enc)(struct nfsd4_compoundres *, __be32, union nfsd4_op_u *u); 525662306a36Sopenharmony_ci 525762306a36Sopenharmony_ci/* 525862306a36Sopenharmony_ci * Note: nfsd4_enc_ops vector is shared for v4.0 and v4.1 525962306a36Sopenharmony_ci * since we don't need to filter out obsolete ops as this is 526062306a36Sopenharmony_ci * done in the decoding phase. 526162306a36Sopenharmony_ci */ 526262306a36Sopenharmony_cistatic const nfsd4_enc nfsd4_enc_ops[] = { 526362306a36Sopenharmony_ci [OP_ACCESS] = nfsd4_encode_access, 526462306a36Sopenharmony_ci [OP_CLOSE] = nfsd4_encode_close, 526562306a36Sopenharmony_ci [OP_COMMIT] = nfsd4_encode_commit, 526662306a36Sopenharmony_ci [OP_CREATE] = nfsd4_encode_create, 526762306a36Sopenharmony_ci [OP_DELEGPURGE] = nfsd4_encode_noop, 526862306a36Sopenharmony_ci [OP_DELEGRETURN] = nfsd4_encode_noop, 526962306a36Sopenharmony_ci [OP_GETATTR] = nfsd4_encode_getattr, 527062306a36Sopenharmony_ci [OP_GETFH] = nfsd4_encode_getfh, 527162306a36Sopenharmony_ci [OP_LINK] = nfsd4_encode_link, 527262306a36Sopenharmony_ci [OP_LOCK] = nfsd4_encode_lock, 527362306a36Sopenharmony_ci [OP_LOCKT] = nfsd4_encode_lockt, 527462306a36Sopenharmony_ci [OP_LOCKU] = nfsd4_encode_locku, 527562306a36Sopenharmony_ci [OP_LOOKUP] = nfsd4_encode_noop, 527662306a36Sopenharmony_ci [OP_LOOKUPP] = nfsd4_encode_noop, 527762306a36Sopenharmony_ci [OP_NVERIFY] = nfsd4_encode_noop, 527862306a36Sopenharmony_ci [OP_OPEN] = nfsd4_encode_open, 527962306a36Sopenharmony_ci [OP_OPENATTR] = nfsd4_encode_noop, 528062306a36Sopenharmony_ci [OP_OPEN_CONFIRM] = nfsd4_encode_open_confirm, 528162306a36Sopenharmony_ci [OP_OPEN_DOWNGRADE] = nfsd4_encode_open_downgrade, 528262306a36Sopenharmony_ci [OP_PUTFH] = nfsd4_encode_noop, 528362306a36Sopenharmony_ci [OP_PUTPUBFH] = nfsd4_encode_noop, 528462306a36Sopenharmony_ci [OP_PUTROOTFH] = nfsd4_encode_noop, 528562306a36Sopenharmony_ci [OP_READ] = nfsd4_encode_read, 528662306a36Sopenharmony_ci [OP_READDIR] = nfsd4_encode_readdir, 528762306a36Sopenharmony_ci [OP_READLINK] = nfsd4_encode_readlink, 528862306a36Sopenharmony_ci [OP_REMOVE] = nfsd4_encode_remove, 528962306a36Sopenharmony_ci [OP_RENAME] = nfsd4_encode_rename, 529062306a36Sopenharmony_ci [OP_RENEW] = nfsd4_encode_noop, 529162306a36Sopenharmony_ci [OP_RESTOREFH] = nfsd4_encode_noop, 529262306a36Sopenharmony_ci [OP_SAVEFH] = nfsd4_encode_noop, 529362306a36Sopenharmony_ci [OP_SECINFO] = nfsd4_encode_secinfo, 529462306a36Sopenharmony_ci [OP_SETATTR] = nfsd4_encode_setattr, 529562306a36Sopenharmony_ci [OP_SETCLIENTID] = nfsd4_encode_setclientid, 529662306a36Sopenharmony_ci [OP_SETCLIENTID_CONFIRM] = nfsd4_encode_noop, 529762306a36Sopenharmony_ci [OP_VERIFY] = nfsd4_encode_noop, 529862306a36Sopenharmony_ci [OP_WRITE] = nfsd4_encode_write, 529962306a36Sopenharmony_ci [OP_RELEASE_LOCKOWNER] = nfsd4_encode_noop, 530062306a36Sopenharmony_ci 530162306a36Sopenharmony_ci /* NFSv4.1 operations */ 530262306a36Sopenharmony_ci [OP_BACKCHANNEL_CTL] = nfsd4_encode_noop, 530362306a36Sopenharmony_ci [OP_BIND_CONN_TO_SESSION] = nfsd4_encode_bind_conn_to_session, 530462306a36Sopenharmony_ci [OP_EXCHANGE_ID] = nfsd4_encode_exchange_id, 530562306a36Sopenharmony_ci [OP_CREATE_SESSION] = nfsd4_encode_create_session, 530662306a36Sopenharmony_ci [OP_DESTROY_SESSION] = nfsd4_encode_noop, 530762306a36Sopenharmony_ci [OP_FREE_STATEID] = nfsd4_encode_noop, 530862306a36Sopenharmony_ci [OP_GET_DIR_DELEGATION] = nfsd4_encode_noop, 530962306a36Sopenharmony_ci#ifdef CONFIG_NFSD_PNFS 531062306a36Sopenharmony_ci [OP_GETDEVICEINFO] = nfsd4_encode_getdeviceinfo, 531162306a36Sopenharmony_ci [OP_GETDEVICELIST] = nfsd4_encode_noop, 531262306a36Sopenharmony_ci [OP_LAYOUTCOMMIT] = nfsd4_encode_layoutcommit, 531362306a36Sopenharmony_ci [OP_LAYOUTGET] = nfsd4_encode_layoutget, 531462306a36Sopenharmony_ci [OP_LAYOUTRETURN] = nfsd4_encode_layoutreturn, 531562306a36Sopenharmony_ci#else 531662306a36Sopenharmony_ci [OP_GETDEVICEINFO] = nfsd4_encode_noop, 531762306a36Sopenharmony_ci [OP_GETDEVICELIST] = nfsd4_encode_noop, 531862306a36Sopenharmony_ci [OP_LAYOUTCOMMIT] = nfsd4_encode_noop, 531962306a36Sopenharmony_ci [OP_LAYOUTGET] = nfsd4_encode_noop, 532062306a36Sopenharmony_ci [OP_LAYOUTRETURN] = nfsd4_encode_noop, 532162306a36Sopenharmony_ci#endif 532262306a36Sopenharmony_ci [OP_SECINFO_NO_NAME] = nfsd4_encode_secinfo_no_name, 532362306a36Sopenharmony_ci [OP_SEQUENCE] = nfsd4_encode_sequence, 532462306a36Sopenharmony_ci [OP_SET_SSV] = nfsd4_encode_noop, 532562306a36Sopenharmony_ci [OP_TEST_STATEID] = nfsd4_encode_test_stateid, 532662306a36Sopenharmony_ci [OP_WANT_DELEGATION] = nfsd4_encode_noop, 532762306a36Sopenharmony_ci [OP_DESTROY_CLIENTID] = nfsd4_encode_noop, 532862306a36Sopenharmony_ci [OP_RECLAIM_COMPLETE] = nfsd4_encode_noop, 532962306a36Sopenharmony_ci 533062306a36Sopenharmony_ci /* NFSv4.2 operations */ 533162306a36Sopenharmony_ci [OP_ALLOCATE] = nfsd4_encode_noop, 533262306a36Sopenharmony_ci [OP_COPY] = nfsd4_encode_copy, 533362306a36Sopenharmony_ci [OP_COPY_NOTIFY] = nfsd4_encode_copy_notify, 533462306a36Sopenharmony_ci [OP_DEALLOCATE] = nfsd4_encode_noop, 533562306a36Sopenharmony_ci [OP_IO_ADVISE] = nfsd4_encode_noop, 533662306a36Sopenharmony_ci [OP_LAYOUTERROR] = nfsd4_encode_noop, 533762306a36Sopenharmony_ci [OP_LAYOUTSTATS] = nfsd4_encode_noop, 533862306a36Sopenharmony_ci [OP_OFFLOAD_CANCEL] = nfsd4_encode_noop, 533962306a36Sopenharmony_ci [OP_OFFLOAD_STATUS] = nfsd4_encode_offload_status, 534062306a36Sopenharmony_ci [OP_READ_PLUS] = nfsd4_encode_read_plus, 534162306a36Sopenharmony_ci [OP_SEEK] = nfsd4_encode_seek, 534262306a36Sopenharmony_ci [OP_WRITE_SAME] = nfsd4_encode_noop, 534362306a36Sopenharmony_ci [OP_CLONE] = nfsd4_encode_noop, 534462306a36Sopenharmony_ci 534562306a36Sopenharmony_ci /* RFC 8276 extended atributes operations */ 534662306a36Sopenharmony_ci [OP_GETXATTR] = nfsd4_encode_getxattr, 534762306a36Sopenharmony_ci [OP_SETXATTR] = nfsd4_encode_setxattr, 534862306a36Sopenharmony_ci [OP_LISTXATTRS] = nfsd4_encode_listxattrs, 534962306a36Sopenharmony_ci [OP_REMOVEXATTR] = nfsd4_encode_removexattr, 535062306a36Sopenharmony_ci}; 535162306a36Sopenharmony_ci 535262306a36Sopenharmony_ci/* 535362306a36Sopenharmony_ci * Calculate whether we still have space to encode repsize bytes. 535462306a36Sopenharmony_ci * There are two considerations: 535562306a36Sopenharmony_ci * - For NFS versions >=4.1, the size of the reply must stay within 535662306a36Sopenharmony_ci * session limits 535762306a36Sopenharmony_ci * - For all NFS versions, we must stay within limited preallocated 535862306a36Sopenharmony_ci * buffer space. 535962306a36Sopenharmony_ci * 536062306a36Sopenharmony_ci * This is called before the operation is processed, so can only provide 536162306a36Sopenharmony_ci * an upper estimate. For some nonidempotent operations (such as 536262306a36Sopenharmony_ci * getattr), it's not necessarily a problem if that estimate is wrong, 536362306a36Sopenharmony_ci * as we can fail it after processing without significant side effects. 536462306a36Sopenharmony_ci */ 536562306a36Sopenharmony_ci__be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 respsize) 536662306a36Sopenharmony_ci{ 536762306a36Sopenharmony_ci struct xdr_buf *buf = &resp->rqstp->rq_res; 536862306a36Sopenharmony_ci struct nfsd4_slot *slot = resp->cstate.slot; 536962306a36Sopenharmony_ci 537062306a36Sopenharmony_ci if (buf->len + respsize <= buf->buflen) 537162306a36Sopenharmony_ci return nfs_ok; 537262306a36Sopenharmony_ci if (!nfsd4_has_session(&resp->cstate)) 537362306a36Sopenharmony_ci return nfserr_resource; 537462306a36Sopenharmony_ci if (slot->sl_flags & NFSD4_SLOT_CACHETHIS) { 537562306a36Sopenharmony_ci WARN_ON_ONCE(1); 537662306a36Sopenharmony_ci return nfserr_rep_too_big_to_cache; 537762306a36Sopenharmony_ci } 537862306a36Sopenharmony_ci return nfserr_rep_too_big; 537962306a36Sopenharmony_ci} 538062306a36Sopenharmony_ci 538162306a36Sopenharmony_civoid 538262306a36Sopenharmony_cinfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) 538362306a36Sopenharmony_ci{ 538462306a36Sopenharmony_ci struct xdr_stream *xdr = resp->xdr; 538562306a36Sopenharmony_ci struct nfs4_stateowner *so = resp->cstate.replay_owner; 538662306a36Sopenharmony_ci struct svc_rqst *rqstp = resp->rqstp; 538762306a36Sopenharmony_ci const struct nfsd4_operation *opdesc = op->opdesc; 538862306a36Sopenharmony_ci int post_err_offset; 538962306a36Sopenharmony_ci nfsd4_enc encoder; 539062306a36Sopenharmony_ci __be32 *p; 539162306a36Sopenharmony_ci 539262306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8); 539362306a36Sopenharmony_ci if (!p) 539462306a36Sopenharmony_ci goto release; 539562306a36Sopenharmony_ci *p++ = cpu_to_be32(op->opnum); 539662306a36Sopenharmony_ci post_err_offset = xdr->buf->len; 539762306a36Sopenharmony_ci 539862306a36Sopenharmony_ci if (op->opnum == OP_ILLEGAL) 539962306a36Sopenharmony_ci goto status; 540062306a36Sopenharmony_ci if (op->status && opdesc && 540162306a36Sopenharmony_ci !(opdesc->op_flags & OP_NONTRIVIAL_ERROR_ENCODE)) 540262306a36Sopenharmony_ci goto status; 540362306a36Sopenharmony_ci BUG_ON(op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) || 540462306a36Sopenharmony_ci !nfsd4_enc_ops[op->opnum]); 540562306a36Sopenharmony_ci encoder = nfsd4_enc_ops[op->opnum]; 540662306a36Sopenharmony_ci op->status = encoder(resp, op->status, &op->u); 540762306a36Sopenharmony_ci if (op->status) 540862306a36Sopenharmony_ci trace_nfsd_compound_encode_err(rqstp, op->opnum, op->status); 540962306a36Sopenharmony_ci xdr_commit_encode(xdr); 541062306a36Sopenharmony_ci 541162306a36Sopenharmony_ci /* nfsd4_check_resp_size guarantees enough room for error status */ 541262306a36Sopenharmony_ci if (!op->status) { 541362306a36Sopenharmony_ci int space_needed = 0; 541462306a36Sopenharmony_ci if (!nfsd4_last_compound_op(rqstp)) 541562306a36Sopenharmony_ci space_needed = COMPOUND_ERR_SLACK_SPACE; 541662306a36Sopenharmony_ci op->status = nfsd4_check_resp_size(resp, space_needed); 541762306a36Sopenharmony_ci } 541862306a36Sopenharmony_ci if (op->status == nfserr_resource && nfsd4_has_session(&resp->cstate)) { 541962306a36Sopenharmony_ci struct nfsd4_slot *slot = resp->cstate.slot; 542062306a36Sopenharmony_ci 542162306a36Sopenharmony_ci if (slot->sl_flags & NFSD4_SLOT_CACHETHIS) 542262306a36Sopenharmony_ci op->status = nfserr_rep_too_big_to_cache; 542362306a36Sopenharmony_ci else 542462306a36Sopenharmony_ci op->status = nfserr_rep_too_big; 542562306a36Sopenharmony_ci } 542662306a36Sopenharmony_ci if (op->status == nfserr_resource || 542762306a36Sopenharmony_ci op->status == nfserr_rep_too_big || 542862306a36Sopenharmony_ci op->status == nfserr_rep_too_big_to_cache) { 542962306a36Sopenharmony_ci /* 543062306a36Sopenharmony_ci * The operation may have already been encoded or 543162306a36Sopenharmony_ci * partially encoded. No op returns anything additional 543262306a36Sopenharmony_ci * in the case of one of these three errors, so we can 543362306a36Sopenharmony_ci * just truncate back to after the status. But it's a 543462306a36Sopenharmony_ci * bug if we had to do this on a non-idempotent op: 543562306a36Sopenharmony_ci */ 543662306a36Sopenharmony_ci warn_on_nonidempotent_op(op); 543762306a36Sopenharmony_ci xdr_truncate_encode(xdr, post_err_offset); 543862306a36Sopenharmony_ci } 543962306a36Sopenharmony_ci if (so) { 544062306a36Sopenharmony_ci int len = xdr->buf->len - post_err_offset; 544162306a36Sopenharmony_ci 544262306a36Sopenharmony_ci so->so_replay.rp_status = op->status; 544362306a36Sopenharmony_ci so->so_replay.rp_buflen = len; 544462306a36Sopenharmony_ci read_bytes_from_xdr_buf(xdr->buf, post_err_offset, 544562306a36Sopenharmony_ci so->so_replay.rp_buf, len); 544662306a36Sopenharmony_ci } 544762306a36Sopenharmony_cistatus: 544862306a36Sopenharmony_ci *p = op->status; 544962306a36Sopenharmony_cirelease: 545062306a36Sopenharmony_ci if (opdesc && opdesc->op_release) 545162306a36Sopenharmony_ci opdesc->op_release(&op->u); 545262306a36Sopenharmony_ci 545362306a36Sopenharmony_ci /* 545462306a36Sopenharmony_ci * Account for pages consumed while encoding this operation. 545562306a36Sopenharmony_ci * The xdr_stream primitives don't manage rq_next_page. 545662306a36Sopenharmony_ci */ 545762306a36Sopenharmony_ci rqstp->rq_next_page = xdr->page_ptr + 1; 545862306a36Sopenharmony_ci} 545962306a36Sopenharmony_ci 546062306a36Sopenharmony_ci/* 546162306a36Sopenharmony_ci * Encode the reply stored in the stateowner reply cache 546262306a36Sopenharmony_ci * 546362306a36Sopenharmony_ci * XDR note: do not encode rp->rp_buflen: the buffer contains the 546462306a36Sopenharmony_ci * previously sent already encoded operation. 546562306a36Sopenharmony_ci */ 546662306a36Sopenharmony_civoid 546762306a36Sopenharmony_cinfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op) 546862306a36Sopenharmony_ci{ 546962306a36Sopenharmony_ci __be32 *p; 547062306a36Sopenharmony_ci struct nfs4_replay *rp = op->replay; 547162306a36Sopenharmony_ci 547262306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 8 + rp->rp_buflen); 547362306a36Sopenharmony_ci if (!p) { 547462306a36Sopenharmony_ci WARN_ON_ONCE(1); 547562306a36Sopenharmony_ci return; 547662306a36Sopenharmony_ci } 547762306a36Sopenharmony_ci *p++ = cpu_to_be32(op->opnum); 547862306a36Sopenharmony_ci *p++ = rp->rp_status; /* already xdr'ed */ 547962306a36Sopenharmony_ci 548062306a36Sopenharmony_ci p = xdr_encode_opaque_fixed(p, rp->rp_buf, rp->rp_buflen); 548162306a36Sopenharmony_ci} 548262306a36Sopenharmony_ci 548362306a36Sopenharmony_civoid nfsd4_release_compoundargs(struct svc_rqst *rqstp) 548462306a36Sopenharmony_ci{ 548562306a36Sopenharmony_ci struct nfsd4_compoundargs *args = rqstp->rq_argp; 548662306a36Sopenharmony_ci 548762306a36Sopenharmony_ci if (args->ops != args->iops) { 548862306a36Sopenharmony_ci vfree(args->ops); 548962306a36Sopenharmony_ci args->ops = args->iops; 549062306a36Sopenharmony_ci } 549162306a36Sopenharmony_ci while (args->to_free) { 549262306a36Sopenharmony_ci struct svcxdr_tmpbuf *tb = args->to_free; 549362306a36Sopenharmony_ci args->to_free = tb->next; 549462306a36Sopenharmony_ci kfree(tb); 549562306a36Sopenharmony_ci } 549662306a36Sopenharmony_ci} 549762306a36Sopenharmony_ci 549862306a36Sopenharmony_cibool 549962306a36Sopenharmony_cinfs4svc_decode_compoundargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) 550062306a36Sopenharmony_ci{ 550162306a36Sopenharmony_ci struct nfsd4_compoundargs *args = rqstp->rq_argp; 550262306a36Sopenharmony_ci 550362306a36Sopenharmony_ci /* svcxdr_tmp_alloc */ 550462306a36Sopenharmony_ci args->to_free = NULL; 550562306a36Sopenharmony_ci 550662306a36Sopenharmony_ci args->xdr = xdr; 550762306a36Sopenharmony_ci args->ops = args->iops; 550862306a36Sopenharmony_ci args->rqstp = rqstp; 550962306a36Sopenharmony_ci 551062306a36Sopenharmony_ci return nfsd4_decode_compound(args); 551162306a36Sopenharmony_ci} 551262306a36Sopenharmony_ci 551362306a36Sopenharmony_cibool 551462306a36Sopenharmony_cinfs4svc_encode_compoundres(struct svc_rqst *rqstp, struct xdr_stream *xdr) 551562306a36Sopenharmony_ci{ 551662306a36Sopenharmony_ci struct nfsd4_compoundres *resp = rqstp->rq_resp; 551762306a36Sopenharmony_ci __be32 *p; 551862306a36Sopenharmony_ci 551962306a36Sopenharmony_ci /* 552062306a36Sopenharmony_ci * Send buffer space for the following items is reserved 552162306a36Sopenharmony_ci * at the top of nfsd4_proc_compound(). 552262306a36Sopenharmony_ci */ 552362306a36Sopenharmony_ci p = resp->statusp; 552462306a36Sopenharmony_ci 552562306a36Sopenharmony_ci *p++ = resp->cstate.status; 552662306a36Sopenharmony_ci *p++ = htonl(resp->taglen); 552762306a36Sopenharmony_ci memcpy(p, resp->tag, resp->taglen); 552862306a36Sopenharmony_ci p += XDR_QUADLEN(resp->taglen); 552962306a36Sopenharmony_ci *p++ = htonl(resp->opcnt); 553062306a36Sopenharmony_ci 553162306a36Sopenharmony_ci nfsd4_sequence_done(resp); 553262306a36Sopenharmony_ci return true; 553362306a36Sopenharmony_ci} 5534