1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2020 Robert Mustacchi
14 */
15
16 /*
17 * Test memory based streams: opem_memstream(3C), open_wmemstream(3C), and
18 * fmemopen(3C).
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/types.h>
24 #include <sys/sysmacros.h>
25 #include <strings.h>
26 #include <err.h>
27 #include <errno.h>
28 #include <wchar.h>
29 #include <umem.h>
30 #include <locale.h>
31
32 typedef boolean_t (*memstream_test_f)(void);
33 static char *fmemopen_str1 = "The Road goes ever on and on\n"
34 "Down from the door where it began.\n";
35 const wchar_t *wstream_str = L"いつか終わる夢";
36 /*
37 * smatch doesn't support wide-character constants (wchar_t foo = L'xxx'), so
38 * instead use a string which it'll happily accept.
39 */
40 const wchar_t *wstr_const = L"光";
41
42 const char *
_umem_debug_init(void)43 _umem_debug_init(void)
44 {
45 return ("default,verbose");
46 }
47
48 const char *
_umem_logging_init(void)49 _umem_logging_init(void)
50 {
51 return ("fail,contents");
52 }
53
54 static boolean_t
fmemopen_badopen(void * buf,size_t size,const char * mode,int err)55 fmemopen_badopen(void *buf, size_t size, const char *mode, int err)
56 {
57 FILE *f = fmemopen(buf, size, mode);
58
59 if (f != NULL) {
60 warnx("fmemopen() succeeded erroneously");
61 (void) fclose(f);
62 return (B_FALSE);
63 }
64
65 if (errno != err) {
66 warnx("fmemopen() open failed with wrong errno, "
67 "found %d (%s), expected %d (%s)", errno, strerror(errno),
68 err, strerror(err));
69 return (B_FALSE);
70 }
71
72 return (B_TRUE);
73 }
74
75 static boolean_t
fmemopen_badmode(void)76 fmemopen_badmode(void)
77 {
78 return (fmemopen_badopen(fmemopen_str1, strlen(fmemopen_str1), "foobar",
79 EINVAL));
80 }
81
82 static boolean_t
fmemopen_zerobuf1(void)83 fmemopen_zerobuf1(void)
84 {
85 return (fmemopen_badopen(fmemopen_str1, 0, "w", EINVAL));
86 }
87
88 static boolean_t
fmemopen_zerobuf2(void)89 fmemopen_zerobuf2(void)
90 {
91 return (fmemopen_badopen(NULL, 0, "w+", EINVAL));
92 }
93
94 static boolean_t
fmemopen_nullbuf1(void)95 fmemopen_nullbuf1(void)
96 {
97 return (fmemopen_badopen(NULL, 10, "r", EINVAL));
98 }
99
100 static boolean_t
fmemopen_nullbuf2(void)101 fmemopen_nullbuf2(void)
102 {
103 return (fmemopen_badopen(NULL, 10, "w", EINVAL));
104 }
105
106 static boolean_t
fmemopen_nullbuf3(void)107 fmemopen_nullbuf3(void)
108 {
109 return (fmemopen_badopen(NULL, 10, "a", EINVAL));
110 }
111
112 static boolean_t
fmemopen_nullbuf4(void)113 fmemopen_nullbuf4(void)
114 {
115 return (fmemopen_badopen(NULL, 10, "ax", EINVAL));
116 }
117
118 static boolean_t
fmemopen_sizemax(void)119 fmemopen_sizemax(void)
120 {
121 return (fmemopen_badopen(NULL, SIZE_MAX, "w+", ENOMEM));
122 }
123
124 static boolean_t
fmemopen_cantalloc(void)125 fmemopen_cantalloc(void)
126 {
127 boolean_t ret;
128
129 umem_setmtbf(1);
130 ret = fmemopen_badopen(NULL, 10, "w+", ENOMEM);
131 umem_setmtbf(0);
132 return (ret);
133 }
134
135 static boolean_t
open_memstream_badopen(char ** bufp,size_t * sizep,int err)136 open_memstream_badopen(char **bufp, size_t *sizep, int err)
137 {
138 FILE *f = open_memstream(bufp, sizep);
139
140 if (f != NULL) {
141 warnx("open_memstream() succeeded erroneously");
142 (void) fclose(f);
143 return (B_FALSE);
144 }
145
146 if (errno != err) {
147 warnx("open_memstream() open failed with wrong errno, "
148 "found %d (%s), expected %d (%s)", errno, strerror(errno),
149 err, strerror(err));
150 return (B_FALSE);
151 }
152
153 return (B_TRUE);
154 }
155
156 static boolean_t
open_memstream_badbuf(void)157 open_memstream_badbuf(void)
158 {
159 size_t s, check;
160 boolean_t ret;
161
162 arc4random_buf(&s, sizeof (s));
163 check = s;
164 ret = open_memstream_badopen(NULL, &s, EINVAL);
165 if (check != s) {
166 warnx("open_memstream() open erroneously wrote to size "
167 "pointer");
168 return (B_FALSE);
169 }
170 return (ret);
171 }
172
173 static boolean_t
open_memstream_badsize(void)174 open_memstream_badsize(void)
175 {
176 char *c;
177 return (open_memstream_badopen(&c, NULL, EINVAL));
178 }
179
180 static boolean_t
open_memstream_allnull(void)181 open_memstream_allnull(void)
182 {
183 return (open_memstream_badopen(NULL, NULL, EINVAL));
184 }
185
186 static boolean_t
open_memstream_cantalloc(void)187 open_memstream_cantalloc(void)
188 {
189 boolean_t ret;
190 char *c;
191 size_t len;
192
193 umem_setmtbf(1);
194 ret = open_memstream_badopen(&c, &len, EAGAIN);
195 umem_setmtbf(0);
196 return (ret);
197 }
198
199 static boolean_t
open_wmemstream_badopen(wchar_t ** bufp,size_t * sizep,int err)200 open_wmemstream_badopen(wchar_t **bufp, size_t *sizep, int err)
201 {
202 FILE *f = open_wmemstream(bufp, sizep);
203
204 if (f != NULL) {
205 warnx("open_wmemstream() succeeded erroneously");
206 (void) fclose(f);
207 return (B_FALSE);
208 }
209
210 if (errno != err) {
211 warnx("open_wmemstream() open failed with wrong errno, "
212 "found %d (%s), expected %d (%s)", errno, strerror(errno),
213 err, strerror(err));
214 return (B_FALSE);
215 }
216
217 return (B_TRUE);
218 }
219
220 static boolean_t
open_wmemstream_badbuf(void)221 open_wmemstream_badbuf(void)
222 {
223 size_t s, check;
224 boolean_t ret;
225
226 arc4random_buf(&s, sizeof (s));
227 check = s;
228 ret = open_wmemstream_badopen(NULL, &s, EINVAL);
229 if (check != s) {
230 warnx("open_wmemstream() open erroneously wrote to size "
231 "pointer");
232 return (B_FALSE);
233 }
234 return (ret);
235 }
236
237 static boolean_t
open_wmemstream_badsize(void)238 open_wmemstream_badsize(void)
239 {
240 wchar_t *c;
241 return (open_wmemstream_badopen(&c, NULL, EINVAL));
242 }
243
244 static boolean_t
open_wmemstream_allnull(void)245 open_wmemstream_allnull(void)
246 {
247 return (open_wmemstream_badopen(NULL, NULL, EINVAL));
248 }
249
250 static boolean_t
open_wmemstream_cantalloc(void)251 open_wmemstream_cantalloc(void)
252 {
253 boolean_t ret;
254 wchar_t *c;
255 size_t len;
256
257 umem_setmtbf(1);
258 ret = open_wmemstream_badopen(&c, &len, EAGAIN);
259 umem_setmtbf(0);
260 return (ret);
261 }
262
263 static boolean_t
fmemopen_fill_putc(FILE * f,size_t len,boolean_t buffer)264 fmemopen_fill_putc(FILE *f, size_t len, boolean_t buffer)
265 {
266 boolean_t ret = B_TRUE;
267 size_t i;
268
269 for (i = 0; i < BUFSIZ * 2; i++) {
270 if (fputc('a', f) != 'a') {
271 break;
272 }
273 }
274
275 if (buffer) {
276 if (i < len) {
277 warnx("write mismatch, had %zu bytes, wrote %zu",
278 len, i);
279 ret = B_FALSE;
280 }
281
282 if (fflush(f) == 0) {
283 warnx("somehow flushed overly full stream, expected "
284 "failure");
285 ret = B_FALSE;
286 }
287 } else if (i != len) {
288 warnx("write mismatch, had %zu bytes, wrote %zu", len, i);
289 ret = B_FALSE;
290 }
291
292 if (feof(f) != 0) {
293 warn("EOF mistakenly set on write");
294 ret = B_FALSE;
295 }
296
297 if (ferror(f) == 0) {
298 warn("feof not set on write past the end");
299 ret = B_FALSE;
300 }
301
302 if (fclose(f) != 0) {
303 warn("failed to close memory stream");
304 return (B_FALSE);
305 }
306
307 return (ret);
308 }
309
310 static boolean_t
fmemopen_fill_fwrite(FILE * f,size_t len,boolean_t buffer)311 fmemopen_fill_fwrite(FILE *f, size_t len, boolean_t buffer)
312 {
313 boolean_t ret = B_TRUE;
314 size_t i;
315 char buf[BUFSIZ];
316
317 (void) memset(buf, 'a', sizeof (buf));
318 i = fwrite(buf, sizeof (buf), 1, f);
319
320 if (buffer) {
321 if (i != 1) {
322 warnx("write mismatch, expected 1 entry, found %zu", i);
323 ret = B_FALSE;
324 }
325
326 if (fflush(f) == 0) {
327 warnx("somehow flushed overly full stream, expected "
328 "failure");
329 ret = B_FALSE;
330 }
331 } else if (i != 0 && i != len) {
332 warnx("write mismatch, had %zu bytes, wrote %zu", len, i);
333 ret = B_FALSE;
334 }
335
336 if (feof(f) != 0) {
337 warn("EOF mistakenly set on write");
338 ret = B_FALSE;
339 }
340
341 if (ferror(f) == 0) {
342 warn("feof not set on write past the end");
343 ret = B_FALSE;
344 }
345
346 if (fclose(f) != 0) {
347 warn("failed to close memory stream");
348 return (B_FALSE);
349 }
350
351 return (ret);
352 }
353
354 static boolean_t
fmemopen_fill_alt_fwrite(FILE * f,size_t len,boolean_t buffer)355 fmemopen_fill_alt_fwrite(FILE *f, size_t len, boolean_t buffer)
356 {
357 boolean_t ret = B_TRUE;
358 size_t i;
359 char buf[BUFSIZ];
360
361 (void) memset(buf, 'a', sizeof (buf));
362 i = fwrite(buf, 1, sizeof (buf), f);
363
364 if (buffer) {
365 if (i < len) {
366 warnx("write mismatch, had %zu bytes, wrote %zu",
367 len, i);
368 ret = B_FALSE;
369 }
370
371 if (fflush(f) == 0) {
372 warnx("somehow flushed overly full stream, expected "
373 "failure");
374 ret = B_FALSE;
375 }
376 } else if (i != len) {
377 warnx("write mismatch, had %zu bytes, wrote %zu", len, i);
378 ret = B_FALSE;
379 }
380
381 if (feof(f) != 0) {
382 warn("EOF mistakenly set on write");
383 ret = B_FALSE;
384 }
385
386 if (ferror(f) == 0) {
387 warn("feof not set on write past the end");
388 ret = B_FALSE;
389 }
390
391 if (fclose(f) != 0) {
392 warn("failed to close memory stream");
393 return (B_FALSE);
394 }
395
396 return (ret);
397 }
398
399 static boolean_t
fmemopen_fill_fputs(FILE * f,size_t len,boolean_t buffer)400 fmemopen_fill_fputs(FILE *f, size_t len, boolean_t buffer)
401 {
402 boolean_t ret = B_TRUE;
403 size_t i;
404 char buf[17];
405
406 (void) memset(buf, 'a', sizeof (buf));
407 buf[16] = '\0';
408 for (i = 0; i < BUFSIZ * 2; i += 16) {
409 if (fputs(buf, f) != 16) {
410 break;
411 }
412 }
413
414 /*
415 * We don't check flushing in the puts case because fputs seems to clear
416 * the buffer as a side effect.
417 */
418 if (buffer) {
419 if (i < len) {
420 warnx("write mismatch, had %zu bytes, wrote %zu",
421 len, i);
422 ret = B_FALSE;
423 }
424 } else if (i != len) {
425 warnx("write mismatch, had %zu bytes, wrote %zu", len, i);
426 ret = B_FALSE;
427 }
428
429 if (feof(f) != 0) {
430 warn("EOF mistakenly set on write");
431 ret = B_FALSE;
432 }
433
434 if (ferror(f) == 0) {
435 warn("feof not set on write past the end");
436 ret = B_FALSE;
437 }
438
439 if (fclose(f) != 0) {
440 warn("failed to close memory stream");
441 return (B_FALSE);
442 }
443
444 return (ret);
445 }
446
447
448 static boolean_t
fmemopen_fill_default(void)449 fmemopen_fill_default(void)
450 {
451 FILE *f;
452
453 f = fmemopen(NULL, 128, "w+");
454 if (f == NULL) {
455 warn("failed to open fmemopen stream");
456 return (B_FALSE);
457 }
458
459 return (fmemopen_fill_putc(f, 128, B_TRUE));
460 }
461
462 static boolean_t
fmemopen_fill_lbuf(void)463 fmemopen_fill_lbuf(void)
464 {
465 FILE *f;
466
467 f = fmemopen(NULL, 128, "w+");
468 if (f == NULL) {
469 warn("failed to open fmemopen stream");
470 return (B_FALSE);
471 }
472
473 if (setvbuf(f, NULL, _IOLBF, BUFSIZ) != 0) {
474 warn("failed to set buffer to line-buffered mode");
475 }
476
477 return (fmemopen_fill_putc(f, 128, B_TRUE));
478 }
479
480 static boolean_t
fmemopen_fill_nobuf(void)481 fmemopen_fill_nobuf(void)
482 {
483 FILE *f;
484
485 f = fmemopen(NULL, 128, "w+");
486 if (f == NULL) {
487 warn("failed to open fmemopen stream");
488 return (B_FALSE);
489 }
490
491 if (setvbuf(f, NULL, _IONBF, 0) != 0) {
492 warn("failed to set buffer to non-buffered mode");
493 }
494
495 return (fmemopen_fill_putc(f, 128, B_FALSE));
496 }
497
498 static boolean_t
fmemopen_fwrite_default(void)499 fmemopen_fwrite_default(void)
500 {
501 FILE *f;
502
503 f = fmemopen(NULL, 128, "w+");
504 if (f == NULL) {
505 warn("failed to open fmemopen stream");
506 return (B_FALSE);
507 }
508
509 return (fmemopen_fill_fwrite(f, 128, B_TRUE));
510 }
511
512 static boolean_t
fmemopen_fwrite_lbuf(void)513 fmemopen_fwrite_lbuf(void)
514 {
515 FILE *f;
516
517 f = fmemopen(NULL, 128, "w+");
518 if (f == NULL) {
519 warn("failed to open fmemopen stream");
520 return (B_FALSE);
521 }
522
523 if (setvbuf(f, NULL, _IOLBF, BUFSIZ) != 0) {
524 warn("failed to set buffer to line-buffered mode");
525 }
526
527 return (fmemopen_fill_fwrite(f, 128, B_TRUE));
528 }
529
530 static boolean_t
fmemopen_fwrite_nobuf(void)531 fmemopen_fwrite_nobuf(void)
532 {
533 FILE *f;
534
535 f = fmemopen(NULL, 128, "w+");
536 if (f == NULL) {
537 warn("failed to open fmemopen stream");
538 return (B_FALSE);
539 }
540
541 if (setvbuf(f, NULL, _IONBF, 0) != 0) {
542 warn("failed to set buffer to non-buffered mode");
543 }
544
545 return (fmemopen_fill_fwrite(f, 128, B_FALSE));
546 }
547
548 static boolean_t
fmemopen_alt_fwrite_default(void)549 fmemopen_alt_fwrite_default(void)
550 {
551 FILE *f;
552
553 f = fmemopen(NULL, 128, "w+");
554 if (f == NULL) {
555 warn("failed to open fmemopen stream");
556 return (B_FALSE);
557 }
558
559 return (fmemopen_fill_alt_fwrite(f, 128, B_TRUE));
560 }
561
562 static boolean_t
fmemopen_alt_fwrite_lbuf(void)563 fmemopen_alt_fwrite_lbuf(void)
564 {
565 FILE *f;
566
567 f = fmemopen(NULL, 128, "w+");
568 if (f == NULL) {
569 warn("failed to open fmemopen stream");
570 return (B_FALSE);
571 }
572
573 if (setvbuf(f, NULL, _IOLBF, BUFSIZ) != 0) {
574 warn("failed to set buffer to line-buffered mode");
575 }
576
577 return (fmemopen_fill_alt_fwrite(f, 128, B_TRUE));
578 }
579
580 static boolean_t
fmemopen_alt_fwrite_nobuf(void)581 fmemopen_alt_fwrite_nobuf(void)
582 {
583 FILE *f;
584
585 f = fmemopen(NULL, 128, "w+");
586 if (f == NULL) {
587 warn("failed to open fmemopen stream");
588 return (B_FALSE);
589 }
590
591 if (setvbuf(f, NULL, _IONBF, 0) != 0) {
592 warn("failed to set buffer to non-buffered mode");
593 }
594
595 return (fmemopen_fill_alt_fwrite(f, 128, B_FALSE));
596 }
597
598 static boolean_t
fmemopen_fputs_default(void)599 fmemopen_fputs_default(void)
600 {
601 FILE *f;
602
603 f = fmemopen(NULL, 128, "w+");
604 if (f == NULL) {
605 warn("failed to open fmemopen stream");
606 return (B_FALSE);
607 }
608
609 return (fmemopen_fill_fputs(f, 128, B_TRUE));
610 }
611
612 static boolean_t
fmemopen_fputs_lbuf(void)613 fmemopen_fputs_lbuf(void)
614 {
615 FILE *f;
616
617 f = fmemopen(NULL, 128, "w+");
618 if (f == NULL) {
619 warn("failed to open fmemopen stream");
620 return (B_FALSE);
621 }
622
623 if (setvbuf(f, NULL, _IOLBF, BUFSIZ) != 0) {
624 warn("failed to set buffer to line-buffered mode");
625 }
626
627 return (fmemopen_fill_fputs(f, 128, B_TRUE));
628 }
629
630 static boolean_t
fmemopen_fputs_nobuf(void)631 fmemopen_fputs_nobuf(void)
632 {
633 FILE *f;
634
635 f = fmemopen(NULL, 128, "w+");
636 if (f == NULL) {
637 warn("failed to open fmemopen stream");
638 return (B_FALSE);
639 }
640
641 if (setvbuf(f, NULL, _IONBF, 0) != 0) {
642 warn("failed to set buffer to non-buffered mode");
643 }
644
645 return (fmemopen_fill_fputs(f, 128, B_FALSE));
646 }
647
648 static boolean_t
memstream_check_seek(FILE * f,size_t len,int whence)649 memstream_check_seek(FILE *f, size_t len, int whence)
650 {
651 off_t o;
652 long l;
653 boolean_t ret = B_TRUE;
654
655 if (fseeko(f, 0, whence) != 0) {
656 warn("failed to seek, whence: %d", whence);
657 return (B_FALSE);
658 }
659
660 if ((o = ftello(f)) == -1) {
661 warn("failed to get offset from ftello");
662 ret = B_FALSE;
663 } else if (o < 0 || (size_t)o != len) {
664 warnx("found bad stream position: expected %zu, found: %zu",
665 len, (size_t)o);
666 ret = B_FALSE;
667 }
668
669 if ((l = ftell(f)) == -1) {
670 warn("failed to get offset from ftell");
671 ret = B_FALSE;
672 } else if (l < 0 || (size_t)l != len) {
673 warnx("found bad stream position: expected %zu, found: %zu",
674 len, (size_t)l);
675 ret = B_FALSE;
676 }
677
678 return (ret);
679 }
680
681 static boolean_t
fmemopen_defseek_r(void)682 fmemopen_defseek_r(void)
683 {
684 FILE *f;
685 size_t len = strlen(fmemopen_str1);
686 boolean_t ret, ret2;
687
688 f = fmemopen(fmemopen_str1, len, "r");
689 if (f == NULL) {
690 warn("failed to open fmemopen stream");
691 return (B_FALSE);
692 }
693
694 ret = memstream_check_seek(f, 0, SEEK_CUR);
695 ret2 = memstream_check_seek(f, len, SEEK_END);
696 (void) fclose(f);
697 return (ret && ret2);
698 }
699
700 static boolean_t
fmemopen_defseek_rp(void)701 fmemopen_defseek_rp(void)
702 {
703 FILE *f;
704 size_t len = strlen(fmemopen_str1);
705 boolean_t ret, ret2;
706
707 f = fmemopen(fmemopen_str1, len, "r+");
708 if (f == NULL) {
709 warn("failed to open fmemopen stream");
710 return (B_FALSE);
711 }
712
713 ret = memstream_check_seek(f, 0, SEEK_CUR);
714 ret2 = memstream_check_seek(f, len, SEEK_END);
715 (void) fclose(f);
716 return (ret && ret2);
717 }
718
719 static boolean_t
fmemopen_defseek_w(void)720 fmemopen_defseek_w(void)
721 {
722 FILE *f;
723 size_t len = strlen(fmemopen_str1);
724 boolean_t ret, ret2;
725 char *str;
726
727 if ((str = strdup(fmemopen_str1)) == NULL) {
728 warn("failed to duplicate string");
729 return (B_FALSE);
730 }
731
732 f = fmemopen(str, len, "w");
733 if (f == NULL) {
734 warn("failed to open fmemopen stream");
735 free(str);
736 return (B_FALSE);
737 }
738
739 ret = memstream_check_seek(f, 0, SEEK_CUR);
740 ret2 = memstream_check_seek(f, 0, SEEK_END);
741 (void) fclose(f);
742 free(str);
743 return (ret && ret2);
744 }
745
746 static boolean_t
fmemopen_defseek_wp(void)747 fmemopen_defseek_wp(void)
748 {
749 FILE *f;
750 size_t len = strlen(fmemopen_str1);
751 boolean_t ret, ret2;
752 char *str;
753
754 if ((str = strdup(fmemopen_str1)) == NULL) {
755 warn("failed to duplicate string");
756 return (B_FALSE);
757 }
758
759 f = fmemopen(str, len, "w+");
760 if (f == NULL) {
761 warn("failed to open fmemopen stream");
762 free(str);
763 return (B_FALSE);
764 }
765
766 ret = memstream_check_seek(f, 0, SEEK_CUR);
767 ret2 = memstream_check_seek(f, 0, SEEK_END);
768 (void) fclose(f);
769 free(str);
770 return (ret && ret2);
771 }
772
773 static boolean_t
fmemopen_defseek_a(void)774 fmemopen_defseek_a(void)
775 {
776 FILE *f;
777 size_t len = strlen(fmemopen_str1);
778 boolean_t ret, ret2;
779 char *str;
780
781 if ((str = strdup(fmemopen_str1)) == NULL) {
782 warn("failed to duplicate string");
783 return (B_FALSE);
784 }
785
786 f = fmemopen(str, len, "a");
787 if (f == NULL) {
788 warn("failed to open fmemopen stream");
789 free(str);
790 return (B_FALSE);
791 }
792
793 ret = memstream_check_seek(f, len, SEEK_CUR);
794 ret2 = memstream_check_seek(f, len, SEEK_END);
795 (void) fclose(f);
796 free(str);
797 return (ret && ret2);
798 }
799
800 static boolean_t
fmemopen_defseek_ap(void)801 fmemopen_defseek_ap(void)
802 {
803 FILE *f;
804 size_t len = strlen(fmemopen_str1);
805 boolean_t ret, ret2;
806 char *str;
807
808 if ((str = strdup(fmemopen_str1)) == NULL) {
809 warn("failed to duplicate string");
810 return (B_FALSE);
811 }
812
813 f = fmemopen(str, len, "a+");
814 if (f == NULL) {
815 warn("failed to open fmemopen stream");
816 free(str);
817 return (B_FALSE);
818 }
819
820 ret = memstream_check_seek(f, len, SEEK_CUR);
821 ret2 = memstream_check_seek(f, len, SEEK_END);
822 (void) fclose(f);
823 free(str);
824 return (ret && ret2);
825 }
826
827 static boolean_t
fmemopen_defseek_a_nbyte(void)828 fmemopen_defseek_a_nbyte(void)
829 {
830 FILE *f;
831 size_t len = strlen(fmemopen_str1);
832 boolean_t ret, ret2;
833 char *str;
834
835 if ((str = strdup(fmemopen_str1)) == NULL) {
836 warn("failed to duplicate string");
837 return (B_FALSE);
838 }
839 str[8] = '\0';
840
841 f = fmemopen(str, len, "a");
842 if (f == NULL) {
843 warn("failed to open fmemopen stream");
844 free(str);
845 return (B_FALSE);
846 }
847
848 ret = memstream_check_seek(f, 8, SEEK_CUR);
849 ret2 = memstream_check_seek(f, 8, SEEK_END);
850 (void) fclose(f);
851 free(str);
852 return (ret && ret2);
853 }
854
855 static boolean_t
fmemopen_defseek_ap_nbyte(void)856 fmemopen_defseek_ap_nbyte(void)
857 {
858 FILE *f;
859 size_t len = strlen(fmemopen_str1);
860 boolean_t ret, ret2;
861 char *str;
862
863 if ((str = strdup(fmemopen_str1)) == NULL) {
864 warn("failed to duplicate string");
865 return (B_FALSE);
866 }
867 str[12] = '\0';
868
869 f = fmemopen(str, len, "a+");
870 if (f == NULL) {
871 warn("failed to open fmemopen stream");
872 free(str);
873 return (B_FALSE);
874 }
875
876 ret = memstream_check_seek(f, 12, SEEK_CUR);
877 ret2 = memstream_check_seek(f, 12, SEEK_END);
878 (void) fclose(f);
879 free(str);
880 return (ret && ret2);
881 }
882
883 static boolean_t
fmemopen_defseek_ap_null(void)884 fmemopen_defseek_ap_null(void)
885 {
886 FILE *f;
887 size_t len = strlen(fmemopen_str1);
888 boolean_t ret, ret2;
889
890 f = fmemopen(NULL, len, "a+");
891 if (f == NULL) {
892 warn("failed to open fmemopen stream");
893 return (B_FALSE);
894 }
895
896 ret = memstream_check_seek(f, 0, SEEK_CUR);
897 ret2 = memstream_check_seek(f, 0, SEEK_END);
898 (void) fclose(f);
899 return (ret && ret2);
900 }
901
902 static boolean_t
fmemopen_read_eof_fgetc(void)903 fmemopen_read_eof_fgetc(void)
904 {
905 FILE *f;
906 size_t len = strlen(fmemopen_str1);
907 boolean_t ret = B_TRUE, ret2, ret3;
908
909 f = fmemopen(fmemopen_str1, len, "r");
910 if (f == NULL) {
911 warn("failed to open fmemopen stream");
912 return (B_FALSE);
913 }
914
915 while (fgetc(f) != EOF) {
916 continue;
917 }
918
919 if (feof(f) == 0) {
920 warnx("stream not at end of EOF");
921 ret = B_FALSE;
922 }
923
924 ret2 = memstream_check_seek(f, len, SEEK_CUR);
925 ret3 = memstream_check_seek(f, len, SEEK_END);
926 (void) fclose(f);
927 return (ret && ret2 && ret3);
928 }
929
930 static boolean_t
fmemopen_read_eof_fgets(void)931 fmemopen_read_eof_fgets(void)
932 {
933 FILE *f;
934 size_t len = strlen(fmemopen_str1);
935 boolean_t ret = B_TRUE, ret2, ret3;
936 char buf[BUFSIZ];
937
938 f = fmemopen(fmemopen_str1, len, "r");
939 if (f == NULL) {
940 warn("failed to open fmemopen stream");
941 return (B_FALSE);
942 }
943
944 while (fgets(buf, sizeof (buf), f) != NULL) {
945 continue;
946 }
947
948 if (feof(f) == 0) {
949 warnx("stream not at end of EOF");
950 ret = B_FALSE;
951 }
952
953 ret2 = memstream_check_seek(f, len, SEEK_CUR);
954 ret3 = memstream_check_seek(f, len, SEEK_END);
955 (void) fclose(f);
956 return (ret && ret2 && ret3);
957 }
958
959 static boolean_t
fmemopen_read_eof_fread(void)960 fmemopen_read_eof_fread(void)
961 {
962 FILE *f;
963 size_t len = strlen(fmemopen_str1);
964 boolean_t ret = B_TRUE, ret2, ret3;
965 char buf[BUFSIZ];
966
967 f = fmemopen(fmemopen_str1, len, "r");
968 if (f == NULL) {
969 warn("failed to open fmemopen stream");
970 return (B_FALSE);
971 }
972
973 while (fread(buf, sizeof (buf), 1, f) != 0) {
974 continue;
975 }
976
977 if (feof(f) == 0) {
978 warnx("stream not at end of EOF");
979 ret = B_FALSE;
980 }
981
982 ret2 = memstream_check_seek(f, len, SEEK_CUR);
983 ret3 = memstream_check_seek(f, len, SEEK_END);
984 (void) fclose(f);
985 return (ret && ret2 && ret3);
986 }
987
988 static boolean_t
fmemopen_read_eof_fread2(void)989 fmemopen_read_eof_fread2(void)
990 {
991 FILE *f;
992 size_t len = strlen(fmemopen_str1);
993 boolean_t ret = B_TRUE, ret2, ret3;
994 char buf[BUFSIZ];
995
996 f = fmemopen(fmemopen_str1, len, "r");
997 if (f == NULL) {
998 warn("failed to open fmemopen stream");
999 return (B_FALSE);
1000 }
1001
1002 while (fread(buf, 1, sizeof (buf), f) != 0) {
1003 continue;
1004 }
1005
1006 if (feof(f) == 0) {
1007 warnx("stream not at end of EOF");
1008 ret = B_FALSE;
1009 }
1010
1011 ret2 = memstream_check_seek(f, len, SEEK_CUR);
1012 ret3 = memstream_check_seek(f, len, SEEK_END);
1013 (void) fclose(f);
1014 return (ret && ret2 && ret3);
1015 }
1016
1017 static boolean_t
fmemopen_bad_seeks(void)1018 fmemopen_bad_seeks(void)
1019 {
1020 FILE *f;
1021 boolean_t ret = B_TRUE;
1022 size_t len = strlen(fmemopen_str1);
1023 uint_t i;
1024 struct {
1025 int ret;
1026 int whence;
1027 long off;
1028 long newpos;
1029 } seeks[] = {
1030 { 0, SEEK_CUR, 0, 0 },
1031 { -1, SEEK_CUR, -1, 0 },
1032 { -1, SEEK_SET, -5, 0 },
1033 { -1, SEEK_END, -128, 0 },
1034 { -1, SEEK_END, 1, 0 },
1035 { -1, SEEK_SET, 128, 0 },
1036 { 0, SEEK_SET, 16, 16 },
1037 { -1, SEEK_CUR, -20, 16 },
1038 { 0, SEEK_CUR, -16, 0 },
1039 };
1040
1041 f = fmemopen(fmemopen_str1, len, "r");
1042 if (f == NULL) {
1043 warn("failed to open fmemopen stream");
1044 return (B_FALSE);
1045 }
1046
1047 for (i = 0; i < ARRAY_SIZE(seeks); i++) {
1048 int r;
1049
1050 r = fseek(f, seeks[i].off, seeks[i].whence);
1051 if (r != seeks[i].ret) {
1052 warnx("found bad return value for seek %d/%ld, "
1053 "expected %d, found %d", seeks[i].whence,
1054 seeks[i].off, seeks[i].ret, r);
1055 ret = B_FALSE;
1056 }
1057
1058 ret &= memstream_check_seek(f, seeks[i].newpos, SEEK_CUR);
1059 }
1060
1061 (void) fclose(f);
1062 return (ret);
1063 }
1064
1065 static boolean_t
fmemopen_open_trunc(void)1066 fmemopen_open_trunc(void)
1067 {
1068 char buf[16];
1069 FILE *f;
1070 boolean_t ret = B_TRUE;
1071
1072 (void) memset(buf, 'a', sizeof (buf));
1073 f = fmemopen(buf, sizeof (buf), "w+");
1074 if (f == NULL) {
1075 warn("failed to create fmemopen stream");
1076 return (B_FALSE);
1077 }
1078
1079 if (buf[0] != '\0') {
1080 warnx("w+ mode didn't truncate the buffer");
1081 ret = B_FALSE;
1082 }
1083
1084 (void) fclose(f);
1085 return (ret);
1086 }
1087
1088 static boolean_t
fmemopen_write_nul(void)1089 fmemopen_write_nul(void)
1090 {
1091 char buf[BUFSIZ];
1092 FILE *f;
1093 boolean_t ret = B_TRUE;
1094 size_t npos = sizeof (buf) - 32;
1095
1096 (void) memset(buf, 'a', sizeof (buf));
1097
1098 f = fmemopen(buf, sizeof (buf), "w");
1099 if (f == NULL) {
1100 warn("failed to create fmemopen stream");
1101 return (B_FALSE);
1102 }
1103
1104 if (fputc('b', f) != 'b') {
1105 warn("failed to write 'b' character to stream");
1106 ret = B_FALSE;
1107 }
1108
1109 if (fflush(f) != 0) {
1110 warn("failed to flush stream");
1111 ret = B_FALSE;
1112 }
1113
1114 if (buf[0] != 'b' || buf[1] != '\0') {
1115 warn("stream didn't properly write character and nul");
1116 ret = B_FALSE;
1117 }
1118
1119 if (fseek(f, sizeof (buf) - 32, SEEK_SET)) {
1120 warn("failed to seek stream");
1121 ret = B_FALSE;
1122 }
1123
1124 if (fflush(f) != 0) {
1125 warn("failed to flush stream");
1126 ret = B_FALSE;
1127 }
1128
1129 if (buf[npos] != 'a' || buf[npos - 1] != 'a' ||
1130 buf[npos + 1] != 'a') {
1131 warnx("seeking incorrectly inserted a nul");
1132 ret = B_FALSE;
1133 }
1134
1135 (void) fclose(f);
1136
1137 if (buf[npos] != 'a' || buf[npos - 1] != 'a' ||
1138 buf[npos + 1] != 'a') {
1139 warnx("seeking incorrectly inserted a nul");
1140 ret = B_FALSE;
1141 }
1142
1143 return (ret);
1144 }
1145
1146 static boolean_t
fmemopen_append_nul(void)1147 fmemopen_append_nul(void)
1148 {
1149 char buf[32], buf2[32];
1150 FILE *f;
1151 boolean_t ret = B_TRUE;
1152
1153 (void) memset(buf, 'a', sizeof (buf));
1154 buf[8] = '\0';
1155
1156 f = fmemopen(buf, sizeof (buf), "a");
1157 if (f == NULL) {
1158 warn("failed to create fmemopen stream");
1159 return (B_FALSE);
1160 }
1161
1162 if (fputc('b', f) != 'b') {
1163 warn("failed to write 'b' character to stream");
1164 ret = B_FALSE;
1165 }
1166
1167 if (fflush(f) != 0) {
1168 warn("failed to flush stream");
1169 ret = B_FALSE;
1170 }
1171
1172 if (buf[8] != 'b' || buf[9] != '\0') {
1173 warn("stream didn't properly write character and nul");
1174 ret = B_FALSE;
1175 }
1176
1177 /*
1178 * Append mode shouldn't insert a NUL if we write the entire buffer.
1179 */
1180 (void) memset(buf2, 'b', sizeof (buf2));
1181 if (fwrite(buf2, sizeof (buf2) - ftell(f), 1, f) != 1) {
1182 warn("failed to write buf2");
1183 ret = B_FALSE;
1184 }
1185
1186 if (fflush(f) != 0) {
1187 warn("failed to flush stream");
1188 ret = B_FALSE;
1189 }
1190
1191 if (buf[sizeof (buf) - 1] != 'b') {
1192 warnx("found invalid character: %x", buf[sizeof (buf) - 1]);
1193 ret = B_FALSE;
1194 }
1195
1196 (void) fclose(f);
1197
1198 if (buf[sizeof (buf) - 1] != 'b') {
1199 warnx("found invalid character: %x", buf[sizeof (buf) - 1]);
1200 ret = B_FALSE;
1201 }
1202
1203 return (ret);
1204 }
1205
1206 static boolean_t
fmemopen_read_nul(void)1207 fmemopen_read_nul(void)
1208 {
1209 char buf[32];
1210 FILE *f;
1211
1212 (void) memset(buf, '\0', sizeof (buf));
1213
1214 f = fmemopen(buf, sizeof (buf), "r+");
1215 if (f == NULL) {
1216 warn("failed to create fmemopen stream");
1217 return (B_FALSE);
1218 }
1219
1220 if (fgetc(f) != '\0') {
1221 warnx("failed to read nul character");
1222 return (B_FALSE);
1223 }
1224
1225 (void) fclose(f);
1226 return (B_TRUE);
1227 }
1228
1229 static boolean_t
open_memstream_def_seek(void)1230 open_memstream_def_seek(void)
1231 {
1232 char *c;
1233 size_t s;
1234 FILE *f;
1235 boolean_t ret, ret2;
1236
1237 if ((f = open_memstream(&c, &s)) == NULL) {
1238 warn("failed to call open_memstream()");
1239 return (B_FALSE);
1240 }
1241
1242 ret = memstream_check_seek(f, 0, SEEK_CUR);
1243 ret2 = memstream_check_seek(f, 0, SEEK_END);
1244 (void) fclose(f);
1245 free(c);
1246 return (ret && ret2);
1247 }
1248
1249 static boolean_t
open_wmemstream_def_seek(void)1250 open_wmemstream_def_seek(void)
1251 {
1252 wchar_t *c;
1253 size_t s;
1254 FILE *f;
1255 boolean_t ret, ret2;
1256
1257 if ((f = open_wmemstream(&c, &s)) == NULL) {
1258 warn("failed to call open_wmemstream()");
1259 return (B_FALSE);
1260 }
1261
1262 ret = memstream_check_seek(f, 0, SEEK_CUR);
1263 ret2 = memstream_check_seek(f, 0, SEEK_END);
1264 (void) fclose(f);
1265 free(c);
1266 return (ret && ret2);
1267 }
1268
1269 static boolean_t
open_memstream_no_read(void)1270 open_memstream_no_read(void)
1271 {
1272 char *c;
1273 size_t s;
1274 FILE *f;
1275 boolean_t ret = B_TRUE;
1276
1277 if ((f = open_memstream(&c, &s)) == NULL) {
1278 warn("failed to call open_memstream()");
1279 return (B_FALSE);
1280 }
1281
1282 if (fgetc(f) != EOF) {
1283 warnx("read succeeded when it should have failed");
1284 ret = B_FALSE;
1285 }
1286
1287 if (errno != EBADF) {
1288 warnx("found wrong errno, expected %d, found %d", EBADF, errno);
1289 ret = B_FALSE;
1290 }
1291
1292 (void) fclose(f);
1293 free(c);
1294 return (ret);
1295 }
1296
1297 static boolean_t
open_wmemstream_no_read(void)1298 open_wmemstream_no_read(void)
1299 {
1300 wchar_t *c;
1301 size_t s;
1302 FILE *f;
1303 boolean_t ret = B_TRUE;
1304
1305 if ((f = open_wmemstream(&c, &s)) == NULL) {
1306 warn("failed to call open_wmemstream()");
1307 return (B_FALSE);
1308 }
1309
1310 if (fgetc(f) != EOF) {
1311 warnx("read succeeded when it should have failed");
1312 ret = B_FALSE;
1313 }
1314
1315 if (errno != EBADF) {
1316 warnx("found wrong errno, expected %d, found %d", EBADF, errno);
1317 ret = B_FALSE;
1318 }
1319
1320 (void) fclose(f);
1321 free(c);
1322 return (ret);
1323 }
1324
1325 static boolean_t
open_memstream_bad_flush(void)1326 open_memstream_bad_flush(void)
1327 {
1328 char *c;
1329 size_t s;
1330 FILE *f;
1331 boolean_t ret = B_TRUE;
1332
1333 if ((f = open_memstream(&c, &s)) == NULL) {
1334 warn("failed to call open_memstream()");
1335 return (B_FALSE);
1336 }
1337
1338 /* Force the buffer to exist */
1339 if (fputc('a', f) != 'a') {
1340 warn("failed to write character to buffer");
1341 ret = B_FALSE;
1342 }
1343
1344 if (fseek(f, BUFSIZ * 2 + 1, SEEK_END) != 0) {
1345 warn("Failed to seek beyond buffer size");
1346 ret = B_FALSE;
1347 }
1348
1349 umem_setmtbf(1);
1350 if (fputc('a', f) != 'a') {
1351 warn("failed to write character to buffer");
1352 ret = B_FALSE;
1353 }
1354
1355 if (fflush(f) != EOF) {
1356 warnx("fflush succeeded when it should have failed");
1357 }
1358
1359 if (errno != EAGAIN) {
1360 warnx("bad errno, found %d, expected %d", errno, EAGAIN);
1361 }
1362 umem_setmtbf(0);
1363
1364 (void) fclose(f);
1365 free(c);
1366 return (ret);
1367 }
1368
1369 static boolean_t
open_wmemstream_bad_flush(void)1370 open_wmemstream_bad_flush(void)
1371 {
1372 wchar_t *c;
1373 size_t s;
1374 FILE *f;
1375 boolean_t ret = B_TRUE;
1376
1377 if ((f = open_wmemstream(&c, &s)) == NULL) {
1378 warn("failed to call open_wmemstream()");
1379 return (B_FALSE);
1380 }
1381
1382 /* Force the buffer to exist */
1383 if (fputwc('a', f) != 'a') {
1384 warn("failed to write character to buffer");
1385 ret = B_FALSE;
1386 }
1387
1388 if (fseek(f, BUFSIZ * 2 + 1, SEEK_END) != 0) {
1389 warn("Failed to seek beyond buffer size");
1390 ret = B_FALSE;
1391 }
1392
1393 umem_setmtbf(1);
1394 if (fputc('a', f) != 'a') {
1395 warn("failed to write character to buffer");
1396 ret = B_FALSE;
1397 }
1398
1399 if (fflush(f) != EOF) {
1400 warnx("fflush succeeded when it should have failed");
1401 }
1402
1403 if (errno != EAGAIN) {
1404 warnx("bad errno, found %d, expected %d", errno, EAGAIN);
1405 }
1406 umem_setmtbf(0);
1407
1408 (void) fclose(f);
1409 free(c);
1410 return (ret);
1411 }
1412
1413 static boolean_t
memstream_bad_seek(void)1414 memstream_bad_seek(void)
1415 {
1416 FILE *f, *fw;
1417 boolean_t ret = B_TRUE;
1418 uint_t i;
1419 char *c;
1420 wchar_t *w;
1421 size_t s1, s2;
1422 struct {
1423 int ret;
1424 int whence;
1425 long off;
1426 long newpos;
1427 } seeks[] = {
1428 { 0, SEEK_CUR, 0, 0 },
1429 { -1, SEEK_CUR, -1, 0 },
1430 { -1, SEEK_SET, -5, 0 },
1431 { -1, SEEK_END, -5, 0 },
1432 { 0, SEEK_SET, 16, 16 },
1433 { -1, SEEK_CUR, -20, 16 },
1434 { 0, SEEK_CUR, -16, 0 },
1435 };
1436
1437 f = open_memstream(&c, &s1);
1438 fw = open_wmemstream(&w, &s2);
1439 if (f == NULL || fw == NULL) {
1440 warnx("failed to create memory streams");
1441 return (B_FALSE);
1442 }
1443
1444 for (i = 0; i < ARRAY_SIZE(seeks); i++) {
1445 int r;
1446
1447 r = fseek(f, seeks[i].off, seeks[i].whence);
1448 if (r != seeks[i].ret) {
1449 warnx("found bad return value for seek %d/%ld, "
1450 "expected %d, found %d", seeks[i].whence,
1451 seeks[i].off, seeks[i].ret, r);
1452 ret = B_FALSE;
1453 }
1454
1455 ret &= memstream_check_seek(f, seeks[i].newpos, SEEK_CUR);
1456
1457 r = fseek(fw, seeks[i].off, seeks[i].whence);
1458 if (r != seeks[i].ret) {
1459 warnx("found bad return value for seek %d/%ld, "
1460 "expected %d, found %d", seeks[i].whence,
1461 seeks[i].off, seeks[i].ret, r);
1462 ret = B_FALSE;
1463 }
1464
1465 ret &= memstream_check_seek(fw, seeks[i].newpos, SEEK_CUR);
1466 }
1467
1468 (void) fclose(f);
1469 (void) fclose(fw);
1470 free(c);
1471 free(w);
1472 return (ret);
1473 }
1474
1475 static boolean_t
open_memstream_append_nul(void)1476 open_memstream_append_nul(void)
1477 {
1478 char *c;
1479 size_t s;
1480 FILE *f;
1481 boolean_t ret = B_TRUE;
1482
1483 if ((f = open_memstream(&c, &s)) == NULL) {
1484 warn("failed to call open_memstream()");
1485 return (B_FALSE);
1486 }
1487
1488 if (fputc('a', f) != 'a') {
1489 warn("failed to write 'a' to stream");
1490 ret = B_FALSE;
1491 }
1492
1493 if (fflush(f) != 0) {
1494 warn("failed to flush stream");
1495 ret = B_FALSE;
1496 }
1497
1498 if (c[s] != '\0') {
1499 warnx("missing nul character, found %x", c[s]);
1500 ret = B_FALSE;
1501 }
1502
1503 if (fseek(f, arc4random_uniform(2 * BUFSIZ), SEEK_SET) != 0) {
1504 warn("failed to seek");
1505 ret = B_FALSE;
1506 }
1507
1508 if (fflush(f) != 0) {
1509 warn("failed to flush stream");
1510 ret = B_FALSE;
1511 }
1512
1513 if (c[s] != '\0') {
1514 warnx("missing nul character, found %x", c[s]);
1515 ret = B_FALSE;
1516 }
1517
1518 (void) fclose(f);
1519 free(c);
1520 return (ret);
1521 }
1522
1523 static boolean_t
open_wmemstream_append_nul(void)1524 open_wmemstream_append_nul(void)
1525 {
1526 wchar_t *c;
1527 size_t s;
1528 FILE *f;
1529 boolean_t ret = B_TRUE;
1530
1531 if ((f = open_wmemstream(&c, &s)) == NULL) {
1532 warn("failed to call open_wmemstream()");
1533 return (B_FALSE);
1534 }
1535
1536 if (fputwc('a', f) != 'a') {
1537 warn("failed to write 'a' to stream");
1538 ret = B_FALSE;
1539 }
1540
1541 if (fflush(f) != 0) {
1542 warn("failed to flush stream");
1543 ret = B_FALSE;
1544 }
1545
1546 if (c[s] != L'\0') {
1547 warnx("missing nul character, found %" _PRIxWC, c[s]);
1548 ret = B_FALSE;
1549 }
1550
1551 if (fseek(f, arc4random_uniform(2 * BUFSIZ), SEEK_SET) != 0) {
1552 warn("failed to seek");
1553 ret = B_FALSE;
1554 }
1555
1556 if (fflush(f) != 0) {
1557 warn("failed to flush stream");
1558 ret = B_FALSE;
1559 }
1560
1561 if (c[s] != L'\0') {
1562 warnx("missing nul character, found %" _PRIxWC, c[s]);
1563 ret = B_FALSE;
1564 }
1565
1566 (void) fclose(f);
1567 free(c);
1568 return (ret);
1569 }
1570
1571 static boolean_t
open_wmemstream_embed_nuls(void)1572 open_wmemstream_embed_nuls(void)
1573 {
1574 const char str[] = { 'H', 'e', 'l', 'l', 'o', '\0', 'w',
1575 'o', 'r', 'd' };
1576 const wchar_t wstr[] = { L'H', L'e', L'l', L'l', L'o', L'\0', L'w',
1577 L'o', L'r', L'd' };
1578 wchar_t *c;
1579 size_t s;
1580 FILE *f;
1581 boolean_t ret = B_TRUE;
1582
1583 if ((f = open_wmemstream(&c, &s)) == NULL) {
1584 warn("failed to call open_wmemstream()");
1585 return (B_FALSE);
1586 }
1587
1588 if (fwrite(str, sizeof (char), ARRAY_SIZE(str), f) == 0) {
1589 warn("failed to write data buffer");
1590 ret = B_FALSE;
1591 }
1592
1593 if (fflush(f) != 0) {
1594 warn("failed to flush data buffer");
1595 ret = B_FALSE;
1596 }
1597
1598 if (ARRAY_SIZE(wstr) != s) {
1599 warnx("size mismatch, wrote %zu chars, found %zu chars",
1600 ARRAY_SIZE(wstr), s);
1601 ret = B_FALSE;
1602 }
1603
1604 if (bcmp(wstr, c, sizeof (wstr)) != 0) {
1605 warnx("data not written in expected format");
1606 ret = B_FALSE;
1607 }
1608
1609 (void) fclose(f);
1610 free(c);
1611 return (ret);
1612 }
1613
1614 static boolean_t
open_wmemstream_wide_write(void)1615 open_wmemstream_wide_write(void)
1616 {
1617 size_t slen = wcslen(wstream_str);
1618 wchar_t *c;
1619 size_t s;
1620 FILE *f;
1621 boolean_t ret = B_TRUE;
1622
1623 if ((f = open_wmemstream(&c, &s)) == NULL) {
1624 warn("failed to call open_wmemstream()");
1625 return (B_FALSE);
1626 }
1627
1628 if (fputws(wstream_str, f) == -1) {
1629 warn("failed to write string");
1630 ret = B_FALSE;
1631 }
1632
1633 if (fflush(f) != 0) {
1634 warn("failed to flush stream");
1635 ret = B_FALSE;
1636 }
1637
1638 if (s != slen) {
1639 warnx("size mismatch, expected %zu chars, but found %zu",
1640 slen, s);
1641 ret = B_FALSE;
1642 }
1643
1644 if (wcscmp(wstream_str, c) != 0) {
1645 warnx("basic write doesn't match!");
1646 ret = B_FALSE;
1647 }
1648
1649 ret &= memstream_check_seek(f, slen, SEEK_CUR);
1650 ret &= memstream_check_seek(f, slen, SEEK_END);
1651
1652 (void) fclose(f);
1653 free(c);
1654 return (ret);
1655 }
1656
1657 /*
1658 * Make sure that if we seek somewhere and flush that it doesn't cause us to
1659 * grow.
1660 */
1661 static boolean_t
open_wmemstream_seek_grow(void)1662 open_wmemstream_seek_grow(void)
1663 {
1664 size_t slen = wcslen(wstream_str);
1665 wchar_t *c;
1666 size_t s;
1667 FILE *f;
1668 boolean_t ret = B_TRUE;
1669
1670 if ((f = open_wmemstream(&c, &s)) == NULL) {
1671 warn("failed to call open_wmemstream()");
1672 return (B_FALSE);
1673 }
1674
1675 if (fflush(f) != 0) {
1676 warn("failed to flush stream");
1677 ret = B_FALSE;
1678 }
1679
1680 if (s != 0) {
1681 warn("bad initial size");
1682 ret = B_FALSE;
1683 }
1684
1685 ret &= memstream_check_seek(f, 0, SEEK_CUR);
1686 ret &= memstream_check_seek(f, 0, SEEK_END);
1687 if (fseek(f, 2048, SEEK_SET) != 0) {
1688 warn("failed to seek");
1689 }
1690
1691 ret &= memstream_check_seek(f, 2048, SEEK_CUR);
1692
1693 if (fflush(f) != 0) {
1694 warn("failed to flush stream");
1695 ret = B_FALSE;
1696 }
1697
1698 if (s != 0) {
1699 warnx("bad size after seek");
1700 ret = B_FALSE;
1701 }
1702
1703 if (fputws(wstream_str, f) == -1) {
1704 warn("failed to write string");
1705 ret = B_FALSE;
1706 }
1707
1708 if (fflush(f) != 0) {
1709 warn("failed to flush stream");
1710 ret = B_FALSE;
1711 }
1712
1713 if (s != slen + 2048) {
1714 warnx("size is off after seek and write, found %zu", s);
1715 ret = B_FALSE;
1716 }
1717
1718 ret &= memstream_check_seek(f, s, SEEK_CUR);
1719 ret &= memstream_check_seek(f, s, SEEK_END);
1720
1721 (void) fclose(f);
1722 free(c);
1723 return (ret);
1724 }
1725
1726 static boolean_t
open_wmemstream_byte_writes(void)1727 open_wmemstream_byte_writes(void)
1728 {
1729 wchar_t *c;
1730 size_t s, len, i;
1731 FILE *f;
1732 boolean_t ret = B_TRUE;
1733
1734 if ((f = open_wmemstream(&c, &s)) == NULL) {
1735 warn("failed to call open_wmemstream()");
1736 return (B_FALSE);
1737 }
1738
1739 /*
1740 * Use non-buffered mode so that way we can make sure to detect mbs
1741 * state errors right away.
1742 */
1743 if (setvbuf(f, NULL, _IONBF, 0) != 0) {
1744 warnx("failed to set to non-buffered mode");
1745 ret = B_FALSE;
1746 }
1747
1748 len = wcslen(wstream_str);
1749 for (i = 0; i < len; i++) {
1750 char buf[MB_CUR_MAX + 1];
1751 int mblen, curmb;
1752
1753 mblen = wctomb(buf, wstream_str[i]);
1754
1755 if (mblen == -1) {
1756 warn("failed to convert wc %zu", i);
1757 ret = B_FALSE;
1758 continue;
1759 }
1760 for (curmb = 0; curmb < mblen; curmb++) {
1761 if (fputc(buf[curmb], f) == EOF) {
1762 warn("failed to write byte %d of wc %zu",
1763 curmb, i);
1764 ret = B_FALSE;
1765 }
1766 }
1767 }
1768
1769 if (fflush(f) != 0) {
1770 warn("failed to flush stream");
1771 ret = B_FALSE;
1772 }
1773
1774 if (s != len) {
1775 warnx("found wrong number of wide characters, expected %zu, "
1776 "found %zu", len + 1, s);
1777 ret = B_FALSE;
1778 }
1779
1780 if (wcscmp(c, wstream_str) != 0) {
1781 warnx("the wide character strings don't compare equally");
1782 ret = B_FALSE;
1783 }
1784
1785 (void) fclose(f);
1786 free(c);
1787 return (ret);
1788 }
1789
1790 static boolean_t
open_wmemstream_bad_seq(void)1791 open_wmemstream_bad_seq(void)
1792 {
1793 wchar_t *c, test = wstr_const[0];
1794 size_t s;
1795 FILE *f;
1796 char buf[MB_CUR_MAX + 1];
1797 boolean_t ret = B_TRUE;
1798
1799 if (wctomb(buf, test) == -1) {
1800 warn("failed to convert 光 to multi-byte sequence");
1801 return (B_FALSE);
1802 }
1803
1804 if ((f = open_wmemstream(&c, &s)) == NULL) {
1805 warn("failed to call open_wmemstream()");
1806 return (B_FALSE);
1807 }
1808
1809 /*
1810 * Make sure to use a non-buffered mode so that way writes immediately
1811 * get sent to the underlying stream.
1812 */
1813 if (setvbuf(f, NULL, _IONBF, 0) != 0) {
1814 warnx("failed to set to non-buffered mode");
1815 ret = B_FALSE;
1816 }
1817
1818 if (fputc(buf[0], f) == EOF) {
1819 warn("failed to write 0x%x to buffer", buf[0]);
1820 ret = B_FALSE;
1821 }
1822
1823 if (fputc(buf[0], f) != EOF) {
1824 warnx("successfully wrote 0x%x to buffer, but should have "
1825 "failed", buf[0]);
1826 ret = B_FALSE;
1827 }
1828
1829 if (errno != EIO) {
1830 warnx("found wrong errno, expected EIO, but found 0x%x", errno);
1831 ret = B_FALSE;
1832 }
1833
1834 (void) fclose(f);
1835 free(c);
1836 return (ret);
1837 }
1838
1839 static boolean_t
open_wmemstream_bad_seq_fflush(void)1840 open_wmemstream_bad_seq_fflush(void)
1841 {
1842 wchar_t *c, test = wstr_const[0];
1843 size_t s;
1844 FILE *f;
1845 char buf[MB_CUR_MAX + 1];
1846 boolean_t ret = B_TRUE;
1847
1848 if (wctomb(buf, test) == -1) {
1849 warn("failed to convert 光 to multi-byte sequence");
1850 return (B_FALSE);
1851 }
1852
1853 if ((f = open_wmemstream(&c, &s)) == NULL) {
1854 warn("failed to call open_wmemstream()");
1855 return (B_FALSE);
1856 }
1857
1858 if (fputc(buf[0], f) == EOF) {
1859 warn("failed to write 0x%x to buffer", buf[0]);
1860 ret = B_FALSE;
1861 }
1862
1863 if (fputc(buf[0], f) == EOF) {
1864 warn("failed to write bad byte 0x%x to buffer", buf[0]);
1865 ret = B_FALSE;
1866 }
1867
1868 if (fflush(f) == 0) {
1869 warn("fflush succeeded, expected failure");
1870 ret = B_FALSE;
1871 }
1872
1873 if (errno != EIO) {
1874 warn("found wrong errno, expected EIO, but found 0x%x", errno);
1875 ret = B_FALSE;
1876 }
1877
1878 (void) fclose(f);
1879 free(c);
1880 return (ret);
1881 }
1882
1883 /*
1884 * When writing individual bytes out, we need to make sure that we don't
1885 * incorrectly count buffered data as offsets like we do for other byte oriented
1886 * consumers of the ftell family.
1887 */
1888 static boolean_t
open_wmemstream_ftell(void)1889 open_wmemstream_ftell(void)
1890 {
1891 wchar_t *c, test = wstr_const[0];
1892 size_t s, i, wclen;
1893 FILE *f;
1894 char buf[MB_CUR_MAX + 1];
1895 boolean_t ret = B_TRUE;
1896 long loc;
1897
1898 if ((wclen = wctomb(buf, test)) == -1) {
1899 warn("failed to convert 光 to multi-byte sequence");
1900 return (B_FALSE);
1901 }
1902
1903 if ((f = open_wmemstream(&c, &s)) == NULL) {
1904 warn("failed to call open_wmemstream()");
1905 return (B_FALSE);
1906 }
1907
1908 if ((loc = ftell(f)) != 0) {
1909 warnx("stream at bad loc after start, found %ld, expected 0",
1910 loc);
1911 ret = B_FALSE;
1912 }
1913
1914 if (fputwc(test, f) == WEOF) {
1915 warn("failed to write wide character to stream");
1916 ret = B_FALSE;
1917 }
1918
1919 if ((loc = ftell(f)) != 1) {
1920 warnx("stream at bad loc after writing a single wide char, "
1921 "found %ld, expected 1", loc);
1922 ret = B_FALSE;
1923 }
1924
1925 for (i = 0; i < wclen - 1; i++) {
1926 if (fputc(buf[i], f) == EOF) {
1927 warn("failed to write mb char 0x%x", buf[i]);
1928 ret = B_FALSE;
1929 }
1930
1931 if ((loc = ftell(f)) != 1) {
1932 warnx("stream at bad loc after putting partial mb seq, "
1933 "found %ld, expected 1", loc);
1934 ret = B_FALSE;
1935 }
1936 }
1937
1938 /*
1939 * Only after we advance the final char should it be two.
1940 */
1941 if (fputc(buf[i], f) == EOF) {
1942 warn("failed to write mb char 0x%x", buf[i]);
1943 ret = B_FALSE;
1944 }
1945
1946 if ((loc = ftell(f)) != 2) {
1947 warnx("stream at bad loc after writing a mb seq, "
1948 "found %ld, expected 2", loc);
1949 ret = B_FALSE;
1950 }
1951
1952 if (fflush(f) != 0) {
1953 warn("failed to flush stream");
1954 ret = B_FALSE;
1955 }
1956
1957 if (s != 2) {
1958 warnx("size of stream is wrong, found %zu, expected 2", s);
1959 ret = B_FALSE;
1960 }
1961
1962 if (s != loc) {
1963 warnx("size of buffer, %zu does not match pre-fflush "
1964 "ftell loc: %ld", s, loc);
1965 ret = B_FALSE;
1966 }
1967
1968 loc = ftell(f);
1969 if (s != loc) {
1970 warnx("size of buffer, %zu does not match post-fflush "
1971 "ftell loc: %ld", s, loc);
1972 ret = B_FALSE;
1973 }
1974
1975 (void) fclose(f);
1976 free(c);
1977 return (ret);
1978 }
1979
1980
1981 typedef struct memstream_test {
1982 memstream_test_f mt_func;
1983 const char *mt_test;
1984 } memstream_test_t;
1985
1986 static const memstream_test_t memstream_tests[] = {
1987 { fmemopen_badmode, "fmemopen: bad mode argument" },
1988 { fmemopen_zerobuf1, "fmemopen: bad buffer size, valid buf" },
1989 { fmemopen_zerobuf2, "fmemopen: bad buffer size, NULL buf" },
1990 { fmemopen_nullbuf1, "fmemopen: invalid NULL buf, mode: r" },
1991 { fmemopen_nullbuf2, "fmemopen: invalid NULL buf, mode: w" },
1992 { fmemopen_nullbuf3, "fmemopen: invalid NULL buf, mode: a" },
1993 { fmemopen_nullbuf4, "fmemopen: invalid NULL buf, mode: ax" },
1994 { fmemopen_sizemax, "fmemopen: bad open ask for SIZE_MAX bytes" },
1995 { fmemopen_cantalloc, "fmemopen: simulate malloc failure at open" },
1996 { open_memstream_badbuf, "open_memstream: bad buf" },
1997 { open_memstream_badsize, "open_memstream: bad size" },
1998 { open_memstream_allnull, "open_memstream: bad buf and size" },
1999 { open_memstream_cantalloc, "open_memstream: simulate malloc failure "
2000 "at " "open" },
2001 { open_wmemstream_badbuf, "open_wmemstream: bad buf" },
2002 { open_wmemstream_badsize, "open_wmemstream: bad size" },
2003 { open_wmemstream_allnull, "open_wmemstream: bad buf and size" },
2004 { open_wmemstream_cantalloc, "open_wmemstream: simulate malloc "
2005 "failure at open" },
2006 { fmemopen_fill_default, "fmemopen: write beyond end of buffer: putc "
2007 "(buf smaller than BUFSIZ)" },
2008 { fmemopen_fill_lbuf, "fmemopen: write beyond end of buffer: putc "
2009 "(line buffering)" },
2010 { fmemopen_fill_nobuf, "fmemopen: write beyond end of buffer: putc "
2011 "(no stdio buffering)" },
2012 { fmemopen_fwrite_default, "fmemopen: write beyond end of buffer: "
2013 "fwrite (buf smaller than BUFSIZ)" },
2014 { fmemopen_fwrite_lbuf, "fmemopen: write beyond end of buffer: fwrite "
2015 "(line buffering)" },
2016 { fmemopen_fwrite_nobuf, "fmemopen: write beyond end of buffer: fwrite "
2017 "(no stdio buffering)" },
2018 { fmemopen_alt_fwrite_default, "fmemopen: write beyond end of buffer: "
2019 "fwrite 2 (buf smaller than BUFSIZ)" },
2020 { fmemopen_alt_fwrite_lbuf, "fmemopen: write beyond end of buffer: "
2021 "fwrite 2 (line buffering)" },
2022 { fmemopen_alt_fwrite_nobuf, "fmemopen: write beyond end of buffer: "
2023 "fwrite 2 (no stdio buffering)" },
2024 { fmemopen_fputs_default, "fmemopen: write beyond end of buffer: fputs "
2025 "(buf smaller than BUFSIZ)" },
2026 { fmemopen_fputs_lbuf, "fmemopen: write beyond end of buffer: fputs "
2027 "(line buffering)" },
2028 { fmemopen_fputs_nobuf, "fmemopen: write beyond end of buffer: fputs "
2029 "(no stdio buffering)" },
2030 { fmemopen_defseek_r, "fmemopen: default position and log. size, "
2031 "mode: r"},
2032 { fmemopen_defseek_rp, "fmemopen: default position and log. size, "
2033 "mode: r+"},
2034 { fmemopen_defseek_w, "fmemopen: default position and log. size, "
2035 "mode: w"},
2036 { fmemopen_defseek_wp, "fmemopen: default position and log. size, "
2037 "mode: w+"},
2038 { fmemopen_defseek_a, "fmemopen: default position and log. size, "
2039 "mode: a"},
2040 { fmemopen_defseek_ap, "fmemopen: default position and log. size, "
2041 "mode: a+"},
2042 { fmemopen_defseek_a_nbyte, "fmemopen: default position and log. size, "
2043 "mode: a, nul byte"},
2044 { fmemopen_defseek_ap_nbyte, "fmemopen: default position and log. "
2045 "size, mode: a+, nul byte"},
2046 { fmemopen_defseek_ap_null, "fmemopen: default position and log. size, "
2047 "mode: a+, NULL buf"},
2048 { fmemopen_read_eof_fgetc, "fmemopen: read until EOF with fgetc" },
2049 { fmemopen_read_eof_fgets, "fmemopen: read until EOF with fgets" },
2050 { fmemopen_read_eof_fread, "fmemopen: read until EOF with fread" },
2051 { fmemopen_read_eof_fread2, "fmemopen: read until EOF with fread 2" },
2052 { fmemopen_bad_seeks, "fmemopen: invalid seeks" },
2053 { fmemopen_open_trunc, "fmemopen: w+ mode truncates buffer" },
2054 { fmemopen_write_nul, "fmemopen: NULs properly inserted (w)" },
2055 { fmemopen_append_nul, "fmemopen: NULs properly inserted (a)" },
2056 { fmemopen_read_nul, "fmemopen: read NUL character normally" },
2057 { open_memstream_def_seek, "open_memstream: default position and "
2058 "logical size" },
2059 { open_wmemstream_def_seek, "wopen_memstream: default position and "
2060 "logical size" },
2061 { open_memstream_no_read, "open_memstream: read doesn't work" },
2062 { open_wmemstream_no_read, "open_wmemstream: read doesn't work" },
2063 { open_memstream_bad_flush, "open_memstream: flush failure due to "
2064 "induced memory failure" },
2065 { open_wmemstream_bad_flush, "open_wmemstream: flush failure due to "
2066 "induced memory failure" },
2067 { memstream_bad_seek, "open_[w]memstream: bad seeks" },
2068 { open_memstream_append_nul, "open_memstream: appends NULs" },
2069 { open_wmemstream_append_nul, "open_wmemstream: appends NULs" },
2070 { open_wmemstream_embed_nuls, "open_wmemstream: handles embedded "
2071 "NULs" },
2072 { open_wmemstream_wide_write, "open_wmemstream: write wide chars" },
2073 { open_wmemstream_seek_grow, "open_wmemstream: seeking doesn't grow" },
2074 { open_wmemstream_byte_writes, "open_wmemstream: Write mb sequences" },
2075 { open_wmemstream_bad_seq, "open_wmemstream: detect bad utf-8 "
2076 "sequence" },
2077 { open_wmemstream_bad_seq_fflush, "open_wmemstream: detect bad utf-8 "
2078 "sequence 2 (fflush)" },
2079 { open_wmemstream_ftell, "open_wmemstream: ftell buffering behavior" }
2080 };
2081
2082 int
main(void)2083 main(void)
2084 {
2085 uint_t i;
2086 uint_t passes = 0;
2087 uint_t ntests = ARRAY_SIZE(memstream_tests);
2088
2089 /*
2090 * Set a UTF-8 locale to make sure to exercise open_wmemstream()'s
2091 * mbstate logic in a more interesting way than ASCII.
2092 */
2093 (void) setlocale(LC_ALL, "en_US.UTF-8");
2094 for (i = 0; i < ntests; i++) {
2095 boolean_t r;
2096
2097 r = memstream_tests[i].mt_func();
2098 (void) fprintf(stderr, "TEST %s: %s\n", r ? "PASSED" : "FAILED",
2099 memstream_tests[i].mt_test);
2100 if (r) {
2101 passes++;
2102 }
2103 }
2104
2105 (void) printf("%d/%d test%s passed\n", passes, ntests,
2106 passes > 1 ? "s" : "");
2107 return (passes == ntests ? EXIT_SUCCESS : EXIT_FAILURE);
2108 }
2109