1 /* $NetBSD: t_fopen.c,v 1.5 2017/11/06 23:06:55 kre 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.5 2017/11/06 23:06:55 kre Exp $"); 33 34 #include <atf-c.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <limits.h> 38 #include <paths.h> 39 #include <stdio.h> 40 #include <string.h> 41 #include <unistd.h> 42 43 static const char *path = "fopen"; 44 45 ATF_TC_WITH_CLEANUP(fdopen_close); 46 ATF_TC_HEAD(fdopen_close, tc) 47 { 48 atf_tc_set_md_var(tc, "descr", "See that descriptors are closed"); 49 } 50 51 ATF_TC_BODY(fdopen_close, tc) 52 { 53 FILE *f; 54 int fd; 55 56 /* 57 * Check that the file descriptor 58 * used to fdopen(3) a stream is 59 * closed once the stream is closed. 60 */ 61 fd = open(path, O_RDWR | O_CREAT); 62 63 ATF_REQUIRE(fd >= 0); 64 65 f = fdopen(fd, "w+"); 66 67 ATF_REQUIRE(f != NULL); 68 ATF_REQUIRE(fclose(f) == 0); 69 ATF_REQUIRE(close(fd) == -1); 70 ATF_REQUIRE(unlink(path) == 0); 71 } 72 73 ATF_TC_CLEANUP(fdopen_close, tc) 74 { 75 (void)unlink(path); 76 } 77 78 ATF_TC_WITH_CLEANUP(fdopen_err); 79 ATF_TC_HEAD(fdopen_err, tc) 80 { 81 atf_tc_set_md_var(tc, "descr", "Test errors from fdopen(3)"); 82 } 83 84 ATF_TC_BODY(fdopen_err, tc) 85 { 86 int fd; 87 88 fd = open(path, O_RDONLY | O_CREAT); 89 ATF_REQUIRE(fd >= 0); 90 91 errno = 0; 92 ATF_REQUIRE_ERRNO(EINVAL, fdopen(fd, "w") == NULL); 93 94 errno = 0; 95 ATF_REQUIRE_ERRNO(EINVAL, fdopen(fd, "a") == NULL); 96 97 ATF_REQUIRE(close(fd) == 0); 98 99 errno = 0; 100 ATF_REQUIRE_ERRNO(EBADF, fdopen(fd, "r") == NULL); 101 102 errno = 0; 103 ATF_REQUIRE_ERRNO(EBADF, fdopen(-1, "w+") == NULL); 104 105 (void)unlink(path); 106 } 107 108 ATF_TC_CLEANUP(fdopen_err, tc) 109 { 110 (void)unlink(path); 111 } 112 113 ATF_TC_WITH_CLEANUP(fdopen_seek); 114 ATF_TC_HEAD(fdopen_seek, tc) 115 { 116 atf_tc_set_md_var(tc, "descr", "Test stream position with fdopen(3)"); 117 } 118 119 ATF_TC_BODY(fdopen_seek, tc) 120 { 121 FILE *f; 122 int fd; 123 124 /* 125 * Verify that the file position associated 126 * with the stream corresponds with the offset 127 * set earlier for the file descriptor. 128 */ 129 fd = open(path, O_RDWR | O_CREAT); 130 131 ATF_REQUIRE(fd >= 0); 132 ATF_REQUIRE(write(fd, "garbage", 7) == 7); 133 ATF_REQUIRE(lseek(fd, 3, SEEK_SET) == 3); 134 135 f = fdopen(fd, "r+"); 136 137 ATF_REQUIRE(f != NULL); 138 ATF_REQUIRE(ftell(f) == 3); 139 ATF_REQUIRE(fclose(f) == 0); 140 ATF_REQUIRE(unlink(path) == 0); 141 } 142 143 ATF_TC_CLEANUP(fdopen_seek, tc) 144 { 145 (void)unlink(path); 146 } 147 148 ATF_TC_WITH_CLEANUP(fopen_err); 149 ATF_TC_HEAD(fopen_err, tc) 150 { 151 atf_tc_set_md_var(tc, "descr", "Test errors from fopen(3)"); 152 } 153 154 ATF_TC_BODY(fopen_err, tc) 155 { 156 static const char *mode[] = { 157 "x", "xr", "xr", "+r+", "R", "W+", " aXX", "Xr", " r+", "" }; 158 159 char buf[PATH_MAX + 1]; 160 size_t i; 161 FILE *f; 162 163 f = fopen(path, "w+"); 164 165 ATF_REQUIRE(f != NULL); 166 ATF_REQUIRE(fclose(f) == 0); 167 168 /* 169 * Note that also "invalid" characters 170 * may follow the mode-string whenever 171 * the first character is valid. 172 */ 173 for (i = 0; i < __arraycount(mode); i++) { 174 175 errno = 0; 176 f = fopen(path, mode[i]); 177 178 if (f == NULL && errno == EINVAL) 179 continue; 180 181 if (f != NULL) 182 (void)fclose(f); 183 184 atf_tc_fail_nonfatal("opened file as '%s'", mode[i]); 185 } 186 187 (void)unlink(path); 188 (void)memset(buf, 'x', sizeof(buf)); 189 190 errno = 0; 191 ATF_REQUIRE_ERRNO(EISDIR, fopen("/usr/bin", "w") == NULL); 192 193 errno = 0; 194 ATF_REQUIRE_ERRNO(ENOENT, fopen("/a/b/c/d/e/f", "r") == NULL); 195 196 errno = 0; 197 ATF_REQUIRE_ERRNO(ENAMETOOLONG, fopen(buf, "r+") == NULL); 198 } 199 200 ATF_TC_CLEANUP(fopen_err, tc) 201 { 202 (void)unlink(path); 203 } 204 205 ATF_TC_WITH_CLEANUP(fopen_append); 206 ATF_TC_HEAD(fopen_append, tc) 207 { 208 atf_tc_set_md_var(tc, "descr", "Test that append-mode works"); 209 } 210 211 ATF_TC_BODY(fopen_append, tc) 212 { 213 char buf[15]; 214 FILE *f; 215 216 (void)memset(buf, 'x', sizeof(buf)); 217 218 f = fopen(path, "w+"); 219 220 ATF_REQUIRE(f != NULL); 221 ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 222 ATF_REQUIRE(fclose(f) == 0); 223 224 f = fopen(path, "a"); 225 226 ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 227 ATF_REQUIRE(fclose(f) == 0); 228 229 f = fopen(path, "r"); 230 231 ATF_REQUIRE(fread(buf, 1, sizeof(buf), f) == 14); 232 ATF_REQUIRE(strncmp(buf, "garbagegarbage", 14) == 0); 233 234 ATF_REQUIRE(fclose(f) == 0); 235 ATF_REQUIRE(unlink(path) == 0); 236 } 237 238 ATF_TC_CLEANUP(fopen_append, tc) 239 { 240 (void)unlink(path); 241 } 242 243 ATF_TC_WITH_CLEANUP(fopen_mode); 244 ATF_TC_HEAD(fopen_mode, tc) 245 { 246 atf_tc_set_md_var(tc, "descr", "Test fopen(3) modes"); 247 } 248 249 ATF_TC_BODY(fopen_mode, tc) 250 { 251 size_t i; 252 FILE *f; 253 254 static const char *mode[] = { 255 "r", "r+", "w", "w+", "a", "a+", 256 "rb", "r+b", "wb", "w+b", "ab", "a+b", 257 "re", "r+e", "we", "w+e", "ae", "a+e", 258 "rf", "r+f", "wf", "w+f", "af", "a+f", 259 "rl", "r+l", "wl", "w+l", "al", "a+l" 260 }; 261 262 f = fopen(path, "w+"); 263 264 ATF_REQUIRE(f != NULL); 265 ATF_REQUIRE(fclose(f) == 0); 266 267 /* 268 * Verify that various modes work. 269 */ 270 for (i = 0; i < __arraycount(mode); i++) { 271 272 f = fopen(path, mode[i]); 273 274 if (f != NULL) { 275 ATF_REQUIRE(fclose(f) == 0); 276 continue; 277 } 278 279 atf_tc_fail_nonfatal("failed to open file as %s", mode[i]); 280 } 281 282 (void)unlink(path); 283 } 284 285 ATF_TC_CLEANUP(fopen_mode, tc) 286 { 287 (void)unlink(path); 288 } 289 290 ATF_TC(fopen_perm); 291 ATF_TC_HEAD(fopen_perm, tc) 292 { 293 atf_tc_set_md_var(tc, "descr", "Test permissions with fopen(3)"); 294 atf_tc_set_md_var(tc, "require.user", "unprivileged"); 295 } 296 297 ATF_TC_BODY(fopen_perm, tc) 298 { 299 300 errno = 0; 301 ATF_REQUIRE_ERRNO(EACCES, fopen("/bin/ls", "a+") == NULL); 302 303 errno = 0; 304 ATF_REQUIRE_ERRNO(EACCES, fopen("/bin/ls", "w+") == NULL); 305 } 306 307 ATF_TC(fopen_regular); 308 ATF_TC_HEAD(fopen_regular, tc) 309 { 310 atf_tc_set_md_var(tc, "descr", "Test fopen(3) with 'f' mode"); 311 } 312 313 ATF_TC_BODY(fopen_regular, tc) 314 { 315 static const char *mode[] = { "rf", "r+f", "wf", "w+f", "af", "a+f" }; 316 static const char *devs[] = { _PATH_DEVNULL }; 317 318 size_t i, j; 319 FILE *f; 320 321 for (i = 0; i < __arraycount(devs); i++) { 322 323 for (j = 0; j < __arraycount(mode); j++) { 324 325 errno = 0; 326 f = fopen(devs[i], mode[j]); 327 328 if (f == NULL && errno == EFTYPE) 329 continue; 330 331 if (f != NULL) { 332 (void)fclose(f); 333 334 atf_tc_fail_nonfatal("opened %s as %s", 335 devs[i], mode[j]); 336 } else { 337 atf_tc_fail_nonfatal( 338 "err %d (%s) from open of %s as %s", errno, 339 strerror(errno), devs[i], mode[j]); 340 } 341 } 342 } 343 } 344 345 static char linkpath[] = "symlink"; 346 347 ATF_TC_WITH_CLEANUP(fopen_symlink); 348 ATF_TC_HEAD(fopen_symlink, tc) 349 { 350 atf_tc_set_md_var(tc, "descr", "Test fopen(3) with 'l' mode"); 351 } 352 353 ATF_TC_BODY(fopen_symlink, tc) 354 { 355 static const char *mode[] = { "rl", "r+l", "wl", "w+l", "al", "a+l" }; 356 size_t j; 357 FILE *f; 358 359 ATF_CHECK(symlink("/dev/null", linkpath) != -1); 360 361 for (j = 0; j < __arraycount(mode); j++) { 362 363 errno = 0; 364 f = fopen(linkpath, mode[j]); 365 366 if (f == NULL && errno == EFTYPE) 367 continue; 368 369 if (f != NULL) { 370 (void)fclose(f); 371 372 atf_tc_fail_nonfatal("opened %s as %s", linkpath, 373 mode[j]); 374 } else { 375 atf_tc_fail_nonfatal( 376 "err %d (%s) from open of %s as %s", errno, 377 strerror(errno), linkpath, mode[j]); 378 } 379 } 380 ATF_REQUIRE(unlink(linkpath) == 0); 381 } 382 383 ATF_TC_CLEANUP(fopen_symlink, tc) 384 { 385 (void)unlink(linkpath); 386 } 387 388 ATF_TC_WITH_CLEANUP(fopen_seek); 389 ATF_TC_HEAD(fopen_seek, tc) 390 { 391 atf_tc_set_md_var(tc, "descr", "Test initial stream position"); 392 } 393 394 ATF_TC_BODY(fopen_seek, tc) 395 { 396 FILE *f; 397 398 f = fopen(path, "w+"); 399 400 ATF_REQUIRE(f != NULL); 401 ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 402 ATF_REQUIRE(fclose(f) == 0); 403 404 /* 405 * The position of the stream should be 406 * at the start, except for append-mode. 407 */ 408 f = fopen(path, "r"); 409 410 ATF_REQUIRE(f != NULL); 411 ATF_REQUIRE(ftello(f) == 0); 412 ATF_REQUIRE(fclose(f) == 0); 413 414 f = fopen(path, "a"); 415 416 ATF_REQUIRE(f != NULL); 417 ATF_REQUIRE(ftello(f) == 7); 418 ATF_REQUIRE(fclose(f) == 0); 419 ATF_REQUIRE(unlink(path) == 0); 420 } 421 422 ATF_TC_CLEANUP(fopen_seek, tc) 423 { 424 (void)unlink(path); 425 } 426 427 ATF_TC_WITH_CLEANUP(freopen_std); 428 ATF_TC_HEAD(freopen_std, tc) 429 { 430 atf_tc_set_md_var(tc, "descr", "A basic test of freopen(3)"); 431 } 432 433 ATF_TC_BODY(freopen_std, tc) 434 { 435 FILE *std[2] = { stdin, stdout }; 436 char buf[15]; 437 size_t i; 438 FILE *f; 439 440 /* 441 * Associate a standard stream with a custom stream. 442 * Then write to the standard stream and verify that 443 * the result now appears in the custom stream. 444 */ 445 for (i = 0; i < __arraycount(std); i++) { 446 447 (void)memset(buf, 'x', sizeof(buf)); 448 449 f = fopen(path, "w+"); 450 ATF_REQUIRE(f != NULL); 451 452 f = freopen(path, "w+", std[i]); 453 ATF_REQUIRE(f != NULL); 454 455 ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 456 ATF_REQUIRE(fprintf(std[i], "garbage") == 7); 457 ATF_REQUIRE(fclose(f) == 0); 458 459 f = fopen(path, "r"); 460 461 ATF_REQUIRE(f != NULL); 462 ATF_REQUIRE(fread(buf, 1, sizeof(buf), f) == 14); 463 ATF_REQUIRE(strncmp(buf, "garbagegarbage", 14) == 0); 464 465 ATF_REQUIRE(fclose(f) == 0); 466 } 467 468 ATF_REQUIRE(unlink(path) == 0); 469 } 470 471 ATF_TC_CLEANUP(freopen_std, tc) 472 { 473 (void)unlink(path); 474 } 475 476 ATF_TP_ADD_TCS(tp) 477 { 478 479 ATF_TP_ADD_TC(tp, fdopen_close); 480 ATF_TP_ADD_TC(tp, fdopen_err); 481 ATF_TP_ADD_TC(tp, fdopen_seek); 482 ATF_TP_ADD_TC(tp, fopen_append); 483 ATF_TP_ADD_TC(tp, fopen_err); 484 ATF_TP_ADD_TC(tp, fopen_mode); 485 ATF_TP_ADD_TC(tp, fopen_perm); 486 ATF_TP_ADD_TC(tp, fopen_regular); 487 ATF_TP_ADD_TC(tp, fopen_symlink); 488 ATF_TP_ADD_TC(tp, fopen_seek); 489 ATF_TP_ADD_TC(tp, freopen_std); 490 491 return atf_no_error(); 492 } 493