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 * TODO: Neil Brown made the following observation: We currently 361da177e4SLinus Torvalds * initially reserve NFSD_BUFSIZE space on the transmit queue and 371da177e4SLinus Torvalds * never release any of that until the request is complete. 381da177e4SLinus Torvalds * It would be good to calculate a new maximum response size while 391da177e4SLinus Torvalds * decoding the COMPOUND, and call svc_reserve with this number 401da177e4SLinus Torvalds * at the end of nfs4svc_decode_compoundargs. 411da177e4SLinus Torvalds */ 421da177e4SLinus Torvalds 435a0e3ad6STejun Heo #include <linux/slab.h> 441da177e4SLinus Torvalds #include <linux/namei.h> 45341eb184SBoaz Harrosh #include <linux/statfs.h> 460733d213SAndy Adamson #include <linux/utsname.h> 471da177e4SLinus Torvalds #include <linux/nfsd_idmap.h> 481da177e4SLinus Torvalds #include <linux/nfs4_acl.h> 494796f457SJ. Bruce Fields #include <linux/sunrpc/svcauth_gss.h> 509a74af21SBoaz Harrosh 519a74af21SBoaz Harrosh #include "xdr4.h" 520a3adadeSJ. Bruce Fields #include "vfs.h" 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds #define NFSDDBG_FACILITY NFSDDBG_XDR 551da177e4SLinus Torvalds 5642ca0993SJ.Bruce Fields /* 5742ca0993SJ.Bruce Fields * As per referral draft, the fsid for a referral MUST be different from the fsid of the containing 5842ca0993SJ.Bruce Fields * directory in order to indicate to the client that a filesystem boundary is present 5942ca0993SJ.Bruce Fields * We use a fixed fsid for a referral 6042ca0993SJ.Bruce Fields */ 6142ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MAJOR 0x8000000ULL 6242ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MINOR 0x8000000ULL 6342ca0993SJ.Bruce Fields 64b37ad28bSAl Viro static __be32 65b37ad28bSAl Viro check_filename(char *str, int len, __be32 err) 661da177e4SLinus Torvalds { 671da177e4SLinus Torvalds int i; 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds if (len == 0) 701da177e4SLinus Torvalds return nfserr_inval; 711da177e4SLinus Torvalds if (isdotent(str, len)) 721da177e4SLinus Torvalds return err; 731da177e4SLinus Torvalds for (i = 0; i < len; i++) 741da177e4SLinus Torvalds if (str[i] == '/') 751da177e4SLinus Torvalds return err; 761da177e4SLinus Torvalds return 0; 771da177e4SLinus Torvalds } 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds #define DECODE_HEAD \ 802ebbc012SAl Viro __be32 *p; \ 81b37ad28bSAl Viro __be32 status 821da177e4SLinus Torvalds #define DECODE_TAIL \ 831da177e4SLinus Torvalds status = 0; \ 841da177e4SLinus Torvalds out: \ 851da177e4SLinus Torvalds return status; \ 861da177e4SLinus Torvalds xdr_error: \ 87817cb9d4SChuck Lever dprintk("NFSD: xdr error (%s:%d)\n", \ 88817cb9d4SChuck Lever __FILE__, __LINE__); \ 891da177e4SLinus Torvalds status = nfserr_bad_xdr; \ 901da177e4SLinus Torvalds goto out 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds #define READ32(x) (x) = ntohl(*p++) 931da177e4SLinus Torvalds #define READ64(x) do { \ 941da177e4SLinus Torvalds (x) = (u64)ntohl(*p++) << 32; \ 951da177e4SLinus Torvalds (x) |= ntohl(*p++); \ 961da177e4SLinus Torvalds } while (0) 971da177e4SLinus Torvalds #define READTIME(x) do { \ 981da177e4SLinus Torvalds p++; \ 991da177e4SLinus Torvalds (x) = ntohl(*p++); \ 1001da177e4SLinus Torvalds p++; \ 1011da177e4SLinus Torvalds } while (0) 1021da177e4SLinus Torvalds #define READMEM(x,nbytes) do { \ 1031da177e4SLinus Torvalds x = (char *)p; \ 1041da177e4SLinus Torvalds p += XDR_QUADLEN(nbytes); \ 1051da177e4SLinus Torvalds } while (0) 1061da177e4SLinus Torvalds #define SAVEMEM(x,nbytes) do { \ 1071da177e4SLinus Torvalds if (!(x = (p==argp->tmp || p == argp->tmpp) ? \ 1081da177e4SLinus Torvalds savemem(argp, p, nbytes) : \ 1091da177e4SLinus Torvalds (char *)p)) { \ 110817cb9d4SChuck Lever dprintk("NFSD: xdr error (%s:%d)\n", \ 111817cb9d4SChuck Lever __FILE__, __LINE__); \ 1121da177e4SLinus Torvalds goto xdr_error; \ 1131da177e4SLinus Torvalds } \ 1141da177e4SLinus Torvalds p += XDR_QUADLEN(nbytes); \ 1151da177e4SLinus Torvalds } while (0) 1161da177e4SLinus Torvalds #define COPYMEM(x,nbytes) do { \ 1171da177e4SLinus Torvalds memcpy((x), p, nbytes); \ 1181da177e4SLinus Torvalds p += XDR_QUADLEN(nbytes); \ 1191da177e4SLinus Torvalds } while (0) 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds /* READ_BUF, read_buf(): nbytes must be <= PAGE_SIZE */ 1221da177e4SLinus Torvalds #define READ_BUF(nbytes) do { \ 1231da177e4SLinus Torvalds if (nbytes <= (u32)((char *)argp->end - (char *)argp->p)) { \ 1241da177e4SLinus Torvalds p = argp->p; \ 1251da177e4SLinus Torvalds argp->p += XDR_QUADLEN(nbytes); \ 1261da177e4SLinus Torvalds } else if (!(p = read_buf(argp, nbytes))) { \ 127817cb9d4SChuck Lever dprintk("NFSD: xdr error (%s:%d)\n", \ 128817cb9d4SChuck Lever __FILE__, __LINE__); \ 1291da177e4SLinus Torvalds goto xdr_error; \ 1301da177e4SLinus Torvalds } \ 1311da177e4SLinus Torvalds } while (0) 1321da177e4SLinus Torvalds 133ca2a05aaSJ. Bruce Fields static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes) 1341da177e4SLinus Torvalds { 1351da177e4SLinus Torvalds /* We want more bytes than seem to be available. 1361da177e4SLinus Torvalds * Maybe we need a new page, maybe we have just run out 1371da177e4SLinus Torvalds */ 138ca2a05aaSJ. Bruce Fields unsigned int avail = (char *)argp->end - (char *)argp->p; 1392ebbc012SAl Viro __be32 *p; 1401da177e4SLinus Torvalds if (avail + argp->pagelen < nbytes) 1411da177e4SLinus Torvalds return NULL; 1421da177e4SLinus Torvalds if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */ 1431da177e4SLinus Torvalds return NULL; 1441da177e4SLinus Torvalds /* ok, we can do it with the current plus the next page */ 1451da177e4SLinus Torvalds if (nbytes <= sizeof(argp->tmp)) 1461da177e4SLinus Torvalds p = argp->tmp; 1471da177e4SLinus Torvalds else { 1481da177e4SLinus Torvalds kfree(argp->tmpp); 1491da177e4SLinus Torvalds p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL); 1501da177e4SLinus Torvalds if (!p) 1511da177e4SLinus Torvalds return NULL; 1521da177e4SLinus Torvalds 1531da177e4SLinus Torvalds } 154ca2a05aaSJ. Bruce Fields /* 155ca2a05aaSJ. Bruce Fields * The following memcpy is safe because read_buf is always 156ca2a05aaSJ. Bruce Fields * called with nbytes > avail, and the two cases above both 157ca2a05aaSJ. Bruce Fields * guarantee p points to at least nbytes bytes. 158ca2a05aaSJ. Bruce Fields */ 1591da177e4SLinus Torvalds memcpy(p, argp->p, avail); 1601da177e4SLinus Torvalds /* step to next page */ 1611da177e4SLinus Torvalds argp->p = page_address(argp->pagelist[0]); 1621da177e4SLinus Torvalds argp->pagelist++; 1631da177e4SLinus Torvalds if (argp->pagelen < PAGE_SIZE) { 1642bc3c117SNeil Brown argp->end = argp->p + (argp->pagelen>>2); 1651da177e4SLinus Torvalds argp->pagelen = 0; 1661da177e4SLinus Torvalds } else { 1672bc3c117SNeil Brown argp->end = argp->p + (PAGE_SIZE>>2); 1681da177e4SLinus Torvalds argp->pagelen -= PAGE_SIZE; 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds memcpy(((char*)p)+avail, argp->p, (nbytes - avail)); 1711da177e4SLinus Torvalds argp->p += XDR_QUADLEN(nbytes - avail); 1721da177e4SLinus Torvalds return p; 1731da177e4SLinus Torvalds } 1741da177e4SLinus Torvalds 17560adfc50SAndy Adamson static int zero_clientid(clientid_t *clid) 17660adfc50SAndy Adamson { 17760adfc50SAndy Adamson return (clid->cl_boot == 0) && (clid->cl_id == 0); 17860adfc50SAndy Adamson } 17960adfc50SAndy Adamson 1801da177e4SLinus Torvalds static int 1811da177e4SLinus Torvalds defer_free(struct nfsd4_compoundargs *argp, 1821da177e4SLinus Torvalds void (*release)(const void *), void *p) 1831da177e4SLinus Torvalds { 1841da177e4SLinus Torvalds struct tmpbuf *tb; 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds tb = kmalloc(sizeof(*tb), GFP_KERNEL); 1871da177e4SLinus Torvalds if (!tb) 1881da177e4SLinus Torvalds return -ENOMEM; 1891da177e4SLinus Torvalds tb->buf = p; 1901da177e4SLinus Torvalds tb->release = release; 1911da177e4SLinus Torvalds tb->next = argp->to_free; 1921da177e4SLinus Torvalds argp->to_free = tb; 1931da177e4SLinus Torvalds return 0; 1941da177e4SLinus Torvalds } 1951da177e4SLinus Torvalds 1962ebbc012SAl Viro static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes) 1971da177e4SLinus Torvalds { 1981da177e4SLinus Torvalds if (p == argp->tmp) { 199a4db5fe5SJ. Bruce Fields p = kmalloc(nbytes, GFP_KERNEL); 200a4db5fe5SJ. Bruce Fields if (!p) 201a4db5fe5SJ. Bruce Fields return NULL; 2021da177e4SLinus Torvalds memcpy(p, argp->tmp, nbytes); 2031da177e4SLinus Torvalds } else { 20473dff8beSEric Sesterhenn BUG_ON(p != argp->tmpp); 2051da177e4SLinus Torvalds argp->tmpp = NULL; 2061da177e4SLinus Torvalds } 2071da177e4SLinus Torvalds if (defer_free(argp, kfree, p)) { 208a4db5fe5SJ. Bruce Fields kfree(p); 2091da177e4SLinus Torvalds return NULL; 2101da177e4SLinus Torvalds } else 2111da177e4SLinus Torvalds return (char *)p; 2121da177e4SLinus Torvalds } 2131da177e4SLinus Torvalds 214b37ad28bSAl Viro static __be32 2151da177e4SLinus Torvalds nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval) 2161da177e4SLinus Torvalds { 2171da177e4SLinus Torvalds u32 bmlen; 2181da177e4SLinus Torvalds DECODE_HEAD; 2191da177e4SLinus Torvalds 2201da177e4SLinus Torvalds bmval[0] = 0; 2211da177e4SLinus Torvalds bmval[1] = 0; 2227e705706SAndy Adamson bmval[2] = 0; 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds READ_BUF(4); 2251da177e4SLinus Torvalds READ32(bmlen); 2261da177e4SLinus Torvalds if (bmlen > 1000) 2271da177e4SLinus Torvalds goto xdr_error; 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds READ_BUF(bmlen << 2); 2301da177e4SLinus Torvalds if (bmlen > 0) 2311da177e4SLinus Torvalds READ32(bmval[0]); 2321da177e4SLinus Torvalds if (bmlen > 1) 2331da177e4SLinus Torvalds READ32(bmval[1]); 2347e705706SAndy Adamson if (bmlen > 2) 2357e705706SAndy Adamson READ32(bmval[2]); 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds DECODE_TAIL; 2381da177e4SLinus Torvalds } 2391da177e4SLinus Torvalds 240b37ad28bSAl Viro static __be32 2413c8e0316SYu Zhiguo nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, 242c0d6fc8aSBenny Halevy struct iattr *iattr, struct nfs4_acl **acl) 2431da177e4SLinus Torvalds { 2441da177e4SLinus Torvalds int expected_len, len = 0; 2451da177e4SLinus Torvalds u32 dummy32; 2461da177e4SLinus Torvalds char *buf; 247b8dd7b9aSAl Viro int host_err; 2481da177e4SLinus Torvalds 2491da177e4SLinus Torvalds DECODE_HEAD; 2501da177e4SLinus Torvalds iattr->ia_valid = 0; 2511da177e4SLinus Torvalds if ((status = nfsd4_decode_bitmap(argp, bmval))) 2521da177e4SLinus Torvalds return status; 2531da177e4SLinus Torvalds 2541da177e4SLinus Torvalds READ_BUF(4); 2551da177e4SLinus Torvalds READ32(expected_len); 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds if (bmval[0] & FATTR4_WORD0_SIZE) { 2581da177e4SLinus Torvalds READ_BUF(8); 2591da177e4SLinus Torvalds len += 8; 2601da177e4SLinus Torvalds READ64(iattr->ia_size); 2611da177e4SLinus Torvalds iattr->ia_valid |= ATTR_SIZE; 2621da177e4SLinus Torvalds } 2631da177e4SLinus Torvalds if (bmval[0] & FATTR4_WORD0_ACL) { 26428e05dd8SJ. Bruce Fields int nace; 26528e05dd8SJ. Bruce Fields struct nfs4_ace *ace; 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds READ_BUF(4); len += 4; 2681da177e4SLinus Torvalds READ32(nace); 2691da177e4SLinus Torvalds 27028e05dd8SJ. Bruce Fields if (nace > NFS4_ACL_MAX) 27128e05dd8SJ. Bruce Fields return nfserr_resource; 27228e05dd8SJ. Bruce Fields 27328e05dd8SJ. Bruce Fields *acl = nfs4_acl_new(nace); 2741da177e4SLinus Torvalds if (*acl == NULL) { 275b8dd7b9aSAl Viro host_err = -ENOMEM; 2761da177e4SLinus Torvalds goto out_nfserr; 2771da177e4SLinus Torvalds } 27828e05dd8SJ. Bruce Fields defer_free(argp, kfree, *acl); 2791da177e4SLinus Torvalds 28028e05dd8SJ. Bruce Fields (*acl)->naces = nace; 28128e05dd8SJ. Bruce Fields for (ace = (*acl)->aces; ace < (*acl)->aces + nace; ace++) { 2821da177e4SLinus Torvalds READ_BUF(16); len += 16; 28328e05dd8SJ. Bruce Fields READ32(ace->type); 28428e05dd8SJ. Bruce Fields READ32(ace->flag); 28528e05dd8SJ. Bruce Fields READ32(ace->access_mask); 2861da177e4SLinus Torvalds READ32(dummy32); 2871da177e4SLinus Torvalds READ_BUF(dummy32); 2881da177e4SLinus Torvalds len += XDR_QUADLEN(dummy32) << 2; 2891da177e4SLinus Torvalds READMEM(buf, dummy32); 29028e05dd8SJ. Bruce Fields ace->whotype = nfs4_acl_get_whotype(buf, dummy32); 291b8dd7b9aSAl Viro host_err = 0; 29228e05dd8SJ. Bruce Fields if (ace->whotype != NFS4_ACL_WHO_NAMED) 29328e05dd8SJ. Bruce Fields ace->who = 0; 29428e05dd8SJ. Bruce Fields else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP) 295b8dd7b9aSAl Viro host_err = nfsd_map_name_to_gid(argp->rqstp, 29628e05dd8SJ. Bruce Fields buf, dummy32, &ace->who); 2971da177e4SLinus Torvalds else 298b8dd7b9aSAl Viro host_err = nfsd_map_name_to_uid(argp->rqstp, 29928e05dd8SJ. Bruce Fields buf, dummy32, &ace->who); 300b8dd7b9aSAl Viro if (host_err) 3011da177e4SLinus Torvalds goto out_nfserr; 3021da177e4SLinus Torvalds } 3031da177e4SLinus Torvalds } else 3041da177e4SLinus Torvalds *acl = NULL; 3051da177e4SLinus Torvalds if (bmval[1] & FATTR4_WORD1_MODE) { 3061da177e4SLinus Torvalds READ_BUF(4); 3071da177e4SLinus Torvalds len += 4; 3081da177e4SLinus Torvalds READ32(iattr->ia_mode); 3091da177e4SLinus Torvalds iattr->ia_mode &= (S_IFMT | S_IALLUGO); 3101da177e4SLinus Torvalds iattr->ia_valid |= ATTR_MODE; 3111da177e4SLinus Torvalds } 3121da177e4SLinus Torvalds if (bmval[1] & FATTR4_WORD1_OWNER) { 3131da177e4SLinus Torvalds READ_BUF(4); 3141da177e4SLinus Torvalds len += 4; 3151da177e4SLinus Torvalds READ32(dummy32); 3161da177e4SLinus Torvalds READ_BUF(dummy32); 3171da177e4SLinus Torvalds len += (XDR_QUADLEN(dummy32) << 2); 3181da177e4SLinus Torvalds READMEM(buf, dummy32); 319b8dd7b9aSAl Viro if ((host_err = nfsd_map_name_to_uid(argp->rqstp, buf, dummy32, &iattr->ia_uid))) 3201da177e4SLinus Torvalds goto out_nfserr; 3211da177e4SLinus Torvalds iattr->ia_valid |= ATTR_UID; 3221da177e4SLinus Torvalds } 3231da177e4SLinus Torvalds if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) { 3241da177e4SLinus Torvalds READ_BUF(4); 3251da177e4SLinus Torvalds len += 4; 3261da177e4SLinus Torvalds READ32(dummy32); 3271da177e4SLinus Torvalds READ_BUF(dummy32); 3281da177e4SLinus Torvalds len += (XDR_QUADLEN(dummy32) << 2); 3291da177e4SLinus Torvalds READMEM(buf, dummy32); 330b8dd7b9aSAl Viro if ((host_err = nfsd_map_name_to_gid(argp->rqstp, buf, dummy32, &iattr->ia_gid))) 3311da177e4SLinus Torvalds goto out_nfserr; 3321da177e4SLinus Torvalds iattr->ia_valid |= ATTR_GID; 3331da177e4SLinus Torvalds } 3341da177e4SLinus Torvalds if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) { 3351da177e4SLinus Torvalds READ_BUF(4); 3361da177e4SLinus Torvalds len += 4; 3371da177e4SLinus Torvalds READ32(dummy32); 3381da177e4SLinus Torvalds switch (dummy32) { 3391da177e4SLinus Torvalds case NFS4_SET_TO_CLIENT_TIME: 3401da177e4SLinus Torvalds /* We require the high 32 bits of 'seconds' to be 0, and we ignore 3411da177e4SLinus Torvalds all 32 bits of 'nseconds'. */ 3421da177e4SLinus Torvalds READ_BUF(12); 3431da177e4SLinus Torvalds len += 12; 3441da177e4SLinus Torvalds READ32(dummy32); 3451da177e4SLinus Torvalds if (dummy32) 3461da177e4SLinus Torvalds return nfserr_inval; 3471da177e4SLinus Torvalds READ32(iattr->ia_atime.tv_sec); 3481da177e4SLinus Torvalds READ32(iattr->ia_atime.tv_nsec); 3491da177e4SLinus Torvalds if (iattr->ia_atime.tv_nsec >= (u32)1000000000) 3501da177e4SLinus Torvalds return nfserr_inval; 3511da177e4SLinus Torvalds iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); 3521da177e4SLinus Torvalds break; 3531da177e4SLinus Torvalds case NFS4_SET_TO_SERVER_TIME: 3541da177e4SLinus Torvalds iattr->ia_valid |= ATTR_ATIME; 3551da177e4SLinus Torvalds break; 3561da177e4SLinus Torvalds default: 3571da177e4SLinus Torvalds goto xdr_error; 3581da177e4SLinus Torvalds } 3591da177e4SLinus Torvalds } 3601da177e4SLinus Torvalds if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) { 3611da177e4SLinus Torvalds READ_BUF(4); 3621da177e4SLinus Torvalds len += 4; 3631da177e4SLinus Torvalds READ32(dummy32); 3641da177e4SLinus Torvalds switch (dummy32) { 3651da177e4SLinus Torvalds case NFS4_SET_TO_CLIENT_TIME: 3661da177e4SLinus Torvalds /* We require the high 32 bits of 'seconds' to be 0, and we ignore 3671da177e4SLinus Torvalds all 32 bits of 'nseconds'. */ 3681da177e4SLinus Torvalds READ_BUF(12); 3691da177e4SLinus Torvalds len += 12; 3701da177e4SLinus Torvalds READ32(dummy32); 3711da177e4SLinus Torvalds if (dummy32) 3721da177e4SLinus Torvalds return nfserr_inval; 3731da177e4SLinus Torvalds READ32(iattr->ia_mtime.tv_sec); 3741da177e4SLinus Torvalds READ32(iattr->ia_mtime.tv_nsec); 3751da177e4SLinus Torvalds if (iattr->ia_mtime.tv_nsec >= (u32)1000000000) 3761da177e4SLinus Torvalds return nfserr_inval; 3771da177e4SLinus Torvalds iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); 3781da177e4SLinus Torvalds break; 3791da177e4SLinus Torvalds case NFS4_SET_TO_SERVER_TIME: 3801da177e4SLinus Torvalds iattr->ia_valid |= ATTR_MTIME; 3811da177e4SLinus Torvalds break; 3821da177e4SLinus Torvalds default: 3831da177e4SLinus Torvalds goto xdr_error; 3841da177e4SLinus Torvalds } 3851da177e4SLinus Torvalds } 3863c8e0316SYu Zhiguo if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0 3873c8e0316SYu Zhiguo || bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1 3883c8e0316SYu Zhiguo || bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2) 3893c8e0316SYu Zhiguo READ_BUF(expected_len - len); 3903c8e0316SYu Zhiguo else if (len != expected_len) 3911da177e4SLinus Torvalds goto xdr_error; 3921da177e4SLinus Torvalds 3931da177e4SLinus Torvalds DECODE_TAIL; 3941da177e4SLinus Torvalds 3951da177e4SLinus Torvalds out_nfserr: 396b8dd7b9aSAl Viro status = nfserrno(host_err); 3971da177e4SLinus Torvalds goto out; 3981da177e4SLinus Torvalds } 3991da177e4SLinus Torvalds 400b37ad28bSAl Viro static __be32 401e31a1b66SBenny Halevy nfsd4_decode_stateid(struct nfsd4_compoundargs *argp, stateid_t *sid) 402e31a1b66SBenny Halevy { 403e31a1b66SBenny Halevy DECODE_HEAD; 404e31a1b66SBenny Halevy 405e31a1b66SBenny Halevy READ_BUF(sizeof(stateid_t)); 406e31a1b66SBenny Halevy READ32(sid->si_generation); 407e31a1b66SBenny Halevy COPYMEM(&sid->si_opaque, sizeof(stateid_opaque_t)); 408e31a1b66SBenny Halevy 409e31a1b66SBenny Halevy DECODE_TAIL; 410e31a1b66SBenny Halevy } 411e31a1b66SBenny Halevy 412e31a1b66SBenny Halevy static __be32 4131da177e4SLinus Torvalds nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access) 4141da177e4SLinus Torvalds { 4151da177e4SLinus Torvalds DECODE_HEAD; 4161da177e4SLinus Torvalds 4171da177e4SLinus Torvalds READ_BUF(4); 4181da177e4SLinus Torvalds READ32(access->ac_req_access); 4191da177e4SLinus Torvalds 4201da177e4SLinus Torvalds DECODE_TAIL; 4211da177e4SLinus Torvalds } 4221da177e4SLinus Torvalds 423b37ad28bSAl Viro static __be32 4241da177e4SLinus Torvalds nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close) 4251da177e4SLinus Torvalds { 4261da177e4SLinus Torvalds DECODE_HEAD; 4271da177e4SLinus Torvalds 4281da177e4SLinus Torvalds close->cl_stateowner = NULL; 429e31a1b66SBenny Halevy READ_BUF(4); 4301da177e4SLinus Torvalds READ32(close->cl_seqid); 431e31a1b66SBenny Halevy return nfsd4_decode_stateid(argp, &close->cl_stateid); 4321da177e4SLinus Torvalds 4331da177e4SLinus Torvalds DECODE_TAIL; 4341da177e4SLinus Torvalds } 4351da177e4SLinus Torvalds 4361da177e4SLinus Torvalds 437b37ad28bSAl Viro static __be32 4381da177e4SLinus Torvalds nfsd4_decode_commit(struct nfsd4_compoundargs *argp, struct nfsd4_commit *commit) 4391da177e4SLinus Torvalds { 4401da177e4SLinus Torvalds DECODE_HEAD; 4411da177e4SLinus Torvalds 4421da177e4SLinus Torvalds READ_BUF(12); 4431da177e4SLinus Torvalds READ64(commit->co_offset); 4441da177e4SLinus Torvalds READ32(commit->co_count); 4451da177e4SLinus Torvalds 4461da177e4SLinus Torvalds DECODE_TAIL; 4471da177e4SLinus Torvalds } 4481da177e4SLinus Torvalds 449b37ad28bSAl Viro static __be32 4501da177e4SLinus Torvalds nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create) 4511da177e4SLinus Torvalds { 4521da177e4SLinus Torvalds DECODE_HEAD; 4531da177e4SLinus Torvalds 4541da177e4SLinus Torvalds READ_BUF(4); 4551da177e4SLinus Torvalds READ32(create->cr_type); 4561da177e4SLinus Torvalds switch (create->cr_type) { 4571da177e4SLinus Torvalds case NF4LNK: 4581da177e4SLinus Torvalds READ_BUF(4); 4591da177e4SLinus Torvalds READ32(create->cr_linklen); 4601da177e4SLinus Torvalds READ_BUF(create->cr_linklen); 4611da177e4SLinus Torvalds SAVEMEM(create->cr_linkname, create->cr_linklen); 4621da177e4SLinus Torvalds break; 4631da177e4SLinus Torvalds case NF4BLK: 4641da177e4SLinus Torvalds case NF4CHR: 4651da177e4SLinus Torvalds READ_BUF(8); 4661da177e4SLinus Torvalds READ32(create->cr_specdata1); 4671da177e4SLinus Torvalds READ32(create->cr_specdata2); 4681da177e4SLinus Torvalds break; 4691da177e4SLinus Torvalds case NF4SOCK: 4701da177e4SLinus Torvalds case NF4FIFO: 4711da177e4SLinus Torvalds case NF4DIR: 4721da177e4SLinus Torvalds default: 4731da177e4SLinus Torvalds break; 4741da177e4SLinus Torvalds } 4751da177e4SLinus Torvalds 4761da177e4SLinus Torvalds READ_BUF(4); 4771da177e4SLinus Torvalds READ32(create->cr_namelen); 4781da177e4SLinus Torvalds READ_BUF(create->cr_namelen); 4791da177e4SLinus Torvalds SAVEMEM(create->cr_name, create->cr_namelen); 4801da177e4SLinus Torvalds if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval))) 4811da177e4SLinus Torvalds return status; 4821da177e4SLinus Torvalds 4833c8e0316SYu Zhiguo status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr, 4843c8e0316SYu Zhiguo &create->cr_acl); 485c0d6fc8aSBenny Halevy if (status) 4861da177e4SLinus Torvalds goto out; 4871da177e4SLinus Torvalds 4881da177e4SLinus Torvalds DECODE_TAIL; 4891da177e4SLinus Torvalds } 4901da177e4SLinus Torvalds 491b37ad28bSAl Viro static inline __be32 4921da177e4SLinus Torvalds nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr) 4931da177e4SLinus Torvalds { 494e31a1b66SBenny Halevy return nfsd4_decode_stateid(argp, &dr->dr_stateid); 4951da177e4SLinus Torvalds } 4961da177e4SLinus Torvalds 497b37ad28bSAl Viro static inline __be32 4981da177e4SLinus Torvalds nfsd4_decode_getattr(struct nfsd4_compoundargs *argp, struct nfsd4_getattr *getattr) 4991da177e4SLinus Torvalds { 5001da177e4SLinus Torvalds return nfsd4_decode_bitmap(argp, getattr->ga_bmval); 5011da177e4SLinus Torvalds } 5021da177e4SLinus Torvalds 503b37ad28bSAl Viro static __be32 5041da177e4SLinus Torvalds nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link) 5051da177e4SLinus Torvalds { 5061da177e4SLinus Torvalds DECODE_HEAD; 5071da177e4SLinus Torvalds 5081da177e4SLinus Torvalds READ_BUF(4); 5091da177e4SLinus Torvalds READ32(link->li_namelen); 5101da177e4SLinus Torvalds READ_BUF(link->li_namelen); 5111da177e4SLinus Torvalds SAVEMEM(link->li_name, link->li_namelen); 5121da177e4SLinus Torvalds if ((status = check_filename(link->li_name, link->li_namelen, nfserr_inval))) 5131da177e4SLinus Torvalds return status; 5141da177e4SLinus Torvalds 5151da177e4SLinus Torvalds DECODE_TAIL; 5161da177e4SLinus Torvalds } 5171da177e4SLinus Torvalds 518b37ad28bSAl Viro static __be32 5191da177e4SLinus Torvalds nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock) 5201da177e4SLinus Torvalds { 5211da177e4SLinus Torvalds DECODE_HEAD; 5221da177e4SLinus Torvalds 5233a65588aSJ. Bruce Fields lock->lk_replay_owner = NULL; 5241da177e4SLinus Torvalds /* 5251da177e4SLinus Torvalds * type, reclaim(boolean), offset, length, new_lock_owner(boolean) 5261da177e4SLinus Torvalds */ 5271da177e4SLinus Torvalds READ_BUF(28); 5281da177e4SLinus Torvalds READ32(lock->lk_type); 5291da177e4SLinus Torvalds if ((lock->lk_type < NFS4_READ_LT) || (lock->lk_type > NFS4_WRITEW_LT)) 5301da177e4SLinus Torvalds goto xdr_error; 5311da177e4SLinus Torvalds READ32(lock->lk_reclaim); 5321da177e4SLinus Torvalds READ64(lock->lk_offset); 5331da177e4SLinus Torvalds READ64(lock->lk_length); 5341da177e4SLinus Torvalds READ32(lock->lk_is_new); 5351da177e4SLinus Torvalds 5361da177e4SLinus Torvalds if (lock->lk_is_new) { 537e31a1b66SBenny Halevy READ_BUF(4); 5381da177e4SLinus Torvalds READ32(lock->lk_new_open_seqid); 539e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &lock->lk_new_open_stateid); 540e31a1b66SBenny Halevy if (status) 541e31a1b66SBenny Halevy return status; 542e31a1b66SBenny Halevy READ_BUF(8 + sizeof(clientid_t)); 5431da177e4SLinus Torvalds READ32(lock->lk_new_lock_seqid); 5441da177e4SLinus Torvalds COPYMEM(&lock->lk_new_clientid, sizeof(clientid_t)); 5451da177e4SLinus Torvalds READ32(lock->lk_new_owner.len); 5461da177e4SLinus Torvalds READ_BUF(lock->lk_new_owner.len); 5471da177e4SLinus Torvalds READMEM(lock->lk_new_owner.data, lock->lk_new_owner.len); 5481da177e4SLinus Torvalds } else { 549e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &lock->lk_old_lock_stateid); 550e31a1b66SBenny Halevy if (status) 551e31a1b66SBenny Halevy return status; 552e31a1b66SBenny Halevy READ_BUF(4); 5531da177e4SLinus Torvalds READ32(lock->lk_old_lock_seqid); 5541da177e4SLinus Torvalds } 5551da177e4SLinus Torvalds 5561da177e4SLinus Torvalds DECODE_TAIL; 5571da177e4SLinus Torvalds } 5581da177e4SLinus Torvalds 559b37ad28bSAl Viro static __be32 5601da177e4SLinus Torvalds nfsd4_decode_lockt(struct nfsd4_compoundargs *argp, struct nfsd4_lockt *lockt) 5611da177e4SLinus Torvalds { 5621da177e4SLinus Torvalds DECODE_HEAD; 5631da177e4SLinus Torvalds 5641da177e4SLinus Torvalds READ_BUF(32); 5651da177e4SLinus Torvalds READ32(lockt->lt_type); 5661da177e4SLinus Torvalds if((lockt->lt_type < NFS4_READ_LT) || (lockt->lt_type > NFS4_WRITEW_LT)) 5671da177e4SLinus Torvalds goto xdr_error; 5681da177e4SLinus Torvalds READ64(lockt->lt_offset); 5691da177e4SLinus Torvalds READ64(lockt->lt_length); 5701da177e4SLinus Torvalds COPYMEM(&lockt->lt_clientid, 8); 5711da177e4SLinus Torvalds READ32(lockt->lt_owner.len); 5721da177e4SLinus Torvalds READ_BUF(lockt->lt_owner.len); 5731da177e4SLinus Torvalds READMEM(lockt->lt_owner.data, lockt->lt_owner.len); 5741da177e4SLinus Torvalds 57560adfc50SAndy Adamson if (argp->minorversion && !zero_clientid(&lockt->lt_clientid)) 57660adfc50SAndy Adamson return nfserr_inval; 5771da177e4SLinus Torvalds DECODE_TAIL; 5781da177e4SLinus Torvalds } 5791da177e4SLinus Torvalds 580b37ad28bSAl Viro static __be32 5811da177e4SLinus Torvalds nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku) 5821da177e4SLinus Torvalds { 5831da177e4SLinus Torvalds DECODE_HEAD; 5841da177e4SLinus Torvalds 5851da177e4SLinus Torvalds locku->lu_stateowner = NULL; 586e31a1b66SBenny Halevy READ_BUF(8); 5871da177e4SLinus Torvalds READ32(locku->lu_type); 5881da177e4SLinus Torvalds if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT)) 5891da177e4SLinus Torvalds goto xdr_error; 5901da177e4SLinus Torvalds READ32(locku->lu_seqid); 591e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &locku->lu_stateid); 592e31a1b66SBenny Halevy if (status) 593e31a1b66SBenny Halevy return status; 594e31a1b66SBenny Halevy READ_BUF(16); 5951da177e4SLinus Torvalds READ64(locku->lu_offset); 5961da177e4SLinus Torvalds READ64(locku->lu_length); 5971da177e4SLinus Torvalds 5981da177e4SLinus Torvalds DECODE_TAIL; 5991da177e4SLinus Torvalds } 6001da177e4SLinus Torvalds 601b37ad28bSAl Viro static __be32 6021da177e4SLinus Torvalds nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup) 6031da177e4SLinus Torvalds { 6041da177e4SLinus Torvalds DECODE_HEAD; 6051da177e4SLinus Torvalds 6061da177e4SLinus Torvalds READ_BUF(4); 6071da177e4SLinus Torvalds READ32(lookup->lo_len); 6081da177e4SLinus Torvalds READ_BUF(lookup->lo_len); 6091da177e4SLinus Torvalds SAVEMEM(lookup->lo_name, lookup->lo_len); 6101da177e4SLinus Torvalds if ((status = check_filename(lookup->lo_name, lookup->lo_len, nfserr_noent))) 6111da177e4SLinus Torvalds return status; 6121da177e4SLinus Torvalds 6131da177e4SLinus Torvalds DECODE_TAIL; 6141da177e4SLinus Torvalds } 6151da177e4SLinus Torvalds 616b37ad28bSAl Viro static __be32 6171da177e4SLinus Torvalds nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) 6181da177e4SLinus Torvalds { 6191da177e4SLinus Torvalds DECODE_HEAD; 6201da177e4SLinus Torvalds 6211da177e4SLinus Torvalds memset(open->op_bmval, 0, sizeof(open->op_bmval)); 6221da177e4SLinus Torvalds open->op_iattr.ia_valid = 0; 6231da177e4SLinus Torvalds open->op_stateowner = NULL; 6241da177e4SLinus Torvalds 6251da177e4SLinus Torvalds /* seqid, share_access, share_deny, clientid, ownerlen */ 6261da177e4SLinus Torvalds READ_BUF(16 + sizeof(clientid_t)); 6271da177e4SLinus Torvalds READ32(open->op_seqid); 6281da177e4SLinus Torvalds READ32(open->op_share_access); 6291da177e4SLinus Torvalds READ32(open->op_share_deny); 6301da177e4SLinus Torvalds COPYMEM(&open->op_clientid, sizeof(clientid_t)); 6311da177e4SLinus Torvalds READ32(open->op_owner.len); 6321da177e4SLinus Torvalds 6331da177e4SLinus Torvalds /* owner, open_flag */ 6341da177e4SLinus Torvalds READ_BUF(open->op_owner.len + 4); 6351da177e4SLinus Torvalds SAVEMEM(open->op_owner.data, open->op_owner.len); 6361da177e4SLinus Torvalds READ32(open->op_create); 6371da177e4SLinus Torvalds switch (open->op_create) { 6381da177e4SLinus Torvalds case NFS4_OPEN_NOCREATE: 6391da177e4SLinus Torvalds break; 6401da177e4SLinus Torvalds case NFS4_OPEN_CREATE: 6411da177e4SLinus Torvalds READ_BUF(4); 6421da177e4SLinus Torvalds READ32(open->op_createmode); 6431da177e4SLinus Torvalds switch (open->op_createmode) { 6441da177e4SLinus Torvalds case NFS4_CREATE_UNCHECKED: 6451da177e4SLinus Torvalds case NFS4_CREATE_GUARDED: 646c0d6fc8aSBenny Halevy status = nfsd4_decode_fattr(argp, open->op_bmval, 6473c8e0316SYu Zhiguo &open->op_iattr, &open->op_acl); 648c0d6fc8aSBenny Halevy if (status) 6491da177e4SLinus Torvalds goto out; 6501da177e4SLinus Torvalds break; 6511da177e4SLinus Torvalds case NFS4_CREATE_EXCLUSIVE: 6521da177e4SLinus Torvalds READ_BUF(8); 6531da177e4SLinus Torvalds COPYMEM(open->op_verf.data, 8); 6541da177e4SLinus Torvalds break; 65579fb54abSBenny Halevy case NFS4_CREATE_EXCLUSIVE4_1: 65679fb54abSBenny Halevy if (argp->minorversion < 1) 65779fb54abSBenny Halevy goto xdr_error; 65879fb54abSBenny Halevy READ_BUF(8); 65979fb54abSBenny Halevy COPYMEM(open->op_verf.data, 8); 66079fb54abSBenny Halevy status = nfsd4_decode_fattr(argp, open->op_bmval, 6613c8e0316SYu Zhiguo &open->op_iattr, &open->op_acl); 66279fb54abSBenny Halevy if (status) 66379fb54abSBenny Halevy goto out; 66479fb54abSBenny Halevy break; 6651da177e4SLinus Torvalds default: 6661da177e4SLinus Torvalds goto xdr_error; 6671da177e4SLinus Torvalds } 6681da177e4SLinus Torvalds break; 6691da177e4SLinus Torvalds default: 6701da177e4SLinus Torvalds goto xdr_error; 6711da177e4SLinus Torvalds } 6721da177e4SLinus Torvalds 6731da177e4SLinus Torvalds /* open_claim */ 6741da177e4SLinus Torvalds READ_BUF(4); 6751da177e4SLinus Torvalds READ32(open->op_claim_type); 6761da177e4SLinus Torvalds switch (open->op_claim_type) { 6771da177e4SLinus Torvalds case NFS4_OPEN_CLAIM_NULL: 6781da177e4SLinus Torvalds case NFS4_OPEN_CLAIM_DELEGATE_PREV: 6791da177e4SLinus Torvalds READ_BUF(4); 6801da177e4SLinus Torvalds READ32(open->op_fname.len); 6811da177e4SLinus Torvalds READ_BUF(open->op_fname.len); 6821da177e4SLinus Torvalds SAVEMEM(open->op_fname.data, open->op_fname.len); 6831da177e4SLinus Torvalds if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval))) 6841da177e4SLinus Torvalds return status; 6851da177e4SLinus Torvalds break; 6861da177e4SLinus Torvalds case NFS4_OPEN_CLAIM_PREVIOUS: 6871da177e4SLinus Torvalds READ_BUF(4); 6881da177e4SLinus Torvalds READ32(open->op_delegate_type); 6891da177e4SLinus Torvalds break; 6901da177e4SLinus Torvalds case NFS4_OPEN_CLAIM_DELEGATE_CUR: 691e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid); 692e31a1b66SBenny Halevy if (status) 693e31a1b66SBenny Halevy return status; 694e31a1b66SBenny Halevy READ_BUF(4); 6951da177e4SLinus Torvalds READ32(open->op_fname.len); 6961da177e4SLinus Torvalds READ_BUF(open->op_fname.len); 6971da177e4SLinus Torvalds SAVEMEM(open->op_fname.data, open->op_fname.len); 6981da177e4SLinus Torvalds if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval))) 6991da177e4SLinus Torvalds return status; 7001da177e4SLinus Torvalds break; 7011da177e4SLinus Torvalds default: 7021da177e4SLinus Torvalds goto xdr_error; 7031da177e4SLinus Torvalds } 7041da177e4SLinus Torvalds 7051da177e4SLinus Torvalds DECODE_TAIL; 7061da177e4SLinus Torvalds } 7071da177e4SLinus Torvalds 708b37ad28bSAl Viro static __be32 7091da177e4SLinus Torvalds nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_confirm *open_conf) 7101da177e4SLinus Torvalds { 7111da177e4SLinus Torvalds DECODE_HEAD; 7121da177e4SLinus Torvalds 7131da177e4SLinus Torvalds open_conf->oc_stateowner = NULL; 714e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &open_conf->oc_req_stateid); 715e31a1b66SBenny Halevy if (status) 716e31a1b66SBenny Halevy return status; 717e31a1b66SBenny Halevy READ_BUF(4); 7181da177e4SLinus Torvalds READ32(open_conf->oc_seqid); 7191da177e4SLinus Torvalds 7201da177e4SLinus Torvalds DECODE_TAIL; 7211da177e4SLinus Torvalds } 7221da177e4SLinus Torvalds 723b37ad28bSAl Viro static __be32 7241da177e4SLinus Torvalds nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_downgrade *open_down) 7251da177e4SLinus Torvalds { 7261da177e4SLinus Torvalds DECODE_HEAD; 7271da177e4SLinus Torvalds 7281da177e4SLinus Torvalds open_down->od_stateowner = NULL; 729e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &open_down->od_stateid); 730e31a1b66SBenny Halevy if (status) 731e31a1b66SBenny Halevy return status; 732e31a1b66SBenny Halevy READ_BUF(12); 7331da177e4SLinus Torvalds READ32(open_down->od_seqid); 7341da177e4SLinus Torvalds READ32(open_down->od_share_access); 7351da177e4SLinus Torvalds READ32(open_down->od_share_deny); 7361da177e4SLinus Torvalds 7371da177e4SLinus Torvalds DECODE_TAIL; 7381da177e4SLinus Torvalds } 7391da177e4SLinus Torvalds 740b37ad28bSAl Viro static __be32 7411da177e4SLinus Torvalds nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh) 7421da177e4SLinus Torvalds { 7431da177e4SLinus Torvalds DECODE_HEAD; 7441da177e4SLinus Torvalds 7451da177e4SLinus Torvalds READ_BUF(4); 7461da177e4SLinus Torvalds READ32(putfh->pf_fhlen); 7471da177e4SLinus Torvalds if (putfh->pf_fhlen > NFS4_FHSIZE) 7481da177e4SLinus Torvalds goto xdr_error; 7491da177e4SLinus Torvalds READ_BUF(putfh->pf_fhlen); 7501da177e4SLinus Torvalds SAVEMEM(putfh->pf_fhval, putfh->pf_fhlen); 7511da177e4SLinus Torvalds 7521da177e4SLinus Torvalds DECODE_TAIL; 7531da177e4SLinus Torvalds } 7541da177e4SLinus Torvalds 755b37ad28bSAl Viro static __be32 7561da177e4SLinus Torvalds nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read) 7571da177e4SLinus Torvalds { 7581da177e4SLinus Torvalds DECODE_HEAD; 7591da177e4SLinus Torvalds 760e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &read->rd_stateid); 761e31a1b66SBenny Halevy if (status) 762e31a1b66SBenny Halevy return status; 763e31a1b66SBenny Halevy READ_BUF(12); 7641da177e4SLinus Torvalds READ64(read->rd_offset); 7651da177e4SLinus Torvalds READ32(read->rd_length); 7661da177e4SLinus Torvalds 7671da177e4SLinus Torvalds DECODE_TAIL; 7681da177e4SLinus Torvalds } 7691da177e4SLinus Torvalds 770b37ad28bSAl Viro static __be32 7711da177e4SLinus Torvalds nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, struct nfsd4_readdir *readdir) 7721da177e4SLinus Torvalds { 7731da177e4SLinus Torvalds DECODE_HEAD; 7741da177e4SLinus Torvalds 7751da177e4SLinus Torvalds READ_BUF(24); 7761da177e4SLinus Torvalds READ64(readdir->rd_cookie); 7771da177e4SLinus Torvalds COPYMEM(readdir->rd_verf.data, sizeof(readdir->rd_verf.data)); 7781da177e4SLinus Torvalds READ32(readdir->rd_dircount); /* just in case you needed a useless field... */ 7791da177e4SLinus Torvalds READ32(readdir->rd_maxcount); 7801da177e4SLinus Torvalds if ((status = nfsd4_decode_bitmap(argp, readdir->rd_bmval))) 7811da177e4SLinus Torvalds goto out; 7821da177e4SLinus Torvalds 7831da177e4SLinus Torvalds DECODE_TAIL; 7841da177e4SLinus Torvalds } 7851da177e4SLinus Torvalds 786b37ad28bSAl Viro static __be32 7871da177e4SLinus Torvalds nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove) 7881da177e4SLinus Torvalds { 7891da177e4SLinus Torvalds DECODE_HEAD; 7901da177e4SLinus Torvalds 7911da177e4SLinus Torvalds READ_BUF(4); 7921da177e4SLinus Torvalds READ32(remove->rm_namelen); 7931da177e4SLinus Torvalds READ_BUF(remove->rm_namelen); 7941da177e4SLinus Torvalds SAVEMEM(remove->rm_name, remove->rm_namelen); 7951da177e4SLinus Torvalds if ((status = check_filename(remove->rm_name, remove->rm_namelen, nfserr_noent))) 7961da177e4SLinus Torvalds return status; 7971da177e4SLinus Torvalds 7981da177e4SLinus Torvalds DECODE_TAIL; 7991da177e4SLinus Torvalds } 8001da177e4SLinus Torvalds 801b37ad28bSAl Viro static __be32 8021da177e4SLinus Torvalds nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename) 8031da177e4SLinus Torvalds { 8041da177e4SLinus Torvalds DECODE_HEAD; 8051da177e4SLinus Torvalds 8061da177e4SLinus Torvalds READ_BUF(4); 8071da177e4SLinus Torvalds READ32(rename->rn_snamelen); 8081da177e4SLinus Torvalds READ_BUF(rename->rn_snamelen + 4); 8091da177e4SLinus Torvalds SAVEMEM(rename->rn_sname, rename->rn_snamelen); 8101da177e4SLinus Torvalds READ32(rename->rn_tnamelen); 8111da177e4SLinus Torvalds READ_BUF(rename->rn_tnamelen); 8121da177e4SLinus Torvalds SAVEMEM(rename->rn_tname, rename->rn_tnamelen); 8131da177e4SLinus Torvalds if ((status = check_filename(rename->rn_sname, rename->rn_snamelen, nfserr_noent))) 8141da177e4SLinus Torvalds return status; 8151da177e4SLinus Torvalds if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen, nfserr_inval))) 8161da177e4SLinus Torvalds return status; 8171da177e4SLinus Torvalds 8181da177e4SLinus Torvalds DECODE_TAIL; 8191da177e4SLinus Torvalds } 8201da177e4SLinus Torvalds 821b37ad28bSAl Viro static __be32 8221da177e4SLinus Torvalds nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid) 8231da177e4SLinus Torvalds { 8241da177e4SLinus Torvalds DECODE_HEAD; 8251da177e4SLinus Torvalds 8261da177e4SLinus Torvalds READ_BUF(sizeof(clientid_t)); 8271da177e4SLinus Torvalds COPYMEM(clientid, sizeof(clientid_t)); 8281da177e4SLinus Torvalds 8291da177e4SLinus Torvalds DECODE_TAIL; 8301da177e4SLinus Torvalds } 8311da177e4SLinus Torvalds 832b37ad28bSAl Viro static __be32 833dcb488a3SAndy Adamson nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp, 834dcb488a3SAndy Adamson struct nfsd4_secinfo *secinfo) 835dcb488a3SAndy Adamson { 836dcb488a3SAndy Adamson DECODE_HEAD; 837dcb488a3SAndy Adamson 838dcb488a3SAndy Adamson READ_BUF(4); 839dcb488a3SAndy Adamson READ32(secinfo->si_namelen); 840dcb488a3SAndy Adamson READ_BUF(secinfo->si_namelen); 841dcb488a3SAndy Adamson SAVEMEM(secinfo->si_name, secinfo->si_namelen); 842dcb488a3SAndy Adamson status = check_filename(secinfo->si_name, secinfo->si_namelen, 843dcb488a3SAndy Adamson nfserr_noent); 844dcb488a3SAndy Adamson if (status) 845dcb488a3SAndy Adamson return status; 846dcb488a3SAndy Adamson DECODE_TAIL; 847dcb488a3SAndy Adamson } 848dcb488a3SAndy Adamson 849dcb488a3SAndy Adamson static __be32 8501da177e4SLinus Torvalds nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr) 8511da177e4SLinus Torvalds { 852e31a1b66SBenny Halevy __be32 status; 8531da177e4SLinus Torvalds 854e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &setattr->sa_stateid); 855e31a1b66SBenny Halevy if (status) 856e31a1b66SBenny Halevy return status; 8573c8e0316SYu Zhiguo return nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr, 8583c8e0316SYu Zhiguo &setattr->sa_acl); 8591da177e4SLinus Torvalds } 8601da177e4SLinus Torvalds 861b37ad28bSAl Viro static __be32 8621da177e4SLinus Torvalds nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid *setclientid) 8631da177e4SLinus Torvalds { 8641da177e4SLinus Torvalds DECODE_HEAD; 8651da177e4SLinus Torvalds 8661da177e4SLinus Torvalds READ_BUF(12); 8671da177e4SLinus Torvalds COPYMEM(setclientid->se_verf.data, 8); 8681da177e4SLinus Torvalds READ32(setclientid->se_namelen); 8691da177e4SLinus Torvalds 8701da177e4SLinus Torvalds READ_BUF(setclientid->se_namelen + 8); 8711da177e4SLinus Torvalds SAVEMEM(setclientid->se_name, setclientid->se_namelen); 8721da177e4SLinus Torvalds READ32(setclientid->se_callback_prog); 8731da177e4SLinus Torvalds READ32(setclientid->se_callback_netid_len); 8741da177e4SLinus Torvalds 8751da177e4SLinus Torvalds READ_BUF(setclientid->se_callback_netid_len + 4); 8761da177e4SLinus Torvalds SAVEMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len); 8771da177e4SLinus Torvalds READ32(setclientid->se_callback_addr_len); 8781da177e4SLinus Torvalds 8791da177e4SLinus Torvalds READ_BUF(setclientid->se_callback_addr_len + 4); 8801da177e4SLinus Torvalds SAVEMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len); 8811da177e4SLinus Torvalds READ32(setclientid->se_callback_ident); 8821da177e4SLinus Torvalds 8831da177e4SLinus Torvalds DECODE_TAIL; 8841da177e4SLinus Torvalds } 8851da177e4SLinus Torvalds 886b37ad28bSAl Viro static __be32 8871da177e4SLinus Torvalds nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid_confirm *scd_c) 8881da177e4SLinus Torvalds { 8891da177e4SLinus Torvalds DECODE_HEAD; 8901da177e4SLinus Torvalds 8911da177e4SLinus Torvalds READ_BUF(8 + sizeof(nfs4_verifier)); 8921da177e4SLinus Torvalds COPYMEM(&scd_c->sc_clientid, 8); 8931da177e4SLinus Torvalds COPYMEM(&scd_c->sc_confirm, sizeof(nfs4_verifier)); 8941da177e4SLinus Torvalds 8951da177e4SLinus Torvalds DECODE_TAIL; 8961da177e4SLinus Torvalds } 8971da177e4SLinus Torvalds 8981da177e4SLinus Torvalds /* Also used for NVERIFY */ 899b37ad28bSAl Viro static __be32 9001da177e4SLinus Torvalds nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify) 9011da177e4SLinus Torvalds { 9021da177e4SLinus Torvalds #if 0 9031da177e4SLinus Torvalds struct nfsd4_compoundargs save = { 9041da177e4SLinus Torvalds .p = argp->p, 9051da177e4SLinus Torvalds .end = argp->end, 9061da177e4SLinus Torvalds .rqstp = argp->rqstp, 9071da177e4SLinus Torvalds }; 9081da177e4SLinus Torvalds u32 ve_bmval[2]; 9091da177e4SLinus Torvalds struct iattr ve_iattr; /* request */ 9101da177e4SLinus Torvalds struct nfs4_acl *ve_acl; /* request */ 9111da177e4SLinus Torvalds #endif 9121da177e4SLinus Torvalds DECODE_HEAD; 9131da177e4SLinus Torvalds 9141da177e4SLinus Torvalds if ((status = nfsd4_decode_bitmap(argp, verify->ve_bmval))) 9151da177e4SLinus Torvalds goto out; 9161da177e4SLinus Torvalds 9171da177e4SLinus Torvalds /* For convenience's sake, we compare raw xdr'd attributes in 9181da177e4SLinus Torvalds * nfsd4_proc_verify; however we still decode here just to return 9191da177e4SLinus Torvalds * correct error in case of bad xdr. */ 9201da177e4SLinus Torvalds #if 0 9211da177e4SLinus Torvalds status = nfsd4_decode_fattr(ve_bmval, &ve_iattr, &ve_acl); 9221da177e4SLinus Torvalds if (status == nfserr_inval) { 9231da177e4SLinus Torvalds status = nfserrno(status); 9241da177e4SLinus Torvalds goto out; 9251da177e4SLinus Torvalds } 9261da177e4SLinus Torvalds #endif 9271da177e4SLinus Torvalds READ_BUF(4); 9281da177e4SLinus Torvalds READ32(verify->ve_attrlen); 9291da177e4SLinus Torvalds READ_BUF(verify->ve_attrlen); 9301da177e4SLinus Torvalds SAVEMEM(verify->ve_attrval, verify->ve_attrlen); 9311da177e4SLinus Torvalds 9321da177e4SLinus Torvalds DECODE_TAIL; 9331da177e4SLinus Torvalds } 9341da177e4SLinus Torvalds 935b37ad28bSAl Viro static __be32 9361da177e4SLinus Torvalds nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) 9371da177e4SLinus Torvalds { 9381da177e4SLinus Torvalds int avail; 9391da177e4SLinus Torvalds int v; 9401da177e4SLinus Torvalds int len; 9411da177e4SLinus Torvalds DECODE_HEAD; 9421da177e4SLinus Torvalds 943e31a1b66SBenny Halevy status = nfsd4_decode_stateid(argp, &write->wr_stateid); 944e31a1b66SBenny Halevy if (status) 945e31a1b66SBenny Halevy return status; 946e31a1b66SBenny Halevy READ_BUF(16); 9471da177e4SLinus Torvalds READ64(write->wr_offset); 9481da177e4SLinus Torvalds READ32(write->wr_stable_how); 9491da177e4SLinus Torvalds if (write->wr_stable_how > 2) 9501da177e4SLinus Torvalds goto xdr_error; 9511da177e4SLinus Torvalds READ32(write->wr_buflen); 9521da177e4SLinus Torvalds 9531da177e4SLinus Torvalds /* Sorry .. no magic macros for this.. * 9541da177e4SLinus Torvalds * READ_BUF(write->wr_buflen); 9551da177e4SLinus Torvalds * SAVEMEM(write->wr_buf, write->wr_buflen); 9561da177e4SLinus Torvalds */ 9571da177e4SLinus Torvalds avail = (char*)argp->end - (char*)argp->p; 9581da177e4SLinus Torvalds if (avail + argp->pagelen < write->wr_buflen) { 959817cb9d4SChuck Lever dprintk("NFSD: xdr error (%s:%d)\n", 960817cb9d4SChuck Lever __FILE__, __LINE__); 9611da177e4SLinus Torvalds goto xdr_error; 9621da177e4SLinus Torvalds } 9633cc03b16SNeilBrown argp->rqstp->rq_vec[0].iov_base = p; 9643cc03b16SNeilBrown argp->rqstp->rq_vec[0].iov_len = avail; 9651da177e4SLinus Torvalds v = 0; 9661da177e4SLinus Torvalds len = write->wr_buflen; 9673cc03b16SNeilBrown while (len > argp->rqstp->rq_vec[v].iov_len) { 9683cc03b16SNeilBrown len -= argp->rqstp->rq_vec[v].iov_len; 9691da177e4SLinus Torvalds v++; 9703cc03b16SNeilBrown argp->rqstp->rq_vec[v].iov_base = page_address(argp->pagelist[0]); 9711da177e4SLinus Torvalds argp->pagelist++; 9721da177e4SLinus Torvalds if (argp->pagelen >= PAGE_SIZE) { 9733cc03b16SNeilBrown argp->rqstp->rq_vec[v].iov_len = PAGE_SIZE; 9741da177e4SLinus Torvalds argp->pagelen -= PAGE_SIZE; 9751da177e4SLinus Torvalds } else { 9763cc03b16SNeilBrown argp->rqstp->rq_vec[v].iov_len = argp->pagelen; 9771da177e4SLinus Torvalds argp->pagelen -= len; 9781da177e4SLinus Torvalds } 9791da177e4SLinus Torvalds } 9802ebbc012SAl Viro argp->end = (__be32*) (argp->rqstp->rq_vec[v].iov_base + argp->rqstp->rq_vec[v].iov_len); 9812ebbc012SAl Viro argp->p = (__be32*) (argp->rqstp->rq_vec[v].iov_base + (XDR_QUADLEN(len) << 2)); 9823cc03b16SNeilBrown argp->rqstp->rq_vec[v].iov_len = len; 9831da177e4SLinus Torvalds write->wr_vlen = v+1; 9841da177e4SLinus Torvalds 9851da177e4SLinus Torvalds DECODE_TAIL; 9861da177e4SLinus Torvalds } 9871da177e4SLinus Torvalds 988b37ad28bSAl Viro static __be32 9891da177e4SLinus Torvalds nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_release_lockowner *rlockowner) 9901da177e4SLinus Torvalds { 9911da177e4SLinus Torvalds DECODE_HEAD; 9921da177e4SLinus Torvalds 9931da177e4SLinus Torvalds READ_BUF(12); 9941da177e4SLinus Torvalds COPYMEM(&rlockowner->rl_clientid, sizeof(clientid_t)); 9951da177e4SLinus Torvalds READ32(rlockowner->rl_owner.len); 9961da177e4SLinus Torvalds READ_BUF(rlockowner->rl_owner.len); 9971da177e4SLinus Torvalds READMEM(rlockowner->rl_owner.data, rlockowner->rl_owner.len); 9981da177e4SLinus Torvalds 99960adfc50SAndy Adamson if (argp->minorversion && !zero_clientid(&rlockowner->rl_clientid)) 100060adfc50SAndy Adamson return nfserr_inval; 10011da177e4SLinus Torvalds DECODE_TAIL; 10021da177e4SLinus Torvalds } 10031da177e4SLinus Torvalds 1004b37ad28bSAl Viro static __be32 10052db134ebSAndy Adamson nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp, 10060733d213SAndy Adamson struct nfsd4_exchange_id *exid) 10072db134ebSAndy Adamson { 10080733d213SAndy Adamson int dummy; 10090733d213SAndy Adamson DECODE_HEAD; 10100733d213SAndy Adamson 10110733d213SAndy Adamson READ_BUF(NFS4_VERIFIER_SIZE); 10120733d213SAndy Adamson COPYMEM(exid->verifier.data, NFS4_VERIFIER_SIZE); 10130733d213SAndy Adamson 10140733d213SAndy Adamson READ_BUF(4); 10150733d213SAndy Adamson READ32(exid->clname.len); 10160733d213SAndy Adamson 10170733d213SAndy Adamson READ_BUF(exid->clname.len); 10180733d213SAndy Adamson SAVEMEM(exid->clname.data, exid->clname.len); 10190733d213SAndy Adamson 10200733d213SAndy Adamson READ_BUF(4); 10210733d213SAndy Adamson READ32(exid->flags); 10220733d213SAndy Adamson 10230733d213SAndy Adamson /* Ignore state_protect4_a */ 10240733d213SAndy Adamson READ_BUF(4); 10250733d213SAndy Adamson READ32(exid->spa_how); 10260733d213SAndy Adamson switch (exid->spa_how) { 10270733d213SAndy Adamson case SP4_NONE: 10280733d213SAndy Adamson break; 10290733d213SAndy Adamson case SP4_MACH_CRED: 10300733d213SAndy Adamson /* spo_must_enforce */ 10310733d213SAndy Adamson READ_BUF(4); 10320733d213SAndy Adamson READ32(dummy); 10330733d213SAndy Adamson READ_BUF(dummy * 4); 10340733d213SAndy Adamson p += dummy; 10350733d213SAndy Adamson 10360733d213SAndy Adamson /* spo_must_allow */ 10370733d213SAndy Adamson READ_BUF(4); 10380733d213SAndy Adamson READ32(dummy); 10390733d213SAndy Adamson READ_BUF(dummy * 4); 10400733d213SAndy Adamson p += dummy; 10410733d213SAndy Adamson break; 10420733d213SAndy Adamson case SP4_SSV: 10430733d213SAndy Adamson /* ssp_ops */ 10440733d213SAndy Adamson READ_BUF(4); 10450733d213SAndy Adamson READ32(dummy); 10460733d213SAndy Adamson READ_BUF(dummy * 4); 10470733d213SAndy Adamson p += dummy; 10480733d213SAndy Adamson 10490733d213SAndy Adamson READ_BUF(4); 10500733d213SAndy Adamson READ32(dummy); 10510733d213SAndy Adamson READ_BUF(dummy * 4); 10520733d213SAndy Adamson p += dummy; 10530733d213SAndy Adamson 10540733d213SAndy Adamson /* ssp_hash_algs<> */ 10550733d213SAndy Adamson READ_BUF(4); 10560733d213SAndy Adamson READ32(dummy); 10570733d213SAndy Adamson READ_BUF(dummy); 10580733d213SAndy Adamson p += XDR_QUADLEN(dummy); 10590733d213SAndy Adamson 10600733d213SAndy Adamson /* ssp_encr_algs<> */ 10610733d213SAndy Adamson READ_BUF(4); 10620733d213SAndy Adamson READ32(dummy); 10630733d213SAndy Adamson READ_BUF(dummy); 10640733d213SAndy Adamson p += XDR_QUADLEN(dummy); 10650733d213SAndy Adamson 10660733d213SAndy Adamson /* ssp_window and ssp_num_gss_handles */ 10670733d213SAndy Adamson READ_BUF(8); 10680733d213SAndy Adamson READ32(dummy); 10690733d213SAndy Adamson READ32(dummy); 10700733d213SAndy Adamson break; 10710733d213SAndy Adamson default: 10720733d213SAndy Adamson goto xdr_error; 10730733d213SAndy Adamson } 10740733d213SAndy Adamson 10750733d213SAndy Adamson /* Ignore Implementation ID */ 10760733d213SAndy Adamson READ_BUF(4); /* nfs_impl_id4 array length */ 10770733d213SAndy Adamson READ32(dummy); 10780733d213SAndy Adamson 10790733d213SAndy Adamson if (dummy > 1) 10800733d213SAndy Adamson goto xdr_error; 10810733d213SAndy Adamson 10820733d213SAndy Adamson if (dummy == 1) { 10830733d213SAndy Adamson /* nii_domain */ 10840733d213SAndy Adamson READ_BUF(4); 10850733d213SAndy Adamson READ32(dummy); 10860733d213SAndy Adamson READ_BUF(dummy); 10870733d213SAndy Adamson p += XDR_QUADLEN(dummy); 10880733d213SAndy Adamson 10890733d213SAndy Adamson /* nii_name */ 10900733d213SAndy Adamson READ_BUF(4); 10910733d213SAndy Adamson READ32(dummy); 10920733d213SAndy Adamson READ_BUF(dummy); 10930733d213SAndy Adamson p += XDR_QUADLEN(dummy); 10940733d213SAndy Adamson 10950733d213SAndy Adamson /* nii_date */ 10960733d213SAndy Adamson READ_BUF(12); 10970733d213SAndy Adamson p += 3; 10980733d213SAndy Adamson } 10990733d213SAndy Adamson DECODE_TAIL; 11002db134ebSAndy Adamson } 11012db134ebSAndy Adamson 11022db134ebSAndy Adamson static __be32 11032db134ebSAndy Adamson nfsd4_decode_create_session(struct nfsd4_compoundargs *argp, 11042db134ebSAndy Adamson struct nfsd4_create_session *sess) 11052db134ebSAndy Adamson { 1106ec6b5d7bSAndy Adamson DECODE_HEAD; 1107ec6b5d7bSAndy Adamson 1108ec6b5d7bSAndy Adamson u32 dummy; 1109ec6b5d7bSAndy Adamson char *machine_name; 1110ec6b5d7bSAndy Adamson int i; 1111ec6b5d7bSAndy Adamson int nr_secflavs; 1112ec6b5d7bSAndy Adamson 1113ec6b5d7bSAndy Adamson READ_BUF(16); 1114ec6b5d7bSAndy Adamson COPYMEM(&sess->clientid, 8); 1115ec6b5d7bSAndy Adamson READ32(sess->seqid); 1116ec6b5d7bSAndy Adamson READ32(sess->flags); 1117ec6b5d7bSAndy Adamson 1118ec6b5d7bSAndy Adamson /* Fore channel attrs */ 1119ec6b5d7bSAndy Adamson READ_BUF(28); 1120ec6b5d7bSAndy Adamson READ32(dummy); /* headerpadsz is always 0 */ 1121ec6b5d7bSAndy Adamson READ32(sess->fore_channel.maxreq_sz); 1122ec6b5d7bSAndy Adamson READ32(sess->fore_channel.maxresp_sz); 1123ec6b5d7bSAndy Adamson READ32(sess->fore_channel.maxresp_cached); 1124ec6b5d7bSAndy Adamson READ32(sess->fore_channel.maxops); 1125ec6b5d7bSAndy Adamson READ32(sess->fore_channel.maxreqs); 1126ec6b5d7bSAndy Adamson READ32(sess->fore_channel.nr_rdma_attrs); 1127ec6b5d7bSAndy Adamson if (sess->fore_channel.nr_rdma_attrs == 1) { 1128ec6b5d7bSAndy Adamson READ_BUF(4); 1129ec6b5d7bSAndy Adamson READ32(sess->fore_channel.rdma_attrs); 1130ec6b5d7bSAndy Adamson } else if (sess->fore_channel.nr_rdma_attrs > 1) { 1131ec6b5d7bSAndy Adamson dprintk("Too many fore channel attr bitmaps!\n"); 1132ec6b5d7bSAndy Adamson goto xdr_error; 1133ec6b5d7bSAndy Adamson } 1134ec6b5d7bSAndy Adamson 1135ec6b5d7bSAndy Adamson /* Back channel attrs */ 1136ec6b5d7bSAndy Adamson READ_BUF(28); 1137ec6b5d7bSAndy Adamson READ32(dummy); /* headerpadsz is always 0 */ 1138ec6b5d7bSAndy Adamson READ32(sess->back_channel.maxreq_sz); 1139ec6b5d7bSAndy Adamson READ32(sess->back_channel.maxresp_sz); 1140ec6b5d7bSAndy Adamson READ32(sess->back_channel.maxresp_cached); 1141ec6b5d7bSAndy Adamson READ32(sess->back_channel.maxops); 1142ec6b5d7bSAndy Adamson READ32(sess->back_channel.maxreqs); 1143ec6b5d7bSAndy Adamson READ32(sess->back_channel.nr_rdma_attrs); 1144ec6b5d7bSAndy Adamson if (sess->back_channel.nr_rdma_attrs == 1) { 1145ec6b5d7bSAndy Adamson READ_BUF(4); 1146ec6b5d7bSAndy Adamson READ32(sess->back_channel.rdma_attrs); 1147ec6b5d7bSAndy Adamson } else if (sess->back_channel.nr_rdma_attrs > 1) { 1148ec6b5d7bSAndy Adamson dprintk("Too many back channel attr bitmaps!\n"); 1149ec6b5d7bSAndy Adamson goto xdr_error; 1150ec6b5d7bSAndy Adamson } 1151ec6b5d7bSAndy Adamson 1152ec6b5d7bSAndy Adamson READ_BUF(8); 1153ec6b5d7bSAndy Adamson READ32(sess->callback_prog); 1154ec6b5d7bSAndy Adamson 1155ec6b5d7bSAndy Adamson /* callback_sec_params4 */ 1156ec6b5d7bSAndy Adamson READ32(nr_secflavs); 1157ec6b5d7bSAndy Adamson for (i = 0; i < nr_secflavs; ++i) { 1158ec6b5d7bSAndy Adamson READ_BUF(4); 1159ec6b5d7bSAndy Adamson READ32(dummy); 1160ec6b5d7bSAndy Adamson switch (dummy) { 1161ec6b5d7bSAndy Adamson case RPC_AUTH_NULL: 1162ec6b5d7bSAndy Adamson /* Nothing to read */ 1163ec6b5d7bSAndy Adamson break; 1164ec6b5d7bSAndy Adamson case RPC_AUTH_UNIX: 1165ec6b5d7bSAndy Adamson READ_BUF(8); 1166ec6b5d7bSAndy Adamson /* stamp */ 1167ec6b5d7bSAndy Adamson READ32(dummy); 1168ec6b5d7bSAndy Adamson 1169ec6b5d7bSAndy Adamson /* machine name */ 1170ec6b5d7bSAndy Adamson READ32(dummy); 1171ec6b5d7bSAndy Adamson READ_BUF(dummy); 1172ec6b5d7bSAndy Adamson SAVEMEM(machine_name, dummy); 1173ec6b5d7bSAndy Adamson 1174ec6b5d7bSAndy Adamson /* uid, gid */ 1175ec6b5d7bSAndy Adamson READ_BUF(8); 1176ec6b5d7bSAndy Adamson READ32(sess->uid); 1177ec6b5d7bSAndy Adamson READ32(sess->gid); 1178ec6b5d7bSAndy Adamson 1179ec6b5d7bSAndy Adamson /* more gids */ 1180ec6b5d7bSAndy Adamson READ_BUF(4); 1181ec6b5d7bSAndy Adamson READ32(dummy); 1182ec6b5d7bSAndy Adamson READ_BUF(dummy * 4); 1183ec6b5d7bSAndy Adamson for (i = 0; i < dummy; ++i) 1184ec6b5d7bSAndy Adamson READ32(dummy); 1185ec6b5d7bSAndy Adamson break; 1186ec6b5d7bSAndy Adamson case RPC_AUTH_GSS: 1187ec6b5d7bSAndy Adamson dprintk("RPC_AUTH_GSS callback secflavor " 1188ec6b5d7bSAndy Adamson "not supported!\n"); 1189ec6b5d7bSAndy Adamson READ_BUF(8); 1190ec6b5d7bSAndy Adamson /* gcbp_service */ 1191ec6b5d7bSAndy Adamson READ32(dummy); 1192ec6b5d7bSAndy Adamson /* gcbp_handle_from_server */ 1193ec6b5d7bSAndy Adamson READ32(dummy); 1194ec6b5d7bSAndy Adamson READ_BUF(dummy); 1195ec6b5d7bSAndy Adamson p += XDR_QUADLEN(dummy); 1196ec6b5d7bSAndy Adamson /* gcbp_handle_from_client */ 1197ec6b5d7bSAndy Adamson READ_BUF(4); 1198ec6b5d7bSAndy Adamson READ32(dummy); 1199ec6b5d7bSAndy Adamson READ_BUF(dummy); 1200ec6b5d7bSAndy Adamson p += XDR_QUADLEN(dummy); 1201ec6b5d7bSAndy Adamson break; 1202ec6b5d7bSAndy Adamson default: 1203ec6b5d7bSAndy Adamson dprintk("Illegal callback secflavor\n"); 1204ec6b5d7bSAndy Adamson return nfserr_inval; 1205ec6b5d7bSAndy Adamson } 1206ec6b5d7bSAndy Adamson } 1207ec6b5d7bSAndy Adamson DECODE_TAIL; 12082db134ebSAndy Adamson } 12092db134ebSAndy Adamson 12102db134ebSAndy Adamson static __be32 12112db134ebSAndy Adamson nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp, 12122db134ebSAndy Adamson struct nfsd4_destroy_session *destroy_session) 12132db134ebSAndy Adamson { 1214e10e0cfcSBenny Halevy DECODE_HEAD; 1215e10e0cfcSBenny Halevy READ_BUF(NFS4_MAX_SESSIONID_LEN); 1216e10e0cfcSBenny Halevy COPYMEM(destroy_session->sessionid.data, NFS4_MAX_SESSIONID_LEN); 1217e10e0cfcSBenny Halevy 1218e10e0cfcSBenny Halevy DECODE_TAIL; 12192db134ebSAndy Adamson } 12202db134ebSAndy Adamson 12212db134ebSAndy Adamson static __be32 12222db134ebSAndy Adamson nfsd4_decode_sequence(struct nfsd4_compoundargs *argp, 12232db134ebSAndy Adamson struct nfsd4_sequence *seq) 12242db134ebSAndy Adamson { 1225b85d4c01SBenny Halevy DECODE_HEAD; 1226b85d4c01SBenny Halevy 1227b85d4c01SBenny Halevy READ_BUF(NFS4_MAX_SESSIONID_LEN + 16); 1228b85d4c01SBenny Halevy COPYMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN); 1229b85d4c01SBenny Halevy READ32(seq->seqid); 1230b85d4c01SBenny Halevy READ32(seq->slotid); 1231b85d4c01SBenny Halevy READ32(seq->maxslots); 1232b85d4c01SBenny Halevy READ32(seq->cachethis); 1233b85d4c01SBenny Halevy 1234b85d4c01SBenny Halevy DECODE_TAIL; 12352db134ebSAndy Adamson } 12362db134ebSAndy Adamson 12374dc6ec00SJ. Bruce Fields static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc) 12384dc6ec00SJ. Bruce Fields { 12394dc6ec00SJ. Bruce Fields DECODE_HEAD; 12404dc6ec00SJ. Bruce Fields 12414dc6ec00SJ. Bruce Fields READ_BUF(4); 12424dc6ec00SJ. Bruce Fields READ32(rc->rca_one_fs); 12434dc6ec00SJ. Bruce Fields 12444dc6ec00SJ. Bruce Fields DECODE_TAIL; 12454dc6ec00SJ. Bruce Fields } 12464dc6ec00SJ. Bruce Fields 12472db134ebSAndy Adamson static __be32 1248347e0ad9SBenny Halevy nfsd4_decode_noop(struct nfsd4_compoundargs *argp, void *p) 1249347e0ad9SBenny Halevy { 1250347e0ad9SBenny Halevy return nfs_ok; 1251347e0ad9SBenny Halevy } 1252347e0ad9SBenny Halevy 12533c375c6fSBenny Halevy static __be32 12543c375c6fSBenny Halevy nfsd4_decode_notsupp(struct nfsd4_compoundargs *argp, void *p) 12553c375c6fSBenny Halevy { 12561e685ec2SBenny Halevy return nfserr_notsupp; 12573c375c6fSBenny Halevy } 12583c375c6fSBenny Halevy 1259347e0ad9SBenny Halevy typedef __be32(*nfsd4_dec)(struct nfsd4_compoundargs *argp, void *); 1260347e0ad9SBenny Halevy 1261347e0ad9SBenny Halevy static nfsd4_dec nfsd4_dec_ops[] = { 1262ad1060c8SJ. Bruce Fields [OP_ACCESS] = (nfsd4_dec)nfsd4_decode_access, 1263ad1060c8SJ. Bruce Fields [OP_CLOSE] = (nfsd4_dec)nfsd4_decode_close, 1264ad1060c8SJ. Bruce Fields [OP_COMMIT] = (nfsd4_dec)nfsd4_decode_commit, 1265ad1060c8SJ. Bruce Fields [OP_CREATE] = (nfsd4_dec)nfsd4_decode_create, 1266ad1060c8SJ. Bruce Fields [OP_DELEGPURGE] = (nfsd4_dec)nfsd4_decode_notsupp, 1267ad1060c8SJ. Bruce Fields [OP_DELEGRETURN] = (nfsd4_dec)nfsd4_decode_delegreturn, 1268ad1060c8SJ. Bruce Fields [OP_GETATTR] = (nfsd4_dec)nfsd4_decode_getattr, 1269ad1060c8SJ. Bruce Fields [OP_GETFH] = (nfsd4_dec)nfsd4_decode_noop, 1270ad1060c8SJ. Bruce Fields [OP_LINK] = (nfsd4_dec)nfsd4_decode_link, 1271ad1060c8SJ. Bruce Fields [OP_LOCK] = (nfsd4_dec)nfsd4_decode_lock, 1272ad1060c8SJ. Bruce Fields [OP_LOCKT] = (nfsd4_dec)nfsd4_decode_lockt, 1273ad1060c8SJ. Bruce Fields [OP_LOCKU] = (nfsd4_dec)nfsd4_decode_locku, 1274ad1060c8SJ. Bruce Fields [OP_LOOKUP] = (nfsd4_dec)nfsd4_decode_lookup, 1275ad1060c8SJ. Bruce Fields [OP_LOOKUPP] = (nfsd4_dec)nfsd4_decode_noop, 1276ad1060c8SJ. Bruce Fields [OP_NVERIFY] = (nfsd4_dec)nfsd4_decode_verify, 1277ad1060c8SJ. Bruce Fields [OP_OPEN] = (nfsd4_dec)nfsd4_decode_open, 1278ad1060c8SJ. Bruce Fields [OP_OPENATTR] = (nfsd4_dec)nfsd4_decode_notsupp, 1279ad1060c8SJ. Bruce Fields [OP_OPEN_CONFIRM] = (nfsd4_dec)nfsd4_decode_open_confirm, 1280ad1060c8SJ. Bruce Fields [OP_OPEN_DOWNGRADE] = (nfsd4_dec)nfsd4_decode_open_downgrade, 1281ad1060c8SJ. Bruce Fields [OP_PUTFH] = (nfsd4_dec)nfsd4_decode_putfh, 1282a1c8c4d1SJ. Bruce Fields [OP_PUTPUBFH] = (nfsd4_dec)nfsd4_decode_noop, 1283ad1060c8SJ. Bruce Fields [OP_PUTROOTFH] = (nfsd4_dec)nfsd4_decode_noop, 1284ad1060c8SJ. Bruce Fields [OP_READ] = (nfsd4_dec)nfsd4_decode_read, 1285ad1060c8SJ. Bruce Fields [OP_READDIR] = (nfsd4_dec)nfsd4_decode_readdir, 1286ad1060c8SJ. Bruce Fields [OP_READLINK] = (nfsd4_dec)nfsd4_decode_noop, 1287ad1060c8SJ. Bruce Fields [OP_REMOVE] = (nfsd4_dec)nfsd4_decode_remove, 1288ad1060c8SJ. Bruce Fields [OP_RENAME] = (nfsd4_dec)nfsd4_decode_rename, 1289ad1060c8SJ. Bruce Fields [OP_RENEW] = (nfsd4_dec)nfsd4_decode_renew, 1290ad1060c8SJ. Bruce Fields [OP_RESTOREFH] = (nfsd4_dec)nfsd4_decode_noop, 1291ad1060c8SJ. Bruce Fields [OP_SAVEFH] = (nfsd4_dec)nfsd4_decode_noop, 1292ad1060c8SJ. Bruce Fields [OP_SECINFO] = (nfsd4_dec)nfsd4_decode_secinfo, 1293ad1060c8SJ. Bruce Fields [OP_SETATTR] = (nfsd4_dec)nfsd4_decode_setattr, 1294ad1060c8SJ. Bruce Fields [OP_SETCLIENTID] = (nfsd4_dec)nfsd4_decode_setclientid, 1295ad1060c8SJ. Bruce Fields [OP_SETCLIENTID_CONFIRM] = (nfsd4_dec)nfsd4_decode_setclientid_confirm, 1296ad1060c8SJ. Bruce Fields [OP_VERIFY] = (nfsd4_dec)nfsd4_decode_verify, 1297ad1060c8SJ. Bruce Fields [OP_WRITE] = (nfsd4_dec)nfsd4_decode_write, 1298ad1060c8SJ. Bruce Fields [OP_RELEASE_LOCKOWNER] = (nfsd4_dec)nfsd4_decode_release_lockowner, 1299347e0ad9SBenny Halevy }; 1300347e0ad9SBenny Halevy 13012db134ebSAndy Adamson static nfsd4_dec nfsd41_dec_ops[] = { 13029064caaeSRandy Dunlap [OP_ACCESS] = (nfsd4_dec)nfsd4_decode_access, 13039064caaeSRandy Dunlap [OP_CLOSE] = (nfsd4_dec)nfsd4_decode_close, 13049064caaeSRandy Dunlap [OP_COMMIT] = (nfsd4_dec)nfsd4_decode_commit, 13059064caaeSRandy Dunlap [OP_CREATE] = (nfsd4_dec)nfsd4_decode_create, 13069064caaeSRandy Dunlap [OP_DELEGPURGE] = (nfsd4_dec)nfsd4_decode_notsupp, 13079064caaeSRandy Dunlap [OP_DELEGRETURN] = (nfsd4_dec)nfsd4_decode_delegreturn, 13089064caaeSRandy Dunlap [OP_GETATTR] = (nfsd4_dec)nfsd4_decode_getattr, 13099064caaeSRandy Dunlap [OP_GETFH] = (nfsd4_dec)nfsd4_decode_noop, 13109064caaeSRandy Dunlap [OP_LINK] = (nfsd4_dec)nfsd4_decode_link, 13119064caaeSRandy Dunlap [OP_LOCK] = (nfsd4_dec)nfsd4_decode_lock, 13129064caaeSRandy Dunlap [OP_LOCKT] = (nfsd4_dec)nfsd4_decode_lockt, 13139064caaeSRandy Dunlap [OP_LOCKU] = (nfsd4_dec)nfsd4_decode_locku, 13149064caaeSRandy Dunlap [OP_LOOKUP] = (nfsd4_dec)nfsd4_decode_lookup, 13159064caaeSRandy Dunlap [OP_LOOKUPP] = (nfsd4_dec)nfsd4_decode_noop, 13169064caaeSRandy Dunlap [OP_NVERIFY] = (nfsd4_dec)nfsd4_decode_verify, 13179064caaeSRandy Dunlap [OP_OPEN] = (nfsd4_dec)nfsd4_decode_open, 13189064caaeSRandy Dunlap [OP_OPENATTR] = (nfsd4_dec)nfsd4_decode_notsupp, 13199064caaeSRandy Dunlap [OP_OPEN_CONFIRM] = (nfsd4_dec)nfsd4_decode_notsupp, 13209064caaeSRandy Dunlap [OP_OPEN_DOWNGRADE] = (nfsd4_dec)nfsd4_decode_open_downgrade, 13219064caaeSRandy Dunlap [OP_PUTFH] = (nfsd4_dec)nfsd4_decode_putfh, 13229064caaeSRandy Dunlap [OP_PUTPUBFH] = (nfsd4_dec)nfsd4_decode_notsupp, 13239064caaeSRandy Dunlap [OP_PUTROOTFH] = (nfsd4_dec)nfsd4_decode_noop, 13249064caaeSRandy Dunlap [OP_READ] = (nfsd4_dec)nfsd4_decode_read, 13259064caaeSRandy Dunlap [OP_READDIR] = (nfsd4_dec)nfsd4_decode_readdir, 13269064caaeSRandy Dunlap [OP_READLINK] = (nfsd4_dec)nfsd4_decode_noop, 13279064caaeSRandy Dunlap [OP_REMOVE] = (nfsd4_dec)nfsd4_decode_remove, 13289064caaeSRandy Dunlap [OP_RENAME] = (nfsd4_dec)nfsd4_decode_rename, 13299064caaeSRandy Dunlap [OP_RENEW] = (nfsd4_dec)nfsd4_decode_notsupp, 13309064caaeSRandy Dunlap [OP_RESTOREFH] = (nfsd4_dec)nfsd4_decode_noop, 13319064caaeSRandy Dunlap [OP_SAVEFH] = (nfsd4_dec)nfsd4_decode_noop, 13329064caaeSRandy Dunlap [OP_SECINFO] = (nfsd4_dec)nfsd4_decode_secinfo, 13339064caaeSRandy Dunlap [OP_SETATTR] = (nfsd4_dec)nfsd4_decode_setattr, 13349064caaeSRandy Dunlap [OP_SETCLIENTID] = (nfsd4_dec)nfsd4_decode_notsupp, 13359064caaeSRandy Dunlap [OP_SETCLIENTID_CONFIRM]= (nfsd4_dec)nfsd4_decode_notsupp, 13369064caaeSRandy Dunlap [OP_VERIFY] = (nfsd4_dec)nfsd4_decode_verify, 13379064caaeSRandy Dunlap [OP_WRITE] = (nfsd4_dec)nfsd4_decode_write, 13389064caaeSRandy Dunlap [OP_RELEASE_LOCKOWNER] = (nfsd4_dec)nfsd4_decode_notsupp, 13392db134ebSAndy Adamson 13402db134ebSAndy Adamson /* new operations for NFSv4.1 */ 13419064caaeSRandy Dunlap [OP_BACKCHANNEL_CTL] = (nfsd4_dec)nfsd4_decode_notsupp, 13429064caaeSRandy Dunlap [OP_BIND_CONN_TO_SESSION]= (nfsd4_dec)nfsd4_decode_notsupp, 13439064caaeSRandy Dunlap [OP_EXCHANGE_ID] = (nfsd4_dec)nfsd4_decode_exchange_id, 13449064caaeSRandy Dunlap [OP_CREATE_SESSION] = (nfsd4_dec)nfsd4_decode_create_session, 13459064caaeSRandy Dunlap [OP_DESTROY_SESSION] = (nfsd4_dec)nfsd4_decode_destroy_session, 13469064caaeSRandy Dunlap [OP_FREE_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp, 13479064caaeSRandy Dunlap [OP_GET_DIR_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp, 13489064caaeSRandy Dunlap [OP_GETDEVICEINFO] = (nfsd4_dec)nfsd4_decode_notsupp, 13499064caaeSRandy Dunlap [OP_GETDEVICELIST] = (nfsd4_dec)nfsd4_decode_notsupp, 13509064caaeSRandy Dunlap [OP_LAYOUTCOMMIT] = (nfsd4_dec)nfsd4_decode_notsupp, 13519064caaeSRandy Dunlap [OP_LAYOUTGET] = (nfsd4_dec)nfsd4_decode_notsupp, 13529064caaeSRandy Dunlap [OP_LAYOUTRETURN] = (nfsd4_dec)nfsd4_decode_notsupp, 13539064caaeSRandy Dunlap [OP_SECINFO_NO_NAME] = (nfsd4_dec)nfsd4_decode_notsupp, 13549064caaeSRandy Dunlap [OP_SEQUENCE] = (nfsd4_dec)nfsd4_decode_sequence, 13559064caaeSRandy Dunlap [OP_SET_SSV] = (nfsd4_dec)nfsd4_decode_notsupp, 13569064caaeSRandy Dunlap [OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp, 13579064caaeSRandy Dunlap [OP_WANT_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp, 13589064caaeSRandy Dunlap [OP_DESTROY_CLIENTID] = (nfsd4_dec)nfsd4_decode_notsupp, 13594dc6ec00SJ. Bruce Fields [OP_RECLAIM_COMPLETE] = (nfsd4_dec)nfsd4_decode_reclaim_complete, 13602db134ebSAndy Adamson }; 13612db134ebSAndy Adamson 1362f2feb96bSBenny Halevy struct nfsd4_minorversion_ops { 1363f2feb96bSBenny Halevy nfsd4_dec *decoders; 1364f2feb96bSBenny Halevy int nops; 1365f2feb96bSBenny Halevy }; 1366f2feb96bSBenny Halevy 1367f2feb96bSBenny Halevy static struct nfsd4_minorversion_ops nfsd4_minorversion[] = { 1368ad1060c8SJ. Bruce Fields [0] = { nfsd4_dec_ops, ARRAY_SIZE(nfsd4_dec_ops) }, 13692db134ebSAndy Adamson [1] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) }, 1370f2feb96bSBenny Halevy }; 1371f2feb96bSBenny Halevy 1372347e0ad9SBenny Halevy static __be32 13731da177e4SLinus Torvalds nfsd4_decode_compound(struct nfsd4_compoundargs *argp) 13741da177e4SLinus Torvalds { 13751da177e4SLinus Torvalds DECODE_HEAD; 13761da177e4SLinus Torvalds struct nfsd4_op *op; 1377f2feb96bSBenny Halevy struct nfsd4_minorversion_ops *ops; 13781da177e4SLinus Torvalds int i; 13791da177e4SLinus Torvalds 13801da177e4SLinus Torvalds /* 13811da177e4SLinus Torvalds * XXX: According to spec, we should check the tag 13821da177e4SLinus Torvalds * for UTF-8 compliance. I'm postponing this for 13831da177e4SLinus Torvalds * now because it seems that some clients do use 13841da177e4SLinus Torvalds * binary tags. 13851da177e4SLinus Torvalds */ 13861da177e4SLinus Torvalds READ_BUF(4); 13871da177e4SLinus Torvalds READ32(argp->taglen); 13881da177e4SLinus Torvalds READ_BUF(argp->taglen + 8); 13891da177e4SLinus Torvalds SAVEMEM(argp->tag, argp->taglen); 13901da177e4SLinus Torvalds READ32(argp->minorversion); 13911da177e4SLinus Torvalds READ32(argp->opcnt); 13921da177e4SLinus Torvalds 13931da177e4SLinus Torvalds if (argp->taglen > NFSD4_MAX_TAGLEN) 13941da177e4SLinus Torvalds goto xdr_error; 13951da177e4SLinus Torvalds if (argp->opcnt > 100) 13961da177e4SLinus Torvalds goto xdr_error; 13971da177e4SLinus Torvalds 1398e8c96f8cSTobias Klauser if (argp->opcnt > ARRAY_SIZE(argp->iops)) { 13991da177e4SLinus Torvalds argp->ops = kmalloc(argp->opcnt * sizeof(*argp->ops), GFP_KERNEL); 14001da177e4SLinus Torvalds if (!argp->ops) { 14011da177e4SLinus Torvalds argp->ops = argp->iops; 1402817cb9d4SChuck Lever dprintk("nfsd: couldn't allocate room for COMPOUND\n"); 14031da177e4SLinus Torvalds goto xdr_error; 14041da177e4SLinus Torvalds } 14051da177e4SLinus Torvalds } 14061da177e4SLinus Torvalds 1407f2feb96bSBenny Halevy if (argp->minorversion >= ARRAY_SIZE(nfsd4_minorversion)) 140830cff1ffSBenny Halevy argp->opcnt = 0; 140930cff1ffSBenny Halevy 1410f2feb96bSBenny Halevy ops = &nfsd4_minorversion[argp->minorversion]; 14111da177e4SLinus Torvalds for (i = 0; i < argp->opcnt; i++) { 14121da177e4SLinus Torvalds op = &argp->ops[i]; 14131da177e4SLinus Torvalds op->replay = NULL; 14141da177e4SLinus Torvalds 14151da177e4SLinus Torvalds /* 14161da177e4SLinus Torvalds * We can't use READ_BUF() here because we need to handle 14171da177e4SLinus Torvalds * a missing opcode as an OP_WRITE + 1. So we need to check 14181da177e4SLinus Torvalds * to see if we're truly at the end of our buffer or if there 14191da177e4SLinus Torvalds * is another page we need to flip to. 14201da177e4SLinus Torvalds */ 14211da177e4SLinus Torvalds 14221da177e4SLinus Torvalds if (argp->p == argp->end) { 14231da177e4SLinus Torvalds if (argp->pagelen < 4) { 14241da177e4SLinus Torvalds /* There isn't an opcode still on the wire */ 14251da177e4SLinus Torvalds op->opnum = OP_WRITE + 1; 14261da177e4SLinus Torvalds op->status = nfserr_bad_xdr; 14271da177e4SLinus Torvalds argp->opcnt = i+1; 14281da177e4SLinus Torvalds break; 14291da177e4SLinus Torvalds } 14301da177e4SLinus Torvalds 14311da177e4SLinus Torvalds /* 14321da177e4SLinus Torvalds * False alarm. We just hit a page boundary, but there 14331da177e4SLinus Torvalds * is still data available. Move pointer across page 14341da177e4SLinus Torvalds * boundary. *snip from READ_BUF* 14351da177e4SLinus Torvalds */ 14361da177e4SLinus Torvalds argp->p = page_address(argp->pagelist[0]); 14371da177e4SLinus Torvalds argp->pagelist++; 14381da177e4SLinus Torvalds if (argp->pagelen < PAGE_SIZE) { 14392bc3c117SNeil Brown argp->end = argp->p + (argp->pagelen>>2); 14401da177e4SLinus Torvalds argp->pagelen = 0; 14411da177e4SLinus Torvalds } else { 14422bc3c117SNeil Brown argp->end = argp->p + (PAGE_SIZE>>2); 14431da177e4SLinus Torvalds argp->pagelen -= PAGE_SIZE; 14441da177e4SLinus Torvalds } 14451da177e4SLinus Torvalds } 14461da177e4SLinus Torvalds op->opnum = ntohl(*argp->p++); 14471da177e4SLinus Torvalds 1448de3cab79SRicardo Labiaga if (op->opnum >= FIRST_NFS4_OP && op->opnum <= LAST_NFS4_OP) 1449f2feb96bSBenny Halevy op->status = ops->decoders[op->opnum](argp, &op->u); 1450347e0ad9SBenny Halevy else { 14511da177e4SLinus Torvalds op->opnum = OP_ILLEGAL; 14521da177e4SLinus Torvalds op->status = nfserr_op_illegal; 14531da177e4SLinus Torvalds } 14541da177e4SLinus Torvalds 14551da177e4SLinus Torvalds if (op->status) { 14561da177e4SLinus Torvalds argp->opcnt = i+1; 14571da177e4SLinus Torvalds break; 14581da177e4SLinus Torvalds } 14591da177e4SLinus Torvalds } 14601da177e4SLinus Torvalds 14611da177e4SLinus Torvalds DECODE_TAIL; 14621da177e4SLinus Torvalds } 14631da177e4SLinus Torvalds 14641da177e4SLinus Torvalds #define WRITE32(n) *p++ = htonl(n) 14651da177e4SLinus Torvalds #define WRITE64(n) do { \ 14661da177e4SLinus Torvalds *p++ = htonl((u32)((n) >> 32)); \ 14671da177e4SLinus Torvalds *p++ = htonl((u32)(n)); \ 14681da177e4SLinus Torvalds } while (0) 14695108b276SHarvey Harrison #define WRITEMEM(ptr,nbytes) do { if (nbytes > 0) { \ 14701da177e4SLinus Torvalds *(p + XDR_QUADLEN(nbytes) -1) = 0; \ 14711da177e4SLinus Torvalds memcpy(p, ptr, nbytes); \ 14721da177e4SLinus Torvalds p += XDR_QUADLEN(nbytes); \ 14735108b276SHarvey Harrison }} while (0) 1474c654b8a9SJ. Bruce Fields 1475c654b8a9SJ. Bruce Fields static void write32(__be32 **p, u32 n) 1476c654b8a9SJ. Bruce Fields { 1477c654b8a9SJ. Bruce Fields *(*p)++ = n; 1478c654b8a9SJ. Bruce Fields } 1479c654b8a9SJ. Bruce Fields 1480c654b8a9SJ. Bruce Fields static void write64(__be32 **p, u64 n) 1481c654b8a9SJ. Bruce Fields { 1482c654b8a9SJ. Bruce Fields write32(p, (u32)(n >> 32)); 1483c654b8a9SJ. Bruce Fields write32(p, (u32)n); 1484c654b8a9SJ. Bruce Fields } 1485c654b8a9SJ. Bruce Fields 1486c654b8a9SJ. Bruce Fields static void write_change(__be32 **p, struct kstat *stat, struct inode *inode) 1487c654b8a9SJ. Bruce Fields { 1488c654b8a9SJ. Bruce Fields if (IS_I_VERSION(inode)) { 1489c654b8a9SJ. Bruce Fields write64(p, inode->i_version); 1490c654b8a9SJ. Bruce Fields } else { 1491c654b8a9SJ. Bruce Fields write32(p, stat->ctime.tv_sec); 1492c654b8a9SJ. Bruce Fields write32(p, stat->ctime.tv_nsec); 1493c654b8a9SJ. Bruce Fields } 1494c654b8a9SJ. Bruce Fields } 1495c654b8a9SJ. Bruce Fields 1496c654b8a9SJ. Bruce Fields static void write_cinfo(__be32 **p, struct nfsd4_change_info *c) 1497c654b8a9SJ. Bruce Fields { 1498c654b8a9SJ. Bruce Fields write32(p, c->atomic); 1499c654b8a9SJ. Bruce Fields if (c->change_supported) { 1500c654b8a9SJ. Bruce Fields write64(p, c->before_change); 1501c654b8a9SJ. Bruce Fields write64(p, c->after_change); 1502c654b8a9SJ. Bruce Fields } else { 1503c654b8a9SJ. Bruce Fields write32(p, c->before_ctime_sec); 1504c654b8a9SJ. Bruce Fields write32(p, c->before_ctime_nsec); 1505c654b8a9SJ. Bruce Fields write32(p, c->after_ctime_sec); 1506c654b8a9SJ. Bruce Fields write32(p, c->after_ctime_nsec); 1507c654b8a9SJ. Bruce Fields } 1508c654b8a9SJ. Bruce Fields } 15091da177e4SLinus Torvalds 15101da177e4SLinus Torvalds #define RESERVE_SPACE(nbytes) do { \ 15111da177e4SLinus Torvalds p = resp->p; \ 15121da177e4SLinus Torvalds BUG_ON(p + XDR_QUADLEN(nbytes) > resp->end); \ 15131da177e4SLinus Torvalds } while (0) 15141da177e4SLinus Torvalds #define ADJUST_ARGS() resp->p = p 15151da177e4SLinus Torvalds 15161da177e4SLinus Torvalds /* 15171da177e4SLinus Torvalds * Header routine to setup seqid operation replay cache 15181da177e4SLinus Torvalds */ 15191da177e4SLinus Torvalds #define ENCODE_SEQID_OP_HEAD \ 15202ebbc012SAl Viro __be32 *save; \ 15211da177e4SLinus Torvalds \ 15221da177e4SLinus Torvalds save = resp->p; 15231da177e4SLinus Torvalds 15241da177e4SLinus Torvalds /* 15257fb64ceeSNeilBrown * Routine for encoding the result of a "seqid-mutating" NFSv4 operation. This 15267fb64ceeSNeilBrown * is where sequence id's are incremented, and the replay cache is filled. 15277fb64ceeSNeilBrown * Note that we increment sequence id's here, at the last moment, so we're sure 15287fb64ceeSNeilBrown * we know whether the error to be returned is a sequence id mutating error. 15291da177e4SLinus Torvalds */ 15301da177e4SLinus Torvalds 15311da177e4SLinus Torvalds #define ENCODE_SEQID_OP_TAIL(stateowner) do { \ 15321da177e4SLinus Torvalds if (seqid_mutating_err(nfserr) && stateowner) { \ 15331da177e4SLinus Torvalds stateowner->so_seqid++; \ 15341da177e4SLinus Torvalds stateowner->so_replay.rp_status = nfserr; \ 15351da177e4SLinus Torvalds stateowner->so_replay.rp_buflen = \ 15361da177e4SLinus Torvalds (((char *)(resp)->p - (char *)save)); \ 15371da177e4SLinus Torvalds memcpy(stateowner->so_replay.rp_buf, save, \ 15381da177e4SLinus Torvalds stateowner->so_replay.rp_buflen); \ 15391da177e4SLinus Torvalds } } while (0); 15401da177e4SLinus Torvalds 154181c3f413SJ.Bruce Fields /* Encode as an array of strings the string given with components 15423ad2f3fbSDaniel Mack * separated @sep. 154381c3f413SJ.Bruce Fields */ 1544b37ad28bSAl Viro static __be32 nfsd4_encode_components(char sep, char *components, 15452ebbc012SAl Viro __be32 **pp, int *buflen) 154681c3f413SJ.Bruce Fields { 15472ebbc012SAl Viro __be32 *p = *pp; 15482ebbc012SAl Viro __be32 *countp = p; 154981c3f413SJ.Bruce Fields int strlen, count=0; 155081c3f413SJ.Bruce Fields char *str, *end; 155181c3f413SJ.Bruce Fields 155281c3f413SJ.Bruce Fields dprintk("nfsd4_encode_components(%s)\n", components); 155381c3f413SJ.Bruce Fields if ((*buflen -= 4) < 0) 155481c3f413SJ.Bruce Fields return nfserr_resource; 155581c3f413SJ.Bruce Fields WRITE32(0); /* We will fill this in with @count later */ 155681c3f413SJ.Bruce Fields end = str = components; 155781c3f413SJ.Bruce Fields while (*end) { 155881c3f413SJ.Bruce Fields for (; *end && (*end != sep); end++) 155981c3f413SJ.Bruce Fields ; /* Point to end of component */ 156081c3f413SJ.Bruce Fields strlen = end - str; 156181c3f413SJ.Bruce Fields if (strlen) { 156281c3f413SJ.Bruce Fields if ((*buflen -= ((XDR_QUADLEN(strlen) << 2) + 4)) < 0) 156381c3f413SJ.Bruce Fields return nfserr_resource; 156481c3f413SJ.Bruce Fields WRITE32(strlen); 156581c3f413SJ.Bruce Fields WRITEMEM(str, strlen); 156681c3f413SJ.Bruce Fields count++; 156781c3f413SJ.Bruce Fields } 156881c3f413SJ.Bruce Fields else 156981c3f413SJ.Bruce Fields end++; 157081c3f413SJ.Bruce Fields str = end; 157181c3f413SJ.Bruce Fields } 157281c3f413SJ.Bruce Fields *pp = p; 157381c3f413SJ.Bruce Fields p = countp; 157481c3f413SJ.Bruce Fields WRITE32(count); 157581c3f413SJ.Bruce Fields return 0; 157681c3f413SJ.Bruce Fields } 157781c3f413SJ.Bruce Fields 157881c3f413SJ.Bruce Fields /* 157981c3f413SJ.Bruce Fields * encode a location element of a fs_locations structure 158081c3f413SJ.Bruce Fields */ 1581b37ad28bSAl Viro static __be32 nfsd4_encode_fs_location4(struct nfsd4_fs_location *location, 15822ebbc012SAl Viro __be32 **pp, int *buflen) 158381c3f413SJ.Bruce Fields { 1584b37ad28bSAl Viro __be32 status; 15852ebbc012SAl Viro __be32 *p = *pp; 158681c3f413SJ.Bruce Fields 158781c3f413SJ.Bruce Fields status = nfsd4_encode_components(':', location->hosts, &p, buflen); 158881c3f413SJ.Bruce Fields if (status) 158981c3f413SJ.Bruce Fields return status; 159081c3f413SJ.Bruce Fields status = nfsd4_encode_components('/', location->path, &p, buflen); 159181c3f413SJ.Bruce Fields if (status) 159281c3f413SJ.Bruce Fields return status; 159381c3f413SJ.Bruce Fields *pp = p; 159481c3f413SJ.Bruce Fields return 0; 159581c3f413SJ.Bruce Fields } 159681c3f413SJ.Bruce Fields 159781c3f413SJ.Bruce Fields /* 159881c3f413SJ.Bruce Fields * Return the path to an export point in the pseudo filesystem namespace 159981c3f413SJ.Bruce Fields * Returned string is safe to use as long as the caller holds a reference 160081c3f413SJ.Bruce Fields * to @exp. 160181c3f413SJ.Bruce Fields */ 1602b37ad28bSAl Viro static char *nfsd4_path(struct svc_rqst *rqstp, struct svc_export *exp, __be32 *stat) 160381c3f413SJ.Bruce Fields { 160481c3f413SJ.Bruce Fields struct svc_fh tmp_fh; 16052671a4bfSTrond Myklebust char *path = NULL, *rootpath; 16062671a4bfSTrond Myklebust size_t rootlen; 160781c3f413SJ.Bruce Fields 160881c3f413SJ.Bruce Fields fh_init(&tmp_fh, NFS4_FHSIZE); 1609df547efbSJ. Bruce Fields *stat = exp_pseudoroot(rqstp, &tmp_fh); 1610cc45f017SAl Viro if (*stat) 1611cc45f017SAl Viro return NULL; 161254775491SJan Blunck rootpath = tmp_fh.fh_export->ex_pathname; 161381c3f413SJ.Bruce Fields 161454775491SJan Blunck path = exp->ex_pathname; 161581c3f413SJ.Bruce Fields 16162671a4bfSTrond Myklebust rootlen = strlen(rootpath); 16172671a4bfSTrond Myklebust if (strncmp(path, rootpath, rootlen)) { 1618817cb9d4SChuck Lever dprintk("nfsd: fs_locations failed;" 161981c3f413SJ.Bruce Fields "%s is not contained in %s\n", path, rootpath); 1620cc45f017SAl Viro *stat = nfserr_notsupp; 16212671a4bfSTrond Myklebust path = NULL; 16222671a4bfSTrond Myklebust goto out; 162381c3f413SJ.Bruce Fields } 16242671a4bfSTrond Myklebust path += rootlen; 16252671a4bfSTrond Myklebust out: 16262671a4bfSTrond Myklebust fh_put(&tmp_fh); 16272671a4bfSTrond Myklebust return path; 162881c3f413SJ.Bruce Fields } 162981c3f413SJ.Bruce Fields 163081c3f413SJ.Bruce Fields /* 163181c3f413SJ.Bruce Fields * encode a fs_locations structure 163281c3f413SJ.Bruce Fields */ 1633b37ad28bSAl Viro static __be32 nfsd4_encode_fs_locations(struct svc_rqst *rqstp, 163481c3f413SJ.Bruce Fields struct svc_export *exp, 16352ebbc012SAl Viro __be32 **pp, int *buflen) 163681c3f413SJ.Bruce Fields { 1637b37ad28bSAl Viro __be32 status; 1638cc45f017SAl Viro int i; 16392ebbc012SAl Viro __be32 *p = *pp; 164081c3f413SJ.Bruce Fields struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs; 1641cc45f017SAl Viro char *root = nfsd4_path(rqstp, exp, &status); 164281c3f413SJ.Bruce Fields 1643cc45f017SAl Viro if (status) 1644cc45f017SAl Viro return status; 164581c3f413SJ.Bruce Fields status = nfsd4_encode_components('/', root, &p, buflen); 164681c3f413SJ.Bruce Fields if (status) 164781c3f413SJ.Bruce Fields return status; 164881c3f413SJ.Bruce Fields if ((*buflen -= 4) < 0) 164981c3f413SJ.Bruce Fields return nfserr_resource; 165081c3f413SJ.Bruce Fields WRITE32(fslocs->locations_count); 165181c3f413SJ.Bruce Fields for (i=0; i<fslocs->locations_count; i++) { 165281c3f413SJ.Bruce Fields status = nfsd4_encode_fs_location4(&fslocs->locations[i], 165381c3f413SJ.Bruce Fields &p, buflen); 165481c3f413SJ.Bruce Fields if (status) 165581c3f413SJ.Bruce Fields return status; 165681c3f413SJ.Bruce Fields } 165781c3f413SJ.Bruce Fields *pp = p; 165881c3f413SJ.Bruce Fields return 0; 165981c3f413SJ.Bruce Fields } 16601da177e4SLinus Torvalds 16611da177e4SLinus Torvalds static u32 nfs4_ftypes[16] = { 16621da177e4SLinus Torvalds NF4BAD, NF4FIFO, NF4CHR, NF4BAD, 16631da177e4SLinus Torvalds NF4DIR, NF4BAD, NF4BLK, NF4BAD, 16641da177e4SLinus Torvalds NF4REG, NF4BAD, NF4LNK, NF4BAD, 16651da177e4SLinus Torvalds NF4SOCK, NF4BAD, NF4LNK, NF4BAD, 16661da177e4SLinus Torvalds }; 16671da177e4SLinus Torvalds 1668b37ad28bSAl Viro static __be32 16691da177e4SLinus Torvalds nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group, 16702ebbc012SAl Viro __be32 **p, int *buflen) 16711da177e4SLinus Torvalds { 16721da177e4SLinus Torvalds int status; 16731da177e4SLinus Torvalds 16741da177e4SLinus Torvalds if (*buflen < (XDR_QUADLEN(IDMAP_NAMESZ) << 2) + 4) 16751da177e4SLinus Torvalds return nfserr_resource; 16761da177e4SLinus Torvalds if (whotype != NFS4_ACL_WHO_NAMED) 16771da177e4SLinus Torvalds status = nfs4_acl_write_who(whotype, (u8 *)(*p + 1)); 16781da177e4SLinus Torvalds else if (group) 16791da177e4SLinus Torvalds status = nfsd_map_gid_to_name(rqstp, id, (u8 *)(*p + 1)); 16801da177e4SLinus Torvalds else 16811da177e4SLinus Torvalds status = nfsd_map_uid_to_name(rqstp, id, (u8 *)(*p + 1)); 16821da177e4SLinus Torvalds if (status < 0) 16831da177e4SLinus Torvalds return nfserrno(status); 16841da177e4SLinus Torvalds *p = xdr_encode_opaque(*p, NULL, status); 16851da177e4SLinus Torvalds *buflen -= (XDR_QUADLEN(status) << 2) + 4; 16861da177e4SLinus Torvalds BUG_ON(*buflen < 0); 16871da177e4SLinus Torvalds return 0; 16881da177e4SLinus Torvalds } 16891da177e4SLinus Torvalds 1690b37ad28bSAl Viro static inline __be32 16912ebbc012SAl Viro nfsd4_encode_user(struct svc_rqst *rqstp, uid_t uid, __be32 **p, int *buflen) 16921da177e4SLinus Torvalds { 16931da177e4SLinus Torvalds return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, uid, 0, p, buflen); 16941da177e4SLinus Torvalds } 16951da177e4SLinus Torvalds 1696b37ad28bSAl Viro static inline __be32 16972ebbc012SAl Viro nfsd4_encode_group(struct svc_rqst *rqstp, uid_t gid, __be32 **p, int *buflen) 16981da177e4SLinus Torvalds { 16991da177e4SLinus Torvalds return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, gid, 1, p, buflen); 17001da177e4SLinus Torvalds } 17011da177e4SLinus Torvalds 1702b37ad28bSAl Viro static inline __be32 17031da177e4SLinus Torvalds nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group, 17042ebbc012SAl Viro __be32 **p, int *buflen) 17051da177e4SLinus Torvalds { 17061da177e4SLinus Torvalds return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen); 17071da177e4SLinus Torvalds } 17081da177e4SLinus Torvalds 170942ca0993SJ.Bruce Fields #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \ 171042ca0993SJ.Bruce Fields FATTR4_WORD0_RDATTR_ERROR) 171142ca0993SJ.Bruce Fields #define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID 171242ca0993SJ.Bruce Fields 1713b37ad28bSAl Viro static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *rdattr_err) 171442ca0993SJ.Bruce Fields { 171542ca0993SJ.Bruce Fields /* As per referral draft: */ 171642ca0993SJ.Bruce Fields if (*bmval0 & ~WORD0_ABSENT_FS_ATTRS || 171742ca0993SJ.Bruce Fields *bmval1 & ~WORD1_ABSENT_FS_ATTRS) { 171842ca0993SJ.Bruce Fields if (*bmval0 & FATTR4_WORD0_RDATTR_ERROR || 171942ca0993SJ.Bruce Fields *bmval0 & FATTR4_WORD0_FS_LOCATIONS) 172042ca0993SJ.Bruce Fields *rdattr_err = NFSERR_MOVED; 172142ca0993SJ.Bruce Fields else 172242ca0993SJ.Bruce Fields return nfserr_moved; 172342ca0993SJ.Bruce Fields } 172442ca0993SJ.Bruce Fields *bmval0 &= WORD0_ABSENT_FS_ATTRS; 172542ca0993SJ.Bruce Fields *bmval1 &= WORD1_ABSENT_FS_ATTRS; 172642ca0993SJ.Bruce Fields return 0; 172742ca0993SJ.Bruce Fields } 17281da177e4SLinus Torvalds 17291da177e4SLinus Torvalds /* 17301da177e4SLinus Torvalds * Note: @fhp can be NULL; in this case, we might have to compose the filehandle 17311da177e4SLinus Torvalds * ourselves. 17321da177e4SLinus Torvalds * 17331da177e4SLinus Torvalds * @countp is the buffer size in _words_; upon successful return this becomes 17341da177e4SLinus Torvalds * replaced with the number of words written. 17351da177e4SLinus Torvalds */ 1736b37ad28bSAl Viro __be32 17371da177e4SLinus Torvalds nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, 17382ebbc012SAl Viro struct dentry *dentry, __be32 *buffer, int *countp, u32 *bmval, 1739406a7ea9SFrank Filz struct svc_rqst *rqstp, int ignore_crossmnt) 17401da177e4SLinus Torvalds { 17411da177e4SLinus Torvalds u32 bmval0 = bmval[0]; 17421da177e4SLinus Torvalds u32 bmval1 = bmval[1]; 17437e705706SAndy Adamson u32 bmval2 = bmval[2]; 17441da177e4SLinus Torvalds struct kstat stat; 17451da177e4SLinus Torvalds struct svc_fh tempfh; 17461da177e4SLinus Torvalds struct kstatfs statfs; 17471da177e4SLinus Torvalds int buflen = *countp << 2; 17482ebbc012SAl Viro __be32 *attrlenp; 17491da177e4SLinus Torvalds u32 dummy; 17501da177e4SLinus Torvalds u64 dummy64; 175142ca0993SJ.Bruce Fields u32 rdattr_err = 0; 17522ebbc012SAl Viro __be32 *p = buffer; 1753b37ad28bSAl Viro __be32 status; 1754b8dd7b9aSAl Viro int err; 17551da177e4SLinus Torvalds int aclsupport = 0; 17561da177e4SLinus Torvalds struct nfs4_acl *acl = NULL; 17577e705706SAndy Adamson struct nfsd4_compoundres *resp = rqstp->rq_resp; 17587e705706SAndy Adamson u32 minorversion = resp->cstate.minorversion; 1759*ebabe9a9SChristoph Hellwig struct path path = { 1760*ebabe9a9SChristoph Hellwig .mnt = exp->ex_path.mnt, 1761*ebabe9a9SChristoph Hellwig .dentry = dentry, 1762*ebabe9a9SChristoph Hellwig }; 17631da177e4SLinus Torvalds 17641da177e4SLinus Torvalds BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1); 17657e705706SAndy Adamson BUG_ON(bmval0 & ~nfsd_suppattrs0(minorversion)); 17667e705706SAndy Adamson BUG_ON(bmval1 & ~nfsd_suppattrs1(minorversion)); 17677e705706SAndy Adamson BUG_ON(bmval2 & ~nfsd_suppattrs2(minorversion)); 17681da177e4SLinus Torvalds 176942ca0993SJ.Bruce Fields if (exp->ex_fslocs.migrated) { 17707e705706SAndy Adamson BUG_ON(bmval[2]); 177142ca0993SJ.Bruce Fields status = fattr_handle_absent_fs(&bmval0, &bmval1, &rdattr_err); 177242ca0993SJ.Bruce Fields if (status) 177342ca0993SJ.Bruce Fields goto out; 177442ca0993SJ.Bruce Fields } 177542ca0993SJ.Bruce Fields 177654775491SJan Blunck err = vfs_getattr(exp->ex_path.mnt, dentry, &stat); 1777b8dd7b9aSAl Viro if (err) 17781da177e4SLinus Torvalds goto out_nfserr; 1779a16e92edSJ. Bruce Fields if ((bmval0 & (FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL | 1780a16e92edSJ. Bruce Fields FATTR4_WORD0_MAXNAME)) || 17811da177e4SLinus Torvalds (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | 17821da177e4SLinus Torvalds FATTR4_WORD1_SPACE_TOTAL))) { 1783*ebabe9a9SChristoph Hellwig err = vfs_statfs(&path, &statfs); 1784b8dd7b9aSAl Viro if (err) 17851da177e4SLinus Torvalds goto out_nfserr; 17861da177e4SLinus Torvalds } 17871da177e4SLinus Torvalds if ((bmval0 & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) && !fhp) { 17881da177e4SLinus Torvalds fh_init(&tempfh, NFS4_FHSIZE); 17891da177e4SLinus Torvalds status = fh_compose(&tempfh, exp, dentry, NULL); 17901da177e4SLinus Torvalds if (status) 17911da177e4SLinus Torvalds goto out; 17921da177e4SLinus Torvalds fhp = &tempfh; 17931da177e4SLinus Torvalds } 17941da177e4SLinus Torvalds if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT 17951da177e4SLinus Torvalds | FATTR4_WORD0_SUPPORTED_ATTRS)) { 1796b8dd7b9aSAl Viro err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl); 1797b8dd7b9aSAl Viro aclsupport = (err == 0); 17981da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_ACL) { 1799b8dd7b9aSAl Viro if (err == -EOPNOTSUPP) 18001da177e4SLinus Torvalds bmval0 &= ~FATTR4_WORD0_ACL; 1801b8dd7b9aSAl Viro else if (err == -EINVAL) { 18021da177e4SLinus Torvalds status = nfserr_attrnotsupp; 18031da177e4SLinus Torvalds goto out; 1804b8dd7b9aSAl Viro } else if (err != 0) 18051da177e4SLinus Torvalds goto out_nfserr; 18061da177e4SLinus Torvalds } 18071da177e4SLinus Torvalds } 18081da177e4SLinus Torvalds if ((buflen -= 16) < 0) 18091da177e4SLinus Torvalds goto out_resource; 18101da177e4SLinus Torvalds 18117e705706SAndy Adamson if (unlikely(bmval2)) { 18127e705706SAndy Adamson WRITE32(3); 18137e705706SAndy Adamson WRITE32(bmval0); 18147e705706SAndy Adamson WRITE32(bmval1); 18157e705706SAndy Adamson WRITE32(bmval2); 18167e705706SAndy Adamson } else if (likely(bmval1)) { 18171da177e4SLinus Torvalds WRITE32(2); 18181da177e4SLinus Torvalds WRITE32(bmval0); 18191da177e4SLinus Torvalds WRITE32(bmval1); 18207e705706SAndy Adamson } else { 18217e705706SAndy Adamson WRITE32(1); 18227e705706SAndy Adamson WRITE32(bmval0); 18237e705706SAndy Adamson } 18241da177e4SLinus Torvalds attrlenp = p++; /* to be backfilled later */ 18251da177e4SLinus Torvalds 18261da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) { 18277e705706SAndy Adamson u32 word0 = nfsd_suppattrs0(minorversion); 18287e705706SAndy Adamson u32 word1 = nfsd_suppattrs1(minorversion); 18297e705706SAndy Adamson u32 word2 = nfsd_suppattrs2(minorversion); 18307e705706SAndy Adamson 18311da177e4SLinus Torvalds if ((buflen -= 12) < 0) 18321da177e4SLinus Torvalds goto out_resource; 183342ca0993SJ.Bruce Fields if (!aclsupport) 183442ca0993SJ.Bruce Fields word0 &= ~FATTR4_WORD0_ACL; 18357e705706SAndy Adamson if (!word2) { 18361da177e4SLinus Torvalds WRITE32(2); 183742ca0993SJ.Bruce Fields WRITE32(word0); 18387e705706SAndy Adamson WRITE32(word1); 18397e705706SAndy Adamson } else { 18407e705706SAndy Adamson WRITE32(3); 18417e705706SAndy Adamson WRITE32(word0); 18427e705706SAndy Adamson WRITE32(word1); 18437e705706SAndy Adamson WRITE32(word2); 18447e705706SAndy Adamson } 18451da177e4SLinus Torvalds } 18461da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_TYPE) { 18471da177e4SLinus Torvalds if ((buflen -= 4) < 0) 18481da177e4SLinus Torvalds goto out_resource; 18491da177e4SLinus Torvalds dummy = nfs4_ftypes[(stat.mode & S_IFMT) >> 12]; 18501da177e4SLinus Torvalds if (dummy == NF4BAD) 18511da177e4SLinus Torvalds goto out_serverfault; 18521da177e4SLinus Torvalds WRITE32(dummy); 18531da177e4SLinus Torvalds } 18541da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) { 18551da177e4SLinus Torvalds if ((buflen -= 4) < 0) 18561da177e4SLinus Torvalds goto out_resource; 185749640001SNeilBrown if (exp->ex_flags & NFSEXP_NOSUBTREECHECK) 1858e34ac862SNeilBrown WRITE32(NFS4_FH_PERSISTENT); 185949640001SNeilBrown else 1860e34ac862SNeilBrown WRITE32(NFS4_FH_PERSISTENT|NFS4_FH_VOL_RENAME); 18611da177e4SLinus Torvalds } 18621da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_CHANGE) { 18631da177e4SLinus Torvalds if ((buflen -= 8) < 0) 18641da177e4SLinus Torvalds goto out_resource; 1865c654b8a9SJ. Bruce Fields write_change(&p, &stat, dentry->d_inode); 18661da177e4SLinus Torvalds } 18671da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_SIZE) { 18681da177e4SLinus Torvalds if ((buflen -= 8) < 0) 18691da177e4SLinus Torvalds goto out_resource; 18701da177e4SLinus Torvalds WRITE64(stat.size); 18711da177e4SLinus Torvalds } 18721da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) { 18731da177e4SLinus Torvalds if ((buflen -= 4) < 0) 18741da177e4SLinus Torvalds goto out_resource; 18751da177e4SLinus Torvalds WRITE32(1); 18761da177e4SLinus Torvalds } 18771da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_SYMLINK_SUPPORT) { 18781da177e4SLinus Torvalds if ((buflen -= 4) < 0) 18791da177e4SLinus Torvalds goto out_resource; 18801da177e4SLinus Torvalds WRITE32(1); 18811da177e4SLinus Torvalds } 18821da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_NAMED_ATTR) { 18831da177e4SLinus Torvalds if ((buflen -= 4) < 0) 18841da177e4SLinus Torvalds goto out_resource; 18851da177e4SLinus Torvalds WRITE32(0); 18861da177e4SLinus Torvalds } 18871da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FSID) { 18881da177e4SLinus Torvalds if ((buflen -= 16) < 0) 18891da177e4SLinus Torvalds goto out_resource; 189042ca0993SJ.Bruce Fields if (exp->ex_fslocs.migrated) { 189142ca0993SJ.Bruce Fields WRITE64(NFS4_REFERRAL_FSID_MAJOR); 189242ca0993SJ.Bruce Fields WRITE64(NFS4_REFERRAL_FSID_MINOR); 1893af6a4e28SNeilBrown } else switch(fsid_source(fhp)) { 1894af6a4e28SNeilBrown case FSIDSOURCE_FSID: 18951da177e4SLinus Torvalds WRITE64((u64)exp->ex_fsid); 18961da177e4SLinus Torvalds WRITE64((u64)0); 1897af6a4e28SNeilBrown break; 1898af6a4e28SNeilBrown case FSIDSOURCE_DEV: 18991da177e4SLinus Torvalds WRITE32(0); 19001da177e4SLinus Torvalds WRITE32(MAJOR(stat.dev)); 19011da177e4SLinus Torvalds WRITE32(0); 19021da177e4SLinus Torvalds WRITE32(MINOR(stat.dev)); 1903af6a4e28SNeilBrown break; 1904af6a4e28SNeilBrown case FSIDSOURCE_UUID: 1905af6a4e28SNeilBrown WRITEMEM(exp->ex_uuid, 16); 1906af6a4e28SNeilBrown break; 19071da177e4SLinus Torvalds } 19081da177e4SLinus Torvalds } 19091da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) { 19101da177e4SLinus Torvalds if ((buflen -= 4) < 0) 19111da177e4SLinus Torvalds goto out_resource; 19121da177e4SLinus Torvalds WRITE32(0); 19131da177e4SLinus Torvalds } 19141da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_LEASE_TIME) { 19151da177e4SLinus Torvalds if ((buflen -= 4) < 0) 19161da177e4SLinus Torvalds goto out_resource; 1917cf07d2eaSJ. Bruce Fields WRITE32(nfsd4_lease); 19181da177e4SLinus Torvalds } 19191da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) { 19201da177e4SLinus Torvalds if ((buflen -= 4) < 0) 19211da177e4SLinus Torvalds goto out_resource; 192242ca0993SJ.Bruce Fields WRITE32(rdattr_err); 19231da177e4SLinus Torvalds } 19241da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_ACL) { 19251da177e4SLinus Torvalds struct nfs4_ace *ace; 19261da177e4SLinus Torvalds 19271da177e4SLinus Torvalds if (acl == NULL) { 19281da177e4SLinus Torvalds if ((buflen -= 4) < 0) 19291da177e4SLinus Torvalds goto out_resource; 19301da177e4SLinus Torvalds 19311da177e4SLinus Torvalds WRITE32(0); 19321da177e4SLinus Torvalds goto out_acl; 19331da177e4SLinus Torvalds } 19341da177e4SLinus Torvalds if ((buflen -= 4) < 0) 19351da177e4SLinus Torvalds goto out_resource; 19361da177e4SLinus Torvalds WRITE32(acl->naces); 19371da177e4SLinus Torvalds 193828e05dd8SJ. Bruce Fields for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) { 19391da177e4SLinus Torvalds if ((buflen -= 4*3) < 0) 19401da177e4SLinus Torvalds goto out_resource; 19411da177e4SLinus Torvalds WRITE32(ace->type); 19421da177e4SLinus Torvalds WRITE32(ace->flag); 19431da177e4SLinus Torvalds WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL); 19441da177e4SLinus Torvalds status = nfsd4_encode_aclname(rqstp, ace->whotype, 19451da177e4SLinus Torvalds ace->who, ace->flag & NFS4_ACE_IDENTIFIER_GROUP, 19461da177e4SLinus Torvalds &p, &buflen); 19471da177e4SLinus Torvalds if (status == nfserr_resource) 19481da177e4SLinus Torvalds goto out_resource; 19491da177e4SLinus Torvalds if (status) 19501da177e4SLinus Torvalds goto out; 19511da177e4SLinus Torvalds } 19521da177e4SLinus Torvalds } 19531da177e4SLinus Torvalds out_acl: 19541da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_ACLSUPPORT) { 19551da177e4SLinus Torvalds if ((buflen -= 4) < 0) 19561da177e4SLinus Torvalds goto out_resource; 19571da177e4SLinus Torvalds WRITE32(aclsupport ? 19581da177e4SLinus Torvalds ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0); 19591da177e4SLinus Torvalds } 19601da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_CANSETTIME) { 19611da177e4SLinus Torvalds if ((buflen -= 4) < 0) 19621da177e4SLinus Torvalds goto out_resource; 19631da177e4SLinus Torvalds WRITE32(1); 19641da177e4SLinus Torvalds } 19651da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) { 19661da177e4SLinus Torvalds if ((buflen -= 4) < 0) 19671da177e4SLinus Torvalds goto out_resource; 19681da177e4SLinus Torvalds WRITE32(1); 19691da177e4SLinus Torvalds } 19701da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) { 19711da177e4SLinus Torvalds if ((buflen -= 4) < 0) 19721da177e4SLinus Torvalds goto out_resource; 19731da177e4SLinus Torvalds WRITE32(1); 19741da177e4SLinus Torvalds } 19751da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_CHOWN_RESTRICTED) { 19761da177e4SLinus Torvalds if ((buflen -= 4) < 0) 19771da177e4SLinus Torvalds goto out_resource; 19781da177e4SLinus Torvalds WRITE32(1); 19791da177e4SLinus Torvalds } 19801da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FILEHANDLE) { 19811da177e4SLinus Torvalds buflen -= (XDR_QUADLEN(fhp->fh_handle.fh_size) << 2) + 4; 19821da177e4SLinus Torvalds if (buflen < 0) 19831da177e4SLinus Torvalds goto out_resource; 19841da177e4SLinus Torvalds WRITE32(fhp->fh_handle.fh_size); 19851da177e4SLinus Torvalds WRITEMEM(&fhp->fh_handle.fh_base, fhp->fh_handle.fh_size); 19861da177e4SLinus Torvalds } 19871da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FILEID) { 19881da177e4SLinus Torvalds if ((buflen -= 8) < 0) 19891da177e4SLinus Torvalds goto out_resource; 199040ee5dc6SPeter Staubach WRITE64(stat.ino); 19911da177e4SLinus Torvalds } 19921da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FILES_AVAIL) { 19931da177e4SLinus Torvalds if ((buflen -= 8) < 0) 19941da177e4SLinus Torvalds goto out_resource; 19951da177e4SLinus Torvalds WRITE64((u64) statfs.f_ffree); 19961da177e4SLinus Torvalds } 19971da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FILES_FREE) { 19981da177e4SLinus Torvalds if ((buflen -= 8) < 0) 19991da177e4SLinus Torvalds goto out_resource; 20001da177e4SLinus Torvalds WRITE64((u64) statfs.f_ffree); 20011da177e4SLinus Torvalds } 20021da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_FILES_TOTAL) { 20031da177e4SLinus Torvalds if ((buflen -= 8) < 0) 20041da177e4SLinus Torvalds goto out_resource; 20051da177e4SLinus Torvalds WRITE64((u64) statfs.f_files); 20061da177e4SLinus Torvalds } 200781c3f413SJ.Bruce Fields if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) { 200881c3f413SJ.Bruce Fields status = nfsd4_encode_fs_locations(rqstp, exp, &p, &buflen); 200981c3f413SJ.Bruce Fields if (status == nfserr_resource) 201081c3f413SJ.Bruce Fields goto out_resource; 201181c3f413SJ.Bruce Fields if (status) 201281c3f413SJ.Bruce Fields goto out; 201381c3f413SJ.Bruce Fields } 20141da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) { 20151da177e4SLinus Torvalds if ((buflen -= 4) < 0) 20161da177e4SLinus Torvalds goto out_resource; 20171da177e4SLinus Torvalds WRITE32(1); 20181da177e4SLinus Torvalds } 20191da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_MAXFILESIZE) { 20201da177e4SLinus Torvalds if ((buflen -= 8) < 0) 20211da177e4SLinus Torvalds goto out_resource; 20221da177e4SLinus Torvalds WRITE64(~(u64)0); 20231da177e4SLinus Torvalds } 20241da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_MAXLINK) { 20251da177e4SLinus Torvalds if ((buflen -= 4) < 0) 20261da177e4SLinus Torvalds goto out_resource; 20271da177e4SLinus Torvalds WRITE32(255); 20281da177e4SLinus Torvalds } 20291da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_MAXNAME) { 20301da177e4SLinus Torvalds if ((buflen -= 4) < 0) 20311da177e4SLinus Torvalds goto out_resource; 2032a16e92edSJ. Bruce Fields WRITE32(statfs.f_namelen); 20331da177e4SLinus Torvalds } 20341da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_MAXREAD) { 20351da177e4SLinus Torvalds if ((buflen -= 8) < 0) 20361da177e4SLinus Torvalds goto out_resource; 20377adae489SGreg Banks WRITE64((u64) svc_max_payload(rqstp)); 20381da177e4SLinus Torvalds } 20391da177e4SLinus Torvalds if (bmval0 & FATTR4_WORD0_MAXWRITE) { 20401da177e4SLinus Torvalds if ((buflen -= 8) < 0) 20411da177e4SLinus Torvalds goto out_resource; 20427adae489SGreg Banks WRITE64((u64) svc_max_payload(rqstp)); 20431da177e4SLinus Torvalds } 20441da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_MODE) { 20451da177e4SLinus Torvalds if ((buflen -= 4) < 0) 20461da177e4SLinus Torvalds goto out_resource; 20471da177e4SLinus Torvalds WRITE32(stat.mode & S_IALLUGO); 20481da177e4SLinus Torvalds } 20491da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_NO_TRUNC) { 20501da177e4SLinus Torvalds if ((buflen -= 4) < 0) 20511da177e4SLinus Torvalds goto out_resource; 20521da177e4SLinus Torvalds WRITE32(1); 20531da177e4SLinus Torvalds } 20541da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_NUMLINKS) { 20551da177e4SLinus Torvalds if ((buflen -= 4) < 0) 20561da177e4SLinus Torvalds goto out_resource; 20571da177e4SLinus Torvalds WRITE32(stat.nlink); 20581da177e4SLinus Torvalds } 20591da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_OWNER) { 20601da177e4SLinus Torvalds status = nfsd4_encode_user(rqstp, stat.uid, &p, &buflen); 20611da177e4SLinus Torvalds if (status == nfserr_resource) 20621da177e4SLinus Torvalds goto out_resource; 20631da177e4SLinus Torvalds if (status) 20641da177e4SLinus Torvalds goto out; 20651da177e4SLinus Torvalds } 20661da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_OWNER_GROUP) { 20671da177e4SLinus Torvalds status = nfsd4_encode_group(rqstp, stat.gid, &p, &buflen); 20681da177e4SLinus Torvalds if (status == nfserr_resource) 20691da177e4SLinus Torvalds goto out_resource; 20701da177e4SLinus Torvalds if (status) 20711da177e4SLinus Torvalds goto out; 20721da177e4SLinus Torvalds } 20731da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_RAWDEV) { 20741da177e4SLinus Torvalds if ((buflen -= 8) < 0) 20751da177e4SLinus Torvalds goto out_resource; 20761da177e4SLinus Torvalds WRITE32((u32) MAJOR(stat.rdev)); 20771da177e4SLinus Torvalds WRITE32((u32) MINOR(stat.rdev)); 20781da177e4SLinus Torvalds } 20791da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) { 20801da177e4SLinus Torvalds if ((buflen -= 8) < 0) 20811da177e4SLinus Torvalds goto out_resource; 20821da177e4SLinus Torvalds dummy64 = (u64)statfs.f_bavail * (u64)statfs.f_bsize; 20831da177e4SLinus Torvalds WRITE64(dummy64); 20841da177e4SLinus Torvalds } 20851da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_SPACE_FREE) { 20861da177e4SLinus Torvalds if ((buflen -= 8) < 0) 20871da177e4SLinus Torvalds goto out_resource; 20881da177e4SLinus Torvalds dummy64 = (u64)statfs.f_bfree * (u64)statfs.f_bsize; 20891da177e4SLinus Torvalds WRITE64(dummy64); 20901da177e4SLinus Torvalds } 20911da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) { 20921da177e4SLinus Torvalds if ((buflen -= 8) < 0) 20931da177e4SLinus Torvalds goto out_resource; 20941da177e4SLinus Torvalds dummy64 = (u64)statfs.f_blocks * (u64)statfs.f_bsize; 20951da177e4SLinus Torvalds WRITE64(dummy64); 20961da177e4SLinus Torvalds } 20971da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_SPACE_USED) { 20981da177e4SLinus Torvalds if ((buflen -= 8) < 0) 20991da177e4SLinus Torvalds goto out_resource; 21001da177e4SLinus Torvalds dummy64 = (u64)stat.blocks << 9; 21011da177e4SLinus Torvalds WRITE64(dummy64); 21021da177e4SLinus Torvalds } 21031da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_TIME_ACCESS) { 21041da177e4SLinus Torvalds if ((buflen -= 12) < 0) 21051da177e4SLinus Torvalds goto out_resource; 21061da177e4SLinus Torvalds WRITE32(0); 21071da177e4SLinus Torvalds WRITE32(stat.atime.tv_sec); 21081da177e4SLinus Torvalds WRITE32(stat.atime.tv_nsec); 21091da177e4SLinus Torvalds } 21101da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_TIME_DELTA) { 21111da177e4SLinus Torvalds if ((buflen -= 12) < 0) 21121da177e4SLinus Torvalds goto out_resource; 21131da177e4SLinus Torvalds WRITE32(0); 21141da177e4SLinus Torvalds WRITE32(1); 21151da177e4SLinus Torvalds WRITE32(0); 21161da177e4SLinus Torvalds } 21171da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_TIME_METADATA) { 21181da177e4SLinus Torvalds if ((buflen -= 12) < 0) 21191da177e4SLinus Torvalds goto out_resource; 21201da177e4SLinus Torvalds WRITE32(0); 21211da177e4SLinus Torvalds WRITE32(stat.ctime.tv_sec); 21221da177e4SLinus Torvalds WRITE32(stat.ctime.tv_nsec); 21231da177e4SLinus Torvalds } 21241da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_TIME_MODIFY) { 21251da177e4SLinus Torvalds if ((buflen -= 12) < 0) 21261da177e4SLinus Torvalds goto out_resource; 21271da177e4SLinus Torvalds WRITE32(0); 21281da177e4SLinus Torvalds WRITE32(stat.mtime.tv_sec); 21291da177e4SLinus Torvalds WRITE32(stat.mtime.tv_nsec); 21301da177e4SLinus Torvalds } 21311da177e4SLinus Torvalds if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) { 21321da177e4SLinus Torvalds if ((buflen -= 8) < 0) 21331da177e4SLinus Torvalds goto out_resource; 2134406a7ea9SFrank Filz /* 2135406a7ea9SFrank Filz * Get parent's attributes if not ignoring crossmount 2136406a7ea9SFrank Filz * and this is the root of a cross-mounted filesystem. 2137406a7ea9SFrank Filz */ 2138406a7ea9SFrank Filz if (ignore_crossmnt == 0 && 2139462d6057SAl Viro dentry == exp->ex_path.mnt->mnt_root) { 2140462d6057SAl Viro struct path path = exp->ex_path; 2141462d6057SAl Viro path_get(&path); 2142462d6057SAl Viro while (follow_up(&path)) { 2143462d6057SAl Viro if (path.dentry != path.mnt->mnt_root) 2144462d6057SAl Viro break; 2145462d6057SAl Viro } 2146462d6057SAl Viro err = vfs_getattr(path.mnt, path.dentry, &stat); 2147462d6057SAl Viro path_put(&path); 214840ee5dc6SPeter Staubach if (err) 214940ee5dc6SPeter Staubach goto out_nfserr; 215040ee5dc6SPeter Staubach } 215140ee5dc6SPeter Staubach WRITE64(stat.ino); 21521da177e4SLinus Torvalds } 21538c18f205SBenny Halevy if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) { 21548c18f205SBenny Halevy WRITE32(3); 21558c18f205SBenny Halevy WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD0); 21568c18f205SBenny Halevy WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD1); 21578c18f205SBenny Halevy WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD2); 21588c18f205SBenny Halevy } 21597e705706SAndy Adamson 21601da177e4SLinus Torvalds *attrlenp = htonl((char *)p - (char *)attrlenp - 4); 21611da177e4SLinus Torvalds *countp = p - buffer; 21621da177e4SLinus Torvalds status = nfs_ok; 21631da177e4SLinus Torvalds 21641da177e4SLinus Torvalds out: 216528e05dd8SJ. Bruce Fields kfree(acl); 21661da177e4SLinus Torvalds if (fhp == &tempfh) 21671da177e4SLinus Torvalds fh_put(&tempfh); 21681da177e4SLinus Torvalds return status; 21691da177e4SLinus Torvalds out_nfserr: 2170b8dd7b9aSAl Viro status = nfserrno(err); 21711da177e4SLinus Torvalds goto out; 21721da177e4SLinus Torvalds out_resource: 21731da177e4SLinus Torvalds *countp = 0; 21741da177e4SLinus Torvalds status = nfserr_resource; 21751da177e4SLinus Torvalds goto out; 21761da177e4SLinus Torvalds out_serverfault: 21771da177e4SLinus Torvalds status = nfserr_serverfault; 21781da177e4SLinus Torvalds goto out; 21791da177e4SLinus Torvalds } 21801da177e4SLinus Torvalds 2181c0ce6ec8SJ. Bruce Fields static inline int attributes_need_mount(u32 *bmval) 2182c0ce6ec8SJ. Bruce Fields { 2183c0ce6ec8SJ. Bruce Fields if (bmval[0] & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_LEASE_TIME)) 2184c0ce6ec8SJ. Bruce Fields return 1; 2185c0ce6ec8SJ. Bruce Fields if (bmval[1] & ~FATTR4_WORD1_MOUNTED_ON_FILEID) 2186c0ce6ec8SJ. Bruce Fields return 1; 2187c0ce6ec8SJ. Bruce Fields return 0; 2188c0ce6ec8SJ. Bruce Fields } 2189c0ce6ec8SJ. Bruce Fields 2190b37ad28bSAl Viro static __be32 21911da177e4SLinus Torvalds nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd, 21922ebbc012SAl Viro const char *name, int namlen, __be32 *p, int *buflen) 21931da177e4SLinus Torvalds { 21941da177e4SLinus Torvalds struct svc_export *exp = cd->rd_fhp->fh_export; 21951da177e4SLinus Torvalds struct dentry *dentry; 2196b37ad28bSAl Viro __be32 nfserr; 2197406a7ea9SFrank Filz int ignore_crossmnt = 0; 21981da177e4SLinus Torvalds 21991da177e4SLinus Torvalds dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen); 22001da177e4SLinus Torvalds if (IS_ERR(dentry)) 22011da177e4SLinus Torvalds return nfserrno(PTR_ERR(dentry)); 2202b2c0cea6SJ. Bruce Fields if (!dentry->d_inode) { 2203b2c0cea6SJ. Bruce Fields /* 2204b2c0cea6SJ. Bruce Fields * nfsd_buffered_readdir drops the i_mutex between 2205b2c0cea6SJ. Bruce Fields * readdir and calling this callback, leaving a window 2206b2c0cea6SJ. Bruce Fields * where this directory entry could have gone away. 2207b2c0cea6SJ. Bruce Fields */ 2208b2c0cea6SJ. Bruce Fields dput(dentry); 2209b2c0cea6SJ. Bruce Fields return nfserr_noent; 2210b2c0cea6SJ. Bruce Fields } 22111da177e4SLinus Torvalds 22121da177e4SLinus Torvalds exp_get(exp); 2213406a7ea9SFrank Filz /* 2214406a7ea9SFrank Filz * In the case of a mountpoint, the client may be asking for 2215406a7ea9SFrank Filz * attributes that are only properties of the underlying filesystem 2216406a7ea9SFrank Filz * as opposed to the cross-mounted file system. In such a case, 2217406a7ea9SFrank Filz * we will not follow the cross mount and will fill the attribtutes 2218406a7ea9SFrank Filz * directly from the mountpoint dentry. 2219406a7ea9SFrank Filz */ 22203227fa41SJ. Bruce Fields if (nfsd_mountpoint(dentry, exp)) { 2221021d3a72SJ.Bruce Fields int err; 2222021d3a72SJ.Bruce Fields 22233227fa41SJ. Bruce Fields if (!(exp->ex_flags & NFSEXP_V4ROOT) 22243227fa41SJ. Bruce Fields && !attributes_need_mount(cd->rd_bmval)) { 22253227fa41SJ. Bruce Fields ignore_crossmnt = 1; 22263227fa41SJ. Bruce Fields goto out_encode; 22273227fa41SJ. Bruce Fields } 2228dcb488a3SAndy Adamson /* 2229dcb488a3SAndy Adamson * Why the heck aren't we just using nfsd_lookup?? 2230dcb488a3SAndy Adamson * Different "."/".." handling? Something else? 2231dcb488a3SAndy Adamson * At least, add a comment here to explain.... 2232dcb488a3SAndy Adamson */ 2233021d3a72SJ.Bruce Fields err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp); 2234021d3a72SJ.Bruce Fields if (err) { 2235021d3a72SJ.Bruce Fields nfserr = nfserrno(err); 22361da177e4SLinus Torvalds goto out_put; 22371da177e4SLinus Torvalds } 2238dcb488a3SAndy Adamson nfserr = check_nfsd_access(exp, cd->rd_rqstp); 2239dcb488a3SAndy Adamson if (nfserr) 2240dcb488a3SAndy Adamson goto out_put; 22411da177e4SLinus Torvalds 22421da177e4SLinus Torvalds } 22433227fa41SJ. Bruce Fields out_encode: 22441da177e4SLinus Torvalds nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval, 2245406a7ea9SFrank Filz cd->rd_rqstp, ignore_crossmnt); 22461da177e4SLinus Torvalds out_put: 22471da177e4SLinus Torvalds dput(dentry); 22481da177e4SLinus Torvalds exp_put(exp); 22491da177e4SLinus Torvalds return nfserr; 22501da177e4SLinus Torvalds } 22511da177e4SLinus Torvalds 22522ebbc012SAl Viro static __be32 * 2253b37ad28bSAl Viro nfsd4_encode_rdattr_error(__be32 *p, int buflen, __be32 nfserr) 22541da177e4SLinus Torvalds { 22552ebbc012SAl Viro __be32 *attrlenp; 22561da177e4SLinus Torvalds 22571da177e4SLinus Torvalds if (buflen < 6) 22581da177e4SLinus Torvalds return NULL; 22591da177e4SLinus Torvalds *p++ = htonl(2); 22601da177e4SLinus Torvalds *p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */ 22611da177e4SLinus Torvalds *p++ = htonl(0); /* bmval1 */ 22621da177e4SLinus Torvalds 22631da177e4SLinus Torvalds attrlenp = p++; 22641da177e4SLinus Torvalds *p++ = nfserr; /* no htonl */ 22651da177e4SLinus Torvalds *attrlenp = htonl((char *)p - (char *)attrlenp - 4); 22661da177e4SLinus Torvalds return p; 22671da177e4SLinus Torvalds } 22681da177e4SLinus Torvalds 22691da177e4SLinus Torvalds static int 2270a0ad13efSNeilBrown nfsd4_encode_dirent(void *ccdv, const char *name, int namlen, 2271a0ad13efSNeilBrown loff_t offset, u64 ino, unsigned int d_type) 22721da177e4SLinus Torvalds { 2273a0ad13efSNeilBrown struct readdir_cd *ccd = ccdv; 22741da177e4SLinus Torvalds struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common); 22751da177e4SLinus Torvalds int buflen; 22762ebbc012SAl Viro __be32 *p = cd->buffer; 2277b2c0cea6SJ. Bruce Fields __be32 *cookiep; 2278b37ad28bSAl Viro __be32 nfserr = nfserr_toosmall; 22791da177e4SLinus Torvalds 22801da177e4SLinus Torvalds /* In nfsv4, "." and ".." never make it onto the wire.. */ 22811da177e4SLinus Torvalds if (name && isdotent(name, namlen)) { 22821da177e4SLinus Torvalds cd->common.err = nfs_ok; 22831da177e4SLinus Torvalds return 0; 22841da177e4SLinus Torvalds } 22851da177e4SLinus Torvalds 22861da177e4SLinus Torvalds if (cd->offset) 22871da177e4SLinus Torvalds xdr_encode_hyper(cd->offset, (u64) offset); 22881da177e4SLinus Torvalds 22891da177e4SLinus Torvalds buflen = cd->buflen - 4 - XDR_QUADLEN(namlen); 22901da177e4SLinus Torvalds if (buflen < 0) 22911da177e4SLinus Torvalds goto fail; 22921da177e4SLinus Torvalds 22931da177e4SLinus Torvalds *p++ = xdr_one; /* mark entry present */ 2294b2c0cea6SJ. Bruce Fields cookiep = p; 22951da177e4SLinus Torvalds p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */ 22961da177e4SLinus Torvalds p = xdr_encode_array(p, name, namlen); /* name length & name */ 22971da177e4SLinus Torvalds 22981da177e4SLinus Torvalds nfserr = nfsd4_encode_dirent_fattr(cd, name, namlen, p, &buflen); 22991da177e4SLinus Torvalds switch (nfserr) { 23001da177e4SLinus Torvalds case nfs_ok: 23011da177e4SLinus Torvalds p += buflen; 23021da177e4SLinus Torvalds break; 23031da177e4SLinus Torvalds case nfserr_resource: 23041da177e4SLinus Torvalds nfserr = nfserr_toosmall; 23051da177e4SLinus Torvalds goto fail; 23061da177e4SLinus Torvalds case nfserr_dropit: 23071da177e4SLinus Torvalds goto fail; 2308b2c0cea6SJ. Bruce Fields case nfserr_noent: 2309b2c0cea6SJ. Bruce Fields goto skip_entry; 23101da177e4SLinus Torvalds default: 23111da177e4SLinus Torvalds /* 23121da177e4SLinus Torvalds * If the client requested the RDATTR_ERROR attribute, 23131da177e4SLinus Torvalds * we stuff the error code into this attribute 23141da177e4SLinus Torvalds * and continue. If this attribute was not requested, 23151da177e4SLinus Torvalds * then in accordance with the spec, we fail the 23161da177e4SLinus Torvalds * entire READDIR operation(!) 23171da177e4SLinus Torvalds */ 23181da177e4SLinus Torvalds if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR)) 23191da177e4SLinus Torvalds goto fail; 23201da177e4SLinus Torvalds p = nfsd4_encode_rdattr_error(p, buflen, nfserr); 232134081efcSFred Isaman if (p == NULL) { 232234081efcSFred Isaman nfserr = nfserr_toosmall; 23231da177e4SLinus Torvalds goto fail; 23241da177e4SLinus Torvalds } 232534081efcSFred Isaman } 23261da177e4SLinus Torvalds cd->buflen -= (p - cd->buffer); 23271da177e4SLinus Torvalds cd->buffer = p; 2328b2c0cea6SJ. Bruce Fields cd->offset = cookiep; 2329b2c0cea6SJ. Bruce Fields skip_entry: 23301da177e4SLinus Torvalds cd->common.err = nfs_ok; 23311da177e4SLinus Torvalds return 0; 23321da177e4SLinus Torvalds fail: 23331da177e4SLinus Torvalds cd->common.err = nfserr; 23341da177e4SLinus Torvalds return -EINVAL; 23351da177e4SLinus Torvalds } 23361da177e4SLinus Torvalds 2337e2f282b9SBenny Halevy static void 2338e2f282b9SBenny Halevy nfsd4_encode_stateid(struct nfsd4_compoundres *resp, stateid_t *sid) 2339e2f282b9SBenny Halevy { 2340bc749ca4SJ. Bruce Fields __be32 *p; 2341e2f282b9SBenny Halevy 2342e2f282b9SBenny Halevy RESERVE_SPACE(sizeof(stateid_t)); 2343e2f282b9SBenny Halevy WRITE32(sid->si_generation); 2344e2f282b9SBenny Halevy WRITEMEM(&sid->si_opaque, sizeof(stateid_opaque_t)); 2345e2f282b9SBenny Halevy ADJUST_ARGS(); 2346e2f282b9SBenny Halevy } 2347e2f282b9SBenny Halevy 2348695e12f8SBenny Halevy static __be32 2349b37ad28bSAl Viro nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_access *access) 23501da177e4SLinus Torvalds { 2351bc749ca4SJ. Bruce Fields __be32 *p; 23521da177e4SLinus Torvalds 23531da177e4SLinus Torvalds if (!nfserr) { 23541da177e4SLinus Torvalds RESERVE_SPACE(8); 23551da177e4SLinus Torvalds WRITE32(access->ac_supported); 23561da177e4SLinus Torvalds WRITE32(access->ac_resp_access); 23571da177e4SLinus Torvalds ADJUST_ARGS(); 23581da177e4SLinus Torvalds } 2359695e12f8SBenny Halevy return nfserr; 23601da177e4SLinus Torvalds } 23611da177e4SLinus Torvalds 2362695e12f8SBenny Halevy static __be32 2363b37ad28bSAl Viro nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_close *close) 23641da177e4SLinus Torvalds { 23651da177e4SLinus Torvalds ENCODE_SEQID_OP_HEAD; 23661da177e4SLinus Torvalds 2367e2f282b9SBenny Halevy if (!nfserr) 2368e2f282b9SBenny Halevy nfsd4_encode_stateid(resp, &close->cl_stateid); 2369e2f282b9SBenny Halevy 23701da177e4SLinus Torvalds ENCODE_SEQID_OP_TAIL(close->cl_stateowner); 2371695e12f8SBenny Halevy return nfserr; 23721da177e4SLinus Torvalds } 23731da177e4SLinus Torvalds 23741da177e4SLinus Torvalds 2375695e12f8SBenny Halevy static __be32 2376b37ad28bSAl Viro nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_commit *commit) 23771da177e4SLinus Torvalds { 2378bc749ca4SJ. Bruce Fields __be32 *p; 23791da177e4SLinus Torvalds 23801da177e4SLinus Torvalds if (!nfserr) { 23811da177e4SLinus Torvalds RESERVE_SPACE(8); 23821da177e4SLinus Torvalds WRITEMEM(commit->co_verf.data, 8); 23831da177e4SLinus Torvalds ADJUST_ARGS(); 23841da177e4SLinus Torvalds } 2385695e12f8SBenny Halevy return nfserr; 23861da177e4SLinus Torvalds } 23871da177e4SLinus Torvalds 2388695e12f8SBenny Halevy static __be32 2389b37ad28bSAl Viro nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_create *create) 23901da177e4SLinus Torvalds { 2391bc749ca4SJ. Bruce Fields __be32 *p; 23921da177e4SLinus Torvalds 23931da177e4SLinus Torvalds if (!nfserr) { 23941da177e4SLinus Torvalds RESERVE_SPACE(32); 2395c654b8a9SJ. Bruce Fields write_cinfo(&p, &create->cr_cinfo); 23961da177e4SLinus Torvalds WRITE32(2); 23971da177e4SLinus Torvalds WRITE32(create->cr_bmval[0]); 23981da177e4SLinus Torvalds WRITE32(create->cr_bmval[1]); 23991da177e4SLinus Torvalds ADJUST_ARGS(); 24001da177e4SLinus Torvalds } 2401695e12f8SBenny Halevy return nfserr; 24021da177e4SLinus Torvalds } 24031da177e4SLinus Torvalds 2404b37ad28bSAl Viro static __be32 2405b37ad28bSAl Viro nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_getattr *getattr) 24061da177e4SLinus Torvalds { 24071da177e4SLinus Torvalds struct svc_fh *fhp = getattr->ga_fhp; 24081da177e4SLinus Torvalds int buflen; 24091da177e4SLinus Torvalds 24101da177e4SLinus Torvalds if (nfserr) 24111da177e4SLinus Torvalds return nfserr; 24121da177e4SLinus Torvalds 24131da177e4SLinus Torvalds buflen = resp->end - resp->p - (COMPOUND_ERR_SLACK_SPACE >> 2); 24141da177e4SLinus Torvalds nfserr = nfsd4_encode_fattr(fhp, fhp->fh_export, fhp->fh_dentry, 24151da177e4SLinus Torvalds resp->p, &buflen, getattr->ga_bmval, 2416406a7ea9SFrank Filz resp->rqstp, 0); 24171da177e4SLinus Torvalds if (!nfserr) 24181da177e4SLinus Torvalds resp->p += buflen; 24191da177e4SLinus Torvalds return nfserr; 24201da177e4SLinus Torvalds } 24211da177e4SLinus Torvalds 2422695e12f8SBenny Halevy static __be32 2423695e12f8SBenny Halevy nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh **fhpp) 24241da177e4SLinus Torvalds { 2425695e12f8SBenny Halevy struct svc_fh *fhp = *fhpp; 24261da177e4SLinus Torvalds unsigned int len; 2427bc749ca4SJ. Bruce Fields __be32 *p; 24281da177e4SLinus Torvalds 24291da177e4SLinus Torvalds if (!nfserr) { 24301da177e4SLinus Torvalds len = fhp->fh_handle.fh_size; 24311da177e4SLinus Torvalds RESERVE_SPACE(len + 4); 24321da177e4SLinus Torvalds WRITE32(len); 24331da177e4SLinus Torvalds WRITEMEM(&fhp->fh_handle.fh_base, len); 24341da177e4SLinus Torvalds ADJUST_ARGS(); 24351da177e4SLinus Torvalds } 2436695e12f8SBenny Halevy return nfserr; 24371da177e4SLinus Torvalds } 24381da177e4SLinus Torvalds 24391da177e4SLinus Torvalds /* 24401da177e4SLinus Torvalds * Including all fields other than the name, a LOCK4denied structure requires 24411da177e4SLinus Torvalds * 8(clientid) + 4(namelen) + 8(offset) + 8(length) + 4(type) = 32 bytes. 24421da177e4SLinus Torvalds */ 24431da177e4SLinus Torvalds static void 24441da177e4SLinus Torvalds nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denied *ld) 24451da177e4SLinus Torvalds { 2446bc749ca4SJ. Bruce Fields __be32 *p; 24471da177e4SLinus Torvalds 24481da177e4SLinus Torvalds RESERVE_SPACE(32 + XDR_LEN(ld->ld_sop ? ld->ld_sop->so_owner.len : 0)); 24491da177e4SLinus Torvalds WRITE64(ld->ld_start); 24501da177e4SLinus Torvalds WRITE64(ld->ld_length); 24511da177e4SLinus Torvalds WRITE32(ld->ld_type); 24521da177e4SLinus Torvalds if (ld->ld_sop) { 24531da177e4SLinus Torvalds WRITEMEM(&ld->ld_clientid, 8); 24541da177e4SLinus Torvalds WRITE32(ld->ld_sop->so_owner.len); 24551da177e4SLinus Torvalds WRITEMEM(ld->ld_sop->so_owner.data, ld->ld_sop->so_owner.len); 24561da177e4SLinus Torvalds kref_put(&ld->ld_sop->so_ref, nfs4_free_stateowner); 24571da177e4SLinus Torvalds } else { /* non - nfsv4 lock in conflict, no clientid nor owner */ 24581da177e4SLinus Torvalds WRITE64((u64)0); /* clientid */ 24591da177e4SLinus Torvalds WRITE32(0); /* length of owner name */ 24601da177e4SLinus Torvalds } 24611da177e4SLinus Torvalds ADJUST_ARGS(); 24621da177e4SLinus Torvalds } 24631da177e4SLinus Torvalds 2464695e12f8SBenny Halevy static __be32 2465b37ad28bSAl Viro nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lock *lock) 24661da177e4SLinus Torvalds { 24671da177e4SLinus Torvalds ENCODE_SEQID_OP_HEAD; 24681da177e4SLinus Torvalds 2469e2f282b9SBenny Halevy if (!nfserr) 2470e2f282b9SBenny Halevy nfsd4_encode_stateid(resp, &lock->lk_resp_stateid); 2471e2f282b9SBenny Halevy else if (nfserr == nfserr_denied) 24721da177e4SLinus Torvalds nfsd4_encode_lock_denied(resp, &lock->lk_denied); 24731da177e4SLinus Torvalds 24743a65588aSJ. Bruce Fields ENCODE_SEQID_OP_TAIL(lock->lk_replay_owner); 2475695e12f8SBenny Halevy return nfserr; 24761da177e4SLinus Torvalds } 24771da177e4SLinus Torvalds 2478695e12f8SBenny Halevy static __be32 2479b37ad28bSAl Viro nfsd4_encode_lockt(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lockt *lockt) 24801da177e4SLinus Torvalds { 24811da177e4SLinus Torvalds if (nfserr == nfserr_denied) 24821da177e4SLinus Torvalds nfsd4_encode_lock_denied(resp, &lockt->lt_denied); 2483695e12f8SBenny Halevy return nfserr; 24841da177e4SLinus Torvalds } 24851da177e4SLinus Torvalds 2486695e12f8SBenny Halevy static __be32 2487b37ad28bSAl Viro nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_locku *locku) 24881da177e4SLinus Torvalds { 24891da177e4SLinus Torvalds ENCODE_SEQID_OP_HEAD; 24901da177e4SLinus Torvalds 2491e2f282b9SBenny Halevy if (!nfserr) 2492e2f282b9SBenny Halevy nfsd4_encode_stateid(resp, &locku->lu_stateid); 24931da177e4SLinus Torvalds 24941da177e4SLinus Torvalds ENCODE_SEQID_OP_TAIL(locku->lu_stateowner); 2495695e12f8SBenny Halevy return nfserr; 24961da177e4SLinus Torvalds } 24971da177e4SLinus Torvalds 24981da177e4SLinus Torvalds 2499695e12f8SBenny Halevy static __be32 2500b37ad28bSAl Viro nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_link *link) 25011da177e4SLinus Torvalds { 2502bc749ca4SJ. Bruce Fields __be32 *p; 25031da177e4SLinus Torvalds 25041da177e4SLinus Torvalds if (!nfserr) { 25051da177e4SLinus Torvalds RESERVE_SPACE(20); 2506c654b8a9SJ. Bruce Fields write_cinfo(&p, &link->li_cinfo); 25071da177e4SLinus Torvalds ADJUST_ARGS(); 25081da177e4SLinus Torvalds } 2509695e12f8SBenny Halevy return nfserr; 25101da177e4SLinus Torvalds } 25111da177e4SLinus Torvalds 25121da177e4SLinus Torvalds 2513695e12f8SBenny Halevy static __be32 2514b37ad28bSAl Viro nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open *open) 25151da177e4SLinus Torvalds { 2516bc749ca4SJ. Bruce Fields __be32 *p; 25171da177e4SLinus Torvalds ENCODE_SEQID_OP_HEAD; 25181da177e4SLinus Torvalds 25191da177e4SLinus Torvalds if (nfserr) 25201da177e4SLinus Torvalds goto out; 25211da177e4SLinus Torvalds 2522e2f282b9SBenny Halevy nfsd4_encode_stateid(resp, &open->op_stateid); 2523e2f282b9SBenny Halevy RESERVE_SPACE(40); 2524c654b8a9SJ. Bruce Fields write_cinfo(&p, &open->op_cinfo); 25251da177e4SLinus Torvalds WRITE32(open->op_rflags); 25261da177e4SLinus Torvalds WRITE32(2); 25271da177e4SLinus Torvalds WRITE32(open->op_bmval[0]); 25281da177e4SLinus Torvalds WRITE32(open->op_bmval[1]); 25291da177e4SLinus Torvalds WRITE32(open->op_delegate_type); 25301da177e4SLinus Torvalds ADJUST_ARGS(); 25311da177e4SLinus Torvalds 25321da177e4SLinus Torvalds switch (open->op_delegate_type) { 25331da177e4SLinus Torvalds case NFS4_OPEN_DELEGATE_NONE: 25341da177e4SLinus Torvalds break; 25351da177e4SLinus Torvalds case NFS4_OPEN_DELEGATE_READ: 2536e2f282b9SBenny Halevy nfsd4_encode_stateid(resp, &open->op_delegate_stateid); 2537e2f282b9SBenny Halevy RESERVE_SPACE(20); 25387b190fecSNeilBrown WRITE32(open->op_recall); 25391da177e4SLinus Torvalds 25401da177e4SLinus Torvalds /* 25411da177e4SLinus Torvalds * TODO: ACE's in delegations 25421da177e4SLinus Torvalds */ 25431da177e4SLinus Torvalds WRITE32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE); 25441da177e4SLinus Torvalds WRITE32(0); 25451da177e4SLinus Torvalds WRITE32(0); 25461da177e4SLinus Torvalds WRITE32(0); /* XXX: is NULL principal ok? */ 25471da177e4SLinus Torvalds ADJUST_ARGS(); 25481da177e4SLinus Torvalds break; 25491da177e4SLinus Torvalds case NFS4_OPEN_DELEGATE_WRITE: 2550e2f282b9SBenny Halevy nfsd4_encode_stateid(resp, &open->op_delegate_stateid); 2551e2f282b9SBenny Halevy RESERVE_SPACE(32); 25521da177e4SLinus Torvalds WRITE32(0); 25531da177e4SLinus Torvalds 25541da177e4SLinus Torvalds /* 25551da177e4SLinus Torvalds * TODO: space_limit's in delegations 25561da177e4SLinus Torvalds */ 25571da177e4SLinus Torvalds WRITE32(NFS4_LIMIT_SIZE); 25581da177e4SLinus Torvalds WRITE32(~(u32)0); 25591da177e4SLinus Torvalds WRITE32(~(u32)0); 25601da177e4SLinus Torvalds 25611da177e4SLinus Torvalds /* 25621da177e4SLinus Torvalds * TODO: ACE's in delegations 25631da177e4SLinus Torvalds */ 25641da177e4SLinus Torvalds WRITE32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE); 25651da177e4SLinus Torvalds WRITE32(0); 25661da177e4SLinus Torvalds WRITE32(0); 25671da177e4SLinus Torvalds WRITE32(0); /* XXX: is NULL principal ok? */ 25681da177e4SLinus Torvalds ADJUST_ARGS(); 25691da177e4SLinus Torvalds break; 25701da177e4SLinus Torvalds default: 25711da177e4SLinus Torvalds BUG(); 25721da177e4SLinus Torvalds } 25731da177e4SLinus Torvalds /* XXX save filehandle here */ 25741da177e4SLinus Torvalds out: 25751da177e4SLinus Torvalds ENCODE_SEQID_OP_TAIL(open->op_stateowner); 2576695e12f8SBenny Halevy return nfserr; 25771da177e4SLinus Torvalds } 25781da177e4SLinus Torvalds 2579695e12f8SBenny Halevy static __be32 2580b37ad28bSAl Viro nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_confirm *oc) 25811da177e4SLinus Torvalds { 25821da177e4SLinus Torvalds ENCODE_SEQID_OP_HEAD; 25831da177e4SLinus Torvalds 2584e2f282b9SBenny Halevy if (!nfserr) 2585e2f282b9SBenny Halevy nfsd4_encode_stateid(resp, &oc->oc_resp_stateid); 25861da177e4SLinus Torvalds 25871da177e4SLinus Torvalds ENCODE_SEQID_OP_TAIL(oc->oc_stateowner); 2588695e12f8SBenny Halevy return nfserr; 25891da177e4SLinus Torvalds } 25901da177e4SLinus Torvalds 2591695e12f8SBenny Halevy static __be32 2592b37ad28bSAl Viro nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_downgrade *od) 25931da177e4SLinus Torvalds { 25941da177e4SLinus Torvalds ENCODE_SEQID_OP_HEAD; 25951da177e4SLinus Torvalds 2596e2f282b9SBenny Halevy if (!nfserr) 2597e2f282b9SBenny Halevy nfsd4_encode_stateid(resp, &od->od_stateid); 25981da177e4SLinus Torvalds 25991da177e4SLinus Torvalds ENCODE_SEQID_OP_TAIL(od->od_stateowner); 2600695e12f8SBenny Halevy return nfserr; 26011da177e4SLinus Torvalds } 26021da177e4SLinus Torvalds 2603b37ad28bSAl Viro static __be32 2604b37ad28bSAl Viro nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr, 260544524359SNeilBrown struct nfsd4_read *read) 26061da177e4SLinus Torvalds { 26071da177e4SLinus Torvalds u32 eof; 26081da177e4SLinus Torvalds int v, pn; 26091da177e4SLinus Torvalds unsigned long maxcount; 26101da177e4SLinus Torvalds long len; 2611bc749ca4SJ. Bruce Fields __be32 *p; 26121da177e4SLinus Torvalds 26131da177e4SLinus Torvalds if (nfserr) 26141da177e4SLinus Torvalds return nfserr; 26151da177e4SLinus Torvalds if (resp->xbuf->page_len) 26161da177e4SLinus Torvalds return nfserr_resource; 26171da177e4SLinus Torvalds 26181da177e4SLinus Torvalds RESERVE_SPACE(8); /* eof flag and byte count */ 26191da177e4SLinus Torvalds 26207adae489SGreg Banks maxcount = svc_max_payload(resp->rqstp); 26211da177e4SLinus Torvalds if (maxcount > read->rd_length) 26221da177e4SLinus Torvalds maxcount = read->rd_length; 26231da177e4SLinus Torvalds 26241da177e4SLinus Torvalds len = maxcount; 26251da177e4SLinus Torvalds v = 0; 26261da177e4SLinus Torvalds while (len > 0) { 262744524359SNeilBrown pn = resp->rqstp->rq_resused++; 26283cc03b16SNeilBrown resp->rqstp->rq_vec[v].iov_base = 262944524359SNeilBrown page_address(resp->rqstp->rq_respages[pn]); 26303cc03b16SNeilBrown resp->rqstp->rq_vec[v].iov_len = 263144524359SNeilBrown len < PAGE_SIZE ? len : PAGE_SIZE; 26321da177e4SLinus Torvalds v++; 26331da177e4SLinus Torvalds len -= PAGE_SIZE; 26341da177e4SLinus Torvalds } 26351da177e4SLinus Torvalds read->rd_vlen = v; 26361da177e4SLinus Torvalds 26371da177e4SLinus Torvalds nfserr = nfsd_read(read->rd_rqstp, read->rd_fhp, read->rd_filp, 26383cc03b16SNeilBrown read->rd_offset, resp->rqstp->rq_vec, read->rd_vlen, 26391da177e4SLinus Torvalds &maxcount); 26401da177e4SLinus Torvalds 26411da177e4SLinus Torvalds if (nfserr == nfserr_symlink) 26421da177e4SLinus Torvalds nfserr = nfserr_inval; 26431da177e4SLinus Torvalds if (nfserr) 26441da177e4SLinus Torvalds return nfserr; 264544524359SNeilBrown eof = (read->rd_offset + maxcount >= 264644524359SNeilBrown read->rd_fhp->fh_dentry->d_inode->i_size); 26471da177e4SLinus Torvalds 26481da177e4SLinus Torvalds WRITE32(eof); 26491da177e4SLinus Torvalds WRITE32(maxcount); 26501da177e4SLinus Torvalds ADJUST_ARGS(); 26516ed6deccSNeilBrown resp->xbuf->head[0].iov_len = (char*)p 26526ed6deccSNeilBrown - (char*)resp->xbuf->head[0].iov_base; 26531da177e4SLinus Torvalds resp->xbuf->page_len = maxcount; 26541da177e4SLinus Torvalds 26556ed6deccSNeilBrown /* Use rest of head for padding and remaining ops: */ 26566ed6deccSNeilBrown resp->xbuf->tail[0].iov_base = p; 26571da177e4SLinus Torvalds resp->xbuf->tail[0].iov_len = 0; 26581da177e4SLinus Torvalds if (maxcount&3) { 26596ed6deccSNeilBrown RESERVE_SPACE(4); 26606ed6deccSNeilBrown WRITE32(0); 26611da177e4SLinus Torvalds resp->xbuf->tail[0].iov_base += maxcount&3; 26621da177e4SLinus Torvalds resp->xbuf->tail[0].iov_len = 4 - (maxcount&3); 26636ed6deccSNeilBrown ADJUST_ARGS(); 26641da177e4SLinus Torvalds } 26651da177e4SLinus Torvalds return 0; 26661da177e4SLinus Torvalds } 26671da177e4SLinus Torvalds 2668b37ad28bSAl Viro static __be32 2669b37ad28bSAl Viro nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readlink *readlink) 26701da177e4SLinus Torvalds { 26711da177e4SLinus Torvalds int maxcount; 26721da177e4SLinus Torvalds char *page; 2673bc749ca4SJ. Bruce Fields __be32 *p; 26741da177e4SLinus Torvalds 26751da177e4SLinus Torvalds if (nfserr) 26761da177e4SLinus Torvalds return nfserr; 26771da177e4SLinus Torvalds if (resp->xbuf->page_len) 26781da177e4SLinus Torvalds return nfserr_resource; 26791da177e4SLinus Torvalds 268044524359SNeilBrown page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused++]); 26811da177e4SLinus Torvalds 26821da177e4SLinus Torvalds maxcount = PAGE_SIZE; 26831da177e4SLinus Torvalds RESERVE_SPACE(4); 26841da177e4SLinus Torvalds 26851da177e4SLinus Torvalds /* 26861da177e4SLinus Torvalds * XXX: By default, the ->readlink() VFS op will truncate symlinks 26871da177e4SLinus Torvalds * if they would overflow the buffer. Is this kosher in NFSv4? If 26881da177e4SLinus Torvalds * not, one easy fix is: if ->readlink() precisely fills the buffer, 26891da177e4SLinus Torvalds * assume that truncation occurred, and return NFS4ERR_RESOURCE. 26901da177e4SLinus Torvalds */ 26911da177e4SLinus Torvalds nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, page, &maxcount); 26921da177e4SLinus Torvalds if (nfserr == nfserr_isdir) 26931da177e4SLinus Torvalds return nfserr_inval; 26941da177e4SLinus Torvalds if (nfserr) 26951da177e4SLinus Torvalds return nfserr; 26961da177e4SLinus Torvalds 26971da177e4SLinus Torvalds WRITE32(maxcount); 26981da177e4SLinus Torvalds ADJUST_ARGS(); 26996ed6deccSNeilBrown resp->xbuf->head[0].iov_len = (char*)p 27006ed6deccSNeilBrown - (char*)resp->xbuf->head[0].iov_base; 27011da177e4SLinus Torvalds resp->xbuf->page_len = maxcount; 27026ed6deccSNeilBrown 27036ed6deccSNeilBrown /* Use rest of head for padding and remaining ops: */ 27046ed6deccSNeilBrown resp->xbuf->tail[0].iov_base = p; 27056ed6deccSNeilBrown resp->xbuf->tail[0].iov_len = 0; 27061da177e4SLinus Torvalds if (maxcount&3) { 27076ed6deccSNeilBrown RESERVE_SPACE(4); 27086ed6deccSNeilBrown WRITE32(0); 27091da177e4SLinus Torvalds resp->xbuf->tail[0].iov_base += maxcount&3; 27101da177e4SLinus Torvalds resp->xbuf->tail[0].iov_len = 4 - (maxcount&3); 27116ed6deccSNeilBrown ADJUST_ARGS(); 27121da177e4SLinus Torvalds } 27131da177e4SLinus Torvalds return 0; 27141da177e4SLinus Torvalds } 27151da177e4SLinus Torvalds 2716b37ad28bSAl Viro static __be32 2717b37ad28bSAl Viro nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readdir *readdir) 27181da177e4SLinus Torvalds { 27191da177e4SLinus Torvalds int maxcount; 27201da177e4SLinus Torvalds loff_t offset; 27212ebbc012SAl Viro __be32 *page, *savep, *tailbase; 2722bc749ca4SJ. Bruce Fields __be32 *p; 27231da177e4SLinus Torvalds 27241da177e4SLinus Torvalds if (nfserr) 27251da177e4SLinus Torvalds return nfserr; 27261da177e4SLinus Torvalds if (resp->xbuf->page_len) 27271da177e4SLinus Torvalds return nfserr_resource; 27281da177e4SLinus Torvalds 27291da177e4SLinus Torvalds RESERVE_SPACE(8); /* verifier */ 27301da177e4SLinus Torvalds savep = p; 27311da177e4SLinus Torvalds 27321da177e4SLinus Torvalds /* XXX: Following NFSv3, we ignore the READDIR verifier for now. */ 27331da177e4SLinus Torvalds WRITE32(0); 27341da177e4SLinus Torvalds WRITE32(0); 27351da177e4SLinus Torvalds ADJUST_ARGS(); 27361da177e4SLinus Torvalds resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base; 2737bb6e8a9fSNeilBrown tailbase = p; 27381da177e4SLinus Torvalds 27391da177e4SLinus Torvalds maxcount = PAGE_SIZE; 27401da177e4SLinus Torvalds if (maxcount > readdir->rd_maxcount) 27411da177e4SLinus Torvalds maxcount = readdir->rd_maxcount; 27421da177e4SLinus Torvalds 27431da177e4SLinus Torvalds /* 27441da177e4SLinus Torvalds * Convert from bytes to words, account for the two words already 27451da177e4SLinus Torvalds * written, make sure to leave two words at the end for the next 27461da177e4SLinus Torvalds * pointer and eof field. 27471da177e4SLinus Torvalds */ 27481da177e4SLinus Torvalds maxcount = (maxcount >> 2) - 4; 27491da177e4SLinus Torvalds if (maxcount < 0) { 27501da177e4SLinus Torvalds nfserr = nfserr_toosmall; 27511da177e4SLinus Torvalds goto err_no_verf; 27521da177e4SLinus Torvalds } 27531da177e4SLinus Torvalds 275444524359SNeilBrown page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused++]); 27551da177e4SLinus Torvalds readdir->common.err = 0; 27561da177e4SLinus Torvalds readdir->buflen = maxcount; 27571da177e4SLinus Torvalds readdir->buffer = page; 27581da177e4SLinus Torvalds readdir->offset = NULL; 27591da177e4SLinus Torvalds 27601da177e4SLinus Torvalds offset = readdir->rd_cookie; 27611da177e4SLinus Torvalds nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp, 27621da177e4SLinus Torvalds &offset, 27631da177e4SLinus Torvalds &readdir->common, nfsd4_encode_dirent); 27641da177e4SLinus Torvalds if (nfserr == nfs_ok && 27651da177e4SLinus Torvalds readdir->common.err == nfserr_toosmall && 27661da177e4SLinus Torvalds readdir->buffer == page) 27671da177e4SLinus Torvalds nfserr = nfserr_toosmall; 27681da177e4SLinus Torvalds if (nfserr == nfserr_symlink) 27691da177e4SLinus Torvalds nfserr = nfserr_notdir; 27701da177e4SLinus Torvalds if (nfserr) 27711da177e4SLinus Torvalds goto err_no_verf; 27721da177e4SLinus Torvalds 27731da177e4SLinus Torvalds if (readdir->offset) 27741da177e4SLinus Torvalds xdr_encode_hyper(readdir->offset, offset); 27751da177e4SLinus Torvalds 27761da177e4SLinus Torvalds p = readdir->buffer; 27771da177e4SLinus Torvalds *p++ = 0; /* no more entries */ 27781da177e4SLinus Torvalds *p++ = htonl(readdir->common.err == nfserr_eof); 277944524359SNeilBrown resp->xbuf->page_len = ((char*)p) - (char*)page_address( 278044524359SNeilBrown resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); 27811da177e4SLinus Torvalds 2782bb6e8a9fSNeilBrown /* Use rest of head for padding and remaining ops: */ 2783bb6e8a9fSNeilBrown resp->xbuf->tail[0].iov_base = tailbase; 27841da177e4SLinus Torvalds resp->xbuf->tail[0].iov_len = 0; 27851da177e4SLinus Torvalds resp->p = resp->xbuf->tail[0].iov_base; 2786bb6e8a9fSNeilBrown resp->end = resp->p + (PAGE_SIZE - resp->xbuf->head[0].iov_len)/4; 27871da177e4SLinus Torvalds 27881da177e4SLinus Torvalds return 0; 27891da177e4SLinus Torvalds err_no_verf: 27901da177e4SLinus Torvalds p = savep; 27911da177e4SLinus Torvalds ADJUST_ARGS(); 27921da177e4SLinus Torvalds return nfserr; 27931da177e4SLinus Torvalds } 27941da177e4SLinus Torvalds 2795695e12f8SBenny Halevy static __be32 2796b37ad28bSAl Viro nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_remove *remove) 27971da177e4SLinus Torvalds { 2798bc749ca4SJ. Bruce Fields __be32 *p; 27991da177e4SLinus Torvalds 28001da177e4SLinus Torvalds if (!nfserr) { 28011da177e4SLinus Torvalds RESERVE_SPACE(20); 2802c654b8a9SJ. Bruce Fields write_cinfo(&p, &remove->rm_cinfo); 28031da177e4SLinus Torvalds ADJUST_ARGS(); 28041da177e4SLinus Torvalds } 2805695e12f8SBenny Halevy return nfserr; 28061da177e4SLinus Torvalds } 28071da177e4SLinus Torvalds 2808695e12f8SBenny Halevy static __be32 2809b37ad28bSAl Viro nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_rename *rename) 28101da177e4SLinus Torvalds { 2811bc749ca4SJ. Bruce Fields __be32 *p; 28121da177e4SLinus Torvalds 28131da177e4SLinus Torvalds if (!nfserr) { 28141da177e4SLinus Torvalds RESERVE_SPACE(40); 2815c654b8a9SJ. Bruce Fields write_cinfo(&p, &rename->rn_sinfo); 2816c654b8a9SJ. Bruce Fields write_cinfo(&p, &rename->rn_tinfo); 28171da177e4SLinus Torvalds ADJUST_ARGS(); 28181da177e4SLinus Torvalds } 2819695e12f8SBenny Halevy return nfserr; 28201da177e4SLinus Torvalds } 28211da177e4SLinus Torvalds 2822695e12f8SBenny Halevy static __be32 2823ca5c8cdeSAl Viro nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr, 2824dcb488a3SAndy Adamson struct nfsd4_secinfo *secinfo) 2825dcb488a3SAndy Adamson { 2826dcb488a3SAndy Adamson int i = 0; 2827dcb488a3SAndy Adamson struct svc_export *exp = secinfo->si_exp; 28284796f457SJ. Bruce Fields u32 nflavs; 28294796f457SJ. Bruce Fields struct exp_flavor_info *flavs; 28304796f457SJ. Bruce Fields struct exp_flavor_info def_flavs[2]; 2831bc749ca4SJ. Bruce Fields __be32 *p; 2832dcb488a3SAndy Adamson 2833dcb488a3SAndy Adamson if (nfserr) 2834dcb488a3SAndy Adamson goto out; 28354796f457SJ. Bruce Fields if (exp->ex_nflavors) { 28364796f457SJ. Bruce Fields flavs = exp->ex_flavors; 28374796f457SJ. Bruce Fields nflavs = exp->ex_nflavors; 28384796f457SJ. Bruce Fields } else { /* Handling of some defaults in absence of real secinfo: */ 28394796f457SJ. Bruce Fields flavs = def_flavs; 28404796f457SJ. Bruce Fields if (exp->ex_client->flavour->flavour == RPC_AUTH_UNIX) { 28414796f457SJ. Bruce Fields nflavs = 2; 28424796f457SJ. Bruce Fields flavs[0].pseudoflavor = RPC_AUTH_UNIX; 28434796f457SJ. Bruce Fields flavs[1].pseudoflavor = RPC_AUTH_NULL; 28444796f457SJ. Bruce Fields } else if (exp->ex_client->flavour->flavour == RPC_AUTH_GSS) { 28454796f457SJ. Bruce Fields nflavs = 1; 28464796f457SJ. Bruce Fields flavs[0].pseudoflavor 28474796f457SJ. Bruce Fields = svcauth_gss_flavor(exp->ex_client); 28484796f457SJ. Bruce Fields } else { 28494796f457SJ. Bruce Fields nflavs = 1; 28504796f457SJ. Bruce Fields flavs[0].pseudoflavor 28514796f457SJ. Bruce Fields = exp->ex_client->flavour->flavour; 28524796f457SJ. Bruce Fields } 28534796f457SJ. Bruce Fields } 28544796f457SJ. Bruce Fields 2855dcb488a3SAndy Adamson RESERVE_SPACE(4); 28564796f457SJ. Bruce Fields WRITE32(nflavs); 2857dcb488a3SAndy Adamson ADJUST_ARGS(); 28584796f457SJ. Bruce Fields for (i = 0; i < nflavs; i++) { 28594796f457SJ. Bruce Fields u32 flav = flavs[i].pseudoflavor; 2860dcb488a3SAndy Adamson struct gss_api_mech *gm = gss_mech_get_by_pseudoflavor(flav); 2861dcb488a3SAndy Adamson 2862dcb488a3SAndy Adamson if (gm) { 2863dcb488a3SAndy Adamson RESERVE_SPACE(4); 2864dcb488a3SAndy Adamson WRITE32(RPC_AUTH_GSS); 2865dcb488a3SAndy Adamson ADJUST_ARGS(); 2866dcb488a3SAndy Adamson RESERVE_SPACE(4 + gm->gm_oid.len); 2867dcb488a3SAndy Adamson WRITE32(gm->gm_oid.len); 2868dcb488a3SAndy Adamson WRITEMEM(gm->gm_oid.data, gm->gm_oid.len); 2869dcb488a3SAndy Adamson ADJUST_ARGS(); 2870dcb488a3SAndy Adamson RESERVE_SPACE(4); 2871dcb488a3SAndy Adamson WRITE32(0); /* qop */ 2872dcb488a3SAndy Adamson ADJUST_ARGS(); 2873dcb488a3SAndy Adamson RESERVE_SPACE(4); 2874dcb488a3SAndy Adamson WRITE32(gss_pseudoflavor_to_service(gm, flav)); 2875dcb488a3SAndy Adamson ADJUST_ARGS(); 2876dcb488a3SAndy Adamson gss_mech_put(gm); 2877dcb488a3SAndy Adamson } else { 2878dcb488a3SAndy Adamson RESERVE_SPACE(4); 2879dcb488a3SAndy Adamson WRITE32(flav); 2880dcb488a3SAndy Adamson ADJUST_ARGS(); 2881dcb488a3SAndy Adamson } 2882dcb488a3SAndy Adamson } 2883dcb488a3SAndy Adamson out: 2884dcb488a3SAndy Adamson if (exp) 2885dcb488a3SAndy Adamson exp_put(exp); 2886695e12f8SBenny Halevy return nfserr; 2887dcb488a3SAndy Adamson } 2888dcb488a3SAndy Adamson 28891da177e4SLinus Torvalds /* 28901da177e4SLinus Torvalds * The SETATTR encode routine is special -- it always encodes a bitmap, 28911da177e4SLinus Torvalds * regardless of the error status. 28921da177e4SLinus Torvalds */ 2893695e12f8SBenny Halevy static __be32 2894b37ad28bSAl Viro nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setattr *setattr) 28951da177e4SLinus Torvalds { 2896bc749ca4SJ. Bruce Fields __be32 *p; 28971da177e4SLinus Torvalds 28981da177e4SLinus Torvalds RESERVE_SPACE(12); 28991da177e4SLinus Torvalds if (nfserr) { 29001da177e4SLinus Torvalds WRITE32(2); 29011da177e4SLinus Torvalds WRITE32(0); 29021da177e4SLinus Torvalds WRITE32(0); 29031da177e4SLinus Torvalds } 29041da177e4SLinus Torvalds else { 29051da177e4SLinus Torvalds WRITE32(2); 29061da177e4SLinus Torvalds WRITE32(setattr->sa_bmval[0]); 29071da177e4SLinus Torvalds WRITE32(setattr->sa_bmval[1]); 29081da177e4SLinus Torvalds } 29091da177e4SLinus Torvalds ADJUST_ARGS(); 2910695e12f8SBenny Halevy return nfserr; 29111da177e4SLinus Torvalds } 29121da177e4SLinus Torvalds 2913695e12f8SBenny Halevy static __be32 2914b37ad28bSAl Viro nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setclientid *scd) 29151da177e4SLinus Torvalds { 2916bc749ca4SJ. Bruce Fields __be32 *p; 29171da177e4SLinus Torvalds 29181da177e4SLinus Torvalds if (!nfserr) { 29191da177e4SLinus Torvalds RESERVE_SPACE(8 + sizeof(nfs4_verifier)); 29201da177e4SLinus Torvalds WRITEMEM(&scd->se_clientid, 8); 29211da177e4SLinus Torvalds WRITEMEM(&scd->se_confirm, sizeof(nfs4_verifier)); 29221da177e4SLinus Torvalds ADJUST_ARGS(); 29231da177e4SLinus Torvalds } 29241da177e4SLinus Torvalds else if (nfserr == nfserr_clid_inuse) { 29251da177e4SLinus Torvalds RESERVE_SPACE(8); 29261da177e4SLinus Torvalds WRITE32(0); 29271da177e4SLinus Torvalds WRITE32(0); 29281da177e4SLinus Torvalds ADJUST_ARGS(); 29291da177e4SLinus Torvalds } 2930695e12f8SBenny Halevy return nfserr; 29311da177e4SLinus Torvalds } 29321da177e4SLinus Torvalds 2933695e12f8SBenny Halevy static __be32 2934b37ad28bSAl Viro nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_write *write) 29351da177e4SLinus Torvalds { 2936bc749ca4SJ. Bruce Fields __be32 *p; 29371da177e4SLinus Torvalds 29381da177e4SLinus Torvalds if (!nfserr) { 29391da177e4SLinus Torvalds RESERVE_SPACE(16); 29401da177e4SLinus Torvalds WRITE32(write->wr_bytes_written); 29411da177e4SLinus Torvalds WRITE32(write->wr_how_written); 29421da177e4SLinus Torvalds WRITEMEM(write->wr_verifier.data, 8); 29431da177e4SLinus Torvalds ADJUST_ARGS(); 29441da177e4SLinus Torvalds } 2945695e12f8SBenny Halevy return nfserr; 29461da177e4SLinus Torvalds } 29471da177e4SLinus Torvalds 2948695e12f8SBenny Halevy static __be32 29492db134ebSAndy Adamson nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, int nfserr, 29502db134ebSAndy Adamson struct nfsd4_exchange_id *exid) 29512db134ebSAndy Adamson { 2952bc749ca4SJ. Bruce Fields __be32 *p; 29530733d213SAndy Adamson char *major_id; 29540733d213SAndy Adamson char *server_scope; 29550733d213SAndy Adamson int major_id_sz; 29560733d213SAndy Adamson int server_scope_sz; 29570733d213SAndy Adamson uint64_t minor_id = 0; 29580733d213SAndy Adamson 29590733d213SAndy Adamson if (nfserr) 29602db134ebSAndy Adamson return nfserr; 29610733d213SAndy Adamson 29620733d213SAndy Adamson major_id = utsname()->nodename; 29630733d213SAndy Adamson major_id_sz = strlen(major_id); 29640733d213SAndy Adamson server_scope = utsname()->nodename; 29650733d213SAndy Adamson server_scope_sz = strlen(server_scope); 29660733d213SAndy Adamson 29670733d213SAndy Adamson RESERVE_SPACE( 29680733d213SAndy Adamson 8 /* eir_clientid */ + 29690733d213SAndy Adamson 4 /* eir_sequenceid */ + 29700733d213SAndy Adamson 4 /* eir_flags */ + 29710733d213SAndy Adamson 4 /* spr_how (SP4_NONE) */ + 29720733d213SAndy Adamson 8 /* so_minor_id */ + 29730733d213SAndy Adamson 4 /* so_major_id.len */ + 29740733d213SAndy Adamson (XDR_QUADLEN(major_id_sz) * 4) + 29750733d213SAndy Adamson 4 /* eir_server_scope.len */ + 29760733d213SAndy Adamson (XDR_QUADLEN(server_scope_sz) * 4) + 29770733d213SAndy Adamson 4 /* eir_server_impl_id.count (0) */); 29780733d213SAndy Adamson 29790733d213SAndy Adamson WRITEMEM(&exid->clientid, 8); 29800733d213SAndy Adamson WRITE32(exid->seqid); 29810733d213SAndy Adamson WRITE32(exid->flags); 29820733d213SAndy Adamson 29830733d213SAndy Adamson /* state_protect4_r. Currently only support SP4_NONE */ 29840733d213SAndy Adamson BUG_ON(exid->spa_how != SP4_NONE); 29850733d213SAndy Adamson WRITE32(exid->spa_how); 29860733d213SAndy Adamson 29870733d213SAndy Adamson /* The server_owner struct */ 29880733d213SAndy Adamson WRITE64(minor_id); /* Minor id */ 29890733d213SAndy Adamson /* major id */ 29900733d213SAndy Adamson WRITE32(major_id_sz); 29910733d213SAndy Adamson WRITEMEM(major_id, major_id_sz); 29920733d213SAndy Adamson 29930733d213SAndy Adamson /* Server scope */ 29940733d213SAndy Adamson WRITE32(server_scope_sz); 29950733d213SAndy Adamson WRITEMEM(server_scope, server_scope_sz); 29960733d213SAndy Adamson 29970733d213SAndy Adamson /* Implementation id */ 29980733d213SAndy Adamson WRITE32(0); /* zero length nfs_impl_id4 array */ 29990733d213SAndy Adamson ADJUST_ARGS(); 30000733d213SAndy Adamson return 0; 30012db134ebSAndy Adamson } 30022db134ebSAndy Adamson 30032db134ebSAndy Adamson static __be32 30042db134ebSAndy Adamson nfsd4_encode_create_session(struct nfsd4_compoundres *resp, int nfserr, 30052db134ebSAndy Adamson struct nfsd4_create_session *sess) 30062db134ebSAndy Adamson { 3007bc749ca4SJ. Bruce Fields __be32 *p; 3008ec6b5d7bSAndy Adamson 3009ec6b5d7bSAndy Adamson if (nfserr) 30102db134ebSAndy Adamson return nfserr; 3011ec6b5d7bSAndy Adamson 3012ec6b5d7bSAndy Adamson RESERVE_SPACE(24); 3013ec6b5d7bSAndy Adamson WRITEMEM(sess->sessionid.data, NFS4_MAX_SESSIONID_LEN); 3014ec6b5d7bSAndy Adamson WRITE32(sess->seqid); 3015ec6b5d7bSAndy Adamson WRITE32(sess->flags); 3016ec6b5d7bSAndy Adamson ADJUST_ARGS(); 3017ec6b5d7bSAndy Adamson 3018ec6b5d7bSAndy Adamson RESERVE_SPACE(28); 3019ec6b5d7bSAndy Adamson WRITE32(0); /* headerpadsz */ 3020ec6b5d7bSAndy Adamson WRITE32(sess->fore_channel.maxreq_sz); 3021ec6b5d7bSAndy Adamson WRITE32(sess->fore_channel.maxresp_sz); 3022ec6b5d7bSAndy Adamson WRITE32(sess->fore_channel.maxresp_cached); 3023ec6b5d7bSAndy Adamson WRITE32(sess->fore_channel.maxops); 3024ec6b5d7bSAndy Adamson WRITE32(sess->fore_channel.maxreqs); 3025ec6b5d7bSAndy Adamson WRITE32(sess->fore_channel.nr_rdma_attrs); 3026ec6b5d7bSAndy Adamson ADJUST_ARGS(); 3027ec6b5d7bSAndy Adamson 3028ec6b5d7bSAndy Adamson if (sess->fore_channel.nr_rdma_attrs) { 3029ec6b5d7bSAndy Adamson RESERVE_SPACE(4); 3030ec6b5d7bSAndy Adamson WRITE32(sess->fore_channel.rdma_attrs); 3031ec6b5d7bSAndy Adamson ADJUST_ARGS(); 3032ec6b5d7bSAndy Adamson } 3033ec6b5d7bSAndy Adamson 3034ec6b5d7bSAndy Adamson RESERVE_SPACE(28); 3035ec6b5d7bSAndy Adamson WRITE32(0); /* headerpadsz */ 3036ec6b5d7bSAndy Adamson WRITE32(sess->back_channel.maxreq_sz); 3037ec6b5d7bSAndy Adamson WRITE32(sess->back_channel.maxresp_sz); 3038ec6b5d7bSAndy Adamson WRITE32(sess->back_channel.maxresp_cached); 3039ec6b5d7bSAndy Adamson WRITE32(sess->back_channel.maxops); 3040ec6b5d7bSAndy Adamson WRITE32(sess->back_channel.maxreqs); 3041ec6b5d7bSAndy Adamson WRITE32(sess->back_channel.nr_rdma_attrs); 3042ec6b5d7bSAndy Adamson ADJUST_ARGS(); 3043ec6b5d7bSAndy Adamson 3044ec6b5d7bSAndy Adamson if (sess->back_channel.nr_rdma_attrs) { 3045ec6b5d7bSAndy Adamson RESERVE_SPACE(4); 3046ec6b5d7bSAndy Adamson WRITE32(sess->back_channel.rdma_attrs); 3047ec6b5d7bSAndy Adamson ADJUST_ARGS(); 3048ec6b5d7bSAndy Adamson } 3049ec6b5d7bSAndy Adamson return 0; 30502db134ebSAndy Adamson } 30512db134ebSAndy Adamson 30522db134ebSAndy Adamson static __be32 30532db134ebSAndy Adamson nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, int nfserr, 30542db134ebSAndy Adamson struct nfsd4_destroy_session *destroy_session) 30552db134ebSAndy Adamson { 30562db134ebSAndy Adamson return nfserr; 30572db134ebSAndy Adamson } 30582db134ebSAndy Adamson 3059bf864a31SAndy Adamson __be32 30602db134ebSAndy Adamson nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr, 30612db134ebSAndy Adamson struct nfsd4_sequence *seq) 30622db134ebSAndy Adamson { 3063bc749ca4SJ. Bruce Fields __be32 *p; 3064b85d4c01SBenny Halevy 3065b85d4c01SBenny Halevy if (nfserr) 30662db134ebSAndy Adamson return nfserr; 3067b85d4c01SBenny Halevy 3068b85d4c01SBenny Halevy RESERVE_SPACE(NFS4_MAX_SESSIONID_LEN + 20); 3069b85d4c01SBenny Halevy WRITEMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN); 3070b85d4c01SBenny Halevy WRITE32(seq->seqid); 3071b85d4c01SBenny Halevy WRITE32(seq->slotid); 3072b85d4c01SBenny Halevy WRITE32(seq->maxslots); 3073b85d4c01SBenny Halevy /* 3074b85d4c01SBenny Halevy * FIXME: for now: 3075b85d4c01SBenny Halevy * target_maxslots = maxslots 3076b85d4c01SBenny Halevy * status_flags = 0 3077b85d4c01SBenny Halevy */ 3078b85d4c01SBenny Halevy WRITE32(seq->maxslots); 3079b85d4c01SBenny Halevy WRITE32(0); 3080b85d4c01SBenny Halevy 3081b85d4c01SBenny Halevy ADJUST_ARGS(); 3082557ce264SAndy Adamson resp->cstate.datap = p; /* DRC cache data pointer */ 3083b85d4c01SBenny Halevy return 0; 30842db134ebSAndy Adamson } 30852db134ebSAndy Adamson 30862db134ebSAndy Adamson static __be32 3087695e12f8SBenny Halevy nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p) 3088695e12f8SBenny Halevy { 3089695e12f8SBenny Halevy return nfserr; 3090695e12f8SBenny Halevy } 3091695e12f8SBenny Halevy 3092695e12f8SBenny Halevy typedef __be32(* nfsd4_enc)(struct nfsd4_compoundres *, __be32, void *); 3093695e12f8SBenny Halevy 30942db134ebSAndy Adamson /* 30952db134ebSAndy Adamson * Note: nfsd4_enc_ops vector is shared for v4.0 and v4.1 30962db134ebSAndy Adamson * since we don't need to filter out obsolete ops as this is 30972db134ebSAndy Adamson * done in the decoding phase. 30982db134ebSAndy Adamson */ 3099695e12f8SBenny Halevy static nfsd4_enc nfsd4_enc_ops[] = { 3100ad1060c8SJ. Bruce Fields [OP_ACCESS] = (nfsd4_enc)nfsd4_encode_access, 3101ad1060c8SJ. Bruce Fields [OP_CLOSE] = (nfsd4_enc)nfsd4_encode_close, 3102ad1060c8SJ. Bruce Fields [OP_COMMIT] = (nfsd4_enc)nfsd4_encode_commit, 3103ad1060c8SJ. Bruce Fields [OP_CREATE] = (nfsd4_enc)nfsd4_encode_create, 3104ad1060c8SJ. Bruce Fields [OP_DELEGPURGE] = (nfsd4_enc)nfsd4_encode_noop, 3105ad1060c8SJ. Bruce Fields [OP_DELEGRETURN] = (nfsd4_enc)nfsd4_encode_noop, 3106ad1060c8SJ. Bruce Fields [OP_GETATTR] = (nfsd4_enc)nfsd4_encode_getattr, 3107ad1060c8SJ. Bruce Fields [OP_GETFH] = (nfsd4_enc)nfsd4_encode_getfh, 3108ad1060c8SJ. Bruce Fields [OP_LINK] = (nfsd4_enc)nfsd4_encode_link, 3109ad1060c8SJ. Bruce Fields [OP_LOCK] = (nfsd4_enc)nfsd4_encode_lock, 3110ad1060c8SJ. Bruce Fields [OP_LOCKT] = (nfsd4_enc)nfsd4_encode_lockt, 3111ad1060c8SJ. Bruce Fields [OP_LOCKU] = (nfsd4_enc)nfsd4_encode_locku, 3112ad1060c8SJ. Bruce Fields [OP_LOOKUP] = (nfsd4_enc)nfsd4_encode_noop, 3113ad1060c8SJ. Bruce Fields [OP_LOOKUPP] = (nfsd4_enc)nfsd4_encode_noop, 3114ad1060c8SJ. Bruce Fields [OP_NVERIFY] = (nfsd4_enc)nfsd4_encode_noop, 3115ad1060c8SJ. Bruce Fields [OP_OPEN] = (nfsd4_enc)nfsd4_encode_open, 311684f09f46SBenny Halevy [OP_OPENATTR] = (nfsd4_enc)nfsd4_encode_noop, 3117ad1060c8SJ. Bruce Fields [OP_OPEN_CONFIRM] = (nfsd4_enc)nfsd4_encode_open_confirm, 3118ad1060c8SJ. Bruce Fields [OP_OPEN_DOWNGRADE] = (nfsd4_enc)nfsd4_encode_open_downgrade, 3119ad1060c8SJ. Bruce Fields [OP_PUTFH] = (nfsd4_enc)nfsd4_encode_noop, 3120ad1060c8SJ. Bruce Fields [OP_PUTPUBFH] = (nfsd4_enc)nfsd4_encode_noop, 3121ad1060c8SJ. Bruce Fields [OP_PUTROOTFH] = (nfsd4_enc)nfsd4_encode_noop, 3122ad1060c8SJ. Bruce Fields [OP_READ] = (nfsd4_enc)nfsd4_encode_read, 3123ad1060c8SJ. Bruce Fields [OP_READDIR] = (nfsd4_enc)nfsd4_encode_readdir, 3124ad1060c8SJ. Bruce Fields [OP_READLINK] = (nfsd4_enc)nfsd4_encode_readlink, 3125ad1060c8SJ. Bruce Fields [OP_REMOVE] = (nfsd4_enc)nfsd4_encode_remove, 3126ad1060c8SJ. Bruce Fields [OP_RENAME] = (nfsd4_enc)nfsd4_encode_rename, 3127ad1060c8SJ. Bruce Fields [OP_RENEW] = (nfsd4_enc)nfsd4_encode_noop, 3128ad1060c8SJ. Bruce Fields [OP_RESTOREFH] = (nfsd4_enc)nfsd4_encode_noop, 3129ad1060c8SJ. Bruce Fields [OP_SAVEFH] = (nfsd4_enc)nfsd4_encode_noop, 3130ad1060c8SJ. Bruce Fields [OP_SECINFO] = (nfsd4_enc)nfsd4_encode_secinfo, 3131ad1060c8SJ. Bruce Fields [OP_SETATTR] = (nfsd4_enc)nfsd4_encode_setattr, 3132ad1060c8SJ. Bruce Fields [OP_SETCLIENTID] = (nfsd4_enc)nfsd4_encode_setclientid, 3133ad1060c8SJ. Bruce Fields [OP_SETCLIENTID_CONFIRM] = (nfsd4_enc)nfsd4_encode_noop, 3134ad1060c8SJ. Bruce Fields [OP_VERIFY] = (nfsd4_enc)nfsd4_encode_noop, 3135ad1060c8SJ. Bruce Fields [OP_WRITE] = (nfsd4_enc)nfsd4_encode_write, 3136ad1060c8SJ. Bruce Fields [OP_RELEASE_LOCKOWNER] = (nfsd4_enc)nfsd4_encode_noop, 31372db134ebSAndy Adamson 31382db134ebSAndy Adamson /* NFSv4.1 operations */ 31392db134ebSAndy Adamson [OP_BACKCHANNEL_CTL] = (nfsd4_enc)nfsd4_encode_noop, 31402db134ebSAndy Adamson [OP_BIND_CONN_TO_SESSION] = (nfsd4_enc)nfsd4_encode_noop, 31412db134ebSAndy Adamson [OP_EXCHANGE_ID] = (nfsd4_enc)nfsd4_encode_exchange_id, 31422db134ebSAndy Adamson [OP_CREATE_SESSION] = (nfsd4_enc)nfsd4_encode_create_session, 31432db134ebSAndy Adamson [OP_DESTROY_SESSION] = (nfsd4_enc)nfsd4_encode_destroy_session, 31442db134ebSAndy Adamson [OP_FREE_STATEID] = (nfsd4_enc)nfsd4_encode_noop, 31452db134ebSAndy Adamson [OP_GET_DIR_DELEGATION] = (nfsd4_enc)nfsd4_encode_noop, 31462db134ebSAndy Adamson [OP_GETDEVICEINFO] = (nfsd4_enc)nfsd4_encode_noop, 31472db134ebSAndy Adamson [OP_GETDEVICELIST] = (nfsd4_enc)nfsd4_encode_noop, 31482db134ebSAndy Adamson [OP_LAYOUTCOMMIT] = (nfsd4_enc)nfsd4_encode_noop, 31492db134ebSAndy Adamson [OP_LAYOUTGET] = (nfsd4_enc)nfsd4_encode_noop, 31502db134ebSAndy Adamson [OP_LAYOUTRETURN] = (nfsd4_enc)nfsd4_encode_noop, 31512db134ebSAndy Adamson [OP_SECINFO_NO_NAME] = (nfsd4_enc)nfsd4_encode_noop, 31522db134ebSAndy Adamson [OP_SEQUENCE] = (nfsd4_enc)nfsd4_encode_sequence, 31532db134ebSAndy Adamson [OP_SET_SSV] = (nfsd4_enc)nfsd4_encode_noop, 31542db134ebSAndy Adamson [OP_TEST_STATEID] = (nfsd4_enc)nfsd4_encode_noop, 31552db134ebSAndy Adamson [OP_WANT_DELEGATION] = (nfsd4_enc)nfsd4_encode_noop, 31562db134ebSAndy Adamson [OP_DESTROY_CLIENTID] = (nfsd4_enc)nfsd4_encode_noop, 31572db134ebSAndy Adamson [OP_RECLAIM_COMPLETE] = (nfsd4_enc)nfsd4_encode_noop, 3158695e12f8SBenny Halevy }; 3159695e12f8SBenny Halevy 3160496c262cSAndy Adamson /* 3161496c262cSAndy Adamson * Calculate the total amount of memory that the compound response has taken 3162496c262cSAndy Adamson * after encoding the current operation. 3163496c262cSAndy Adamson * 3164496c262cSAndy Adamson * pad: add on 8 bytes for the next operation's op_code and status so that 3165496c262cSAndy Adamson * there is room to cache a failure on the next operation. 3166496c262cSAndy Adamson * 3167496c262cSAndy Adamson * Compare this length to the session se_fmaxresp_cached. 3168496c262cSAndy Adamson * 3169496c262cSAndy Adamson * Our se_fmaxresp_cached will always be a multiple of PAGE_SIZE, and so 3170496c262cSAndy Adamson * will be at least a page and will therefore hold the xdr_buf head. 3171496c262cSAndy Adamson */ 3172496c262cSAndy Adamson static int nfsd4_check_drc_limit(struct nfsd4_compoundres *resp) 3173496c262cSAndy Adamson { 3174496c262cSAndy Adamson int status = 0; 3175496c262cSAndy Adamson struct xdr_buf *xb = &resp->rqstp->rq_res; 3176496c262cSAndy Adamson struct nfsd4_compoundargs *args = resp->rqstp->rq_argp; 3177496c262cSAndy Adamson struct nfsd4_session *session = NULL; 3178496c262cSAndy Adamson struct nfsd4_slot *slot = resp->cstate.slot; 3179496c262cSAndy Adamson u32 length, tlen = 0, pad = 8; 3180496c262cSAndy Adamson 3181496c262cSAndy Adamson if (!nfsd4_has_session(&resp->cstate)) 3182496c262cSAndy Adamson return status; 3183496c262cSAndy Adamson 3184496c262cSAndy Adamson session = resp->cstate.session; 3185557ce264SAndy Adamson if (session == NULL || slot->sl_cachethis == 0) 3186496c262cSAndy Adamson return status; 3187496c262cSAndy Adamson 3188496c262cSAndy Adamson if (resp->opcnt >= args->opcnt) 3189496c262cSAndy Adamson pad = 0; /* this is the last operation */ 3190496c262cSAndy Adamson 3191496c262cSAndy Adamson if (xb->page_len == 0) { 3192496c262cSAndy Adamson length = (char *)resp->p - (char *)xb->head[0].iov_base + pad; 3193496c262cSAndy Adamson } else { 3194496c262cSAndy Adamson if (xb->tail[0].iov_base && xb->tail[0].iov_len > 0) 3195496c262cSAndy Adamson tlen = (char *)resp->p - (char *)xb->tail[0].iov_base; 3196496c262cSAndy Adamson 3197496c262cSAndy Adamson length = xb->head[0].iov_len + xb->page_len + tlen + pad; 3198496c262cSAndy Adamson } 3199496c262cSAndy Adamson dprintk("%s length %u, xb->page_len %u tlen %u pad %u\n", __func__, 3200496c262cSAndy Adamson length, xb->page_len, tlen, pad); 3201496c262cSAndy Adamson 32026c18ba9fSAlexandros Batsakis if (length <= session->se_fchannel.maxresp_cached) 3203496c262cSAndy Adamson return status; 3204496c262cSAndy Adamson else 3205496c262cSAndy Adamson return nfserr_rep_too_big_to_cache; 3206496c262cSAndy Adamson } 3207496c262cSAndy Adamson 32081da177e4SLinus Torvalds void 32091da177e4SLinus Torvalds nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) 32101da177e4SLinus Torvalds { 32112ebbc012SAl Viro __be32 *statp; 3212bc749ca4SJ. Bruce Fields __be32 *p; 32131da177e4SLinus Torvalds 32141da177e4SLinus Torvalds RESERVE_SPACE(8); 32151da177e4SLinus Torvalds WRITE32(op->opnum); 32161da177e4SLinus Torvalds statp = p++; /* to be backfilled at the end */ 32171da177e4SLinus Torvalds ADJUST_ARGS(); 32181da177e4SLinus Torvalds 3219695e12f8SBenny Halevy if (op->opnum == OP_ILLEGAL) 3220695e12f8SBenny Halevy goto status; 3221695e12f8SBenny Halevy BUG_ON(op->opnum < 0 || op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) || 3222695e12f8SBenny Halevy !nfsd4_enc_ops[op->opnum]); 3223695e12f8SBenny Halevy op->status = nfsd4_enc_ops[op->opnum](resp, op->status, &op->u); 3224496c262cSAndy Adamson /* nfsd4_check_drc_limit guarantees enough room for error status */ 3225496c262cSAndy Adamson if (!op->status && nfsd4_check_drc_limit(resp)) 3226496c262cSAndy Adamson op->status = nfserr_rep_too_big_to_cache; 3227695e12f8SBenny Halevy status: 32281da177e4SLinus Torvalds /* 32291da177e4SLinus Torvalds * Note: We write the status directly, instead of using WRITE32(), 32301da177e4SLinus Torvalds * since it is already in network byte order. 32311da177e4SLinus Torvalds */ 32321da177e4SLinus Torvalds *statp = op->status; 32331da177e4SLinus Torvalds } 32341da177e4SLinus Torvalds 32351da177e4SLinus Torvalds /* 32361da177e4SLinus Torvalds * Encode the reply stored in the stateowner reply cache 32371da177e4SLinus Torvalds * 32381da177e4SLinus Torvalds * XDR note: do not encode rp->rp_buflen: the buffer contains the 32391da177e4SLinus Torvalds * previously sent already encoded operation. 32401da177e4SLinus Torvalds * 32411da177e4SLinus Torvalds * called with nfs4_lock_state() held 32421da177e4SLinus Torvalds */ 32431da177e4SLinus Torvalds void 32441da177e4SLinus Torvalds nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op) 32451da177e4SLinus Torvalds { 3246bc749ca4SJ. Bruce Fields __be32 *p; 32471da177e4SLinus Torvalds struct nfs4_replay *rp = op->replay; 32481da177e4SLinus Torvalds 32491da177e4SLinus Torvalds BUG_ON(!rp); 32501da177e4SLinus Torvalds 32511da177e4SLinus Torvalds RESERVE_SPACE(8); 32521da177e4SLinus Torvalds WRITE32(op->opnum); 32531da177e4SLinus Torvalds *p++ = rp->rp_status; /* already xdr'ed */ 32541da177e4SLinus Torvalds ADJUST_ARGS(); 32551da177e4SLinus Torvalds 32561da177e4SLinus Torvalds RESERVE_SPACE(rp->rp_buflen); 32571da177e4SLinus Torvalds WRITEMEM(rp->rp_buf, rp->rp_buflen); 32581da177e4SLinus Torvalds ADJUST_ARGS(); 32591da177e4SLinus Torvalds } 32601da177e4SLinus Torvalds 32611da177e4SLinus Torvalds int 32622ebbc012SAl Viro nfs4svc_encode_voidres(struct svc_rqst *rqstp, __be32 *p, void *dummy) 32631da177e4SLinus Torvalds { 32641da177e4SLinus Torvalds return xdr_ressize_check(rqstp, p); 32651da177e4SLinus Torvalds } 32661da177e4SLinus Torvalds 32671da177e4SLinus Torvalds void nfsd4_release_compoundargs(struct nfsd4_compoundargs *args) 32681da177e4SLinus Torvalds { 32691da177e4SLinus Torvalds if (args->ops != args->iops) { 32701da177e4SLinus Torvalds kfree(args->ops); 32711da177e4SLinus Torvalds args->ops = args->iops; 32721da177e4SLinus Torvalds } 32731da177e4SLinus Torvalds kfree(args->tmpp); 32741da177e4SLinus Torvalds args->tmpp = NULL; 32751da177e4SLinus Torvalds while (args->to_free) { 32761da177e4SLinus Torvalds struct tmpbuf *tb = args->to_free; 32771da177e4SLinus Torvalds args->to_free = tb->next; 32781da177e4SLinus Torvalds tb->release(tb->buf); 32791da177e4SLinus Torvalds kfree(tb); 32801da177e4SLinus Torvalds } 32811da177e4SLinus Torvalds } 32821da177e4SLinus Torvalds 32831da177e4SLinus Torvalds int 32842ebbc012SAl Viro nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compoundargs *args) 32851da177e4SLinus Torvalds { 3286b37ad28bSAl Viro __be32 status; 32871da177e4SLinus Torvalds 32881da177e4SLinus Torvalds args->p = p; 32891da177e4SLinus Torvalds args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len; 32901da177e4SLinus Torvalds args->pagelist = rqstp->rq_arg.pages; 32911da177e4SLinus Torvalds args->pagelen = rqstp->rq_arg.page_len; 32921da177e4SLinus Torvalds args->tmpp = NULL; 32931da177e4SLinus Torvalds args->to_free = NULL; 32941da177e4SLinus Torvalds args->ops = args->iops; 32951da177e4SLinus Torvalds args->rqstp = rqstp; 32961da177e4SLinus Torvalds 32971da177e4SLinus Torvalds status = nfsd4_decode_compound(args); 32981da177e4SLinus Torvalds if (status) { 32991da177e4SLinus Torvalds nfsd4_release_compoundargs(args); 33001da177e4SLinus Torvalds } 33011da177e4SLinus Torvalds return !status; 33021da177e4SLinus Torvalds } 33031da177e4SLinus Torvalds 33041da177e4SLinus Torvalds int 33052ebbc012SAl Viro nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compoundres *resp) 33061da177e4SLinus Torvalds { 33071da177e4SLinus Torvalds /* 33081da177e4SLinus Torvalds * All that remains is to write the tag and operation count... 33091da177e4SLinus Torvalds */ 3310557ce264SAndy Adamson struct nfsd4_compound_state *cs = &resp->cstate; 33111da177e4SLinus Torvalds struct kvec *iov; 33121da177e4SLinus Torvalds p = resp->tagp; 33131da177e4SLinus Torvalds *p++ = htonl(resp->taglen); 33141da177e4SLinus Torvalds memcpy(p, resp->tag, resp->taglen); 33151da177e4SLinus Torvalds p += XDR_QUADLEN(resp->taglen); 33161da177e4SLinus Torvalds *p++ = htonl(resp->opcnt); 33171da177e4SLinus Torvalds 33181da177e4SLinus Torvalds if (rqstp->rq_res.page_len) 33191da177e4SLinus Torvalds iov = &rqstp->rq_res.tail[0]; 33201da177e4SLinus Torvalds else 33211da177e4SLinus Torvalds iov = &rqstp->rq_res.head[0]; 33221da177e4SLinus Torvalds iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base; 33231da177e4SLinus Torvalds BUG_ON(iov->iov_len > PAGE_SIZE); 332426c0c75eSJ. Bruce Fields if (nfsd4_has_session(cs)) { 332526c0c75eSJ. Bruce Fields if (cs->status != nfserr_replay_cache) { 3326da3846a2SAndy Adamson nfsd4_store_cache_entry(resp); 3327da3846a2SAndy Adamson dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__); 3328dbd65a7eSBenny Halevy cs->slot->sl_inuse = false; 332926c0c75eSJ. Bruce Fields } 3330d7682988SBenny Halevy /* Renew the clientid on success and on replay */ 3331d7682988SBenny Halevy release_session_client(cs->session); 3332da3846a2SAndy Adamson } 33331da177e4SLinus Torvalds return 1; 33341da177e4SLinus Torvalds } 33351da177e4SLinus Torvalds 33361da177e4SLinus Torvalds /* 33371da177e4SLinus Torvalds * Local variables: 33381da177e4SLinus Torvalds * c-basic-offset: 8 33391da177e4SLinus Torvalds * End: 33401da177e4SLinus Torvalds */ 3341