11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Server-side XDR for NFSv4 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (c) 2002 The Regents of the University of Michigan. 51da177e4SLinus Torvalds * All rights reserved. 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * Kendrick Smith <kmsmith@umich.edu> 81da177e4SLinus Torvalds * Andy Adamson <andros@umich.edu> 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * Redistribution and use in source and binary forms, with or without 111da177e4SLinus Torvalds * modification, are permitted provided that the following conditions 121da177e4SLinus Torvalds * are met: 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds * 1. Redistributions of source code must retain the above copyright 151da177e4SLinus Torvalds * notice, this list of conditions and the following disclaimer. 161da177e4SLinus Torvalds * 2. Redistributions in binary form must reproduce the above copyright 171da177e4SLinus Torvalds * notice, this list of conditions and the following disclaimer in the 181da177e4SLinus Torvalds * documentation and/or other materials provided with the distribution. 191da177e4SLinus Torvalds * 3. Neither the name of the University nor the names of its 201da177e4SLinus Torvalds * contributors may be used to endorse or promote products derived 211da177e4SLinus Torvalds * from this software without specific prior written permission. 221da177e4SLinus Torvalds * 231da177e4SLinus Torvalds * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 241da177e4SLinus Torvalds * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 251da177e4SLinus Torvalds * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 261da177e4SLinus Torvalds * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 271da177e4SLinus Torvalds * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 281da177e4SLinus Torvalds * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 291da177e4SLinus Torvalds * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 301da177e4SLinus Torvalds * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 311da177e4SLinus Torvalds * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 321da177e4SLinus Torvalds * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 331da177e4SLinus Torvalds * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 341da177e4SLinus Torvalds */ 351da177e4SLinus Torvalds 3696bcad50SChristoph Hellwig #include <linux/file.h> 375a0e3ad6STejun Heo #include <linux/slab.h> 381da177e4SLinus Torvalds #include <linux/namei.h> 39341eb184SBoaz Harrosh #include <linux/statfs.h> 400733d213SAndy Adamson #include <linux/utsname.h> 4117456804SBryan Schumaker #include <linux/pagemap.h> 424796f457SJ. Bruce Fields #include <linux/sunrpc/svcauth_gss.h> 4384e1b21dSOlga Kornievskaia #include <linux/sunrpc/addr.h> 4423e50fe3SFrank van der Linden #include <linux/xattr.h> 4523e50fe3SFrank van der Linden #include <uapi/linux/xattr.h> 469a74af21SBoaz Harrosh 472ca72e17SJ. Bruce Fields #include "idmap.h" 482ca72e17SJ. Bruce Fields #include "acl.h" 499a74af21SBoaz Harrosh #include "xdr4.h" 500a3adadeSJ. Bruce Fields #include "vfs.h" 5117456804SBryan Schumaker #include "state.h" 521091006cSJ. Bruce Fields #include "cache.h" 533d733711SStanislav Kinsbursky #include "netns.h" 549cf514ccSChristoph Hellwig #include "pnfs.h" 555c4583b2SJeff Layton #include "filecache.h" 562ca72e17SJ. Bruce Fields 57*08281341SChuck Lever #include "trace.h" 58*08281341SChuck Lever 5918032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL 6018032ca0SDavid Quigley #include <linux/security.h> 6118032ca0SDavid Quigley #endif 6218032ca0SDavid Quigley 6318032ca0SDavid Quigley 641da177e4SLinus Torvalds #define NFSDDBG_FACILITY NFSDDBG_XDR 651da177e4SLinus Torvalds 665cf23dbbSJ. Bruce Fields const u32 nfsd_suppattrs[3][3] = { 67916d2d84SJ. Bruce Fields {NFSD4_SUPPORTED_ATTRS_WORD0, 68916d2d84SJ. Bruce Fields NFSD4_SUPPORTED_ATTRS_WORD1, 69916d2d84SJ. Bruce Fields NFSD4_SUPPORTED_ATTRS_WORD2}, 70916d2d84SJ. Bruce Fields 71916d2d84SJ. Bruce Fields {NFSD4_1_SUPPORTED_ATTRS_WORD0, 72916d2d84SJ. Bruce Fields NFSD4_1_SUPPORTED_ATTRS_WORD1, 73916d2d84SJ. Bruce Fields NFSD4_1_SUPPORTED_ATTRS_WORD2}, 74916d2d84SJ. Bruce Fields 75916d2d84SJ. Bruce Fields {NFSD4_1_SUPPORTED_ATTRS_WORD0, 76916d2d84SJ. Bruce Fields NFSD4_1_SUPPORTED_ATTRS_WORD1, 77916d2d84SJ. Bruce Fields NFSD4_2_SUPPORTED_ATTRS_WORD2}, 78916d2d84SJ. Bruce Fields }; 79916d2d84SJ. Bruce Fields 8042ca0993SJ.Bruce Fields /* 8142ca0993SJ.Bruce Fields * As per referral draft, the fsid for a referral MUST be different from the fsid of the containing 8242ca0993SJ.Bruce Fields * directory in order to indicate to the client that a filesystem boundary is present 8342ca0993SJ.Bruce Fields * We use a fixed fsid for a referral 8442ca0993SJ.Bruce Fields */ 8542ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MAJOR 0x8000000ULL 8642ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MINOR 0x8000000ULL 8742ca0993SJ.Bruce Fields 88b37ad28bSAl Viro static __be32 89a36b1725SJ. Bruce Fields check_filename(char *str, int len) 901da177e4SLinus Torvalds { 911da177e4SLinus Torvalds int i; 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds if (len == 0) 941da177e4SLinus Torvalds return nfserr_inval; 951da177e4SLinus Torvalds if (isdotent(str, len)) 96a36b1725SJ. Bruce Fields return nfserr_badname; 971da177e4SLinus Torvalds for (i = 0; i < len; i++) 981da177e4SLinus Torvalds if (str[i] == '/') 99a36b1725SJ. Bruce Fields return nfserr_badname; 1001da177e4SLinus Torvalds return 0; 1011da177e4SLinus Torvalds } 1021da177e4SLinus Torvalds 1031da177e4SLinus Torvalds #define DECODE_HEAD \ 1042ebbc012SAl Viro __be32 *p; \ 105b37ad28bSAl Viro __be32 status 1061da177e4SLinus Torvalds #define DECODE_TAIL \ 1071da177e4SLinus Torvalds status = 0; \ 1081da177e4SLinus Torvalds out: \ 1091da177e4SLinus Torvalds return status; \ 1101da177e4SLinus Torvalds xdr_error: \ 111817cb9d4SChuck Lever dprintk("NFSD: xdr error (%s:%d)\n", \ 112817cb9d4SChuck Lever __FILE__, __LINE__); \ 1131da177e4SLinus Torvalds status = nfserr_bad_xdr; \ 1141da177e4SLinus Torvalds goto out 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds #define READMEM(x,nbytes) do { \ 1171da177e4SLinus Torvalds x = (char *)p; \ 1181da177e4SLinus Torvalds p += XDR_QUADLEN(nbytes); \ 1191da177e4SLinus Torvalds } while (0) 1201da177e4SLinus Torvalds #define SAVEMEM(x,nbytes) do { \ 1211da177e4SLinus Torvalds if (!(x = (p==argp->tmp || p == argp->tmpp) ? \ 1221da177e4SLinus Torvalds savemem(argp, p, nbytes) : \ 1231da177e4SLinus Torvalds (char *)p)) { \ 124817cb9d4SChuck Lever dprintk("NFSD: xdr error (%s:%d)\n", \ 125817cb9d4SChuck Lever __FILE__, __LINE__); \ 1261da177e4SLinus Torvalds goto xdr_error; \ 1271da177e4SLinus Torvalds } \ 1281da177e4SLinus Torvalds p += XDR_QUADLEN(nbytes); \ 1291da177e4SLinus Torvalds } while (0) 1301da177e4SLinus Torvalds #define COPYMEM(x,nbytes) do { \ 1311da177e4SLinus Torvalds memcpy((x), p, nbytes); \ 1321da177e4SLinus Torvalds p += XDR_QUADLEN(nbytes); \ 1331da177e4SLinus Torvalds } while (0) 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds /* READ_BUF, read_buf(): nbytes must be <= PAGE_SIZE */ 1361da177e4SLinus Torvalds #define READ_BUF(nbytes) do { \ 1371da177e4SLinus Torvalds if (nbytes <= (u32)((char *)argp->end - (char *)argp->p)) { \ 1381da177e4SLinus Torvalds p = argp->p; \ 1391da177e4SLinus Torvalds argp->p += XDR_QUADLEN(nbytes); \ 1401da177e4SLinus Torvalds } else if (!(p = read_buf(argp, nbytes))) { \ 141817cb9d4SChuck Lever dprintk("NFSD: xdr error (%s:%d)\n", \ 142817cb9d4SChuck Lever __FILE__, __LINE__); \ 1431da177e4SLinus Torvalds goto xdr_error; \ 1441da177e4SLinus Torvalds } \ 1451da177e4SLinus Torvalds } while (0) 1461da177e4SLinus Torvalds 147590b7431SJ. Bruce Fields static void next_decode_page(struct nfsd4_compoundargs *argp) 148590b7431SJ. Bruce Fields { 149590b7431SJ. Bruce Fields argp->p = page_address(argp->pagelist[0]); 150365da4adSJ. Bruce Fields argp->pagelist++; 151590b7431SJ. Bruce Fields if (argp->pagelen < PAGE_SIZE) { 152fc788f64SChuck Lever argp->end = argp->p + XDR_QUADLEN(argp->pagelen); 153590b7431SJ. Bruce Fields argp->pagelen = 0; 154590b7431SJ. Bruce Fields } else { 155590b7431SJ. Bruce Fields argp->end = argp->p + (PAGE_SIZE>>2); 156590b7431SJ. Bruce Fields argp->pagelen -= PAGE_SIZE; 157590b7431SJ. Bruce Fields } 158590b7431SJ. Bruce Fields } 159590b7431SJ. Bruce Fields 160ca2a05aaSJ. Bruce Fields static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes) 1611da177e4SLinus Torvalds { 1621da177e4SLinus Torvalds /* We want more bytes than seem to be available. 1631da177e4SLinus Torvalds * Maybe we need a new page, maybe we have just run out 1641da177e4SLinus Torvalds */ 165ca2a05aaSJ. Bruce Fields unsigned int avail = (char *)argp->end - (char *)argp->p; 1662ebbc012SAl Viro __be32 *p; 167eae03e2aSChuck Lever 168eae03e2aSChuck Lever if (argp->pagelen == 0) { 169eae03e2aSChuck Lever struct kvec *vec = &argp->rqstp->rq_arg.tail[0]; 170eae03e2aSChuck Lever 171eae03e2aSChuck Lever if (!argp->tail) { 172eae03e2aSChuck Lever argp->tail = true; 173eae03e2aSChuck Lever avail = vec->iov_len; 174eae03e2aSChuck Lever argp->p = vec->iov_base; 175eae03e2aSChuck Lever argp->end = vec->iov_base + avail; 176eae03e2aSChuck Lever } 177eae03e2aSChuck Lever 178eae03e2aSChuck Lever if (avail < nbytes) 179eae03e2aSChuck Lever return NULL; 180eae03e2aSChuck Lever 181eae03e2aSChuck Lever p = argp->p; 182eae03e2aSChuck Lever argp->p += XDR_QUADLEN(nbytes); 183eae03e2aSChuck Lever return p; 184eae03e2aSChuck Lever } 185eae03e2aSChuck Lever 1861da177e4SLinus Torvalds if (avail + argp->pagelen < nbytes) 1871da177e4SLinus Torvalds return NULL; 1881da177e4SLinus Torvalds if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */ 1891da177e4SLinus Torvalds return NULL; 1901da177e4SLinus Torvalds /* ok, we can do it with the current plus the next page */ 1911da177e4SLinus Torvalds if (nbytes <= sizeof(argp->tmp)) 1921da177e4SLinus Torvalds p = argp->tmp; 1931da177e4SLinus Torvalds else { 1941da177e4SLinus Torvalds kfree(argp->tmpp); 1951da177e4SLinus Torvalds p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL); 1961da177e4SLinus Torvalds if (!p) 1971da177e4SLinus Torvalds return NULL; 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds } 200ca2a05aaSJ. Bruce Fields /* 201ca2a05aaSJ. Bruce Fields * The following memcpy is safe because read_buf is always 202ca2a05aaSJ. Bruce Fields * called with nbytes > avail, and the two cases above both 203ca2a05aaSJ. Bruce Fields * guarantee p points to at least nbytes bytes. 204ca2a05aaSJ. Bruce Fields */ 2051da177e4SLinus Torvalds memcpy(p, argp->p, avail); 206590b7431SJ. Bruce Fields next_decode_page(argp); 2071da177e4SLinus Torvalds memcpy(((char*)p)+avail, argp->p, (nbytes - avail)); 2081da177e4SLinus Torvalds argp->p += XDR_QUADLEN(nbytes - avail); 2091da177e4SLinus Torvalds return p; 2101da177e4SLinus Torvalds } 2111da177e4SLinus Torvalds 2122b86e3aaSJ. Bruce Fields static unsigned int compoundargs_bytes_left(struct nfsd4_compoundargs *argp) 2132b86e3aaSJ. Bruce Fields { 2142b86e3aaSJ. Bruce Fields unsigned int this = (char *)argp->end - (char *)argp->p; 2152b86e3aaSJ. Bruce Fields 2162b86e3aaSJ. Bruce Fields return this + argp->pagelen; 2172b86e3aaSJ. Bruce Fields } 2182b86e3aaSJ. Bruce Fields 21960adfc50SAndy Adamson static int zero_clientid(clientid_t *clid) 22060adfc50SAndy Adamson { 22160adfc50SAndy Adamson return (clid->cl_boot == 0) && (clid->cl_id == 0); 22260adfc50SAndy Adamson } 22360adfc50SAndy Adamson 2242d8498dbSChristoph Hellwig /** 225d5e23383SJ. Bruce Fields * svcxdr_tmpalloc - allocate memory to be freed after compound processing 226ce043ac8SJ. Bruce Fields * @argp: NFSv4 compound argument structure 227ed992753STrond Myklebust * @len: length of buffer to allocate 2282d8498dbSChristoph Hellwig * 229ed992753STrond Myklebust * Allocates a buffer of size @len to be freed when processing the compound 230ed992753STrond Myklebust * operation described in @argp finishes. 2312d8498dbSChristoph Hellwig */ 232d5e23383SJ. Bruce Fields static void * 233d5e23383SJ. Bruce Fields svcxdr_tmpalloc(struct nfsd4_compoundargs *argp, u32 len) 2341da177e4SLinus Torvalds { 235d5e23383SJ. Bruce Fields struct svcxdr_tmpbuf *tb; 2361da177e4SLinus Torvalds 237d5e23383SJ. Bruce Fields tb = kmalloc(sizeof(*tb) + len, GFP_KERNEL); 2381da177e4SLinus Torvalds if (!tb) 239d5e23383SJ. Bruce Fields return NULL; 2401da177e4SLinus Torvalds tb->next = argp->to_free; 2411da177e4SLinus Torvalds argp->to_free = tb; 242d5e23383SJ. Bruce Fields return tb->buf; 2431da177e4SLinus Torvalds } 2441da177e4SLinus Torvalds 24529c353b3SJ. Bruce Fields /* 24629c353b3SJ. Bruce Fields * For xdr strings that need to be passed to other kernel api's 24729c353b3SJ. Bruce Fields * as null-terminated strings. 24829c353b3SJ. Bruce Fields * 24929c353b3SJ. Bruce Fields * Note null-terminating in place usually isn't safe since the 25029c353b3SJ. Bruce Fields * buffer might end on a page boundary. 25129c353b3SJ. Bruce Fields */ 25229c353b3SJ. Bruce Fields static char * 25329c353b3SJ. Bruce Fields svcxdr_dupstr(struct nfsd4_compoundargs *argp, void *buf, u32 len) 25429c353b3SJ. Bruce Fields { 255d5e23383SJ. Bruce Fields char *p = svcxdr_tmpalloc(argp, len + 1); 25629c353b3SJ. Bruce Fields 25729c353b3SJ. Bruce Fields if (!p) 25829c353b3SJ. Bruce Fields return NULL; 25929c353b3SJ. Bruce Fields memcpy(p, buf, len); 26029c353b3SJ. Bruce Fields p[len] = '\0'; 26129c353b3SJ. Bruce Fields return p; 2621da177e4SLinus Torvalds } 2631da177e4SLinus Torvalds 264874c7b8eSFrank van der Linden static __be32 265874c7b8eSFrank van der Linden svcxdr_construct_vector(struct nfsd4_compoundargs *argp, struct kvec *head, 266874c7b8eSFrank van der Linden struct page ***pagelist, u32 buflen) 267874c7b8eSFrank van der Linden { 268874c7b8eSFrank van der Linden int avail; 269874c7b8eSFrank van der Linden int len; 270874c7b8eSFrank van der Linden int pages; 271874c7b8eSFrank van der Linden 272874c7b8eSFrank van der Linden /* Sorry .. no magic macros for this.. * 273874c7b8eSFrank van der Linden * READ_BUF(write->wr_buflen); 274874c7b8eSFrank van der Linden * SAVEMEM(write->wr_buf, write->wr_buflen); 275874c7b8eSFrank van der Linden */ 276874c7b8eSFrank van der Linden avail = (char *)argp->end - (char *)argp->p; 277874c7b8eSFrank van der Linden if (avail + argp->pagelen < buflen) { 278874c7b8eSFrank van der Linden dprintk("NFSD: xdr error (%s:%d)\n", 279874c7b8eSFrank van der Linden __FILE__, __LINE__); 280874c7b8eSFrank van der Linden return nfserr_bad_xdr; 281874c7b8eSFrank van der Linden } 282874c7b8eSFrank van der Linden head->iov_base = argp->p; 283874c7b8eSFrank van der Linden head->iov_len = avail; 284874c7b8eSFrank van der Linden *pagelist = argp->pagelist; 285874c7b8eSFrank van der Linden 286874c7b8eSFrank van der Linden len = XDR_QUADLEN(buflen) << 2; 287874c7b8eSFrank van der Linden if (len >= avail) { 288874c7b8eSFrank van der Linden len -= avail; 289874c7b8eSFrank van der Linden 290874c7b8eSFrank van der Linden pages = len >> PAGE_SHIFT; 291874c7b8eSFrank van der Linden argp->pagelist += pages; 292874c7b8eSFrank van der Linden argp->pagelen -= pages * PAGE_SIZE; 293874c7b8eSFrank van der Linden len -= pages * PAGE_SIZE; 294874c7b8eSFrank van der Linden 295874c7b8eSFrank van der Linden next_decode_page(argp); 296874c7b8eSFrank van der Linden } 297874c7b8eSFrank van der Linden argp->p += XDR_QUADLEN(len); 298874c7b8eSFrank van der Linden 299874c7b8eSFrank van der Linden return 0; 300874c7b8eSFrank van der Linden } 301874c7b8eSFrank van der Linden 3022d8498dbSChristoph Hellwig /** 3032d8498dbSChristoph Hellwig * savemem - duplicate a chunk of memory for later processing 3042d8498dbSChristoph Hellwig * @argp: NFSv4 compound argument structure to be freed with 3052d8498dbSChristoph Hellwig * @p: pointer to be duplicated 3062d8498dbSChristoph Hellwig * @nbytes: length to be duplicated 3072d8498dbSChristoph Hellwig * 3082d8498dbSChristoph Hellwig * Returns a pointer to a copy of @nbytes bytes of memory at @p 3092d8498dbSChristoph Hellwig * that are preserved until processing of the NFSv4 compound 3102d8498dbSChristoph Hellwig * operation described by @argp finishes. 3112d8498dbSChristoph Hellwig */ 3122ebbc012SAl Viro static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes) 3131da177e4SLinus Torvalds { 314d5e23383SJ. Bruce Fields void *ret; 315d5e23383SJ. Bruce Fields 316d5e23383SJ. Bruce Fields ret = svcxdr_tmpalloc(argp, nbytes); 317d5e23383SJ. Bruce Fields if (!ret) 318a4db5fe5SJ. Bruce Fields return NULL; 319d5e23383SJ. Bruce Fields memcpy(ret, p, nbytes); 320d5e23383SJ. Bruce Fields return ret; 3211da177e4SLinus Torvalds } 3221da177e4SLinus Torvalds 3234c94e13eSChristoph Hellwig static __be32 324bdba5368SJ. Bruce Fields nfsd4_decode_time(struct nfsd4_compoundargs *argp, struct timespec64 *tv) 3254c94e13eSChristoph Hellwig { 3264c94e13eSChristoph Hellwig DECODE_HEAD; 3274c94e13eSChristoph Hellwig 3284c94e13eSChristoph Hellwig READ_BUF(12); 329bdba5368SJ. Bruce Fields p = xdr_decode_hyper(p, &tv->tv_sec); 3304c94e13eSChristoph Hellwig tv->tv_nsec = be32_to_cpup(p++); 3314c94e13eSChristoph Hellwig if (tv->tv_nsec >= (u32)1000000000) 3324c94e13eSChristoph Hellwig return nfserr_inval; 3334c94e13eSChristoph Hellwig 3344c94e13eSChristoph Hellwig DECODE_TAIL; 3354c94e13eSChristoph Hellwig } 3364c94e13eSChristoph Hellwig 337b37ad28bSAl Viro static __be32 3381da177e4SLinus Torvalds nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval) 3391da177e4SLinus Torvalds { 3401da177e4SLinus Torvalds u32 bmlen; 3411da177e4SLinus Torvalds DECODE_HEAD; 3421da177e4SLinus Torvalds 3431da177e4SLinus Torvalds bmval[0] = 0; 3441da177e4SLinus Torvalds bmval[1] = 0; 3457e705706SAndy Adamson bmval[2] = 0; 3461da177e4SLinus Torvalds 3471da177e4SLinus Torvalds READ_BUF(4); 34806553991SJ. Bruce Fields bmlen = be32_to_cpup(p++); 3491da177e4SLinus Torvalds if (bmlen > 1000) 3501da177e4SLinus Torvalds goto xdr_error; 3511da177e4SLinus Torvalds 3521da177e4SLinus Torvalds READ_BUF(bmlen << 2); 3531da177e4SLinus Torvalds if (bmlen > 0) 35406553991SJ. Bruce Fields bmval[0] = be32_to_cpup(p++); 3551da177e4SLinus Torvalds if (bmlen > 1) 35606553991SJ. Bruce Fields bmval[1] = be32_to_cpup(p++); 3577e705706SAndy Adamson if (bmlen > 2) 35806553991SJ. Bruce Fields bmval[2] = be32_to_cpup(p++); 3591da177e4SLinus Torvalds 3601da177e4SLinus Torvalds DECODE_TAIL; 3611da177e4SLinus Torvalds } 3621da177e4SLinus Torvalds 363b37ad28bSAl Viro static __be32 3643c8e0316SYu Zhiguo nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, 36518032ca0SDavid Quigley struct iattr *iattr, struct nfs4_acl **acl, 36647057abdSAndreas Gruenbacher struct xdr_netobj *label, int *umask) 3671da177e4SLinus Torvalds { 3681da177e4SLinus Torvalds int expected_len, len = 0; 3691da177e4SLinus Torvalds u32 dummy32; 3701da177e4SLinus Torvalds char *buf; 3711da177e4SLinus Torvalds 3721da177e4SLinus Torvalds DECODE_HEAD; 3731da177e4SLinus Torvalds iattr->ia_valid = 0; 3741da177e4SLinus Torvalds if ((status = nfsd4_decode_bitmap(argp, bmval))) 3751da177e4SLinus Torvalds return status; 3761da177e4SLinus Torvalds 377e864c189SJ. Bruce Fields if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0 378e864c189SJ. Bruce Fields || bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1 379e864c189SJ. Bruce Fields || bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2) { 380e864c189SJ. Bruce Fields if (nfsd_attrs_supported(argp->minorversion, bmval)) 381e864c189SJ. Bruce Fields return nfserr_inval; 382e864c189SJ. Bruce Fields return nfserr_attrnotsupp; 383e864c189SJ. Bruce Fields } 384e864c189SJ. Bruce Fields 3851da177e4SLinus Torvalds READ_BUF(4); 38606553991SJ. Bruce Fields expected_len = be32_to_cpup(p++); 3871da177e4SLinus Torvalds 3881da177e4SLinus Torvalds if (bmval[0] & FATTR4_WORD0_SIZE) { 3891da177e4SLinus Torvalds READ_BUF(8); 3901da177e4SLinus Torvalds len += 8; 391542d1ab3SJ. Bruce Fields p = xdr_decode_hyper(p, &iattr->ia_size); 3921da177e4SLinus Torvalds iattr->ia_valid |= ATTR_SIZE; 3931da177e4SLinus Torvalds } 3941da177e4SLinus Torvalds if (bmval[0] & FATTR4_WORD0_ACL) { 39564a817cfSJ. Bruce Fields u32 nace; 39628e05dd8SJ. Bruce Fields struct nfs4_ace *ace; 3971da177e4SLinus Torvalds 3981da177e4SLinus Torvalds READ_BUF(4); len += 4; 39906553991SJ. Bruce Fields nace = be32_to_cpup(p++); 4001da177e4SLinus Torvalds 4012b86e3aaSJ. Bruce Fields if (nace > compoundargs_bytes_left(argp)/20) 4022b86e3aaSJ. Bruce Fields /* 4032b86e3aaSJ. Bruce Fields * Even with 4-byte names there wouldn't be 4042b86e3aaSJ. Bruce Fields * space for that many aces; something fishy is 4052b86e3aaSJ. Bruce Fields * going on: 4062b86e3aaSJ. Bruce Fields */ 407798df338SJ. Bruce Fields return nfserr_fbig; 40828e05dd8SJ. Bruce Fields 409d5e23383SJ. Bruce Fields *acl = svcxdr_tmpalloc(argp, nfs4_acl_bytes(nace)); 410eba1c99cSKinglong Mee if (*acl == NULL) 411eba1c99cSKinglong Mee return nfserr_jukebox; 412eba1c99cSKinglong Mee 41328e05dd8SJ. Bruce Fields (*acl)->naces = nace; 41428e05dd8SJ. Bruce Fields for (ace = (*acl)->aces; ace < (*acl)->aces + nace; ace++) { 4151da177e4SLinus Torvalds READ_BUF(16); len += 16; 41606553991SJ. Bruce Fields ace->type = be32_to_cpup(p++); 41706553991SJ. Bruce Fields ace->flag = be32_to_cpup(p++); 41806553991SJ. Bruce Fields ace->access_mask = be32_to_cpup(p++); 41906553991SJ. Bruce Fields dummy32 = be32_to_cpup(p++); 4201da177e4SLinus Torvalds READ_BUF(dummy32); 4211da177e4SLinus Torvalds len += XDR_QUADLEN(dummy32) << 2; 4221da177e4SLinus Torvalds READMEM(buf, dummy32); 42328e05dd8SJ. Bruce Fields ace->whotype = nfs4_acl_get_whotype(buf, dummy32); 4243c726023SJ. Bruce Fields status = nfs_ok; 42528e05dd8SJ. Bruce Fields if (ace->whotype != NFS4_ACL_WHO_NAMED) 426ab8e4aeeSEric W. Biederman ; 42728e05dd8SJ. Bruce Fields else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP) 4283c726023SJ. Bruce Fields status = nfsd_map_name_to_gid(argp->rqstp, 429ab8e4aeeSEric W. Biederman buf, dummy32, &ace->who_gid); 4301da177e4SLinus Torvalds else 4313c726023SJ. Bruce Fields status = nfsd_map_name_to_uid(argp->rqstp, 432ab8e4aeeSEric W. Biederman buf, dummy32, &ace->who_uid); 4333c726023SJ. Bruce Fields if (status) 4343c726023SJ. Bruce Fields return status; 4351da177e4SLinus Torvalds } 4361da177e4SLinus Torvalds } else 4371da177e4SLinus Torvalds *acl = NULL; 4381da177e4SLinus Torvalds if (bmval[1] & FATTR4_WORD1_MODE) { 4391da177e4SLinus Torvalds READ_BUF(4); 4401da177e4SLinus Torvalds len += 4; 44106553991SJ. Bruce Fields iattr->ia_mode = be32_to_cpup(p++); 4421da177e4SLinus Torvalds iattr->ia_mode &= (S_IFMT | S_IALLUGO); 4431da177e4SLinus Torvalds iattr->ia_valid |= ATTR_MODE; 4441da177e4SLinus Torvalds } 4451da177e4SLinus Torvalds if (bmval[1] & FATTR4_WORD1_OWNER) { 4461da177e4SLinus Torvalds READ_BUF(4); 4471da177e4SLinus Torvalds len += 4; 44806553991SJ. Bruce Fields dummy32 = be32_to_cpup(p++); 4491da177e4SLinus Torvalds READ_BUF(dummy32); 4501da177e4SLinus Torvalds len += (XDR_QUADLEN(dummy32) << 2); 4511da177e4SLinus Torvalds READMEM(buf, dummy32); 45247c85291SNeilBrown if ((status = nfsd_map_name_to_uid(argp->rqstp, buf, dummy32, &iattr->ia_uid))) 45347c85291SNeilBrown return status; 4541da177e4SLinus Torvalds iattr->ia_valid |= ATTR_UID; 4551da177e4SLinus Torvalds } 4561da177e4SLinus Torvalds if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) { 4571da177e4SLinus Torvalds READ_BUF(4); 4581da177e4SLinus Torvalds len += 4; 45906553991SJ. Bruce Fields dummy32 = be32_to_cpup(p++); 4601da177e4SLinus Torvalds READ_BUF(dummy32); 4611da177e4SLinus Torvalds len += (XDR_QUADLEN(dummy32) << 2); 4621da177e4SLinus Torvalds READMEM(buf, dummy32); 46347c85291SNeilBrown if ((status = nfsd_map_name_to_gid(argp->rqstp, buf, dummy32, &iattr->ia_gid))) 46447c85291SNeilBrown return status; 4651da177e4SLinus Torvalds iattr->ia_valid |= ATTR_GID; 4661da177e4SLinus Torvalds } 4671da177e4SLinus Torvalds if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) { 4681da177e4SLinus Torvalds READ_BUF(4); 4691da177e4SLinus Torvalds len += 4; 47006553991SJ. Bruce Fields dummy32 = be32_to_cpup(p++); 4711da177e4SLinus Torvalds switch (dummy32) { 4721da177e4SLinus Torvalds case NFS4_SET_TO_CLIENT_TIME: 4731da177e4SLinus Torvalds len += 12; 474bdba5368SJ. Bruce Fields status = nfsd4_decode_time(argp, &iattr->ia_atime); 4754c94e13eSChristoph Hellwig if (status) 4764c94e13eSChristoph Hellwig return status; 4771da177e4SLinus Torvalds iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); 4781da177e4SLinus Torvalds break; 4791da177e4SLinus Torvalds case NFS4_SET_TO_SERVER_TIME: 4801da177e4SLinus Torvalds iattr->ia_valid |= ATTR_ATIME; 4811da177e4SLinus Torvalds break; 4821da177e4SLinus Torvalds default: 4831da177e4SLinus Torvalds goto xdr_error; 4841da177e4SLinus Torvalds } 4851da177e4SLinus Torvalds } 4861da177e4SLinus Torvalds if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) { 4871da177e4SLinus Torvalds READ_BUF(4); 4881da177e4SLinus Torvalds len += 4; 48906553991SJ. Bruce Fields dummy32 = be32_to_cpup(p++); 4901da177e4SLinus Torvalds switch (dummy32) { 4911da177e4SLinus Torvalds case NFS4_SET_TO_CLIENT_TIME: 4921da177e4SLinus Torvalds len += 12; 493bdba5368SJ. Bruce Fields status = nfsd4_decode_time(argp, &iattr->ia_mtime); 4944c94e13eSChristoph Hellwig if (status) 4954c94e13eSChristoph Hellwig return status; 4961da177e4SLinus Torvalds iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); 4971da177e4SLinus Torvalds break; 4981da177e4SLinus Torvalds case NFS4_SET_TO_SERVER_TIME: 4991da177e4SLinus Torvalds iattr->ia_valid |= ATTR_MTIME; 5001da177e4SLinus Torvalds break; 5011da177e4SLinus Torvalds default: 5021da177e4SLinus Torvalds goto xdr_error; 5031da177e4SLinus Torvalds } 5041da177e4SLinus Torvalds } 50518032ca0SDavid Quigley 50618032ca0SDavid Quigley label->len = 0; 5072285ae76SArnd Bergmann if (IS_ENABLED(CONFIG_NFSD_V4_SECURITY_LABEL) && 5082285ae76SArnd Bergmann bmval[2] & FATTR4_WORD2_SECURITY_LABEL) { 50918032ca0SDavid Quigley READ_BUF(4); 51018032ca0SDavid Quigley len += 4; 51106553991SJ. Bruce Fields dummy32 = be32_to_cpup(p++); /* lfs: we don't use it */ 51218032ca0SDavid Quigley READ_BUF(4); 51318032ca0SDavid Quigley len += 4; 51406553991SJ. Bruce Fields dummy32 = be32_to_cpup(p++); /* pi: we don't use it either */ 51518032ca0SDavid Quigley READ_BUF(4); 51618032ca0SDavid Quigley len += 4; 51706553991SJ. Bruce Fields dummy32 = be32_to_cpup(p++); 51818032ca0SDavid Quigley READ_BUF(dummy32); 5191ec8c0c4SKinglong Mee if (dummy32 > NFS4_MAXLABELLEN) 52018032ca0SDavid Quigley return nfserr_badlabel; 52118032ca0SDavid Quigley len += (XDR_QUADLEN(dummy32) << 2); 52218032ca0SDavid Quigley READMEM(buf, dummy32); 52329c353b3SJ. Bruce Fields label->len = dummy32; 52429c353b3SJ. Bruce Fields label->data = svcxdr_dupstr(argp, buf, dummy32); 52518032ca0SDavid Quigley if (!label->data) 52618032ca0SDavid Quigley return nfserr_jukebox; 52718032ca0SDavid Quigley } 52847057abdSAndreas Gruenbacher if (bmval[2] & FATTR4_WORD2_MODE_UMASK) { 52947057abdSAndreas Gruenbacher if (!umask) 53047057abdSAndreas Gruenbacher goto xdr_error; 53147057abdSAndreas Gruenbacher READ_BUF(8); 53247057abdSAndreas Gruenbacher len += 8; 53347057abdSAndreas Gruenbacher dummy32 = be32_to_cpup(p++); 53447057abdSAndreas Gruenbacher iattr->ia_mode = dummy32 & (S_IFMT | S_IALLUGO); 53547057abdSAndreas Gruenbacher dummy32 = be32_to_cpup(p++); 53647057abdSAndreas Gruenbacher *umask = dummy32 & S_IRWXUGO; 53747057abdSAndreas Gruenbacher iattr->ia_valid |= ATTR_MODE; 53847057abdSAndreas Gruenbacher } 539e864c189SJ. Bruce Fields if (len != expected_len) 5401da177e4SLinus Torvalds goto xdr_error; 5411da177e4SLinus Torvalds 5421da177e4SLinus Torvalds DECODE_TAIL; 5431da177e4SLinus Torvalds } 5441da177e4SLinus Torvalds 545b37ad28bSAl Viro static __be32 546e31a1b66SBenny Halevy nfsd4_decode_stateid(struct nfsd4_compoundargs *argp, stateid_t *sid) 547e31a1b66SBenny Halevy { 548e31a1b66SBenny Halevy DECODE_HEAD; 549e31a1b66SBenny Halevy 550e31a1b66SBenny Halevy READ_BUF(sizeof(stateid_t)); 55106553991SJ. Bruce Fields sid->si_generation = be32_to_cpup(p++); 552e31a1b66SBenny Halevy COPYMEM(&sid->si_opaque, sizeof(stateid_opaque_t)); 553e31a1b66SBenny Halevy 554e31a1b66SBenny Halevy DECODE_TAIL; 555e31a1b66SBenny Halevy } 556e31a1b66SBenny Halevy 557e31a1b66SBenny Halevy static __be32 5581da177e4SLinus Torvalds nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access) 5591da177e4SLinus Torvalds { 5601da177e4SLinus Torvalds DECODE_HEAD; 5611da177e4SLinus Torvalds 5621da177e4SLinus Torvalds READ_BUF(4); 56306553991SJ. Bruce Fields access->ac_req_access = be32_to_cpup(p++); 5641da177e4SLinus Torvalds 5651da177e4SLinus Torvalds DECODE_TAIL; 5661da177e4SLinus Torvalds } 5671da177e4SLinus Torvalds 568acb2887eSJ. Bruce Fields static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_cb_sec *cbs) 569acb2887eSJ. Bruce Fields { 570acb2887eSJ. Bruce Fields DECODE_HEAD; 571e45d1a18STrond Myklebust struct user_namespace *userns = nfsd_user_namespace(argp->rqstp); 57212fc3e92SJ. Bruce Fields u32 dummy, uid, gid; 573acb2887eSJ. Bruce Fields char *machine_name; 574acb2887eSJ. Bruce Fields int i; 575acb2887eSJ. Bruce Fields int nr_secflavs; 576acb2887eSJ. Bruce Fields 577acb2887eSJ. Bruce Fields /* callback_sec_params4 */ 578acb2887eSJ. Bruce Fields READ_BUF(4); 57906553991SJ. Bruce Fields nr_secflavs = be32_to_cpup(p++); 58057569a70SJ. Bruce Fields if (nr_secflavs) 58112fc3e92SJ. Bruce Fields cbs->flavor = (u32)(-1); 58257569a70SJ. Bruce Fields else 58357569a70SJ. Bruce Fields /* Is this legal? Be generous, take it to mean AUTH_NONE: */ 58457569a70SJ. Bruce Fields cbs->flavor = 0; 585acb2887eSJ. Bruce Fields for (i = 0; i < nr_secflavs; ++i) { 586acb2887eSJ. Bruce Fields READ_BUF(4); 58706553991SJ. Bruce Fields dummy = be32_to_cpup(p++); 588acb2887eSJ. Bruce Fields switch (dummy) { 589acb2887eSJ. Bruce Fields case RPC_AUTH_NULL: 590acb2887eSJ. Bruce Fields /* Nothing to read */ 59112fc3e92SJ. Bruce Fields if (cbs->flavor == (u32)(-1)) 59212fc3e92SJ. Bruce Fields cbs->flavor = RPC_AUTH_NULL; 593acb2887eSJ. Bruce Fields break; 594acb2887eSJ. Bruce Fields case RPC_AUTH_UNIX: 595acb2887eSJ. Bruce Fields READ_BUF(8); 596acb2887eSJ. Bruce Fields /* stamp */ 59706553991SJ. Bruce Fields dummy = be32_to_cpup(p++); 598acb2887eSJ. Bruce Fields 599acb2887eSJ. Bruce Fields /* machine name */ 60006553991SJ. Bruce Fields dummy = be32_to_cpup(p++); 601acb2887eSJ. Bruce Fields READ_BUF(dummy); 602acb2887eSJ. Bruce Fields SAVEMEM(machine_name, dummy); 603acb2887eSJ. Bruce Fields 604acb2887eSJ. Bruce Fields /* uid, gid */ 605acb2887eSJ. Bruce Fields READ_BUF(8); 60606553991SJ. Bruce Fields uid = be32_to_cpup(p++); 60706553991SJ. Bruce Fields gid = be32_to_cpup(p++); 608acb2887eSJ. Bruce Fields 609acb2887eSJ. Bruce Fields /* more gids */ 610acb2887eSJ. Bruce Fields READ_BUF(4); 61106553991SJ. Bruce Fields dummy = be32_to_cpup(p++); 612acb2887eSJ. Bruce Fields READ_BUF(dummy * 4); 61312fc3e92SJ. Bruce Fields if (cbs->flavor == (u32)(-1)) { 614e45d1a18STrond Myklebust kuid_t kuid = make_kuid(userns, uid); 615e45d1a18STrond Myklebust kgid_t kgid = make_kgid(userns, gid); 61603bc6d1cSEric W. Biederman if (uid_valid(kuid) && gid_valid(kgid)) { 61703bc6d1cSEric W. Biederman cbs->uid = kuid; 61803bc6d1cSEric W. Biederman cbs->gid = kgid; 61912fc3e92SJ. Bruce Fields cbs->flavor = RPC_AUTH_UNIX; 62003bc6d1cSEric W. Biederman } else { 62103bc6d1cSEric W. Biederman dprintk("RPC_AUTH_UNIX with invalid" 62203bc6d1cSEric W. Biederman "uid or gid ignoring!\n"); 62303bc6d1cSEric W. Biederman } 62412fc3e92SJ. Bruce Fields } 625acb2887eSJ. Bruce Fields break; 626acb2887eSJ. Bruce Fields case RPC_AUTH_GSS: 627acb2887eSJ. Bruce Fields dprintk("RPC_AUTH_GSS callback secflavor " 628acb2887eSJ. Bruce Fields "not supported!\n"); 629acb2887eSJ. Bruce Fields READ_BUF(8); 630acb2887eSJ. Bruce Fields /* gcbp_service */ 63106553991SJ. Bruce Fields dummy = be32_to_cpup(p++); 632acb2887eSJ. Bruce Fields /* gcbp_handle_from_server */ 63306553991SJ. Bruce Fields dummy = be32_to_cpup(p++); 634acb2887eSJ. Bruce Fields READ_BUF(dummy); 635acb2887eSJ. Bruce Fields p += XDR_QUADLEN(dummy); 636acb2887eSJ. Bruce Fields /* gcbp_handle_from_client */ 637acb2887eSJ. Bruce Fields READ_BUF(4); 63806553991SJ. Bruce Fields dummy = be32_to_cpup(p++); 639acb2887eSJ. Bruce Fields READ_BUF(dummy); 640acb2887eSJ. Bruce Fields break; 641acb2887eSJ. Bruce Fields default: 642acb2887eSJ. Bruce Fields dprintk("Illegal callback secflavor\n"); 643acb2887eSJ. Bruce Fields return nfserr_inval; 644acb2887eSJ. Bruce Fields } 645acb2887eSJ. Bruce Fields } 646acb2887eSJ. Bruce Fields DECODE_TAIL; 647acb2887eSJ. Bruce Fields } 648acb2887eSJ. Bruce Fields 649cb73a9f4SJ. Bruce Fields static __be32 nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs *argp, struct nfsd4_backchannel_ctl *bc) 650cb73a9f4SJ. Bruce Fields { 651cb73a9f4SJ. Bruce Fields DECODE_HEAD; 652cb73a9f4SJ. Bruce Fields 653cb73a9f4SJ. Bruce Fields READ_BUF(4); 65406553991SJ. Bruce Fields bc->bc_cb_program = be32_to_cpup(p++); 655cb73a9f4SJ. Bruce Fields nfsd4_decode_cb_sec(argp, &bc->bc_cb_sec); 656cb73a9f4SJ. Bruce Fields 657cb73a9f4SJ. Bruce Fields DECODE_TAIL; 658cb73a9f4SJ. Bruce Fields } 659cb73a9f4SJ. Bruce Fields 6601d1bc8f2SJ. Bruce Fields static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp, struct nfsd4_bind_conn_to_session *bcts) 6611d1bc8f2SJ. Bruce Fields { 6621d1bc8f2SJ. Bruce Fields DECODE_HEAD; 6631d1bc8f2SJ. Bruce Fields 6641d1bc8f2SJ. Bruce Fields READ_BUF(NFS4_MAX_SESSIONID_LEN + 8); 6651d1bc8f2SJ. Bruce Fields COPYMEM(bcts->sessionid.data, NFS4_MAX_SESSIONID_LEN); 66606553991SJ. Bruce Fields bcts->dir = be32_to_cpup(p++); 6676ce2357fSBryan Schumaker /* XXX: skipping ctsa_use_conn_in_rdma_mode. Perhaps Tom Tucker 6686ce2357fSBryan Schumaker * could help us figure out we should be using it. */ 6691d1bc8f2SJ. Bruce Fields DECODE_TAIL; 6701d1bc8f2SJ. Bruce Fields } 6711d1bc8f2SJ. Bruce Fields 672b37ad28bSAl Viro static __be32 6731da177e4SLinus Torvalds nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close) 6741da177e4SLinus Torvalds { 6751da177e4SLinus Torvalds DECODE_HEAD; 6761da177e4SLinus Torvalds 677e31a1b66SBenny Halevy READ_BUF(4); 67806553991SJ. Bruce Fields close->cl_seqid = be32_to_cpup(p++); 679e31a1b66SBenny Halevy return nfsd4_decode_stateid(argp, &close->cl_stateid); 6801da177e4SLinus Torvalds 6811da177e4SLinus Torvalds DECODE_TAIL; 6821da177e4SLinus Torvalds } 6831da177e4SLinus Torvalds 6841da177e4SLinus Torvalds 685b37ad28bSAl Viro static __be32 6861da177e4SLinus Torvalds nfsd4_decode_commit(struct nfsd4_compoundargs *argp, struct nfsd4_commit *commit) 6871da177e4SLinus Torvalds { 6881da177e4SLinus Torvalds DECODE_HEAD; 6891da177e4SLinus Torvalds 6901da177e4SLinus Torvalds READ_BUF(12); 691542d1ab3SJ. Bruce Fields p = xdr_decode_hyper(p, &commit->co_offset); 69206553991SJ. Bruce Fields commit->co_count = be32_to_cpup(p++); 6931da177e4SLinus Torvalds 6941da177e4SLinus Torvalds DECODE_TAIL; 6951da177e4SLinus Torvalds } 6961da177e4SLinus Torvalds 697b37ad28bSAl Viro static __be32 6981da177e4SLinus Torvalds nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create) 6991da177e4SLinus Torvalds { 7001da177e4SLinus Torvalds DECODE_HEAD; 7011da177e4SLinus Torvalds 7021da177e4SLinus Torvalds READ_BUF(4); 70306553991SJ. Bruce Fields create->cr_type = be32_to_cpup(p++); 7041da177e4SLinus Torvalds switch (create->cr_type) { 7051da177e4SLinus Torvalds case NF4LNK: 7061da177e4SLinus Torvalds READ_BUF(4); 7077fb84306SJ. Bruce Fields create->cr_datalen = be32_to_cpup(p++); 7087fb84306SJ. Bruce Fields READ_BUF(create->cr_datalen); 70929c353b3SJ. Bruce Fields create->cr_data = svcxdr_dupstr(argp, p, create->cr_datalen); 7107fb84306SJ. Bruce Fields if (!create->cr_data) 71176f47128SJ. Bruce Fields return nfserr_jukebox; 7121da177e4SLinus Torvalds break; 7131da177e4SLinus Torvalds case NF4BLK: 7141da177e4SLinus Torvalds case NF4CHR: 7151da177e4SLinus Torvalds READ_BUF(8); 71606553991SJ. Bruce Fields create->cr_specdata1 = be32_to_cpup(p++); 71706553991SJ. Bruce Fields create->cr_specdata2 = be32_to_cpup(p++); 7181da177e4SLinus Torvalds break; 7191da177e4SLinus Torvalds case NF4SOCK: 7201da177e4SLinus Torvalds case NF4FIFO: 7211da177e4SLinus Torvalds case NF4DIR: 7221da177e4SLinus Torvalds default: 7231da177e4SLinus Torvalds break; 7241da177e4SLinus Torvalds } 7251da177e4SLinus Torvalds 7261da177e4SLinus Torvalds READ_BUF(4); 72706553991SJ. Bruce Fields create->cr_namelen = be32_to_cpup(p++); 7281da177e4SLinus Torvalds READ_BUF(create->cr_namelen); 7291da177e4SLinus Torvalds SAVEMEM(create->cr_name, create->cr_namelen); 730a36b1725SJ. Bruce Fields if ((status = check_filename(create->cr_name, create->cr_namelen))) 7311da177e4SLinus Torvalds return status; 7321da177e4SLinus Torvalds 7333c8e0316SYu Zhiguo status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr, 73447057abdSAndreas Gruenbacher &create->cr_acl, &create->cr_label, 735880a3a53SJ. Bruce Fields &create->cr_umask); 736c0d6fc8aSBenny Halevy if (status) 7371da177e4SLinus Torvalds goto out; 7381da177e4SLinus Torvalds 7391da177e4SLinus Torvalds DECODE_TAIL; 7401da177e4SLinus Torvalds } 7411da177e4SLinus Torvalds 742b37ad28bSAl Viro static inline __be32 7431da177e4SLinus Torvalds nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr) 7441da177e4SLinus Torvalds { 745e31a1b66SBenny Halevy return nfsd4_decode_stateid(argp, &dr->dr_stateid); 7461da177e4SLinus Torvalds } 7471da177e4SLinus Torvalds 748b37ad28bSAl Viro static inline __be32 7491da177e4SLinus Torvalds nfsd4_decode_getattr(struct nfsd4_compoundargs *argp, struct nfsd4_getattr *getattr) 7501da177e4SLinus Torvalds { 7511da177e4SLinus Torvalds return nfsd4_decode_bitmap(argp, getattr->ga_bmval); 7521da177e4SLinus Torvalds } 7531da177e4SLinus Torvalds 754b37ad28bSAl Viro static __be32 7551da177e4SLinus Torvalds nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link) 7561da177e4SLinus Torvalds { 7571da177e4SLinus Torvalds DECODE_HEAD; 7581da177e4SLinus Torvalds 7591da177e4SLinus Torvalds READ_BUF(4); 76006553991SJ. Bruce Fields link->li_namelen = be32_to_cpup(p++); 7611da177e4SLinus Torvalds READ_BUF(link->li_namelen); 7621da177e4SLinus Torvalds SAVEMEM(link->li_name, link->li_namelen); 763a36b1725SJ. Bruce Fields if ((status = check_filename(link->li_name, link->li_namelen))) 7641da177e4SLinus Torvalds return status; 7651da177e4SLinus Torvalds 7661da177e4SLinus Torvalds DECODE_TAIL; 7671da177e4SLinus Torvalds } 7681da177e4SLinus Torvalds 769b37ad28bSAl Viro static __be32 7701da177e4SLinus Torvalds nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock) 7711da177e4SLinus Torvalds { 7721da177e4SLinus Torvalds DECODE_HEAD; 7731da177e4SLinus Torvalds 7741da177e4SLinus Torvalds /* 7751da177e4SLinus Torvalds * type, reclaim(boolean), offset, length, new_lock_owner(boolean) 7761da177e4SLinus Torvalds */ 7771da177e4SLinus Torvalds READ_BUF(28); 77806553991SJ. Bruce Fields lock->lk_type = be32_to_cpup(p++); 7791da177e4SLinus Torvalds if ((lock->lk_type < NFS4_READ_LT) || (lock->lk_type > NFS4_WRITEW_LT)) 7801da177e4SLinus Torvalds goto xdr_error; 78106553991SJ. Bruce Fields lock->lk_reclaim = be32_to_cpup(p++); 782542d1ab3SJ. Bruce Fields p = xdr_decode_hyper(p, &lock->lk_offset); 783542d1ab3SJ. Bruce Fields p = xdr_decode_hyper(p, &lock->lk_length); 78406553991SJ. Bruce Fields lock->lk_is_new = be32_to_cpup(p++); 7851da177e4SLinus Torvalds 7861da177e4SLinus Torvalds if (lock->lk_is_new) { 787e31a1b66SBenny Halevy READ_BUF(4); 78806553991SJ. Bruce Fields lock->lk_new_open_seqid = be32_to_cpup(p++); 789e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &lock->lk_new_open_stateid); 790e31a1b66SBenny Halevy if (status) 791e31a1b66SBenny Halevy return status; 792e31a1b66SBenny Halevy READ_BUF(8 + sizeof(clientid_t)); 79306553991SJ. Bruce Fields lock->lk_new_lock_seqid = be32_to_cpup(p++); 7941da177e4SLinus Torvalds COPYMEM(&lock->lk_new_clientid, sizeof(clientid_t)); 79506553991SJ. Bruce Fields lock->lk_new_owner.len = be32_to_cpup(p++); 7961da177e4SLinus Torvalds READ_BUF(lock->lk_new_owner.len); 7971da177e4SLinus Torvalds READMEM(lock->lk_new_owner.data, lock->lk_new_owner.len); 7981da177e4SLinus Torvalds } else { 799e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &lock->lk_old_lock_stateid); 800e31a1b66SBenny Halevy if (status) 801e31a1b66SBenny Halevy return status; 802e31a1b66SBenny Halevy READ_BUF(4); 80306553991SJ. Bruce Fields lock->lk_old_lock_seqid = be32_to_cpup(p++); 8041da177e4SLinus Torvalds } 8051da177e4SLinus Torvalds 8061da177e4SLinus Torvalds DECODE_TAIL; 8071da177e4SLinus Torvalds } 8081da177e4SLinus Torvalds 809b37ad28bSAl Viro static __be32 8101da177e4SLinus Torvalds nfsd4_decode_lockt(struct nfsd4_compoundargs *argp, struct nfsd4_lockt *lockt) 8111da177e4SLinus Torvalds { 8121da177e4SLinus Torvalds DECODE_HEAD; 8131da177e4SLinus Torvalds 8141da177e4SLinus Torvalds READ_BUF(32); 81506553991SJ. Bruce Fields lockt->lt_type = be32_to_cpup(p++); 8161da177e4SLinus Torvalds if((lockt->lt_type < NFS4_READ_LT) || (lockt->lt_type > NFS4_WRITEW_LT)) 8171da177e4SLinus Torvalds goto xdr_error; 818542d1ab3SJ. Bruce Fields p = xdr_decode_hyper(p, &lockt->lt_offset); 819542d1ab3SJ. Bruce Fields p = xdr_decode_hyper(p, &lockt->lt_length); 8201da177e4SLinus Torvalds COPYMEM(&lockt->lt_clientid, 8); 82106553991SJ. Bruce Fields lockt->lt_owner.len = be32_to_cpup(p++); 8221da177e4SLinus Torvalds READ_BUF(lockt->lt_owner.len); 8231da177e4SLinus Torvalds READMEM(lockt->lt_owner.data, lockt->lt_owner.len); 8241da177e4SLinus Torvalds 8251da177e4SLinus Torvalds DECODE_TAIL; 8261da177e4SLinus Torvalds } 8271da177e4SLinus Torvalds 828b37ad28bSAl Viro static __be32 8291da177e4SLinus Torvalds nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku) 8301da177e4SLinus Torvalds { 8311da177e4SLinus Torvalds DECODE_HEAD; 8321da177e4SLinus Torvalds 833e31a1b66SBenny Halevy READ_BUF(8); 83406553991SJ. Bruce Fields locku->lu_type = be32_to_cpup(p++); 8351da177e4SLinus Torvalds if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT)) 8361da177e4SLinus Torvalds goto xdr_error; 83706553991SJ. Bruce Fields locku->lu_seqid = be32_to_cpup(p++); 838e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &locku->lu_stateid); 839e31a1b66SBenny Halevy if (status) 840e31a1b66SBenny Halevy return status; 841e31a1b66SBenny Halevy READ_BUF(16); 842542d1ab3SJ. Bruce Fields p = xdr_decode_hyper(p, &locku->lu_offset); 843542d1ab3SJ. Bruce Fields p = xdr_decode_hyper(p, &locku->lu_length); 8441da177e4SLinus Torvalds 8451da177e4SLinus Torvalds DECODE_TAIL; 8461da177e4SLinus Torvalds } 8471da177e4SLinus Torvalds 848b37ad28bSAl Viro static __be32 8491da177e4SLinus Torvalds nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup) 8501da177e4SLinus Torvalds { 8511da177e4SLinus Torvalds DECODE_HEAD; 8521da177e4SLinus Torvalds 8531da177e4SLinus Torvalds READ_BUF(4); 85406553991SJ. Bruce Fields lookup->lo_len = be32_to_cpup(p++); 8551da177e4SLinus Torvalds READ_BUF(lookup->lo_len); 8561da177e4SLinus Torvalds SAVEMEM(lookup->lo_name, lookup->lo_len); 857a36b1725SJ. Bruce Fields if ((status = check_filename(lookup->lo_name, lookup->lo_len))) 8581da177e4SLinus Torvalds return status; 8591da177e4SLinus Torvalds 8601da177e4SLinus Torvalds DECODE_TAIL; 8611da177e4SLinus Torvalds } 8621da177e4SLinus Torvalds 8632c8bd7e0SBenny Halevy static __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *share_access, u32 *deleg_want, u32 *deleg_when) 86404f9e664SJ. Bruce Fields { 86504f9e664SJ. Bruce Fields __be32 *p; 86604f9e664SJ. Bruce Fields u32 w; 86704f9e664SJ. Bruce Fields 86804f9e664SJ. Bruce Fields READ_BUF(4); 86906553991SJ. Bruce Fields w = be32_to_cpup(p++); 8702c8bd7e0SBenny Halevy *share_access = w & NFS4_SHARE_ACCESS_MASK; 8712c8bd7e0SBenny Halevy *deleg_want = w & NFS4_SHARE_WANT_MASK; 8722c8bd7e0SBenny Halevy if (deleg_when) 8732c8bd7e0SBenny Halevy *deleg_when = w & NFS4_SHARE_WHEN_MASK; 8742c8bd7e0SBenny Halevy 87504f9e664SJ. Bruce Fields switch (w & NFS4_SHARE_ACCESS_MASK) { 87604f9e664SJ. Bruce Fields case NFS4_SHARE_ACCESS_READ: 87704f9e664SJ. Bruce Fields case NFS4_SHARE_ACCESS_WRITE: 87804f9e664SJ. Bruce Fields case NFS4_SHARE_ACCESS_BOTH: 87904f9e664SJ. Bruce Fields break; 88004f9e664SJ. Bruce Fields default: 88104f9e664SJ. Bruce Fields return nfserr_bad_xdr; 88204f9e664SJ. Bruce Fields } 883fc0d14feSBenny Halevy w &= ~NFS4_SHARE_ACCESS_MASK; 88404f9e664SJ. Bruce Fields if (!w) 88504f9e664SJ. Bruce Fields return nfs_ok; 88604f9e664SJ. Bruce Fields if (!argp->minorversion) 88704f9e664SJ. Bruce Fields return nfserr_bad_xdr; 88804f9e664SJ. Bruce Fields switch (w & NFS4_SHARE_WANT_MASK) { 88904f9e664SJ. Bruce Fields case NFS4_SHARE_WANT_NO_PREFERENCE: 89004f9e664SJ. Bruce Fields case NFS4_SHARE_WANT_READ_DELEG: 89104f9e664SJ. Bruce Fields case NFS4_SHARE_WANT_WRITE_DELEG: 89204f9e664SJ. Bruce Fields case NFS4_SHARE_WANT_ANY_DELEG: 89304f9e664SJ. Bruce Fields case NFS4_SHARE_WANT_NO_DELEG: 89404f9e664SJ. Bruce Fields case NFS4_SHARE_WANT_CANCEL: 89504f9e664SJ. Bruce Fields break; 89604f9e664SJ. Bruce Fields default: 89704f9e664SJ. Bruce Fields return nfserr_bad_xdr; 89804f9e664SJ. Bruce Fields } 89992bac8c5SBenny Halevy w &= ~NFS4_SHARE_WANT_MASK; 90004f9e664SJ. Bruce Fields if (!w) 90104f9e664SJ. Bruce Fields return nfs_ok; 9022c8bd7e0SBenny Halevy 9032c8bd7e0SBenny Halevy if (!deleg_when) /* open_downgrade */ 9042c8bd7e0SBenny Halevy return nfserr_inval; 90504f9e664SJ. Bruce Fields switch (w) { 90604f9e664SJ. Bruce Fields case NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL: 90704f9e664SJ. Bruce Fields case NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED: 908c668fc6dSBenny Halevy case (NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL | 909c668fc6dSBenny Halevy NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED): 91004f9e664SJ. Bruce Fields return nfs_ok; 91104f9e664SJ. Bruce Fields } 91204f9e664SJ. Bruce Fields xdr_error: 91304f9e664SJ. Bruce Fields return nfserr_bad_xdr; 91404f9e664SJ. Bruce Fields } 91504f9e664SJ. Bruce Fields 91604f9e664SJ. Bruce Fields static __be32 nfsd4_decode_share_deny(struct nfsd4_compoundargs *argp, u32 *x) 91704f9e664SJ. Bruce Fields { 91804f9e664SJ. Bruce Fields __be32 *p; 91904f9e664SJ. Bruce Fields 92004f9e664SJ. Bruce Fields READ_BUF(4); 92106553991SJ. Bruce Fields *x = be32_to_cpup(p++); 92204f9e664SJ. Bruce Fields /* Note: unlinke access bits, deny bits may be zero. */ 92301cd4afaSDan Carpenter if (*x & ~NFS4_SHARE_DENY_BOTH) 92404f9e664SJ. Bruce Fields return nfserr_bad_xdr; 92504f9e664SJ. Bruce Fields return nfs_ok; 92604f9e664SJ. Bruce Fields xdr_error: 92704f9e664SJ. Bruce Fields return nfserr_bad_xdr; 92804f9e664SJ. Bruce Fields } 92904f9e664SJ. Bruce Fields 930a084daf5SJ. Bruce Fields static __be32 nfsd4_decode_opaque(struct nfsd4_compoundargs *argp, struct xdr_netobj *o) 931a084daf5SJ. Bruce Fields { 932a084daf5SJ. Bruce Fields __be32 *p; 933a084daf5SJ. Bruce Fields 934a084daf5SJ. Bruce Fields READ_BUF(4); 93506553991SJ. Bruce Fields o->len = be32_to_cpup(p++); 936a084daf5SJ. Bruce Fields 937a084daf5SJ. Bruce Fields if (o->len == 0 || o->len > NFS4_OPAQUE_LIMIT) 938a084daf5SJ. Bruce Fields return nfserr_bad_xdr; 939a084daf5SJ. Bruce Fields 940a084daf5SJ. Bruce Fields READ_BUF(o->len); 941a084daf5SJ. Bruce Fields SAVEMEM(o->data, o->len); 942a084daf5SJ. Bruce Fields return nfs_ok; 943a084daf5SJ. Bruce Fields xdr_error: 944a084daf5SJ. Bruce Fields return nfserr_bad_xdr; 945a084daf5SJ. Bruce Fields } 946a084daf5SJ. Bruce Fields 947b37ad28bSAl Viro static __be32 9481da177e4SLinus Torvalds nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) 9491da177e4SLinus Torvalds { 9501da177e4SLinus Torvalds DECODE_HEAD; 9512c8bd7e0SBenny Halevy u32 dummy; 9521da177e4SLinus Torvalds 9531da177e4SLinus Torvalds memset(open->op_bmval, 0, sizeof(open->op_bmval)); 9541da177e4SLinus Torvalds open->op_iattr.ia_valid = 0; 955fe0750e5SJ. Bruce Fields open->op_openowner = NULL; 9561da177e4SLinus Torvalds 9579d313b17SJ. Bruce Fields open->op_xdr_error = 0; 9581da177e4SLinus Torvalds /* seqid, share_access, share_deny, clientid, ownerlen */ 95904f9e664SJ. Bruce Fields READ_BUF(4); 96006553991SJ. Bruce Fields open->op_seqid = be32_to_cpup(p++); 9612c8bd7e0SBenny Halevy /* decode, yet ignore deleg_when until supported */ 9622c8bd7e0SBenny Halevy status = nfsd4_decode_share_access(argp, &open->op_share_access, 9632c8bd7e0SBenny Halevy &open->op_deleg_want, &dummy); 96404f9e664SJ. Bruce Fields if (status) 96504f9e664SJ. Bruce Fields goto xdr_error; 96604f9e664SJ. Bruce Fields status = nfsd4_decode_share_deny(argp, &open->op_share_deny); 96704f9e664SJ. Bruce Fields if (status) 96804f9e664SJ. Bruce Fields goto xdr_error; 969a084daf5SJ. Bruce Fields READ_BUF(sizeof(clientid_t)); 9701da177e4SLinus Torvalds COPYMEM(&open->op_clientid, sizeof(clientid_t)); 971a084daf5SJ. Bruce Fields status = nfsd4_decode_opaque(argp, &open->op_owner); 972a084daf5SJ. Bruce Fields if (status) 973a084daf5SJ. Bruce Fields goto xdr_error; 974a084daf5SJ. Bruce Fields READ_BUF(4); 97506553991SJ. Bruce Fields open->op_create = be32_to_cpup(p++); 9761da177e4SLinus Torvalds switch (open->op_create) { 9771da177e4SLinus Torvalds case NFS4_OPEN_NOCREATE: 9781da177e4SLinus Torvalds break; 9791da177e4SLinus Torvalds case NFS4_OPEN_CREATE: 9801da177e4SLinus Torvalds READ_BUF(4); 98106553991SJ. Bruce Fields open->op_createmode = be32_to_cpup(p++); 9821da177e4SLinus Torvalds switch (open->op_createmode) { 9831da177e4SLinus Torvalds case NFS4_CREATE_UNCHECKED: 9841da177e4SLinus Torvalds case NFS4_CREATE_GUARDED: 985c0d6fc8aSBenny Halevy status = nfsd4_decode_fattr(argp, open->op_bmval, 98647057abdSAndreas Gruenbacher &open->op_iattr, &open->op_acl, &open->op_label, 987880a3a53SJ. Bruce Fields &open->op_umask); 988c0d6fc8aSBenny Halevy if (status) 9891da177e4SLinus Torvalds goto out; 9901da177e4SLinus Torvalds break; 9911da177e4SLinus Torvalds case NFS4_CREATE_EXCLUSIVE: 992ab4684d1SChuck Lever READ_BUF(NFS4_VERIFIER_SIZE); 993ab4684d1SChuck Lever COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE); 9941da177e4SLinus Torvalds break; 99579fb54abSBenny Halevy case NFS4_CREATE_EXCLUSIVE4_1: 99679fb54abSBenny Halevy if (argp->minorversion < 1) 99779fb54abSBenny Halevy goto xdr_error; 998ab4684d1SChuck Lever READ_BUF(NFS4_VERIFIER_SIZE); 999ab4684d1SChuck Lever COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE); 100079fb54abSBenny Halevy status = nfsd4_decode_fattr(argp, open->op_bmval, 100147057abdSAndreas Gruenbacher &open->op_iattr, &open->op_acl, &open->op_label, 1002880a3a53SJ. Bruce Fields &open->op_umask); 100379fb54abSBenny Halevy if (status) 100479fb54abSBenny Halevy goto out; 100579fb54abSBenny Halevy break; 10061da177e4SLinus Torvalds default: 10071da177e4SLinus Torvalds goto xdr_error; 10081da177e4SLinus Torvalds } 10091da177e4SLinus Torvalds break; 10101da177e4SLinus Torvalds default: 10111da177e4SLinus Torvalds goto xdr_error; 10121da177e4SLinus Torvalds } 10131da177e4SLinus Torvalds 10141da177e4SLinus Torvalds /* open_claim */ 10151da177e4SLinus Torvalds READ_BUF(4); 101606553991SJ. Bruce Fields open->op_claim_type = be32_to_cpup(p++); 10171da177e4SLinus Torvalds switch (open->op_claim_type) { 10181da177e4SLinus Torvalds case NFS4_OPEN_CLAIM_NULL: 10191da177e4SLinus Torvalds case NFS4_OPEN_CLAIM_DELEGATE_PREV: 10201da177e4SLinus Torvalds READ_BUF(4); 102106553991SJ. Bruce Fields open->op_fname.len = be32_to_cpup(p++); 10221da177e4SLinus Torvalds READ_BUF(open->op_fname.len); 10231da177e4SLinus Torvalds SAVEMEM(open->op_fname.data, open->op_fname.len); 1024a36b1725SJ. Bruce Fields if ((status = check_filename(open->op_fname.data, open->op_fname.len))) 10251da177e4SLinus Torvalds return status; 10261da177e4SLinus Torvalds break; 10271da177e4SLinus Torvalds case NFS4_OPEN_CLAIM_PREVIOUS: 10281da177e4SLinus Torvalds READ_BUF(4); 102906553991SJ. Bruce Fields open->op_delegate_type = be32_to_cpup(p++); 10301da177e4SLinus Torvalds break; 10311da177e4SLinus Torvalds case NFS4_OPEN_CLAIM_DELEGATE_CUR: 1032e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid); 1033e31a1b66SBenny Halevy if (status) 1034e31a1b66SBenny Halevy return status; 1035e31a1b66SBenny Halevy READ_BUF(4); 103606553991SJ. Bruce Fields open->op_fname.len = be32_to_cpup(p++); 10371da177e4SLinus Torvalds READ_BUF(open->op_fname.len); 10381da177e4SLinus Torvalds SAVEMEM(open->op_fname.data, open->op_fname.len); 1039a36b1725SJ. Bruce Fields if ((status = check_filename(open->op_fname.data, open->op_fname.len))) 10401da177e4SLinus Torvalds return status; 10411da177e4SLinus Torvalds break; 10428b289b2cSJ. Bruce Fields case NFS4_OPEN_CLAIM_FH: 10438b289b2cSJ. Bruce Fields case NFS4_OPEN_CLAIM_DELEG_PREV_FH: 10448b289b2cSJ. Bruce Fields if (argp->minorversion < 1) 10458b289b2cSJ. Bruce Fields goto xdr_error; 10468b289b2cSJ. Bruce Fields /* void */ 10478b289b2cSJ. Bruce Fields break; 10488b289b2cSJ. Bruce Fields case NFS4_OPEN_CLAIM_DELEG_CUR_FH: 10498b289b2cSJ. Bruce Fields if (argp->minorversion < 1) 10508b289b2cSJ. Bruce Fields goto xdr_error; 10518b289b2cSJ. Bruce Fields status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid); 10528b289b2cSJ. Bruce Fields if (status) 10538b289b2cSJ. Bruce Fields return status; 10548b289b2cSJ. Bruce Fields break; 10551da177e4SLinus Torvalds default: 10561da177e4SLinus Torvalds goto xdr_error; 10571da177e4SLinus Torvalds } 10581da177e4SLinus Torvalds 10591da177e4SLinus Torvalds DECODE_TAIL; 10601da177e4SLinus Torvalds } 10611da177e4SLinus Torvalds 1062b37ad28bSAl Viro static __be32 10631da177e4SLinus Torvalds nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_confirm *open_conf) 10641da177e4SLinus Torvalds { 10651da177e4SLinus Torvalds DECODE_HEAD; 10661da177e4SLinus Torvalds 1067e1a90ebdSAnna Schumaker if (argp->minorversion >= 1) 1068e1a90ebdSAnna Schumaker return nfserr_notsupp; 1069e1a90ebdSAnna Schumaker 1070e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &open_conf->oc_req_stateid); 1071e31a1b66SBenny Halevy if (status) 1072e31a1b66SBenny Halevy return status; 1073e31a1b66SBenny Halevy READ_BUF(4); 107406553991SJ. Bruce Fields open_conf->oc_seqid = be32_to_cpup(p++); 10751da177e4SLinus Torvalds 10761da177e4SLinus Torvalds DECODE_TAIL; 10771da177e4SLinus Torvalds } 10781da177e4SLinus Torvalds 1079b37ad28bSAl Viro static __be32 10801da177e4SLinus Torvalds nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_downgrade *open_down) 10811da177e4SLinus Torvalds { 10821da177e4SLinus Torvalds DECODE_HEAD; 10831da177e4SLinus Torvalds 1084e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &open_down->od_stateid); 1085e31a1b66SBenny Halevy if (status) 1086e31a1b66SBenny Halevy return status; 108704f9e664SJ. Bruce Fields READ_BUF(4); 108806553991SJ. Bruce Fields open_down->od_seqid = be32_to_cpup(p++); 10892c8bd7e0SBenny Halevy status = nfsd4_decode_share_access(argp, &open_down->od_share_access, 10902c8bd7e0SBenny Halevy &open_down->od_deleg_want, NULL); 109104f9e664SJ. Bruce Fields if (status) 109204f9e664SJ. Bruce Fields return status; 109304f9e664SJ. Bruce Fields status = nfsd4_decode_share_deny(argp, &open_down->od_share_deny); 109404f9e664SJ. Bruce Fields if (status) 109504f9e664SJ. Bruce Fields return status; 10961da177e4SLinus Torvalds DECODE_TAIL; 10971da177e4SLinus Torvalds } 10981da177e4SLinus Torvalds 1099b37ad28bSAl Viro static __be32 11001da177e4SLinus Torvalds nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh) 11011da177e4SLinus Torvalds { 11021da177e4SLinus Torvalds DECODE_HEAD; 11031da177e4SLinus Torvalds 11041da177e4SLinus Torvalds READ_BUF(4); 110506553991SJ. Bruce Fields putfh->pf_fhlen = be32_to_cpup(p++); 11061da177e4SLinus Torvalds if (putfh->pf_fhlen > NFS4_FHSIZE) 11071da177e4SLinus Torvalds goto xdr_error; 11081da177e4SLinus Torvalds READ_BUF(putfh->pf_fhlen); 11091da177e4SLinus Torvalds SAVEMEM(putfh->pf_fhval, putfh->pf_fhlen); 11101da177e4SLinus Torvalds 11111da177e4SLinus Torvalds DECODE_TAIL; 11121da177e4SLinus Torvalds } 11131da177e4SLinus Torvalds 1114b37ad28bSAl Viro static __be32 1115e1a90ebdSAnna Schumaker nfsd4_decode_putpubfh(struct nfsd4_compoundargs *argp, void *p) 1116e1a90ebdSAnna Schumaker { 1117e1a90ebdSAnna Schumaker if (argp->minorversion == 0) 1118e1a90ebdSAnna Schumaker return nfs_ok; 1119e1a90ebdSAnna Schumaker return nfserr_notsupp; 1120e1a90ebdSAnna Schumaker } 1121e1a90ebdSAnna Schumaker 1122e1a90ebdSAnna Schumaker static __be32 11231da177e4SLinus Torvalds nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read) 11241da177e4SLinus Torvalds { 11251da177e4SLinus Torvalds DECODE_HEAD; 11261da177e4SLinus Torvalds 1127e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &read->rd_stateid); 1128e31a1b66SBenny Halevy if (status) 1129e31a1b66SBenny Halevy return status; 1130e31a1b66SBenny Halevy READ_BUF(12); 1131542d1ab3SJ. Bruce Fields p = xdr_decode_hyper(p, &read->rd_offset); 113206553991SJ. Bruce Fields read->rd_length = be32_to_cpup(p++); 11331da177e4SLinus Torvalds 11341da177e4SLinus Torvalds DECODE_TAIL; 11351da177e4SLinus Torvalds } 11361da177e4SLinus Torvalds 1137b37ad28bSAl Viro static __be32 11381da177e4SLinus Torvalds nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, struct nfsd4_readdir *readdir) 11391da177e4SLinus Torvalds { 11401da177e4SLinus Torvalds DECODE_HEAD; 11411da177e4SLinus Torvalds 11421da177e4SLinus Torvalds READ_BUF(24); 1143542d1ab3SJ. Bruce Fields p = xdr_decode_hyper(p, &readdir->rd_cookie); 11441da177e4SLinus Torvalds COPYMEM(readdir->rd_verf.data, sizeof(readdir->rd_verf.data)); 114506553991SJ. Bruce Fields readdir->rd_dircount = be32_to_cpup(p++); 114606553991SJ. Bruce Fields readdir->rd_maxcount = be32_to_cpup(p++); 11471da177e4SLinus Torvalds if ((status = nfsd4_decode_bitmap(argp, readdir->rd_bmval))) 11481da177e4SLinus Torvalds goto out; 11491da177e4SLinus Torvalds 11501da177e4SLinus Torvalds DECODE_TAIL; 11511da177e4SLinus Torvalds } 11521da177e4SLinus Torvalds 1153b37ad28bSAl Viro static __be32 11541da177e4SLinus Torvalds nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove) 11551da177e4SLinus Torvalds { 11561da177e4SLinus Torvalds DECODE_HEAD; 11571da177e4SLinus Torvalds 11581da177e4SLinus Torvalds READ_BUF(4); 115906553991SJ. Bruce Fields remove->rm_namelen = be32_to_cpup(p++); 11601da177e4SLinus Torvalds READ_BUF(remove->rm_namelen); 11611da177e4SLinus Torvalds SAVEMEM(remove->rm_name, remove->rm_namelen); 1162a36b1725SJ. Bruce Fields if ((status = check_filename(remove->rm_name, remove->rm_namelen))) 11631da177e4SLinus Torvalds return status; 11641da177e4SLinus Torvalds 11651da177e4SLinus Torvalds DECODE_TAIL; 11661da177e4SLinus Torvalds } 11671da177e4SLinus Torvalds 1168b37ad28bSAl Viro static __be32 11691da177e4SLinus Torvalds nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename) 11701da177e4SLinus Torvalds { 11711da177e4SLinus Torvalds DECODE_HEAD; 11721da177e4SLinus Torvalds 11731da177e4SLinus Torvalds READ_BUF(4); 117406553991SJ. Bruce Fields rename->rn_snamelen = be32_to_cpup(p++); 11754aed9c46SJ. Bruce Fields READ_BUF(rename->rn_snamelen); 11761da177e4SLinus Torvalds SAVEMEM(rename->rn_sname, rename->rn_snamelen); 11774aed9c46SJ. Bruce Fields READ_BUF(4); 117806553991SJ. Bruce Fields rename->rn_tnamelen = be32_to_cpup(p++); 11791da177e4SLinus Torvalds READ_BUF(rename->rn_tnamelen); 11801da177e4SLinus Torvalds SAVEMEM(rename->rn_tname, rename->rn_tnamelen); 1181a36b1725SJ. Bruce Fields if ((status = check_filename(rename->rn_sname, rename->rn_snamelen))) 11821da177e4SLinus Torvalds return status; 1183a36b1725SJ. Bruce Fields if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen))) 11841da177e4SLinus Torvalds return status; 11851da177e4SLinus Torvalds 11861da177e4SLinus Torvalds DECODE_TAIL; 11871da177e4SLinus Torvalds } 11881da177e4SLinus Torvalds 1189b37ad28bSAl Viro static __be32 11901da177e4SLinus Torvalds nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid) 11911da177e4SLinus Torvalds { 11921da177e4SLinus Torvalds DECODE_HEAD; 11931da177e4SLinus Torvalds 1194e1a90ebdSAnna Schumaker if (argp->minorversion >= 1) 1195e1a90ebdSAnna Schumaker return nfserr_notsupp; 1196e1a90ebdSAnna Schumaker 11971da177e4SLinus Torvalds READ_BUF(sizeof(clientid_t)); 11981da177e4SLinus Torvalds COPYMEM(clientid, sizeof(clientid_t)); 11991da177e4SLinus Torvalds 12001da177e4SLinus Torvalds DECODE_TAIL; 12011da177e4SLinus Torvalds } 12021da177e4SLinus Torvalds 1203b37ad28bSAl Viro static __be32 1204dcb488a3SAndy Adamson nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp, 1205dcb488a3SAndy Adamson struct nfsd4_secinfo *secinfo) 1206dcb488a3SAndy Adamson { 1207dcb488a3SAndy Adamson DECODE_HEAD; 1208dcb488a3SAndy Adamson 1209dcb488a3SAndy Adamson READ_BUF(4); 121006553991SJ. Bruce Fields secinfo->si_namelen = be32_to_cpup(p++); 1211dcb488a3SAndy Adamson READ_BUF(secinfo->si_namelen); 1212dcb488a3SAndy Adamson SAVEMEM(secinfo->si_name, secinfo->si_namelen); 1213a36b1725SJ. Bruce Fields status = check_filename(secinfo->si_name, secinfo->si_namelen); 1214dcb488a3SAndy Adamson if (status) 1215dcb488a3SAndy Adamson return status; 1216dcb488a3SAndy Adamson DECODE_TAIL; 1217dcb488a3SAndy Adamson } 1218dcb488a3SAndy Adamson 1219dcb488a3SAndy Adamson static __be32 122004f4ad16SJ. Bruce Fields nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp, 122104f4ad16SJ. Bruce Fields struct nfsd4_secinfo_no_name *sin) 122204f4ad16SJ. Bruce Fields { 122304f4ad16SJ. Bruce Fields DECODE_HEAD; 122404f4ad16SJ. Bruce Fields 122504f4ad16SJ. Bruce Fields READ_BUF(4); 122606553991SJ. Bruce Fields sin->sin_style = be32_to_cpup(p++); 122704f4ad16SJ. Bruce Fields DECODE_TAIL; 122804f4ad16SJ. Bruce Fields } 122904f4ad16SJ. Bruce Fields 123004f4ad16SJ. Bruce Fields static __be32 12311da177e4SLinus Torvalds nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr) 12321da177e4SLinus Torvalds { 1233e31a1b66SBenny Halevy __be32 status; 12341da177e4SLinus Torvalds 1235e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &setattr->sa_stateid); 1236e31a1b66SBenny Halevy if (status) 1237e31a1b66SBenny Halevy return status; 12383c8e0316SYu Zhiguo return nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr, 123947057abdSAndreas Gruenbacher &setattr->sa_acl, &setattr->sa_label, NULL); 12401da177e4SLinus Torvalds } 12411da177e4SLinus Torvalds 1242b37ad28bSAl Viro static __be32 12431da177e4SLinus Torvalds nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid *setclientid) 12441da177e4SLinus Torvalds { 12451da177e4SLinus Torvalds DECODE_HEAD; 12461da177e4SLinus Torvalds 1247e1a90ebdSAnna Schumaker if (argp->minorversion >= 1) 1248e1a90ebdSAnna Schumaker return nfserr_notsupp; 1249e1a90ebdSAnna Schumaker 1250ab4684d1SChuck Lever READ_BUF(NFS4_VERIFIER_SIZE); 1251ab4684d1SChuck Lever COPYMEM(setclientid->se_verf.data, NFS4_VERIFIER_SIZE); 12521da177e4SLinus Torvalds 1253a084daf5SJ. Bruce Fields status = nfsd4_decode_opaque(argp, &setclientid->se_name); 1254a084daf5SJ. Bruce Fields if (status) 1255a084daf5SJ. Bruce Fields return nfserr_bad_xdr; 1256a084daf5SJ. Bruce Fields READ_BUF(8); 125706553991SJ. Bruce Fields setclientid->se_callback_prog = be32_to_cpup(p++); 125806553991SJ. Bruce Fields setclientid->se_callback_netid_len = be32_to_cpup(p++); 12594aed9c46SJ. Bruce Fields READ_BUF(setclientid->se_callback_netid_len); 12601da177e4SLinus Torvalds SAVEMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len); 12614aed9c46SJ. Bruce Fields READ_BUF(4); 126206553991SJ. Bruce Fields setclientid->se_callback_addr_len = be32_to_cpup(p++); 12631da177e4SLinus Torvalds 12644aed9c46SJ. Bruce Fields READ_BUF(setclientid->se_callback_addr_len); 12651da177e4SLinus Torvalds SAVEMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len); 12664aed9c46SJ. Bruce Fields READ_BUF(4); 126706553991SJ. Bruce Fields setclientid->se_callback_ident = be32_to_cpup(p++); 12681da177e4SLinus Torvalds 12691da177e4SLinus Torvalds DECODE_TAIL; 12701da177e4SLinus Torvalds } 12711da177e4SLinus Torvalds 1272b37ad28bSAl Viro static __be32 12731da177e4SLinus Torvalds nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid_confirm *scd_c) 12741da177e4SLinus Torvalds { 12751da177e4SLinus Torvalds DECODE_HEAD; 12761da177e4SLinus Torvalds 1277e1a90ebdSAnna Schumaker if (argp->minorversion >= 1) 1278e1a90ebdSAnna Schumaker return nfserr_notsupp; 1279e1a90ebdSAnna Schumaker 1280ab4684d1SChuck Lever READ_BUF(8 + NFS4_VERIFIER_SIZE); 12811da177e4SLinus Torvalds COPYMEM(&scd_c->sc_clientid, 8); 1282ab4684d1SChuck Lever COPYMEM(&scd_c->sc_confirm, NFS4_VERIFIER_SIZE); 12831da177e4SLinus Torvalds 12841da177e4SLinus Torvalds DECODE_TAIL; 12851da177e4SLinus Torvalds } 12861da177e4SLinus Torvalds 12871da177e4SLinus Torvalds /* Also used for NVERIFY */ 1288b37ad28bSAl Viro static __be32 12891da177e4SLinus Torvalds nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify) 12901da177e4SLinus Torvalds { 12911da177e4SLinus Torvalds DECODE_HEAD; 12921da177e4SLinus Torvalds 12931da177e4SLinus Torvalds if ((status = nfsd4_decode_bitmap(argp, verify->ve_bmval))) 12941da177e4SLinus Torvalds goto out; 12951da177e4SLinus Torvalds 12961da177e4SLinus Torvalds /* For convenience's sake, we compare raw xdr'd attributes in 1297e5f95703SJ. Bruce Fields * nfsd4_proc_verify */ 1298e5f95703SJ. Bruce Fields 12991da177e4SLinus Torvalds READ_BUF(4); 130006553991SJ. Bruce Fields verify->ve_attrlen = be32_to_cpup(p++); 13011da177e4SLinus Torvalds READ_BUF(verify->ve_attrlen); 13021da177e4SLinus Torvalds SAVEMEM(verify->ve_attrval, verify->ve_attrlen); 13031da177e4SLinus Torvalds 13041da177e4SLinus Torvalds DECODE_TAIL; 13051da177e4SLinus Torvalds } 13061da177e4SLinus Torvalds 1307b37ad28bSAl Viro static __be32 13081da177e4SLinus Torvalds nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) 13091da177e4SLinus Torvalds { 13101da177e4SLinus Torvalds DECODE_HEAD; 13111da177e4SLinus Torvalds 1312e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &write->wr_stateid); 1313e31a1b66SBenny Halevy if (status) 1314e31a1b66SBenny Halevy return status; 1315e31a1b66SBenny Halevy READ_BUF(16); 1316542d1ab3SJ. Bruce Fields p = xdr_decode_hyper(p, &write->wr_offset); 131706553991SJ. Bruce Fields write->wr_stable_how = be32_to_cpup(p++); 131854bbb7d2SKinglong Mee if (write->wr_stable_how > NFS_FILE_SYNC) 13191da177e4SLinus Torvalds goto xdr_error; 132006553991SJ. Bruce Fields write->wr_buflen = be32_to_cpup(p++); 13211da177e4SLinus Torvalds 1322874c7b8eSFrank van der Linden status = svcxdr_construct_vector(argp, &write->wr_head, 1323874c7b8eSFrank van der Linden &write->wr_pagelist, write->wr_buflen); 1324874c7b8eSFrank van der Linden if (status) 1325874c7b8eSFrank van der Linden return status; 13261da177e4SLinus Torvalds 13271da177e4SLinus Torvalds DECODE_TAIL; 13281da177e4SLinus Torvalds } 13291da177e4SLinus Torvalds 1330b37ad28bSAl Viro static __be32 13311da177e4SLinus Torvalds nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_release_lockowner *rlockowner) 13321da177e4SLinus Torvalds { 13331da177e4SLinus Torvalds DECODE_HEAD; 13341da177e4SLinus Torvalds 1335e1a90ebdSAnna Schumaker if (argp->minorversion >= 1) 1336e1a90ebdSAnna Schumaker return nfserr_notsupp; 1337e1a90ebdSAnna Schumaker 13381da177e4SLinus Torvalds READ_BUF(12); 13391da177e4SLinus Torvalds COPYMEM(&rlockowner->rl_clientid, sizeof(clientid_t)); 134006553991SJ. Bruce Fields rlockowner->rl_owner.len = be32_to_cpup(p++); 13411da177e4SLinus Torvalds READ_BUF(rlockowner->rl_owner.len); 13421da177e4SLinus Torvalds READMEM(rlockowner->rl_owner.data, rlockowner->rl_owner.len); 13431da177e4SLinus Torvalds 134460adfc50SAndy Adamson if (argp->minorversion && !zero_clientid(&rlockowner->rl_clientid)) 134560adfc50SAndy Adamson return nfserr_inval; 13461da177e4SLinus Torvalds DECODE_TAIL; 13471da177e4SLinus Torvalds } 13481da177e4SLinus Torvalds 1349b37ad28bSAl Viro static __be32 13502db134ebSAndy Adamson nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp, 13510733d213SAndy Adamson struct nfsd4_exchange_id *exid) 13522db134ebSAndy Adamson { 13535afa040bSMi Jinlong int dummy, tmp; 13540733d213SAndy Adamson DECODE_HEAD; 13550733d213SAndy Adamson 13560733d213SAndy Adamson READ_BUF(NFS4_VERIFIER_SIZE); 13570733d213SAndy Adamson COPYMEM(exid->verifier.data, NFS4_VERIFIER_SIZE); 13580733d213SAndy Adamson 1359a084daf5SJ. Bruce Fields status = nfsd4_decode_opaque(argp, &exid->clname); 1360a084daf5SJ. Bruce Fields if (status) 1361a084daf5SJ. Bruce Fields return nfserr_bad_xdr; 13620733d213SAndy Adamson 13630733d213SAndy Adamson READ_BUF(4); 136406553991SJ. Bruce Fields exid->flags = be32_to_cpup(p++); 13650733d213SAndy Adamson 13660733d213SAndy Adamson /* Ignore state_protect4_a */ 13670733d213SAndy Adamson READ_BUF(4); 136806553991SJ. Bruce Fields exid->spa_how = be32_to_cpup(p++); 13690733d213SAndy Adamson switch (exid->spa_how) { 13700733d213SAndy Adamson case SP4_NONE: 13710733d213SAndy Adamson break; 13720733d213SAndy Adamson case SP4_MACH_CRED: 13730733d213SAndy Adamson /* spo_must_enforce */ 1374ed941643SAndrew Elble status = nfsd4_decode_bitmap(argp, 1375ed941643SAndrew Elble exid->spo_must_enforce); 1376ed941643SAndrew Elble if (status) 1377ed941643SAndrew Elble goto out; 13780733d213SAndy Adamson /* spo_must_allow */ 1379ed941643SAndrew Elble status = nfsd4_decode_bitmap(argp, exid->spo_must_allow); 1380ed941643SAndrew Elble if (status) 1381ed941643SAndrew Elble goto out; 13820733d213SAndy Adamson break; 13830733d213SAndy Adamson case SP4_SSV: 13840733d213SAndy Adamson /* ssp_ops */ 13850733d213SAndy Adamson READ_BUF(4); 138606553991SJ. Bruce Fields dummy = be32_to_cpup(p++); 13870733d213SAndy Adamson READ_BUF(dummy * 4); 13880733d213SAndy Adamson p += dummy; 13890733d213SAndy Adamson 13900733d213SAndy Adamson READ_BUF(4); 139106553991SJ. Bruce Fields dummy = be32_to_cpup(p++); 13920733d213SAndy Adamson READ_BUF(dummy * 4); 13930733d213SAndy Adamson p += dummy; 13940733d213SAndy Adamson 13950733d213SAndy Adamson /* ssp_hash_algs<> */ 13960733d213SAndy Adamson READ_BUF(4); 139706553991SJ. Bruce Fields tmp = be32_to_cpup(p++); 13985afa040bSMi Jinlong while (tmp--) { 13990733d213SAndy Adamson READ_BUF(4); 140006553991SJ. Bruce Fields dummy = be32_to_cpup(p++); 14010733d213SAndy Adamson READ_BUF(dummy); 14020733d213SAndy Adamson p += XDR_QUADLEN(dummy); 14035afa040bSMi Jinlong } 14045afa040bSMi Jinlong 14055afa040bSMi Jinlong /* ssp_encr_algs<> */ 14065afa040bSMi Jinlong READ_BUF(4); 140706553991SJ. Bruce Fields tmp = be32_to_cpup(p++); 14085afa040bSMi Jinlong while (tmp--) { 14095afa040bSMi Jinlong READ_BUF(4); 141006553991SJ. Bruce Fields dummy = be32_to_cpup(p++); 14115afa040bSMi Jinlong READ_BUF(dummy); 14125afa040bSMi Jinlong p += XDR_QUADLEN(dummy); 14135afa040bSMi Jinlong } 14140733d213SAndy Adamson 14155ed96bc5Snixiaoming /* ignore ssp_window and ssp_num_gss_handles: */ 14160733d213SAndy Adamson READ_BUF(8); 14170733d213SAndy Adamson break; 14180733d213SAndy Adamson default: 14190733d213SAndy Adamson goto xdr_error; 14200733d213SAndy Adamson } 14210733d213SAndy Adamson 14220733d213SAndy Adamson READ_BUF(4); /* nfs_impl_id4 array length */ 142306553991SJ. Bruce Fields dummy = be32_to_cpup(p++); 14240733d213SAndy Adamson 14250733d213SAndy Adamson if (dummy > 1) 14260733d213SAndy Adamson goto xdr_error; 14270733d213SAndy Adamson 14280733d213SAndy Adamson if (dummy == 1) { 142979123444SJ. Bruce Fields status = nfsd4_decode_opaque(argp, &exid->nii_domain); 143079123444SJ. Bruce Fields if (status) 143179123444SJ. Bruce Fields goto xdr_error; 14320733d213SAndy Adamson 14330733d213SAndy Adamson /* nii_name */ 143479123444SJ. Bruce Fields status = nfsd4_decode_opaque(argp, &exid->nii_name); 143579123444SJ. Bruce Fields if (status) 143679123444SJ. Bruce Fields goto xdr_error; 14370733d213SAndy Adamson 14380733d213SAndy Adamson /* nii_date */ 143979123444SJ. Bruce Fields status = nfsd4_decode_time(argp, &exid->nii_time); 144079123444SJ. Bruce Fields if (status) 144179123444SJ. Bruce Fields goto xdr_error; 14420733d213SAndy Adamson } 14430733d213SAndy Adamson DECODE_TAIL; 14442db134ebSAndy Adamson } 14452db134ebSAndy Adamson 14462db134ebSAndy Adamson static __be32 14472db134ebSAndy Adamson nfsd4_decode_create_session(struct nfsd4_compoundargs *argp, 14482db134ebSAndy Adamson struct nfsd4_create_session *sess) 14492db134ebSAndy Adamson { 1450ec6b5d7bSAndy Adamson DECODE_HEAD; 1451ec6b5d7bSAndy Adamson 1452ec6b5d7bSAndy Adamson READ_BUF(16); 1453ec6b5d7bSAndy Adamson COPYMEM(&sess->clientid, 8); 145406553991SJ. Bruce Fields sess->seqid = be32_to_cpup(p++); 145506553991SJ. Bruce Fields sess->flags = be32_to_cpup(p++); 1456ec6b5d7bSAndy Adamson 1457ec6b5d7bSAndy Adamson /* Fore channel attrs */ 1458ec6b5d7bSAndy Adamson READ_BUF(28); 1459b96811cdSTrond Myklebust p++; /* headerpadsz is always 0 */ 146006553991SJ. Bruce Fields sess->fore_channel.maxreq_sz = be32_to_cpup(p++); 146106553991SJ. Bruce Fields sess->fore_channel.maxresp_sz = be32_to_cpup(p++); 146206553991SJ. Bruce Fields sess->fore_channel.maxresp_cached = be32_to_cpup(p++); 146306553991SJ. Bruce Fields sess->fore_channel.maxops = be32_to_cpup(p++); 146406553991SJ. Bruce Fields sess->fore_channel.maxreqs = be32_to_cpup(p++); 146506553991SJ. Bruce Fields sess->fore_channel.nr_rdma_attrs = be32_to_cpup(p++); 1466ec6b5d7bSAndy Adamson if (sess->fore_channel.nr_rdma_attrs == 1) { 1467ec6b5d7bSAndy Adamson READ_BUF(4); 146806553991SJ. Bruce Fields sess->fore_channel.rdma_attrs = be32_to_cpup(p++); 1469ec6b5d7bSAndy Adamson } else if (sess->fore_channel.nr_rdma_attrs > 1) { 1470ec6b5d7bSAndy Adamson dprintk("Too many fore channel attr bitmaps!\n"); 1471ec6b5d7bSAndy Adamson goto xdr_error; 1472ec6b5d7bSAndy Adamson } 1473ec6b5d7bSAndy Adamson 1474ec6b5d7bSAndy Adamson /* Back channel attrs */ 1475ec6b5d7bSAndy Adamson READ_BUF(28); 1476b96811cdSTrond Myklebust p++; /* headerpadsz is always 0 */ 147706553991SJ. Bruce Fields sess->back_channel.maxreq_sz = be32_to_cpup(p++); 147806553991SJ. Bruce Fields sess->back_channel.maxresp_sz = be32_to_cpup(p++); 147906553991SJ. Bruce Fields sess->back_channel.maxresp_cached = be32_to_cpup(p++); 148006553991SJ. Bruce Fields sess->back_channel.maxops = be32_to_cpup(p++); 148106553991SJ. Bruce Fields sess->back_channel.maxreqs = be32_to_cpup(p++); 148206553991SJ. Bruce Fields sess->back_channel.nr_rdma_attrs = be32_to_cpup(p++); 1483ec6b5d7bSAndy Adamson if (sess->back_channel.nr_rdma_attrs == 1) { 1484ec6b5d7bSAndy Adamson READ_BUF(4); 148506553991SJ. Bruce Fields sess->back_channel.rdma_attrs = be32_to_cpup(p++); 1486ec6b5d7bSAndy Adamson } else if (sess->back_channel.nr_rdma_attrs > 1) { 1487ec6b5d7bSAndy Adamson dprintk("Too many back channel attr bitmaps!\n"); 1488ec6b5d7bSAndy Adamson goto xdr_error; 1489ec6b5d7bSAndy Adamson } 1490ec6b5d7bSAndy Adamson 1491acb2887eSJ. Bruce Fields READ_BUF(4); 149206553991SJ. Bruce Fields sess->callback_prog = be32_to_cpup(p++); 1493acb2887eSJ. Bruce Fields nfsd4_decode_cb_sec(argp, &sess->cb_sec); 1494ec6b5d7bSAndy Adamson DECODE_TAIL; 14952db134ebSAndy Adamson } 14962db134ebSAndy Adamson 14972db134ebSAndy Adamson static __be32 14982db134ebSAndy Adamson nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp, 14992db134ebSAndy Adamson struct nfsd4_destroy_session *destroy_session) 15002db134ebSAndy Adamson { 1501e10e0cfcSBenny Halevy DECODE_HEAD; 1502e10e0cfcSBenny Halevy READ_BUF(NFS4_MAX_SESSIONID_LEN); 1503e10e0cfcSBenny Halevy COPYMEM(destroy_session->sessionid.data, NFS4_MAX_SESSIONID_LEN); 1504e10e0cfcSBenny Halevy 1505e10e0cfcSBenny Halevy DECODE_TAIL; 15062db134ebSAndy Adamson } 15072db134ebSAndy Adamson 15082db134ebSAndy Adamson static __be32 1509e1ca12dfSBryan Schumaker nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp, 1510e1ca12dfSBryan Schumaker struct nfsd4_free_stateid *free_stateid) 1511e1ca12dfSBryan Schumaker { 1512e1ca12dfSBryan Schumaker DECODE_HEAD; 1513e1ca12dfSBryan Schumaker 1514e1ca12dfSBryan Schumaker READ_BUF(sizeof(stateid_t)); 151506553991SJ. Bruce Fields free_stateid->fr_stateid.si_generation = be32_to_cpup(p++); 1516e1ca12dfSBryan Schumaker COPYMEM(&free_stateid->fr_stateid.si_opaque, sizeof(stateid_opaque_t)); 1517e1ca12dfSBryan Schumaker 1518e1ca12dfSBryan Schumaker DECODE_TAIL; 1519e1ca12dfSBryan Schumaker } 1520e1ca12dfSBryan Schumaker 1521e1ca12dfSBryan Schumaker static __be32 15222db134ebSAndy Adamson nfsd4_decode_sequence(struct nfsd4_compoundargs *argp, 15232db134ebSAndy Adamson struct nfsd4_sequence *seq) 15242db134ebSAndy Adamson { 1525b85d4c01SBenny Halevy DECODE_HEAD; 1526b85d4c01SBenny Halevy 1527b85d4c01SBenny Halevy READ_BUF(NFS4_MAX_SESSIONID_LEN + 16); 1528b85d4c01SBenny Halevy COPYMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN); 152906553991SJ. Bruce Fields seq->seqid = be32_to_cpup(p++); 153006553991SJ. Bruce Fields seq->slotid = be32_to_cpup(p++); 153106553991SJ. Bruce Fields seq->maxslots = be32_to_cpup(p++); 153206553991SJ. Bruce Fields seq->cachethis = be32_to_cpup(p++); 1533b85d4c01SBenny Halevy 1534b85d4c01SBenny Halevy DECODE_TAIL; 15352db134ebSAndy Adamson } 15362db134ebSAndy Adamson 153717456804SBryan Schumaker static __be32 153817456804SBryan Schumaker nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, struct nfsd4_test_stateid *test_stateid) 153917456804SBryan Schumaker { 154017456804SBryan Schumaker int i; 154103cfb420SBryan Schumaker __be32 *p, status; 154203cfb420SBryan Schumaker struct nfsd4_test_stateid_id *stateid; 154317456804SBryan Schumaker 154417456804SBryan Schumaker READ_BUF(4); 154517456804SBryan Schumaker test_stateid->ts_num_ids = ntohl(*p++); 154617456804SBryan Schumaker 154703cfb420SBryan Schumaker INIT_LIST_HEAD(&test_stateid->ts_stateid_list); 154817456804SBryan Schumaker 154917456804SBryan Schumaker for (i = 0; i < test_stateid->ts_num_ids; i++) { 1550d5e23383SJ. Bruce Fields stateid = svcxdr_tmpalloc(argp, sizeof(*stateid)); 155103cfb420SBryan Schumaker if (!stateid) { 1552afcf6792SAl Viro status = nfserrno(-ENOMEM); 155303cfb420SBryan Schumaker goto out; 155403cfb420SBryan Schumaker } 155503cfb420SBryan Schumaker 155603cfb420SBryan Schumaker INIT_LIST_HEAD(&stateid->ts_id_list); 155703cfb420SBryan Schumaker list_add_tail(&stateid->ts_id_list, &test_stateid->ts_stateid_list); 155803cfb420SBryan Schumaker 155903cfb420SBryan Schumaker status = nfsd4_decode_stateid(argp, &stateid->ts_id_stateid); 156017456804SBryan Schumaker if (status) 156103cfb420SBryan Schumaker goto out; 156217456804SBryan Schumaker } 156317456804SBryan Schumaker 156417456804SBryan Schumaker status = 0; 156517456804SBryan Schumaker out: 156617456804SBryan Schumaker return status; 156717456804SBryan Schumaker xdr_error: 156817456804SBryan Schumaker dprintk("NFSD: xdr error (%s:%d)\n", __FILE__, __LINE__); 156917456804SBryan Schumaker status = nfserr_bad_xdr; 157017456804SBryan Schumaker goto out; 157117456804SBryan Schumaker } 157217456804SBryan Schumaker 1573345c2842SMi Jinlong static __be32 nfsd4_decode_destroy_clientid(struct nfsd4_compoundargs *argp, struct nfsd4_destroy_clientid *dc) 1574345c2842SMi Jinlong { 1575345c2842SMi Jinlong DECODE_HEAD; 1576345c2842SMi Jinlong 1577345c2842SMi Jinlong READ_BUF(8); 1578345c2842SMi Jinlong COPYMEM(&dc->clientid, 8); 1579345c2842SMi Jinlong 1580345c2842SMi Jinlong DECODE_TAIL; 1581345c2842SMi Jinlong } 1582345c2842SMi Jinlong 15834dc6ec00SJ. Bruce Fields static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc) 15844dc6ec00SJ. Bruce Fields { 15854dc6ec00SJ. Bruce Fields DECODE_HEAD; 15864dc6ec00SJ. Bruce Fields 15874dc6ec00SJ. Bruce Fields READ_BUF(4); 158806553991SJ. Bruce Fields rc->rca_one_fs = be32_to_cpup(p++); 15894dc6ec00SJ. Bruce Fields 15904dc6ec00SJ. Bruce Fields DECODE_TAIL; 15914dc6ec00SJ. Bruce Fields } 15924dc6ec00SJ. Bruce Fields 15939cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS 15949cf514ccSChristoph Hellwig static __be32 15959cf514ccSChristoph Hellwig nfsd4_decode_getdeviceinfo(struct nfsd4_compoundargs *argp, 15969cf514ccSChristoph Hellwig struct nfsd4_getdeviceinfo *gdev) 15979cf514ccSChristoph Hellwig { 15989cf514ccSChristoph Hellwig DECODE_HEAD; 15999cf514ccSChristoph Hellwig u32 num, i; 16009cf514ccSChristoph Hellwig 16019cf514ccSChristoph Hellwig READ_BUF(sizeof(struct nfsd4_deviceid) + 3 * 4); 16029cf514ccSChristoph Hellwig COPYMEM(&gdev->gd_devid, sizeof(struct nfsd4_deviceid)); 16039cf514ccSChristoph Hellwig gdev->gd_layout_type = be32_to_cpup(p++); 16049cf514ccSChristoph Hellwig gdev->gd_maxcount = be32_to_cpup(p++); 16059cf514ccSChristoph Hellwig num = be32_to_cpup(p++); 16069cf514ccSChristoph Hellwig if (num) { 16073171822fSScott Mayhew if (num > 1000) 16083171822fSScott Mayhew goto xdr_error; 16099cf514ccSChristoph Hellwig READ_BUF(4 * num); 16109cf514ccSChristoph Hellwig gdev->gd_notify_types = be32_to_cpup(p++); 16119cf514ccSChristoph Hellwig for (i = 1; i < num; i++) { 16129cf514ccSChristoph Hellwig if (be32_to_cpup(p++)) { 16139cf514ccSChristoph Hellwig status = nfserr_inval; 16149cf514ccSChristoph Hellwig goto out; 16159cf514ccSChristoph Hellwig } 16169cf514ccSChristoph Hellwig } 16179cf514ccSChristoph Hellwig } 16189cf514ccSChristoph Hellwig DECODE_TAIL; 16199cf514ccSChristoph Hellwig } 16209cf514ccSChristoph Hellwig 16219cf514ccSChristoph Hellwig static __be32 16229cf514ccSChristoph Hellwig nfsd4_decode_layoutget(struct nfsd4_compoundargs *argp, 16239cf514ccSChristoph Hellwig struct nfsd4_layoutget *lgp) 16249cf514ccSChristoph Hellwig { 16259cf514ccSChristoph Hellwig DECODE_HEAD; 16269cf514ccSChristoph Hellwig 16279cf514ccSChristoph Hellwig READ_BUF(36); 16289cf514ccSChristoph Hellwig lgp->lg_signal = be32_to_cpup(p++); 16299cf514ccSChristoph Hellwig lgp->lg_layout_type = be32_to_cpup(p++); 16309cf514ccSChristoph Hellwig lgp->lg_seg.iomode = be32_to_cpup(p++); 16319cf514ccSChristoph Hellwig p = xdr_decode_hyper(p, &lgp->lg_seg.offset); 16329cf514ccSChristoph Hellwig p = xdr_decode_hyper(p, &lgp->lg_seg.length); 16339cf514ccSChristoph Hellwig p = xdr_decode_hyper(p, &lgp->lg_minlength); 1634db59c0efSKinglong Mee 1635db59c0efSKinglong Mee status = nfsd4_decode_stateid(argp, &lgp->lg_sid); 1636db59c0efSKinglong Mee if (status) 1637db59c0efSKinglong Mee return status; 1638db59c0efSKinglong Mee 16399cf514ccSChristoph Hellwig READ_BUF(4); 16409cf514ccSChristoph Hellwig lgp->lg_maxcount = be32_to_cpup(p++); 16419cf514ccSChristoph Hellwig 16429cf514ccSChristoph Hellwig DECODE_TAIL; 16439cf514ccSChristoph Hellwig } 16449cf514ccSChristoph Hellwig 16459cf514ccSChristoph Hellwig static __be32 16469cf514ccSChristoph Hellwig nfsd4_decode_layoutcommit(struct nfsd4_compoundargs *argp, 16479cf514ccSChristoph Hellwig struct nfsd4_layoutcommit *lcp) 16489cf514ccSChristoph Hellwig { 16499cf514ccSChristoph Hellwig DECODE_HEAD; 16509cf514ccSChristoph Hellwig u32 timechange; 16519cf514ccSChristoph Hellwig 16529cf514ccSChristoph Hellwig READ_BUF(20); 16539cf514ccSChristoph Hellwig p = xdr_decode_hyper(p, &lcp->lc_seg.offset); 16549cf514ccSChristoph Hellwig p = xdr_decode_hyper(p, &lcp->lc_seg.length); 16559cf514ccSChristoph Hellwig lcp->lc_reclaim = be32_to_cpup(p++); 1656db59c0efSKinglong Mee 1657db59c0efSKinglong Mee status = nfsd4_decode_stateid(argp, &lcp->lc_sid); 1658db59c0efSKinglong Mee if (status) 1659db59c0efSKinglong Mee return status; 1660db59c0efSKinglong Mee 16619cf514ccSChristoph Hellwig READ_BUF(4); 16629cf514ccSChristoph Hellwig lcp->lc_newoffset = be32_to_cpup(p++); 16639cf514ccSChristoph Hellwig if (lcp->lc_newoffset) { 16649cf514ccSChristoph Hellwig READ_BUF(8); 16659cf514ccSChristoph Hellwig p = xdr_decode_hyper(p, &lcp->lc_last_wr); 16669cf514ccSChristoph Hellwig } else 16679cf514ccSChristoph Hellwig lcp->lc_last_wr = 0; 16689cf514ccSChristoph Hellwig READ_BUF(4); 16699cf514ccSChristoph Hellwig timechange = be32_to_cpup(p++); 16709cf514ccSChristoph Hellwig if (timechange) { 16719cf514ccSChristoph Hellwig status = nfsd4_decode_time(argp, &lcp->lc_mtime); 16729cf514ccSChristoph Hellwig if (status) 16739cf514ccSChristoph Hellwig return status; 16749cf514ccSChristoph Hellwig } else { 16759cf514ccSChristoph Hellwig lcp->lc_mtime.tv_nsec = UTIME_NOW; 16769cf514ccSChristoph Hellwig } 16779cf514ccSChristoph Hellwig READ_BUF(8); 16789cf514ccSChristoph Hellwig lcp->lc_layout_type = be32_to_cpup(p++); 16799cf514ccSChristoph Hellwig 16809cf514ccSChristoph Hellwig /* 16819cf514ccSChristoph Hellwig * Save the layout update in XDR format and let the layout driver deal 16829cf514ccSChristoph Hellwig * with it later. 16839cf514ccSChristoph Hellwig */ 16849cf514ccSChristoph Hellwig lcp->lc_up_len = be32_to_cpup(p++); 16859cf514ccSChristoph Hellwig if (lcp->lc_up_len > 0) { 16869cf514ccSChristoph Hellwig READ_BUF(lcp->lc_up_len); 16879cf514ccSChristoph Hellwig READMEM(lcp->lc_up_layout, lcp->lc_up_len); 16889cf514ccSChristoph Hellwig } 16899cf514ccSChristoph Hellwig 16909cf514ccSChristoph Hellwig DECODE_TAIL; 16919cf514ccSChristoph Hellwig } 16929cf514ccSChristoph Hellwig 16939cf514ccSChristoph Hellwig static __be32 16949cf514ccSChristoph Hellwig nfsd4_decode_layoutreturn(struct nfsd4_compoundargs *argp, 16959cf514ccSChristoph Hellwig struct nfsd4_layoutreturn *lrp) 16969cf514ccSChristoph Hellwig { 16979cf514ccSChristoph Hellwig DECODE_HEAD; 16989cf514ccSChristoph Hellwig 16999cf514ccSChristoph Hellwig READ_BUF(16); 17009cf514ccSChristoph Hellwig lrp->lr_reclaim = be32_to_cpup(p++); 17019cf514ccSChristoph Hellwig lrp->lr_layout_type = be32_to_cpup(p++); 17029cf514ccSChristoph Hellwig lrp->lr_seg.iomode = be32_to_cpup(p++); 17039cf514ccSChristoph Hellwig lrp->lr_return_type = be32_to_cpup(p++); 17049cf514ccSChristoph Hellwig if (lrp->lr_return_type == RETURN_FILE) { 17059cf514ccSChristoph Hellwig READ_BUF(16); 17069cf514ccSChristoph Hellwig p = xdr_decode_hyper(p, &lrp->lr_seg.offset); 17079cf514ccSChristoph Hellwig p = xdr_decode_hyper(p, &lrp->lr_seg.length); 1708db59c0efSKinglong Mee 1709db59c0efSKinglong Mee status = nfsd4_decode_stateid(argp, &lrp->lr_sid); 1710db59c0efSKinglong Mee if (status) 1711db59c0efSKinglong Mee return status; 1712db59c0efSKinglong Mee 17139cf514ccSChristoph Hellwig READ_BUF(4); 17149cf514ccSChristoph Hellwig lrp->lrf_body_len = be32_to_cpup(p++); 17159cf514ccSChristoph Hellwig if (lrp->lrf_body_len > 0) { 17169cf514ccSChristoph Hellwig READ_BUF(lrp->lrf_body_len); 17179cf514ccSChristoph Hellwig READMEM(lrp->lrf_body, lrp->lrf_body_len); 17189cf514ccSChristoph Hellwig } 17199cf514ccSChristoph Hellwig } else { 17209cf514ccSChristoph Hellwig lrp->lr_seg.offset = 0; 17219cf514ccSChristoph Hellwig lrp->lr_seg.length = NFS4_MAX_UINT64; 17229cf514ccSChristoph Hellwig } 17239cf514ccSChristoph Hellwig 17249cf514ccSChristoph Hellwig DECODE_TAIL; 17259cf514ccSChristoph Hellwig } 17269cf514ccSChristoph Hellwig #endif /* CONFIG_NFSD_PNFS */ 17279cf514ccSChristoph Hellwig 17282db134ebSAndy Adamson static __be32 172995d871f0SAnna Schumaker nfsd4_decode_fallocate(struct nfsd4_compoundargs *argp, 173095d871f0SAnna Schumaker struct nfsd4_fallocate *fallocate) 173195d871f0SAnna Schumaker { 173295d871f0SAnna Schumaker DECODE_HEAD; 173395d871f0SAnna Schumaker 173495d871f0SAnna Schumaker status = nfsd4_decode_stateid(argp, &fallocate->falloc_stateid); 173595d871f0SAnna Schumaker if (status) 173695d871f0SAnna Schumaker return status; 173795d871f0SAnna Schumaker 173895d871f0SAnna Schumaker READ_BUF(16); 173995d871f0SAnna Schumaker p = xdr_decode_hyper(p, &fallocate->falloc_offset); 174095d871f0SAnna Schumaker xdr_decode_hyper(p, &fallocate->falloc_length); 174195d871f0SAnna Schumaker 174295d871f0SAnna Schumaker DECODE_TAIL; 174395d871f0SAnna Schumaker } 174495d871f0SAnna Schumaker 174595d871f0SAnna Schumaker static __be32 1746ffa0160aSChristoph Hellwig nfsd4_decode_clone(struct nfsd4_compoundargs *argp, struct nfsd4_clone *clone) 1747ffa0160aSChristoph Hellwig { 1748ffa0160aSChristoph Hellwig DECODE_HEAD; 1749ffa0160aSChristoph Hellwig 1750ffa0160aSChristoph Hellwig status = nfsd4_decode_stateid(argp, &clone->cl_src_stateid); 1751ffa0160aSChristoph Hellwig if (status) 1752ffa0160aSChristoph Hellwig return status; 1753ffa0160aSChristoph Hellwig status = nfsd4_decode_stateid(argp, &clone->cl_dst_stateid); 1754ffa0160aSChristoph Hellwig if (status) 1755ffa0160aSChristoph Hellwig return status; 1756ffa0160aSChristoph Hellwig 1757ffa0160aSChristoph Hellwig READ_BUF(8 + 8 + 8); 1758ffa0160aSChristoph Hellwig p = xdr_decode_hyper(p, &clone->cl_src_pos); 1759ffa0160aSChristoph Hellwig p = xdr_decode_hyper(p, &clone->cl_dst_pos); 1760ffa0160aSChristoph Hellwig p = xdr_decode_hyper(p, &clone->cl_count); 1761ffa0160aSChristoph Hellwig DECODE_TAIL; 1762ffa0160aSChristoph Hellwig } 1763ffa0160aSChristoph Hellwig 176484e1b21dSOlga Kornievskaia static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp, 176584e1b21dSOlga Kornievskaia struct nl4_server *ns) 176684e1b21dSOlga Kornievskaia { 176784e1b21dSOlga Kornievskaia DECODE_HEAD; 176884e1b21dSOlga Kornievskaia struct nfs42_netaddr *naddr; 176984e1b21dSOlga Kornievskaia 177084e1b21dSOlga Kornievskaia READ_BUF(4); 177184e1b21dSOlga Kornievskaia ns->nl4_type = be32_to_cpup(p++); 177284e1b21dSOlga Kornievskaia 177384e1b21dSOlga Kornievskaia /* currently support for 1 inter-server source server */ 177484e1b21dSOlga Kornievskaia switch (ns->nl4_type) { 177584e1b21dSOlga Kornievskaia case NL4_NETADDR: 177684e1b21dSOlga Kornievskaia naddr = &ns->u.nl4_addr; 177784e1b21dSOlga Kornievskaia 177884e1b21dSOlga Kornievskaia READ_BUF(4); 177984e1b21dSOlga Kornievskaia naddr->netid_len = be32_to_cpup(p++); 178084e1b21dSOlga Kornievskaia if (naddr->netid_len > RPCBIND_MAXNETIDLEN) 178184e1b21dSOlga Kornievskaia goto xdr_error; 178284e1b21dSOlga Kornievskaia 178384e1b21dSOlga Kornievskaia READ_BUF(naddr->netid_len + 4); /* 4 for uaddr len */ 178484e1b21dSOlga Kornievskaia COPYMEM(naddr->netid, naddr->netid_len); 178584e1b21dSOlga Kornievskaia 178684e1b21dSOlga Kornievskaia naddr->addr_len = be32_to_cpup(p++); 178784e1b21dSOlga Kornievskaia if (naddr->addr_len > RPCBIND_MAXUADDRLEN) 178884e1b21dSOlga Kornievskaia goto xdr_error; 178984e1b21dSOlga Kornievskaia 179084e1b21dSOlga Kornievskaia READ_BUF(naddr->addr_len); 179184e1b21dSOlga Kornievskaia COPYMEM(naddr->addr, naddr->addr_len); 179284e1b21dSOlga Kornievskaia break; 179384e1b21dSOlga Kornievskaia default: 179484e1b21dSOlga Kornievskaia goto xdr_error; 179584e1b21dSOlga Kornievskaia } 179684e1b21dSOlga Kornievskaia DECODE_TAIL; 179784e1b21dSOlga Kornievskaia } 179884e1b21dSOlga Kornievskaia 1799ffa0160aSChristoph Hellwig static __be32 180029ae7f9dSAnna Schumaker nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy) 180129ae7f9dSAnna Schumaker { 180229ae7f9dSAnna Schumaker DECODE_HEAD; 180384e1b21dSOlga Kornievskaia struct nl4_server *ns_dummy; 180484e1b21dSOlga Kornievskaia int i, count; 180529ae7f9dSAnna Schumaker 180629ae7f9dSAnna Schumaker status = nfsd4_decode_stateid(argp, ©->cp_src_stateid); 180729ae7f9dSAnna Schumaker if (status) 180829ae7f9dSAnna Schumaker return status; 180929ae7f9dSAnna Schumaker status = nfsd4_decode_stateid(argp, ©->cp_dst_stateid); 181029ae7f9dSAnna Schumaker if (status) 181129ae7f9dSAnna Schumaker return status; 181229ae7f9dSAnna Schumaker 181329ae7f9dSAnna Schumaker READ_BUF(8 + 8 + 8 + 4 + 4 + 4); 181429ae7f9dSAnna Schumaker p = xdr_decode_hyper(p, ©->cp_src_pos); 181529ae7f9dSAnna Schumaker p = xdr_decode_hyper(p, ©->cp_dst_pos); 181629ae7f9dSAnna Schumaker p = xdr_decode_hyper(p, ©->cp_count); 1817edcc8452SJ. Bruce Fields p++; /* ca_consecutive: we always do consecutive copies */ 181829ae7f9dSAnna Schumaker copy->cp_synchronous = be32_to_cpup(p++); 181984e1b21dSOlga Kornievskaia 182084e1b21dSOlga Kornievskaia count = be32_to_cpup(p++); 182184e1b21dSOlga Kornievskaia 182284e1b21dSOlga Kornievskaia copy->cp_intra = false; 182384e1b21dSOlga Kornievskaia if (count == 0) { /* intra-server copy */ 182484e1b21dSOlga Kornievskaia copy->cp_intra = true; 182584e1b21dSOlga Kornievskaia goto intra; 182684e1b21dSOlga Kornievskaia } 182784e1b21dSOlga Kornievskaia 182884e1b21dSOlga Kornievskaia /* decode all the supplied server addresses but use first */ 182984e1b21dSOlga Kornievskaia status = nfsd4_decode_nl4_server(argp, ©->cp_src); 183084e1b21dSOlga Kornievskaia if (status) 183184e1b21dSOlga Kornievskaia return status; 183284e1b21dSOlga Kornievskaia 183384e1b21dSOlga Kornievskaia ns_dummy = kmalloc(sizeof(struct nl4_server), GFP_KERNEL); 183484e1b21dSOlga Kornievskaia if (ns_dummy == NULL) 183584e1b21dSOlga Kornievskaia return nfserrno(-ENOMEM); 183684e1b21dSOlga Kornievskaia for (i = 0; i < count - 1; i++) { 183784e1b21dSOlga Kornievskaia status = nfsd4_decode_nl4_server(argp, ns_dummy); 183884e1b21dSOlga Kornievskaia if (status) { 183984e1b21dSOlga Kornievskaia kfree(ns_dummy); 184084e1b21dSOlga Kornievskaia return status; 184184e1b21dSOlga Kornievskaia } 184284e1b21dSOlga Kornievskaia } 184384e1b21dSOlga Kornievskaia kfree(ns_dummy); 184484e1b21dSOlga Kornievskaia intra: 184529ae7f9dSAnna Schumaker 184629ae7f9dSAnna Schumaker DECODE_TAIL; 184729ae7f9dSAnna Schumaker } 184829ae7f9dSAnna Schumaker 184929ae7f9dSAnna Schumaker static __be32 18506308bc98SOlga Kornievskaia nfsd4_decode_offload_status(struct nfsd4_compoundargs *argp, 18516308bc98SOlga Kornievskaia struct nfsd4_offload_status *os) 18526308bc98SOlga Kornievskaia { 18536308bc98SOlga Kornievskaia return nfsd4_decode_stateid(argp, &os->stateid); 18546308bc98SOlga Kornievskaia } 18556308bc98SOlga Kornievskaia 18566308bc98SOlga Kornievskaia static __be32 185751911868SOlga Kornievskaia nfsd4_decode_copy_notify(struct nfsd4_compoundargs *argp, 185851911868SOlga Kornievskaia struct nfsd4_copy_notify *cn) 185951911868SOlga Kornievskaia { 18605aff7d08SChuck Lever __be32 status; 186151911868SOlga Kornievskaia 186251911868SOlga Kornievskaia status = nfsd4_decode_stateid(argp, &cn->cpn_src_stateid); 186351911868SOlga Kornievskaia if (status) 186451911868SOlga Kornievskaia return status; 186551911868SOlga Kornievskaia return nfsd4_decode_nl4_server(argp, &cn->cpn_dst); 186651911868SOlga Kornievskaia } 186751911868SOlga Kornievskaia 186851911868SOlga Kornievskaia static __be32 186924bab491SAnna Schumaker nfsd4_decode_seek(struct nfsd4_compoundargs *argp, struct nfsd4_seek *seek) 187024bab491SAnna Schumaker { 187124bab491SAnna Schumaker DECODE_HEAD; 187224bab491SAnna Schumaker 187324bab491SAnna Schumaker status = nfsd4_decode_stateid(argp, &seek->seek_stateid); 187424bab491SAnna Schumaker if (status) 187524bab491SAnna Schumaker return status; 187624bab491SAnna Schumaker 187724bab491SAnna Schumaker READ_BUF(8 + 4); 187824bab491SAnna Schumaker p = xdr_decode_hyper(p, &seek->seek_offset); 187924bab491SAnna Schumaker seek->seek_whence = be32_to_cpup(p); 188024bab491SAnna Schumaker 188124bab491SAnna Schumaker DECODE_TAIL; 188224bab491SAnna Schumaker } 188324bab491SAnna Schumaker 188423e50fe3SFrank van der Linden /* 188523e50fe3SFrank van der Linden * XDR data that is more than PAGE_SIZE in size is normally part of a 188623e50fe3SFrank van der Linden * read or write. However, the size of extended attributes is limited 188723e50fe3SFrank van der Linden * by the maximum request size, and then further limited by the underlying 188823e50fe3SFrank van der Linden * filesystem limits. This can exceed PAGE_SIZE (currently, XATTR_SIZE_MAX 188923e50fe3SFrank van der Linden * is 64k). Since there is no kvec- or page-based interface to xattrs, 189023e50fe3SFrank van der Linden * and we're not dealing with contiguous pages, we need to do some copying. 189123e50fe3SFrank van der Linden */ 189223e50fe3SFrank van der Linden 189323e50fe3SFrank van der Linden /* 189423e50fe3SFrank van der Linden * Decode data into buffer. Uses head and pages constructed by 189523e50fe3SFrank van der Linden * svcxdr_construct_vector. 189623e50fe3SFrank van der Linden */ 189723e50fe3SFrank van der Linden static __be32 189823e50fe3SFrank van der Linden nfsd4_vbuf_from_vector(struct nfsd4_compoundargs *argp, struct kvec *head, 189923e50fe3SFrank van der Linden struct page **pages, char **bufp, u32 buflen) 190023e50fe3SFrank van der Linden { 190123e50fe3SFrank van der Linden char *tmp, *dp; 190223e50fe3SFrank van der Linden u32 len; 190323e50fe3SFrank van der Linden 190423e50fe3SFrank van der Linden if (buflen <= head->iov_len) { 190523e50fe3SFrank van der Linden /* 190623e50fe3SFrank van der Linden * We're in luck, the head has enough space. Just return 190723e50fe3SFrank van der Linden * the head, no need for copying. 190823e50fe3SFrank van der Linden */ 190923e50fe3SFrank van der Linden *bufp = head->iov_base; 191023e50fe3SFrank van der Linden return 0; 191123e50fe3SFrank van der Linden } 191223e50fe3SFrank van der Linden 191323e50fe3SFrank van der Linden tmp = svcxdr_tmpalloc(argp, buflen); 191423e50fe3SFrank van der Linden if (tmp == NULL) 191523e50fe3SFrank van der Linden return nfserr_jukebox; 191623e50fe3SFrank van der Linden 191723e50fe3SFrank van der Linden dp = tmp; 191823e50fe3SFrank van der Linden memcpy(dp, head->iov_base, head->iov_len); 191923e50fe3SFrank van der Linden buflen -= head->iov_len; 192023e50fe3SFrank van der Linden dp += head->iov_len; 192123e50fe3SFrank van der Linden 192223e50fe3SFrank van der Linden while (buflen > 0) { 192323e50fe3SFrank van der Linden len = min_t(u32, buflen, PAGE_SIZE); 192423e50fe3SFrank van der Linden memcpy(dp, page_address(*pages), len); 192523e50fe3SFrank van der Linden 192623e50fe3SFrank van der Linden buflen -= len; 192723e50fe3SFrank van der Linden dp += len; 192823e50fe3SFrank van der Linden pages++; 192923e50fe3SFrank van der Linden } 193023e50fe3SFrank van der Linden 193123e50fe3SFrank van der Linden *bufp = tmp; 193223e50fe3SFrank van der Linden return 0; 193323e50fe3SFrank van der Linden } 193423e50fe3SFrank van der Linden 193523e50fe3SFrank van der Linden /* 193623e50fe3SFrank van der Linden * Get a user extended attribute name from the XDR buffer. 193723e50fe3SFrank van der Linden * It will not have the "user." prefix, so prepend it. 193823e50fe3SFrank van der Linden * Lastly, check for nul characters in the name. 193923e50fe3SFrank van der Linden */ 194023e50fe3SFrank van der Linden static __be32 194123e50fe3SFrank van der Linden nfsd4_decode_xattr_name(struct nfsd4_compoundargs *argp, char **namep) 194223e50fe3SFrank van der Linden { 194323e50fe3SFrank van der Linden DECODE_HEAD; 194423e50fe3SFrank van der Linden char *name, *sp, *dp; 194523e50fe3SFrank van der Linden u32 namelen, cnt; 194623e50fe3SFrank van der Linden 194723e50fe3SFrank van der Linden READ_BUF(4); 194823e50fe3SFrank van der Linden namelen = be32_to_cpup(p++); 194923e50fe3SFrank van der Linden 195023e50fe3SFrank van der Linden if (namelen > (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)) 195123e50fe3SFrank van der Linden return nfserr_nametoolong; 195223e50fe3SFrank van der Linden 195323e50fe3SFrank van der Linden if (namelen == 0) 195423e50fe3SFrank van der Linden goto xdr_error; 195523e50fe3SFrank van der Linden 195623e50fe3SFrank van der Linden READ_BUF(namelen); 195723e50fe3SFrank van der Linden 195823e50fe3SFrank van der Linden name = svcxdr_tmpalloc(argp, namelen + XATTR_USER_PREFIX_LEN + 1); 195923e50fe3SFrank van der Linden if (!name) 196023e50fe3SFrank van der Linden return nfserr_jukebox; 196123e50fe3SFrank van der Linden 196223e50fe3SFrank van der Linden memcpy(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); 196323e50fe3SFrank van der Linden 196423e50fe3SFrank van der Linden /* 196523e50fe3SFrank van der Linden * Copy the extended attribute name over while checking for 0 196623e50fe3SFrank van der Linden * characters. 196723e50fe3SFrank van der Linden */ 196823e50fe3SFrank van der Linden sp = (char *)p; 196923e50fe3SFrank van der Linden dp = name + XATTR_USER_PREFIX_LEN; 197023e50fe3SFrank van der Linden cnt = namelen; 197123e50fe3SFrank van der Linden 197223e50fe3SFrank van der Linden while (cnt-- > 0) { 197323e50fe3SFrank van der Linden if (*sp == '\0') 197423e50fe3SFrank van der Linden goto xdr_error; 197523e50fe3SFrank van der Linden *dp++ = *sp++; 197623e50fe3SFrank van der Linden } 197723e50fe3SFrank van der Linden *dp = '\0'; 197823e50fe3SFrank van der Linden 197923e50fe3SFrank van der Linden *namep = name; 198023e50fe3SFrank van der Linden 198123e50fe3SFrank van der Linden DECODE_TAIL; 198223e50fe3SFrank van der Linden } 198323e50fe3SFrank van der Linden 198423e50fe3SFrank van der Linden /* 198523e50fe3SFrank van der Linden * A GETXATTR op request comes without a length specifier. We just set the 198623e50fe3SFrank van der Linden * maximum length for the reply based on XATTR_SIZE_MAX and the maximum 198723e50fe3SFrank van der Linden * channel reply size. nfsd_getxattr will probe the length of the xattr, 198823e50fe3SFrank van der Linden * check it against getxa_len, and allocate + return the value. 198923e50fe3SFrank van der Linden */ 199023e50fe3SFrank van der Linden static __be32 199123e50fe3SFrank van der Linden nfsd4_decode_getxattr(struct nfsd4_compoundargs *argp, 199223e50fe3SFrank van der Linden struct nfsd4_getxattr *getxattr) 199323e50fe3SFrank van der Linden { 199423e50fe3SFrank van der Linden __be32 status; 199523e50fe3SFrank van der Linden u32 maxcount; 199623e50fe3SFrank van der Linden 199723e50fe3SFrank van der Linden status = nfsd4_decode_xattr_name(argp, &getxattr->getxa_name); 199823e50fe3SFrank van der Linden if (status) 199923e50fe3SFrank van der Linden return status; 200023e50fe3SFrank van der Linden 200123e50fe3SFrank van der Linden maxcount = svc_max_payload(argp->rqstp); 200223e50fe3SFrank van der Linden maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount); 200323e50fe3SFrank van der Linden 200423e50fe3SFrank van der Linden getxattr->getxa_len = maxcount; 200523e50fe3SFrank van der Linden 200623e50fe3SFrank van der Linden return status; 200723e50fe3SFrank van der Linden } 200823e50fe3SFrank van der Linden 200923e50fe3SFrank van der Linden static __be32 201023e50fe3SFrank van der Linden nfsd4_decode_setxattr(struct nfsd4_compoundargs *argp, 201123e50fe3SFrank van der Linden struct nfsd4_setxattr *setxattr) 201223e50fe3SFrank van der Linden { 201323e50fe3SFrank van der Linden DECODE_HEAD; 201423e50fe3SFrank van der Linden u32 flags, maxcount, size; 201523e50fe3SFrank van der Linden struct kvec head; 201623e50fe3SFrank van der Linden struct page **pagelist; 201723e50fe3SFrank van der Linden 201823e50fe3SFrank van der Linden READ_BUF(4); 201923e50fe3SFrank van der Linden flags = be32_to_cpup(p++); 202023e50fe3SFrank van der Linden 202123e50fe3SFrank van der Linden if (flags > SETXATTR4_REPLACE) 202223e50fe3SFrank van der Linden return nfserr_inval; 202323e50fe3SFrank van der Linden setxattr->setxa_flags = flags; 202423e50fe3SFrank van der Linden 202523e50fe3SFrank van der Linden status = nfsd4_decode_xattr_name(argp, &setxattr->setxa_name); 202623e50fe3SFrank van der Linden if (status) 202723e50fe3SFrank van der Linden return status; 202823e50fe3SFrank van der Linden 202923e50fe3SFrank van der Linden maxcount = svc_max_payload(argp->rqstp); 203023e50fe3SFrank van der Linden maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount); 203123e50fe3SFrank van der Linden 203223e50fe3SFrank van der Linden READ_BUF(4); 203323e50fe3SFrank van der Linden size = be32_to_cpup(p++); 203423e50fe3SFrank van der Linden if (size > maxcount) 203523e50fe3SFrank van der Linden return nfserr_xattr2big; 203623e50fe3SFrank van der Linden 203723e50fe3SFrank van der Linden setxattr->setxa_len = size; 203823e50fe3SFrank van der Linden if (size > 0) { 203923e50fe3SFrank van der Linden status = svcxdr_construct_vector(argp, &head, &pagelist, size); 204023e50fe3SFrank van der Linden if (status) 204123e50fe3SFrank van der Linden return status; 204223e50fe3SFrank van der Linden 204323e50fe3SFrank van der Linden status = nfsd4_vbuf_from_vector(argp, &head, pagelist, 204423e50fe3SFrank van der Linden &setxattr->setxa_buf, size); 204523e50fe3SFrank van der Linden } 204623e50fe3SFrank van der Linden 204723e50fe3SFrank van der Linden DECODE_TAIL; 204823e50fe3SFrank van der Linden } 204923e50fe3SFrank van der Linden 205023e50fe3SFrank van der Linden static __be32 205123e50fe3SFrank van der Linden nfsd4_decode_listxattrs(struct nfsd4_compoundargs *argp, 205223e50fe3SFrank van der Linden struct nfsd4_listxattrs *listxattrs) 205323e50fe3SFrank van der Linden { 205423e50fe3SFrank van der Linden DECODE_HEAD; 205523e50fe3SFrank van der Linden u32 maxcount; 205623e50fe3SFrank van der Linden 205723e50fe3SFrank van der Linden READ_BUF(12); 205823e50fe3SFrank van der Linden p = xdr_decode_hyper(p, &listxattrs->lsxa_cookie); 205923e50fe3SFrank van der Linden 206023e50fe3SFrank van der Linden /* 206123e50fe3SFrank van der Linden * If the cookie is too large to have even one user.x attribute 206223e50fe3SFrank van der Linden * plus trailing '\0' left in a maximum size buffer, it's invalid. 206323e50fe3SFrank van der Linden */ 206423e50fe3SFrank van der Linden if (listxattrs->lsxa_cookie >= 206523e50fe3SFrank van der Linden (XATTR_LIST_MAX / (XATTR_USER_PREFIX_LEN + 2))) 206623e50fe3SFrank van der Linden return nfserr_badcookie; 206723e50fe3SFrank van der Linden 206823e50fe3SFrank van der Linden maxcount = be32_to_cpup(p++); 206923e50fe3SFrank van der Linden if (maxcount < 8) 207023e50fe3SFrank van der Linden /* Always need at least 2 words (length and one character) */ 207123e50fe3SFrank van der Linden return nfserr_inval; 207223e50fe3SFrank van der Linden 207323e50fe3SFrank van der Linden maxcount = min(maxcount, svc_max_payload(argp->rqstp)); 207423e50fe3SFrank van der Linden listxattrs->lsxa_maxcount = maxcount; 207523e50fe3SFrank van der Linden 207623e50fe3SFrank van der Linden DECODE_TAIL; 207723e50fe3SFrank van der Linden } 207823e50fe3SFrank van der Linden 207923e50fe3SFrank van der Linden static __be32 208023e50fe3SFrank van der Linden nfsd4_decode_removexattr(struct nfsd4_compoundargs *argp, 208123e50fe3SFrank van der Linden struct nfsd4_removexattr *removexattr) 208223e50fe3SFrank van der Linden { 208323e50fe3SFrank van der Linden return nfsd4_decode_xattr_name(argp, &removexattr->rmxa_name); 208423e50fe3SFrank van der Linden } 208523e50fe3SFrank van der Linden 208624bab491SAnna Schumaker static __be32 2087347e0ad9SBenny Halevy nfsd4_decode_noop(struct nfsd4_compoundargs *argp, void *p) 2088347e0ad9SBenny Halevy { 2089347e0ad9SBenny Halevy return nfs_ok; 2090347e0ad9SBenny Halevy } 2091347e0ad9SBenny Halevy 20923c375c6fSBenny Halevy static __be32 20933c375c6fSBenny Halevy nfsd4_decode_notsupp(struct nfsd4_compoundargs *argp, void *p) 20943c375c6fSBenny Halevy { 20951e685ec2SBenny Halevy return nfserr_notsupp; 20963c375c6fSBenny Halevy } 20973c375c6fSBenny Halevy 2098347e0ad9SBenny Halevy typedef __be32(*nfsd4_dec)(struct nfsd4_compoundargs *argp, void *); 2099347e0ad9SBenny Halevy 2100c1df609dSChuck Lever static const nfsd4_dec nfsd4_dec_ops[] = { 2101ad1060c8SJ. Bruce Fields [OP_ACCESS] = (nfsd4_dec)nfsd4_decode_access, 2102ad1060c8SJ. Bruce Fields [OP_CLOSE] = (nfsd4_dec)nfsd4_decode_close, 2103ad1060c8SJ. Bruce Fields [OP_COMMIT] = (nfsd4_dec)nfsd4_decode_commit, 2104ad1060c8SJ. Bruce Fields [OP_CREATE] = (nfsd4_dec)nfsd4_decode_create, 2105ad1060c8SJ. Bruce Fields [OP_DELEGPURGE] = (nfsd4_dec)nfsd4_decode_notsupp, 2106ad1060c8SJ. Bruce Fields [OP_DELEGRETURN] = (nfsd4_dec)nfsd4_decode_delegreturn, 2107ad1060c8SJ. Bruce Fields [OP_GETATTR] = (nfsd4_dec)nfsd4_decode_getattr, 2108ad1060c8SJ. Bruce Fields [OP_GETFH] = (nfsd4_dec)nfsd4_decode_noop, 2109ad1060c8SJ. Bruce Fields [OP_LINK] = (nfsd4_dec)nfsd4_decode_link, 2110ad1060c8SJ. Bruce Fields [OP_LOCK] = (nfsd4_dec)nfsd4_decode_lock, 2111ad1060c8SJ. Bruce Fields [OP_LOCKT] = (nfsd4_dec)nfsd4_decode_lockt, 2112ad1060c8SJ. Bruce Fields [OP_LOCKU] = (nfsd4_dec)nfsd4_decode_locku, 2113ad1060c8SJ. Bruce Fields [OP_LOOKUP] = (nfsd4_dec)nfsd4_decode_lookup, 2114ad1060c8SJ. Bruce Fields [OP_LOOKUPP] = (nfsd4_dec)nfsd4_decode_noop, 2115ad1060c8SJ. Bruce Fields [OP_NVERIFY] = (nfsd4_dec)nfsd4_decode_verify, 2116ad1060c8SJ. Bruce Fields [OP_OPEN] = (nfsd4_dec)nfsd4_decode_open, 2117ad1060c8SJ. Bruce Fields [OP_OPENATTR] = (nfsd4_dec)nfsd4_decode_notsupp, 2118ad1060c8SJ. Bruce Fields [OP_OPEN_CONFIRM] = (nfsd4_dec)nfsd4_decode_open_confirm, 2119ad1060c8SJ. Bruce Fields [OP_OPEN_DOWNGRADE] = (nfsd4_dec)nfsd4_decode_open_downgrade, 2120ad1060c8SJ. Bruce Fields [OP_PUTFH] = (nfsd4_dec)nfsd4_decode_putfh, 2121e1a90ebdSAnna Schumaker [OP_PUTPUBFH] = (nfsd4_dec)nfsd4_decode_putpubfh, 2122ad1060c8SJ. Bruce Fields [OP_PUTROOTFH] = (nfsd4_dec)nfsd4_decode_noop, 2123ad1060c8SJ. Bruce Fields [OP_READ] = (nfsd4_dec)nfsd4_decode_read, 2124ad1060c8SJ. Bruce Fields [OP_READDIR] = (nfsd4_dec)nfsd4_decode_readdir, 2125ad1060c8SJ. Bruce Fields [OP_READLINK] = (nfsd4_dec)nfsd4_decode_noop, 2126ad1060c8SJ. Bruce Fields [OP_REMOVE] = (nfsd4_dec)nfsd4_decode_remove, 2127ad1060c8SJ. Bruce Fields [OP_RENAME] = (nfsd4_dec)nfsd4_decode_rename, 2128ad1060c8SJ. Bruce Fields [OP_RENEW] = (nfsd4_dec)nfsd4_decode_renew, 2129ad1060c8SJ. Bruce Fields [OP_RESTOREFH] = (nfsd4_dec)nfsd4_decode_noop, 2130ad1060c8SJ. Bruce Fields [OP_SAVEFH] = (nfsd4_dec)nfsd4_decode_noop, 2131ad1060c8SJ. Bruce Fields [OP_SECINFO] = (nfsd4_dec)nfsd4_decode_secinfo, 2132ad1060c8SJ. Bruce Fields [OP_SETATTR] = (nfsd4_dec)nfsd4_decode_setattr, 2133ad1060c8SJ. Bruce Fields [OP_SETCLIENTID] = (nfsd4_dec)nfsd4_decode_setclientid, 2134ad1060c8SJ. Bruce Fields [OP_SETCLIENTID_CONFIRM] = (nfsd4_dec)nfsd4_decode_setclientid_confirm, 2135ad1060c8SJ. Bruce Fields [OP_VERIFY] = (nfsd4_dec)nfsd4_decode_verify, 2136ad1060c8SJ. Bruce Fields [OP_WRITE] = (nfsd4_dec)nfsd4_decode_write, 2137ad1060c8SJ. Bruce Fields [OP_RELEASE_LOCKOWNER] = (nfsd4_dec)nfsd4_decode_release_lockowner, 21382db134ebSAndy Adamson 21392db134ebSAndy Adamson /* new operations for NFSv4.1 */ 2140cb73a9f4SJ. Bruce Fields [OP_BACKCHANNEL_CTL] = (nfsd4_dec)nfsd4_decode_backchannel_ctl, 21411d1bc8f2SJ. Bruce Fields [OP_BIND_CONN_TO_SESSION]= (nfsd4_dec)nfsd4_decode_bind_conn_to_session, 21429064caaeSRandy Dunlap [OP_EXCHANGE_ID] = (nfsd4_dec)nfsd4_decode_exchange_id, 21439064caaeSRandy Dunlap [OP_CREATE_SESSION] = (nfsd4_dec)nfsd4_decode_create_session, 21449064caaeSRandy Dunlap [OP_DESTROY_SESSION] = (nfsd4_dec)nfsd4_decode_destroy_session, 2145e1ca12dfSBryan Schumaker [OP_FREE_STATEID] = (nfsd4_dec)nfsd4_decode_free_stateid, 21469064caaeSRandy Dunlap [OP_GET_DIR_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp, 21479cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS 21489cf514ccSChristoph Hellwig [OP_GETDEVICEINFO] = (nfsd4_dec)nfsd4_decode_getdeviceinfo, 21499cf514ccSChristoph Hellwig [OP_GETDEVICELIST] = (nfsd4_dec)nfsd4_decode_notsupp, 21509cf514ccSChristoph Hellwig [OP_LAYOUTCOMMIT] = (nfsd4_dec)nfsd4_decode_layoutcommit, 21519cf514ccSChristoph Hellwig [OP_LAYOUTGET] = (nfsd4_dec)nfsd4_decode_layoutget, 21529cf514ccSChristoph Hellwig [OP_LAYOUTRETURN] = (nfsd4_dec)nfsd4_decode_layoutreturn, 21539cf514ccSChristoph Hellwig #else 21549064caaeSRandy Dunlap [OP_GETDEVICEINFO] = (nfsd4_dec)nfsd4_decode_notsupp, 21559064caaeSRandy Dunlap [OP_GETDEVICELIST] = (nfsd4_dec)nfsd4_decode_notsupp, 21569064caaeSRandy Dunlap [OP_LAYOUTCOMMIT] = (nfsd4_dec)nfsd4_decode_notsupp, 21579064caaeSRandy Dunlap [OP_LAYOUTGET] = (nfsd4_dec)nfsd4_decode_notsupp, 21589064caaeSRandy Dunlap [OP_LAYOUTRETURN] = (nfsd4_dec)nfsd4_decode_notsupp, 21599cf514ccSChristoph Hellwig #endif 216004f4ad16SJ. Bruce Fields [OP_SECINFO_NO_NAME] = (nfsd4_dec)nfsd4_decode_secinfo_no_name, 21619064caaeSRandy Dunlap [OP_SEQUENCE] = (nfsd4_dec)nfsd4_decode_sequence, 21629064caaeSRandy Dunlap [OP_SET_SSV] = (nfsd4_dec)nfsd4_decode_notsupp, 216317456804SBryan Schumaker [OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_test_stateid, 21649064caaeSRandy Dunlap [OP_WANT_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp, 2165345c2842SMi Jinlong [OP_DESTROY_CLIENTID] = (nfsd4_dec)nfsd4_decode_destroy_clientid, 21664dc6ec00SJ. Bruce Fields [OP_RECLAIM_COMPLETE] = (nfsd4_dec)nfsd4_decode_reclaim_complete, 216787a15a80SAnna Schumaker 216887a15a80SAnna Schumaker /* new operations for NFSv4.2 */ 216995d871f0SAnna Schumaker [OP_ALLOCATE] = (nfsd4_dec)nfsd4_decode_fallocate, 217029ae7f9dSAnna Schumaker [OP_COPY] = (nfsd4_dec)nfsd4_decode_copy, 217151911868SOlga Kornievskaia [OP_COPY_NOTIFY] = (nfsd4_dec)nfsd4_decode_copy_notify, 2172b0cb9085SAnna Schumaker [OP_DEALLOCATE] = (nfsd4_dec)nfsd4_decode_fallocate, 217387a15a80SAnna Schumaker [OP_IO_ADVISE] = (nfsd4_dec)nfsd4_decode_notsupp, 217487a15a80SAnna Schumaker [OP_LAYOUTERROR] = (nfsd4_dec)nfsd4_decode_notsupp, 217587a15a80SAnna Schumaker [OP_LAYOUTSTATS] = (nfsd4_dec)nfsd4_decode_notsupp, 2176885e2bf3SOlga Kornievskaia [OP_OFFLOAD_CANCEL] = (nfsd4_dec)nfsd4_decode_offload_status, 21776308bc98SOlga Kornievskaia [OP_OFFLOAD_STATUS] = (nfsd4_dec)nfsd4_decode_offload_status, 2178528b8493SAnna Schumaker [OP_READ_PLUS] = (nfsd4_dec)nfsd4_decode_read, 217924bab491SAnna Schumaker [OP_SEEK] = (nfsd4_dec)nfsd4_decode_seek, 218087a15a80SAnna Schumaker [OP_WRITE_SAME] = (nfsd4_dec)nfsd4_decode_notsupp, 2181ffa0160aSChristoph Hellwig [OP_CLONE] = (nfsd4_dec)nfsd4_decode_clone, 218223e50fe3SFrank van der Linden /* RFC 8276 extended atributes operations */ 218323e50fe3SFrank van der Linden [OP_GETXATTR] = (nfsd4_dec)nfsd4_decode_getxattr, 218423e50fe3SFrank van der Linden [OP_SETXATTR] = (nfsd4_dec)nfsd4_decode_setxattr, 218523e50fe3SFrank van der Linden [OP_LISTXATTRS] = (nfsd4_dec)nfsd4_decode_listxattrs, 218623e50fe3SFrank van der Linden [OP_REMOVEXATTR] = (nfsd4_dec)nfsd4_decode_removexattr, 21872db134ebSAndy Adamson }; 21882db134ebSAndy Adamson 2189e1a90ebdSAnna Schumaker static inline bool 2190e1a90ebdSAnna Schumaker nfsd4_opnum_in_range(struct nfsd4_compoundargs *argp, struct nfsd4_op *op) 2191e1a90ebdSAnna Schumaker { 21928217d146SAnna Schumaker if (op->opnum < FIRST_NFS4_OP) 2193e1a90ebdSAnna Schumaker return false; 21948217d146SAnna Schumaker else if (argp->minorversion == 0 && op->opnum > LAST_NFS40_OP) 2195e1a90ebdSAnna Schumaker return false; 21968217d146SAnna Schumaker else if (argp->minorversion == 1 && op->opnum > LAST_NFS41_OP) 21978217d146SAnna Schumaker return false; 21988217d146SAnna Schumaker else if (argp->minorversion == 2 && op->opnum > LAST_NFS42_OP) 2199e1a90ebdSAnna Schumaker return false; 2200e1a90ebdSAnna Schumaker return true; 2201e1a90ebdSAnna Schumaker } 2202f2feb96bSBenny Halevy 2203347e0ad9SBenny Halevy static __be32 22041da177e4SLinus Torvalds nfsd4_decode_compound(struct nfsd4_compoundargs *argp) 22051da177e4SLinus Torvalds { 22061da177e4SLinus Torvalds DECODE_HEAD; 22071da177e4SLinus Torvalds struct nfsd4_op *op; 22081091006cSJ. Bruce Fields bool cachethis = false; 2209a5cddc88SJ. Bruce Fields int auth_slack= argp->rqstp->rq_auth_slack; 2210a5cddc88SJ. Bruce Fields int max_reply = auth_slack + 8; /* opcnt, status */ 2211b0e35fdaSJ. Bruce Fields int readcount = 0; 2212b0e35fdaSJ. Bruce Fields int readbytes = 0; 22131da177e4SLinus Torvalds int i; 22141da177e4SLinus Torvalds 22151da177e4SLinus Torvalds READ_BUF(4); 221606553991SJ. Bruce Fields argp->taglen = be32_to_cpup(p++); 22174aed9c46SJ. Bruce Fields READ_BUF(argp->taglen); 22181da177e4SLinus Torvalds SAVEMEM(argp->tag, argp->taglen); 22194aed9c46SJ. Bruce Fields READ_BUF(8); 222006553991SJ. Bruce Fields argp->minorversion = be32_to_cpup(p++); 222106553991SJ. Bruce Fields argp->opcnt = be32_to_cpup(p++); 22224f0cefbfSJ. Bruce Fields max_reply += 4 + (XDR_QUADLEN(argp->taglen) << 2); 22231da177e4SLinus Torvalds 22241da177e4SLinus Torvalds if (argp->taglen > NFSD4_MAX_TAGLEN) 22251da177e4SLinus Torvalds goto xdr_error; 22260078117cSJ. Bruce Fields /* 22270078117cSJ. Bruce Fields * NFS4ERR_RESOURCE is a more helpful error than GARBAGE_ARGS 22280078117cSJ. Bruce Fields * here, so we return success at the xdr level so that 22290078117cSJ. Bruce Fields * nfsd4_proc can handle this is an NFS-level error. 22300078117cSJ. Bruce Fields */ 22310078117cSJ. Bruce Fields if (argp->opcnt > NFSD_MAX_OPS_PER_COMPOUND) 22320078117cSJ. Bruce Fields return 0; 22331da177e4SLinus Torvalds 2234e8c96f8cSTobias Klauser if (argp->opcnt > ARRAY_SIZE(argp->iops)) { 22355d6031caSJ. Bruce Fields argp->ops = kzalloc(argp->opcnt * sizeof(*argp->ops), GFP_KERNEL); 22361da177e4SLinus Torvalds if (!argp->ops) { 22371da177e4SLinus Torvalds argp->ops = argp->iops; 2238817cb9d4SChuck Lever dprintk("nfsd: couldn't allocate room for COMPOUND\n"); 22391da177e4SLinus Torvalds goto xdr_error; 22401da177e4SLinus Torvalds } 22411da177e4SLinus Torvalds } 22421da177e4SLinus Torvalds 2243e1a90ebdSAnna Schumaker if (argp->minorversion > NFSD_SUPPORTED_MINOR_VERSION) 224430cff1ffSBenny Halevy argp->opcnt = 0; 224530cff1ffSBenny Halevy 22461da177e4SLinus Torvalds for (i = 0; i < argp->opcnt; i++) { 22471da177e4SLinus Torvalds op = &argp->ops[i]; 22481da177e4SLinus Torvalds op->replay = NULL; 22491da177e4SLinus Torvalds 22508a61b18cSJ. Bruce Fields READ_BUF(4); 225106553991SJ. Bruce Fields op->opnum = be32_to_cpup(p++); 22521da177e4SLinus Torvalds 2253*08281341SChuck Lever if (nfsd4_opnum_in_range(argp, op)) { 2254e1a90ebdSAnna Schumaker op->status = nfsd4_dec_ops[op->opnum](argp, &op->u); 2255*08281341SChuck Lever if (op->status != nfs_ok) 2256*08281341SChuck Lever trace_nfsd_compound_decode_err(argp->rqstp, 2257*08281341SChuck Lever argp->opcnt, i, 2258*08281341SChuck Lever op->opnum, 2259*08281341SChuck Lever op->status); 2260*08281341SChuck Lever } else { 22611da177e4SLinus Torvalds op->opnum = OP_ILLEGAL; 22621da177e4SLinus Torvalds op->status = nfserr_op_illegal; 22631da177e4SLinus Torvalds } 2264f4f9ef4aSJ. Bruce Fields op->opdesc = OPDESC(op); 22651091006cSJ. Bruce Fields /* 22661091006cSJ. Bruce Fields * We'll try to cache the result in the DRC if any one 22671091006cSJ. Bruce Fields * op in the compound wants to be cached: 22681091006cSJ. Bruce Fields */ 22691091006cSJ. Bruce Fields cachethis |= nfsd4_cache_this_op(op); 22706ff40decSJ. Bruce Fields 2271528b8493SAnna Schumaker if (op->opnum == OP_READ || op->opnum == OP_READ_PLUS) { 2272b0e35fdaSJ. Bruce Fields readcount++; 2273b0e35fdaSJ. Bruce Fields readbytes += nfsd4_max_reply(argp->rqstp, op); 2274b0e35fdaSJ. Bruce Fields } else 22754f0cefbfSJ. Bruce Fields max_reply += nfsd4_max_reply(argp->rqstp, op); 2276f7b43d0cSJ. Bruce Fields /* 22777323f0d2SKinglong Mee * OP_LOCK and OP_LOCKT may return a conflicting lock. 22787323f0d2SKinglong Mee * (Special case because it will just skip encoding this 22797323f0d2SKinglong Mee * if it runs out of xdr buffer space, and it is the only 22807323f0d2SKinglong Mee * operation that behaves this way.) 2281f7b43d0cSJ. Bruce Fields */ 22827323f0d2SKinglong Mee if (op->opnum == OP_LOCK || op->opnum == OP_LOCKT) 2283f7b43d0cSJ. Bruce Fields max_reply += NFS4_OPAQUE_LIMIT; 2284e372ba60SJ. Bruce Fields 2285e372ba60SJ. Bruce Fields if (op->status) { 2286e372ba60SJ. Bruce Fields argp->opcnt = i+1; 2287e372ba60SJ. Bruce Fields break; 2288e372ba60SJ. Bruce Fields } 22891da177e4SLinus Torvalds } 22901091006cSJ. Bruce Fields /* Sessions make the DRC unnecessary: */ 22911091006cSJ. Bruce Fields if (argp->minorversion) 22921091006cSJ. Bruce Fields cachethis = false; 2293b0e35fdaSJ. Bruce Fields svc_reserve(argp->rqstp, max_reply + readbytes); 22941091006cSJ. Bruce Fields argp->rqstp->rq_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE; 22951da177e4SLinus Torvalds 2296a5cddc88SJ. Bruce Fields if (readcount > 1 || max_reply > PAGE_SIZE - auth_slack) 2297779fb0f3SJeff Layton clear_bit(RQ_SPLICE_OK, &argp->rqstp->rq_flags); 2298b0e35fdaSJ. Bruce Fields 22991da177e4SLinus Torvalds DECODE_TAIL; 23001da177e4SLinus Torvalds } 23011da177e4SLinus Torvalds 2302b8800921SNeilBrown static __be32 *encode_change(__be32 *p, struct kstat *stat, struct inode *inode, 2303b8800921SNeilBrown struct svc_export *exp) 2304c654b8a9SJ. Bruce Fields { 2305b8800921SNeilBrown if (exp->ex_flags & NFSEXP_V4ROOT) { 2306b8800921SNeilBrown *p++ = cpu_to_be32(convert_to_wallclock(exp->cd->flush_time)); 2307b8800921SNeilBrown *p++ = 0; 2308b8800921SNeilBrown } else if (IS_I_VERSION(inode)) { 230939ca1bf6SAmir Goldstein p = xdr_encode_hyper(p, nfsd4_change_attribute(stat, inode)); 2310c654b8a9SJ. Bruce Fields } else { 2311d05d5744SJ. Bruce Fields *p++ = cpu_to_be32(stat->ctime.tv_sec); 2312d05d5744SJ. Bruce Fields *p++ = cpu_to_be32(stat->ctime.tv_nsec); 2313c654b8a9SJ. Bruce Fields } 2314d05d5744SJ. Bruce Fields return p; 2315c654b8a9SJ. Bruce Fields } 2316c654b8a9SJ. Bruce Fields 231716945141SJ. Bruce Fields /* 231816945141SJ. Bruce Fields * ctime (in NFSv4, time_metadata) is not writeable, and the client 231916945141SJ. Bruce Fields * doesn't really care what resolution could theoretically be stored by 232016945141SJ. Bruce Fields * the filesystem. 232116945141SJ. Bruce Fields * 232216945141SJ. Bruce Fields * The client cares how close together changes can be while still 232316945141SJ. Bruce Fields * guaranteeing ctime changes. For most filesystems (which have 232416945141SJ. Bruce Fields * timestamps with nanosecond fields) that is limited by the resolution 232516945141SJ. Bruce Fields * of the time returned from current_time() (which I'm assuming to be 232616945141SJ. Bruce Fields * 1/HZ). 232716945141SJ. Bruce Fields */ 232816945141SJ. Bruce Fields static __be32 *encode_time_delta(__be32 *p, struct inode *inode) 232916945141SJ. Bruce Fields { 2330e4598e38SArnd Bergmann struct timespec64 ts; 233116945141SJ. Bruce Fields u32 ns; 233216945141SJ. Bruce Fields 233316945141SJ. Bruce Fields ns = max_t(u32, NSEC_PER_SEC/HZ, inode->i_sb->s_time_gran); 2334e4598e38SArnd Bergmann ts = ns_to_timespec64(ns); 233516945141SJ. Bruce Fields 233616945141SJ. Bruce Fields p = xdr_encode_hyper(p, ts.tv_sec); 233716945141SJ. Bruce Fields *p++ = cpu_to_be32(ts.tv_nsec); 233816945141SJ. Bruce Fields 233916945141SJ. Bruce Fields return p; 234016945141SJ. Bruce Fields } 234116945141SJ. Bruce Fields 2342d05d5744SJ. Bruce Fields static __be32 *encode_cinfo(__be32 *p, struct nfsd4_change_info *c) 2343c654b8a9SJ. Bruce Fields { 2344d05d5744SJ. Bruce Fields *p++ = cpu_to_be32(c->atomic); 2345c654b8a9SJ. Bruce Fields if (c->change_supported) { 2346d05d5744SJ. Bruce Fields p = xdr_encode_hyper(p, c->before_change); 2347d05d5744SJ. Bruce Fields p = xdr_encode_hyper(p, c->after_change); 2348c654b8a9SJ. Bruce Fields } else { 2349d05d5744SJ. Bruce Fields *p++ = cpu_to_be32(c->before_ctime_sec); 2350d05d5744SJ. Bruce Fields *p++ = cpu_to_be32(c->before_ctime_nsec); 2351d05d5744SJ. Bruce Fields *p++ = cpu_to_be32(c->after_ctime_sec); 2352d05d5744SJ. Bruce Fields *p++ = cpu_to_be32(c->after_ctime_nsec); 2353c654b8a9SJ. Bruce Fields } 2354d05d5744SJ. Bruce Fields return p; 2355c654b8a9SJ. Bruce Fields } 23561da177e4SLinus Torvalds 235781c3f413SJ.Bruce Fields /* Encode as an array of strings the string given with components 2358e7a0444aSWeston Andros Adamson * separated @sep, escaped with esc_enter and esc_exit. 235981c3f413SJ.Bruce Fields */ 2360ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_components_esc(struct xdr_stream *xdr, char sep, 2361ddd1ea56SJ. Bruce Fields char *components, char esc_enter, 2362ddd1ea56SJ. Bruce Fields char esc_exit) 236381c3f413SJ.Bruce Fields { 2364ddd1ea56SJ. Bruce Fields __be32 *p; 2365082d4bd7SJ. Bruce Fields __be32 pathlen; 2366082d4bd7SJ. Bruce Fields int pathlen_offset; 236781c3f413SJ.Bruce Fields int strlen, count=0; 2368e7a0444aSWeston Andros Adamson char *str, *end, *next; 236981c3f413SJ.Bruce Fields 237081c3f413SJ.Bruce Fields dprintk("nfsd4_encode_components(%s)\n", components); 2371082d4bd7SJ. Bruce Fields 2372082d4bd7SJ. Bruce Fields pathlen_offset = xdr->buf->len; 2373ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2374ddd1ea56SJ. Bruce Fields if (!p) 237581c3f413SJ.Bruce Fields return nfserr_resource; 2376082d4bd7SJ. Bruce Fields p++; /* We will fill this in with @count later */ 2377082d4bd7SJ. Bruce Fields 237881c3f413SJ.Bruce Fields end = str = components; 237981c3f413SJ.Bruce Fields while (*end) { 2380e7a0444aSWeston Andros Adamson bool found_esc = false; 2381e7a0444aSWeston Andros Adamson 2382e7a0444aSWeston Andros Adamson /* try to parse as esc_start, ..., esc_end, sep */ 2383e7a0444aSWeston Andros Adamson if (*str == esc_enter) { 2384e7a0444aSWeston Andros Adamson for (; *end && (*end != esc_exit); end++) 2385e7a0444aSWeston Andros Adamson /* find esc_exit or end of string */; 2386e7a0444aSWeston Andros Adamson next = end + 1; 2387e7a0444aSWeston Andros Adamson if (*end && (!*next || *next == sep)) { 2388e7a0444aSWeston Andros Adamson str++; 2389e7a0444aSWeston Andros Adamson found_esc = true; 2390e7a0444aSWeston Andros Adamson } 2391e7a0444aSWeston Andros Adamson } 2392e7a0444aSWeston Andros Adamson 2393e7a0444aSWeston Andros Adamson if (!found_esc) 239481c3f413SJ.Bruce Fields for (; *end && (*end != sep); end++) 2395e7a0444aSWeston Andros Adamson /* find sep or end of string */; 2396e7a0444aSWeston Andros Adamson 239781c3f413SJ.Bruce Fields strlen = end - str; 239881c3f413SJ.Bruce Fields if (strlen) { 2399ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, strlen + 4); 2400ddd1ea56SJ. Bruce Fields if (!p) 240181c3f413SJ.Bruce Fields return nfserr_resource; 24020c0c267bSJ. Bruce Fields p = xdr_encode_opaque(p, str, strlen); 240381c3f413SJ.Bruce Fields count++; 240481c3f413SJ.Bruce Fields } 240581c3f413SJ.Bruce Fields else 240681c3f413SJ.Bruce Fields end++; 24075a64e569SBenjamin Coddington if (found_esc) 24085a64e569SBenjamin Coddington end = next; 24095a64e569SBenjamin Coddington 241081c3f413SJ.Bruce Fields str = end; 241181c3f413SJ.Bruce Fields } 2412bf7491f1SBenjamin Coddington pathlen = htonl(count); 2413082d4bd7SJ. Bruce Fields write_bytes_to_xdr_buf(xdr->buf, pathlen_offset, &pathlen, 4); 241481c3f413SJ.Bruce Fields return 0; 241581c3f413SJ.Bruce Fields } 241681c3f413SJ.Bruce Fields 2417e7a0444aSWeston Andros Adamson /* Encode as an array of strings the string given with components 2418e7a0444aSWeston Andros Adamson * separated @sep. 2419e7a0444aSWeston Andros Adamson */ 2420ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_components(struct xdr_stream *xdr, char sep, 2421ddd1ea56SJ. Bruce Fields char *components) 2422e7a0444aSWeston Andros Adamson { 2423ddd1ea56SJ. Bruce Fields return nfsd4_encode_components_esc(xdr, sep, components, 0, 0); 2424e7a0444aSWeston Andros Adamson } 2425e7a0444aSWeston Andros Adamson 242681c3f413SJ.Bruce Fields /* 242781c3f413SJ.Bruce Fields * encode a location element of a fs_locations structure 242881c3f413SJ.Bruce Fields */ 2429ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_fs_location4(struct xdr_stream *xdr, 2430ddd1ea56SJ. Bruce Fields struct nfsd4_fs_location *location) 243181c3f413SJ.Bruce Fields { 2432b37ad28bSAl Viro __be32 status; 243381c3f413SJ.Bruce Fields 2434ddd1ea56SJ. Bruce Fields status = nfsd4_encode_components_esc(xdr, ':', location->hosts, 2435e7a0444aSWeston Andros Adamson '[', ']'); 243681c3f413SJ.Bruce Fields if (status) 243781c3f413SJ.Bruce Fields return status; 2438ddd1ea56SJ. Bruce Fields status = nfsd4_encode_components(xdr, '/', location->path); 243981c3f413SJ.Bruce Fields if (status) 244081c3f413SJ.Bruce Fields return status; 244181c3f413SJ.Bruce Fields return 0; 244281c3f413SJ.Bruce Fields } 244381c3f413SJ.Bruce Fields 244481c3f413SJ.Bruce Fields /* 2445ed748aacSTrond Myklebust * Encode a path in RFC3530 'pathname4' format 244681c3f413SJ.Bruce Fields */ 2447ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_path(struct xdr_stream *xdr, 2448ddd1ea56SJ. Bruce Fields const struct path *root, 2449ddd1ea56SJ. Bruce Fields const struct path *path) 245081c3f413SJ.Bruce Fields { 2451301f0268SAl Viro struct path cur = *path; 2452ddd1ea56SJ. Bruce Fields __be32 *p; 2453ed748aacSTrond Myklebust struct dentry **components = NULL; 2454ed748aacSTrond Myklebust unsigned int ncomponents = 0; 2455ed748aacSTrond Myklebust __be32 err = nfserr_jukebox; 245681c3f413SJ.Bruce Fields 2457ed748aacSTrond Myklebust dprintk("nfsd4_encode_components("); 245881c3f413SJ.Bruce Fields 2459ed748aacSTrond Myklebust path_get(&cur); 2460ed748aacSTrond Myklebust /* First walk the path up to the nfsd root, and store the 2461ed748aacSTrond Myklebust * dentries/path components in an array. 2462ed748aacSTrond Myklebust */ 2463ed748aacSTrond Myklebust for (;;) { 2464b77a4b2eSKinglong Mee if (path_equal(&cur, root)) 2465ed748aacSTrond Myklebust break; 2466ed748aacSTrond Myklebust if (cur.dentry == cur.mnt->mnt_root) { 2467ed748aacSTrond Myklebust if (follow_up(&cur)) 2468ed748aacSTrond Myklebust continue; 2469ed748aacSTrond Myklebust goto out_free; 247081c3f413SJ.Bruce Fields } 2471ed748aacSTrond Myklebust if ((ncomponents & 15) == 0) { 2472ed748aacSTrond Myklebust struct dentry **new; 2473ed748aacSTrond Myklebust new = krealloc(components, 2474ed748aacSTrond Myklebust sizeof(*new) * (ncomponents + 16), 2475ed748aacSTrond Myklebust GFP_KERNEL); 2476ed748aacSTrond Myklebust if (!new) 2477ed748aacSTrond Myklebust goto out_free; 2478ed748aacSTrond Myklebust components = new; 2479ed748aacSTrond Myklebust } 2480ed748aacSTrond Myklebust components[ncomponents++] = cur.dentry; 2481ed748aacSTrond Myklebust cur.dentry = dget_parent(cur.dentry); 2482ed748aacSTrond Myklebust } 2483ddd1ea56SJ. Bruce Fields err = nfserr_resource; 2484ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2485ddd1ea56SJ. Bruce Fields if (!p) 2486ed748aacSTrond Myklebust goto out_free; 2487c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(ncomponents); 2488ed748aacSTrond Myklebust 2489ed748aacSTrond Myklebust while (ncomponents) { 2490ed748aacSTrond Myklebust struct dentry *dentry = components[ncomponents - 1]; 2491301f0268SAl Viro unsigned int len; 2492ed748aacSTrond Myklebust 2493301f0268SAl Viro spin_lock(&dentry->d_lock); 2494301f0268SAl Viro len = dentry->d_name.len; 2495ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, len + 4); 2496ddd1ea56SJ. Bruce Fields if (!p) { 2497301f0268SAl Viro spin_unlock(&dentry->d_lock); 2498ed748aacSTrond Myklebust goto out_free; 2499301f0268SAl Viro } 25000c0c267bSJ. Bruce Fields p = xdr_encode_opaque(p, dentry->d_name.name, len); 2501a455589fSAl Viro dprintk("/%pd", dentry); 2502301f0268SAl Viro spin_unlock(&dentry->d_lock); 2503ed748aacSTrond Myklebust dput(dentry); 2504ed748aacSTrond Myklebust ncomponents--; 2505ed748aacSTrond Myklebust } 2506ed748aacSTrond Myklebust 2507ed748aacSTrond Myklebust err = 0; 2508ed748aacSTrond Myklebust out_free: 2509ed748aacSTrond Myklebust dprintk(")\n"); 2510ed748aacSTrond Myklebust while (ncomponents) 2511ed748aacSTrond Myklebust dput(components[--ncomponents]); 2512ed748aacSTrond Myklebust kfree(components); 2513ed748aacSTrond Myklebust path_put(&cur); 2514ed748aacSTrond Myklebust return err; 2515ed748aacSTrond Myklebust } 2516ed748aacSTrond Myklebust 2517ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_fsloc_fsroot(struct xdr_stream *xdr, 2518ddd1ea56SJ. Bruce Fields struct svc_rqst *rqstp, const struct path *path) 2519ed748aacSTrond Myklebust { 2520ed748aacSTrond Myklebust struct svc_export *exp_ps; 2521ed748aacSTrond Myklebust __be32 res; 2522ed748aacSTrond Myklebust 2523ed748aacSTrond Myklebust exp_ps = rqst_find_fsidzero_export(rqstp); 2524ed748aacSTrond Myklebust if (IS_ERR(exp_ps)) 2525ed748aacSTrond Myklebust return nfserrno(PTR_ERR(exp_ps)); 2526ddd1ea56SJ. Bruce Fields res = nfsd4_encode_path(xdr, &exp_ps->ex_path, path); 2527ed748aacSTrond Myklebust exp_put(exp_ps); 2528ed748aacSTrond Myklebust return res; 252981c3f413SJ.Bruce Fields } 253081c3f413SJ.Bruce Fields 253181c3f413SJ.Bruce Fields /* 253281c3f413SJ.Bruce Fields * encode a fs_locations structure 253381c3f413SJ.Bruce Fields */ 2534ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_fs_locations(struct xdr_stream *xdr, 2535ddd1ea56SJ. Bruce Fields struct svc_rqst *rqstp, struct svc_export *exp) 253681c3f413SJ.Bruce Fields { 2537b37ad28bSAl Viro __be32 status; 2538cc45f017SAl Viro int i; 2539ddd1ea56SJ. Bruce Fields __be32 *p; 254081c3f413SJ.Bruce Fields struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs; 254181c3f413SJ.Bruce Fields 2542ddd1ea56SJ. Bruce Fields status = nfsd4_encode_fsloc_fsroot(xdr, rqstp, &exp->ex_path); 254381c3f413SJ.Bruce Fields if (status) 254481c3f413SJ.Bruce Fields return status; 2545ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2546ddd1ea56SJ. Bruce Fields if (!p) 254781c3f413SJ.Bruce Fields return nfserr_resource; 2548c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(fslocs->locations_count); 254981c3f413SJ.Bruce Fields for (i=0; i<fslocs->locations_count; i++) { 2550ddd1ea56SJ. Bruce Fields status = nfsd4_encode_fs_location4(xdr, &fslocs->locations[i]); 255181c3f413SJ.Bruce Fields if (status) 255281c3f413SJ.Bruce Fields return status; 255381c3f413SJ.Bruce Fields } 255481c3f413SJ.Bruce Fields return 0; 255581c3f413SJ.Bruce Fields } 25561da177e4SLinus Torvalds 25573d2544b1SJ. Bruce Fields static u32 nfs4_file_type(umode_t mode) 25583d2544b1SJ. Bruce Fields { 25593d2544b1SJ. Bruce Fields switch (mode & S_IFMT) { 25603d2544b1SJ. Bruce Fields case S_IFIFO: return NF4FIFO; 25613d2544b1SJ. Bruce Fields case S_IFCHR: return NF4CHR; 25623d2544b1SJ. Bruce Fields case S_IFDIR: return NF4DIR; 25633d2544b1SJ. Bruce Fields case S_IFBLK: return NF4BLK; 25643d2544b1SJ. Bruce Fields case S_IFLNK: return NF4LNK; 25653d2544b1SJ. Bruce Fields case S_IFREG: return NF4REG; 25663d2544b1SJ. Bruce Fields case S_IFSOCK: return NF4SOCK; 25673d2544b1SJ. Bruce Fields default: return NF4BAD; 256825fef48bSTom Rix } 25693d2544b1SJ. Bruce Fields } 25701da177e4SLinus Torvalds 2571b37ad28bSAl Viro static inline __be32 2572ddd1ea56SJ. Bruce Fields nfsd4_encode_aclname(struct xdr_stream *xdr, struct svc_rqst *rqstp, 2573ddd1ea56SJ. Bruce Fields struct nfs4_ace *ace) 25741da177e4SLinus Torvalds { 25753554116dSJ. Bruce Fields if (ace->whotype != NFS4_ACL_WHO_NAMED) 2576ddd1ea56SJ. Bruce Fields return nfs4_acl_write_who(xdr, ace->whotype); 25773554116dSJ. Bruce Fields else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP) 2578ddd1ea56SJ. Bruce Fields return nfsd4_encode_group(xdr, rqstp, ace->who_gid); 2579ab8e4aeeSEric W. Biederman else 2580ddd1ea56SJ. Bruce Fields return nfsd4_encode_user(xdr, rqstp, ace->who_uid); 25811da177e4SLinus Torvalds } 25821da177e4SLinus Torvalds 25836896f15aSKinglong Mee static inline __be32 25848a4c3926SJeff Layton nfsd4_encode_layout_types(struct xdr_stream *xdr, u32 layout_types) 25856896f15aSKinglong Mee { 25866896f15aSKinglong Mee __be32 *p; 25878a4c3926SJeff Layton unsigned long i = hweight_long(layout_types); 25886896f15aSKinglong Mee 25898a4c3926SJeff Layton p = xdr_reserve_space(xdr, 4 + 4 * i); 25906896f15aSKinglong Mee if (!p) 25916896f15aSKinglong Mee return nfserr_resource; 25928a4c3926SJeff Layton 25938a4c3926SJeff Layton *p++ = cpu_to_be32(i); 25948a4c3926SJeff Layton 25958a4c3926SJeff Layton for (i = LAYOUT_NFSV4_1_FILES; i < LAYOUT_TYPE_MAX; ++i) 25968a4c3926SJeff Layton if (layout_types & (1 << i)) 25978a4c3926SJeff Layton *p++ = cpu_to_be32(i); 25986896f15aSKinglong Mee 25996896f15aSKinglong Mee return 0; 26006896f15aSKinglong Mee } 26016896f15aSKinglong Mee 260242ca0993SJ.Bruce Fields #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \ 260342ca0993SJ.Bruce Fields FATTR4_WORD0_RDATTR_ERROR) 260442ca0993SJ.Bruce Fields #define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID 2605c2227a39SKinglong Mee #define WORD2_ABSENT_FS_ATTRS 0 260642ca0993SJ.Bruce Fields 260718032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL 260818032ca0SDavid Quigley static inline __be32 2609ddd1ea56SJ. Bruce Fields nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp, 2610ddd1ea56SJ. Bruce Fields void *context, int len) 261118032ca0SDavid Quigley { 2612ddd1ea56SJ. Bruce Fields __be32 *p; 261318032ca0SDavid Quigley 2614ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, len + 4 + 4 + 4); 2615ddd1ea56SJ. Bruce Fields if (!p) 261618032ca0SDavid Quigley return nfserr_resource; 261718032ca0SDavid Quigley 261818032ca0SDavid Quigley /* 261918032ca0SDavid Quigley * For now we use a 0 here to indicate the null translation; in 262018032ca0SDavid Quigley * the future we may place a call to translation code here. 262118032ca0SDavid Quigley */ 2622c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); /* lfs */ 2623c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); /* pi */ 262418032ca0SDavid Quigley p = xdr_encode_opaque(p, context, len); 262518032ca0SDavid Quigley return 0; 262618032ca0SDavid Quigley } 262718032ca0SDavid Quigley #else 262818032ca0SDavid Quigley static inline __be32 2629ddd1ea56SJ. Bruce Fields nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp, 2630ddd1ea56SJ. Bruce Fields void *context, int len) 263118032ca0SDavid Quigley { return 0; } 263218032ca0SDavid Quigley #endif 263318032ca0SDavid Quigley 2634c2227a39SKinglong Mee static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *bmval2, u32 *rdattr_err) 263542ca0993SJ.Bruce Fields { 263642ca0993SJ.Bruce Fields /* As per referral draft: */ 263742ca0993SJ.Bruce Fields if (*bmval0 & ~WORD0_ABSENT_FS_ATTRS || 263842ca0993SJ.Bruce Fields *bmval1 & ~WORD1_ABSENT_FS_ATTRS) { 263942ca0993SJ.Bruce Fields if (*bmval0 & FATTR4_WORD0_RDATTR_ERROR || 264042ca0993SJ.Bruce Fields *bmval0 & FATTR4_WORD0_FS_LOCATIONS) 264142ca0993SJ.Bruce Fields *rdattr_err = NFSERR_MOVED; 264242ca0993SJ.Bruce Fields else 264342ca0993SJ.Bruce Fields return nfserr_moved; 264442ca0993SJ.Bruce Fields } 264542ca0993SJ.Bruce Fields *bmval0 &= WORD0_ABSENT_FS_ATTRS; 264642ca0993SJ.Bruce Fields *bmval1 &= WORD1_ABSENT_FS_ATTRS; 2647c2227a39SKinglong Mee *bmval2 &= WORD2_ABSENT_FS_ATTRS; 264842ca0993SJ.Bruce Fields return 0; 264942ca0993SJ.Bruce Fields } 26501da177e4SLinus Torvalds 2651ae7095a7SJ. Bruce Fields 2652ae7095a7SJ. Bruce Fields static int get_parent_attributes(struct svc_export *exp, struct kstat *stat) 2653ae7095a7SJ. Bruce Fields { 2654ae7095a7SJ. Bruce Fields struct path path = exp->ex_path; 2655ae7095a7SJ. Bruce Fields int err; 2656ae7095a7SJ. Bruce Fields 2657ae7095a7SJ. Bruce Fields path_get(&path); 2658ae7095a7SJ. Bruce Fields while (follow_up(&path)) { 2659ae7095a7SJ. Bruce Fields if (path.dentry != path.mnt->mnt_root) 2660ae7095a7SJ. Bruce Fields break; 2661ae7095a7SJ. Bruce Fields } 2662a528d35eSDavid Howells err = vfs_getattr(&path, stat, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT); 2663ae7095a7SJ. Bruce Fields path_put(&path); 2664ae7095a7SJ. Bruce Fields return err; 2665ae7095a7SJ. Bruce Fields } 2666ae7095a7SJ. Bruce Fields 266775976de6SKinglong Mee static __be32 266875976de6SKinglong Mee nfsd4_encode_bitmap(struct xdr_stream *xdr, u32 bmval0, u32 bmval1, u32 bmval2) 266975976de6SKinglong Mee { 267075976de6SKinglong Mee __be32 *p; 267175976de6SKinglong Mee 267275976de6SKinglong Mee if (bmval2) { 267375976de6SKinglong Mee p = xdr_reserve_space(xdr, 16); 267475976de6SKinglong Mee if (!p) 267575976de6SKinglong Mee goto out_resource; 267675976de6SKinglong Mee *p++ = cpu_to_be32(3); 267775976de6SKinglong Mee *p++ = cpu_to_be32(bmval0); 267875976de6SKinglong Mee *p++ = cpu_to_be32(bmval1); 267975976de6SKinglong Mee *p++ = cpu_to_be32(bmval2); 268075976de6SKinglong Mee } else if (bmval1) { 268175976de6SKinglong Mee p = xdr_reserve_space(xdr, 12); 268275976de6SKinglong Mee if (!p) 268375976de6SKinglong Mee goto out_resource; 268475976de6SKinglong Mee *p++ = cpu_to_be32(2); 268575976de6SKinglong Mee *p++ = cpu_to_be32(bmval0); 268675976de6SKinglong Mee *p++ = cpu_to_be32(bmval1); 268775976de6SKinglong Mee } else { 268875976de6SKinglong Mee p = xdr_reserve_space(xdr, 8); 268975976de6SKinglong Mee if (!p) 269075976de6SKinglong Mee goto out_resource; 269175976de6SKinglong Mee *p++ = cpu_to_be32(1); 269275976de6SKinglong Mee *p++ = cpu_to_be32(bmval0); 269375976de6SKinglong Mee } 269475976de6SKinglong Mee 269575976de6SKinglong Mee return 0; 269675976de6SKinglong Mee out_resource: 269775976de6SKinglong Mee return nfserr_resource; 269875976de6SKinglong Mee } 269975976de6SKinglong Mee 27001da177e4SLinus Torvalds /* 27011da177e4SLinus Torvalds * Note: @fhp can be NULL; in this case, we might have to compose the filehandle 27021da177e4SLinus Torvalds * ourselves. 27031da177e4SLinus Torvalds */ 2704da2ebce6SJeff Layton static __be32 2705d5184658SJ. Bruce Fields nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp, 2706d5184658SJ. Bruce Fields struct svc_export *exp, 2707d5184658SJ. Bruce Fields struct dentry *dentry, u32 *bmval, 2708406a7ea9SFrank Filz struct svc_rqst *rqstp, int ignore_crossmnt) 27091da177e4SLinus Torvalds { 27101da177e4SLinus Torvalds u32 bmval0 = bmval[0]; 27111da177e4SLinus Torvalds u32 bmval1 = bmval[1]; 27127e705706SAndy Adamson u32 bmval2 = bmval[2]; 27131da177e4SLinus Torvalds struct kstat stat; 2714d50e6136SJ. Bruce Fields struct svc_fh *tempfh = NULL; 27151da177e4SLinus Torvalds struct kstatfs statfs; 2716ddd1ea56SJ. Bruce Fields __be32 *p; 27171fcea5b2SJ. Bruce Fields int starting_len = xdr->buf->len; 2718082d4bd7SJ. Bruce Fields int attrlen_offset; 2719082d4bd7SJ. Bruce Fields __be32 attrlen; 27201da177e4SLinus Torvalds u32 dummy; 27211da177e4SLinus Torvalds u64 dummy64; 272242ca0993SJ.Bruce Fields u32 rdattr_err = 0; 2723b37ad28bSAl Viro __be32 status; 2724b8dd7b9aSAl Viro int err; 27251da177e4SLinus Torvalds struct nfs4_acl *acl = NULL; 27260ab88ca4SArnd Bergmann #ifdef CONFIG_NFSD_V4_SECURITY_LABEL 272718032ca0SDavid Quigley void *context = NULL; 272818032ca0SDavid Quigley int contextlen; 27290ab88ca4SArnd Bergmann #endif 273018032ca0SDavid Quigley bool contextsupport = false; 27317e705706SAndy Adamson struct nfsd4_compoundres *resp = rqstp->rq_resp; 27327e705706SAndy Adamson u32 minorversion = resp->cstate.minorversion; 2733ebabe9a9SChristoph Hellwig struct path path = { 2734ebabe9a9SChristoph Hellwig .mnt = exp->ex_path.mnt, 2735ebabe9a9SChristoph Hellwig .dentry = dentry, 2736ebabe9a9SChristoph Hellwig }; 27373d733711SStanislav Kinsbursky struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); 27381da177e4SLinus Torvalds 27391da177e4SLinus Torvalds BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1); 2740916d2d84SJ. Bruce Fields BUG_ON(!nfsd_attrs_supported(minorversion, bmval)); 27411da177e4SLinus Torvalds 274242ca0993SJ.Bruce Fields if (exp->ex_fslocs.migrated) { 2743c2227a39SKinglong Mee status = fattr_handle_absent_fs(&bmval0, &bmval1, &bmval2, &rdattr_err); 274442ca0993SJ.Bruce Fields if (status) 274542ca0993SJ.Bruce Fields goto out; 274642ca0993SJ.Bruce Fields } 274742ca0993SJ.Bruce Fields 2748a528d35eSDavid Howells err = vfs_getattr(&path, &stat, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT); 2749b8dd7b9aSAl Viro if (err) 27501da177e4SLinus Torvalds goto out_nfserr; 275112337901SChristoph Hellwig if ((bmval0 & (FATTR4_WORD0_FILES_AVAIL | FATTR4_WORD0_FILES_FREE | 275212337901SChristoph Hellwig FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_MAXNAME)) || 27531da177e4SLinus Torvalds (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | 27541da177e4SLinus Torvalds FATTR4_WORD1_SPACE_TOTAL))) { 2755ebabe9a9SChristoph Hellwig err = vfs_statfs(&path, &statfs); 2756b8dd7b9aSAl Viro if (err) 27571da177e4SLinus Torvalds goto out_nfserr; 27581da177e4SLinus Torvalds } 27591da177e4SLinus Torvalds if ((bmval0 & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) && !fhp) { 2760d50e6136SJ. Bruce Fields tempfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL); 2761d50e6136SJ. Bruce Fields status = nfserr_jukebox; 2762d50e6136SJ. Bruce Fields if (!tempfh) 2763d50e6136SJ. Bruce Fields goto out; 2764d50e6136SJ. Bruce Fields fh_init(tempfh, NFS4_FHSIZE); 2765d50e6136SJ. Bruce Fields status = fh_compose(tempfh, exp, dentry, NULL); 27661da177e4SLinus Torvalds if (status) 27671da177e4SLinus Torvalds goto out; 2768d50e6136SJ. Bruce Fields fhp = tempfh; 27691da177e4SLinus Torvalds } 27701da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_ACL) { 27710c9d65e7SAndreas Gruenbacher err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl); 2772b8dd7b9aSAl Viro if (err == -EOPNOTSUPP) 27731da177e4SLinus Torvalds bmval0 &= ~FATTR4_WORD0_ACL; 2774b8dd7b9aSAl Viro else if (err == -EINVAL) { 27751da177e4SLinus Torvalds status = nfserr_attrnotsupp; 27761da177e4SLinus Torvalds goto out; 2777b8dd7b9aSAl Viro } else if (err != 0) 27781da177e4SLinus Torvalds goto out_nfserr; 27791da177e4SLinus Torvalds } 27802b44f1baSBenny Halevy 278118032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL 2782c2227a39SKinglong Mee if ((bmval2 & FATTR4_WORD2_SECURITY_LABEL) || 2783c2227a39SKinglong Mee bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) { 278432ddd944SJ. Bruce Fields if (exp->ex_flags & NFSEXP_SECURITY_LABEL) 27852b0143b5SDavid Howells err = security_inode_getsecctx(d_inode(dentry), 278618032ca0SDavid Quigley &context, &contextlen); 278732ddd944SJ. Bruce Fields else 278832ddd944SJ. Bruce Fields err = -EOPNOTSUPP; 278918032ca0SDavid Quigley contextsupport = (err == 0); 279018032ca0SDavid Quigley if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) { 279118032ca0SDavid Quigley if (err == -EOPNOTSUPP) 279218032ca0SDavid Quigley bmval2 &= ~FATTR4_WORD2_SECURITY_LABEL; 279318032ca0SDavid Quigley else if (err) 279418032ca0SDavid Quigley goto out_nfserr; 279518032ca0SDavid Quigley } 279618032ca0SDavid Quigley } 279718032ca0SDavid Quigley #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */ 279818032ca0SDavid Quigley 279975976de6SKinglong Mee status = nfsd4_encode_bitmap(xdr, bmval0, bmval1, bmval2); 280075976de6SKinglong Mee if (status) 280175976de6SKinglong Mee goto out; 2802082d4bd7SJ. Bruce Fields 2803082d4bd7SJ. Bruce Fields attrlen_offset = xdr->buf->len; 2804ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2805ddd1ea56SJ. Bruce Fields if (!p) 2806ddd1ea56SJ. Bruce Fields goto out_resource; 2807082d4bd7SJ. Bruce Fields p++; /* to be backfilled later */ 28081da177e4SLinus Torvalds 28091da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) { 2810dcd20869SJ. Bruce Fields u32 supp[3]; 2811dcd20869SJ. Bruce Fields 2812dcd20869SJ. Bruce Fields memcpy(supp, nfsd_suppattrs[minorversion], sizeof(supp)); 28137e705706SAndy Adamson 28140c9d65e7SAndreas Gruenbacher if (!IS_POSIXACL(dentry->d_inode)) 2815916d2d84SJ. Bruce Fields supp[0] &= ~FATTR4_WORD0_ACL; 281618032ca0SDavid Quigley if (!contextsupport) 2817916d2d84SJ. Bruce Fields supp[2] &= ~FATTR4_WORD2_SECURITY_LABEL; 2818916d2d84SJ. Bruce Fields if (!supp[2]) { 2819ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 12); 2820ddd1ea56SJ. Bruce Fields if (!p) 28212b44f1baSBenny Halevy goto out_resource; 2822c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(2); 2823916d2d84SJ. Bruce Fields *p++ = cpu_to_be32(supp[0]); 2824916d2d84SJ. Bruce Fields *p++ = cpu_to_be32(supp[1]); 28257e705706SAndy Adamson } else { 2826ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 16); 2827ddd1ea56SJ. Bruce Fields if (!p) 28282b44f1baSBenny Halevy goto out_resource; 2829c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(3); 2830916d2d84SJ. Bruce Fields *p++ = cpu_to_be32(supp[0]); 2831916d2d84SJ. Bruce Fields *p++ = cpu_to_be32(supp[1]); 2832916d2d84SJ. Bruce Fields *p++ = cpu_to_be32(supp[2]); 28337e705706SAndy Adamson } 28341da177e4SLinus Torvalds } 28351da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_TYPE) { 2836ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2837ddd1ea56SJ. Bruce Fields if (!p) 28381da177e4SLinus Torvalds goto out_resource; 28393d2544b1SJ. Bruce Fields dummy = nfs4_file_type(stat.mode); 28406b6d8137SJ. Bruce Fields if (dummy == NF4BAD) { 28416b6d8137SJ. Bruce Fields status = nfserr_serverfault; 28426b6d8137SJ. Bruce Fields goto out; 28436b6d8137SJ. Bruce Fields } 2844c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(dummy); 28451da177e4SLinus Torvalds } 28461da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) { 2847ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2848ddd1ea56SJ. Bruce Fields if (!p) 28491da177e4SLinus Torvalds goto out_resource; 285049640001SNeilBrown if (exp->ex_flags & NFSEXP_NOSUBTREECHECK) 2851c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(NFS4_FH_PERSISTENT); 285249640001SNeilBrown else 2853c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(NFS4_FH_PERSISTENT| 2854c373b0a4SJ. Bruce Fields NFS4_FH_VOL_RENAME); 28551da177e4SLinus Torvalds } 28561da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_CHANGE) { 2857ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 2858ddd1ea56SJ. Bruce Fields if (!p) 28591da177e4SLinus Torvalds goto out_resource; 2860b8800921SNeilBrown p = encode_change(p, &stat, d_inode(dentry), exp); 28611da177e4SLinus Torvalds } 28621da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_SIZE) { 2863ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 2864ddd1ea56SJ. Bruce Fields if (!p) 28651da177e4SLinus Torvalds goto out_resource; 2866b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, stat.size); 28671da177e4SLinus Torvalds } 28681da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) { 2869ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2870ddd1ea56SJ. Bruce Fields if (!p) 28711da177e4SLinus Torvalds goto out_resource; 2872c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(1); 28731da177e4SLinus Torvalds } 28741da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_SYMLINK_SUPPORT) { 2875ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2876ddd1ea56SJ. Bruce Fields if (!p) 28771da177e4SLinus Torvalds goto out_resource; 2878c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(1); 28791da177e4SLinus Torvalds } 28801da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_NAMED_ATTR) { 2881ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2882ddd1ea56SJ. Bruce Fields if (!p) 28831da177e4SLinus Torvalds goto out_resource; 2884c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 28851da177e4SLinus Torvalds } 28861da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FSID) { 2887ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 16); 2888ddd1ea56SJ. Bruce Fields if (!p) 28891da177e4SLinus Torvalds goto out_resource; 289042ca0993SJ.Bruce Fields if (exp->ex_fslocs.migrated) { 2891b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MAJOR); 2892b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MINOR); 2893af6a4e28SNeilBrown } else switch(fsid_source(fhp)) { 2894af6a4e28SNeilBrown case FSIDSOURCE_FSID: 2895b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (u64)exp->ex_fsid); 2896b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (u64)0); 2897af6a4e28SNeilBrown break; 2898af6a4e28SNeilBrown case FSIDSOURCE_DEV: 2899c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 2900c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(MAJOR(stat.dev)); 2901c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 2902c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(MINOR(stat.dev)); 2903af6a4e28SNeilBrown break; 2904af6a4e28SNeilBrown case FSIDSOURCE_UUID: 290594eb3689SKinglong Mee p = xdr_encode_opaque_fixed(p, exp->ex_uuid, 290694eb3689SKinglong Mee EX_UUID_LEN); 2907af6a4e28SNeilBrown break; 29081da177e4SLinus Torvalds } 29091da177e4SLinus Torvalds } 29101da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) { 2911ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2912ddd1ea56SJ. Bruce Fields if (!p) 29131da177e4SLinus Torvalds goto out_resource; 2914c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 29151da177e4SLinus Torvalds } 29161da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_LEASE_TIME) { 2917ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2918ddd1ea56SJ. Bruce Fields if (!p) 29191da177e4SLinus Torvalds goto out_resource; 2920c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(nn->nfsd4_lease); 29211da177e4SLinus Torvalds } 29221da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) { 2923ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2924ddd1ea56SJ. Bruce Fields if (!p) 29251da177e4SLinus Torvalds goto out_resource; 2926c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(rdattr_err); 29271da177e4SLinus Torvalds } 29281da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_ACL) { 29291da177e4SLinus Torvalds struct nfs4_ace *ace; 29301da177e4SLinus Torvalds 29311da177e4SLinus Torvalds if (acl == NULL) { 2932ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2933ddd1ea56SJ. Bruce Fields if (!p) 29341da177e4SLinus Torvalds goto out_resource; 29351da177e4SLinus Torvalds 2936c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 29371da177e4SLinus Torvalds goto out_acl; 29381da177e4SLinus Torvalds } 2939ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2940ddd1ea56SJ. Bruce Fields if (!p) 29411da177e4SLinus Torvalds goto out_resource; 2942c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(acl->naces); 29431da177e4SLinus Torvalds 294428e05dd8SJ. Bruce Fields for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) { 2945ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4*3); 2946ddd1ea56SJ. Bruce Fields if (!p) 29471da177e4SLinus Torvalds goto out_resource; 2948c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(ace->type); 2949c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(ace->flag); 2950c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(ace->access_mask & 2951c373b0a4SJ. Bruce Fields NFS4_ACE_MASK_ALL); 2952ddd1ea56SJ. Bruce Fields status = nfsd4_encode_aclname(xdr, rqstp, ace); 29531da177e4SLinus Torvalds if (status) 29541da177e4SLinus Torvalds goto out; 29551da177e4SLinus Torvalds } 29561da177e4SLinus Torvalds } 29571da177e4SLinus Torvalds out_acl: 29581da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_ACLSUPPORT) { 2959ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2960ddd1ea56SJ. Bruce Fields if (!p) 29611da177e4SLinus Torvalds goto out_resource; 29620c9d65e7SAndreas Gruenbacher *p++ = cpu_to_be32(IS_POSIXACL(dentry->d_inode) ? 29631da177e4SLinus Torvalds ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0); 29641da177e4SLinus Torvalds } 29651da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_CANSETTIME) { 2966ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2967ddd1ea56SJ. Bruce Fields if (!p) 29681da177e4SLinus Torvalds goto out_resource; 2969c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(1); 29701da177e4SLinus Torvalds } 29711da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) { 2972ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2973ddd1ea56SJ. Bruce Fields if (!p) 29741da177e4SLinus Torvalds goto out_resource; 2975c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 29761da177e4SLinus Torvalds } 29771da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) { 2978ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2979ddd1ea56SJ. Bruce Fields if (!p) 29801da177e4SLinus Torvalds goto out_resource; 2981c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(1); 29821da177e4SLinus Torvalds } 29831da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_CHOWN_RESTRICTED) { 2984ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 2985ddd1ea56SJ. Bruce Fields if (!p) 29861da177e4SLinus Torvalds goto out_resource; 2987c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(1); 29881da177e4SLinus Torvalds } 29891da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FILEHANDLE) { 2990ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, fhp->fh_handle.fh_size + 4); 2991ddd1ea56SJ. Bruce Fields if (!p) 29921da177e4SLinus Torvalds goto out_resource; 29930c0c267bSJ. Bruce Fields p = xdr_encode_opaque(p, &fhp->fh_handle.fh_base, 29940c0c267bSJ. Bruce Fields fhp->fh_handle.fh_size); 29951da177e4SLinus Torvalds } 29961da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FILEID) { 2997ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 2998ddd1ea56SJ. Bruce Fields if (!p) 29991da177e4SLinus Torvalds goto out_resource; 3000b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, stat.ino); 30011da177e4SLinus Torvalds } 30021da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FILES_AVAIL) { 3003ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 3004ddd1ea56SJ. Bruce Fields if (!p) 30051da177e4SLinus Torvalds goto out_resource; 3006b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (u64) statfs.f_ffree); 30071da177e4SLinus Torvalds } 30081da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FILES_FREE) { 3009ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 3010ddd1ea56SJ. Bruce Fields if (!p) 30111da177e4SLinus Torvalds goto out_resource; 3012b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (u64) statfs.f_ffree); 30131da177e4SLinus Torvalds } 30141da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FILES_TOTAL) { 3015ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 3016ddd1ea56SJ. Bruce Fields if (!p) 30171da177e4SLinus Torvalds goto out_resource; 3018b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (u64) statfs.f_files); 30191da177e4SLinus Torvalds } 302081c3f413SJ.Bruce Fields if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) { 3021ddd1ea56SJ. Bruce Fields status = nfsd4_encode_fs_locations(xdr, rqstp, exp); 302281c3f413SJ.Bruce Fields if (status) 302381c3f413SJ.Bruce Fields goto out; 302481c3f413SJ.Bruce Fields } 30251da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) { 3026ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 3027ddd1ea56SJ. Bruce Fields if (!p) 30281da177e4SLinus Torvalds goto out_resource; 3029c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(1); 30301da177e4SLinus Torvalds } 30311da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_MAXFILESIZE) { 3032ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 3033ddd1ea56SJ. Bruce Fields if (!p) 30341da177e4SLinus Torvalds goto out_resource; 3035b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, exp->ex_path.mnt->mnt_sb->s_maxbytes); 30361da177e4SLinus Torvalds } 30371da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_MAXLINK) { 3038ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 3039ddd1ea56SJ. Bruce Fields if (!p) 30401da177e4SLinus Torvalds goto out_resource; 3041c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(255); 30421da177e4SLinus Torvalds } 30431da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_MAXNAME) { 3044ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 3045ddd1ea56SJ. Bruce Fields if (!p) 30461da177e4SLinus Torvalds goto out_resource; 3047c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(statfs.f_namelen); 30481da177e4SLinus Torvalds } 30491da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_MAXREAD) { 3050ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 3051ddd1ea56SJ. Bruce Fields if (!p) 30521da177e4SLinus Torvalds goto out_resource; 3053b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (u64) svc_max_payload(rqstp)); 30541da177e4SLinus Torvalds } 30551da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_MAXWRITE) { 3056ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 3057ddd1ea56SJ. Bruce Fields if (!p) 30581da177e4SLinus Torvalds goto out_resource; 3059b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (u64) svc_max_payload(rqstp)); 30601da177e4SLinus Torvalds } 30611da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_MODE) { 3062ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 3063ddd1ea56SJ. Bruce Fields if (!p) 30641da177e4SLinus Torvalds goto out_resource; 3065c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(stat.mode & S_IALLUGO); 30661da177e4SLinus Torvalds } 30671da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_NO_TRUNC) { 3068ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 3069ddd1ea56SJ. Bruce Fields if (!p) 30701da177e4SLinus Torvalds goto out_resource; 3071c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(1); 30721da177e4SLinus Torvalds } 30731da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_NUMLINKS) { 3074ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 3075ddd1ea56SJ. Bruce Fields if (!p) 30761da177e4SLinus Torvalds goto out_resource; 3077c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(stat.nlink); 30781da177e4SLinus Torvalds } 30791da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_OWNER) { 3080ddd1ea56SJ. Bruce Fields status = nfsd4_encode_user(xdr, rqstp, stat.uid); 30811da177e4SLinus Torvalds if (status) 30821da177e4SLinus Torvalds goto out; 30831da177e4SLinus Torvalds } 30841da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_OWNER_GROUP) { 3085ddd1ea56SJ. Bruce Fields status = nfsd4_encode_group(xdr, rqstp, stat.gid); 30861da177e4SLinus Torvalds if (status) 30871da177e4SLinus Torvalds goto out; 30881da177e4SLinus Torvalds } 30891da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_RAWDEV) { 3090ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 3091ddd1ea56SJ. Bruce Fields if (!p) 30921da177e4SLinus Torvalds goto out_resource; 3093c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32((u32) MAJOR(stat.rdev)); 3094c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32((u32) MINOR(stat.rdev)); 30951da177e4SLinus Torvalds } 30961da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) { 3097ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 3098ddd1ea56SJ. Bruce Fields if (!p) 30991da177e4SLinus Torvalds goto out_resource; 31001da177e4SLinus Torvalds dummy64 = (u64)statfs.f_bavail * (u64)statfs.f_bsize; 3101b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, dummy64); 31021da177e4SLinus Torvalds } 31031da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_SPACE_FREE) { 3104ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 3105ddd1ea56SJ. Bruce Fields if (!p) 31061da177e4SLinus Torvalds goto out_resource; 31071da177e4SLinus Torvalds dummy64 = (u64)statfs.f_bfree * (u64)statfs.f_bsize; 3108b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, dummy64); 31091da177e4SLinus Torvalds } 31101da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) { 3111ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 3112ddd1ea56SJ. Bruce Fields if (!p) 31131da177e4SLinus Torvalds goto out_resource; 31141da177e4SLinus Torvalds dummy64 = (u64)statfs.f_blocks * (u64)statfs.f_bsize; 3115b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, dummy64); 31161da177e4SLinus Torvalds } 31171da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_SPACE_USED) { 3118ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 3119ddd1ea56SJ. Bruce Fields if (!p) 31201da177e4SLinus Torvalds goto out_resource; 31211da177e4SLinus Torvalds dummy64 = (u64)stat.blocks << 9; 3122b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, dummy64); 31231da177e4SLinus Torvalds } 31241da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_TIME_ACCESS) { 3125ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 12); 3126ddd1ea56SJ. Bruce Fields if (!p) 31271da177e4SLinus Torvalds goto out_resource; 3128b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (s64)stat.atime.tv_sec); 3129c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(stat.atime.tv_nsec); 31301da177e4SLinus Torvalds } 31311da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_TIME_DELTA) { 3132ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 12); 3133ddd1ea56SJ. Bruce Fields if (!p) 31341da177e4SLinus Torvalds goto out_resource; 313516945141SJ. Bruce Fields p = encode_time_delta(p, d_inode(dentry)); 31361da177e4SLinus Torvalds } 31371da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_TIME_METADATA) { 3138ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 12); 3139ddd1ea56SJ. Bruce Fields if (!p) 31401da177e4SLinus Torvalds goto out_resource; 3141b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (s64)stat.ctime.tv_sec); 3142c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(stat.ctime.tv_nsec); 31431da177e4SLinus Torvalds } 31441da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_TIME_MODIFY) { 3145ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 12); 3146ddd1ea56SJ. Bruce Fields if (!p) 31471da177e4SLinus Torvalds goto out_resource; 3148b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (s64)stat.mtime.tv_sec); 3149c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(stat.mtime.tv_nsec); 31501da177e4SLinus Torvalds } 31511da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) { 31520a2050d7SKinglong Mee struct kstat parent_stat; 31530a2050d7SKinglong Mee u64 ino = stat.ino; 31540a2050d7SKinglong Mee 3155ddd1ea56SJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 3156ddd1ea56SJ. Bruce Fields if (!p) 31571da177e4SLinus Torvalds goto out_resource; 3158406a7ea9SFrank Filz /* 3159406a7ea9SFrank Filz * Get parent's attributes if not ignoring crossmount 3160406a7ea9SFrank Filz * and this is the root of a cross-mounted filesystem. 3161406a7ea9SFrank Filz */ 3162406a7ea9SFrank Filz if (ignore_crossmnt == 0 && 31630a2050d7SKinglong Mee dentry == exp->ex_path.mnt->mnt_root) { 31640a2050d7SKinglong Mee err = get_parent_attributes(exp, &parent_stat); 31650a2050d7SKinglong Mee if (err) 31660a2050d7SKinglong Mee goto out_nfserr; 31670a2050d7SKinglong Mee ino = parent_stat.ino; 31680a2050d7SKinglong Mee } 31690a2050d7SKinglong Mee p = xdr_encode_hyper(p, ino); 31701da177e4SLinus Torvalds } 31719cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS 31726896f15aSKinglong Mee if (bmval1 & FATTR4_WORD1_FS_LAYOUT_TYPES) { 31738a4c3926SJeff Layton status = nfsd4_encode_layout_types(xdr, exp->ex_layout_types); 31746896f15aSKinglong Mee if (status) 31756896f15aSKinglong Mee goto out; 31769cf514ccSChristoph Hellwig } 31776896f15aSKinglong Mee 31786896f15aSKinglong Mee if (bmval2 & FATTR4_WORD2_LAYOUT_TYPES) { 31798a4c3926SJeff Layton status = nfsd4_encode_layout_types(xdr, exp->ex_layout_types); 31806896f15aSKinglong Mee if (status) 31816896f15aSKinglong Mee goto out; 31829cf514ccSChristoph Hellwig } 31839cf514ccSChristoph Hellwig 31849cf514ccSChristoph Hellwig if (bmval2 & FATTR4_WORD2_LAYOUT_BLKSIZE) { 31859cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 4); 31869cf514ccSChristoph Hellwig if (!p) 31879cf514ccSChristoph Hellwig goto out_resource; 31889cf514ccSChristoph Hellwig *p++ = cpu_to_be32(stat.blksize); 31899cf514ccSChristoph Hellwig } 31909cf514ccSChristoph Hellwig #endif /* CONFIG_NFSD_PNFS */ 31918c18f205SBenny Halevy if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) { 3192b26b78cbSTrond Myklebust u32 supp[3]; 3193b26b78cbSTrond Myklebust 3194b26b78cbSTrond Myklebust memcpy(supp, nfsd_suppattrs[minorversion], sizeof(supp)); 3195b26b78cbSTrond Myklebust supp[0] &= NFSD_SUPPATTR_EXCLCREAT_WORD0; 3196b26b78cbSTrond Myklebust supp[1] &= NFSD_SUPPATTR_EXCLCREAT_WORD1; 3197b26b78cbSTrond Myklebust supp[2] &= NFSD_SUPPATTR_EXCLCREAT_WORD2; 3198b26b78cbSTrond Myklebust 3199b26b78cbSTrond Myklebust status = nfsd4_encode_bitmap(xdr, supp[0], supp[1], supp[2]); 320075976de6SKinglong Mee if (status) 320175976de6SKinglong Mee goto out; 32028c18f205SBenny Halevy } 32037e705706SAndy Adamson 3204a8585763SJ. Bruce Fields if (bmval2 & FATTR4_WORD2_CHANGE_ATTR_TYPE) { 3205a8585763SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 3206a8585763SJ. Bruce Fields if (!p) 3207a8585763SJ. Bruce Fields goto out_resource; 3208a8585763SJ. Bruce Fields if (IS_I_VERSION(d_inode(dentry))) 3209a8585763SJ. Bruce Fields *p++ = cpu_to_be32(NFS4_CHANGE_TYPE_IS_MONOTONIC_INCR); 3210a8585763SJ. Bruce Fields else 3211a8585763SJ. Bruce Fields *p++ = cpu_to_be32(NFS4_CHANGE_TYPE_IS_TIME_METADATA); 3212a8585763SJ. Bruce Fields } 3213a8585763SJ. Bruce Fields 32140ab88ca4SArnd Bergmann #ifdef CONFIG_NFSD_V4_SECURITY_LABEL 32157d580722SKinglong Mee if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) { 32167d580722SKinglong Mee status = nfsd4_encode_security_label(xdr, rqstp, context, 32177d580722SKinglong Mee contextlen); 32187d580722SKinglong Mee if (status) 32197d580722SKinglong Mee goto out; 32207d580722SKinglong Mee } 32210ab88ca4SArnd Bergmann #endif 32227d580722SKinglong Mee 32230e885e84SFrank van der Linden if (bmval2 & FATTR4_WORD2_XATTR_SUPPORT) { 32240e885e84SFrank van der Linden p = xdr_reserve_space(xdr, 4); 32250e885e84SFrank van der Linden if (!p) 32260e885e84SFrank van der Linden goto out_resource; 32270e885e84SFrank van der Linden err = xattr_supported_namespace(d_inode(dentry), 32280e885e84SFrank van der Linden XATTR_USER_PREFIX); 32290e885e84SFrank van der Linden *p++ = cpu_to_be32(err == 0); 32300e885e84SFrank van der Linden } 32310e885e84SFrank van der Linden 3232082d4bd7SJ. Bruce Fields attrlen = htonl(xdr->buf->len - attrlen_offset - 4); 3233082d4bd7SJ. Bruce Fields write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, 4); 32341da177e4SLinus Torvalds status = nfs_ok; 32351da177e4SLinus Torvalds 32361da177e4SLinus Torvalds out: 3237ba4e55bbSJ. Bruce Fields #ifdef CONFIG_NFSD_V4_SECURITY_LABEL 323818032ca0SDavid Quigley if (context) 323918032ca0SDavid Quigley security_release_secctx(context, contextlen); 3240ba4e55bbSJ. Bruce Fields #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */ 324128e05dd8SJ. Bruce Fields kfree(acl); 324218df11d0SYan, Zheng if (tempfh) { 3243d50e6136SJ. Bruce Fields fh_put(tempfh); 324418df11d0SYan, Zheng kfree(tempfh); 324518df11d0SYan, Zheng } 32461fcea5b2SJ. Bruce Fields if (status) 32471fcea5b2SJ. Bruce Fields xdr_truncate_encode(xdr, starting_len); 32481da177e4SLinus Torvalds return status; 32491da177e4SLinus Torvalds out_nfserr: 3250b8dd7b9aSAl Viro status = nfserrno(err); 32511da177e4SLinus Torvalds goto out; 32521da177e4SLinus Torvalds out_resource: 32531da177e4SLinus Torvalds status = nfserr_resource; 32541da177e4SLinus Torvalds goto out; 32551da177e4SLinus Torvalds } 32561da177e4SLinus Torvalds 32572825a7f9SJ. Bruce Fields static void svcxdr_init_encode_from_buffer(struct xdr_stream *xdr, 32582825a7f9SJ. Bruce Fields struct xdr_buf *buf, __be32 *p, int bytes) 32592825a7f9SJ. Bruce Fields { 32602825a7f9SJ. Bruce Fields xdr->scratch.iov_len = 0; 32612825a7f9SJ. Bruce Fields memset(buf, 0, sizeof(struct xdr_buf)); 32622825a7f9SJ. Bruce Fields buf->head[0].iov_base = p; 32632825a7f9SJ. Bruce Fields buf->head[0].iov_len = 0; 32642825a7f9SJ. Bruce Fields buf->len = 0; 32652825a7f9SJ. Bruce Fields xdr->buf = buf; 32662825a7f9SJ. Bruce Fields xdr->iov = buf->head; 32672825a7f9SJ. Bruce Fields xdr->p = p; 32682825a7f9SJ. Bruce Fields xdr->end = (void *)p + bytes; 32692825a7f9SJ. Bruce Fields buf->buflen = bytes; 32702825a7f9SJ. Bruce Fields } 32712825a7f9SJ. Bruce Fields 3272d5184658SJ. Bruce Fields __be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words, 3273d5184658SJ. Bruce Fields struct svc_fh *fhp, struct svc_export *exp, 3274d5184658SJ. Bruce Fields struct dentry *dentry, u32 *bmval, 3275d5184658SJ. Bruce Fields struct svc_rqst *rqstp, int ignore_crossmnt) 3276d5184658SJ. Bruce Fields { 32772825a7f9SJ. Bruce Fields struct xdr_buf dummy; 3278d5184658SJ. Bruce Fields struct xdr_stream xdr; 3279d5184658SJ. Bruce Fields __be32 ret; 3280d5184658SJ. Bruce Fields 32812825a7f9SJ. Bruce Fields svcxdr_init_encode_from_buffer(&xdr, &dummy, *p, words << 2); 3282d5184658SJ. Bruce Fields ret = nfsd4_encode_fattr(&xdr, fhp, exp, dentry, bmval, rqstp, 3283d5184658SJ. Bruce Fields ignore_crossmnt); 3284d5184658SJ. Bruce Fields *p = xdr.p; 3285d5184658SJ. Bruce Fields return ret; 3286d5184658SJ. Bruce Fields } 3287d5184658SJ. Bruce Fields 3288c0ce6ec8SJ. Bruce Fields static inline int attributes_need_mount(u32 *bmval) 3289c0ce6ec8SJ. Bruce Fields { 3290c0ce6ec8SJ. Bruce Fields if (bmval[0] & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_LEASE_TIME)) 3291c0ce6ec8SJ. Bruce Fields return 1; 3292c0ce6ec8SJ. Bruce Fields if (bmval[1] & ~FATTR4_WORD1_MOUNTED_ON_FILEID) 3293c0ce6ec8SJ. Bruce Fields return 1; 3294c0ce6ec8SJ. Bruce Fields return 0; 3295c0ce6ec8SJ. Bruce Fields } 3296c0ce6ec8SJ. Bruce Fields 3297b37ad28bSAl Viro static __be32 3298561f0ed4SJ. Bruce Fields nfsd4_encode_dirent_fattr(struct xdr_stream *xdr, struct nfsd4_readdir *cd, 3299561f0ed4SJ. Bruce Fields const char *name, int namlen) 33001da177e4SLinus Torvalds { 33011da177e4SLinus Torvalds struct svc_export *exp = cd->rd_fhp->fh_export; 33021da177e4SLinus Torvalds struct dentry *dentry; 3303b37ad28bSAl Viro __be32 nfserr; 3304406a7ea9SFrank Filz int ignore_crossmnt = 0; 33051da177e4SLinus Torvalds 33066c2d4798SAl Viro dentry = lookup_positive_unlocked(name, cd->rd_fhp->fh_dentry, namlen); 33071da177e4SLinus Torvalds if (IS_ERR(dentry)) 33081da177e4SLinus Torvalds return nfserrno(PTR_ERR(dentry)); 33091da177e4SLinus Torvalds 33101da177e4SLinus Torvalds exp_get(exp); 3311406a7ea9SFrank Filz /* 3312406a7ea9SFrank Filz * In the case of a mountpoint, the client may be asking for 3313406a7ea9SFrank Filz * attributes that are only properties of the underlying filesystem 3314406a7ea9SFrank Filz * as opposed to the cross-mounted file system. In such a case, 3315406a7ea9SFrank Filz * we will not follow the cross mount and will fill the attribtutes 3316406a7ea9SFrank Filz * directly from the mountpoint dentry. 3317406a7ea9SFrank Filz */ 33183227fa41SJ. Bruce Fields if (nfsd_mountpoint(dentry, exp)) { 3319021d3a72SJ.Bruce Fields int err; 3320021d3a72SJ.Bruce Fields 33213227fa41SJ. Bruce Fields if (!(exp->ex_flags & NFSEXP_V4ROOT) 33223227fa41SJ. Bruce Fields && !attributes_need_mount(cd->rd_bmval)) { 33233227fa41SJ. Bruce Fields ignore_crossmnt = 1; 33243227fa41SJ. Bruce Fields goto out_encode; 33253227fa41SJ. Bruce Fields } 3326dcb488a3SAndy Adamson /* 3327dcb488a3SAndy Adamson * Why the heck aren't we just using nfsd_lookup?? 3328dcb488a3SAndy Adamson * Different "."/".." handling? Something else? 3329dcb488a3SAndy Adamson * At least, add a comment here to explain.... 3330dcb488a3SAndy Adamson */ 3331021d3a72SJ.Bruce Fields err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp); 3332021d3a72SJ.Bruce Fields if (err) { 3333021d3a72SJ.Bruce Fields nfserr = nfserrno(err); 33341da177e4SLinus Torvalds goto out_put; 33351da177e4SLinus Torvalds } 3336dcb488a3SAndy Adamson nfserr = check_nfsd_access(exp, cd->rd_rqstp); 3337dcb488a3SAndy Adamson if (nfserr) 3338dcb488a3SAndy Adamson goto out_put; 33391da177e4SLinus Torvalds 33401da177e4SLinus Torvalds } 33413227fa41SJ. Bruce Fields out_encode: 3342561f0ed4SJ. Bruce Fields nfserr = nfsd4_encode_fattr(xdr, NULL, exp, dentry, cd->rd_bmval, 3343406a7ea9SFrank Filz cd->rd_rqstp, ignore_crossmnt); 33441da177e4SLinus Torvalds out_put: 33451da177e4SLinus Torvalds dput(dentry); 33461da177e4SLinus Torvalds exp_put(exp); 33471da177e4SLinus Torvalds return nfserr; 33481da177e4SLinus Torvalds } 33491da177e4SLinus Torvalds 33502ebbc012SAl Viro static __be32 * 3351561f0ed4SJ. Bruce Fields nfsd4_encode_rdattr_error(struct xdr_stream *xdr, __be32 nfserr) 33521da177e4SLinus Torvalds { 3353561f0ed4SJ. Bruce Fields __be32 *p; 3354561f0ed4SJ. Bruce Fields 3355c3a45617SKinglong Mee p = xdr_reserve_space(xdr, 20); 3356561f0ed4SJ. Bruce Fields if (!p) 33571da177e4SLinus Torvalds return NULL; 33581da177e4SLinus Torvalds *p++ = htonl(2); 33591da177e4SLinus Torvalds *p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */ 33601da177e4SLinus Torvalds *p++ = htonl(0); /* bmval1 */ 33611da177e4SLinus Torvalds 336287915c64SJ. Bruce Fields *p++ = htonl(4); /* attribute length */ 33631da177e4SLinus Torvalds *p++ = nfserr; /* no htonl */ 33641da177e4SLinus Torvalds return p; 33651da177e4SLinus Torvalds } 33661da177e4SLinus Torvalds 33671da177e4SLinus Torvalds static int 3368a0ad13efSNeilBrown nfsd4_encode_dirent(void *ccdv, const char *name, int namlen, 3369a0ad13efSNeilBrown loff_t offset, u64 ino, unsigned int d_type) 33701da177e4SLinus Torvalds { 3371a0ad13efSNeilBrown struct readdir_cd *ccd = ccdv; 33721da177e4SLinus Torvalds struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common); 3373561f0ed4SJ. Bruce Fields struct xdr_stream *xdr = cd->xdr; 3374561f0ed4SJ. Bruce Fields int start_offset = xdr->buf->len; 3375561f0ed4SJ. Bruce Fields int cookie_offset; 3376aee37764SJ. Bruce Fields u32 name_and_cookie; 3377561f0ed4SJ. Bruce Fields int entry_bytes; 3378b37ad28bSAl Viro __be32 nfserr = nfserr_toosmall; 3379561f0ed4SJ. Bruce Fields __be64 wire_offset; 3380561f0ed4SJ. Bruce Fields __be32 *p; 33811da177e4SLinus Torvalds 33821da177e4SLinus Torvalds /* In nfsv4, "." and ".." never make it onto the wire.. */ 33831da177e4SLinus Torvalds if (name && isdotent(name, namlen)) { 33841da177e4SLinus Torvalds cd->common.err = nfs_ok; 33851da177e4SLinus Torvalds return 0; 33861da177e4SLinus Torvalds } 33871da177e4SLinus Torvalds 3388561f0ed4SJ. Bruce Fields if (cd->cookie_offset) { 3389561f0ed4SJ. Bruce Fields wire_offset = cpu_to_be64(offset); 3390561f0ed4SJ. Bruce Fields write_bytes_to_xdr_buf(xdr->buf, cd->cookie_offset, 3391561f0ed4SJ. Bruce Fields &wire_offset, 8); 3392561f0ed4SJ. Bruce Fields } 33931da177e4SLinus Torvalds 3394561f0ed4SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 3395561f0ed4SJ. Bruce Fields if (!p) 33961da177e4SLinus Torvalds goto fail; 33971da177e4SLinus Torvalds *p++ = xdr_one; /* mark entry present */ 3398561f0ed4SJ. Bruce Fields cookie_offset = xdr->buf->len; 3399561f0ed4SJ. Bruce Fields p = xdr_reserve_space(xdr, 3*4 + namlen); 3400561f0ed4SJ. Bruce Fields if (!p) 3401561f0ed4SJ. Bruce Fields goto fail; 34021da177e4SLinus Torvalds p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */ 34031da177e4SLinus Torvalds p = xdr_encode_array(p, name, namlen); /* name length & name */ 34041da177e4SLinus Torvalds 3405561f0ed4SJ. Bruce Fields nfserr = nfsd4_encode_dirent_fattr(xdr, cd, name, namlen); 34061da177e4SLinus Torvalds switch (nfserr) { 34071da177e4SLinus Torvalds case nfs_ok: 34081da177e4SLinus Torvalds break; 34091da177e4SLinus Torvalds case nfserr_resource: 34101da177e4SLinus Torvalds nfserr = nfserr_toosmall; 34111da177e4SLinus Torvalds goto fail; 3412b2c0cea6SJ. Bruce Fields case nfserr_noent: 3413f41c5ad2SKinglong Mee xdr_truncate_encode(xdr, start_offset); 3414b2c0cea6SJ. Bruce Fields goto skip_entry; 34151da177e4SLinus Torvalds default: 34161da177e4SLinus Torvalds /* 34171da177e4SLinus Torvalds * If the client requested the RDATTR_ERROR attribute, 34181da177e4SLinus Torvalds * we stuff the error code into this attribute 34191da177e4SLinus Torvalds * and continue. If this attribute was not requested, 34201da177e4SLinus Torvalds * then in accordance with the spec, we fail the 34211da177e4SLinus Torvalds * entire READDIR operation(!) 34221da177e4SLinus Torvalds */ 34231da177e4SLinus Torvalds if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR)) 34241da177e4SLinus Torvalds goto fail; 3425561f0ed4SJ. Bruce Fields p = nfsd4_encode_rdattr_error(xdr, nfserr); 342634081efcSFred Isaman if (p == NULL) { 342734081efcSFred Isaman nfserr = nfserr_toosmall; 34281da177e4SLinus Torvalds goto fail; 34291da177e4SLinus Torvalds } 343034081efcSFred Isaman } 3431561f0ed4SJ. Bruce Fields nfserr = nfserr_toosmall; 3432561f0ed4SJ. Bruce Fields entry_bytes = xdr->buf->len - start_offset; 3433561f0ed4SJ. Bruce Fields if (entry_bytes > cd->rd_maxcount) 3434561f0ed4SJ. Bruce Fields goto fail; 3435561f0ed4SJ. Bruce Fields cd->rd_maxcount -= entry_bytes; 3436aee37764SJ. Bruce Fields /* 3437aee37764SJ. Bruce Fields * RFC 3530 14.2.24 describes rd_dircount as only a "hint", so 3438aee37764SJ. Bruce Fields * let's always let through the first entry, at least: 3439aee37764SJ. Bruce Fields */ 34400ec016e3SJ. Bruce Fields if (!cd->rd_dircount) 34410ec016e3SJ. Bruce Fields goto fail; 34420ec016e3SJ. Bruce Fields name_and_cookie = 4 + 4 * XDR_QUADLEN(namlen) + 8; 3443aee37764SJ. Bruce Fields if (name_and_cookie > cd->rd_dircount && cd->cookie_offset) 3444aee37764SJ. Bruce Fields goto fail; 3445aee37764SJ. Bruce Fields cd->rd_dircount -= min(cd->rd_dircount, name_and_cookie); 34460ec016e3SJ. Bruce Fields 3447561f0ed4SJ. Bruce Fields cd->cookie_offset = cookie_offset; 3448b2c0cea6SJ. Bruce Fields skip_entry: 34491da177e4SLinus Torvalds cd->common.err = nfs_ok; 34501da177e4SLinus Torvalds return 0; 34511da177e4SLinus Torvalds fail: 3452561f0ed4SJ. Bruce Fields xdr_truncate_encode(xdr, start_offset); 34531da177e4SLinus Torvalds cd->common.err = nfserr; 34541da177e4SLinus Torvalds return -EINVAL; 34551da177e4SLinus Torvalds } 34561da177e4SLinus Torvalds 3457d0a381ddSJ. Bruce Fields static __be32 3458d0a381ddSJ. Bruce Fields nfsd4_encode_stateid(struct xdr_stream *xdr, stateid_t *sid) 3459e2f282b9SBenny Halevy { 3460bc749ca4SJ. Bruce Fields __be32 *p; 3461e2f282b9SBenny Halevy 3462d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, sizeof(stateid_t)); 3463d0a381ddSJ. Bruce Fields if (!p) 3464d0a381ddSJ. Bruce Fields return nfserr_resource; 3465c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sid->si_generation); 34660c0c267bSJ. Bruce Fields p = xdr_encode_opaque_fixed(p, &sid->si_opaque, 34670c0c267bSJ. Bruce Fields sizeof(stateid_opaque_t)); 3468d0a381ddSJ. Bruce Fields return 0; 3469e2f282b9SBenny Halevy } 3470e2f282b9SBenny Halevy 3471695e12f8SBenny Halevy static __be32 3472b37ad28bSAl Viro nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_access *access) 34731da177e4SLinus Torvalds { 3474d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 3475bc749ca4SJ. Bruce Fields __be32 *p; 34761da177e4SLinus Torvalds 3477d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 3478d0a381ddSJ. Bruce Fields if (!p) 3479d0a381ddSJ. Bruce Fields return nfserr_resource; 3480c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(access->ac_supported); 3481c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(access->ac_resp_access); 3482bac966d6SJ. Bruce Fields return 0; 34831da177e4SLinus Torvalds } 34841da177e4SLinus Torvalds 34851d1bc8f2SJ. Bruce Fields static __be32 nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_bind_conn_to_session *bcts) 34861d1bc8f2SJ. Bruce Fields { 3487d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 34881d1bc8f2SJ. Bruce Fields __be32 *p; 34891d1bc8f2SJ. Bruce Fields 3490d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 8); 3491d0a381ddSJ. Bruce Fields if (!p) 3492d0a381ddSJ. Bruce Fields return nfserr_resource; 34930c0c267bSJ. Bruce Fields p = xdr_encode_opaque_fixed(p, bcts->sessionid.data, 34940c0c267bSJ. Bruce Fields NFS4_MAX_SESSIONID_LEN); 3495c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(bcts->dir); 34964ce85c8cSChuck Lever /* Upshifting from TCP to RDMA is not supported */ 3497c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 3498bac966d6SJ. Bruce Fields return 0; 34991d1bc8f2SJ. Bruce Fields } 35001d1bc8f2SJ. Bruce Fields 3501695e12f8SBenny Halevy static __be32 3502b37ad28bSAl Viro nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_close *close) 35031da177e4SLinus Torvalds { 3504d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 3505d0a381ddSJ. Bruce Fields 3506bac966d6SJ. Bruce Fields return nfsd4_encode_stateid(xdr, &close->cl_stateid); 35071da177e4SLinus Torvalds } 35081da177e4SLinus Torvalds 35091da177e4SLinus Torvalds 3510695e12f8SBenny Halevy static __be32 3511b37ad28bSAl Viro nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_commit *commit) 35121da177e4SLinus Torvalds { 3513d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 3514bc749ca4SJ. Bruce Fields __be32 *p; 35151da177e4SLinus Torvalds 3516d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE); 3517d0a381ddSJ. Bruce Fields if (!p) 3518d0a381ddSJ. Bruce Fields return nfserr_resource; 35190c0c267bSJ. Bruce Fields p = xdr_encode_opaque_fixed(p, commit->co_verf.data, 35200c0c267bSJ. Bruce Fields NFS4_VERIFIER_SIZE); 3521bac966d6SJ. Bruce Fields return 0; 35221da177e4SLinus Torvalds } 35231da177e4SLinus Torvalds 3524695e12f8SBenny Halevy static __be32 3525b37ad28bSAl Viro nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_create *create) 35261da177e4SLinus Torvalds { 3527d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 3528bc749ca4SJ. Bruce Fields __be32 *p; 35291da177e4SLinus Torvalds 353075976de6SKinglong Mee p = xdr_reserve_space(xdr, 20); 3531d0a381ddSJ. Bruce Fields if (!p) 3532d0a381ddSJ. Bruce Fields return nfserr_resource; 353375976de6SKinglong Mee encode_cinfo(p, &create->cr_cinfo); 3534b96811cdSTrond Myklebust return nfsd4_encode_bitmap(xdr, create->cr_bmval[0], 353575976de6SKinglong Mee create->cr_bmval[1], create->cr_bmval[2]); 35361da177e4SLinus Torvalds } 35371da177e4SLinus Torvalds 3538b37ad28bSAl Viro static __be32 3539b37ad28bSAl Viro nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_getattr *getattr) 35401da177e4SLinus Torvalds { 35411da177e4SLinus Torvalds struct svc_fh *fhp = getattr->ga_fhp; 3542d5184658SJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 35431da177e4SLinus Torvalds 3544bac966d6SJ. Bruce Fields return nfsd4_encode_fattr(xdr, fhp, fhp->fh_export, fhp->fh_dentry, 3545bac966d6SJ. Bruce Fields getattr->ga_bmval, resp->rqstp, 0); 35461da177e4SLinus Torvalds } 35471da177e4SLinus Torvalds 3548695e12f8SBenny Halevy static __be32 3549695e12f8SBenny Halevy nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh **fhpp) 35501da177e4SLinus Torvalds { 3551d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 3552695e12f8SBenny Halevy struct svc_fh *fhp = *fhpp; 35531da177e4SLinus Torvalds unsigned int len; 3554bc749ca4SJ. Bruce Fields __be32 *p; 35551da177e4SLinus Torvalds 35561da177e4SLinus Torvalds len = fhp->fh_handle.fh_size; 3557d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, len + 4); 3558d0a381ddSJ. Bruce Fields if (!p) 3559d0a381ddSJ. Bruce Fields return nfserr_resource; 35600c0c267bSJ. Bruce Fields p = xdr_encode_opaque(p, &fhp->fh_handle.fh_base, len); 3561bac966d6SJ. Bruce Fields return 0; 35621da177e4SLinus Torvalds } 35631da177e4SLinus Torvalds 35641da177e4SLinus Torvalds /* 35651da177e4SLinus Torvalds * Including all fields other than the name, a LOCK4denied structure requires 35661da177e4SLinus Torvalds * 8(clientid) + 4(namelen) + 8(offset) + 8(length) + 4(type) = 32 bytes. 35671da177e4SLinus Torvalds */ 3568d0a381ddSJ. Bruce Fields static __be32 3569d0a381ddSJ. Bruce Fields nfsd4_encode_lock_denied(struct xdr_stream *xdr, struct nfsd4_lock_denied *ld) 35701da177e4SLinus Torvalds { 35717c13f344SJ. Bruce Fields struct xdr_netobj *conf = &ld->ld_owner; 3572bc749ca4SJ. Bruce Fields __be32 *p; 35731da177e4SLinus Torvalds 35748c7424cfSJ. Bruce Fields again: 3575d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 32 + XDR_LEN(conf->len)); 35768c7424cfSJ. Bruce Fields if (!p) { 35778c7424cfSJ. Bruce Fields /* 35788c7424cfSJ. Bruce Fields * Don't fail to return the result just because we can't 35798c7424cfSJ. Bruce Fields * return the conflicting open: 35808c7424cfSJ. Bruce Fields */ 35818c7424cfSJ. Bruce Fields if (conf->len) { 3582f98bac5aSKinglong Mee kfree(conf->data); 35838c7424cfSJ. Bruce Fields conf->len = 0; 35848c7424cfSJ. Bruce Fields conf->data = NULL; 35858c7424cfSJ. Bruce Fields goto again; 35868c7424cfSJ. Bruce Fields } 3587d0a381ddSJ. Bruce Fields return nfserr_resource; 35888c7424cfSJ. Bruce Fields } 3589b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, ld->ld_start); 3590b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, ld->ld_length); 3591c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(ld->ld_type); 35927c13f344SJ. Bruce Fields if (conf->len) { 35930c0c267bSJ. Bruce Fields p = xdr_encode_opaque_fixed(p, &ld->ld_clientid, 8); 35940c0c267bSJ. Bruce Fields p = xdr_encode_opaque(p, conf->data, conf->len); 3595f98bac5aSKinglong Mee kfree(conf->data); 35961da177e4SLinus Torvalds } else { /* non - nfsv4 lock in conflict, no clientid nor owner */ 3597b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, (u64)0); /* clientid */ 3598c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); /* length of owner name */ 35991da177e4SLinus Torvalds } 3600d0a381ddSJ. Bruce Fields return nfserr_denied; 36011da177e4SLinus Torvalds } 36021da177e4SLinus Torvalds 3603695e12f8SBenny Halevy static __be32 3604b37ad28bSAl Viro nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lock *lock) 36051da177e4SLinus Torvalds { 3606d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 3607d0a381ddSJ. Bruce Fields 3608e2f282b9SBenny Halevy if (!nfserr) 3609d0a381ddSJ. Bruce Fields nfserr = nfsd4_encode_stateid(xdr, &lock->lk_resp_stateid); 3610e2f282b9SBenny Halevy else if (nfserr == nfserr_denied) 3611d0a381ddSJ. Bruce Fields nfserr = nfsd4_encode_lock_denied(xdr, &lock->lk_denied); 3612f98bac5aSKinglong Mee 3613695e12f8SBenny Halevy return nfserr; 36141da177e4SLinus Torvalds } 36151da177e4SLinus Torvalds 3616695e12f8SBenny Halevy static __be32 3617b37ad28bSAl Viro nfsd4_encode_lockt(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lockt *lockt) 36181da177e4SLinus Torvalds { 3619d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 3620d0a381ddSJ. Bruce Fields 36211da177e4SLinus Torvalds if (nfserr == nfserr_denied) 3622d0a381ddSJ. Bruce Fields nfsd4_encode_lock_denied(xdr, &lockt->lt_denied); 3623695e12f8SBenny Halevy return nfserr; 36241da177e4SLinus Torvalds } 36251da177e4SLinus Torvalds 3626695e12f8SBenny Halevy static __be32 3627b37ad28bSAl Viro nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_locku *locku) 36281da177e4SLinus Torvalds { 3629d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 3630d0a381ddSJ. Bruce Fields 3631bac966d6SJ. Bruce Fields return nfsd4_encode_stateid(xdr, &locku->lu_stateid); 36321da177e4SLinus Torvalds } 36331da177e4SLinus Torvalds 36341da177e4SLinus Torvalds 3635695e12f8SBenny Halevy static __be32 3636b37ad28bSAl Viro nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_link *link) 36371da177e4SLinus Torvalds { 3638d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 3639bc749ca4SJ. Bruce Fields __be32 *p; 36401da177e4SLinus Torvalds 3641d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 20); 3642d0a381ddSJ. Bruce Fields if (!p) 3643d0a381ddSJ. Bruce Fields return nfserr_resource; 3644d05d5744SJ. Bruce Fields p = encode_cinfo(p, &link->li_cinfo); 3645bac966d6SJ. Bruce Fields return 0; 36461da177e4SLinus Torvalds } 36471da177e4SLinus Torvalds 36481da177e4SLinus Torvalds 3649695e12f8SBenny Halevy static __be32 3650b37ad28bSAl Viro nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open *open) 36511da177e4SLinus Torvalds { 3652d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 3653bc749ca4SJ. Bruce Fields __be32 *p; 36541da177e4SLinus Torvalds 3655d0a381ddSJ. Bruce Fields nfserr = nfsd4_encode_stateid(xdr, &open->op_stateid); 3656d0a381ddSJ. Bruce Fields if (nfserr) 3657bac966d6SJ. Bruce Fields return nfserr; 365875976de6SKinglong Mee p = xdr_reserve_space(xdr, 24); 3659d0a381ddSJ. Bruce Fields if (!p) 3660d0a381ddSJ. Bruce Fields return nfserr_resource; 3661d05d5744SJ. Bruce Fields p = encode_cinfo(p, &open->op_cinfo); 3662c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(open->op_rflags); 36631da177e4SLinus Torvalds 366475976de6SKinglong Mee nfserr = nfsd4_encode_bitmap(xdr, open->op_bmval[0], open->op_bmval[1], 366575976de6SKinglong Mee open->op_bmval[2]); 366675976de6SKinglong Mee if (nfserr) 3667bac966d6SJ. Bruce Fields return nfserr; 366875976de6SKinglong Mee 366975976de6SKinglong Mee p = xdr_reserve_space(xdr, 4); 367075976de6SKinglong Mee if (!p) 367175976de6SKinglong Mee return nfserr_resource; 367275976de6SKinglong Mee 367375976de6SKinglong Mee *p++ = cpu_to_be32(open->op_delegate_type); 36741da177e4SLinus Torvalds switch (open->op_delegate_type) { 36751da177e4SLinus Torvalds case NFS4_OPEN_DELEGATE_NONE: 36761da177e4SLinus Torvalds break; 36771da177e4SLinus Torvalds case NFS4_OPEN_DELEGATE_READ: 3678d0a381ddSJ. Bruce Fields nfserr = nfsd4_encode_stateid(xdr, &open->op_delegate_stateid); 3679d0a381ddSJ. Bruce Fields if (nfserr) 3680d0a381ddSJ. Bruce Fields return nfserr; 3681d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 20); 3682d0a381ddSJ. Bruce Fields if (!p) 3683d0a381ddSJ. Bruce Fields return nfserr_resource; 3684c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(open->op_recall); 36851da177e4SLinus Torvalds 36861da177e4SLinus Torvalds /* 36871da177e4SLinus Torvalds * TODO: ACE's in delegations 36881da177e4SLinus Torvalds */ 3689c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE); 3690c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 3691c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 3692c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); /* XXX: is NULL principal ok? */ 36931da177e4SLinus Torvalds break; 36941da177e4SLinus Torvalds case NFS4_OPEN_DELEGATE_WRITE: 3695d0a381ddSJ. Bruce Fields nfserr = nfsd4_encode_stateid(xdr, &open->op_delegate_stateid); 3696d0a381ddSJ. Bruce Fields if (nfserr) 3697d0a381ddSJ. Bruce Fields return nfserr; 3698d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 32); 3699d0a381ddSJ. Bruce Fields if (!p) 3700d0a381ddSJ. Bruce Fields return nfserr_resource; 3701c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 37021da177e4SLinus Torvalds 37031da177e4SLinus Torvalds /* 37041da177e4SLinus Torvalds * TODO: space_limit's in delegations 37051da177e4SLinus Torvalds */ 3706c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(NFS4_LIMIT_SIZE); 3707c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(~(u32)0); 3708c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(~(u32)0); 37091da177e4SLinus Torvalds 37101da177e4SLinus Torvalds /* 37111da177e4SLinus Torvalds * TODO: ACE's in delegations 37121da177e4SLinus Torvalds */ 3713c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE); 3714c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 3715c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 3716c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); /* XXX: is NULL principal ok? */ 37171da177e4SLinus Torvalds break; 3718d24433cdSBenny Halevy case NFS4_OPEN_DELEGATE_NONE_EXT: /* 4.1 */ 3719d24433cdSBenny Halevy switch (open->op_why_no_deleg) { 3720d24433cdSBenny Halevy case WND4_CONTENTION: 3721d24433cdSBenny Halevy case WND4_RESOURCE: 3722d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 3723d0a381ddSJ. Bruce Fields if (!p) 3724d0a381ddSJ. Bruce Fields return nfserr_resource; 3725c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(open->op_why_no_deleg); 3726c373b0a4SJ. Bruce Fields /* deleg signaling not supported yet: */ 3727c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 3728d24433cdSBenny Halevy break; 3729d24433cdSBenny Halevy default: 3730d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 3731d0a381ddSJ. Bruce Fields if (!p) 3732d0a381ddSJ. Bruce Fields return nfserr_resource; 3733c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(open->op_why_no_deleg); 3734d24433cdSBenny Halevy } 3735d24433cdSBenny Halevy break; 37361da177e4SLinus Torvalds default: 37371da177e4SLinus Torvalds BUG(); 37381da177e4SLinus Torvalds } 37391da177e4SLinus Torvalds /* XXX save filehandle here */ 3740bac966d6SJ. Bruce Fields return 0; 37411da177e4SLinus Torvalds } 37421da177e4SLinus Torvalds 3743695e12f8SBenny Halevy static __be32 3744b37ad28bSAl Viro nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_confirm *oc) 37451da177e4SLinus Torvalds { 3746d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 3747d0a381ddSJ. Bruce Fields 3748bac966d6SJ. Bruce Fields return nfsd4_encode_stateid(xdr, &oc->oc_resp_stateid); 37491da177e4SLinus Torvalds } 37501da177e4SLinus Torvalds 3751695e12f8SBenny Halevy static __be32 3752b37ad28bSAl Viro nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_downgrade *od) 37531da177e4SLinus Torvalds { 3754d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 3755d0a381ddSJ. Bruce Fields 3756bac966d6SJ. Bruce Fields return nfsd4_encode_stateid(xdr, &od->od_stateid); 37571da177e4SLinus Torvalds } 37581da177e4SLinus Torvalds 3759dc97618dSJ. Bruce Fields static __be32 nfsd4_encode_splice_read( 3760dc97618dSJ. Bruce Fields struct nfsd4_compoundres *resp, 3761dc97618dSJ. Bruce Fields struct nfsd4_read *read, 3762dc97618dSJ. Bruce Fields struct file *file, unsigned long maxcount) 37631da177e4SLinus Torvalds { 3764dc97618dSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 376534a78b48SJ. Bruce Fields struct xdr_buf *buf = xdr->buf; 376676e5492bSChuck Lever int status, space_left; 3767dc97618dSJ. Bruce Fields u32 eof; 3768dc97618dSJ. Bruce Fields __be32 nfserr; 3769fec25fa4SJ. Bruce Fields __be32 *p = xdr->p - 2; 3770dc97618dSJ. Bruce Fields 3771d5d5c304SKinglong Mee /* Make sure there will be room for padding if needed */ 3772d5d5c304SKinglong Mee if (xdr->end - xdr->p < 1) 3773dc97618dSJ. Bruce Fields return nfserr_resource; 3774dc97618dSJ. Bruce Fields 377587c5942eSChuck Lever nfserr = nfsd_splice_read(read->rd_rqstp, read->rd_fhp, 377683a63072STrond Myklebust file, read->rd_offset, &maxcount, &eof); 377787c5942eSChuck Lever read->rd_length = maxcount; 377876e5492bSChuck Lever if (nfserr) 377976e5492bSChuck Lever goto out_err; 378076e5492bSChuck Lever status = svc_encode_result_payload(read->rd_rqstp, 378176e5492bSChuck Lever buf->head[0].iov_len, maxcount); 378276e5492bSChuck Lever if (status) { 378376e5492bSChuck Lever nfserr = nfserrno(status); 378476e5492bSChuck Lever goto out_err; 3785dc97618dSJ. Bruce Fields } 3786dc97618dSJ. Bruce Fields 3787fec25fa4SJ. Bruce Fields *(p++) = htonl(eof); 3788fec25fa4SJ. Bruce Fields *(p++) = htonl(maxcount); 3789dc97618dSJ. Bruce Fields 379034a78b48SJ. Bruce Fields buf->page_len = maxcount; 379134a78b48SJ. Bruce Fields buf->len += maxcount; 379215b23ef5SJ. Bruce Fields xdr->page_ptr += (buf->page_base + maxcount + PAGE_SIZE - 1) 379315b23ef5SJ. Bruce Fields / PAGE_SIZE; 3794dc97618dSJ. Bruce Fields 3795dc97618dSJ. Bruce Fields /* Use rest of head for padding and remaining ops: */ 379634a78b48SJ. Bruce Fields buf->tail[0].iov_base = xdr->p; 379734a78b48SJ. Bruce Fields buf->tail[0].iov_len = 0; 3798fec25fa4SJ. Bruce Fields xdr->iov = buf->tail; 3799dc97618dSJ. Bruce Fields if (maxcount&3) { 3800fec25fa4SJ. Bruce Fields int pad = 4 - (maxcount&3); 3801fec25fa4SJ. Bruce Fields 3802fec25fa4SJ. Bruce Fields *(xdr->p++) = 0; 3803fec25fa4SJ. Bruce Fields 380434a78b48SJ. Bruce Fields buf->tail[0].iov_base += maxcount&3; 3805fec25fa4SJ. Bruce Fields buf->tail[0].iov_len = pad; 3806fec25fa4SJ. Bruce Fields buf->len += pad; 3807dc97618dSJ. Bruce Fields } 3808dc97618dSJ. Bruce Fields 3809dc97618dSJ. Bruce Fields space_left = min_t(int, (void *)xdr->end - (void *)xdr->p, 381034a78b48SJ. Bruce Fields buf->buflen - buf->len); 381134a78b48SJ. Bruce Fields buf->buflen = buf->len + space_left; 3812dc97618dSJ. Bruce Fields xdr->end = (__be32 *)((void *)xdr->end + space_left); 3813dc97618dSJ. Bruce Fields 3814dc97618dSJ. Bruce Fields return 0; 381576e5492bSChuck Lever 381676e5492bSChuck Lever out_err: 381776e5492bSChuck Lever /* 381876e5492bSChuck Lever * nfsd_splice_actor may have already messed with the 381976e5492bSChuck Lever * page length; reset it so as not to confuse 382076e5492bSChuck Lever * xdr_truncate_encode in our caller. 382176e5492bSChuck Lever */ 382276e5492bSChuck Lever buf->page_len = 0; 382376e5492bSChuck Lever return nfserr; 3824dc97618dSJ. Bruce Fields } 3825dc97618dSJ. Bruce Fields 3826dc97618dSJ. Bruce Fields static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp, 3827dc97618dSJ. Bruce Fields struct nfsd4_read *read, 3828dc97618dSJ. Bruce Fields struct file *file, unsigned long maxcount) 3829dc97618dSJ. Bruce Fields { 3830dc97618dSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 38311da177e4SLinus Torvalds u32 eof; 3832dc97618dSJ. Bruce Fields int starting_len = xdr->buf->len - 8; 3833dc97618dSJ. Bruce Fields __be32 nfserr; 3834dc97618dSJ. Bruce Fields __be32 tmp; 3835b0420980SJ. Bruce Fields int pad; 38361da177e4SLinus Torvalds 3837403217f3SAnna Schumaker read->rd_vlen = xdr_reserve_space_vec(xdr, resp->rqstp->rq_vec, maxcount); 3838403217f3SAnna Schumaker if (read->rd_vlen < 0) 3839403217f3SAnna Schumaker return nfserr_resource; 38401da177e4SLinus Torvalds 384187c5942eSChuck Lever nfserr = nfsd_readv(resp->rqstp, read->rd_fhp, file, read->rd_offset, 384283a63072STrond Myklebust resp->rqstp->rq_vec, read->rd_vlen, &maxcount, 384383a63072STrond Myklebust &eof); 384487c5942eSChuck Lever read->rd_length = maxcount; 3845dc97618dSJ. Bruce Fields if (nfserr) 38461da177e4SLinus Torvalds return nfserr; 384703493bcaSChuck Lever if (svc_encode_result_payload(resp->rqstp, starting_len + 8, maxcount)) 384841205539SChuck Lever return nfserr_io; 38497dcf4ab9SChuck Lever xdr_truncate_encode(xdr, starting_len + 8 + xdr_align_size(maxcount)); 3850dc97618dSJ. Bruce Fields 3851dc97618dSJ. Bruce Fields tmp = htonl(eof); 3852dc97618dSJ. Bruce Fields write_bytes_to_xdr_buf(xdr->buf, starting_len , &tmp, 4); 3853dc97618dSJ. Bruce Fields tmp = htonl(maxcount); 3854dc97618dSJ. Bruce Fields write_bytes_to_xdr_buf(xdr->buf, starting_len + 4, &tmp, 4); 3855dc97618dSJ. Bruce Fields 38567dcf4ab9SChuck Lever tmp = xdr_zero; 3857b0420980SJ. Bruce Fields pad = (maxcount&3) ? 4 - (maxcount&3) : 0; 3858b0420980SJ. Bruce Fields write_bytes_to_xdr_buf(xdr->buf, starting_len + 8 + maxcount, 38597dcf4ab9SChuck Lever &tmp, pad); 38601da177e4SLinus Torvalds return 0; 3861dc97618dSJ. Bruce Fields 3862dc97618dSJ. Bruce Fields } 3863dc97618dSJ. Bruce Fields 3864dc97618dSJ. Bruce Fields static __be32 3865dc97618dSJ. Bruce Fields nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr, 3866dc97618dSJ. Bruce Fields struct nfsd4_read *read) 3867dc97618dSJ. Bruce Fields { 3868dc97618dSJ. Bruce Fields unsigned long maxcount; 3869dc97618dSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 38705c4583b2SJeff Layton struct file *file; 3871dc97618dSJ. Bruce Fields int starting_len = xdr->buf->len; 3872dc97618dSJ. Bruce Fields __be32 *p; 3873dc97618dSJ. Bruce Fields 38745c4583b2SJeff Layton if (nfserr) 38755c4583b2SJeff Layton return nfserr; 38765c4583b2SJeff Layton file = read->rd_nf->nf_file; 38775c4583b2SJeff Layton 3878dc97618dSJ. Bruce Fields p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */ 3879dc97618dSJ. Bruce Fields if (!p) { 3880779fb0f3SJeff Layton WARN_ON_ONCE(test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags)); 3881bac966d6SJ. Bruce Fields return nfserr_resource; 3882dc97618dSJ. Bruce Fields } 388368e8bb03SChristoph Hellwig if (resp->xdr.buf->page_len && 388468e8bb03SChristoph Hellwig test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags)) { 3885b0420980SJ. Bruce Fields WARN_ON_ONCE(1); 3886bac966d6SJ. Bruce Fields return nfserr_resource; 3887dc97618dSJ. Bruce Fields } 3888dc97618dSJ. Bruce Fields xdr_commit_encode(xdr); 3889dc97618dSJ. Bruce Fields 3890dc97618dSJ. Bruce Fields maxcount = svc_max_payload(resp->rqstp); 389168e8bb03SChristoph Hellwig maxcount = min_t(unsigned long, maxcount, 389268e8bb03SChristoph Hellwig (xdr->buf->buflen - xdr->buf->len)); 38933c7aa15dSKinglong Mee maxcount = min_t(unsigned long, maxcount, read->rd_length); 3894dc97618dSJ. Bruce Fields 389568e8bb03SChristoph Hellwig if (file->f_op->splice_read && 389668e8bb03SChristoph Hellwig test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags)) 389796bcad50SChristoph Hellwig nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount); 3898dc97618dSJ. Bruce Fields else 389996bcad50SChristoph Hellwig nfserr = nfsd4_encode_readv(resp, read, file, maxcount); 3900dc97618dSJ. Bruce Fields 390196bcad50SChristoph Hellwig if (nfserr) 3902dc97618dSJ. Bruce Fields xdr_truncate_encode(xdr, starting_len); 390396bcad50SChristoph Hellwig 390496bcad50SChristoph Hellwig return nfserr; 39051da177e4SLinus Torvalds } 39061da177e4SLinus Torvalds 3907b37ad28bSAl Viro static __be32 3908b37ad28bSAl Viro nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readlink *readlink) 39091da177e4SLinus Torvalds { 39101da177e4SLinus Torvalds int maxcount; 3911476a7b1fSJ. Bruce Fields __be32 wire_count; 3912476a7b1fSJ. Bruce Fields int zero = 0; 3913ddd1ea56SJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 39141fcea5b2SJ. Bruce Fields int length_offset = xdr->buf->len; 391576e5492bSChuck Lever int status; 3916bc749ca4SJ. Bruce Fields __be32 *p; 39171da177e4SLinus Torvalds 39182825a7f9SJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 39192825a7f9SJ. Bruce Fields if (!p) 39202825a7f9SJ. Bruce Fields return nfserr_resource; 39211da177e4SLinus Torvalds maxcount = PAGE_SIZE; 3922d0a381ddSJ. Bruce Fields 3923476a7b1fSJ. Bruce Fields p = xdr_reserve_space(xdr, maxcount); 3924476a7b1fSJ. Bruce Fields if (!p) 39254e21ac4bSJ. Bruce Fields return nfserr_resource; 39261da177e4SLinus Torvalds /* 3927fd4a0edfSMiklos Szeredi * XXX: By default, vfs_readlink() will truncate symlinks if they 3928fd4a0edfSMiklos Szeredi * would overflow the buffer. Is this kosher in NFSv4? If not, one 3929fd4a0edfSMiklos Szeredi * easy fix is: if vfs_readlink() precisely fills the buffer, assume 3930fd4a0edfSMiklos Szeredi * that truncation occurred, and return NFS4ERR_RESOURCE. 39311da177e4SLinus Torvalds */ 3932476a7b1fSJ. Bruce Fields nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, 3933476a7b1fSJ. Bruce Fields (char *)p, &maxcount); 39341da177e4SLinus Torvalds if (nfserr == nfserr_isdir) 3935d3f627c8SJ. Bruce Fields nfserr = nfserr_inval; 393676e5492bSChuck Lever if (nfserr) 393776e5492bSChuck Lever goto out_err; 393876e5492bSChuck Lever status = svc_encode_result_payload(readlink->rl_rqstp, length_offset, 393976e5492bSChuck Lever maxcount); 394076e5492bSChuck Lever if (status) { 394176e5492bSChuck Lever nfserr = nfserrno(status); 394276e5492bSChuck Lever goto out_err; 3943d3f627c8SJ. Bruce Fields } 39441da177e4SLinus Torvalds 3945476a7b1fSJ. Bruce Fields wire_count = htonl(maxcount); 3946476a7b1fSJ. Bruce Fields write_bytes_to_xdr_buf(xdr->buf, length_offset, &wire_count, 4); 394769bbd9c7SAvi Kivity xdr_truncate_encode(xdr, length_offset + 4 + ALIGN(maxcount, 4)); 3948476a7b1fSJ. Bruce Fields if (maxcount & 3) 3949476a7b1fSJ. Bruce Fields write_bytes_to_xdr_buf(xdr->buf, length_offset + 4 + maxcount, 3950476a7b1fSJ. Bruce Fields &zero, 4 - (maxcount&3)); 39511da177e4SLinus Torvalds return 0; 395276e5492bSChuck Lever 395376e5492bSChuck Lever out_err: 395476e5492bSChuck Lever xdr_truncate_encode(xdr, length_offset); 395576e5492bSChuck Lever return nfserr; 39561da177e4SLinus Torvalds } 39571da177e4SLinus Torvalds 3958b37ad28bSAl Viro static __be32 3959b37ad28bSAl Viro nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readdir *readdir) 39601da177e4SLinus Torvalds { 39611da177e4SLinus Torvalds int maxcount; 3962561f0ed4SJ. Bruce Fields int bytes_left; 39631da177e4SLinus Torvalds loff_t offset; 3964561f0ed4SJ. Bruce Fields __be64 wire_offset; 3965ddd1ea56SJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 39661fcea5b2SJ. Bruce Fields int starting_len = xdr->buf->len; 3967bc749ca4SJ. Bruce Fields __be32 *p; 39681da177e4SLinus Torvalds 3969d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE); 3970d0a381ddSJ. Bruce Fields if (!p) 3971d0a381ddSJ. Bruce Fields return nfserr_resource; 39721da177e4SLinus Torvalds 39731da177e4SLinus Torvalds /* XXX: Following NFSv3, we ignore the READDIR verifier for now. */ 3974c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 3975c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 39764aea24b2SJ. Bruce Fields resp->xdr.buf->head[0].iov_len = ((char *)resp->xdr.p) 39774aea24b2SJ. Bruce Fields - (char *)resp->xdr.buf->head[0].iov_base; 39781da177e4SLinus Torvalds 39791da177e4SLinus Torvalds /* 3980561f0ed4SJ. Bruce Fields * Number of bytes left for directory entries allowing for the 3981561f0ed4SJ. Bruce Fields * final 8 bytes of the readdir and a following failed op: 39821da177e4SLinus Torvalds */ 3983561f0ed4SJ. Bruce Fields bytes_left = xdr->buf->buflen - xdr->buf->len 3984561f0ed4SJ. Bruce Fields - COMPOUND_ERR_SLACK_SPACE - 8; 3985561f0ed4SJ. Bruce Fields if (bytes_left < 0) { 3986561f0ed4SJ. Bruce Fields nfserr = nfserr_resource; 3987561f0ed4SJ. Bruce Fields goto err_no_verf; 3988561f0ed4SJ. Bruce Fields } 39899c2ece6eSScott Mayhew maxcount = svc_max_payload(resp->rqstp); 39909c2ece6eSScott Mayhew maxcount = min_t(u32, readdir->rd_maxcount, maxcount); 3991561f0ed4SJ. Bruce Fields /* 3992561f0ed4SJ. Bruce Fields * Note the rfc defines rd_maxcount as the size of the 3993561f0ed4SJ. Bruce Fields * READDIR4resok structure, which includes the verifier above 3994561f0ed4SJ. Bruce Fields * and the 8 bytes encoded at the end of this function: 3995561f0ed4SJ. Bruce Fields */ 3996561f0ed4SJ. Bruce Fields if (maxcount < 16) { 39971da177e4SLinus Torvalds nfserr = nfserr_toosmall; 39981da177e4SLinus Torvalds goto err_no_verf; 39991da177e4SLinus Torvalds } 4000561f0ed4SJ. Bruce Fields maxcount = min_t(int, maxcount-16, bytes_left); 40011da177e4SLinus Torvalds 4002aee37764SJ. Bruce Fields /* RFC 3530 14.2.24 allows us to ignore dircount when it's 0: */ 4003aee37764SJ. Bruce Fields if (!readdir->rd_dircount) 40049c2ece6eSScott Mayhew readdir->rd_dircount = svc_max_payload(resp->rqstp); 4005aee37764SJ. Bruce Fields 4006561f0ed4SJ. Bruce Fields readdir->xdr = xdr; 4007561f0ed4SJ. Bruce Fields readdir->rd_maxcount = maxcount; 40081da177e4SLinus Torvalds readdir->common.err = 0; 4009561f0ed4SJ. Bruce Fields readdir->cookie_offset = 0; 40101da177e4SLinus Torvalds 40111da177e4SLinus Torvalds offset = readdir->rd_cookie; 40121da177e4SLinus Torvalds nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp, 40131da177e4SLinus Torvalds &offset, 40141da177e4SLinus Torvalds &readdir->common, nfsd4_encode_dirent); 40151da177e4SLinus Torvalds if (nfserr == nfs_ok && 40161da177e4SLinus Torvalds readdir->common.err == nfserr_toosmall && 4017561f0ed4SJ. Bruce Fields xdr->buf->len == starting_len + 8) { 4018561f0ed4SJ. Bruce Fields /* nothing encoded; which limit did we hit?: */ 4019561f0ed4SJ. Bruce Fields if (maxcount - 16 < bytes_left) 4020561f0ed4SJ. Bruce Fields /* It was the fault of rd_maxcount: */ 40211da177e4SLinus Torvalds nfserr = nfserr_toosmall; 4022561f0ed4SJ. Bruce Fields else 4023561f0ed4SJ. Bruce Fields /* We ran out of buffer space: */ 4024561f0ed4SJ. Bruce Fields nfserr = nfserr_resource; 4025561f0ed4SJ. Bruce Fields } 40261da177e4SLinus Torvalds if (nfserr) 40271da177e4SLinus Torvalds goto err_no_verf; 40281da177e4SLinus Torvalds 4029561f0ed4SJ. Bruce Fields if (readdir->cookie_offset) { 4030561f0ed4SJ. Bruce Fields wire_offset = cpu_to_be64(offset); 4031561f0ed4SJ. Bruce Fields write_bytes_to_xdr_buf(xdr->buf, readdir->cookie_offset, 4032561f0ed4SJ. Bruce Fields &wire_offset, 8); 4033561f0ed4SJ. Bruce Fields } 40341da177e4SLinus Torvalds 4035561f0ed4SJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 4036561f0ed4SJ. Bruce Fields if (!p) { 4037561f0ed4SJ. Bruce Fields WARN_ON_ONCE(1); 4038561f0ed4SJ. Bruce Fields goto err_no_verf; 4039561f0ed4SJ. Bruce Fields } 40401da177e4SLinus Torvalds *p++ = 0; /* no more entries */ 40411da177e4SLinus Torvalds *p++ = htonl(readdir->common.err == nfserr_eof); 40421da177e4SLinus Torvalds 40431da177e4SLinus Torvalds return 0; 40441da177e4SLinus Torvalds err_no_verf: 40451fcea5b2SJ. Bruce Fields xdr_truncate_encode(xdr, starting_len); 40461da177e4SLinus Torvalds return nfserr; 40471da177e4SLinus Torvalds } 40481da177e4SLinus Torvalds 4049695e12f8SBenny Halevy static __be32 4050b37ad28bSAl Viro nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_remove *remove) 40511da177e4SLinus Torvalds { 4052d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 4053bc749ca4SJ. Bruce Fields __be32 *p; 40541da177e4SLinus Torvalds 4055d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 20); 4056d0a381ddSJ. Bruce Fields if (!p) 4057d0a381ddSJ. Bruce Fields return nfserr_resource; 4058d05d5744SJ. Bruce Fields p = encode_cinfo(p, &remove->rm_cinfo); 4059bac966d6SJ. Bruce Fields return 0; 40601da177e4SLinus Torvalds } 40611da177e4SLinus Torvalds 4062695e12f8SBenny Halevy static __be32 4063b37ad28bSAl Viro nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_rename *rename) 40641da177e4SLinus Torvalds { 4065d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 4066bc749ca4SJ. Bruce Fields __be32 *p; 40671da177e4SLinus Torvalds 4068d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 40); 4069d0a381ddSJ. Bruce Fields if (!p) 4070d0a381ddSJ. Bruce Fields return nfserr_resource; 4071d05d5744SJ. Bruce Fields p = encode_cinfo(p, &rename->rn_sinfo); 4072d05d5744SJ. Bruce Fields p = encode_cinfo(p, &rename->rn_tinfo); 4073bac966d6SJ. Bruce Fields return 0; 40741da177e4SLinus Torvalds } 40751da177e4SLinus Torvalds 4076695e12f8SBenny Halevy static __be32 4077bac966d6SJ. Bruce Fields nfsd4_do_encode_secinfo(struct xdr_stream *xdr, struct svc_export *exp) 4078dcb488a3SAndy Adamson { 4079676e4ebdSChuck Lever u32 i, nflavs, supported; 40804796f457SJ. Bruce Fields struct exp_flavor_info *flavs; 40814796f457SJ. Bruce Fields struct exp_flavor_info def_flavs[2]; 4082676e4ebdSChuck Lever __be32 *p, *flavorsp; 4083676e4ebdSChuck Lever static bool report = true; 4084dcb488a3SAndy Adamson 40854796f457SJ. Bruce Fields if (exp->ex_nflavors) { 40864796f457SJ. Bruce Fields flavs = exp->ex_flavors; 40874796f457SJ. Bruce Fields nflavs = exp->ex_nflavors; 40884796f457SJ. Bruce Fields } else { /* Handling of some defaults in absence of real secinfo: */ 40894796f457SJ. Bruce Fields flavs = def_flavs; 40904796f457SJ. Bruce Fields if (exp->ex_client->flavour->flavour == RPC_AUTH_UNIX) { 40914796f457SJ. Bruce Fields nflavs = 2; 40924796f457SJ. Bruce Fields flavs[0].pseudoflavor = RPC_AUTH_UNIX; 40934796f457SJ. Bruce Fields flavs[1].pseudoflavor = RPC_AUTH_NULL; 40944796f457SJ. Bruce Fields } else if (exp->ex_client->flavour->flavour == RPC_AUTH_GSS) { 40954796f457SJ. Bruce Fields nflavs = 1; 40964796f457SJ. Bruce Fields flavs[0].pseudoflavor 40974796f457SJ. Bruce Fields = svcauth_gss_flavor(exp->ex_client); 40984796f457SJ. Bruce Fields } else { 40994796f457SJ. Bruce Fields nflavs = 1; 41004796f457SJ. Bruce Fields flavs[0].pseudoflavor 41014796f457SJ. Bruce Fields = exp->ex_client->flavour->flavour; 41024796f457SJ. Bruce Fields } 41034796f457SJ. Bruce Fields } 41044796f457SJ. Bruce Fields 4105676e4ebdSChuck Lever supported = 0; 4106d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 4107d0a381ddSJ. Bruce Fields if (!p) 4108bac966d6SJ. Bruce Fields return nfserr_resource; 4109676e4ebdSChuck Lever flavorsp = p++; /* to be backfilled later */ 4110676e4ebdSChuck Lever 41114796f457SJ. Bruce Fields for (i = 0; i < nflavs; i++) { 4112676e4ebdSChuck Lever rpc_authflavor_t pf = flavs[i].pseudoflavor; 4113a77c806fSChuck Lever struct rpcsec_gss_info info; 4114dcb488a3SAndy Adamson 4115676e4ebdSChuck Lever if (rpcauth_get_gssinfo(pf, &info) == 0) { 4116676e4ebdSChuck Lever supported++; 4117d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 4 + 4 + 4118d0a381ddSJ. Bruce Fields XDR_LEN(info.oid.len) + 4 + 4); 4119d0a381ddSJ. Bruce Fields if (!p) 4120bac966d6SJ. Bruce Fields return nfserr_resource; 4121c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(RPC_AUTH_GSS); 41220c0c267bSJ. Bruce Fields p = xdr_encode_opaque(p, info.oid.data, info.oid.len); 4123c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(info.qop); 4124c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(info.service); 4125676e4ebdSChuck Lever } else if (pf < RPC_AUTH_MAXFLAVOR) { 4126676e4ebdSChuck Lever supported++; 4127d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 4128d0a381ddSJ. Bruce Fields if (!p) 4129bac966d6SJ. Bruce Fields return nfserr_resource; 4130c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(pf); 4131676e4ebdSChuck Lever } else { 4132676e4ebdSChuck Lever if (report) 4133676e4ebdSChuck Lever pr_warn("NFS: SECINFO: security flavor %u " 4134676e4ebdSChuck Lever "is not supported\n", pf); 4135dcb488a3SAndy Adamson } 4136dcb488a3SAndy Adamson } 4137a77c806fSChuck Lever 4138676e4ebdSChuck Lever if (nflavs != supported) 4139676e4ebdSChuck Lever report = false; 4140676e4ebdSChuck Lever *flavorsp = htonl(supported); 4141bac966d6SJ. Bruce Fields return 0; 4142dcb488a3SAndy Adamson } 4143dcb488a3SAndy Adamson 414422b6dee8SMi Jinlong static __be32 414522b6dee8SMi Jinlong nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr, 414622b6dee8SMi Jinlong struct nfsd4_secinfo *secinfo) 414722b6dee8SMi Jinlong { 4148d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 4149d0a381ddSJ. Bruce Fields 4150bac966d6SJ. Bruce Fields return nfsd4_do_encode_secinfo(xdr, secinfo->si_exp); 415122b6dee8SMi Jinlong } 415222b6dee8SMi Jinlong 415322b6dee8SMi Jinlong static __be32 415422b6dee8SMi Jinlong nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr, 415522b6dee8SMi Jinlong struct nfsd4_secinfo_no_name *secinfo) 415622b6dee8SMi Jinlong { 4157d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 4158d0a381ddSJ. Bruce Fields 4159bac966d6SJ. Bruce Fields return nfsd4_do_encode_secinfo(xdr, secinfo->sin_exp); 416022b6dee8SMi Jinlong } 416122b6dee8SMi Jinlong 41621da177e4SLinus Torvalds /* 41631da177e4SLinus Torvalds * The SETATTR encode routine is special -- it always encodes a bitmap, 41641da177e4SLinus Torvalds * regardless of the error status. 41651da177e4SLinus Torvalds */ 4166695e12f8SBenny Halevy static __be32 4167b37ad28bSAl Viro nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setattr *setattr) 41681da177e4SLinus Torvalds { 4169d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 4170bc749ca4SJ. Bruce Fields __be32 *p; 41711da177e4SLinus Torvalds 4172d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 16); 4173d0a381ddSJ. Bruce Fields if (!p) 4174d0a381ddSJ. Bruce Fields return nfserr_resource; 41751da177e4SLinus Torvalds if (nfserr) { 4176c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(3); 4177c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 4178c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 4179c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 41801da177e4SLinus Torvalds } 41811da177e4SLinus Torvalds else { 4182c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(3); 4183c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(setattr->sa_bmval[0]); 4184c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(setattr->sa_bmval[1]); 4185c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(setattr->sa_bmval[2]); 41861da177e4SLinus Torvalds } 4187695e12f8SBenny Halevy return nfserr; 41881da177e4SLinus Torvalds } 41891da177e4SLinus Torvalds 4190695e12f8SBenny Halevy static __be32 4191b37ad28bSAl Viro nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setclientid *scd) 41921da177e4SLinus Torvalds { 4193d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 4194bc749ca4SJ. Bruce Fields __be32 *p; 41951da177e4SLinus Torvalds 41961da177e4SLinus Torvalds if (!nfserr) { 4197d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 8 + NFS4_VERIFIER_SIZE); 4198d0a381ddSJ. Bruce Fields if (!p) 4199d0a381ddSJ. Bruce Fields return nfserr_resource; 42000c0c267bSJ. Bruce Fields p = xdr_encode_opaque_fixed(p, &scd->se_clientid, 8); 42010c0c267bSJ. Bruce Fields p = xdr_encode_opaque_fixed(p, &scd->se_confirm, 42020c0c267bSJ. Bruce Fields NFS4_VERIFIER_SIZE); 42031da177e4SLinus Torvalds } 42041da177e4SLinus Torvalds else if (nfserr == nfserr_clid_inuse) { 4205d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 4206d0a381ddSJ. Bruce Fields if (!p) 4207d0a381ddSJ. Bruce Fields return nfserr_resource; 4208c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 4209c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); 42101da177e4SLinus Torvalds } 4211695e12f8SBenny Halevy return nfserr; 42121da177e4SLinus Torvalds } 42131da177e4SLinus Torvalds 4214695e12f8SBenny Halevy static __be32 4215b37ad28bSAl Viro nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_write *write) 42161da177e4SLinus Torvalds { 4217d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 4218bc749ca4SJ. Bruce Fields __be32 *p; 42191da177e4SLinus Torvalds 4220d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 16); 4221d0a381ddSJ. Bruce Fields if (!p) 4222d0a381ddSJ. Bruce Fields return nfserr_resource; 4223c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(write->wr_bytes_written); 4224c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(write->wr_how_written); 42250c0c267bSJ. Bruce Fields p = xdr_encode_opaque_fixed(p, write->wr_verifier.data, 42260c0c267bSJ. Bruce Fields NFS4_VERIFIER_SIZE); 4227bac966d6SJ. Bruce Fields return 0; 42281da177e4SLinus Torvalds } 42291da177e4SLinus Torvalds 4230695e12f8SBenny Halevy static __be32 423157b7b43bSJ. Bruce Fields nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr, 42322db134ebSAndy Adamson struct nfsd4_exchange_id *exid) 42332db134ebSAndy Adamson { 4234d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 4235bc749ca4SJ. Bruce Fields __be32 *p; 42360733d213SAndy Adamson char *major_id; 42370733d213SAndy Adamson char *server_scope; 42380733d213SAndy Adamson int major_id_sz; 42390733d213SAndy Adamson int server_scope_sz; 42400733d213SAndy Adamson uint64_t minor_id = 0; 42417627d7dcSScott Mayhew struct nfsd_net *nn = net_generic(SVC_NET(resp->rqstp), nfsd_net_id); 42420733d213SAndy Adamson 42437627d7dcSScott Mayhew major_id = nn->nfsd_name; 42447627d7dcSScott Mayhew major_id_sz = strlen(nn->nfsd_name); 42457627d7dcSScott Mayhew server_scope = nn->nfsd_name; 42467627d7dcSScott Mayhew server_scope_sz = strlen(nn->nfsd_name); 42470733d213SAndy Adamson 4248d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 42490733d213SAndy Adamson 8 /* eir_clientid */ + 42500733d213SAndy Adamson 4 /* eir_sequenceid */ + 42510733d213SAndy Adamson 4 /* eir_flags */ + 4252a8bb84bcSKinglong Mee 4 /* spr_how */); 4253d0a381ddSJ. Bruce Fields if (!p) 4254d0a381ddSJ. Bruce Fields return nfserr_resource; 42550733d213SAndy Adamson 42560c0c267bSJ. Bruce Fields p = xdr_encode_opaque_fixed(p, &exid->clientid, 8); 4257c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(exid->seqid); 4258c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(exid->flags); 42590733d213SAndy Adamson 4260c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(exid->spa_how); 4261a8bb84bcSKinglong Mee 426257266a6eSJ. Bruce Fields switch (exid->spa_how) { 426357266a6eSJ. Bruce Fields case SP4_NONE: 426457266a6eSJ. Bruce Fields break; 426557266a6eSJ. Bruce Fields case SP4_MACH_CRED: 426657266a6eSJ. Bruce Fields /* spo_must_enforce bitmap: */ 4267bac966d6SJ. Bruce Fields nfserr = nfsd4_encode_bitmap(xdr, 4268ed941643SAndrew Elble exid->spo_must_enforce[0], 4269ed941643SAndrew Elble exid->spo_must_enforce[1], 4270ed941643SAndrew Elble exid->spo_must_enforce[2]); 4271bac966d6SJ. Bruce Fields if (nfserr) 4272bac966d6SJ. Bruce Fields return nfserr; 4273ed941643SAndrew Elble /* spo_must_allow bitmap: */ 4274bac966d6SJ. Bruce Fields nfserr = nfsd4_encode_bitmap(xdr, 4275ed941643SAndrew Elble exid->spo_must_allow[0], 4276ed941643SAndrew Elble exid->spo_must_allow[1], 4277ed941643SAndrew Elble exid->spo_must_allow[2]); 4278bac966d6SJ. Bruce Fields if (nfserr) 4279bac966d6SJ. Bruce Fields return nfserr; 428057266a6eSJ. Bruce Fields break; 428157266a6eSJ. Bruce Fields default: 428257266a6eSJ. Bruce Fields WARN_ON_ONCE(1); 428357266a6eSJ. Bruce Fields } 42840733d213SAndy Adamson 4285d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 4286a8bb84bcSKinglong Mee 8 /* so_minor_id */ + 4287a8bb84bcSKinglong Mee 4 /* so_major_id.len */ + 4288a8bb84bcSKinglong Mee (XDR_QUADLEN(major_id_sz) * 4) + 4289a8bb84bcSKinglong Mee 4 /* eir_server_scope.len */ + 4290a8bb84bcSKinglong Mee (XDR_QUADLEN(server_scope_sz) * 4) + 4291a8bb84bcSKinglong Mee 4 /* eir_server_impl_id.count (0) */); 4292d0a381ddSJ. Bruce Fields if (!p) 4293d0a381ddSJ. Bruce Fields return nfserr_resource; 4294a8bb84bcSKinglong Mee 42950733d213SAndy Adamson /* The server_owner struct */ 4296b64c7f3bSJ. Bruce Fields p = xdr_encode_hyper(p, minor_id); /* Minor id */ 42970733d213SAndy Adamson /* major id */ 42980c0c267bSJ. Bruce Fields p = xdr_encode_opaque(p, major_id, major_id_sz); 42990733d213SAndy Adamson 43000733d213SAndy Adamson /* Server scope */ 43010c0c267bSJ. Bruce Fields p = xdr_encode_opaque(p, server_scope, server_scope_sz); 43020733d213SAndy Adamson 43030733d213SAndy Adamson /* Implementation id */ 4304c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); /* zero length nfs_impl_id4 array */ 43050733d213SAndy Adamson return 0; 43062db134ebSAndy Adamson } 43072db134ebSAndy Adamson 43082db134ebSAndy Adamson static __be32 430957b7b43bSJ. Bruce Fields nfsd4_encode_create_session(struct nfsd4_compoundres *resp, __be32 nfserr, 43102db134ebSAndy Adamson struct nfsd4_create_session *sess) 43112db134ebSAndy Adamson { 4312d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 4313bc749ca4SJ. Bruce Fields __be32 *p; 4314ec6b5d7bSAndy Adamson 4315d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 24); 4316d0a381ddSJ. Bruce Fields if (!p) 4317d0a381ddSJ. Bruce Fields return nfserr_resource; 43180c0c267bSJ. Bruce Fields p = xdr_encode_opaque_fixed(p, sess->sessionid.data, 43190c0c267bSJ. Bruce Fields NFS4_MAX_SESSIONID_LEN); 4320c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->seqid); 4321c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->flags); 4322ec6b5d7bSAndy Adamson 4323d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 28); 4324d0a381ddSJ. Bruce Fields if (!p) 4325d0a381ddSJ. Bruce Fields return nfserr_resource; 4326c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); /* headerpadsz */ 4327c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->fore_channel.maxreq_sz); 4328c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->fore_channel.maxresp_sz); 4329c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->fore_channel.maxresp_cached); 4330c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->fore_channel.maxops); 4331c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->fore_channel.maxreqs); 4332c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->fore_channel.nr_rdma_attrs); 4333ec6b5d7bSAndy Adamson 4334ec6b5d7bSAndy Adamson if (sess->fore_channel.nr_rdma_attrs) { 4335d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 4336d0a381ddSJ. Bruce Fields if (!p) 4337d0a381ddSJ. Bruce Fields return nfserr_resource; 4338c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->fore_channel.rdma_attrs); 4339ec6b5d7bSAndy Adamson } 4340ec6b5d7bSAndy Adamson 4341d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 28); 4342d0a381ddSJ. Bruce Fields if (!p) 4343d0a381ddSJ. Bruce Fields return nfserr_resource; 4344c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(0); /* headerpadsz */ 4345c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->back_channel.maxreq_sz); 4346c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->back_channel.maxresp_sz); 4347c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->back_channel.maxresp_cached); 4348c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->back_channel.maxops); 4349c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->back_channel.maxreqs); 4350c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->back_channel.nr_rdma_attrs); 4351ec6b5d7bSAndy Adamson 4352ec6b5d7bSAndy Adamson if (sess->back_channel.nr_rdma_attrs) { 4353d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 4); 4354d0a381ddSJ. Bruce Fields if (!p) 4355d0a381ddSJ. Bruce Fields return nfserr_resource; 4356c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(sess->back_channel.rdma_attrs); 4357ec6b5d7bSAndy Adamson } 4358ec6b5d7bSAndy Adamson return 0; 43592db134ebSAndy Adamson } 43602db134ebSAndy Adamson 43612db134ebSAndy Adamson static __be32 436257b7b43bSJ. Bruce Fields nfsd4_encode_sequence(struct nfsd4_compoundres *resp, __be32 nfserr, 43632db134ebSAndy Adamson struct nfsd4_sequence *seq) 43642db134ebSAndy Adamson { 4365d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 4366bc749ca4SJ. Bruce Fields __be32 *p; 4367b85d4c01SBenny Halevy 4368d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 20); 4369d0a381ddSJ. Bruce Fields if (!p) 4370d0a381ddSJ. Bruce Fields return nfserr_resource; 43710c0c267bSJ. Bruce Fields p = xdr_encode_opaque_fixed(p, seq->sessionid.data, 43720c0c267bSJ. Bruce Fields NFS4_MAX_SESSIONID_LEN); 4373c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(seq->seqid); 4374c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(seq->slotid); 4375b7d7ca35SJ. Bruce Fields /* Note slotid's are numbered from zero: */ 4376c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(seq->maxslots - 1); /* sr_highest_slotid */ 4377c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(seq->maxslots - 1); /* sr_target_highest_slotid */ 4378c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(seq->status_flags); 4379b85d4c01SBenny Halevy 4380f5236013SJ. Bruce Fields resp->cstate.data_offset = xdr->buf->len; /* DRC cache data pointer */ 4381b85d4c01SBenny Halevy return 0; 43822db134ebSAndy Adamson } 43832db134ebSAndy Adamson 43842355c596SJ. Bruce Fields static __be32 438557b7b43bSJ. Bruce Fields nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, __be32 nfserr, 438617456804SBryan Schumaker struct nfsd4_test_stateid *test_stateid) 438717456804SBryan Schumaker { 4388d0a381ddSJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 438903cfb420SBryan Schumaker struct nfsd4_test_stateid_id *stateid, *next; 439017456804SBryan Schumaker __be32 *p; 439117456804SBryan Schumaker 4392d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 4 + (4 * test_stateid->ts_num_ids)); 4393d0a381ddSJ. Bruce Fields if (!p) 4394d0a381ddSJ. Bruce Fields return nfserr_resource; 439517456804SBryan Schumaker *p++ = htonl(test_stateid->ts_num_ids); 439617456804SBryan Schumaker 439703cfb420SBryan Schumaker list_for_each_entry_safe(stateid, next, &test_stateid->ts_stateid_list, ts_id_list) { 439802f5fde5SAl Viro *p++ = stateid->ts_id_status; 439917456804SBryan Schumaker } 440017456804SBryan Schumaker 4401bac966d6SJ. Bruce Fields return 0; 440217456804SBryan Schumaker } 440317456804SBryan Schumaker 44049cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS 44059cf514ccSChristoph Hellwig static __be32 44069cf514ccSChristoph Hellwig nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres *resp, __be32 nfserr, 44079cf514ccSChristoph Hellwig struct nfsd4_getdeviceinfo *gdev) 44089cf514ccSChristoph Hellwig { 44099cf514ccSChristoph Hellwig struct xdr_stream *xdr = &resp->xdr; 4410f961e3f2SJ. Bruce Fields const struct nfsd4_layout_ops *ops; 44119cf514ccSChristoph Hellwig u32 starting_len = xdr->buf->len, needed_len; 44129cf514ccSChristoph Hellwig __be32 *p; 44139cf514ccSChristoph Hellwig 44149cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 4); 44159cf514ccSChristoph Hellwig if (!p) 4416bac966d6SJ. Bruce Fields return nfserr_resource; 44179cf514ccSChristoph Hellwig 44189cf514ccSChristoph Hellwig *p++ = cpu_to_be32(gdev->gd_layout_type); 44199cf514ccSChristoph Hellwig 44209cf514ccSChristoph Hellwig /* If maxcount is 0 then just update notifications */ 44219cf514ccSChristoph Hellwig if (gdev->gd_maxcount != 0) { 4422f961e3f2SJ. Bruce Fields ops = nfsd4_layout_ops[gdev->gd_layout_type]; 44239cf514ccSChristoph Hellwig nfserr = ops->encode_getdeviceinfo(xdr, gdev); 44249cf514ccSChristoph Hellwig if (nfserr) { 44259cf514ccSChristoph Hellwig /* 44269cf514ccSChristoph Hellwig * We don't bother to burden the layout drivers with 44279cf514ccSChristoph Hellwig * enforcing gd_maxcount, just tell the client to 44289cf514ccSChristoph Hellwig * come back with a bigger buffer if it's not enough. 44299cf514ccSChristoph Hellwig */ 44309cf514ccSChristoph Hellwig if (xdr->buf->len + 4 > gdev->gd_maxcount) 44319cf514ccSChristoph Hellwig goto toosmall; 4432bac966d6SJ. Bruce Fields return nfserr; 44339cf514ccSChristoph Hellwig } 44349cf514ccSChristoph Hellwig } 44359cf514ccSChristoph Hellwig 44369cf514ccSChristoph Hellwig if (gdev->gd_notify_types) { 44379cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 4 + 4); 44389cf514ccSChristoph Hellwig if (!p) 4439bac966d6SJ. Bruce Fields return nfserr_resource; 44409cf514ccSChristoph Hellwig *p++ = cpu_to_be32(1); /* bitmap length */ 44419cf514ccSChristoph Hellwig *p++ = cpu_to_be32(gdev->gd_notify_types); 44429cf514ccSChristoph Hellwig } else { 44439cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 4); 44449cf514ccSChristoph Hellwig if (!p) 4445bac966d6SJ. Bruce Fields return nfserr_resource; 44469cf514ccSChristoph Hellwig *p++ = 0; 44479cf514ccSChristoph Hellwig } 44489cf514ccSChristoph Hellwig 4449bac966d6SJ. Bruce Fields return 0; 44509cf514ccSChristoph Hellwig toosmall: 44519cf514ccSChristoph Hellwig dprintk("%s: maxcount too small\n", __func__); 44529cf514ccSChristoph Hellwig needed_len = xdr->buf->len + 4 /* notifications */; 44539cf514ccSChristoph Hellwig xdr_truncate_encode(xdr, starting_len); 44549cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 4); 4455bac966d6SJ. Bruce Fields if (!p) 4456bac966d6SJ. Bruce Fields return nfserr_resource; 44579cf514ccSChristoph Hellwig *p++ = cpu_to_be32(needed_len); 4458bac966d6SJ. Bruce Fields return nfserr_toosmall; 44599cf514ccSChristoph Hellwig } 44609cf514ccSChristoph Hellwig 44619cf514ccSChristoph Hellwig static __be32 44629cf514ccSChristoph Hellwig nfsd4_encode_layoutget(struct nfsd4_compoundres *resp, __be32 nfserr, 44639cf514ccSChristoph Hellwig struct nfsd4_layoutget *lgp) 44649cf514ccSChristoph Hellwig { 44659cf514ccSChristoph Hellwig struct xdr_stream *xdr = &resp->xdr; 4466f961e3f2SJ. Bruce Fields const struct nfsd4_layout_ops *ops; 44679cf514ccSChristoph Hellwig __be32 *p; 44689cf514ccSChristoph Hellwig 44699cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 36 + sizeof(stateid_opaque_t)); 44709cf514ccSChristoph Hellwig if (!p) 4471bac966d6SJ. Bruce Fields return nfserr_resource; 44729cf514ccSChristoph Hellwig 44739cf514ccSChristoph Hellwig *p++ = cpu_to_be32(1); /* we always set return-on-close */ 44749cf514ccSChristoph Hellwig *p++ = cpu_to_be32(lgp->lg_sid.si_generation); 44759cf514ccSChristoph Hellwig p = xdr_encode_opaque_fixed(p, &lgp->lg_sid.si_opaque, 44769cf514ccSChristoph Hellwig sizeof(stateid_opaque_t)); 44779cf514ccSChristoph Hellwig 44789cf514ccSChristoph Hellwig *p++ = cpu_to_be32(1); /* we always return a single layout */ 44799cf514ccSChristoph Hellwig p = xdr_encode_hyper(p, lgp->lg_seg.offset); 44809cf514ccSChristoph Hellwig p = xdr_encode_hyper(p, lgp->lg_seg.length); 44819cf514ccSChristoph Hellwig *p++ = cpu_to_be32(lgp->lg_seg.iomode); 44829cf514ccSChristoph Hellwig *p++ = cpu_to_be32(lgp->lg_layout_type); 44839cf514ccSChristoph Hellwig 4484f961e3f2SJ. Bruce Fields ops = nfsd4_layout_ops[lgp->lg_layout_type]; 4485bac966d6SJ. Bruce Fields return ops->encode_layoutget(xdr, lgp); 44869cf514ccSChristoph Hellwig } 44879cf514ccSChristoph Hellwig 44889cf514ccSChristoph Hellwig static __be32 44899cf514ccSChristoph Hellwig nfsd4_encode_layoutcommit(struct nfsd4_compoundres *resp, __be32 nfserr, 44909cf514ccSChristoph Hellwig struct nfsd4_layoutcommit *lcp) 44919cf514ccSChristoph Hellwig { 44929cf514ccSChristoph Hellwig struct xdr_stream *xdr = &resp->xdr; 44939cf514ccSChristoph Hellwig __be32 *p; 44949cf514ccSChristoph Hellwig 44959cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 4); 44969cf514ccSChristoph Hellwig if (!p) 44979cf514ccSChristoph Hellwig return nfserr_resource; 44989cf514ccSChristoph Hellwig *p++ = cpu_to_be32(lcp->lc_size_chg); 44999cf514ccSChristoph Hellwig if (lcp->lc_size_chg) { 45009cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 8); 45019cf514ccSChristoph Hellwig if (!p) 45029cf514ccSChristoph Hellwig return nfserr_resource; 45039cf514ccSChristoph Hellwig p = xdr_encode_hyper(p, lcp->lc_newsize); 45049cf514ccSChristoph Hellwig } 45059cf514ccSChristoph Hellwig 4506bac966d6SJ. Bruce Fields return 0; 45079cf514ccSChristoph Hellwig } 45089cf514ccSChristoph Hellwig 45099cf514ccSChristoph Hellwig static __be32 45109cf514ccSChristoph Hellwig nfsd4_encode_layoutreturn(struct nfsd4_compoundres *resp, __be32 nfserr, 45119cf514ccSChristoph Hellwig struct nfsd4_layoutreturn *lrp) 45129cf514ccSChristoph Hellwig { 45139cf514ccSChristoph Hellwig struct xdr_stream *xdr = &resp->xdr; 45149cf514ccSChristoph Hellwig __be32 *p; 45159cf514ccSChristoph Hellwig 45169cf514ccSChristoph Hellwig p = xdr_reserve_space(xdr, 4); 45179cf514ccSChristoph Hellwig if (!p) 45189cf514ccSChristoph Hellwig return nfserr_resource; 45199cf514ccSChristoph Hellwig *p++ = cpu_to_be32(lrp->lrs_present); 45209cf514ccSChristoph Hellwig if (lrp->lrs_present) 4521376675daSKinglong Mee return nfsd4_encode_stateid(xdr, &lrp->lr_sid); 4522bac966d6SJ. Bruce Fields return 0; 45239cf514ccSChristoph Hellwig } 45249cf514ccSChristoph Hellwig #endif /* CONFIG_NFSD_PNFS */ 45259cf514ccSChristoph Hellwig 45262db134ebSAndy Adamson static __be32 4527e0639dc5SOlga Kornievskaia nfsd42_encode_write_res(struct nfsd4_compoundres *resp, 4528e0639dc5SOlga Kornievskaia struct nfsd42_write_res *write, bool sync) 452929ae7f9dSAnna Schumaker { 453029ae7f9dSAnna Schumaker __be32 *p; 4531e0639dc5SOlga Kornievskaia p = xdr_reserve_space(&resp->xdr, 4); 453229ae7f9dSAnna Schumaker if (!p) 453329ae7f9dSAnna Schumaker return nfserr_resource; 453429ae7f9dSAnna Schumaker 4535e0639dc5SOlga Kornievskaia if (sync) 453629ae7f9dSAnna Schumaker *p++ = cpu_to_be32(0); 4537e0639dc5SOlga Kornievskaia else { 4538e0639dc5SOlga Kornievskaia __be32 nfserr; 4539e0639dc5SOlga Kornievskaia *p++ = cpu_to_be32(1); 4540e0639dc5SOlga Kornievskaia nfserr = nfsd4_encode_stateid(&resp->xdr, &write->cb_stateid); 4541e0639dc5SOlga Kornievskaia if (nfserr) 4542e0639dc5SOlga Kornievskaia return nfserr; 4543e0639dc5SOlga Kornievskaia } 4544e0639dc5SOlga Kornievskaia p = xdr_reserve_space(&resp->xdr, 8 + 4 + NFS4_VERIFIER_SIZE); 4545e0639dc5SOlga Kornievskaia if (!p) 4546e0639dc5SOlga Kornievskaia return nfserr_resource; 4547e0639dc5SOlga Kornievskaia 454829ae7f9dSAnna Schumaker p = xdr_encode_hyper(p, write->wr_bytes_written); 454929ae7f9dSAnna Schumaker *p++ = cpu_to_be32(write->wr_stable_how); 455029ae7f9dSAnna Schumaker p = xdr_encode_opaque_fixed(p, write->wr_verifier.data, 455129ae7f9dSAnna Schumaker NFS4_VERIFIER_SIZE); 455229ae7f9dSAnna Schumaker return nfs_ok; 455329ae7f9dSAnna Schumaker } 455429ae7f9dSAnna Schumaker 455529ae7f9dSAnna Schumaker static __be32 455651911868SOlga Kornievskaia nfsd42_encode_nl4_server(struct nfsd4_compoundres *resp, struct nl4_server *ns) 455751911868SOlga Kornievskaia { 455851911868SOlga Kornievskaia struct xdr_stream *xdr = &resp->xdr; 455951911868SOlga Kornievskaia struct nfs42_netaddr *addr; 456051911868SOlga Kornievskaia __be32 *p; 456151911868SOlga Kornievskaia 456251911868SOlga Kornievskaia p = xdr_reserve_space(xdr, 4); 456351911868SOlga Kornievskaia *p++ = cpu_to_be32(ns->nl4_type); 456451911868SOlga Kornievskaia 456551911868SOlga Kornievskaia switch (ns->nl4_type) { 456651911868SOlga Kornievskaia case NL4_NETADDR: 456751911868SOlga Kornievskaia addr = &ns->u.nl4_addr; 456851911868SOlga Kornievskaia 456951911868SOlga Kornievskaia /* netid_len, netid, uaddr_len, uaddr (port included 457051911868SOlga Kornievskaia * in RPCBIND_MAXUADDRLEN) 457151911868SOlga Kornievskaia */ 457251911868SOlga Kornievskaia p = xdr_reserve_space(xdr, 457351911868SOlga Kornievskaia 4 /* netid len */ + 457451911868SOlga Kornievskaia (XDR_QUADLEN(addr->netid_len) * 4) + 457551911868SOlga Kornievskaia 4 /* uaddr len */ + 457651911868SOlga Kornievskaia (XDR_QUADLEN(addr->addr_len) * 4)); 457751911868SOlga Kornievskaia if (!p) 457851911868SOlga Kornievskaia return nfserr_resource; 457951911868SOlga Kornievskaia 458051911868SOlga Kornievskaia *p++ = cpu_to_be32(addr->netid_len); 458151911868SOlga Kornievskaia p = xdr_encode_opaque_fixed(p, addr->netid, 458251911868SOlga Kornievskaia addr->netid_len); 458351911868SOlga Kornievskaia *p++ = cpu_to_be32(addr->addr_len); 458451911868SOlga Kornievskaia p = xdr_encode_opaque_fixed(p, addr->addr, 458551911868SOlga Kornievskaia addr->addr_len); 458651911868SOlga Kornievskaia break; 458751911868SOlga Kornievskaia default: 458851911868SOlga Kornievskaia WARN_ON_ONCE(ns->nl4_type != NL4_NETADDR); 458951911868SOlga Kornievskaia return nfserr_inval; 459051911868SOlga Kornievskaia } 459151911868SOlga Kornievskaia 459251911868SOlga Kornievskaia return 0; 459351911868SOlga Kornievskaia } 459451911868SOlga Kornievskaia 459551911868SOlga Kornievskaia static __be32 459629ae7f9dSAnna Schumaker nfsd4_encode_copy(struct nfsd4_compoundres *resp, __be32 nfserr, 459729ae7f9dSAnna Schumaker struct nfsd4_copy *copy) 459829ae7f9dSAnna Schumaker { 459929ae7f9dSAnna Schumaker __be32 *p; 460029ae7f9dSAnna Schumaker 4601e0639dc5SOlga Kornievskaia nfserr = nfsd42_encode_write_res(resp, ©->cp_res, 4602e0639dc5SOlga Kornievskaia copy->cp_synchronous); 460329ae7f9dSAnna Schumaker if (nfserr) 460429ae7f9dSAnna Schumaker return nfserr; 460529ae7f9dSAnna Schumaker 460629ae7f9dSAnna Schumaker p = xdr_reserve_space(&resp->xdr, 4 + 4); 4607edcc8452SJ. Bruce Fields *p++ = xdr_one; /* cr_consecutive */ 460829ae7f9dSAnna Schumaker *p++ = cpu_to_be32(copy->cp_synchronous); 4609bac966d6SJ. Bruce Fields return 0; 461029ae7f9dSAnna Schumaker } 461129ae7f9dSAnna Schumaker 461229ae7f9dSAnna Schumaker static __be32 46136308bc98SOlga Kornievskaia nfsd4_encode_offload_status(struct nfsd4_compoundres *resp, __be32 nfserr, 46146308bc98SOlga Kornievskaia struct nfsd4_offload_status *os) 46156308bc98SOlga Kornievskaia { 46166308bc98SOlga Kornievskaia struct xdr_stream *xdr = &resp->xdr; 46176308bc98SOlga Kornievskaia __be32 *p; 46186308bc98SOlga Kornievskaia 46196308bc98SOlga Kornievskaia p = xdr_reserve_space(xdr, 8 + 4); 46206308bc98SOlga Kornievskaia if (!p) 46216308bc98SOlga Kornievskaia return nfserr_resource; 46226308bc98SOlga Kornievskaia p = xdr_encode_hyper(p, os->count); 46236308bc98SOlga Kornievskaia *p++ = cpu_to_be32(0); 4624528b8493SAnna Schumaker return nfserr; 4625528b8493SAnna Schumaker } 4626528b8493SAnna Schumaker 4627528b8493SAnna Schumaker static __be32 4628528b8493SAnna Schumaker nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp, 4629528b8493SAnna Schumaker struct nfsd4_read *read, 46309f0b5792SAnna Schumaker unsigned long *maxcount, u32 *eof, 46319f0b5792SAnna Schumaker loff_t *pos) 4632528b8493SAnna Schumaker { 4633528b8493SAnna Schumaker struct xdr_stream *xdr = &resp->xdr; 4634528b8493SAnna Schumaker struct file *file = read->rd_nf->nf_file; 4635528b8493SAnna Schumaker int starting_len = xdr->buf->len; 46369f0b5792SAnna Schumaker loff_t hole_pos; 4637528b8493SAnna Schumaker __be32 nfserr; 4638528b8493SAnna Schumaker __be32 *p, tmp; 4639528b8493SAnna Schumaker __be64 tmp64; 4640528b8493SAnna Schumaker 46419f0b5792SAnna Schumaker hole_pos = pos ? *pos : vfs_llseek(file, read->rd_offset, SEEK_HOLE); 46422db27992SAnna Schumaker if (hole_pos > read->rd_offset) 4643278765eaSAnna Schumaker *maxcount = min_t(unsigned long, *maxcount, hole_pos - read->rd_offset); 4644278765eaSAnna Schumaker *maxcount = min_t(unsigned long, *maxcount, (xdr->buf->buflen - xdr->buf->len)); 4645528b8493SAnna Schumaker 4646528b8493SAnna Schumaker /* Content type, offset, byte count */ 4647528b8493SAnna Schumaker p = xdr_reserve_space(xdr, 4 + 8 + 4); 4648528b8493SAnna Schumaker if (!p) 4649528b8493SAnna Schumaker return nfserr_resource; 4650528b8493SAnna Schumaker 4651278765eaSAnna Schumaker read->rd_vlen = xdr_reserve_space_vec(xdr, resp->rqstp->rq_vec, *maxcount); 4652528b8493SAnna Schumaker if (read->rd_vlen < 0) 4653528b8493SAnna Schumaker return nfserr_resource; 4654528b8493SAnna Schumaker 4655528b8493SAnna Schumaker nfserr = nfsd_readv(resp->rqstp, read->rd_fhp, file, read->rd_offset, 4656278765eaSAnna Schumaker resp->rqstp->rq_vec, read->rd_vlen, maxcount, eof); 4657528b8493SAnna Schumaker if (nfserr) 4658528b8493SAnna Schumaker return nfserr; 4659528b8493SAnna Schumaker 4660528b8493SAnna Schumaker tmp = htonl(NFS4_CONTENT_DATA); 4661528b8493SAnna Schumaker write_bytes_to_xdr_buf(xdr->buf, starting_len, &tmp, 4); 4662528b8493SAnna Schumaker tmp64 = cpu_to_be64(read->rd_offset); 4663528b8493SAnna Schumaker write_bytes_to_xdr_buf(xdr->buf, starting_len + 4, &tmp64, 8); 4664278765eaSAnna Schumaker tmp = htonl(*maxcount); 4665528b8493SAnna Schumaker write_bytes_to_xdr_buf(xdr->buf, starting_len + 12, &tmp, 4); 4666528b8493SAnna Schumaker return nfs_ok; 4667528b8493SAnna Schumaker } 4668528b8493SAnna Schumaker 4669528b8493SAnna Schumaker static __be32 46702db27992SAnna Schumaker nfsd4_encode_read_plus_hole(struct nfsd4_compoundres *resp, 46712db27992SAnna Schumaker struct nfsd4_read *read, 4672278765eaSAnna Schumaker unsigned long *maxcount, u32 *eof) 46732db27992SAnna Schumaker { 46742db27992SAnna Schumaker struct file *file = read->rd_nf->nf_file; 4675278765eaSAnna Schumaker loff_t data_pos = vfs_llseek(file, read->rd_offset, SEEK_DATA); 46769f0b5792SAnna Schumaker loff_t f_size = i_size_read(file_inode(file)); 4677278765eaSAnna Schumaker unsigned long count; 46782db27992SAnna Schumaker __be32 *p; 46792db27992SAnna Schumaker 4680278765eaSAnna Schumaker if (data_pos == -ENXIO) 46819f0b5792SAnna Schumaker data_pos = f_size; 46829f0b5792SAnna Schumaker else if (data_pos <= read->rd_offset || (data_pos < f_size && data_pos % PAGE_SIZE)) 46839f0b5792SAnna Schumaker return nfsd4_encode_read_plus_data(resp, read, maxcount, eof, &f_size); 4684278765eaSAnna Schumaker count = data_pos - read->rd_offset; 4685278765eaSAnna Schumaker 46862db27992SAnna Schumaker /* Content type, offset, byte count */ 46872db27992SAnna Schumaker p = xdr_reserve_space(&resp->xdr, 4 + 8 + 8); 46882db27992SAnna Schumaker if (!p) 46892db27992SAnna Schumaker return nfserr_resource; 46902db27992SAnna Schumaker 46912db27992SAnna Schumaker *p++ = htonl(NFS4_CONTENT_HOLE); 46922db27992SAnna Schumaker p = xdr_encode_hyper(p, read->rd_offset); 4693278765eaSAnna Schumaker p = xdr_encode_hyper(p, count); 46942db27992SAnna Schumaker 46959f0b5792SAnna Schumaker *eof = (read->rd_offset + count) >= f_size; 4696278765eaSAnna Schumaker *maxcount = min_t(unsigned long, count, *maxcount); 46972db27992SAnna Schumaker return nfs_ok; 46982db27992SAnna Schumaker } 46992db27992SAnna Schumaker 47002db27992SAnna Schumaker static __be32 4701528b8493SAnna Schumaker nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr, 4702528b8493SAnna Schumaker struct nfsd4_read *read) 4703528b8493SAnna Schumaker { 4704278765eaSAnna Schumaker unsigned long maxcount, count; 4705528b8493SAnna Schumaker struct xdr_stream *xdr = &resp->xdr; 4706528b8493SAnna Schumaker struct file *file; 4707528b8493SAnna Schumaker int starting_len = xdr->buf->len; 47089f0b5792SAnna Schumaker int last_segment = xdr->buf->len; 4709528b8493SAnna Schumaker int segments = 0; 4710528b8493SAnna Schumaker __be32 *p, tmp; 47119f0b5792SAnna Schumaker bool is_data; 47122db27992SAnna Schumaker loff_t pos; 4713528b8493SAnna Schumaker u32 eof; 4714528b8493SAnna Schumaker 4715528b8493SAnna Schumaker if (nfserr) 4716528b8493SAnna Schumaker return nfserr; 4717528b8493SAnna Schumaker file = read->rd_nf->nf_file; 4718528b8493SAnna Schumaker 4719528b8493SAnna Schumaker /* eof flag, segment count */ 4720528b8493SAnna Schumaker p = xdr_reserve_space(xdr, 4 + 4); 4721528b8493SAnna Schumaker if (!p) 4722528b8493SAnna Schumaker return nfserr_resource; 4723528b8493SAnna Schumaker xdr_commit_encode(xdr); 4724528b8493SAnna Schumaker 4725528b8493SAnna Schumaker maxcount = svc_max_payload(resp->rqstp); 4726528b8493SAnna Schumaker maxcount = min_t(unsigned long, maxcount, 4727528b8493SAnna Schumaker (xdr->buf->buflen - xdr->buf->len)); 4728528b8493SAnna Schumaker maxcount = min_t(unsigned long, maxcount, read->rd_length); 4729278765eaSAnna Schumaker count = maxcount; 4730528b8493SAnna Schumaker 4731528b8493SAnna Schumaker eof = read->rd_offset >= i_size_read(file_inode(file)); 47322db27992SAnna Schumaker if (eof) 47332db27992SAnna Schumaker goto out; 47342db27992SAnna Schumaker 47359f0b5792SAnna Schumaker pos = vfs_llseek(file, read->rd_offset, SEEK_HOLE); 47369f0b5792SAnna Schumaker is_data = pos > read->rd_offset; 47372db27992SAnna Schumaker 47389f0b5792SAnna Schumaker while (count > 0 && !eof) { 4739278765eaSAnna Schumaker maxcount = count; 47409f0b5792SAnna Schumaker if (is_data) 47419f0b5792SAnna Schumaker nfserr = nfsd4_encode_read_plus_data(resp, read, &maxcount, &eof, 47429f0b5792SAnna Schumaker segments == 0 ? &pos : NULL); 47439f0b5792SAnna Schumaker else 4744278765eaSAnna Schumaker nfserr = nfsd4_encode_read_plus_hole(resp, read, &maxcount, &eof); 4745278765eaSAnna Schumaker if (nfserr) 4746278765eaSAnna Schumaker goto out; 4747278765eaSAnna Schumaker count -= maxcount; 4748278765eaSAnna Schumaker read->rd_offset += maxcount; 47499f0b5792SAnna Schumaker is_data = !is_data; 47509f0b5792SAnna Schumaker last_segment = xdr->buf->len; 4751528b8493SAnna Schumaker segments++; 4752528b8493SAnna Schumaker } 4753528b8493SAnna Schumaker 47542db27992SAnna Schumaker out: 4755278765eaSAnna Schumaker if (nfserr && segments == 0) 4756528b8493SAnna Schumaker xdr_truncate_encode(xdr, starting_len); 4757528b8493SAnna Schumaker else { 4758528b8493SAnna Schumaker tmp = htonl(eof); 4759528b8493SAnna Schumaker write_bytes_to_xdr_buf(xdr->buf, starting_len, &tmp, 4); 4760528b8493SAnna Schumaker tmp = htonl(segments); 4761528b8493SAnna Schumaker write_bytes_to_xdr_buf(xdr->buf, starting_len + 4, &tmp, 4); 47629f0b5792SAnna Schumaker if (nfserr) { 47639f0b5792SAnna Schumaker xdr_truncate_encode(xdr, last_segment); 4764278765eaSAnna Schumaker nfserr = nfs_ok; 4765528b8493SAnna Schumaker } 47669f0b5792SAnna Schumaker } 47676308bc98SOlga Kornievskaia 47686308bc98SOlga Kornievskaia return nfserr; 47696308bc98SOlga Kornievskaia } 47706308bc98SOlga Kornievskaia 47716308bc98SOlga Kornievskaia static __be32 477251911868SOlga Kornievskaia nfsd4_encode_copy_notify(struct nfsd4_compoundres *resp, __be32 nfserr, 477351911868SOlga Kornievskaia struct nfsd4_copy_notify *cn) 477451911868SOlga Kornievskaia { 477551911868SOlga Kornievskaia struct xdr_stream *xdr = &resp->xdr; 477651911868SOlga Kornievskaia __be32 *p; 477751911868SOlga Kornievskaia 477851911868SOlga Kornievskaia if (nfserr) 477951911868SOlga Kornievskaia return nfserr; 478051911868SOlga Kornievskaia 478151911868SOlga Kornievskaia /* 8 sec, 4 nsec */ 478251911868SOlga Kornievskaia p = xdr_reserve_space(xdr, 12); 478351911868SOlga Kornievskaia if (!p) 478451911868SOlga Kornievskaia return nfserr_resource; 478551911868SOlga Kornievskaia 478651911868SOlga Kornievskaia /* cnr_lease_time */ 478751911868SOlga Kornievskaia p = xdr_encode_hyper(p, cn->cpn_sec); 478851911868SOlga Kornievskaia *p++ = cpu_to_be32(cn->cpn_nsec); 478951911868SOlga Kornievskaia 479051911868SOlga Kornievskaia /* cnr_stateid */ 479151911868SOlga Kornievskaia nfserr = nfsd4_encode_stateid(xdr, &cn->cpn_cnr_stateid); 479251911868SOlga Kornievskaia if (nfserr) 479351911868SOlga Kornievskaia return nfserr; 479451911868SOlga Kornievskaia 479551911868SOlga Kornievskaia /* cnr_src.nl_nsvr */ 479651911868SOlga Kornievskaia p = xdr_reserve_space(xdr, 4); 479751911868SOlga Kornievskaia if (!p) 479851911868SOlga Kornievskaia return nfserr_resource; 479951911868SOlga Kornievskaia 480051911868SOlga Kornievskaia *p++ = cpu_to_be32(1); 480151911868SOlga Kornievskaia 480251911868SOlga Kornievskaia return nfsd42_encode_nl4_server(resp, &cn->cpn_src); 480351911868SOlga Kornievskaia } 480451911868SOlga Kornievskaia 480551911868SOlga Kornievskaia static __be32 480624bab491SAnna Schumaker nfsd4_encode_seek(struct nfsd4_compoundres *resp, __be32 nfserr, 480724bab491SAnna Schumaker struct nfsd4_seek *seek) 480824bab491SAnna Schumaker { 480924bab491SAnna Schumaker __be32 *p; 481024bab491SAnna Schumaker 481124bab491SAnna Schumaker p = xdr_reserve_space(&resp->xdr, 4 + 8); 481224bab491SAnna Schumaker *p++ = cpu_to_be32(seek->seek_eof); 481324bab491SAnna Schumaker p = xdr_encode_hyper(p, seek->seek_pos); 481424bab491SAnna Schumaker 4815bac966d6SJ. Bruce Fields return 0; 481624bab491SAnna Schumaker } 481724bab491SAnna Schumaker 481824bab491SAnna Schumaker static __be32 4819695e12f8SBenny Halevy nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p) 4820695e12f8SBenny Halevy { 4821695e12f8SBenny Halevy return nfserr; 4822695e12f8SBenny Halevy } 4823695e12f8SBenny Halevy 482423e50fe3SFrank van der Linden /* 482523e50fe3SFrank van der Linden * Encode kmalloc-ed buffer in to XDR stream. 482623e50fe3SFrank van der Linden */ 4827b9a49237SChuck Lever static __be32 482823e50fe3SFrank van der Linden nfsd4_vbuf_to_stream(struct xdr_stream *xdr, char *buf, u32 buflen) 482923e50fe3SFrank van der Linden { 483023e50fe3SFrank van der Linden u32 cplen; 483123e50fe3SFrank van der Linden __be32 *p; 483223e50fe3SFrank van der Linden 483323e50fe3SFrank van der Linden cplen = min_t(unsigned long, buflen, 483423e50fe3SFrank van der Linden ((void *)xdr->end - (void *)xdr->p)); 483523e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, cplen); 483623e50fe3SFrank van der Linden if (!p) 483723e50fe3SFrank van der Linden return nfserr_resource; 483823e50fe3SFrank van der Linden 483923e50fe3SFrank van der Linden memcpy(p, buf, cplen); 484023e50fe3SFrank van der Linden buf += cplen; 484123e50fe3SFrank van der Linden buflen -= cplen; 484223e50fe3SFrank van der Linden 484323e50fe3SFrank van der Linden while (buflen) { 484423e50fe3SFrank van der Linden cplen = min_t(u32, buflen, PAGE_SIZE); 484523e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, cplen); 484623e50fe3SFrank van der Linden if (!p) 484723e50fe3SFrank van der Linden return nfserr_resource; 484823e50fe3SFrank van der Linden 484923e50fe3SFrank van der Linden memcpy(p, buf, cplen); 485023e50fe3SFrank van der Linden 485123e50fe3SFrank van der Linden if (cplen < PAGE_SIZE) { 485223e50fe3SFrank van der Linden /* 485323e50fe3SFrank van der Linden * We're done, with a length that wasn't page 485423e50fe3SFrank van der Linden * aligned, so possibly not word aligned. Pad 485523e50fe3SFrank van der Linden * any trailing bytes with 0. 485623e50fe3SFrank van der Linden */ 485723e50fe3SFrank van der Linden xdr_encode_opaque_fixed(p, NULL, cplen); 485823e50fe3SFrank van der Linden break; 485923e50fe3SFrank van der Linden } 486023e50fe3SFrank van der Linden 486123e50fe3SFrank van der Linden buflen -= PAGE_SIZE; 486223e50fe3SFrank van der Linden buf += PAGE_SIZE; 486323e50fe3SFrank van der Linden } 486423e50fe3SFrank van der Linden 486523e50fe3SFrank van der Linden return 0; 486623e50fe3SFrank van der Linden } 486723e50fe3SFrank van der Linden 486823e50fe3SFrank van der Linden static __be32 486923e50fe3SFrank van der Linden nfsd4_encode_getxattr(struct nfsd4_compoundres *resp, __be32 nfserr, 487023e50fe3SFrank van der Linden struct nfsd4_getxattr *getxattr) 487123e50fe3SFrank van der Linden { 487223e50fe3SFrank van der Linden struct xdr_stream *xdr = &resp->xdr; 487323e50fe3SFrank van der Linden __be32 *p, err; 487423e50fe3SFrank van der Linden 487523e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, 4); 487623e50fe3SFrank van der Linden if (!p) 487723e50fe3SFrank van der Linden return nfserr_resource; 487823e50fe3SFrank van der Linden 487923e50fe3SFrank van der Linden *p = cpu_to_be32(getxattr->getxa_len); 488023e50fe3SFrank van der Linden 488123e50fe3SFrank van der Linden if (getxattr->getxa_len == 0) 488223e50fe3SFrank van der Linden return 0; 488323e50fe3SFrank van der Linden 488423e50fe3SFrank van der Linden err = nfsd4_vbuf_to_stream(xdr, getxattr->getxa_buf, 488523e50fe3SFrank van der Linden getxattr->getxa_len); 488623e50fe3SFrank van der Linden 488723e50fe3SFrank van der Linden kvfree(getxattr->getxa_buf); 488823e50fe3SFrank van der Linden 488923e50fe3SFrank van der Linden return err; 489023e50fe3SFrank van der Linden } 489123e50fe3SFrank van der Linden 489223e50fe3SFrank van der Linden static __be32 489323e50fe3SFrank van der Linden nfsd4_encode_setxattr(struct nfsd4_compoundres *resp, __be32 nfserr, 489423e50fe3SFrank van der Linden struct nfsd4_setxattr *setxattr) 489523e50fe3SFrank van der Linden { 489623e50fe3SFrank van der Linden struct xdr_stream *xdr = &resp->xdr; 489723e50fe3SFrank van der Linden __be32 *p; 489823e50fe3SFrank van der Linden 489923e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, 20); 490023e50fe3SFrank van der Linden if (!p) 490123e50fe3SFrank van der Linden return nfserr_resource; 490223e50fe3SFrank van der Linden 490323e50fe3SFrank van der Linden encode_cinfo(p, &setxattr->setxa_cinfo); 490423e50fe3SFrank van der Linden 490523e50fe3SFrank van der Linden return 0; 490623e50fe3SFrank van der Linden } 490723e50fe3SFrank van der Linden 490823e50fe3SFrank van der Linden /* 490923e50fe3SFrank van der Linden * See if there are cookie values that can be rejected outright. 491023e50fe3SFrank van der Linden */ 491123e50fe3SFrank van der Linden static __be32 491223e50fe3SFrank van der Linden nfsd4_listxattr_validate_cookie(struct nfsd4_listxattrs *listxattrs, 491323e50fe3SFrank van der Linden u32 *offsetp) 491423e50fe3SFrank van der Linden { 491523e50fe3SFrank van der Linden u64 cookie = listxattrs->lsxa_cookie; 491623e50fe3SFrank van der Linden 491723e50fe3SFrank van der Linden /* 491823e50fe3SFrank van der Linden * If the cookie is larger than the maximum number we can fit 491923e50fe3SFrank van der Linden * in either the buffer we just got back from vfs_listxattr, or, 492023e50fe3SFrank van der Linden * XDR-encoded, in the return buffer, it's invalid. 492123e50fe3SFrank van der Linden */ 492223e50fe3SFrank van der Linden if (cookie > (listxattrs->lsxa_len) / (XATTR_USER_PREFIX_LEN + 2)) 492323e50fe3SFrank van der Linden return nfserr_badcookie; 492423e50fe3SFrank van der Linden 492523e50fe3SFrank van der Linden if (cookie > (listxattrs->lsxa_maxcount / 492623e50fe3SFrank van der Linden (XDR_QUADLEN(XATTR_USER_PREFIX_LEN + 2) + 4))) 492723e50fe3SFrank van der Linden return nfserr_badcookie; 492823e50fe3SFrank van der Linden 492923e50fe3SFrank van der Linden *offsetp = (u32)cookie; 493023e50fe3SFrank van der Linden return 0; 493123e50fe3SFrank van der Linden } 493223e50fe3SFrank van der Linden 493323e50fe3SFrank van der Linden static __be32 493423e50fe3SFrank van der Linden nfsd4_encode_listxattrs(struct nfsd4_compoundres *resp, __be32 nfserr, 493523e50fe3SFrank van der Linden struct nfsd4_listxattrs *listxattrs) 493623e50fe3SFrank van der Linden { 493723e50fe3SFrank van der Linden struct xdr_stream *xdr = &resp->xdr; 493823e50fe3SFrank van der Linden u32 cookie_offset, count_offset, eof; 493923e50fe3SFrank van der Linden u32 left, xdrleft, slen, count; 494023e50fe3SFrank van der Linden u32 xdrlen, offset; 494123e50fe3SFrank van der Linden u64 cookie; 494223e50fe3SFrank van der Linden char *sp; 4943b9a49237SChuck Lever __be32 status, tmp; 494423e50fe3SFrank van der Linden __be32 *p; 494523e50fe3SFrank van der Linden u32 nuser; 494623e50fe3SFrank van der Linden 494723e50fe3SFrank van der Linden eof = 1; 494823e50fe3SFrank van der Linden 494923e50fe3SFrank van der Linden status = nfsd4_listxattr_validate_cookie(listxattrs, &offset); 495023e50fe3SFrank van der Linden if (status) 495123e50fe3SFrank van der Linden goto out; 495223e50fe3SFrank van der Linden 495323e50fe3SFrank van der Linden /* 495423e50fe3SFrank van der Linden * Reserve space for the cookie and the name array count. Record 495523e50fe3SFrank van der Linden * the offsets to save them later. 495623e50fe3SFrank van der Linden */ 495723e50fe3SFrank van der Linden cookie_offset = xdr->buf->len; 495823e50fe3SFrank van der Linden count_offset = cookie_offset + 8; 495923e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, 12); 496023e50fe3SFrank van der Linden if (!p) { 496123e50fe3SFrank van der Linden status = nfserr_resource; 496223e50fe3SFrank van der Linden goto out; 496323e50fe3SFrank van der Linden } 496423e50fe3SFrank van der Linden 496523e50fe3SFrank van der Linden count = 0; 496623e50fe3SFrank van der Linden left = listxattrs->lsxa_len; 496723e50fe3SFrank van der Linden sp = listxattrs->lsxa_buf; 496823e50fe3SFrank van der Linden nuser = 0; 496923e50fe3SFrank van der Linden 497023e50fe3SFrank van der Linden xdrleft = listxattrs->lsxa_maxcount; 497123e50fe3SFrank van der Linden 497223e50fe3SFrank van der Linden while (left > 0 && xdrleft > 0) { 497323e50fe3SFrank van der Linden slen = strlen(sp); 497423e50fe3SFrank van der Linden 497523e50fe3SFrank van der Linden /* 49764cce11faSAlex Dewar * Check if this is a "user." attribute, skip it if not. 497723e50fe3SFrank van der Linden */ 497823e50fe3SFrank van der Linden if (strncmp(sp, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) 497923e50fe3SFrank van der Linden goto contloop; 498023e50fe3SFrank van der Linden 498123e50fe3SFrank van der Linden slen -= XATTR_USER_PREFIX_LEN; 498223e50fe3SFrank van der Linden xdrlen = 4 + ((slen + 3) & ~3); 498323e50fe3SFrank van der Linden if (xdrlen > xdrleft) { 498423e50fe3SFrank van der Linden if (count == 0) { 498523e50fe3SFrank van der Linden /* 498623e50fe3SFrank van der Linden * Can't even fit the first attribute name. 498723e50fe3SFrank van der Linden */ 498823e50fe3SFrank van der Linden status = nfserr_toosmall; 498923e50fe3SFrank van der Linden goto out; 499023e50fe3SFrank van der Linden } 499123e50fe3SFrank van der Linden eof = 0; 499223e50fe3SFrank van der Linden goto wreof; 499323e50fe3SFrank van der Linden } 499423e50fe3SFrank van der Linden 499523e50fe3SFrank van der Linden left -= XATTR_USER_PREFIX_LEN; 499623e50fe3SFrank van der Linden sp += XATTR_USER_PREFIX_LEN; 499723e50fe3SFrank van der Linden if (nuser++ < offset) 499823e50fe3SFrank van der Linden goto contloop; 499923e50fe3SFrank van der Linden 500023e50fe3SFrank van der Linden 500123e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, xdrlen); 500223e50fe3SFrank van der Linden if (!p) { 500323e50fe3SFrank van der Linden status = nfserr_resource; 500423e50fe3SFrank van der Linden goto out; 500523e50fe3SFrank van der Linden } 500623e50fe3SFrank van der Linden 5007e2a1840eSAlex Dewar xdr_encode_opaque(p, sp, slen); 500823e50fe3SFrank van der Linden 500923e50fe3SFrank van der Linden xdrleft -= xdrlen; 501023e50fe3SFrank van der Linden count++; 501123e50fe3SFrank van der Linden contloop: 501223e50fe3SFrank van der Linden sp += slen + 1; 501323e50fe3SFrank van der Linden left -= slen + 1; 501423e50fe3SFrank van der Linden } 501523e50fe3SFrank van der Linden 501623e50fe3SFrank van der Linden /* 501723e50fe3SFrank van der Linden * If there were user attributes to copy, but we didn't copy 501823e50fe3SFrank van der Linden * any, the offset was too large (e.g. the cookie was invalid). 501923e50fe3SFrank van der Linden */ 502023e50fe3SFrank van der Linden if (nuser > 0 && count == 0) { 502123e50fe3SFrank van der Linden status = nfserr_badcookie; 502223e50fe3SFrank van der Linden goto out; 502323e50fe3SFrank van der Linden } 502423e50fe3SFrank van der Linden 502523e50fe3SFrank van der Linden wreof: 502623e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, 4); 502723e50fe3SFrank van der Linden if (!p) { 502823e50fe3SFrank van der Linden status = nfserr_resource; 502923e50fe3SFrank van der Linden goto out; 503023e50fe3SFrank van der Linden } 503123e50fe3SFrank van der Linden *p = cpu_to_be32(eof); 503223e50fe3SFrank van der Linden 503323e50fe3SFrank van der Linden cookie = offset + count; 503423e50fe3SFrank van der Linden 503523e50fe3SFrank van der Linden write_bytes_to_xdr_buf(xdr->buf, cookie_offset, &cookie, 8); 5036b9a49237SChuck Lever tmp = cpu_to_be32(count); 5037b9a49237SChuck Lever write_bytes_to_xdr_buf(xdr->buf, count_offset, &tmp, 4); 503823e50fe3SFrank van der Linden out: 503923e50fe3SFrank van der Linden if (listxattrs->lsxa_len) 504023e50fe3SFrank van der Linden kvfree(listxattrs->lsxa_buf); 504123e50fe3SFrank van der Linden return status; 504223e50fe3SFrank van der Linden } 504323e50fe3SFrank van der Linden 504423e50fe3SFrank van der Linden static __be32 504523e50fe3SFrank van der Linden nfsd4_encode_removexattr(struct nfsd4_compoundres *resp, __be32 nfserr, 504623e50fe3SFrank van der Linden struct nfsd4_removexattr *removexattr) 504723e50fe3SFrank van der Linden { 504823e50fe3SFrank van der Linden struct xdr_stream *xdr = &resp->xdr; 504923e50fe3SFrank van der Linden __be32 *p; 505023e50fe3SFrank van der Linden 505123e50fe3SFrank van der Linden p = xdr_reserve_space(xdr, 20); 505223e50fe3SFrank van der Linden if (!p) 505323e50fe3SFrank van der Linden return nfserr_resource; 505423e50fe3SFrank van der Linden 505523e50fe3SFrank van der Linden p = encode_cinfo(p, &removexattr->rmxa_cinfo); 505623e50fe3SFrank van der Linden return 0; 505723e50fe3SFrank van der Linden } 505823e50fe3SFrank van der Linden 5059695e12f8SBenny Halevy typedef __be32(* nfsd4_enc)(struct nfsd4_compoundres *, __be32, void *); 5060695e12f8SBenny Halevy 50612db134ebSAndy Adamson /* 50622db134ebSAndy Adamson * Note: nfsd4_enc_ops vector is shared for v4.0 and v4.1 50632db134ebSAndy Adamson * since we don't need to filter out obsolete ops as this is 50642db134ebSAndy Adamson * done in the decoding phase. 50652db134ebSAndy Adamson */ 5066c1df609dSChuck Lever static const nfsd4_enc nfsd4_enc_ops[] = { 5067ad1060c8SJ. Bruce Fields [OP_ACCESS] = (nfsd4_enc)nfsd4_encode_access, 5068ad1060c8SJ. Bruce Fields [OP_CLOSE] = (nfsd4_enc)nfsd4_encode_close, 5069ad1060c8SJ. Bruce Fields [OP_COMMIT] = (nfsd4_enc)nfsd4_encode_commit, 5070ad1060c8SJ. Bruce Fields [OP_CREATE] = (nfsd4_enc)nfsd4_encode_create, 5071ad1060c8SJ. Bruce Fields [OP_DELEGPURGE] = (nfsd4_enc)nfsd4_encode_noop, 5072ad1060c8SJ. Bruce Fields [OP_DELEGRETURN] = (nfsd4_enc)nfsd4_encode_noop, 5073ad1060c8SJ. Bruce Fields [OP_GETATTR] = (nfsd4_enc)nfsd4_encode_getattr, 5074ad1060c8SJ. Bruce Fields [OP_GETFH] = (nfsd4_enc)nfsd4_encode_getfh, 5075ad1060c8SJ. Bruce Fields [OP_LINK] = (nfsd4_enc)nfsd4_encode_link, 5076ad1060c8SJ. Bruce Fields [OP_LOCK] = (nfsd4_enc)nfsd4_encode_lock, 5077ad1060c8SJ. Bruce Fields [OP_LOCKT] = (nfsd4_enc)nfsd4_encode_lockt, 5078ad1060c8SJ. Bruce Fields [OP_LOCKU] = (nfsd4_enc)nfsd4_encode_locku, 5079ad1060c8SJ. Bruce Fields [OP_LOOKUP] = (nfsd4_enc)nfsd4_encode_noop, 5080ad1060c8SJ. Bruce Fields [OP_LOOKUPP] = (nfsd4_enc)nfsd4_encode_noop, 5081ad1060c8SJ. Bruce Fields [OP_NVERIFY] = (nfsd4_enc)nfsd4_encode_noop, 5082ad1060c8SJ. Bruce Fields [OP_OPEN] = (nfsd4_enc)nfsd4_encode_open, 508384f09f46SBenny Halevy [OP_OPENATTR] = (nfsd4_enc)nfsd4_encode_noop, 5084ad1060c8SJ. Bruce Fields [OP_OPEN_CONFIRM] = (nfsd4_enc)nfsd4_encode_open_confirm, 5085ad1060c8SJ. Bruce Fields [OP_OPEN_DOWNGRADE] = (nfsd4_enc)nfsd4_encode_open_downgrade, 5086ad1060c8SJ. Bruce Fields [OP_PUTFH] = (nfsd4_enc)nfsd4_encode_noop, 5087ad1060c8SJ. Bruce Fields [OP_PUTPUBFH] = (nfsd4_enc)nfsd4_encode_noop, 5088ad1060c8SJ. Bruce Fields [OP_PUTROOTFH] = (nfsd4_enc)nfsd4_encode_noop, 5089ad1060c8SJ. Bruce Fields [OP_READ] = (nfsd4_enc)nfsd4_encode_read, 5090ad1060c8SJ. Bruce Fields [OP_READDIR] = (nfsd4_enc)nfsd4_encode_readdir, 5091ad1060c8SJ. Bruce Fields [OP_READLINK] = (nfsd4_enc)nfsd4_encode_readlink, 5092ad1060c8SJ. Bruce Fields [OP_REMOVE] = (nfsd4_enc)nfsd4_encode_remove, 5093ad1060c8SJ. Bruce Fields [OP_RENAME] = (nfsd4_enc)nfsd4_encode_rename, 5094ad1060c8SJ. Bruce Fields [OP_RENEW] = (nfsd4_enc)nfsd4_encode_noop, 5095ad1060c8SJ. Bruce Fields [OP_RESTOREFH] = (nfsd4_enc)nfsd4_encode_noop, 5096ad1060c8SJ. Bruce Fields [OP_SAVEFH] = (nfsd4_enc)nfsd4_encode_noop, 5097ad1060c8SJ. Bruce Fields [OP_SECINFO] = (nfsd4_enc)nfsd4_encode_secinfo, 5098ad1060c8SJ. Bruce Fields [OP_SETATTR] = (nfsd4_enc)nfsd4_encode_setattr, 5099ad1060c8SJ. Bruce Fields [OP_SETCLIENTID] = (nfsd4_enc)nfsd4_encode_setclientid, 5100ad1060c8SJ. Bruce Fields [OP_SETCLIENTID_CONFIRM] = (nfsd4_enc)nfsd4_encode_noop, 5101ad1060c8SJ. Bruce Fields [OP_VERIFY] = (nfsd4_enc)nfsd4_encode_noop, 5102ad1060c8SJ. Bruce Fields [OP_WRITE] = (nfsd4_enc)nfsd4_encode_write, 5103ad1060c8SJ. Bruce Fields [OP_RELEASE_LOCKOWNER] = (nfsd4_enc)nfsd4_encode_noop, 51042db134ebSAndy Adamson 51052db134ebSAndy Adamson /* NFSv4.1 operations */ 51062db134ebSAndy Adamson [OP_BACKCHANNEL_CTL] = (nfsd4_enc)nfsd4_encode_noop, 51071d1bc8f2SJ. Bruce Fields [OP_BIND_CONN_TO_SESSION] = (nfsd4_enc)nfsd4_encode_bind_conn_to_session, 51082db134ebSAndy Adamson [OP_EXCHANGE_ID] = (nfsd4_enc)nfsd4_encode_exchange_id, 51092db134ebSAndy Adamson [OP_CREATE_SESSION] = (nfsd4_enc)nfsd4_encode_create_session, 511043212cc7SKinglong Mee [OP_DESTROY_SESSION] = (nfsd4_enc)nfsd4_encode_noop, 511143212cc7SKinglong Mee [OP_FREE_STATEID] = (nfsd4_enc)nfsd4_encode_noop, 51122db134ebSAndy Adamson [OP_GET_DIR_DELEGATION] = (nfsd4_enc)nfsd4_encode_noop, 51139cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS 51149cf514ccSChristoph Hellwig [OP_GETDEVICEINFO] = (nfsd4_enc)nfsd4_encode_getdeviceinfo, 51159cf514ccSChristoph Hellwig [OP_GETDEVICELIST] = (nfsd4_enc)nfsd4_encode_noop, 51169cf514ccSChristoph Hellwig [OP_LAYOUTCOMMIT] = (nfsd4_enc)nfsd4_encode_layoutcommit, 51179cf514ccSChristoph Hellwig [OP_LAYOUTGET] = (nfsd4_enc)nfsd4_encode_layoutget, 51189cf514ccSChristoph Hellwig [OP_LAYOUTRETURN] = (nfsd4_enc)nfsd4_encode_layoutreturn, 51199cf514ccSChristoph Hellwig #else 51202db134ebSAndy Adamson [OP_GETDEVICEINFO] = (nfsd4_enc)nfsd4_encode_noop, 51212db134ebSAndy Adamson [OP_GETDEVICELIST] = (nfsd4_enc)nfsd4_encode_noop, 51222db134ebSAndy Adamson [OP_LAYOUTCOMMIT] = (nfsd4_enc)nfsd4_encode_noop, 51232db134ebSAndy Adamson [OP_LAYOUTGET] = (nfsd4_enc)nfsd4_encode_noop, 51242db134ebSAndy Adamson [OP_LAYOUTRETURN] = (nfsd4_enc)nfsd4_encode_noop, 51259cf514ccSChristoph Hellwig #endif 512622b6dee8SMi Jinlong [OP_SECINFO_NO_NAME] = (nfsd4_enc)nfsd4_encode_secinfo_no_name, 51272db134ebSAndy Adamson [OP_SEQUENCE] = (nfsd4_enc)nfsd4_encode_sequence, 51282db134ebSAndy Adamson [OP_SET_SSV] = (nfsd4_enc)nfsd4_encode_noop, 512917456804SBryan Schumaker [OP_TEST_STATEID] = (nfsd4_enc)nfsd4_encode_test_stateid, 51302db134ebSAndy Adamson [OP_WANT_DELEGATION] = (nfsd4_enc)nfsd4_encode_noop, 51312db134ebSAndy Adamson [OP_DESTROY_CLIENTID] = (nfsd4_enc)nfsd4_encode_noop, 51322db134ebSAndy Adamson [OP_RECLAIM_COMPLETE] = (nfsd4_enc)nfsd4_encode_noop, 513387a15a80SAnna Schumaker 513487a15a80SAnna Schumaker /* NFSv4.2 operations */ 513587a15a80SAnna Schumaker [OP_ALLOCATE] = (nfsd4_enc)nfsd4_encode_noop, 513629ae7f9dSAnna Schumaker [OP_COPY] = (nfsd4_enc)nfsd4_encode_copy, 513751911868SOlga Kornievskaia [OP_COPY_NOTIFY] = (nfsd4_enc)nfsd4_encode_copy_notify, 513887a15a80SAnna Schumaker [OP_DEALLOCATE] = (nfsd4_enc)nfsd4_encode_noop, 513987a15a80SAnna Schumaker [OP_IO_ADVISE] = (nfsd4_enc)nfsd4_encode_noop, 514087a15a80SAnna Schumaker [OP_LAYOUTERROR] = (nfsd4_enc)nfsd4_encode_noop, 514187a15a80SAnna Schumaker [OP_LAYOUTSTATS] = (nfsd4_enc)nfsd4_encode_noop, 514287a15a80SAnna Schumaker [OP_OFFLOAD_CANCEL] = (nfsd4_enc)nfsd4_encode_noop, 51436308bc98SOlga Kornievskaia [OP_OFFLOAD_STATUS] = (nfsd4_enc)nfsd4_encode_offload_status, 5144528b8493SAnna Schumaker [OP_READ_PLUS] = (nfsd4_enc)nfsd4_encode_read_plus, 514524bab491SAnna Schumaker [OP_SEEK] = (nfsd4_enc)nfsd4_encode_seek, 514687a15a80SAnna Schumaker [OP_WRITE_SAME] = (nfsd4_enc)nfsd4_encode_noop, 5147ffa0160aSChristoph Hellwig [OP_CLONE] = (nfsd4_enc)nfsd4_encode_noop, 514823e50fe3SFrank van der Linden 514923e50fe3SFrank van der Linden /* RFC 8276 extended atributes operations */ 515023e50fe3SFrank van der Linden [OP_GETXATTR] = (nfsd4_enc)nfsd4_encode_getxattr, 515123e50fe3SFrank van der Linden [OP_SETXATTR] = (nfsd4_enc)nfsd4_encode_setxattr, 515223e50fe3SFrank van der Linden [OP_LISTXATTRS] = (nfsd4_enc)nfsd4_encode_listxattrs, 515323e50fe3SFrank van der Linden [OP_REMOVEXATTR] = (nfsd4_enc)nfsd4_encode_removexattr, 5154695e12f8SBenny Halevy }; 5155695e12f8SBenny Halevy 5156496c262cSAndy Adamson /* 5157a8095f7eSJ. Bruce Fields * Calculate whether we still have space to encode repsize bytes. 5158a8095f7eSJ. Bruce Fields * There are two considerations: 5159a8095f7eSJ. Bruce Fields * - For NFS versions >=4.1, the size of the reply must stay within 5160a8095f7eSJ. Bruce Fields * session limits 5161a8095f7eSJ. Bruce Fields * - For all NFS versions, we must stay within limited preallocated 5162a8095f7eSJ. Bruce Fields * buffer space. 5163496c262cSAndy Adamson * 5164a8095f7eSJ. Bruce Fields * This is called before the operation is processed, so can only provide 5165a8095f7eSJ. Bruce Fields * an upper estimate. For some nonidempotent operations (such as 5166a8095f7eSJ. Bruce Fields * getattr), it's not necessarily a problem if that estimate is wrong, 5167a8095f7eSJ. Bruce Fields * as we can fail it after processing without significant side effects. 5168496c262cSAndy Adamson */ 5169a8095f7eSJ. Bruce Fields __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 respsize) 5170496c262cSAndy Adamson { 517167492c99SJ. Bruce Fields struct xdr_buf *buf = &resp->rqstp->rq_res; 5172a8095f7eSJ. Bruce Fields struct nfsd4_slot *slot = resp->cstate.slot; 5173496c262cSAndy Adamson 517447ee5298SJ. Bruce Fields if (buf->len + respsize <= buf->buflen) 517547ee5298SJ. Bruce Fields return nfs_ok; 517647ee5298SJ. Bruce Fields if (!nfsd4_has_session(&resp->cstate)) 517747ee5298SJ. Bruce Fields return nfserr_resource; 517847ee5298SJ. Bruce Fields if (slot->sl_flags & NFSD4_SLOT_CACHETHIS) { 517947ee5298SJ. Bruce Fields WARN_ON_ONCE(1); 5180496c262cSAndy Adamson return nfserr_rep_too_big_to_cache; 5181ea8d7720SJ. Bruce Fields } 518247ee5298SJ. Bruce Fields return nfserr_rep_too_big; 5183496c262cSAndy Adamson } 5184496c262cSAndy Adamson 51851da177e4SLinus Torvalds void 51861da177e4SLinus Torvalds nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) 51871da177e4SLinus Torvalds { 5188082d4bd7SJ. Bruce Fields struct xdr_stream *xdr = &resp->xdr; 51899411b1d4SJ. Bruce Fields struct nfs4_stateowner *so = resp->cstate.replay_owner; 51905f4ab945SJ. Bruce Fields struct svc_rqst *rqstp = resp->rqstp; 519134b1744cSJ. Bruce Fields const struct nfsd4_operation *opdesc = op->opdesc; 5192082d4bd7SJ. Bruce Fields int post_err_offset; 519307d1f802SJ. Bruce Fields nfsd4_enc encoder; 5194bc749ca4SJ. Bruce Fields __be32 *p; 51951da177e4SLinus Torvalds 5196d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 8); 5197d0a381ddSJ. Bruce Fields if (!p) { 5198d0a381ddSJ. Bruce Fields WARN_ON_ONCE(1); 5199d0a381ddSJ. Bruce Fields return; 5200d0a381ddSJ. Bruce Fields } 5201c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(op->opnum); 5202082d4bd7SJ. Bruce Fields post_err_offset = xdr->buf->len; 52031da177e4SLinus Torvalds 5204695e12f8SBenny Halevy if (op->opnum == OP_ILLEGAL) 5205695e12f8SBenny Halevy goto status; 5206b7571e4cSJ. Bruce Fields if (op->status && opdesc && 5207b7571e4cSJ. Bruce Fields !(opdesc->op_flags & OP_NONTRIVIAL_ERROR_ENCODE)) 5208b7571e4cSJ. Bruce Fields goto status; 5209695e12f8SBenny Halevy BUG_ON(op->opnum < 0 || op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) || 5210695e12f8SBenny Halevy !nfsd4_enc_ops[op->opnum]); 521107d1f802SJ. Bruce Fields encoder = nfsd4_enc_ops[op->opnum]; 521207d1f802SJ. Bruce Fields op->status = encoder(resp, op->status, &op->u); 5213*08281341SChuck Lever if (op->status) 5214*08281341SChuck Lever trace_nfsd_compound_encode_err(rqstp, op->opnum, op->status); 521534b1744cSJ. Bruce Fields if (opdesc && opdesc->op_release) 521634b1744cSJ. Bruce Fields opdesc->op_release(&op->u); 52172825a7f9SJ. Bruce Fields xdr_commit_encode(xdr); 52182825a7f9SJ. Bruce Fields 5219067e1aceSJ. Bruce Fields /* nfsd4_check_resp_size guarantees enough room for error status */ 52205f4ab945SJ. Bruce Fields if (!op->status) { 52215f4ab945SJ. Bruce Fields int space_needed = 0; 52225f4ab945SJ. Bruce Fields if (!nfsd4_last_compound_op(rqstp)) 52235f4ab945SJ. Bruce Fields space_needed = COMPOUND_ERR_SLACK_SPACE; 52245f4ab945SJ. Bruce Fields op->status = nfsd4_check_resp_size(resp, space_needed); 52255f4ab945SJ. Bruce Fields } 5226c8f13d97SJ. Bruce Fields if (op->status == nfserr_resource && nfsd4_has_session(&resp->cstate)) { 5227c8f13d97SJ. Bruce Fields struct nfsd4_slot *slot = resp->cstate.slot; 5228c8f13d97SJ. Bruce Fields 5229c8f13d97SJ. Bruce Fields if (slot->sl_flags & NFSD4_SLOT_CACHETHIS) 5230c8f13d97SJ. Bruce Fields op->status = nfserr_rep_too_big_to_cache; 5231c8f13d97SJ. Bruce Fields else 5232c8f13d97SJ. Bruce Fields op->status = nfserr_rep_too_big; 5233c8f13d97SJ. Bruce Fields } 523407d1f802SJ. Bruce Fields if (op->status == nfserr_resource || 523507d1f802SJ. Bruce Fields op->status == nfserr_rep_too_big || 523607d1f802SJ. Bruce Fields op->status == nfserr_rep_too_big_to_cache) { 523707d1f802SJ. Bruce Fields /* 523807d1f802SJ. Bruce Fields * The operation may have already been encoded or 523907d1f802SJ. Bruce Fields * partially encoded. No op returns anything additional 524007d1f802SJ. Bruce Fields * in the case of one of these three errors, so we can 524107d1f802SJ. Bruce Fields * just truncate back to after the status. But it's a 524207d1f802SJ. Bruce Fields * bug if we had to do this on a non-idempotent op: 524307d1f802SJ. Bruce Fields */ 524407d1f802SJ. Bruce Fields warn_on_nonidempotent_op(op); 5245082d4bd7SJ. Bruce Fields xdr_truncate_encode(xdr, post_err_offset); 524607d1f802SJ. Bruce Fields } 52479411b1d4SJ. Bruce Fields if (so) { 5248082d4bd7SJ. Bruce Fields int len = xdr->buf->len - post_err_offset; 5249082d4bd7SJ. Bruce Fields 52509411b1d4SJ. Bruce Fields so->so_replay.rp_status = op->status; 5251082d4bd7SJ. Bruce Fields so->so_replay.rp_buflen = len; 5252082d4bd7SJ. Bruce Fields read_bytes_from_xdr_buf(xdr->buf, post_err_offset, 5253082d4bd7SJ. Bruce Fields so->so_replay.rp_buf, len); 52549411b1d4SJ. Bruce Fields } 5255695e12f8SBenny Halevy status: 5256082d4bd7SJ. Bruce Fields /* Note that op->status is already in network byte order: */ 5257082d4bd7SJ. Bruce Fields write_bytes_to_xdr_buf(xdr->buf, post_err_offset - 4, &op->status, 4); 52581da177e4SLinus Torvalds } 52591da177e4SLinus Torvalds 52601da177e4SLinus Torvalds /* 52611da177e4SLinus Torvalds * Encode the reply stored in the stateowner reply cache 52621da177e4SLinus Torvalds * 52631da177e4SLinus Torvalds * XDR note: do not encode rp->rp_buflen: the buffer contains the 52641da177e4SLinus Torvalds * previously sent already encoded operation. 52651da177e4SLinus Torvalds */ 52661da177e4SLinus Torvalds void 5267d0a381ddSJ. Bruce Fields nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op) 52681da177e4SLinus Torvalds { 5269bc749ca4SJ. Bruce Fields __be32 *p; 52701da177e4SLinus Torvalds struct nfs4_replay *rp = op->replay; 52711da177e4SLinus Torvalds 5272d0a381ddSJ. Bruce Fields p = xdr_reserve_space(xdr, 8 + rp->rp_buflen); 5273d0a381ddSJ. Bruce Fields if (!p) { 5274d0a381ddSJ. Bruce Fields WARN_ON_ONCE(1); 5275d0a381ddSJ. Bruce Fields return; 5276d0a381ddSJ. Bruce Fields } 5277c373b0a4SJ. Bruce Fields *p++ = cpu_to_be32(op->opnum); 52781da177e4SLinus Torvalds *p++ = rp->rp_status; /* already xdr'ed */ 52791da177e4SLinus Torvalds 52800c0c267bSJ. Bruce Fields p = xdr_encode_opaque_fixed(p, rp->rp_buf, rp->rp_buflen); 52811da177e4SLinus Torvalds } 52821da177e4SLinus Torvalds 52838537488bSChristoph Hellwig void nfsd4_release_compoundargs(struct svc_rqst *rqstp) 52841da177e4SLinus Torvalds { 52853e98abffSJ. Bruce Fields struct nfsd4_compoundargs *args = rqstp->rq_argp; 52863e98abffSJ. Bruce Fields 52871da177e4SLinus Torvalds if (args->ops != args->iops) { 52881da177e4SLinus Torvalds kfree(args->ops); 52891da177e4SLinus Torvalds args->ops = args->iops; 52901da177e4SLinus Torvalds } 52911da177e4SLinus Torvalds kfree(args->tmpp); 52921da177e4SLinus Torvalds args->tmpp = NULL; 52931da177e4SLinus Torvalds while (args->to_free) { 5294d5e23383SJ. Bruce Fields struct svcxdr_tmpbuf *tb = args->to_free; 52951da177e4SLinus Torvalds args->to_free = tb->next; 52961da177e4SLinus Torvalds kfree(tb); 52971da177e4SLinus Torvalds } 52981da177e4SLinus Torvalds } 52991da177e4SLinus Torvalds 53001da177e4SLinus Torvalds int 5301026fec7eSChristoph Hellwig nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p) 53021da177e4SLinus Torvalds { 5303026fec7eSChristoph Hellwig struct nfsd4_compoundargs *args = rqstp->rq_argp; 5304026fec7eSChristoph Hellwig 5305e874f9f8SJeff Layton if (rqstp->rq_arg.head[0].iov_len % 4) { 5306e874f9f8SJeff Layton /* client is nuts */ 5307e874f9f8SJeff Layton dprintk("%s: compound not properly padded! (peeraddr=%pISc xid=0x%x)", 5308e874f9f8SJeff Layton __func__, svc_addr(rqstp), be32_to_cpu(rqstp->rq_xid)); 5309e874f9f8SJeff Layton return 0; 5310e874f9f8SJeff Layton } 53111da177e4SLinus Torvalds args->p = p; 53121da177e4SLinus Torvalds args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len; 53131da177e4SLinus Torvalds args->pagelist = rqstp->rq_arg.pages; 53141da177e4SLinus Torvalds args->pagelen = rqstp->rq_arg.page_len; 5315eae03e2aSChuck Lever args->tail = false; 53161da177e4SLinus Torvalds args->tmpp = NULL; 53171da177e4SLinus Torvalds args->to_free = NULL; 53181da177e4SLinus Torvalds args->ops = args->iops; 53191da177e4SLinus Torvalds args->rqstp = rqstp; 53201da177e4SLinus Torvalds 53213e98abffSJ. Bruce Fields return !nfsd4_decode_compound(args); 53221da177e4SLinus Torvalds } 53231da177e4SLinus Torvalds 53241da177e4SLinus Torvalds int 532563f8de37SChristoph Hellwig nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p) 53261da177e4SLinus Torvalds { 532763f8de37SChristoph Hellwig struct nfsd4_compoundres *resp = rqstp->rq_resp; 53286ac90391SJ. Bruce Fields struct xdr_buf *buf = resp->xdr.buf; 53296ac90391SJ. Bruce Fields 53306ac90391SJ. Bruce Fields WARN_ON_ONCE(buf->len != buf->head[0].iov_len + buf->page_len + 53316ac90391SJ. Bruce Fields buf->tail[0].iov_len); 5332dd97fddeSJ. Bruce Fields 5333cc028a10SChuck Lever *p = resp->cstate.status; 5334cc028a10SChuck Lever 53352825a7f9SJ. Bruce Fields rqstp->rq_next_page = resp->xdr.page_ptr + 1; 53362825a7f9SJ. Bruce Fields 53371da177e4SLinus Torvalds p = resp->tagp; 53381da177e4SLinus Torvalds *p++ = htonl(resp->taglen); 53391da177e4SLinus Torvalds memcpy(p, resp->tag, resp->taglen); 53401da177e4SLinus Torvalds p += XDR_QUADLEN(resp->taglen); 53411da177e4SLinus Torvalds *p++ = htonl(resp->opcnt); 53421da177e4SLinus Torvalds 5343b607664eSTrond Myklebust nfsd4_sequence_done(resp); 53441da177e4SLinus Torvalds return 1; 53451da177e4SLinus Torvalds } 53461da177e4SLinus Torvalds 53471da177e4SLinus Torvalds /* 53481da177e4SLinus Torvalds * Local variables: 53491da177e4SLinus Torvalds * c-basic-offset: 8 53501da177e4SLinus Torvalds * End: 53511da177e4SLinus Torvalds */ 5352