1 /* $NetBSD: t_fopen.c,v 1.8 2020/02/21 22:14:59 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.8 2020/02/21 22:14:59 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, 0600); 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, 0600); 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, 0600); 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 modstat_t m; 318 319 for (len = 8192; ;) { 320 iov.iov_base = malloc(len); 321 iov.iov_len = len; 322 323 errno = 0; 324 325 if (modctl(MODCTL_STAT, &iov) != 0) { 326 fprintf(stderr, "modctl(MODCTL_STAT) failed: %s\n", 327 strerror(errno)); 328 atf_tc_fail("Failed to query module status"); 329 } 330 if (len >= iov.iov_len) 331 break; 332 free(iov.iov_base); 333 len = iov.iov_len; 334 } 335 336 found = false; 337 count = *(int *)iov.iov_base; 338 ms = (modstat_t *)((char *)iov.iov_base + sizeof(int)); 339 while (count > 0) { 340 memcpy(&m, ms, sizeof(m)); 341 if (strcmp(m.ms_name, name) == 0) { 342 found = true; 343 break; 344 } 345 ms++; 346 count--; 347 } 348 349 free(iov.iov_base); 350 351 return found; 352 } 353 354 #define COMPAT10_MODNAME "compat_10" 355 356 ATF_TC(fopen_nullptr); 357 ATF_TC_HEAD(fopen_nullptr, tc) 358 { 359 atf_tc_set_md_var(tc, "descr", "Test fopen(3) with NULL path (without " 360 COMPAT10_MODNAME ")"); 361 } 362 363 ATF_TC_BODY(fopen_nullptr, tc) 364 { 365 bool compat10; 366 367 check_kernel_modular(); 368 compat10 = is_module_present(COMPAT10_MODNAME); 369 370 if (compat10) 371 atf_tc_skip("Kernel does have the " COMPAT10_MODNAME 372 " module loaded into the kernel"); 373 374 /* NULL shall trigger error */ 375 ATF_REQUIRE_ERRNO(EFAULT, fopen(NULL, "r") == NULL); 376 } 377 378 ATF_TC(fopen_nullptr_compat10); 379 ATF_TC_HEAD(fopen_nullptr_compat10, tc) 380 { 381 atf_tc_set_md_var(tc, "descr", "Test fopen(3) with NULL path (with " 382 COMPAT10_MODNAME ")"); 383 } 384 385 ATF_TC_BODY(fopen_nullptr_compat10, tc) 386 { 387 FILE *fp; 388 bool compat10; 389 390 check_kernel_modular(); 391 compat10 = is_module_present(COMPAT10_MODNAME); 392 393 if (!compat10) 394 atf_tc_skip("Kernel does not have the " COMPAT10_MODNAME 395 " module loaded into the kernel"); 396 397 /* NULL is translated to "." and shall success */ 398 fp = fopen(NULL, "r"); 399 400 ATF_REQUIRE(fp != NULL); 401 ATF_REQUIRE(fclose(fp) == 0); 402 } 403 404 ATF_TC(fopen_perm); 405 ATF_TC_HEAD(fopen_perm, tc) 406 { 407 atf_tc_set_md_var(tc, "descr", "Test permissions with fopen(3)"); 408 atf_tc_set_md_var(tc, "require.user", "unprivileged"); 409 } 410 411 ATF_TC_BODY(fopen_perm, tc) 412 { 413 414 errno = 0; 415 ATF_REQUIRE_ERRNO(EACCES, fopen("/bin/ls", "a+") == NULL); 416 417 errno = 0; 418 ATF_REQUIRE_ERRNO(EACCES, fopen("/bin/ls", "w+") == NULL); 419 } 420 421 ATF_TC(fopen_regular); 422 ATF_TC_HEAD(fopen_regular, tc) 423 { 424 atf_tc_set_md_var(tc, "descr", "Test fopen(3) with 'f' mode"); 425 } 426 427 ATF_TC_BODY(fopen_regular, tc) 428 { 429 static const char *mode[] = { "rf", "r+f", "wf", "w+f", "af", "a+f" }; 430 static const char *devs[] = { _PATH_DEVNULL }; 431 432 size_t i, j; 433 FILE *f; 434 435 for (i = 0; i < __arraycount(devs); i++) { 436 437 for (j = 0; j < __arraycount(mode); j++) { 438 439 errno = 0; 440 f = fopen(devs[i], mode[j]); 441 442 if (f == NULL && errno == EFTYPE) 443 continue; 444 445 if (f != NULL) { 446 (void)fclose(f); 447 448 atf_tc_fail_nonfatal("opened %s as %s", 449 devs[i], mode[j]); 450 } else { 451 atf_tc_fail_nonfatal( 452 "err %d (%s) from open of %s as %s", errno, 453 strerror(errno), devs[i], mode[j]); 454 } 455 } 456 } 457 } 458 459 static char linkpath[] = "symlink"; 460 461 ATF_TC_WITH_CLEANUP(fopen_symlink); 462 ATF_TC_HEAD(fopen_symlink, tc) 463 { 464 atf_tc_set_md_var(tc, "descr", "Test fopen(3) with 'l' mode"); 465 } 466 467 ATF_TC_BODY(fopen_symlink, tc) 468 { 469 static const char *mode[] = { "rl", "r+l", "wl", "w+l", "al", "a+l" }; 470 size_t j; 471 FILE *f; 472 473 ATF_CHECK(symlink("/dev/null", linkpath) != -1); 474 475 for (j = 0; j < __arraycount(mode); j++) { 476 477 errno = 0; 478 f = fopen(linkpath, mode[j]); 479 480 if (f == NULL && errno == EFTYPE) 481 continue; 482 483 if (f != NULL) { 484 (void)fclose(f); 485 486 atf_tc_fail_nonfatal("opened %s as %s", linkpath, 487 mode[j]); 488 } else { 489 atf_tc_fail_nonfatal( 490 "err %d (%s) from open of %s as %s", errno, 491 strerror(errno), linkpath, mode[j]); 492 } 493 } 494 ATF_REQUIRE(unlink(linkpath) == 0); 495 } 496 497 ATF_TC_CLEANUP(fopen_symlink, tc) 498 { 499 (void)unlink(linkpath); 500 } 501 502 ATF_TC_WITH_CLEANUP(fopen_seek); 503 ATF_TC_HEAD(fopen_seek, tc) 504 { 505 atf_tc_set_md_var(tc, "descr", "Test initial stream position"); 506 } 507 508 ATF_TC_BODY(fopen_seek, tc) 509 { 510 FILE *f; 511 512 f = fopen(path, "w+"); 513 514 ATF_REQUIRE(f != NULL); 515 ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 516 ATF_REQUIRE(fclose(f) == 0); 517 518 /* 519 * The position of the stream should be 520 * at the start, except for append-mode. 521 */ 522 f = fopen(path, "r"); 523 524 ATF_REQUIRE(f != NULL); 525 ATF_REQUIRE(ftello(f) == 0); 526 ATF_REQUIRE(fclose(f) == 0); 527 528 f = fopen(path, "a"); 529 530 ATF_REQUIRE(f != NULL); 531 ATF_REQUIRE(ftello(f) == 7); 532 ATF_REQUIRE(fclose(f) == 0); 533 ATF_REQUIRE(unlink(path) == 0); 534 } 535 536 ATF_TC_CLEANUP(fopen_seek, tc) 537 { 538 (void)unlink(path); 539 } 540 541 ATF_TC_WITH_CLEANUP(freopen_std); 542 ATF_TC_HEAD(freopen_std, tc) 543 { 544 atf_tc_set_md_var(tc, "descr", "A basic test of freopen(3)"); 545 } 546 547 ATF_TC_BODY(freopen_std, tc) 548 { 549 FILE *std[2] = { stdin, stdout }; 550 char buf[15]; 551 size_t i; 552 FILE *f; 553 554 /* 555 * Associate a standard stream with a custom stream. 556 * Then write to the standard stream and verify that 557 * the result now appears in the custom stream. 558 */ 559 for (i = 0; i < __arraycount(std); i++) { 560 561 (void)memset(buf, 'x', sizeof(buf)); 562 563 f = fopen(path, "w+"); 564 ATF_REQUIRE(f != NULL); 565 566 f = freopen(path, "w+", std[i]); 567 ATF_REQUIRE(f != NULL); 568 569 ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 570 ATF_REQUIRE(fprintf(std[i], "garbage") == 7); 571 ATF_REQUIRE(fclose(f) == 0); 572 573 f = fopen(path, "r"); 574 575 ATF_REQUIRE(f != NULL); 576 ATF_REQUIRE(fread(buf, 1, sizeof(buf), f) == 14); 577 ATF_REQUIRE(strncmp(buf, "garbagegarbage", 14) == 0); 578 579 ATF_REQUIRE(fclose(f) == 0); 580 } 581 582 ATF_REQUIRE(unlink(path) == 0); 583 } 584 585 ATF_TC_CLEANUP(freopen_std, tc) 586 { 587 (void)unlink(path); 588 } 589 590 ATF_TP_ADD_TCS(tp) 591 { 592 593 ATF_TP_ADD_TC(tp, fdopen_close); 594 ATF_TP_ADD_TC(tp, fdopen_err); 595 ATF_TP_ADD_TC(tp, fdopen_seek); 596 ATF_TP_ADD_TC(tp, fopen_append); 597 ATF_TP_ADD_TC(tp, fopen_err); 598 ATF_TP_ADD_TC(tp, fopen_mode); 599 ATF_TP_ADD_TC(tp, fopen_nullptr); 600 ATF_TP_ADD_TC(tp, fopen_nullptr_compat10); 601 ATF_TP_ADD_TC(tp, fopen_perm); 602 ATF_TP_ADD_TC(tp, fopen_regular); 603 ATF_TP_ADD_TC(tp, fopen_symlink); 604 ATF_TP_ADD_TC(tp, fopen_seek); 605 ATF_TP_ADD_TC(tp, freopen_std); 606 607 return atf_no_error(); 608 } 609