1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * XDR support for nfsd 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> 61da177e4SLinus Torvalds */ 71da177e4SLinus Torvalds 83dadecceSAl Viro #include "vfs.h" 99a74af21SBoaz Harrosh #include "xdr.h" 102e8138a2SJ. Bruce Fields #include "auth.h" 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds #define NFSDDBG_FACILITY NFSDDBG_XDR 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds /* 151da177e4SLinus Torvalds * Mapping of S_IF* types to NFS file types 161da177e4SLinus Torvalds */ 1792b54a4fSChuck Lever static const u32 nfs_ftypes[] = { 181da177e4SLinus Torvalds NFNON, NFCHR, NFCHR, NFBAD, 191da177e4SLinus Torvalds NFDIR, NFBAD, NFBLK, NFBAD, 201da177e4SLinus Torvalds NFREG, NFBAD, NFLNK, NFBAD, 211da177e4SLinus Torvalds NFSOCK, NFBAD, NFLNK, NFBAD, 221da177e4SLinus Torvalds }; 231da177e4SLinus Torvalds 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds /* 26ebcd8e8bSChuck Lever * Basic NFSv2 data types (RFC 1094 Section 2.3) 271da177e4SLinus Torvalds */ 28ebcd8e8bSChuck Lever 29a887eaedSChuck Lever static bool 30a887eaedSChuck Lever svcxdr_encode_stat(struct xdr_stream *xdr, __be32 status) 31a887eaedSChuck Lever { 32a887eaedSChuck Lever __be32 *p; 33a887eaedSChuck Lever 34a887eaedSChuck Lever p = xdr_reserve_space(xdr, sizeof(status)); 35a887eaedSChuck Lever if (!p) 36a887eaedSChuck Lever return false; 37a887eaedSChuck Lever *p = status; 38a887eaedSChuck Lever 39a887eaedSChuck Lever return true; 40a887eaedSChuck Lever } 41a887eaedSChuck Lever 42635a45d3SChuck Lever /** 43635a45d3SChuck Lever * svcxdr_decode_fhandle - Decode an NFSv2 file handle 44635a45d3SChuck Lever * @xdr: XDR stream positioned at an encoded NFSv2 FH 45635a45d3SChuck Lever * @fhp: OUT: filled-in server file handle 46635a45d3SChuck Lever * 47635a45d3SChuck Lever * Return values: 48635a45d3SChuck Lever * %false: The encoded file handle was not valid 49635a45d3SChuck Lever * %true: @fhp has been initialized 50635a45d3SChuck Lever */ 51635a45d3SChuck Lever bool 52ebcd8e8bSChuck Lever svcxdr_decode_fhandle(struct xdr_stream *xdr, struct svc_fh *fhp) 53ebcd8e8bSChuck Lever { 54ebcd8e8bSChuck Lever __be32 *p; 55ebcd8e8bSChuck Lever 56ebcd8e8bSChuck Lever p = xdr_inline_decode(xdr, NFS_FHSIZE); 57ebcd8e8bSChuck Lever if (!p) 58ebcd8e8bSChuck Lever return false; 59ebcd8e8bSChuck Lever fh_init(fhp, NFS_FHSIZE); 60ebcd8e8bSChuck Lever memcpy(&fhp->fh_handle.fh_base, p, NFS_FHSIZE); 61ebcd8e8bSChuck Lever fhp->fh_handle.fh_size = NFS_FHSIZE; 62ebcd8e8bSChuck Lever 63ebcd8e8bSChuck Lever return true; 64ebcd8e8bSChuck Lever } 65ebcd8e8bSChuck Lever 66*e3b4ef22SChuck Lever static bool 67*e3b4ef22SChuck Lever svcxdr_encode_fhandle(struct xdr_stream *xdr, const struct svc_fh *fhp) 681da177e4SLinus Torvalds { 69*e3b4ef22SChuck Lever __be32 *p; 70*e3b4ef22SChuck Lever 71*e3b4ef22SChuck Lever p = xdr_reserve_space(xdr, NFS_FHSIZE); 72*e3b4ef22SChuck Lever if (!p) 73*e3b4ef22SChuck Lever return false; 741da177e4SLinus Torvalds memcpy(p, &fhp->fh_handle.fh_base, NFS_FHSIZE); 75*e3b4ef22SChuck Lever 76*e3b4ef22SChuck Lever return true; 771da177e4SLinus Torvalds } 781da177e4SLinus Torvalds 7992b54a4fSChuck Lever static __be32 * 8092b54a4fSChuck Lever encode_timeval(__be32 *p, const struct timespec64 *time) 8192b54a4fSChuck Lever { 8292b54a4fSChuck Lever *p++ = cpu_to_be32((u32)time->tv_sec); 8392b54a4fSChuck Lever if (time->tv_nsec) 8492b54a4fSChuck Lever *p++ = cpu_to_be32(time->tv_nsec / NSEC_PER_USEC); 8592b54a4fSChuck Lever else 8692b54a4fSChuck Lever *p++ = xdr_zero; 8792b54a4fSChuck Lever return p; 8892b54a4fSChuck Lever } 8992b54a4fSChuck Lever 906d742c18SChuck Lever static bool 916d742c18SChuck Lever svcxdr_decode_filename(struct xdr_stream *xdr, char **name, unsigned int *len) 926d742c18SChuck Lever { 936d742c18SChuck Lever u32 size, i; 946d742c18SChuck Lever __be32 *p; 956d742c18SChuck Lever char *c; 966d742c18SChuck Lever 976d742c18SChuck Lever if (xdr_stream_decode_u32(xdr, &size) < 0) 986d742c18SChuck Lever return false; 996d742c18SChuck Lever if (size == 0 || size > NFS_MAXNAMLEN) 1006d742c18SChuck Lever return false; 1016d742c18SChuck Lever p = xdr_inline_decode(xdr, size); 1026d742c18SChuck Lever if (!p) 1036d742c18SChuck Lever return false; 1046d742c18SChuck Lever 1056d742c18SChuck Lever *len = size; 1066d742c18SChuck Lever *name = (char *)p; 1076d742c18SChuck Lever for (i = 0, c = *name; i < size; i++, c++) 1086d742c18SChuck Lever if (*c == '\0' || *c == '/') 1096d742c18SChuck Lever return false; 1106d742c18SChuck Lever 1116d742c18SChuck Lever return true; 1126d742c18SChuck Lever } 1136d742c18SChuck Lever 1146d742c18SChuck Lever static bool 1156d742c18SChuck Lever svcxdr_decode_diropargs(struct xdr_stream *xdr, struct svc_fh *fhp, 1166d742c18SChuck Lever char **name, unsigned int *len) 1176d742c18SChuck Lever { 1186d742c18SChuck Lever return svcxdr_decode_fhandle(xdr, fhp) && 1196d742c18SChuck Lever svcxdr_decode_filename(xdr, name, len); 1206d742c18SChuck Lever } 1216d742c18SChuck Lever 1222fdd6bd2SChuck Lever static bool 1232fdd6bd2SChuck Lever svcxdr_decode_sattr(struct svc_rqst *rqstp, struct xdr_stream *xdr, 1242fdd6bd2SChuck Lever struct iattr *iap) 1252fdd6bd2SChuck Lever { 1262fdd6bd2SChuck Lever u32 tmp1, tmp2; 1272fdd6bd2SChuck Lever __be32 *p; 1282fdd6bd2SChuck Lever 1292fdd6bd2SChuck Lever p = xdr_inline_decode(xdr, XDR_UNIT * 8); 1302fdd6bd2SChuck Lever if (!p) 1312fdd6bd2SChuck Lever return false; 1322fdd6bd2SChuck Lever 1332fdd6bd2SChuck Lever iap->ia_valid = 0; 1342fdd6bd2SChuck Lever 1352fdd6bd2SChuck Lever /* 1362fdd6bd2SChuck Lever * Some Sun clients put 0xffff in the mode field when they 1372fdd6bd2SChuck Lever * mean 0xffffffff. 1382fdd6bd2SChuck Lever */ 1392fdd6bd2SChuck Lever tmp1 = be32_to_cpup(p++); 1402fdd6bd2SChuck Lever if (tmp1 != (u32)-1 && tmp1 != 0xffff) { 1412fdd6bd2SChuck Lever iap->ia_valid |= ATTR_MODE; 1422fdd6bd2SChuck Lever iap->ia_mode = tmp1; 1432fdd6bd2SChuck Lever } 1442fdd6bd2SChuck Lever 1452fdd6bd2SChuck Lever tmp1 = be32_to_cpup(p++); 1462fdd6bd2SChuck Lever if (tmp1 != (u32)-1) { 1472fdd6bd2SChuck Lever iap->ia_uid = make_kuid(nfsd_user_namespace(rqstp), tmp1); 1482fdd6bd2SChuck Lever if (uid_valid(iap->ia_uid)) 1492fdd6bd2SChuck Lever iap->ia_valid |= ATTR_UID; 1502fdd6bd2SChuck Lever } 1512fdd6bd2SChuck Lever 1522fdd6bd2SChuck Lever tmp1 = be32_to_cpup(p++); 1532fdd6bd2SChuck Lever if (tmp1 != (u32)-1) { 1542fdd6bd2SChuck Lever iap->ia_gid = make_kgid(nfsd_user_namespace(rqstp), tmp1); 1552fdd6bd2SChuck Lever if (gid_valid(iap->ia_gid)) 1562fdd6bd2SChuck Lever iap->ia_valid |= ATTR_GID; 1572fdd6bd2SChuck Lever } 1582fdd6bd2SChuck Lever 1592fdd6bd2SChuck Lever tmp1 = be32_to_cpup(p++); 1602fdd6bd2SChuck Lever if (tmp1 != (u32)-1) { 1612fdd6bd2SChuck Lever iap->ia_valid |= ATTR_SIZE; 1622fdd6bd2SChuck Lever iap->ia_size = tmp1; 1632fdd6bd2SChuck Lever } 1642fdd6bd2SChuck Lever 1652fdd6bd2SChuck Lever tmp1 = be32_to_cpup(p++); 1662fdd6bd2SChuck Lever tmp2 = be32_to_cpup(p++); 1672fdd6bd2SChuck Lever if (tmp1 != (u32)-1 && tmp2 != (u32)-1) { 1682fdd6bd2SChuck Lever iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET; 1692fdd6bd2SChuck Lever iap->ia_atime.tv_sec = tmp1; 1702fdd6bd2SChuck Lever iap->ia_atime.tv_nsec = tmp2 * NSEC_PER_USEC; 1712fdd6bd2SChuck Lever } 1722fdd6bd2SChuck Lever 1732fdd6bd2SChuck Lever tmp1 = be32_to_cpup(p++); 1742fdd6bd2SChuck Lever tmp2 = be32_to_cpup(p++); 1752fdd6bd2SChuck Lever if (tmp1 != (u32)-1 && tmp2 != (u32)-1) { 1762fdd6bd2SChuck Lever iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET; 1772fdd6bd2SChuck Lever iap->ia_mtime.tv_sec = tmp1; 1782fdd6bd2SChuck Lever iap->ia_mtime.tv_nsec = tmp2 * NSEC_PER_USEC; 1792fdd6bd2SChuck Lever /* 1802fdd6bd2SChuck Lever * Passing the invalid value useconds=1000000 for mtime 1812fdd6bd2SChuck Lever * is a Sun convention for "set both mtime and atime to 1822fdd6bd2SChuck Lever * current server time". It's needed to make permissions 1832fdd6bd2SChuck Lever * checks for the "touch" program across v2 mounts to 1842fdd6bd2SChuck Lever * Solaris and Irix boxes work correctly. See description of 1852fdd6bd2SChuck Lever * sattr in section 6.1 of "NFS Illustrated" by 1862fdd6bd2SChuck Lever * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5 1872fdd6bd2SChuck Lever */ 1882fdd6bd2SChuck Lever if (tmp2 == 1000000) 1892fdd6bd2SChuck Lever iap->ia_valid &= ~(ATTR_ATIME_SET|ATTR_MTIME_SET); 1902fdd6bd2SChuck Lever } 1912fdd6bd2SChuck Lever 1922fdd6bd2SChuck Lever return true; 1932fdd6bd2SChuck Lever } 1942fdd6bd2SChuck Lever 195131a21c2SAl Viro static __be32 * 196131a21c2SAl Viro encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, 197a334de28SDavid Shaw struct kstat *stat) 1981da177e4SLinus Torvalds { 199e45d1a18STrond Myklebust struct user_namespace *userns = nfsd_user_namespace(rqstp); 2001da177e4SLinus Torvalds struct dentry *dentry = fhp->fh_dentry; 2011da177e4SLinus Torvalds int type; 20295582b00SDeepa Dinamani struct timespec64 time; 203af6a4e28SNeilBrown u32 f; 2041da177e4SLinus Torvalds 205a334de28SDavid Shaw type = (stat->mode & S_IFMT); 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds *p++ = htonl(nfs_ftypes[type >> 12]); 208082f31a2SJ. Bruce Fields *p++ = htonl((u32) stat->mode); 209a334de28SDavid Shaw *p++ = htonl((u32) stat->nlink); 210e45d1a18STrond Myklebust *p++ = htonl((u32) from_kuid_munged(userns, stat->uid)); 211e45d1a18STrond Myklebust *p++ = htonl((u32) from_kgid_munged(userns, stat->gid)); 2121da177e4SLinus Torvalds 213a334de28SDavid Shaw if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN) { 2141da177e4SLinus Torvalds *p++ = htonl(NFS_MAXPATHLEN); 2151da177e4SLinus Torvalds } else { 216a334de28SDavid Shaw *p++ = htonl((u32) stat->size); 2171da177e4SLinus Torvalds } 218a334de28SDavid Shaw *p++ = htonl((u32) stat->blksize); 2191da177e4SLinus Torvalds if (S_ISCHR(type) || S_ISBLK(type)) 220a334de28SDavid Shaw *p++ = htonl(new_encode_dev(stat->rdev)); 2211da177e4SLinus Torvalds else 2221da177e4SLinus Torvalds *p++ = htonl(0xffffffff); 223a334de28SDavid Shaw *p++ = htonl((u32) stat->blocks); 224af6a4e28SNeilBrown switch (fsid_source(fhp)) { 225af6a4e28SNeilBrown default: 226af6a4e28SNeilBrown case FSIDSOURCE_DEV: 227a334de28SDavid Shaw *p++ = htonl(new_encode_dev(stat->dev)); 228af6a4e28SNeilBrown break; 229af6a4e28SNeilBrown case FSIDSOURCE_FSID: 230af6a4e28SNeilBrown *p++ = htonl((u32) fhp->fh_export->ex_fsid); 231af6a4e28SNeilBrown break; 232af6a4e28SNeilBrown case FSIDSOURCE_UUID: 233af6a4e28SNeilBrown f = ((u32*)fhp->fh_export->ex_uuid)[0]; 234af6a4e28SNeilBrown f ^= ((u32*)fhp->fh_export->ex_uuid)[1]; 235af6a4e28SNeilBrown f ^= ((u32*)fhp->fh_export->ex_uuid)[2]; 236af6a4e28SNeilBrown f ^= ((u32*)fhp->fh_export->ex_uuid)[3]; 237af6a4e28SNeilBrown *p++ = htonl(f); 238af6a4e28SNeilBrown break; 239af6a4e28SNeilBrown } 240a334de28SDavid Shaw *p++ = htonl((u32) stat->ino); 241a334de28SDavid Shaw *p++ = htonl((u32) stat->atime.tv_sec); 242a334de28SDavid Shaw *p++ = htonl(stat->atime.tv_nsec ? stat->atime.tv_nsec / 1000 : 0); 24376c47948SAmir Goldstein time = stat->mtime; 2442b0143b5SDavid Howells lease_get_mtime(d_inode(dentry), &time); 2451da177e4SLinus Torvalds *p++ = htonl((u32) time.tv_sec); 2461da177e4SLinus Torvalds *p++ = htonl(time.tv_nsec ? time.tv_nsec / 1000 : 0); 247a334de28SDavid Shaw *p++ = htonl((u32) stat->ctime.tv_sec); 248a334de28SDavid Shaw *p++ = htonl(stat->ctime.tv_nsec ? stat->ctime.tv_nsec / 1000 : 0); 2491da177e4SLinus Torvalds 2501da177e4SLinus Torvalds return p; 2511da177e4SLinus Torvalds } 2521da177e4SLinus Torvalds 253*e3b4ef22SChuck Lever static bool 25492b54a4fSChuck Lever svcxdr_encode_fattr(struct svc_rqst *rqstp, struct xdr_stream *xdr, 25592b54a4fSChuck Lever const struct svc_fh *fhp, const struct kstat *stat) 25692b54a4fSChuck Lever { 25792b54a4fSChuck Lever struct user_namespace *userns = nfsd_user_namespace(rqstp); 25892b54a4fSChuck Lever struct dentry *dentry = fhp->fh_dentry; 25992b54a4fSChuck Lever int type = stat->mode & S_IFMT; 26092b54a4fSChuck Lever struct timespec64 time; 26192b54a4fSChuck Lever __be32 *p; 26292b54a4fSChuck Lever u32 fsid; 26392b54a4fSChuck Lever 26492b54a4fSChuck Lever p = xdr_reserve_space(xdr, XDR_UNIT * 17); 26592b54a4fSChuck Lever if (!p) 266*e3b4ef22SChuck Lever return false; 26792b54a4fSChuck Lever 26892b54a4fSChuck Lever *p++ = cpu_to_be32(nfs_ftypes[type >> 12]); 26992b54a4fSChuck Lever *p++ = cpu_to_be32((u32)stat->mode); 27092b54a4fSChuck Lever *p++ = cpu_to_be32((u32)stat->nlink); 27192b54a4fSChuck Lever *p++ = cpu_to_be32((u32)from_kuid_munged(userns, stat->uid)); 27292b54a4fSChuck Lever *p++ = cpu_to_be32((u32)from_kgid_munged(userns, stat->gid)); 27392b54a4fSChuck Lever 27492b54a4fSChuck Lever if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN) 27592b54a4fSChuck Lever *p++ = cpu_to_be32(NFS_MAXPATHLEN); 27692b54a4fSChuck Lever else 27792b54a4fSChuck Lever *p++ = cpu_to_be32((u32) stat->size); 27892b54a4fSChuck Lever *p++ = cpu_to_be32((u32) stat->blksize); 27992b54a4fSChuck Lever if (S_ISCHR(type) || S_ISBLK(type)) 28092b54a4fSChuck Lever *p++ = cpu_to_be32(new_encode_dev(stat->rdev)); 28192b54a4fSChuck Lever else 28292b54a4fSChuck Lever *p++ = cpu_to_be32(0xffffffff); 28392b54a4fSChuck Lever *p++ = cpu_to_be32((u32)stat->blocks); 28492b54a4fSChuck Lever 28592b54a4fSChuck Lever switch (fsid_source(fhp)) { 28692b54a4fSChuck Lever case FSIDSOURCE_FSID: 28792b54a4fSChuck Lever fsid = (u32)fhp->fh_export->ex_fsid; 28892b54a4fSChuck Lever break; 28992b54a4fSChuck Lever case FSIDSOURCE_UUID: 29092b54a4fSChuck Lever fsid = ((u32 *)fhp->fh_export->ex_uuid)[0]; 29192b54a4fSChuck Lever fsid ^= ((u32 *)fhp->fh_export->ex_uuid)[1]; 29292b54a4fSChuck Lever fsid ^= ((u32 *)fhp->fh_export->ex_uuid)[2]; 29392b54a4fSChuck Lever fsid ^= ((u32 *)fhp->fh_export->ex_uuid)[3]; 29492b54a4fSChuck Lever break; 29592b54a4fSChuck Lever default: 29692b54a4fSChuck Lever fsid = new_encode_dev(stat->dev); 29792b54a4fSChuck Lever break; 29892b54a4fSChuck Lever } 29992b54a4fSChuck Lever *p++ = cpu_to_be32(fsid); 30092b54a4fSChuck Lever 30192b54a4fSChuck Lever *p++ = cpu_to_be32((u32)stat->ino); 30292b54a4fSChuck Lever p = encode_timeval(p, &stat->atime); 30392b54a4fSChuck Lever time = stat->mtime; 30492b54a4fSChuck Lever lease_get_mtime(d_inode(dentry), &time); 30592b54a4fSChuck Lever p = encode_timeval(p, &time); 30692b54a4fSChuck Lever encode_timeval(p, &stat->ctime); 30792b54a4fSChuck Lever 308*e3b4ef22SChuck Lever return true; 30992b54a4fSChuck Lever } 31092b54a4fSChuck Lever 311a257cdd0SAndreas Gruenbacher /* Helper function for NFSv2 ACL code */ 3124f4a4fadSJ. Bruce Fields __be32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, struct kstat *stat) 313a257cdd0SAndreas Gruenbacher { 3144f4a4fadSJ. Bruce Fields return encode_fattr(rqstp, p, fhp, stat); 315a257cdd0SAndreas Gruenbacher } 3161da177e4SLinus Torvalds 3171da177e4SLinus Torvalds /* 3181da177e4SLinus Torvalds * XDR decode functions 3191da177e4SLinus Torvalds */ 3201da177e4SLinus Torvalds 3211da177e4SLinus Torvalds int 322ebcd8e8bSChuck Lever nfssvc_decode_fhandleargs(struct svc_rqst *rqstp, __be32 *p) 3231da177e4SLinus Torvalds { 324ebcd8e8bSChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 325026fec7eSChristoph Hellwig struct nfsd_fhandle *args = rqstp->rq_argp; 326026fec7eSChristoph Hellwig 327ebcd8e8bSChuck Lever return svcxdr_decode_fhandle(xdr, &args->fh); 3281da177e4SLinus Torvalds } 3291da177e4SLinus Torvalds 3301da177e4SLinus Torvalds int 331026fec7eSChristoph Hellwig nfssvc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p) 3321da177e4SLinus Torvalds { 3332fdd6bd2SChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 334026fec7eSChristoph Hellwig struct nfsd_sattrargs *args = rqstp->rq_argp; 335026fec7eSChristoph Hellwig 3362fdd6bd2SChuck Lever return svcxdr_decode_fhandle(xdr, &args->fh) && 3372fdd6bd2SChuck Lever svcxdr_decode_sattr(rqstp, xdr, &args->attrs); 3381da177e4SLinus Torvalds } 3391da177e4SLinus Torvalds 3401da177e4SLinus Torvalds int 341026fec7eSChristoph Hellwig nfssvc_decode_diropargs(struct svc_rqst *rqstp, __be32 *p) 3421da177e4SLinus Torvalds { 3436d742c18SChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 344026fec7eSChristoph Hellwig struct nfsd_diropargs *args = rqstp->rq_argp; 345026fec7eSChristoph Hellwig 3466d742c18SChuck Lever return svcxdr_decode_diropargs(xdr, &args->fh, &args->name, &args->len); 3471da177e4SLinus Torvalds } 3481da177e4SLinus Torvalds 3491da177e4SLinus Torvalds int 350026fec7eSChristoph Hellwig nfssvc_decode_readargs(struct svc_rqst *rqstp, __be32 *p) 3511da177e4SLinus Torvalds { 3528c293ef9SChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 353026fec7eSChristoph Hellwig struct nfsd_readargs *args = rqstp->rq_argp; 3548c293ef9SChuck Lever u32 totalcount; 3558c293ef9SChuck Lever 3568c293ef9SChuck Lever if (!svcxdr_decode_fhandle(xdr, &args->fh)) 3578c293ef9SChuck Lever return 0; 3588c293ef9SChuck Lever if (xdr_stream_decode_u32(xdr, &args->offset) < 0) 3598c293ef9SChuck Lever return 0; 3608c293ef9SChuck Lever if (xdr_stream_decode_u32(xdr, &args->count) < 0) 3618c293ef9SChuck Lever return 0; 3628c293ef9SChuck Lever /* totalcount is ignored */ 3638c293ef9SChuck Lever if (xdr_stream_decode_u32(xdr, &totalcount) < 0) 3641da177e4SLinus Torvalds return 0; 3651da177e4SLinus Torvalds 3668c293ef9SChuck Lever return 1; 3671da177e4SLinus Torvalds } 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds int 370026fec7eSChristoph Hellwig nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p) 3711da177e4SLinus Torvalds { 372a51b5b73SChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 373026fec7eSChristoph Hellwig struct nfsd_writeargs *args = rqstp->rq_argp; 374db44bac4SJ. Bruce Fields struct kvec *head = rqstp->rq_arg.head; 375a51b5b73SChuck Lever struct kvec *tail = rqstp->rq_arg.tail; 376a51b5b73SChuck Lever u32 beginoffset, totalcount; 377a51b5b73SChuck Lever size_t remaining; 378f34b9568SPeter Staubach 379a51b5b73SChuck Lever if (!svcxdr_decode_fhandle(xdr, &args->fh)) 380a51b5b73SChuck Lever return 0; 381a51b5b73SChuck Lever /* beginoffset is ignored */ 382a51b5b73SChuck Lever if (xdr_stream_decode_u32(xdr, &beginoffset) < 0) 383a51b5b73SChuck Lever return 0; 384a51b5b73SChuck Lever if (xdr_stream_decode_u32(xdr, &args->offset) < 0) 385a51b5b73SChuck Lever return 0; 386a51b5b73SChuck Lever /* totalcount is ignored */ 387a51b5b73SChuck Lever if (xdr_stream_decode_u32(xdr, &totalcount) < 0) 3881da177e4SLinus Torvalds return 0; 3891da177e4SLinus Torvalds 390a51b5b73SChuck Lever /* opaque data */ 391a51b5b73SChuck Lever if (xdr_stream_decode_u32(xdr, &args->len) < 0) 392f34b9568SPeter Staubach return 0; 393a51b5b73SChuck Lever if (args->len > NFSSVC_MAXBLKSIZE_V2) 39413bf9fbfSJ. Bruce Fields return 0; 395a51b5b73SChuck Lever remaining = head->iov_len + rqstp->rq_arg.page_len + tail->iov_len; 396a51b5b73SChuck Lever remaining -= xdr_stream_pos(xdr); 397a51b5b73SChuck Lever if (remaining < xdr_align_size(args->len)) 398f34b9568SPeter Staubach return 0; 399a51b5b73SChuck Lever args->first.iov_base = xdr->p; 400a51b5b73SChuck Lever args->first.iov_len = head->iov_len - xdr_stream_pos(xdr); 401f34b9568SPeter Staubach 402f34b9568SPeter Staubach return 1; 4031da177e4SLinus Torvalds } 4041da177e4SLinus Torvalds 4051da177e4SLinus Torvalds int 406026fec7eSChristoph Hellwig nfssvc_decode_createargs(struct svc_rqst *rqstp, __be32 *p) 4071da177e4SLinus Torvalds { 4087dcf65b9SChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 409026fec7eSChristoph Hellwig struct nfsd_createargs *args = rqstp->rq_argp; 410026fec7eSChristoph Hellwig 4117dcf65b9SChuck Lever return svcxdr_decode_diropargs(xdr, &args->fh, 4127dcf65b9SChuck Lever &args->name, &args->len) && 4137dcf65b9SChuck Lever svcxdr_decode_sattr(rqstp, xdr, &args->attrs); 4141da177e4SLinus Torvalds } 4151da177e4SLinus Torvalds 4161da177e4SLinus Torvalds int 417026fec7eSChristoph Hellwig nfssvc_decode_renameargs(struct svc_rqst *rqstp, __be32 *p) 4181da177e4SLinus Torvalds { 41962aa557eSChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 420026fec7eSChristoph Hellwig struct nfsd_renameargs *args = rqstp->rq_argp; 421026fec7eSChristoph Hellwig 42262aa557eSChuck Lever return svcxdr_decode_diropargs(xdr, &args->ffh, 42362aa557eSChuck Lever &args->fname, &args->flen) && 42462aa557eSChuck Lever svcxdr_decode_diropargs(xdr, &args->tfh, 42562aa557eSChuck Lever &args->tname, &args->tlen); 4261da177e4SLinus Torvalds } 4271da177e4SLinus Torvalds 4281da177e4SLinus Torvalds int 429026fec7eSChristoph Hellwig nfssvc_decode_linkargs(struct svc_rqst *rqstp, __be32 *p) 4301da177e4SLinus Torvalds { 43177edcdf9SChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 432026fec7eSChristoph Hellwig struct nfsd_linkargs *args = rqstp->rq_argp; 433026fec7eSChristoph Hellwig 43477edcdf9SChuck Lever return svcxdr_decode_fhandle(xdr, &args->ffh) && 43577edcdf9SChuck Lever svcxdr_decode_diropargs(xdr, &args->tfh, 43677edcdf9SChuck Lever &args->tname, &args->tlen); 4371da177e4SLinus Torvalds } 4381da177e4SLinus Torvalds 4391da177e4SLinus Torvalds int 440026fec7eSChristoph Hellwig nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p) 4411da177e4SLinus Torvalds { 44209f75a53SChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 443026fec7eSChristoph Hellwig struct nfsd_symlinkargs *args = rqstp->rq_argp; 44409f75a53SChuck Lever struct kvec *head = rqstp->rq_arg.head; 445026fec7eSChristoph Hellwig 44609f75a53SChuck Lever if (!svcxdr_decode_diropargs(xdr, &args->ffh, &args->fname, &args->flen)) 4471da177e4SLinus Torvalds return 0; 44809f75a53SChuck Lever if (xdr_stream_decode_u32(xdr, &args->tlen) < 0) 44909f75a53SChuck Lever return 0; 45038a70315SChuck Lever if (args->tlen == 0) 45138a70315SChuck Lever return 0; 45238a70315SChuck Lever 45309f75a53SChuck Lever args->first.iov_len = head->iov_len - xdr_stream_pos(xdr); 45409f75a53SChuck Lever args->first.iov_base = xdr_inline_decode(xdr, args->tlen); 45509f75a53SChuck Lever if (!args->first.iov_base) 45638a70315SChuck Lever return 0; 45709f75a53SChuck Lever return svcxdr_decode_sattr(rqstp, xdr, &args->attrs); 4581da177e4SLinus Torvalds } 4591da177e4SLinus Torvalds 4601da177e4SLinus Torvalds int 461026fec7eSChristoph Hellwig nfssvc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p) 4621da177e4SLinus Torvalds { 4638688361aSChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 464026fec7eSChristoph Hellwig struct nfsd_readdirargs *args = rqstp->rq_argp; 465026fec7eSChristoph Hellwig 4668688361aSChuck Lever if (!svcxdr_decode_fhandle(xdr, &args->fh)) 4671da177e4SLinus Torvalds return 0; 4688688361aSChuck Lever if (xdr_stream_decode_u32(xdr, &args->cookie) < 0) 4698688361aSChuck Lever return 0; 4708688361aSChuck Lever if (xdr_stream_decode_u32(xdr, &args->count) < 0) 4718688361aSChuck Lever return 0; 4721da177e4SLinus Torvalds 4738688361aSChuck Lever return 1; 4741da177e4SLinus Torvalds } 4751da177e4SLinus Torvalds 4761da177e4SLinus Torvalds /* 4771da177e4SLinus Torvalds * XDR encode functions 4781da177e4SLinus Torvalds */ 4791da177e4SLinus Torvalds 4801da177e4SLinus Torvalds int 481a887eaedSChuck Lever nfssvc_encode_statres(struct svc_rqst *rqstp, __be32 *p) 482cc028a10SChuck Lever { 483a887eaedSChuck Lever struct xdr_stream *xdr = &rqstp->rq_res_stream; 484cc028a10SChuck Lever struct nfsd_stat *resp = rqstp->rq_resp; 485cc028a10SChuck Lever 486a887eaedSChuck Lever return svcxdr_encode_stat(xdr, resp->status); 487cc028a10SChuck Lever } 488cc028a10SChuck Lever 489cc028a10SChuck Lever int 49092b54a4fSChuck Lever nfssvc_encode_attrstatres(struct svc_rqst *rqstp, __be32 *p) 4911da177e4SLinus Torvalds { 49292b54a4fSChuck Lever struct xdr_stream *xdr = &rqstp->rq_res_stream; 49363f8de37SChristoph Hellwig struct nfsd_attrstat *resp = rqstp->rq_resp; 49463f8de37SChristoph Hellwig 49592b54a4fSChuck Lever if (!svcxdr_encode_stat(xdr, resp->status)) 49692b54a4fSChuck Lever return 0; 49792b54a4fSChuck Lever switch (resp->status) { 49892b54a4fSChuck Lever case nfs_ok: 49992b54a4fSChuck Lever if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat)) 50092b54a4fSChuck Lever return 0; 50192b54a4fSChuck Lever break; 50292b54a4fSChuck Lever } 50392b54a4fSChuck Lever 50492b54a4fSChuck Lever return 1; 5051da177e4SLinus Torvalds } 5061da177e4SLinus Torvalds 5071da177e4SLinus Torvalds int 50863f8de37SChristoph Hellwig nfssvc_encode_diropres(struct svc_rqst *rqstp, __be32 *p) 5091da177e4SLinus Torvalds { 510*e3b4ef22SChuck Lever struct xdr_stream *xdr = &rqstp->rq_res_stream; 51163f8de37SChristoph Hellwig struct nfsd_diropres *resp = rqstp->rq_resp; 51263f8de37SChristoph Hellwig 513*e3b4ef22SChuck Lever if (!svcxdr_encode_stat(xdr, resp->status)) 514*e3b4ef22SChuck Lever return 0; 515*e3b4ef22SChuck Lever switch (resp->status) { 516*e3b4ef22SChuck Lever case nfs_ok: 517*e3b4ef22SChuck Lever if (!svcxdr_encode_fhandle(xdr, &resp->fh)) 518*e3b4ef22SChuck Lever return 0; 519*e3b4ef22SChuck Lever if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat)) 520*e3b4ef22SChuck Lever return 0; 521*e3b4ef22SChuck Lever break; 522*e3b4ef22SChuck Lever } 523*e3b4ef22SChuck Lever 524*e3b4ef22SChuck Lever return 1; 5251da177e4SLinus Torvalds } 5261da177e4SLinus Torvalds 5271da177e4SLinus Torvalds int 52863f8de37SChristoph Hellwig nfssvc_encode_readlinkres(struct svc_rqst *rqstp, __be32 *p) 5291da177e4SLinus Torvalds { 53063f8de37SChristoph Hellwig struct nfsd_readlinkres *resp = rqstp->rq_resp; 53176e5492bSChuck Lever struct kvec *head = rqstp->rq_res.head; 53263f8de37SChristoph Hellwig 533cc028a10SChuck Lever *p++ = resp->status; 534f0af2210SChuck Lever if (resp->status != nfs_ok) 535f0af2210SChuck Lever return xdr_ressize_check(rqstp, p); 536f0af2210SChuck Lever 5371da177e4SLinus Torvalds *p++ = htonl(resp->len); 5381da177e4SLinus Torvalds xdr_ressize_check(rqstp, p); 5391da177e4SLinus Torvalds rqstp->rq_res.page_len = resp->len; 5401da177e4SLinus Torvalds if (resp->len & 3) { 5411da177e4SLinus Torvalds /* need to pad the tail */ 5421da177e4SLinus Torvalds rqstp->rq_res.tail[0].iov_base = p; 5431da177e4SLinus Torvalds *p = 0; 5441da177e4SLinus Torvalds rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3); 5451da177e4SLinus Torvalds } 54676e5492bSChuck Lever if (svc_encode_result_payload(rqstp, head->iov_len, resp->len)) 54776e5492bSChuck Lever return 0; 5481da177e4SLinus Torvalds return 1; 5491da177e4SLinus Torvalds } 5501da177e4SLinus Torvalds 5511da177e4SLinus Torvalds int 55263f8de37SChristoph Hellwig nfssvc_encode_readres(struct svc_rqst *rqstp, __be32 *p) 5531da177e4SLinus Torvalds { 55463f8de37SChristoph Hellwig struct nfsd_readres *resp = rqstp->rq_resp; 55576e5492bSChuck Lever struct kvec *head = rqstp->rq_res.head; 55663f8de37SChristoph Hellwig 557cc028a10SChuck Lever *p++ = resp->status; 558f0af2210SChuck Lever if (resp->status != nfs_ok) 559f0af2210SChuck Lever return xdr_ressize_check(rqstp, p); 560f0af2210SChuck Lever 561a334de28SDavid Shaw p = encode_fattr(rqstp, p, &resp->fh, &resp->stat); 5621da177e4SLinus Torvalds *p++ = htonl(resp->count); 5631da177e4SLinus Torvalds xdr_ressize_check(rqstp, p); 5641da177e4SLinus Torvalds 5651da177e4SLinus Torvalds /* now update rqstp->rq_res to reflect data as well */ 5661da177e4SLinus Torvalds rqstp->rq_res.page_len = resp->count; 5671da177e4SLinus Torvalds if (resp->count & 3) { 5681da177e4SLinus Torvalds /* need to pad the tail */ 5691da177e4SLinus Torvalds rqstp->rq_res.tail[0].iov_base = p; 5701da177e4SLinus Torvalds *p = 0; 5711da177e4SLinus Torvalds rqstp->rq_res.tail[0].iov_len = 4 - (resp->count&3); 5721da177e4SLinus Torvalds } 57376e5492bSChuck Lever if (svc_encode_result_payload(rqstp, head->iov_len, resp->count)) 57476e5492bSChuck Lever return 0; 5751da177e4SLinus Torvalds return 1; 5761da177e4SLinus Torvalds } 5771da177e4SLinus Torvalds 5781da177e4SLinus Torvalds int 57963f8de37SChristoph Hellwig nfssvc_encode_readdirres(struct svc_rqst *rqstp, __be32 *p) 5801da177e4SLinus Torvalds { 58163f8de37SChristoph Hellwig struct nfsd_readdirres *resp = rqstp->rq_resp; 58263f8de37SChristoph Hellwig 583cc028a10SChuck Lever *p++ = resp->status; 584f0af2210SChuck Lever if (resp->status != nfs_ok) 585f0af2210SChuck Lever return xdr_ressize_check(rqstp, p); 586f0af2210SChuck Lever 5871da177e4SLinus Torvalds xdr_ressize_check(rqstp, p); 5881da177e4SLinus Torvalds p = resp->buffer; 5891da177e4SLinus Torvalds *p++ = 0; /* no more entries */ 5901da177e4SLinus Torvalds *p++ = htonl((resp->common.err == nfserr_eof)); 5911da177e4SLinus Torvalds rqstp->rq_res.page_len = (((unsigned long)p-1) & ~PAGE_MASK)+1; 5921da177e4SLinus Torvalds 5931da177e4SLinus Torvalds return 1; 5941da177e4SLinus Torvalds } 5951da177e4SLinus Torvalds 5961da177e4SLinus Torvalds int 59763f8de37SChristoph Hellwig nfssvc_encode_statfsres(struct svc_rqst *rqstp, __be32 *p) 5981da177e4SLinus Torvalds { 59963f8de37SChristoph Hellwig struct nfsd_statfsres *resp = rqstp->rq_resp; 6001da177e4SLinus Torvalds struct kstatfs *stat = &resp->stats; 6011da177e4SLinus Torvalds 602cc028a10SChuck Lever *p++ = resp->status; 603f0af2210SChuck Lever if (resp->status != nfs_ok) 604f0af2210SChuck Lever return xdr_ressize_check(rqstp, p); 605f0af2210SChuck Lever 6067adae489SGreg Banks *p++ = htonl(NFSSVC_MAXBLKSIZE_V2); /* max transfer size */ 6071da177e4SLinus Torvalds *p++ = htonl(stat->f_bsize); 6081da177e4SLinus Torvalds *p++ = htonl(stat->f_blocks); 6091da177e4SLinus Torvalds *p++ = htonl(stat->f_bfree); 6101da177e4SLinus Torvalds *p++ = htonl(stat->f_bavail); 6111da177e4SLinus Torvalds return xdr_ressize_check(rqstp, p); 6121da177e4SLinus Torvalds } 6131da177e4SLinus Torvalds 6141da177e4SLinus Torvalds int 615a0ad13efSNeilBrown nfssvc_encode_entry(void *ccdv, const char *name, 616a0ad13efSNeilBrown int namlen, loff_t offset, u64 ino, unsigned int d_type) 6171da177e4SLinus Torvalds { 618a0ad13efSNeilBrown struct readdir_cd *ccd = ccdv; 6191da177e4SLinus Torvalds struct nfsd_readdirres *cd = container_of(ccd, struct nfsd_readdirres, common); 620131a21c2SAl Viro __be32 *p = cd->buffer; 6211da177e4SLinus Torvalds int buflen, slen; 6221da177e4SLinus Torvalds 6231da177e4SLinus Torvalds /* 6241da177e4SLinus Torvalds dprintk("nfsd: entry(%.*s off %ld ino %ld)\n", 6251da177e4SLinus Torvalds namlen, name, offset, ino); 6261da177e4SLinus Torvalds */ 6271da177e4SLinus Torvalds 6281da177e4SLinus Torvalds if (offset > ~((u32) 0)) { 6291da177e4SLinus Torvalds cd->common.err = nfserr_fbig; 6301da177e4SLinus Torvalds return -EINVAL; 6311da177e4SLinus Torvalds } 6321da177e4SLinus Torvalds if (cd->offset) 6331da177e4SLinus Torvalds *cd->offset = htonl(offset); 6341da177e4SLinus Torvalds 6353c7aa15dSKinglong Mee /* truncate filename */ 6363c7aa15dSKinglong Mee namlen = min(namlen, NFS2_MAXNAMLEN); 6371da177e4SLinus Torvalds slen = XDR_QUADLEN(namlen); 6383c7aa15dSKinglong Mee 6391da177e4SLinus Torvalds if ((buflen = cd->buflen - slen - 4) < 0) { 6401da177e4SLinus Torvalds cd->common.err = nfserr_toosmall; 6411da177e4SLinus Torvalds return -EINVAL; 6421da177e4SLinus Torvalds } 64340ee5dc6SPeter Staubach if (ino > ~((u32) 0)) { 64440ee5dc6SPeter Staubach cd->common.err = nfserr_fbig; 64540ee5dc6SPeter Staubach return -EINVAL; 64640ee5dc6SPeter Staubach } 6471da177e4SLinus Torvalds *p++ = xdr_one; /* mark entry present */ 6481da177e4SLinus Torvalds *p++ = htonl((u32) ino); /* file id */ 6491da177e4SLinus Torvalds p = xdr_encode_array(p, name, namlen);/* name length & name */ 6501da177e4SLinus Torvalds cd->offset = p; /* remember pointer */ 651131a21c2SAl Viro *p++ = htonl(~0U); /* offset of next entry */ 6521da177e4SLinus Torvalds 6531da177e4SLinus Torvalds cd->buflen = buflen; 6541da177e4SLinus Torvalds cd->buffer = p; 6551da177e4SLinus Torvalds cd->common.err = nfs_ok; 6561da177e4SLinus Torvalds return 0; 6571da177e4SLinus Torvalds } 6581da177e4SLinus Torvalds 6591da177e4SLinus Torvalds /* 6601da177e4SLinus Torvalds * XDR release functions 6611da177e4SLinus Torvalds */ 6621841b9b6SChuck Lever void nfssvc_release_attrstat(struct svc_rqst *rqstp) 6631da177e4SLinus Torvalds { 6641841b9b6SChuck Lever struct nfsd_attrstat *resp = rqstp->rq_resp; 6651841b9b6SChuck Lever 6661841b9b6SChuck Lever fh_put(&resp->fh); 6671841b9b6SChuck Lever } 6681841b9b6SChuck Lever 6691841b9b6SChuck Lever void nfssvc_release_diropres(struct svc_rqst *rqstp) 6701841b9b6SChuck Lever { 6711841b9b6SChuck Lever struct nfsd_diropres *resp = rqstp->rq_resp; 6721841b9b6SChuck Lever 6731841b9b6SChuck Lever fh_put(&resp->fh); 6741841b9b6SChuck Lever } 6751841b9b6SChuck Lever 6761841b9b6SChuck Lever void nfssvc_release_readres(struct svc_rqst *rqstp) 6771841b9b6SChuck Lever { 6781841b9b6SChuck Lever struct nfsd_readres *resp = rqstp->rq_resp; 6798537488bSChristoph Hellwig 6801da177e4SLinus Torvalds fh_put(&resp->fh); 6811da177e4SLinus Torvalds } 682