1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
24 */
25
26 /*
27 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
29 */
30
31 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
32 /* All Rights Reserved */
33
34 /*
35 * Portions of this source code were derived from Berkeley 4.3 BSD
36 * under license from the Regents of the University of California.
37 */
38
39 /*
40 * xdr_mblk.c, XDR implementation on kernel streams mblks.
41 */
42
43 #include <sys/param.h>
44 #include <sys/types.h>
45 #include <sys/systm.h>
46 #include <sys/stream.h>
47 #include <sys/cmn_err.h>
48 #include <sys/strsubr.h>
49 #include <sys/strsun.h>
50 #include <sys/debug.h>
51 #include <sys/sysmacros.h>
52
53 #include <rpc/types.h>
54 #include <rpc/xdr.h>
55
56 static bool_t xdrmblk_getint32(XDR *, int32_t *);
57 static bool_t xdrmblk_putint32(XDR *, int32_t *);
58 static bool_t xdrmblk_getbytes(XDR *, caddr_t, int);
59 static bool_t xdrmblk_putbytes(XDR *, caddr_t, int);
60 static uint_t xdrmblk_getpos(XDR *);
61 static bool_t xdrmblk_setpos(XDR *, uint_t);
62 static rpc_inline_t *xdrmblk_inline(XDR *, int);
63 static void xdrmblk_destroy(XDR *);
64 static bool_t xdrmblk_control(XDR *, int, void *);
65
66 static mblk_t *xdrmblk_alloc(int);
67 static void xdrmblk_skip_fully_read_mblks(XDR *);
68
69 /*
70 * Xdr on mblks operations vector.
71 */
72 struct xdr_ops xdrmblk_ops = {
73 xdrmblk_getbytes,
74 xdrmblk_putbytes,
75 xdrmblk_getpos,
76 xdrmblk_setpos,
77 xdrmblk_inline,
78 xdrmblk_destroy,
79 xdrmblk_control,
80 xdrmblk_getint32,
81 xdrmblk_putint32
82 };
83
84 /*
85 * The xdrmblk_params structure holds the internal data for the XDR stream.
86 * The x_private member of the XDR points to this structure. The
87 * xdrmblk_params structure is dynamically allocated in xdrmblk_init() and
88 * freed in xdrmblk_destroy().
89 *
90 * The apos and rpos members of the xdrmblk_params structure are used to
91 * implement xdrmblk_getpos() and xdrmblk_setpos().
92 *
93 * In addition to the xdrmblk_params structure we store some additional
94 * internal data directly in the XDR stream structure:
95 *
96 * x_base A pointer to the current mblk (that one we are currently
97 * working with).
98 * x_handy The number of available bytes (either for read or for write) in
99 * the current mblk.
100 */
101 struct xdrmblk_params {
102 int sz;
103 uint_t apos; /* Absolute position of the current mblk */
104 uint_t rpos; /* Relative position in the current mblk */
105 };
106
107 /*
108 * Initialize xdr stream.
109 */
110 void
xdrmblk_init(XDR * xdrs,mblk_t * m,enum xdr_op op,int sz)111 xdrmblk_init(XDR *xdrs, mblk_t *m, enum xdr_op op, int sz)
112 {
113 struct xdrmblk_params *p;
114
115 xdrs->x_op = op;
116 xdrs->x_ops = &xdrmblk_ops;
117 xdrs->x_base = (caddr_t)m;
118 xdrs->x_public = NULL;
119 p = kmem_alloc(sizeof (struct xdrmblk_params), KM_SLEEP);
120 xdrs->x_private = (caddr_t)p;
121
122 p->sz = sz;
123 p->apos = 0;
124 p->rpos = 0;
125
126 if (op == XDR_DECODE) {
127 xdrs->x_handy = (int)MBLKL(m);
128 } else {
129 xdrs->x_handy = (int)MBLKTAIL(m);
130 if (p->sz < sizeof (int32_t))
131 p->sz = sizeof (int32_t);
132 }
133 }
134
135 static void
xdrmblk_destroy(XDR * xdrs)136 xdrmblk_destroy(XDR *xdrs)
137 {
138 kmem_free(xdrs->x_private, sizeof (struct xdrmblk_params));
139 }
140
141 static bool_t
xdrmblk_getint32(XDR * xdrs,int32_t * int32p)142 xdrmblk_getint32(XDR *xdrs, int32_t *int32p)
143 {
144 mblk_t *m;
145 struct xdrmblk_params *p;
146
147 xdrmblk_skip_fully_read_mblks(xdrs);
148
149 /* LINTED pointer alignment */
150 m = (mblk_t *)xdrs->x_base;
151 if (m == NULL)
152 return (FALSE);
153
154 p = (struct xdrmblk_params *)xdrs->x_private;
155
156 /*
157 * If the pointer is not aligned or there is not
158 * enough bytes, pullupmsg to get enough bytes and
159 * align the mblk.
160 */
161 if (!IS_P2ALIGNED(m->b_rptr, sizeof (int32_t)) ||
162 xdrs->x_handy < sizeof (int32_t)) {
163 while (!pullupmsg(m, sizeof (int32_t))) {
164 /*
165 * Could have failed due to not
166 * enough data or an allocb failure.
167 */
168 if (xmsgsize(m) < sizeof (int32_t))
169 return (FALSE);
170 delay(hz);
171 }
172 p->apos += p->rpos;
173 p->rpos = 0;
174 xdrs->x_handy = (int)MBLKL(m);
175 }
176
177 /* LINTED pointer alignment */
178 *int32p = ntohl(*((int32_t *)(m->b_rptr)));
179 m->b_rptr += sizeof (int32_t);
180 xdrs->x_handy -= sizeof (int32_t);
181 p->rpos += sizeof (int32_t);
182
183 return (TRUE);
184 }
185
186 static bool_t
xdrmblk_putint32(XDR * xdrs,int32_t * int32p)187 xdrmblk_putint32(XDR *xdrs, int32_t *int32p)
188 {
189 mblk_t *m;
190 struct xdrmblk_params *p;
191
192 /* LINTED pointer alignment */
193 m = (mblk_t *)xdrs->x_base;
194 if (m == NULL)
195 return (FALSE);
196
197 p = (struct xdrmblk_params *)xdrs->x_private;
198
199 while (!IS_P2ALIGNED(m->b_wptr, sizeof (int32_t)) ||
200 xdrs->x_handy < sizeof (int32_t)) {
201 if (m->b_cont == NULL) {
202 ASSERT(p->sz >= sizeof (int32_t));
203 m->b_cont = xdrmblk_alloc(p->sz);
204 }
205 m = m->b_cont;
206 xdrs->x_base = (caddr_t)m;
207 p->apos += p->rpos;
208 p->rpos = 0;
209 if (m == NULL) {
210 xdrs->x_handy = 0;
211 return (FALSE);
212 }
213 xdrs->x_handy = (int)MBLKTAIL(m);
214 ASSERT(m->b_rptr == m->b_wptr);
215 ASSERT(m->b_rptr >= m->b_datap->db_base);
216 ASSERT(m->b_rptr < m->b_datap->db_lim);
217 }
218 /* LINTED pointer alignment */
219 *(int32_t *)m->b_wptr = htonl(*int32p);
220 m->b_wptr += sizeof (int32_t);
221 xdrs->x_handy -= sizeof (int32_t);
222 p->rpos += sizeof (int32_t);
223 ASSERT(m->b_wptr <= m->b_datap->db_lim);
224 return (TRUE);
225 }
226
227 /*
228 * We pick 16 as a compromise threshold for most architectures.
229 */
230 #define XDRMBLK_BCOPY_LIMIT 16
231
232 static bool_t
xdrmblk_getbytes(XDR * xdrs,caddr_t addr,int len)233 xdrmblk_getbytes(XDR *xdrs, caddr_t addr, int len)
234 {
235 mblk_t *m;
236 struct xdrmblk_params *p;
237 int i;
238
239 /* LINTED pointer alignment */
240 m = (mblk_t *)xdrs->x_base;
241 if (m == NULL)
242 return (FALSE);
243
244 p = (struct xdrmblk_params *)xdrs->x_private;
245
246 /*
247 * Performance tweak: converted explicit bcopy()
248 * call to simple in-line. This function is called
249 * to process things like readdir reply filenames
250 * which are small strings--typically 12 bytes or less.
251 * Overhead of calling bcopy() is obnoxious for such
252 * small copies.
253 */
254 while (xdrs->x_handy < len) {
255 if (xdrs->x_handy > 0) {
256 if (xdrs->x_handy < XDRMBLK_BCOPY_LIMIT) {
257 for (i = 0; i < xdrs->x_handy; i++)
258 *addr++ = *m->b_rptr++;
259 } else {
260 bcopy(m->b_rptr, addr, xdrs->x_handy);
261 m->b_rptr += xdrs->x_handy;
262 addr += xdrs->x_handy;
263 }
264 len -= xdrs->x_handy;
265 p->rpos += xdrs->x_handy;
266 }
267 m = m->b_cont;
268 xdrs->x_base = (caddr_t)m;
269 p->apos += p->rpos;
270 p->rpos = 0;
271 if (m == NULL) {
272 xdrs->x_handy = 0;
273 return (FALSE);
274 }
275 xdrs->x_handy = (int)MBLKL(m);
276 }
277
278 xdrs->x_handy -= len;
279 p->rpos += len;
280
281 if (len < XDRMBLK_BCOPY_LIMIT) {
282 for (i = 0; i < len; i++)
283 *addr++ = *m->b_rptr++;
284 } else {
285 bcopy(m->b_rptr, addr, len);
286 m->b_rptr += len;
287 }
288
289 return (TRUE);
290 }
291
292 /*
293 * Sort of like getbytes except that instead of getting bytes we return the
294 * mblk chain which contains the data. If the data ends in the middle of
295 * an mblk, the mblk is dup'd and split, so that the data will end on an
296 * mblk. Note that it is up to the caller to keep track of the data length
297 * and not walk too far down the mblk chain.
298 */
299
300 bool_t
xdrmblk_getmblk(XDR * xdrs,mblk_t ** mm,uint_t * lenp)301 xdrmblk_getmblk(XDR *xdrs, mblk_t **mm, uint_t *lenp)
302 {
303 mblk_t *m, *nextm;
304 struct xdrmblk_params *p;
305 int len;
306 uint32_t llen;
307
308 if (!xdrmblk_getint32(xdrs, (int32_t *)&llen))
309 return (FALSE);
310
311 *lenp = llen;
312 /* LINTED pointer alignment */
313 m = (mblk_t *)xdrs->x_base;
314 *mm = m;
315
316 /*
317 * Walk the mblk chain until we get to the end or we've gathered
318 * enough data.
319 */
320 len = 0;
321 llen = roundup(llen, BYTES_PER_XDR_UNIT);
322 while (m != NULL && len + (int)MBLKL(m) <= llen) {
323 len += (int)MBLKL(m);
324 m = m->b_cont;
325 }
326 if (len < llen) {
327 if (m == NULL) {
328 return (FALSE);
329 } else {
330 int tail_bytes = llen - len;
331
332 /*
333 * Split the mblk with the last chunk of data and
334 * insert it into the chain. The new mblk goes
335 * after the existing one so that it will get freed
336 * properly.
337 */
338 nextm = dupb(m);
339 if (nextm == NULL)
340 return (FALSE);
341 nextm->b_cont = m->b_cont;
342 m->b_cont = nextm;
343 m->b_wptr = m->b_rptr + tail_bytes;
344 nextm->b_rptr += tail_bytes;
345 ASSERT(nextm->b_rptr != nextm->b_wptr);
346
347 m = nextm; /* for x_base */
348 }
349 }
350 xdrs->x_base = (caddr_t)m;
351 xdrs->x_handy = m != NULL ? MBLKL(m) : 0;
352
353 p = (struct xdrmblk_params *)xdrs->x_private;
354 p->apos += p->rpos + llen;
355 p->rpos = 0;
356
357 return (TRUE);
358 }
359
360 static bool_t
xdrmblk_putbytes(XDR * xdrs,caddr_t addr,int len)361 xdrmblk_putbytes(XDR *xdrs, caddr_t addr, int len)
362 {
363 mblk_t *m;
364 struct xdrmblk_params *p;
365 int i;
366
367 /* LINTED pointer alignment */
368 m = (mblk_t *)xdrs->x_base;
369 if (m == NULL)
370 return (FALSE);
371
372 p = (struct xdrmblk_params *)xdrs->x_private;
373
374 /*
375 * Performance tweak: converted explicit bcopy()
376 * call to simple in-line. This function is called
377 * to process things like readdir reply filenames
378 * which are small strings--typically 12 bytes or less.
379 * Overhead of calling bcopy() is obnoxious for such
380 * small copies.
381 */
382 while (xdrs->x_handy < len) {
383 if (xdrs->x_handy > 0) {
384 if (xdrs->x_handy < XDRMBLK_BCOPY_LIMIT) {
385 for (i = 0; i < xdrs->x_handy; i++)
386 *m->b_wptr++ = *addr++;
387 } else {
388 bcopy(addr, m->b_wptr, xdrs->x_handy);
389 m->b_wptr += xdrs->x_handy;
390 addr += xdrs->x_handy;
391 }
392 len -= xdrs->x_handy;
393 p->rpos += xdrs->x_handy;
394 }
395
396 /*
397 * We don't have enough space, so allocate the
398 * amount we need, or sz, whichever is larger.
399 * It is better to let the underlying transport divide
400 * large chunks than to try and guess what is best.
401 */
402 if (m->b_cont == NULL)
403 m->b_cont = xdrmblk_alloc(MAX(len, p->sz));
404
405 m = m->b_cont;
406 xdrs->x_base = (caddr_t)m;
407 p->apos += p->rpos;
408 p->rpos = 0;
409 if (m == NULL) {
410 xdrs->x_handy = 0;
411 return (FALSE);
412 }
413 xdrs->x_handy = (int)MBLKTAIL(m);
414 ASSERT(m->b_rptr == m->b_wptr);
415 ASSERT(m->b_rptr >= m->b_datap->db_base);
416 ASSERT(m->b_rptr < m->b_datap->db_lim);
417 }
418
419 xdrs->x_handy -= len;
420 p->rpos += len;
421
422 if (len < XDRMBLK_BCOPY_LIMIT) {
423 for (i = 0; i < len; i++)
424 *m->b_wptr++ = *addr++;
425 } else {
426 bcopy(addr, m->b_wptr, len);
427 m->b_wptr += len;
428 }
429 ASSERT(m->b_wptr <= m->b_datap->db_lim);
430 return (TRUE);
431 }
432
433 /*
434 * We avoid a copy by merely adding this mblk to the list. The caller is
435 * responsible for allocating and filling in the mblk. If len is
436 * not a multiple of BYTES_PER_XDR_UNIT, the caller has the option
437 * of making the data a BYTES_PER_XDR_UNIT multiple (b_wptr - b_rptr is
438 * a BYTES_PER_XDR_UNIT multiple), but in this case the caller has to ensure
439 * that the filler bytes are initialized to zero.
440 */
441 bool_t
xdrmblk_putmblk(XDR * xdrs,mblk_t * m,uint_t len)442 xdrmblk_putmblk(XDR *xdrs, mblk_t *m, uint_t len)
443 {
444 int32_t llen = (int32_t)len;
445
446 if (!xdrmblk_putint32(xdrs, &llen))
447 return (FALSE);
448
449 return (xdrmblk_putmblk_raw(xdrs, m));
450 }
451
452 /*
453 * The raw version of putmblk does not prepend the added data with the length.
454 */
455 bool_t
xdrmblk_putmblk_raw(XDR * xdrs,mblk_t * m)456 xdrmblk_putmblk_raw(XDR *xdrs, mblk_t *m)
457 {
458 struct xdrmblk_params *p;
459
460 if ((DLEN(m) % BYTES_PER_XDR_UNIT) != 0)
461 return (FALSE);
462
463 p = (struct xdrmblk_params *)xdrs->x_private;
464
465 /* LINTED pointer alignment */
466 ((mblk_t *)xdrs->x_base)->b_cont = m;
467 p->apos += p->rpos;
468
469 /* base points to the last mblk */
470 while (m->b_cont) {
471 p->apos += MBLKL(m);
472 m = m->b_cont;
473 }
474 xdrs->x_base = (caddr_t)m;
475 xdrs->x_handy = 0;
476 p->rpos = MBLKL(m);
477 return (TRUE);
478 }
479
480 static uint_t
xdrmblk_getpos(XDR * xdrs)481 xdrmblk_getpos(XDR *xdrs)
482 {
483 struct xdrmblk_params *p = (struct xdrmblk_params *)xdrs->x_private;
484
485 return (p->apos + p->rpos);
486 }
487
488 static bool_t
xdrmblk_setpos(XDR * xdrs,uint_t pos)489 xdrmblk_setpos(XDR *xdrs, uint_t pos)
490 {
491 mblk_t *m;
492 struct xdrmblk_params *p;
493
494 p = (struct xdrmblk_params *)xdrs->x_private;
495
496 if (pos < p->apos)
497 return (FALSE);
498
499 if (pos > p->apos + p->rpos + xdrs->x_handy)
500 return (FALSE);
501
502 if (pos == p->apos + p->rpos)
503 return (TRUE);
504
505 /* LINTED pointer alignment */
506 m = (mblk_t *)xdrs->x_base;
507 ASSERT(m != NULL);
508
509 if (xdrs->x_op == XDR_DECODE)
510 m->b_rptr = m->b_rptr - p->rpos + (pos - p->apos);
511 else
512 m->b_wptr = m->b_wptr - p->rpos + (pos - p->apos);
513
514 xdrs->x_handy = p->rpos + xdrs->x_handy - (pos - p->apos);
515 p->rpos = pos - p->apos;
516
517 return (TRUE);
518 }
519
520 #ifdef DEBUG
521 static int xdrmblk_inline_hits = 0;
522 static int xdrmblk_inline_misses = 0;
523 static int do_xdrmblk_inline = 1;
524 #endif
525
526 static rpc_inline_t *
xdrmblk_inline(XDR * xdrs,int len)527 xdrmblk_inline(XDR *xdrs, int len)
528 {
529 rpc_inline_t *buf;
530 mblk_t *m;
531 unsigned char **mptr;
532 struct xdrmblk_params *p;
533
534 /*
535 * Can't inline XDR_FREE calls, doesn't make sense.
536 */
537 if (xdrs->x_op == XDR_FREE)
538 return (NULL);
539
540 #ifdef DEBUG
541 if (!do_xdrmblk_inline) {
542 xdrmblk_inline_misses++;
543 return (NULL);
544 }
545 #endif
546
547 if (xdrs->x_op == XDR_DECODE)
548 xdrmblk_skip_fully_read_mblks(xdrs);
549
550 /*
551 * Can't inline if there isn't enough room.
552 */
553 if (len <= 0 || xdrs->x_handy < len) {
554 #ifdef DEBUG
555 xdrmblk_inline_misses++;
556 #endif
557 return (NULL);
558 }
559
560 /* LINTED pointer alignment */
561 m = (mblk_t *)xdrs->x_base;
562 ASSERT(m != NULL);
563
564 if (xdrs->x_op == XDR_DECODE) {
565 /* LINTED pointer alignment */
566 mptr = &m->b_rptr;
567 } else {
568 /* LINTED pointer alignment */
569 mptr = &m->b_wptr;
570 }
571
572 /*
573 * Can't inline if the buffer is not 4 byte aligned, or if there is
574 * more than one reference to the data block associated with this mblk.
575 * This last check is used because the caller may want to modify the
576 * data in the inlined portion and someone else is holding a reference
577 * to the data who may not want it to be modified.
578 */
579 if (!IS_P2ALIGNED(*mptr, sizeof (int32_t)) ||
580 m->b_datap->db_ref != 1) {
581 #ifdef DEBUG
582 xdrmblk_inline_misses++;
583 #endif
584 return (NULL);
585 }
586
587 buf = (rpc_inline_t *)*mptr;
588
589 p = (struct xdrmblk_params *)xdrs->x_private;
590
591 *mptr += len;
592 xdrs->x_handy -= len;
593 p->rpos += len;
594
595 #ifdef DEBUG
596 xdrmblk_inline_hits++;
597 #endif
598
599 return (buf);
600 }
601
602 static bool_t
xdrmblk_control(XDR * xdrs,int request,void * info)603 xdrmblk_control(XDR *xdrs, int request, void *info)
604 {
605 mblk_t *m;
606 struct xdrmblk_params *p;
607 int32_t *int32p;
608 int len;
609
610 switch (request) {
611 case XDR_PEEK:
612 xdrmblk_skip_fully_read_mblks(xdrs);
613
614 /*
615 * Return the next 4 byte unit in the XDR stream.
616 */
617 if (xdrs->x_handy < sizeof (int32_t))
618 return (FALSE);
619
620 /* LINTED pointer alignment */
621 m = (mblk_t *)xdrs->x_base;
622 ASSERT(m != NULL);
623
624 /*
625 * If the pointer is not aligned, fail the peek
626 */
627 if (!IS_P2ALIGNED(m->b_rptr, sizeof (int32_t)))
628 return (FALSE);
629
630 int32p = (int32_t *)info;
631 /* LINTED pointer alignment */
632 *int32p = ntohl(*((int32_t *)(m->b_rptr)));
633 return (TRUE);
634
635 case XDR_SKIPBYTES:
636 int32p = (int32_t *)info;
637 len = RNDUP((int)(*int32p));
638 if (len < 0)
639 return (FALSE);
640 if (len == 0)
641 return (TRUE);
642
643 /* LINTED pointer alignment */
644 m = (mblk_t *)xdrs->x_base;
645 if (m == NULL)
646 return (FALSE);
647
648 p = (struct xdrmblk_params *)xdrs->x_private;
649
650 while (xdrs->x_handy < len) {
651 if (xdrs->x_handy > 0) {
652 m->b_rptr += xdrs->x_handy;
653 len -= xdrs->x_handy;
654 p->rpos += xdrs->x_handy;
655 }
656 m = m->b_cont;
657 xdrs->x_base = (caddr_t)m;
658 p->apos += p->rpos;
659 p->rpos = 0;
660 if (m == NULL) {
661 xdrs->x_handy = 0;
662 return (FALSE);
663 }
664 xdrs->x_handy = (int)MBLKL(m);
665 }
666
667 xdrs->x_handy -= len;
668 p->rpos += len;
669 m->b_rptr += len;
670 return (TRUE);
671
672 default:
673 return (FALSE);
674 }
675 }
676
677 #define HDR_SPACE 128
678
679 static mblk_t *
xdrmblk_alloc(int sz)680 xdrmblk_alloc(int sz)
681 {
682 mblk_t *mp;
683
684 if (sz == 0)
685 return (NULL);
686
687 /*
688 * Pad the front of the message to allow the lower networking
689 * layers space to add headers as needed.
690 */
691 sz += HDR_SPACE;
692
693 while ((mp = allocb(sz, BPRI_LO)) == NULL) {
694 if (strwaitbuf(sz, BPRI_LO))
695 return (NULL);
696 }
697
698 mp->b_wptr += HDR_SPACE;
699 mp->b_rptr = mp->b_wptr;
700
701 return (mp);
702 }
703
704 /*
705 * Skip fully read or empty mblks
706 */
707 static void
xdrmblk_skip_fully_read_mblks(XDR * xdrs)708 xdrmblk_skip_fully_read_mblks(XDR *xdrs)
709 {
710 mblk_t *m;
711 struct xdrmblk_params *p;
712
713 if (xdrs->x_handy != 0)
714 return;
715
716 /* LINTED pointer alignment */
717 m = (mblk_t *)xdrs->x_base;
718 if (m == NULL)
719 return;
720
721 p = (struct xdrmblk_params *)xdrs->x_private;
722 p->apos += p->rpos;
723 p->rpos = 0;
724
725 do {
726 m = m->b_cont;
727 if (m == NULL)
728 break;
729
730 xdrs->x_handy = (int)MBLKL(m);
731 } while (xdrs->x_handy == 0);
732
733 xdrs->x_base = (caddr_t)m;
734 }
735