How buggy this little piece of code could be? Repair strnvis() buffersize
[dragonfly.git] / lib / libcam / scsi_cmdparse.c
CommitLineData
984263bc
MD
1/*
2 * Taken from the original FreeBSD user SCSI library.
3 */
4/* Copyright (c) 1994 HD Associates
5 * (contact: dufault@hda.com)
6 * All rights reserved.
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 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by HD Associates
19 * 4. Neither the name of the HD Associaates nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 * From: scsi.c,v 1.8 1997/02/22 15:07:54 peter Exp $
35 * $FreeBSD: src/lib/libcam/scsi_cmdparse.c,v 1.3.2.1 2000/08/14 05:42:30 kbyanc Exp $
5adc1304 36 * $DragonFly: src/lib/libcam/scsi_cmdparse.c,v 1.3 2007/11/24 01:53:50 pavalos Exp $
984263bc
MD
37 */
38#include <stdlib.h>
39#include <stdio.h>
40#include <ctype.h>
41#include <string.h>
42#include <sys/errno.h>
43#include <stdarg.h>
44#include <fcntl.h>
45
46#include <cam/cam.h>
47#include <cam/cam_ccb.h>
48#include <cam/scsi/scsi_message.h>
49#include "camlib.h"
50
51/*
52 * Decode: Decode the data section of a scsireq. This decodes
53 * trivial grammar:
54 *
55 * fields : field fields
56 * ;
57 *
58 * field : field_specifier
59 * | control
60 * ;
61 *
62 * control : 's' seek_value
63 * | 's' '+' seek_value
64 * ;
65 *
66 * seek_value : DECIMAL_NUMBER
67 * | 'v' // For indirect seek, i.e., value from the arg list
68 * ;
69 *
70 * field_specifier : type_specifier field_width
71 * | '{' NAME '}' type_specifier field_width
72 * ;
73 *
74 * field_width : DECIMAL_NUMBER
75 * ;
76 *
77 * type_specifier : 'i' // Integral types (i1, i2, i3, i4)
78 * | 'b' // Bits
79 * | 't' // Bits
80 * | 'c' // Character arrays
81 * | 'z' // Character arrays with zeroed trailing spaces
82 * ;
83 *
84 * Notes:
85 * 1. Integral types are swapped into host order.
86 * 2. Bit fields are allocated MSB to LSB to match the SCSI spec documentation.
87 * 3. 's' permits "seeking" in the string. "s+DECIMAL" seeks relative to
88 * DECIMAL; "sDECIMAL" seeks absolute to decimal.
89 * 4. 's' permits an indirect reference. "sv" or "s+v" will get the
90 * next integer value from the arg array.
91 * 5. Field names can be anything between the braces
92 *
93 * BUGS:
94 * i and b types are promoted to ints.
95 *
96 */
97
98static int
99do_buff_decode(u_int8_t *databuf, size_t len,
100 void (*arg_put)(void *, int , void *, int, char *),
5adc1304 101 void *puthook, const char *fmt, va_list ap)
984263bc
MD
102{
103 int assigned = 0;
104 int width;
105 int suppress;
106 int plus;
107 int done = 0;
108 static u_char mask[] = {0, 0x01, 0x03, 0x07, 0x0f,
109 0x1f, 0x3f, 0x7f, 0xff};
110 int value;
111 u_char *base = databuf;
5adc1304 112 char *intendp;
984263bc
MD
113 char letter;
114 char field_name[80];
115
116# define ARG_PUT(ARG) \
117 do \
118 { \
119 if (!suppress) \
120 { \
121 if (arg_put) \
122 (*arg_put)(puthook, (letter == 't' ? \
123 'b' : letter), \
124 (void *)((long)(ARG)), width, \
125 field_name); \
126 else \
127 *(va_arg(ap, int *)) = (ARG); \
128 assigned++; \
129 } \
130 field_name[0] = 0; \
131 suppress = 0; \
132 } while (0)
133
134 u_char bits = 0; /* For bit fields */
135 int shift = 0; /* Bits already shifted out */
136 suppress = 0;
137 field_name[0] = 0;
138
139 while (!done) {
140 switch(letter = *fmt) {
141 case ' ': /* White space */
142 case '\t':
143 case '\r':
144 case '\n':
145 case '\f':
146 fmt++;
147 break;
148
149 case '#': /* Comment */
150 while (*fmt && (*fmt != '\n'))
151 fmt++;
152 if (fmt)
153 fmt++; /* Skip '\n' */
154 break;
155
156 case '*': /* Suppress assignment */
157 fmt++;
158 suppress = 1;
159 break;
160
161 case '{': /* Field Name */
162 {
163 int i = 0;
164 fmt++; /* Skip '{' */
165 while (*fmt && (*fmt != '}')) {
166 if (i < sizeof(field_name))
167 field_name[i++] = *fmt;
168
169 fmt++;
170 }
171 if (fmt)
172 fmt++; /* Skip '}' */
173 field_name[i] = 0;
174 break;
175 }
176
177 case 't': /* Bit (field) */
178 case 'b': /* Bits */
179 fmt++;
5adc1304
PA
180 width = strtol(fmt, &intendp, 10);
181 fmt = intendp;
984263bc
MD
182 if (width > 8)
183 done = 1;
184 else {
185 if (shift <= 0) {
186 bits = *databuf++;
187 shift = 8;
188 }
189 value = (bits >> (shift - width)) &
190 mask[width];
191
192#if 0
193 printf("shift %2d bits %02x value %02x width %2d mask %02x\n",
194 shift, bits, value, width, mask[width]);
195#endif
196
197 ARG_PUT(value);
198
199 shift -= width;
200 }
201 break;
202
203 case 'i': /* Integral values */
204 shift = 0;
205 fmt++;
5adc1304
PA
206 width = strtol(fmt, &intendp, 10);
207 fmt = intendp;
984263bc
MD
208 switch(width) {
209 case 1:
210 ARG_PUT(*databuf);
211 databuf++;
212 break;
213
214 case 2:
215 ARG_PUT((*databuf) << 8 | *(databuf + 1));
216 databuf += 2;
217 break;
218
219 case 3:
220 ARG_PUT((*databuf) << 16 |
221 (*(databuf + 1)) << 8 | *(databuf + 2));
222 databuf += 3;
223 break;
224
225 case 4:
226 ARG_PUT((*databuf) << 24 |
227 (*(databuf + 1)) << 16 |
228 (*(databuf + 2)) << 8 |
229 *(databuf + 3));
230 databuf += 4;
231 break;
232
233 default:
234 done = 1;
235 break;
236 }
237
238 break;
239
240 case 'c': /* Characters (i.e., not swapped) */
241 case 'z': /* Characters with zeroed trailing
242 spaces */
243 shift = 0;
244 fmt++;
5adc1304
PA
245 width = strtol(fmt, &intendp, 10);
246 fmt = intendp;
984263bc
MD
247 if (!suppress) {
248 if (arg_put)
249 (*arg_put)(puthook,
250 (letter == 't' ? 'b' : letter),
251 databuf, width, field_name);
252 else {
253 char *dest;
254 dest = va_arg(ap, char *);
255 bcopy(databuf, dest, width);
256 if (letter == 'z') {
257 char *p;
258 for (p = dest + width - 1;
259 (p >= (char *)dest)
260 && (*p == ' '); p--)
261 *p = 0;
262 }
263 }
264 assigned++;
265 }
266 databuf += width;
267 field_name[0] = 0;
268 suppress = 0;
269 break;
270
271 case 's': /* Seek */
272 shift = 0;
273 fmt++;
274 if (*fmt == '+') {
275 plus = 1;
276 fmt++;
277 } else
278 plus = 0;
279
280 if (tolower(*fmt) == 'v') {
281 /*
282 * You can't suppress a seek value. You also
283 * can't have a variable seek when you are using
284 * "arg_put".
285 */
286 width = (arg_put) ? 0 : va_arg(ap, int);
287 fmt++;
5adc1304
PA
288 } else {
289 width = strtol(fmt, &intendp, 10);
290 fmt = intendp;
291 }
984263bc
MD
292
293 if (plus)
294 databuf += width; /* Relative seek */
295 else
296 databuf = base + width; /* Absolute seek */
297
298 break;
299
300 case 0:
301 done = 1;
302 break;
303
304 default:
305 fprintf(stderr, "Unknown letter in format: %c\n",
306 letter);
307 fmt++;
308 break;
309 }
310 }
311
312 return (assigned);
313}
314
315/* next_field: Return the next field in a command specifier. This
316 * builds up a SCSI command using this trivial grammar:
317 *
318 * fields : field fields
319 * ;
320 *
321 * field : value
322 * | value ':' field_width
323 * ;
324 *
325 * field_width : digit
326 * | 'i' digit // i2 = 2 byte integer, i3 = 3 byte integer etc.
327 * ;
328 *
329 * value : HEX_NUMBER
330 * | 'v' // For indirection.
331 * ;
332 *
333 * Notes:
334 * Bit fields are specified MSB first to match the SCSI spec.
335 *
336 * Examples:
337 * TUR: "0 0 0 0 0 0"
338 * WRITE BUFFER: "38 v:3 0:2 0:3 v v:i3 v:i3 0", mode, buffer_id, list_length
339 *
340 * The function returns the value:
341 * 0: For reached end, with error_p set if an error was found
342 * 1: For valid stuff setup
343 * 2: For "v" was entered as the value (implies use varargs)
344 *
345 */
346
347static int
5adc1304 348next_field(const char **pp, char *fmt, int *width_p, int *value_p, char *name,
984263bc
MD
349 int n_name, int *error_p, int *suppress_p)
350{
5adc1304
PA
351 const char *p = *pp;
352 char *intendp;
984263bc
MD
353
354 int something = 0;
355
356 enum {
357 BETWEEN_FIELDS,
358 START_FIELD,
359 GET_FIELD,
360 DONE,
361 } state;
362
363 int value = 0;
364 int field_size; /* Default to byte field type... */
365 int field_width; /* 1 byte wide */
366 int is_error = 0;
367 int suppress = 0;
368
369 field_size = 8; /* Default to byte field type... */
370 *fmt = 'i';
371 field_width = 1; /* 1 byte wide */
372 if (name)
373 *name = 0;
374
375 state = BETWEEN_FIELDS;
376
377 while (state != DONE) {
378 switch(state) {
379 case BETWEEN_FIELDS:
380 if (*p == 0)
381 state = DONE;
382 else if (isspace(*p))
383 p++;
384 else if (*p == '#') {
385 while (*p && *p != '\n')
386 p++;
387 if (p)
388 p++;
389 } else if (*p == '{') {
390 int i = 0;
391
392 p++;
393
394 while (*p && *p != '}') {
395 if(name && i < n_name) {
396 name[i] = *p;
397 i++;
398 }
399 p++;
400 }
401
402 if(name && i < n_name)
403 name[i] = 0;
404
405 if (*p == '}')
406 p++;
407 } else if (*p == '*') {
408 p++;
409 suppress = 1;
410 } else if (isxdigit(*p)) {
411 something = 1;
5adc1304
PA
412 value = strtol(p, &intendp, 16);
413 p = intendp;
984263bc
MD
414 state = START_FIELD;
415 } else if (tolower(*p) == 'v') {
416 p++;
417 something = 2;
418 value = *value_p;
419 state = START_FIELD;
420 } else if (tolower(*p) == 'i') {
421 /*
422 * Try to work without the "v".
423 */
424 something = 2;
425 value = *value_p;
426 p++;
427
428 *fmt = 'i';
429 field_size = 8;
5adc1304
PA
430 field_width = strtol(p, &intendp, 10);
431 p = intendp;
984263bc
MD
432 state = DONE;
433
434 } else if (tolower(*p) == 't') {
435 /*
436 * XXX: B can't work: Sees the 'b' as a
437 * hex digit in "isxdigit". try "t" for
438 * bit field.
439 */
440 something = 2;
441 value = *value_p;
442 p++;
443
444 *fmt = 'b';
445 field_size = 1;
5adc1304
PA
446 field_width = strtol(p, &intendp, 10);
447 p = intendp;
984263bc
MD
448 state = DONE;
449 } else if (tolower(*p) == 's') {
450 /* Seek */
451 *fmt = 's';
452 p++;
453 if (tolower(*p) == 'v') {
454 p++;
455 something = 2;
456 value = *value_p;
457 } else {
458 something = 1;
5adc1304
PA
459 value = strtol(p, &intendp, 0);
460 p = intendp;
984263bc
MD
461 }
462 state = DONE;
463 } else {
464 fprintf(stderr, "Invalid starting "
465 "character: %c\n", *p);
466 is_error = 1;
467 state = DONE;
468 }
469 break;
470
471 case START_FIELD:
472 if (*p == ':') {
473 p++;
474 field_size = 1; /* Default to bits
475 when specified */
476 state = GET_FIELD;
477 } else
478 state = DONE;
479 break;
480
481 case GET_FIELD:
482 if (isdigit(*p)) {
483 *fmt = 'b';
484 field_size = 1;
5adc1304
PA
485 field_width = strtol(p, &intendp, 10);
486 p = intendp;
984263bc
MD
487 state = DONE;
488 } else if (*p == 'i') {
489
490 /* Integral (bytes) */
491 p++;
492
493 *fmt = 'i';
494 field_size = 8;
5adc1304
PA
495 field_width = strtol(p, &intendp, 10);
496 p = intendp;
984263bc
MD
497 state = DONE;
498 } else if (*p == 'b') {
499
500 /* Bits */
501 p++;
502
503 *fmt = 'b';
504 field_size = 1;
5adc1304
PA
505 field_width = strtol(p, &intendp, 10);
506 p = intendp;
984263bc
MD
507 state = DONE;
508 } else {
509 fprintf(stderr, "Invalid startfield %c "
510 "(%02x)\n", *p, *p);
511 is_error = 1;
512 state = DONE;
513 }
514 break;
515
516 case DONE:
517 break;
518 }
519 }
520
521 if (is_error) {
522 *error_p = 1;
523 return 0;
524 }
525
526 *error_p = 0;
527 *pp = p;
528 *width_p = field_width * field_size;
529 *value_p = value;
530 *suppress_p = suppress;
531
532 return (something);
533}
534
535static int
536do_encode(u_char *buff, size_t vec_max, size_t *used,
5adc1304
PA
537 int (*arg_get)(void *, char *), void *gethook, const char *fmt,
538 va_list ap)
984263bc
MD
539{
540 int ind;
541 int shift;
542 u_char val;
543 int ret;
544 int width, value, error, suppress;
545 char c;
546 int encoded = 0;
547 char field_name[80];
548
549 ind = 0;
550 shift = 0;
551 val = 0;
552
553 while ((ret = next_field(&fmt, &c, &width, &value, field_name,
554 sizeof(field_name), &error, &suppress))) {
555 encoded++;
556
557 if (ret == 2) {
558 if (suppress)
559 value = 0;
560 else
561 value = arg_get ?
562 (*arg_get)(gethook, field_name) :
563 va_arg(ap, int);
564 }
565
566#if 0
567 printf(
568"do_encode: ret %d fmt %c width %d value %d name \"%s\" error %d suppress %d\n",
569 ret, c, width, value, field_name, error, suppress);
570#endif
571 /* Absolute seek */
572 if (c == 's') {
573 ind = value;
574 continue;
575 }
576
577 /* A width of < 8 is a bit field. */
578 if (width < 8) {
579
580 /* This is a bit field. We start with the high bits
581 * so it reads the same as the SCSI spec.
582 */
583
584 shift += width;
585
586 val |= (value << (8 - shift));
587
588 if (shift == 8) {
589 if (ind < vec_max) {
590 buff[ind++] = val;
591 val = 0;
592 }
593 shift = 0;
594 }
595 } else {
596 if (shift) {
597 if (ind < vec_max) {
598 buff[ind++] = val;
599 val = 0;
600 }
601 shift = 0;
602 }
603 switch(width) {
604 case 8: /* 1 byte integer */
605 if (ind < vec_max)
606 buff[ind++] = value;
607 break;
608
609 case 16: /* 2 byte integer */
610 if (ind < vec_max - 2 + 1) {
611 buff[ind++] = value >> 8;
612 buff[ind++] = value;
613 }
614 break;
615
616 case 24: /* 3 byte integer */
617 if (ind < vec_max - 3 + 1) {
618 buff[ind++] = value >> 16;
619 buff[ind++] = value >> 8;
620 buff[ind++] = value;
621 }
622 break;
623
624 case 32: /* 4 byte integer */
625 if (ind < vec_max - 4 + 1) {
626 buff[ind++] = value >> 24;
627 buff[ind++] = value >> 16;
628 buff[ind++] = value >> 8;
629 buff[ind++] = value;
630 }
631 break;
632
633 default:
634 fprintf(stderr, "do_encode: Illegal width\n");
635 break;
636 }
637 }
638 }
639
640 /* Flush out any remaining bits
641 */
642 if (shift && ind < vec_max) {
643 buff[ind++] = val;
644 val = 0;
645 }
646
647
648 if (used)
649 *used = ind;
650
651 if (error)
652 return -1;
653
654 return encoded;
655}
656
657int
5adc1304 658csio_decode(struct ccb_scsiio *csio, const char *fmt, ...)
984263bc
MD
659{
660 va_list ap;
661
662 va_start(ap, fmt);
663
664 return(do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
665 0, 0, fmt, ap));
666}
667
668int
5adc1304 669csio_decode_visit(struct ccb_scsiio *csio, const char *fmt,
984263bc
MD
670 void (*arg_put)(void *, int, void *, int, char *),
671 void *puthook)
672{
673 va_list ap;
674
675 /*
676 * We need some way to output things; we can't do it without
677 * the arg_put function.
678 */
679 if (arg_put == NULL)
680 return(-1);
681
682 bzero(&ap, sizeof(ap));
683
684 return(do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
685 arg_put, puthook, fmt, ap));
686}
687
688int
5adc1304 689buff_decode(u_int8_t *buff, size_t len, const char *fmt, ...)
984263bc
MD
690{
691 va_list ap;
692
693 va_start(ap, fmt);
694
695 return(do_buff_decode(buff, len, 0, 0, fmt, ap));
696}
697
698int
5adc1304 699buff_decode_visit(u_int8_t *buff, size_t len, const char *fmt,
984263bc
MD
700 void (*arg_put)(void *, int, void *, int, char *),
701 void *puthook)
702{
703 va_list ap;
704
705 /*
706 * We need some way to output things; we can't do it without
707 * the arg_put function.
708 */
709 if (arg_put == NULL)
710 return(-1);
711
712 bzero(&ap, sizeof(ap));
713
714 return(do_buff_decode(buff, len, arg_put, puthook, fmt, ap));
715}
716
717/*
718 * Build a SCSI CCB, given the command and data pointers and a format
719 * string describing the
720 */
721int
722csio_build(struct ccb_scsiio *csio, u_int8_t *data_ptr, u_int32_t dxfer_len,
5adc1304
PA
723 u_int32_t flags, int retry_count, int timeout, const char *cmd_spec,
724 ...)
984263bc
MD
725{
726 size_t cmdlen;
727 int retval;
728 va_list ap;
729
730 if (csio == NULL)
731 return(0);
732
733 bzero(csio, sizeof(struct ccb_scsiio));
734
735 va_start(ap, cmd_spec);
736
737 if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
738 &cmdlen, NULL, NULL, cmd_spec, ap)) == -1)
739 return(retval);
740
741 cam_fill_csio(csio,
742 /* retries */ retry_count,
743 /* cbfcnp */ NULL,
744 /* flags */ flags,
745 /* tag_action */ MSG_SIMPLE_Q_TAG,
746 /* data_ptr */ data_ptr,
747 /* dxfer_len */ dxfer_len,
748 /* sense_len */ SSD_FULL_SIZE,
749 /* cdb_len */ cmdlen,
750 /* timeout */ timeout ? timeout : 5000);
751
752 return(retval);
753}
754
755int
756csio_build_visit(struct ccb_scsiio *csio, u_int8_t *data_ptr,
757 u_int32_t dxfer_len, u_int32_t flags, int retry_count,
5adc1304 758 int timeout, const char *cmd_spec,
984263bc
MD
759 int (*arg_get)(void *hook, char *field_name), void *gethook)
760{
761 va_list ap;
762 size_t cmdlen;
763 int retval;
764
765 if (csio == NULL)
766 return(0);
767
768 /*
769 * We need something to encode, but we can't get it without the
770 * arg_get function.
771 */
772 if (arg_get == NULL)
773 return(-1);
774
775 bzero(&ap, sizeof(ap));
776
777 bzero(csio, sizeof(struct ccb_scsiio));
778
779 if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
780 &cmdlen, arg_get, gethook, cmd_spec, ap)) == -1)
781 return(retval);
782
783 cam_fill_csio(csio,
784 /* retries */ retry_count,
785 /* cbfcnp */ NULL,
786 /* flags */ flags,
787 /* tag_action */ MSG_SIMPLE_Q_TAG,
788 /* data_ptr */ data_ptr,
789 /* dxfer_len */ dxfer_len,
790 /* sense_len */ SSD_FULL_SIZE,
791 /* cdb_len */ cmdlen,
792 /* timeout */ timeout ? timeout : 5000);
793
794 return(retval);
795}
796
797int
5adc1304 798csio_encode(struct ccb_scsiio *csio, const char *fmt, ...)
984263bc
MD
799{
800 va_list ap;
801
802 if (csio == NULL)
803 return(0);
804
805 va_start(ap, fmt);
806
807 return(do_encode(csio->data_ptr, csio->dxfer_len, 0, 0, 0, fmt, ap));
808}
809
810int
5adc1304 811buff_encode_visit(u_int8_t *buff, size_t len, const char *fmt,
984263bc
MD
812 int (*arg_get)(void *hook, char *field_name), void *gethook)
813{
814 va_list ap;
815
816 /*
817 * We need something to encode, but we can't get it without the
818 * arg_get function.
819 */
820 if (arg_get == NULL)
821 return(-1);
822
823 bzero(&ap, sizeof(ap));
824
825 return(do_encode(buff, len, 0, arg_get, gethook, fmt, ap));
826}
827
828int
5adc1304 829csio_encode_visit(struct ccb_scsiio *csio, const char *fmt,
984263bc
MD
830 int (*arg_get)(void *hook, char *field_name), void *gethook)
831{
832 va_list ap;
833
834 /*
835 * We need something to encode, but we can't get it without the
836 * arg_get function.
837 */
838 if (arg_get == NULL)
839 return(-1);
840
841 bzero(&ap, sizeof(ap));
842
843 return(do_encode(csio->data_ptr, csio->dxfer_len, 0, arg_get,
844 gethook, fmt, ap));
845}