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 * 43 _umem_debug_init(void) 44 { 45 return ("default,verbose"); 46 } 47 48 const char * 49 _umem_logging_init(void) 50 { 51 return ("fail,contents"); 52 } 53 54 static boolean_t 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 76 fmemopen_badmode(void) 77 { 78 return (fmemopen_badopen(fmemopen_str1, strlen(fmemopen_str1), "foobar", 79 EINVAL)); 80 } 81 82 static boolean_t 83 fmemopen_zerobuf1(void) 84 { 85 return (fmemopen_badopen(fmemopen_str1, 0, "w", EINVAL)); 86 } 87 88 static boolean_t 89 fmemopen_zerobuf2(void) 90 { 91 return (fmemopen_badopen(NULL, 0, "w+", EINVAL)); 92 } 93 94 static boolean_t 95 fmemopen_nullbuf1(void) 96 { 97 return (fmemopen_badopen(NULL, 10, "r", EINVAL)); 98 } 99 100 static boolean_t 101 fmemopen_nullbuf2(void) 102 { 103 return (fmemopen_badopen(NULL, 10, "w", EINVAL)); 104 } 105 106 static boolean_t 107 fmemopen_nullbuf3(void) 108 { 109 return (fmemopen_badopen(NULL, 10, "a", EINVAL)); 110 } 111 112 static boolean_t 113 fmemopen_nullbuf4(void) 114 { 115 return (fmemopen_badopen(NULL, 10, "ax", EINVAL)); 116 } 117 118 static boolean_t 119 fmemopen_sizemax(void) 120 { 121 return (fmemopen_badopen(NULL, SIZE_MAX, "w+", ENOMEM)); 122 } 123 124 static boolean_t 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 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 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 174 open_memstream_badsize(void) 175 { 176 char *c; 177 return (open_memstream_badopen(&c, NULL, EINVAL)); 178 } 179 180 static boolean_t 181 open_memstream_allnull(void) 182 { 183 return (open_memstream_badopen(NULL, NULL, EINVAL)); 184 } 185 186 static boolean_t 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 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 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 238 open_wmemstream_badsize(void) 239 { 240 wchar_t *c; 241 return (open_wmemstream_badopen(&c, NULL, EINVAL)); 242 } 243 244 static boolean_t 245 open_wmemstream_allnull(void) 246 { 247 return (open_wmemstream_badopen(NULL, NULL, EINVAL)); 248 } 249 250 static boolean_t 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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