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 2015 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 struct xdrmblk_params *p;
445 int32_t llen = (int32_t)len;
446
447 if ((DLEN(m) % BYTES_PER_XDR_UNIT) != 0)
448 return (FALSE);
449 if (!xdrmblk_putint32(xdrs, &llen))
450 return (FALSE);
451
452 p = (struct xdrmblk_params *)xdrs->x_private;
453
454 /* LINTED pointer alignment */
455 ((mblk_t *)xdrs->x_base)->b_cont = m;
456 p->apos += p->rpos;
457
458 /* base points to the last mblk */
459 while (m->b_cont) {
460 p->apos += MBLKL(m);
461 m = m->b_cont;
462 }
463 xdrs->x_base = (caddr_t)m;
464 xdrs->x_handy = 0;
465 p->rpos = MBLKL(m);
466 return (TRUE);
467 }
468
469 static uint_t
xdrmblk_getpos(XDR * xdrs)470 xdrmblk_getpos(XDR *xdrs)
471 {
472 struct xdrmblk_params *p = (struct xdrmblk_params *)xdrs->x_private;
473
474 return (p->apos + p->rpos);
475 }
476
477 static bool_t
xdrmblk_setpos(XDR * xdrs,uint_t pos)478 xdrmblk_setpos(XDR *xdrs, uint_t pos)
479 {
480 mblk_t *m;
481 struct xdrmblk_params *p;
482
483 p = (struct xdrmblk_params *)xdrs->x_private;
484
485 if (pos < p->apos)
486 return (FALSE);
487
488 if (pos > p->apos + p->rpos + xdrs->x_handy)
489 return (FALSE);
490
491 if (pos == p->apos + p->rpos)
492 return (TRUE);
493
494 /* LINTED pointer alignment */
495 m = (mblk_t *)xdrs->x_base;
496 ASSERT(m != NULL);
497
498 if (xdrs->x_op == XDR_DECODE)
499 m->b_rptr = m->b_rptr - p->rpos + (pos - p->apos);
500 else
501 m->b_wptr = m->b_wptr - p->rpos + (pos - p->apos);
502
503 xdrs->x_handy = p->rpos + xdrs->x_handy - (pos - p->apos);
504 p->rpos = pos - p->apos;
505
506 return (TRUE);
507 }
508
509 #ifdef DEBUG
510 static int xdrmblk_inline_hits = 0;
511 static int xdrmblk_inline_misses = 0;
512 static int do_xdrmblk_inline = 1;
513 #endif
514
515 static rpc_inline_t *
xdrmblk_inline(XDR * xdrs,int len)516 xdrmblk_inline(XDR *xdrs, int len)
517 {
518 rpc_inline_t *buf;
519 mblk_t *m;
520 unsigned char **mptr;
521 struct xdrmblk_params *p;
522
523 /*
524 * Can't inline XDR_FREE calls, doesn't make sense.
525 */
526 if (xdrs->x_op == XDR_FREE)
527 return (NULL);
528
529 #ifdef DEBUG
530 if (!do_xdrmblk_inline) {
531 xdrmblk_inline_misses++;
532 return (NULL);
533 }
534 #endif
535
536 if (xdrs->x_op == XDR_DECODE)
537 xdrmblk_skip_fully_read_mblks(xdrs);
538
539 /*
540 * Can't inline if there isn't enough room.
541 */
542 if (len <= 0 || xdrs->x_handy < len) {
543 #ifdef DEBUG
544 xdrmblk_inline_misses++;
545 #endif
546 return (NULL);
547 }
548
549 /* LINTED pointer alignment */
550 m = (mblk_t *)xdrs->x_base;
551 ASSERT(m != NULL);
552
553 if (xdrs->x_op == XDR_DECODE) {
554 /* LINTED pointer alignment */
555 mptr = &m->b_rptr;
556 } else {
557 /* LINTED pointer alignment */
558 mptr = &m->b_wptr;
559 }
560
561 /*
562 * Can't inline if the buffer is not 4 byte aligned, or if there is
563 * more than one reference to the data block associated with this mblk.
564 * This last check is used because the caller may want to modify the
565 * data in the inlined portion and someone else is holding a reference
566 * to the data who may not want it to be modified.
567 */
568 if (!IS_P2ALIGNED(*mptr, sizeof (int32_t)) ||
569 m->b_datap->db_ref != 1) {
570 #ifdef DEBUG
571 xdrmblk_inline_misses++;
572 #endif
573 return (NULL);
574 }
575
576 buf = (rpc_inline_t *)*mptr;
577
578 p = (struct xdrmblk_params *)xdrs->x_private;
579
580 *mptr += len;
581 xdrs->x_handy -= len;
582 p->rpos += len;
583
584 #ifdef DEBUG
585 xdrmblk_inline_hits++;
586 #endif
587
588 return (buf);
589 }
590
591 static bool_t
xdrmblk_control(XDR * xdrs,int request,void * info)592 xdrmblk_control(XDR *xdrs, int request, void *info)
593 {
594 mblk_t *m;
595 struct xdrmblk_params *p;
596 int32_t *int32p;
597 int len;
598
599 switch (request) {
600 case XDR_PEEK:
601 xdrmblk_skip_fully_read_mblks(xdrs);
602
603 /*
604 * Return the next 4 byte unit in the XDR stream.
605 */
606 if (xdrs->x_handy < sizeof (int32_t))
607 return (FALSE);
608
609 /* LINTED pointer alignment */
610 m = (mblk_t *)xdrs->x_base;
611 ASSERT(m != NULL);
612
613 /*
614 * If the pointer is not aligned, fail the peek
615 */
616 if (!IS_P2ALIGNED(m->b_rptr, sizeof (int32_t)))
617 return (FALSE);
618
619 int32p = (int32_t *)info;
620 /* LINTED pointer alignment */
621 *int32p = ntohl(*((int32_t *)(m->b_rptr)));
622 return (TRUE);
623
624 case XDR_SKIPBYTES:
625 int32p = (int32_t *)info;
626 len = RNDUP((int)(*int32p));
627 if (len < 0)
628 return (FALSE);
629 if (len == 0)
630 return (TRUE);
631
632 /* LINTED pointer alignment */
633 m = (mblk_t *)xdrs->x_base;
634 if (m == NULL)
635 return (FALSE);
636
637 p = (struct xdrmblk_params *)xdrs->x_private;
638
639 while (xdrs->x_handy < len) {
640 if (xdrs->x_handy > 0) {
641 m->b_rptr += xdrs->x_handy;
642 len -= xdrs->x_handy;
643 p->rpos += xdrs->x_handy;
644 }
645 m = m->b_cont;
646 xdrs->x_base = (caddr_t)m;
647 p->apos += p->rpos;
648 p->rpos = 0;
649 if (m == NULL) {
650 xdrs->x_handy = 0;
651 return (FALSE);
652 }
653 xdrs->x_handy = (int)MBLKL(m);
654 }
655
656 xdrs->x_handy -= len;
657 p->rpos += len;
658 m->b_rptr += len;
659 return (TRUE);
660
661 default:
662 return (FALSE);
663 }
664 }
665
666 #define HDR_SPACE 128
667
668 static mblk_t *
xdrmblk_alloc(int sz)669 xdrmblk_alloc(int sz)
670 {
671 mblk_t *mp;
672
673 if (sz == 0)
674 return (NULL);
675
676 /*
677 * Pad the front of the message to allow the lower networking
678 * layers space to add headers as needed.
679 */
680 sz += HDR_SPACE;
681
682 while ((mp = allocb(sz, BPRI_LO)) == NULL) {
683 if (strwaitbuf(sz, BPRI_LO))
684 return (NULL);
685 }
686
687 mp->b_wptr += HDR_SPACE;
688 mp->b_rptr = mp->b_wptr;
689
690 return (mp);
691 }
692
693 /*
694 * Skip fully read or empty mblks
695 */
696 static void
xdrmblk_skip_fully_read_mblks(XDR * xdrs)697 xdrmblk_skip_fully_read_mblks(XDR *xdrs)
698 {
699 mblk_t *m;
700 struct xdrmblk_params *p;
701
702 if (xdrs->x_handy != 0)
703 return;
704
705 /* LINTED pointer alignment */
706 m = (mblk_t *)xdrs->x_base;
707 if (m == NULL)
708 return;
709
710 p = (struct xdrmblk_params *)xdrs->x_private;
711 p->apos += p->rpos;
712 p->rpos = 0;
713
714 do {
715 m = m->b_cont;
716 if (m == NULL)
717 break;
718
719 xdrs->x_handy = (int)MBLKL(m);
720 } while (xdrs->x_handy == 0);
721
722 xdrs->x_base = (caddr_t)m;
723 }
724