1 /* $NetBSD: t_fopen.c,v 1.6 2019/02/05 17:30:19 kamil Exp $ */ 2 3 /*- 4 * Copyright (c) 2011 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jukka Ruohonen. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 #include <sys/cdefs.h> 32 __RCSID("$NetBSD: t_fopen.c,v 1.6 2019/02/05 17:30:19 kamil Exp $"); 33 34 #include <sys/param.h> 35 #include <sys/types.h> 36 #include <sys/module.h> 37 #include <atf-c.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <limits.h> 41 #include <paths.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 static const char *path = "fopen"; 48 49 ATF_TC_WITH_CLEANUP(fdopen_close); 50 ATF_TC_HEAD(fdopen_close, tc) 51 { 52 atf_tc_set_md_var(tc, "descr", "See that descriptors are closed"); 53 } 54 55 ATF_TC_BODY(fdopen_close, tc) 56 { 57 FILE *f; 58 int fd; 59 60 /* 61 * Check that the file descriptor 62 * used to fdopen(3) a stream is 63 * closed once the stream is closed. 64 */ 65 fd = open(path, O_RDWR | O_CREAT); 66 67 ATF_REQUIRE(fd >= 0); 68 69 f = fdopen(fd, "w+"); 70 71 ATF_REQUIRE(f != NULL); 72 ATF_REQUIRE(fclose(f) == 0); 73 ATF_REQUIRE(close(fd) == -1); 74 ATF_REQUIRE(unlink(path) == 0); 75 } 76 77 ATF_TC_CLEANUP(fdopen_close, tc) 78 { 79 (void)unlink(path); 80 } 81 82 ATF_TC_WITH_CLEANUP(fdopen_err); 83 ATF_TC_HEAD(fdopen_err, tc) 84 { 85 atf_tc_set_md_var(tc, "descr", "Test errors from fdopen(3)"); 86 } 87 88 ATF_TC_BODY(fdopen_err, tc) 89 { 90 int fd; 91 92 fd = open(path, O_RDONLY | O_CREAT); 93 ATF_REQUIRE(fd >= 0); 94 95 errno = 0; 96 ATF_REQUIRE_ERRNO(EINVAL, fdopen(fd, "w") == NULL); 97 98 errno = 0; 99 ATF_REQUIRE_ERRNO(EINVAL, fdopen(fd, "a") == NULL); 100 101 ATF_REQUIRE(close(fd) == 0); 102 103 errno = 0; 104 ATF_REQUIRE_ERRNO(EBADF, fdopen(fd, "r") == NULL); 105 106 errno = 0; 107 ATF_REQUIRE_ERRNO(EBADF, fdopen(-1, "w+") == NULL); 108 109 (void)unlink(path); 110 } 111 112 ATF_TC_CLEANUP(fdopen_err, tc) 113 { 114 (void)unlink(path); 115 } 116 117 ATF_TC_WITH_CLEANUP(fdopen_seek); 118 ATF_TC_HEAD(fdopen_seek, tc) 119 { 120 atf_tc_set_md_var(tc, "descr", "Test stream position with fdopen(3)"); 121 } 122 123 ATF_TC_BODY(fdopen_seek, tc) 124 { 125 FILE *f; 126 int fd; 127 128 /* 129 * Verify that the file position associated 130 * with the stream corresponds with the offset 131 * set earlier for the file descriptor. 132 */ 133 fd = open(path, O_RDWR | O_CREAT); 134 135 ATF_REQUIRE(fd >= 0); 136 ATF_REQUIRE(write(fd, "garbage", 7) == 7); 137 ATF_REQUIRE(lseek(fd, 3, SEEK_SET) == 3); 138 139 f = fdopen(fd, "r+"); 140 141 ATF_REQUIRE(f != NULL); 142 ATF_REQUIRE(ftell(f) == 3); 143 ATF_REQUIRE(fclose(f) == 0); 144 ATF_REQUIRE(unlink(path) == 0); 145 } 146 147 ATF_TC_CLEANUP(fdopen_seek, tc) 148 { 149 (void)unlink(path); 150 } 151 152 ATF_TC_WITH_CLEANUP(fopen_err); 153 ATF_TC_HEAD(fopen_err, tc) 154 { 155 atf_tc_set_md_var(tc, "descr", "Test errors from fopen(3)"); 156 } 157 158 ATF_TC_BODY(fopen_err, tc) 159 { 160 static const char *mode[] = { 161 "x", "xr", "xr", "+r+", "R", "W+", " aXX", "Xr", " r+", "" }; 162 163 char buf[PATH_MAX + 1]; 164 size_t i; 165 FILE *f; 166 167 f = fopen(path, "w+"); 168 169 ATF_REQUIRE(f != NULL); 170 ATF_REQUIRE(fclose(f) == 0); 171 172 /* 173 * Note that also "invalid" characters 174 * may follow the mode-string whenever 175 * the first character is valid. 176 */ 177 for (i = 0; i < __arraycount(mode); i++) { 178 179 errno = 0; 180 f = fopen(path, mode[i]); 181 182 if (f == NULL && errno == EINVAL) 183 continue; 184 185 if (f != NULL) 186 (void)fclose(f); 187 188 atf_tc_fail_nonfatal("opened file as '%s'", mode[i]); 189 } 190 191 (void)unlink(path); 192 (void)memset(buf, 'x', sizeof(buf)); 193 194 errno = 0; 195 ATF_REQUIRE_ERRNO(EISDIR, fopen("/usr/bin", "w") == NULL); 196 197 errno = 0; 198 ATF_REQUIRE_ERRNO(ENOENT, fopen("/a/b/c/d/e/f", "r") == NULL); 199 200 errno = 0; 201 ATF_REQUIRE_ERRNO(ENAMETOOLONG, fopen(buf, "r+") == NULL); 202 } 203 204 ATF_TC_CLEANUP(fopen_err, tc) 205 { 206 (void)unlink(path); 207 } 208 209 ATF_TC_WITH_CLEANUP(fopen_append); 210 ATF_TC_HEAD(fopen_append, tc) 211 { 212 atf_tc_set_md_var(tc, "descr", "Test that append-mode works"); 213 } 214 215 ATF_TC_BODY(fopen_append, tc) 216 { 217 char buf[15]; 218 FILE *f; 219 220 (void)memset(buf, 'x', sizeof(buf)); 221 222 f = fopen(path, "w+"); 223 224 ATF_REQUIRE(f != NULL); 225 ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 226 ATF_REQUIRE(fclose(f) == 0); 227 228 f = fopen(path, "a"); 229 230 ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 231 ATF_REQUIRE(fclose(f) == 0); 232 233 f = fopen(path, "r"); 234 235 ATF_REQUIRE(fread(buf, 1, sizeof(buf), f) == 14); 236 ATF_REQUIRE(strncmp(buf, "garbagegarbage", 14) == 0); 237 238 ATF_REQUIRE(fclose(f) == 0); 239 ATF_REQUIRE(unlink(path) == 0); 240 } 241 242 ATF_TC_CLEANUP(fopen_append, tc) 243 { 244 (void)unlink(path); 245 } 246 247 ATF_TC_WITH_CLEANUP(fopen_mode); 248 ATF_TC_HEAD(fopen_mode, tc) 249 { 250 atf_tc_set_md_var(tc, "descr", "Test fopen(3) modes"); 251 } 252 253 ATF_TC_BODY(fopen_mode, tc) 254 { 255 size_t i; 256 FILE *f; 257 258 static const char *mode[] = { 259 "r", "r+", "w", "w+", "a", "a+", 260 "rb", "r+b", "wb", "w+b", "ab", "a+b", 261 "re", "r+e", "we", "w+e", "ae", "a+e", 262 "rf", "r+f", "wf", "w+f", "af", "a+f", 263 "rl", "r+l", "wl", "w+l", "al", "a+l" 264 }; 265 266 f = fopen(path, "w+"); 267 268 ATF_REQUIRE(f != NULL); 269 ATF_REQUIRE(fclose(f) == 0); 270 271 /* 272 * Verify that various modes work. 273 */ 274 for (i = 0; i < __arraycount(mode); i++) { 275 276 f = fopen(path, mode[i]); 277 278 if (f != NULL) { 279 ATF_REQUIRE(fclose(f) == 0); 280 continue; 281 } 282 283 atf_tc_fail_nonfatal("failed to open file as %s", mode[i]); 284 } 285 286 (void)unlink(path); 287 } 288 289 ATF_TC_CLEANUP(fopen_mode, tc) 290 { 291 (void)unlink(path); 292 } 293 294 static void 295 check_kernel_modular(void) 296 { 297 int err; 298 299 err = modctl(MODCTL_EXISTS, 0); 300 if (err == 0) return; 301 if (errno == ENOSYS) 302 atf_tc_skip("Kernel does not have 'options MODULAR'."); 303 if (errno == EPERM) 304 return; /* Module loading can be administratively forbidden */ 305 ATF_REQUIRE_EQ_MSG(errno, 0, "unexpected error %d from " 306 "modctl(MODCTL_EXISTS, 0)", errno); 307 } 308 309 static bool 310 is_module_present(const char *name) 311 { 312 bool found; 313 size_t len; 314 int count; 315 struct iovec iov; 316 modstat_t *ms; 317 318 for (len = 8192; ;) { 319 iov.iov_base = malloc(len); 320 iov.iov_len = len; 321 322 errno = 0; 323 324 if (modctl(MODCTL_STAT, &iov) != 0) { 325 fprintf(stderr, "modctl(MODCTL_STAT) failed: %s\n", 326 strerror(errno)); 327 atf_tc_fail("Failed to query module status"); 328 } 329 if (len >= iov.iov_len) 330 break; 331 free(iov.iov_base); 332 len = iov.iov_len; 333 } 334 335 found = false; 336 count = *(int *)iov.iov_base; 337 ms = (modstat_t *)((char *)iov.iov_base + sizeof(int)); 338 while (count > 0) { 339 if (strcmp(ms->ms_name, name) == 0) { 340 found = true; 341 break; 342 } 343 ms++; 344 count--; 345 } 346 347 free(iov.iov_base); 348 349 return found; 350 } 351 352 #define COMPAT10_MODNAME "compat_10" 353 354 ATF_TC(fopen_nullptr); 355 ATF_TC_HEAD(fopen_nullptr, tc) 356 { 357 atf_tc_set_md_var(tc, "descr", "Test fopen(3) with NULL path (without " 358 COMPAT10_MODNAME ")"); 359 } 360 361 ATF_TC_BODY(fopen_nullptr, tc) 362 { 363 bool compat10; 364 365 check_kernel_modular(); 366 compat10 = is_module_present(COMPAT10_MODNAME); 367 368 if (compat10) 369 atf_tc_skip("Kernel does have the " COMPAT10_MODNAME 370 " module loaded into the kernel"); 371 372 /* NULL shall trigger error */ 373 ATF_REQUIRE_ERRNO(EFAULT, fopen(NULL, "r") == NULL); 374 } 375 376 ATF_TC(fopen_nullptr_compat10); 377 ATF_TC_HEAD(fopen_nullptr_compat10, tc) 378 { 379 atf_tc_set_md_var(tc, "descr", "Test fopen(3) with NULL path (with " 380 COMPAT10_MODNAME ")"); 381 } 382 383 ATF_TC_BODY(fopen_nullptr_compat10, tc) 384 { 385 FILE *fp; 386 bool compat10; 387 388 check_kernel_modular(); 389 compat10 = is_module_present(COMPAT10_MODNAME); 390 391 if (!compat10) 392 atf_tc_skip("Kernel does not have the " COMPAT10_MODNAME 393 " module loaded into the kernel"); 394 395 /* NULL is translated to "." and shall success */ 396 fp = fopen(NULL, "r"); 397 398 ATF_REQUIRE(fp != NULL); 399 ATF_REQUIRE(fclose(fp) == 0); 400 } 401 402 ATF_TC(fopen_perm); 403 ATF_TC_HEAD(fopen_perm, tc) 404 { 405 atf_tc_set_md_var(tc, "descr", "Test permissions with fopen(3)"); 406 atf_tc_set_md_var(tc, "require.user", "unprivileged"); 407 } 408 409 ATF_TC_BODY(fopen_perm, tc) 410 { 411 412 errno = 0; 413 ATF_REQUIRE_ERRNO(EACCES, fopen("/bin/ls", "a+") == NULL); 414 415 errno = 0; 416 ATF_REQUIRE_ERRNO(EACCES, fopen("/bin/ls", "w+") == NULL); 417 } 418 419 ATF_TC(fopen_regular); 420 ATF_TC_HEAD(fopen_regular, tc) 421 { 422 atf_tc_set_md_var(tc, "descr", "Test fopen(3) with 'f' mode"); 423 } 424 425 ATF_TC_BODY(fopen_regular, tc) 426 { 427 static const char *mode[] = { "rf", "r+f", "wf", "w+f", "af", "a+f" }; 428 static const char *devs[] = { _PATH_DEVNULL }; 429 430 size_t i, j; 431 FILE *f; 432 433 for (i = 0; i < __arraycount(devs); i++) { 434 435 for (j = 0; j < __arraycount(mode); j++) { 436 437 errno = 0; 438 f = fopen(devs[i], mode[j]); 439 440 if (f == NULL && errno == EFTYPE) 441 continue; 442 443 if (f != NULL) { 444 (void)fclose(f); 445 446 atf_tc_fail_nonfatal("opened %s as %s", 447 devs[i], mode[j]); 448 } else { 449 atf_tc_fail_nonfatal( 450 "err %d (%s) from open of %s as %s", errno, 451 strerror(errno), devs[i], mode[j]); 452 } 453 } 454 } 455 } 456 457 static char linkpath[] = "symlink"; 458 459 ATF_TC_WITH_CLEANUP(fopen_symlink); 460 ATF_TC_HEAD(fopen_symlink, tc) 461 { 462 atf_tc_set_md_var(tc, "descr", "Test fopen(3) with 'l' mode"); 463 } 464 465 ATF_TC_BODY(fopen_symlink, tc) 466 { 467 static const char *mode[] = { "rl", "r+l", "wl", "w+l", "al", "a+l" }; 468 size_t j; 469 FILE *f; 470 471 ATF_CHECK(symlink("/dev/null", linkpath) != -1); 472 473 for (j = 0; j < __arraycount(mode); j++) { 474 475 errno = 0; 476 f = fopen(linkpath, mode[j]); 477 478 if (f == NULL && errno == EFTYPE) 479 continue; 480 481 if (f != NULL) { 482 (void)fclose(f); 483 484 atf_tc_fail_nonfatal("opened %s as %s", linkpath, 485 mode[j]); 486 } else { 487 atf_tc_fail_nonfatal( 488 "err %d (%s) from open of %s as %s", errno, 489 strerror(errno), linkpath, mode[j]); 490 } 491 } 492 ATF_REQUIRE(unlink(linkpath) == 0); 493 } 494 495 ATF_TC_CLEANUP(fopen_symlink, tc) 496 { 497 (void)unlink(linkpath); 498 } 499 500 ATF_TC_WITH_CLEANUP(fopen_seek); 501 ATF_TC_HEAD(fopen_seek, tc) 502 { 503 atf_tc_set_md_var(tc, "descr", "Test initial stream position"); 504 } 505 506 ATF_TC_BODY(fopen_seek, tc) 507 { 508 FILE *f; 509 510 f = fopen(path, "w+"); 511 512 ATF_REQUIRE(f != NULL); 513 ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 514 ATF_REQUIRE(fclose(f) == 0); 515 516 /* 517 * The position of the stream should be 518 * at the start, except for append-mode. 519 */ 520 f = fopen(path, "r"); 521 522 ATF_REQUIRE(f != NULL); 523 ATF_REQUIRE(ftello(f) == 0); 524 ATF_REQUIRE(fclose(f) == 0); 525 526 f = fopen(path, "a"); 527 528 ATF_REQUIRE(f != NULL); 529 ATF_REQUIRE(ftello(f) == 7); 530 ATF_REQUIRE(fclose(f) == 0); 531 ATF_REQUIRE(unlink(path) == 0); 532 } 533 534 ATF_TC_CLEANUP(fopen_seek, tc) 535 { 536 (void)unlink(path); 537 } 538 539 ATF_TC_WITH_CLEANUP(freopen_std); 540 ATF_TC_HEAD(freopen_std, tc) 541 { 542 atf_tc_set_md_var(tc, "descr", "A basic test of freopen(3)"); 543 } 544 545 ATF_TC_BODY(freopen_std, tc) 546 { 547 FILE *std[2] = { stdin, stdout }; 548 char buf[15]; 549 size_t i; 550 FILE *f; 551 552 /* 553 * Associate a standard stream with a custom stream. 554 * Then write to the standard stream and verify that 555 * the result now appears in the custom stream. 556 */ 557 for (i = 0; i < __arraycount(std); i++) { 558 559 (void)memset(buf, 'x', sizeof(buf)); 560 561 f = fopen(path, "w+"); 562 ATF_REQUIRE(f != NULL); 563 564 f = freopen(path, "w+", std[i]); 565 ATF_REQUIRE(f != NULL); 566 567 ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 568 ATF_REQUIRE(fprintf(std[i], "garbage") == 7); 569 ATF_REQUIRE(fclose(f) == 0); 570 571 f = fopen(path, "r"); 572 573 ATF_REQUIRE(f != NULL); 574 ATF_REQUIRE(fread(buf, 1, sizeof(buf), f) == 14); 575 ATF_REQUIRE(strncmp(buf, "garbagegarbage", 14) == 0); 576 577 ATF_REQUIRE(fclose(f) == 0); 578 } 579 580 ATF_REQUIRE(unlink(path) == 0); 581 } 582 583 ATF_TC_CLEANUP(freopen_std, tc) 584 { 585 (void)unlink(path); 586 } 587 588 ATF_TP_ADD_TCS(tp) 589 { 590 591 ATF_TP_ADD_TC(tp, fdopen_close); 592 ATF_TP_ADD_TC(tp, fdopen_err); 593 ATF_TP_ADD_TC(tp, fdopen_seek); 594 ATF_TP_ADD_TC(tp, fopen_append); 595 ATF_TP_ADD_TC(tp, fopen_err); 596 ATF_TP_ADD_TC(tp, fopen_mode); 597 ATF_TP_ADD_TC(tp, fopen_nullptr); 598 ATF_TP_ADD_TC(tp, fopen_nullptr_compat10); 599 ATF_TP_ADD_TC(tp, fopen_perm); 600 ATF_TP_ADD_TC(tp, fopen_regular); 601 ATF_TP_ADD_TC(tp, fopen_symlink); 602 ATF_TP_ADD_TC(tp, fopen_seek); 603 ATF_TP_ADD_TC(tp, freopen_std); 604 605 return atf_no_error(); 606 } 607