nfs_common.c revision 203732
1/*- 2 * Copyright (c) 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Rick Macklem at The University of Guelph. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 4. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * @(#)nfs_subs.c 8.8 (Berkeley) 5/22/95 33 */ 34 35#include <sys/cdefs.h> 36__FBSDID("$FreeBSD: head/sys/nfs/nfs_common.c 203732 2010-02-09 23:45:14Z marius $"); 37 38/* 39 * These functions support the macros and help fiddle mbuf chains for 40 * the nfs op functions. They do things like create the rpc header and 41 * copy data between mbuf chains and uio lists. 42 */ 43 44#include <sys/param.h> 45#include <sys/systm.h> 46#include <sys/kernel.h> 47#include <sys/bio.h> 48#include <sys/buf.h> 49#include <sys/proc.h> 50#include <sys/mount.h> 51#include <sys/vnode.h> 52#include <sys/namei.h> 53#include <sys/mbuf.h> 54#include <sys/socket.h> 55#include <sys/stat.h> 56#include <sys/malloc.h> 57#include <sys/sysent.h> 58#include <sys/syscall.h> 59#include <sys/sysctl.h> 60 61#include <vm/vm.h> 62#include <vm/vm_object.h> 63#include <vm/vm_extern.h> 64 65#include <nfs/nfsproto.h> 66#include <nfsserver/nfs.h> 67#include <nfs/xdr_subs.h> 68#include <nfs/nfs_common.h> 69 70#include <netinet/in.h> 71 72enum vtype nv3tov_type[8]= { 73 VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO 74}; 75nfstype nfsv3_type[9] = { 76 NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK, NFFIFO, NFNON 77}; 78 79static void *nfsm_dissect_xx_sub(int s, struct mbuf **md, caddr_t *dpos, 80 int how); 81 82SYSCTL_DECL(_vfs_nfs); 83 84static int nfs_realign_test; 85SYSCTL_INT(_vfs_nfs, OID_AUTO, realign_test, CTLFLAG_RD, &nfs_realign_test, 86 0, "Number of realign tests done"); 87 88static int nfs_realign_count; 89SYSCTL_INT(_vfs_nfs, OID_AUTO, realign_count, CTLFLAG_RD, &nfs_realign_count, 90 0, "Number of mbuf realignments done"); 91 92u_quad_t 93nfs_curusec(void) 94{ 95 struct timeval tv; 96 97 getmicrotime(&tv); 98 return ((u_quad_t)tv.tv_sec * 1000000 + (u_quad_t)tv.tv_usec); 99} 100 101/* 102 * copies mbuf chain to the uio scatter/gather list 103 */ 104int 105nfsm_mbuftouio(struct mbuf **mrep, struct uio *uiop, int siz, caddr_t *dpos) 106{ 107 char *mbufcp, *uiocp; 108 int xfer, left, len; 109 struct mbuf *mp; 110 long uiosiz, rem; 111 int error = 0; 112 113 mp = *mrep; 114 mbufcp = *dpos; 115 len = mtod(mp, caddr_t)+mp->m_len-mbufcp; 116 rem = nfsm_rndup(siz)-siz; 117 while (siz > 0) { 118 if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL) 119 return (EFBIG); 120 left = uiop->uio_iov->iov_len; 121 uiocp = uiop->uio_iov->iov_base; 122 if (left > siz) 123 left = siz; 124 uiosiz = left; 125 while (left > 0) { 126 while (len == 0) { 127 mp = mp->m_next; 128 if (mp == NULL) 129 return (EBADRPC); 130 mbufcp = mtod(mp, caddr_t); 131 len = mp->m_len; 132 } 133 xfer = (left > len) ? len : left; 134#ifdef notdef 135 /* Not Yet.. */ 136 if (uiop->uio_iov->iov_op != NULL) 137 (*(uiop->uio_iov->iov_op)) 138 (mbufcp, uiocp, xfer); 139 else 140#endif 141 if (uiop->uio_segflg == UIO_SYSSPACE) 142 bcopy(mbufcp, uiocp, xfer); 143 else 144 copyout(mbufcp, uiocp, xfer); 145 left -= xfer; 146 len -= xfer; 147 mbufcp += xfer; 148 uiocp += xfer; 149 uiop->uio_offset += xfer; 150 uiop->uio_resid -= xfer; 151 } 152 if (uiop->uio_iov->iov_len <= siz) { 153 uiop->uio_iovcnt--; 154 uiop->uio_iov++; 155 } else { 156 uiop->uio_iov->iov_base = 157 (char *)uiop->uio_iov->iov_base + uiosiz; 158 uiop->uio_iov->iov_len -= uiosiz; 159 } 160 siz -= uiosiz; 161 } 162 *dpos = mbufcp; 163 *mrep = mp; 164 if (rem > 0) { 165 if (len < rem) 166 error = nfs_adv(mrep, dpos, rem, len); 167 else 168 *dpos += rem; 169 } 170 return (error); 171} 172 173/* 174 * Help break down an mbuf chain by setting the first siz bytes contiguous 175 * pointed to by returned val. 176 * This is used by the macros nfsm_dissect for tough 177 * cases. (The macros use the vars. dpos and dpos2) 178 */ 179void * 180nfsm_disct(struct mbuf **mdp, caddr_t *dposp, int siz, int left, int how) 181{ 182 struct mbuf *mp, *mp2; 183 int siz2, xfer; 184 caddr_t ptr, npos = NULL; 185 void *ret; 186 187 mp = *mdp; 188 while (left == 0) { 189 *mdp = mp = mp->m_next; 190 if (mp == NULL) 191 return (NULL); 192 left = mp->m_len; 193 *dposp = mtod(mp, caddr_t); 194 } 195 if (left >= siz) { 196 ret = *dposp; 197 *dposp += siz; 198 } else if (mp->m_next == NULL) { 199 return (NULL); 200 } else if (siz > MHLEN) { 201 panic("nfs S too big"); 202 } else { 203 MGET(mp2, how, MT_DATA); 204 if (mp2 == NULL) 205 return (NULL); 206 mp2->m_len = siz; 207 mp2->m_next = mp->m_next; 208 mp->m_next = mp2; 209 mp->m_len -= left; 210 mp = mp2; 211 ptr = mtod(mp, caddr_t); 212 ret = ptr; 213 bcopy(*dposp, ptr, left); /* Copy what was left */ 214 siz2 = siz-left; 215 ptr += left; 216 mp2 = mp->m_next; 217 npos = mtod(mp2, caddr_t); 218 /* Loop around copying up the siz2 bytes */ 219 while (siz2 > 0) { 220 if (mp2 == NULL) 221 return (NULL); 222 xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2; 223 if (xfer > 0) { 224 bcopy(mtod(mp2, caddr_t), ptr, xfer); 225 mp2->m_data += xfer; 226 mp2->m_len -= xfer; 227 ptr += xfer; 228 siz2 -= xfer; 229 } 230 if (siz2 > 0) { 231 mp2 = mp2->m_next; 232 if (mp2 != NULL) 233 npos = mtod(mp2, caddr_t); 234 } 235 } 236 *mdp = mp2; 237 *dposp = mtod(mp2, caddr_t); 238 if (!nfsm_aligned(*dposp, u_int32_t)) { 239 bcopy(*dposp, npos, mp2->m_len); 240 mp2->m_data = npos; 241 *dposp = npos; 242 } 243 } 244 return (ret); 245} 246 247/* 248 * Advance the position in the mbuf chain. 249 */ 250int 251nfs_adv(struct mbuf **mdp, caddr_t *dposp, int offs, int left) 252{ 253 struct mbuf *m; 254 int s; 255 256 m = *mdp; 257 s = left; 258 while (s < offs) { 259 offs -= s; 260 m = m->m_next; 261 if (m == NULL) 262 return (EBADRPC); 263 s = m->m_len; 264 } 265 *mdp = m; 266 *dposp = mtod(m, caddr_t)+offs; 267 return (0); 268} 269 270void * 271nfsm_build_xx(int s, struct mbuf **mb, caddr_t *bpos) 272{ 273 struct mbuf *mb2; 274 void *ret; 275 276 if (s > M_TRAILINGSPACE(*mb)) { 277 MGET(mb2, M_WAIT, MT_DATA); 278 if (s > MLEN) 279 panic("build > MLEN"); 280 (*mb)->m_next = mb2; 281 *mb = mb2; 282 (*mb)->m_len = 0; 283 *bpos = mtod(*mb, caddr_t); 284 } 285 ret = *bpos; 286 (*mb)->m_len += s; 287 *bpos += s; 288 return (ret); 289} 290 291void * 292nfsm_dissect_xx(int s, struct mbuf **md, caddr_t *dpos) 293{ 294 295 return (nfsm_dissect_xx_sub(s, md, dpos, M_WAIT)); 296} 297 298void * 299nfsm_dissect_xx_nonblock(int s, struct mbuf **md, caddr_t *dpos) 300{ 301 302 return (nfsm_dissect_xx_sub(s, md, dpos, M_DONTWAIT)); 303} 304 305static void * 306nfsm_dissect_xx_sub(int s, struct mbuf **md, caddr_t *dpos, int how) 307{ 308 int t1; 309 char *cp2; 310 void *ret; 311 312 t1 = mtod(*md, caddr_t) + (*md)->m_len - *dpos; 313 if (t1 >= s) { 314 ret = *dpos; 315 *dpos += s; 316 return (ret); 317 } 318 cp2 = nfsm_disct(md, dpos, s, t1, how); 319 return (cp2); 320} 321 322int 323nfsm_strsiz_xx(int *s, int m, struct mbuf **mb, caddr_t *bpos) 324{ 325 u_int32_t *tl; 326 327 tl = nfsm_dissect_xx(NFSX_UNSIGNED, mb, bpos); 328 if (tl == NULL) 329 return (EBADRPC); 330 *s = fxdr_unsigned(int32_t, *tl); 331 if (*s > m) 332 return (EBADRPC); 333 return (0); 334} 335 336int 337nfsm_adv_xx(int s, struct mbuf **md, caddr_t *dpos) 338{ 339 int t1; 340 341 t1 = mtod(*md, caddr_t) + (*md)->m_len - *dpos; 342 if (t1 >= s) { 343 *dpos += s; 344 return (0); 345 } 346 t1 = nfs_adv(md, dpos, s, t1); 347 if (t1) 348 return (t1); 349 return (0); 350} 351 352/* 353 * Check for badly aligned mbuf data and realign by copying the unaligned 354 * portion of the data into a new mbuf chain and freeing the portions of the 355 * old chain that were replaced. 356 * 357 * We cannot simply realign the data within the existing mbuf chain because 358 * the underlying buffers may contain other rpc commands and we cannot afford 359 * to overwrite them. 360 * 361 * We would prefer to avoid this situation entirely. The situation does not 362 * occur with NFS/UDP and is supposed to only occassionally occur with TCP. 363 * Use vfs.nfs.realign_count and realign_test to check this. 364 */ 365int 366nfs_realign(struct mbuf **pm, int how) 367{ 368 struct mbuf *m, *n; 369 int off; 370 371 ++nfs_realign_test; 372 while ((m = *pm) != NULL) { 373 if (!nfsm_aligned(m->m_len, u_int32_t) || 374 !nfsm_aligned(mtod(m, intptr_t), u_int32_t)) { 375 /* 376 * NB: we can't depend on m_pkthdr.len to help us 377 * decide what to do here. May not be worth doing 378 * the m_length calculation as m_copyback will 379 * expand the mbuf chain below as needed. 380 */ 381 if (m_length(m, NULL) >= MINCLSIZE) { 382 /* NB: m_copyback handles space > MCLBYTES */ 383 n = m_getcl(how, MT_DATA, 0); 384 } else 385 n = m_get(how, MT_DATA); 386 if (n == NULL) 387 return (ENOMEM); 388 /* 389 * Align the remainder of the mbuf chain. 390 */ 391 n->m_len = 0; 392 off = 0; 393 while (m != NULL) { 394 m_copyback(n, off, m->m_len, mtod(m, caddr_t)); 395 off += m->m_len; 396 m = m->m_next; 397 } 398 m_freem(*pm); 399 *pm = n; 400 ++nfs_realign_count; 401 break; 402 } 403 pm = &m->m_next; 404 } 405 return (0); 406} 407