1139823Simp/*- 21541Srgrimes * Copyright (c) 1989, 1993 31541Srgrimes * The Regents of the University of California. All rights reserved. 41541Srgrimes * 51541Srgrimes * This code is derived from software contributed to Berkeley by 61541Srgrimes * Rick Macklem at The University of Guelph. 71541Srgrimes * 81541Srgrimes * Redistribution and use in source and binary forms, with or without 91541Srgrimes * modification, are permitted provided that the following conditions 101541Srgrimes * are met: 111541Srgrimes * 1. Redistributions of source code must retain the above copyright 121541Srgrimes * notice, this list of conditions and the following disclaimer. 131541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141541Srgrimes * notice, this list of conditions and the following disclaimer in the 151541Srgrimes * documentation and/or other materials provided with the distribution. 161541Srgrimes * 4. Neither the name of the University nor the names of its contributors 171541Srgrimes * may be used to endorse or promote products derived from this software 181541Srgrimes * without specific prior written permission. 191541Srgrimes * 201541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301541Srgrimes * SUCH DAMAGE. 311541Srgrimes * 3236503Speter * @(#)nfs_subs.c 8.8 (Berkeley) 5/22/95 331541Srgrimes */ 341541Srgrimes 3583651Speter#include <sys/cdefs.h> 3683651Speter__FBSDID("$FreeBSD$"); 3783651Speter 381541Srgrimes/* 391541Srgrimes * These functions support the macros and help fiddle mbuf chains for 40203731Smarius * the nfs op functions. They do things like create the rpc header and 411541Srgrimes * copy data between mbuf chains and uio lists. 421541Srgrimes */ 4383651Speter 441541Srgrimes#include <sys/param.h> 4548274Speter#include <sys/systm.h> 4648274Speter#include <sys/kernel.h> 4760041Sphk#include <sys/bio.h> 4831886Sbde#include <sys/buf.h> 491541Srgrimes#include <sys/proc.h> 501541Srgrimes#include <sys/mount.h> 511541Srgrimes#include <sys/vnode.h> 521541Srgrimes#include <sys/namei.h> 531541Srgrimes#include <sys/mbuf.h> 541541Srgrimes#include <sys/socket.h> 551541Srgrimes#include <sys/stat.h> 569336Sdfr#include <sys/malloc.h> 57203968Smarius#include <sys/module.h> 582997Swollman#include <sys/sysent.h> 592997Swollman#include <sys/syscall.h> 60203732Smarius#include <sys/sysctl.h> 611541Srgrimes 623305Sphk#include <vm/vm.h> 6312662Sdg#include <vm/vm_object.h> 6412662Sdg#include <vm/vm_extern.h> 653305Sphk 669336Sdfr#include <nfs/nfsproto.h> 6783651Speter#include <nfsserver/nfs.h> 681541Srgrimes#include <nfs/xdr_subs.h> 6983651Speter#include <nfs/nfs_common.h> 701541Srgrimes 711541Srgrimes#include <netinet/in.h> 721541Srgrimes 7312911Sphkenum vtype nv3tov_type[8]= { 7412911Sphk VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO 7512911Sphk}; 7683651Speternfstype nfsv3_type[9] = { 7783651Speter NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK, NFFIFO, NFNON 789336Sdfr}; 799336Sdfr 80203731Smariusstatic void *nfsm_dissect_xx_sub(int s, struct mbuf **md, caddr_t *dpos, 81203731Smarius int how); 82138463Sps 83203968SmariusSYSCTL_NODE(_vfs, OID_AUTO, nfs_common, CTLFLAG_RD, 0, "NFS common support"); 84203732Smarius 85203732Smariusstatic int nfs_realign_test; 86203968SmariusSYSCTL_INT(_vfs_nfs_common, OID_AUTO, realign_test, CTLFLAG_RD, 87203968Smarius &nfs_realign_test, 0, "Number of realign tests done"); 88203732Smarius 89203732Smariusstatic int nfs_realign_count; 90203968SmariusSYSCTL_INT(_vfs_nfs_common, OID_AUTO, realign_count, CTLFLAG_RD, 91203968Smarius &nfs_realign_count, 0, "Number of mbuf realignments done"); 92203732Smarius 931541Srgrimes/* 941541Srgrimes * copies mbuf chain to the uio scatter/gather list 951541Srgrimes */ 961549Srgrimesint 9783651Speternfsm_mbuftouio(struct mbuf **mrep, struct uio *uiop, int siz, caddr_t *dpos) 981541Srgrimes{ 9983651Speter char *mbufcp, *uiocp; 10083651Speter int xfer, left, len; 10183651Speter struct mbuf *mp; 1021541Srgrimes long uiosiz, rem; 1031541Srgrimes int error = 0; 1041541Srgrimes 1051541Srgrimes mp = *mrep; 1061541Srgrimes mbufcp = *dpos; 1071541Srgrimes len = mtod(mp, caddr_t)+mp->m_len-mbufcp; 1081541Srgrimes rem = nfsm_rndup(siz)-siz; 1091541Srgrimes while (siz > 0) { 1101541Srgrimes if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL) 1111541Srgrimes return (EFBIG); 1121541Srgrimes left = uiop->uio_iov->iov_len; 1131541Srgrimes uiocp = uiop->uio_iov->iov_base; 1141541Srgrimes if (left > siz) 1151541Srgrimes left = siz; 1161541Srgrimes uiosiz = left; 1171541Srgrimes while (left > 0) { 1181541Srgrimes while (len == 0) { 1191541Srgrimes mp = mp->m_next; 1201541Srgrimes if (mp == NULL) 1211541Srgrimes return (EBADRPC); 1221541Srgrimes mbufcp = mtod(mp, caddr_t); 1231541Srgrimes len = mp->m_len; 1241541Srgrimes } 1251541Srgrimes xfer = (left > len) ? len : left; 1261541Srgrimes#ifdef notdef 1271541Srgrimes /* Not Yet.. */ 1281541Srgrimes if (uiop->uio_iov->iov_op != NULL) 1291541Srgrimes (*(uiop->uio_iov->iov_op)) 1301541Srgrimes (mbufcp, uiocp, xfer); 1311541Srgrimes else 1321541Srgrimes#endif 133195631Smarcel if (uiop->uio_segflg == UIO_SYSSPACE) 1341541Srgrimes bcopy(mbufcp, uiocp, xfer); 135195631Smarcel else 1361541Srgrimes copyout(mbufcp, uiocp, xfer); 1371541Srgrimes left -= xfer; 1381541Srgrimes len -= xfer; 1391541Srgrimes mbufcp += xfer; 1401541Srgrimes uiocp += xfer; 1411541Srgrimes uiop->uio_offset += xfer; 1421541Srgrimes uiop->uio_resid -= xfer; 1431541Srgrimes } 1441541Srgrimes if (uiop->uio_iov->iov_len <= siz) { 1451541Srgrimes uiop->uio_iovcnt--; 1461541Srgrimes uiop->uio_iov++; 1471541Srgrimes } else { 148104908Smike uiop->uio_iov->iov_base = 149104908Smike (char *)uiop->uio_iov->iov_base + uiosiz; 1501541Srgrimes uiop->uio_iov->iov_len -= uiosiz; 1511541Srgrimes } 1521541Srgrimes siz -= uiosiz; 1531541Srgrimes } 1541541Srgrimes *dpos = mbufcp; 1551541Srgrimes *mrep = mp; 1561541Srgrimes if (rem > 0) { 1571541Srgrimes if (len < rem) 1581541Srgrimes error = nfs_adv(mrep, dpos, rem, len); 1591541Srgrimes else 1601541Srgrimes *dpos += rem; 1611541Srgrimes } 1621541Srgrimes return (error); 1631541Srgrimes} 1641541Srgrimes 1651541Srgrimes/* 1661541Srgrimes * Help break down an mbuf chain by setting the first siz bytes contiguous 1671541Srgrimes * pointed to by returned val. 16884057Speter * This is used by the macros nfsm_dissect for tough 1691541Srgrimes * cases. (The macros use the vars. dpos and dpos2) 1701541Srgrimes */ 17184057Spetervoid * 172138463Spsnfsm_disct(struct mbuf **mdp, caddr_t *dposp, int siz, int left, int how) 1731541Srgrimes{ 17483651Speter struct mbuf *mp, *mp2; 17583651Speter int siz2, xfer; 176148008Sps caddr_t ptr, npos = NULL; 17784057Speter void *ret; 1781541Srgrimes 1791541Srgrimes mp = *mdp; 1801541Srgrimes while (left == 0) { 1811541Srgrimes *mdp = mp = mp->m_next; 1821541Srgrimes if (mp == NULL) 183203731Smarius return (NULL); 1841541Srgrimes left = mp->m_len; 1851541Srgrimes *dposp = mtod(mp, caddr_t); 1861541Srgrimes } 1871541Srgrimes if (left >= siz) { 18884057Speter ret = *dposp; 1891541Srgrimes *dposp += siz; 1901541Srgrimes } else if (mp->m_next == NULL) { 191203731Smarius return (NULL); 1921541Srgrimes } else if (siz > MHLEN) { 1931541Srgrimes panic("nfs S too big"); 1941541Srgrimes } else { 195248318Sglebius mp2 = m_get(how, MT_DATA); 196138463Sps if (mp2 == NULL) 197203731Smarius return (NULL); 198148008Sps mp2->m_len = siz; 1991541Srgrimes mp2->m_next = mp->m_next; 2001541Srgrimes mp->m_next = mp2; 2011541Srgrimes mp->m_len -= left; 2021541Srgrimes mp = mp2; 20384057Speter ptr = mtod(mp, caddr_t); 20484057Speter ret = ptr; 20583366Sjulian bcopy(*dposp, ptr, left); /* Copy what was left */ 2061541Srgrimes siz2 = siz-left; 20783366Sjulian ptr += left; 2081541Srgrimes mp2 = mp->m_next; 209148008Sps npos = mtod(mp2, caddr_t); 2101541Srgrimes /* Loop around copying up the siz2 bytes */ 2111541Srgrimes while (siz2 > 0) { 2121541Srgrimes if (mp2 == NULL) 213203731Smarius return (NULL); 2141541Srgrimes xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2; 2151541Srgrimes if (xfer > 0) { 21683366Sjulian bcopy(mtod(mp2, caddr_t), ptr, xfer); 21784079Speter mp2->m_data += xfer; 2181541Srgrimes mp2->m_len -= xfer; 21983366Sjulian ptr += xfer; 2201541Srgrimes siz2 -= xfer; 2211541Srgrimes } 222148008Sps if (siz2 > 0) { 2231541Srgrimes mp2 = mp2->m_next; 224148008Sps if (mp2 != NULL) 225148008Sps npos = mtod(mp2, caddr_t); 226148008Sps } 2271541Srgrimes } 2281541Srgrimes *mdp = mp2; 2291541Srgrimes *dposp = mtod(mp2, caddr_t); 230148008Sps if (!nfsm_aligned(*dposp, u_int32_t)) { 231148008Sps bcopy(*dposp, npos, mp2->m_len); 232148008Sps mp2->m_data = npos; 233148008Sps *dposp = npos; 234148008Sps } 2351541Srgrimes } 236203731Smarius return (ret); 2371541Srgrimes} 2381541Srgrimes 2391541Srgrimes/* 2401541Srgrimes * Advance the position in the mbuf chain. 2411541Srgrimes */ 2421549Srgrimesint 24383651Speternfs_adv(struct mbuf **mdp, caddr_t *dposp, int offs, int left) 2441541Srgrimes{ 24583651Speter struct mbuf *m; 24683651Speter int s; 2471541Srgrimes 2481541Srgrimes m = *mdp; 2491541Srgrimes s = left; 2501541Srgrimes while (s < offs) { 2511541Srgrimes offs -= s; 2521541Srgrimes m = m->m_next; 2531541Srgrimes if (m == NULL) 2541541Srgrimes return (EBADRPC); 2551541Srgrimes s = m->m_len; 2561541Srgrimes } 2571541Srgrimes *mdp = m; 2581541Srgrimes *dposp = mtod(m, caddr_t)+offs; 2591541Srgrimes return (0); 2601541Srgrimes} 2611541Srgrimes 26284002Spetervoid * 26384002Speternfsm_build_xx(int s, struct mbuf **mb, caddr_t *bpos) 2641541Srgrimes{ 26583651Speter struct mbuf *mb2; 26684002Speter void *ret; 2671541Srgrimes 26883651Speter if (s > M_TRAILINGSPACE(*mb)) { 269248318Sglebius mb2 = m_get(M_WAITOK, MT_DATA); 27083651Speter if (s > MLEN) 27183651Speter panic("build > MLEN"); 27283651Speter (*mb)->m_next = mb2; 27383651Speter *mb = mb2; 27483651Speter (*mb)->m_len = 0; 27583651Speter *bpos = mtod(*mb, caddr_t); 2761541Srgrimes } 27784002Speter ret = *bpos; 27883651Speter (*mb)->m_len += s; 27983651Speter *bpos += s; 280203731Smarius return (ret); 2811541Srgrimes} 2821541Srgrimes 28384057Spetervoid * 28484057Speternfsm_dissect_xx(int s, struct mbuf **md, caddr_t *dpos) 2851541Srgrimes{ 286203731Smarius 287243882Sglebius return (nfsm_dissect_xx_sub(s, md, dpos, M_WAITOK)); 288138463Sps} 289138463Sps 290138463Spsvoid * 291138463Spsnfsm_dissect_xx_nonblock(int s, struct mbuf **md, caddr_t *dpos) 292138463Sps{ 293203731Smarius 294243882Sglebius return (nfsm_dissect_xx_sub(s, md, dpos, M_NOWAIT)); 295138463Sps} 296138463Sps 297138463Spsstatic void * 298138463Spsnfsm_dissect_xx_sub(int s, struct mbuf **md, caddr_t *dpos, int how) 299138463Sps{ 30083651Speter int t1; 30183651Speter char *cp2; 30284057Speter void *ret; 3031541Srgrimes 30483651Speter t1 = mtod(*md, caddr_t) + (*md)->m_len - *dpos; 30583651Speter if (t1 >= s) { 30684057Speter ret = *dpos; 30783651Speter *dpos += s; 308203731Smarius return (ret); 30927446Sdfr } 310203731Smarius cp2 = nfsm_disct(md, dpos, s, t1, how); 311203731Smarius return (cp2); 3121541Srgrimes} 3131541Srgrimes 31427446Sdfrint 31588091Siedowsenfsm_strsiz_xx(int *s, int m, struct mbuf **mb, caddr_t *bpos) 31627446Sdfr{ 31788091Siedowse u_int32_t *tl; 31827446Sdfr 31988091Siedowse tl = nfsm_dissect_xx(NFSX_UNSIGNED, mb, bpos); 32088091Siedowse if (tl == NULL) 321203731Smarius return (EBADRPC); 32288091Siedowse *s = fxdr_unsigned(int32_t, *tl); 32383651Speter if (*s > m) 324203731Smarius return (EBADRPC); 325203731Smarius return (0); 32627446Sdfr} 3271541Srgrimes 3285455Sdgint 32988091Siedowsenfsm_adv_xx(int s, struct mbuf **md, caddr_t *dpos) 3309336Sdfr{ 33183651Speter int t1; 3329336Sdfr 33383651Speter t1 = mtod(*md, caddr_t) + (*md)->m_len - *dpos; 33488731Siedowse if (t1 >= s) { 33583651Speter *dpos += s; 336203731Smarius return (0); 3379336Sdfr } 33883651Speter t1 = nfs_adv(md, dpos, s, t1); 33983651Speter if (t1) 340203731Smarius return (t1); 341203731Smarius return (0); 3429336Sdfr} 343203732Smarius 344203732Smarius/* 345203732Smarius * Check for badly aligned mbuf data and realign by copying the unaligned 346203732Smarius * portion of the data into a new mbuf chain and freeing the portions of the 347203732Smarius * old chain that were replaced. 348203732Smarius * 349203732Smarius * We cannot simply realign the data within the existing mbuf chain because 350203732Smarius * the underlying buffers may contain other rpc commands and we cannot afford 351203732Smarius * to overwrite them. 352203732Smarius * 353203732Smarius * We would prefer to avoid this situation entirely. The situation does not 354203732Smarius * occur with NFS/UDP and is supposed to only occassionally occur with TCP. 355221973Srmacklem * Use vfs.nfs_common.realign_count and realign_test to check this. 356203732Smarius */ 357203732Smariusint 358203732Smariusnfs_realign(struct mbuf **pm, int how) 359203732Smarius{ 360203732Smarius struct mbuf *m, *n; 361203732Smarius int off; 362203732Smarius 363203732Smarius ++nfs_realign_test; 364203732Smarius while ((m = *pm) != NULL) { 365203732Smarius if (!nfsm_aligned(m->m_len, u_int32_t) || 366203732Smarius !nfsm_aligned(mtod(m, intptr_t), u_int32_t)) { 367203732Smarius /* 368203732Smarius * NB: we can't depend on m_pkthdr.len to help us 369203732Smarius * decide what to do here. May not be worth doing 370203732Smarius * the m_length calculation as m_copyback will 371203732Smarius * expand the mbuf chain below as needed. 372203732Smarius */ 373203732Smarius if (m_length(m, NULL) >= MINCLSIZE) { 374203732Smarius /* NB: m_copyback handles space > MCLBYTES */ 375203732Smarius n = m_getcl(how, MT_DATA, 0); 376203732Smarius } else 377203732Smarius n = m_get(how, MT_DATA); 378203732Smarius if (n == NULL) 379203732Smarius return (ENOMEM); 380203732Smarius /* 381203732Smarius * Align the remainder of the mbuf chain. 382203732Smarius */ 383203732Smarius n->m_len = 0; 384203732Smarius off = 0; 385203732Smarius while (m != NULL) { 386203732Smarius m_copyback(n, off, m->m_len, mtod(m, caddr_t)); 387203732Smarius off += m->m_len; 388203732Smarius m = m->m_next; 389203732Smarius } 390203732Smarius m_freem(*pm); 391203732Smarius *pm = n; 392203732Smarius ++nfs_realign_count; 393203732Smarius break; 394203732Smarius } 395203732Smarius pm = &m->m_next; 396203732Smarius } 397203732Smarius return (0); 398203732Smarius} 399203968Smarius 400203968Smariusstatic moduledata_t nfs_common_mod = { 401203968Smarius "nfs_common", 402203968Smarius NULL, 403203968Smarius NULL 404203968Smarius}; 405203968Smarius 406203968SmariusDECLARE_MODULE(nfs_common, nfs_common_mod, SI_SUB_VFS, SI_ORDER_ANY); 407203968SmariusMODULE_VERSION(nfs_common, 1); 408