1 /* $NetBSD: t_fopen.c,v 1.3 2011/09/14 14:34:37 martin 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.3 2011/09/14 14:34:37 martin 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, 0600); 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, 0600); 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, 0600); 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 }; 260 261 f = fopen(path, "w+"); 262 263 ATF_REQUIRE(f != NULL); 264 ATF_REQUIRE(fclose(f) == 0); 265 266 /* 267 * Verify that various modes work. 268 */ 269 for (i = 0; i < __arraycount(mode); i++) { 270 271 f = fopen(path, mode[i]); 272 273 if (f != NULL) { 274 ATF_REQUIRE(fclose(f) == 0); 275 continue; 276 } 277 278 atf_tc_fail_nonfatal("failed to open file as %s", mode[i]); 279 } 280 281 (void)unlink(path); 282 } 283 284 ATF_TC_CLEANUP(fopen_mode, tc) 285 { 286 (void)unlink(path); 287 } 288 289 ATF_TC(fopen_perm); 290 ATF_TC_HEAD(fopen_perm, tc) 291 { 292 atf_tc_set_md_var(tc, "descr", "Test permissions with fopen(3)"); 293 atf_tc_set_md_var(tc, "require.user", "unprivileged"); 294 } 295 296 ATF_TC_BODY(fopen_perm, tc) 297 { 298 299 errno = 0; 300 ATF_REQUIRE_ERRNO(EACCES, fopen("/bin/ls", "a+") == NULL); 301 302 errno = 0; 303 ATF_REQUIRE_ERRNO(EACCES, fopen("/bin/ls", "w+") == NULL); 304 } 305 306 #ifdef __NetBSD__ 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 } 337 } 338 } 339 #endif 340 341 ATF_TC_WITH_CLEANUP(fopen_seek); 342 ATF_TC_HEAD(fopen_seek, tc) 343 { 344 atf_tc_set_md_var(tc, "descr", "Test initial stream position"); 345 } 346 347 ATF_TC_BODY(fopen_seek, tc) 348 { 349 FILE *f; 350 351 f = fopen(path, "w+"); 352 353 ATF_REQUIRE(f != NULL); 354 ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 355 ATF_REQUIRE(fclose(f) == 0); 356 357 /* 358 * The position of the stream should be 359 * at the start, except for append-mode. 360 */ 361 f = fopen(path, "r"); 362 363 ATF_REQUIRE(f != NULL); 364 ATF_REQUIRE(ftello(f) == 0); 365 ATF_REQUIRE(fclose(f) == 0); 366 367 f = fopen(path, "a"); 368 369 ATF_REQUIRE(f != NULL); 370 ATF_REQUIRE(ftello(f) == 7); 371 ATF_REQUIRE(fclose(f) == 0); 372 ATF_REQUIRE(unlink(path) == 0); 373 } 374 375 ATF_TC_CLEANUP(fopen_seek, tc) 376 { 377 (void)unlink(path); 378 } 379 380 ATF_TC_WITH_CLEANUP(freopen_std); 381 ATF_TC_HEAD(freopen_std, tc) 382 { 383 atf_tc_set_md_var(tc, "descr", "A basic test of freopen(3)"); 384 } 385 386 ATF_TC_BODY(freopen_std, tc) 387 { 388 FILE *std[2] = { stdin, stdout }; 389 char buf[15]; 390 size_t i; 391 FILE *f; 392 393 /* 394 * Associate a standard stream with a custom stream. 395 * Then write to the standard stream and verify that 396 * the result now appears in the custom stream. 397 */ 398 for (i = 0; i < __arraycount(std); i++) { 399 400 (void)memset(buf, 'x', sizeof(buf)); 401 402 f = fopen(path, "w+"); 403 ATF_REQUIRE(f != NULL); 404 405 f = freopen(path, "w+", std[i]); 406 ATF_REQUIRE(f != NULL); 407 408 ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 409 ATF_REQUIRE(fprintf(std[i], "garbage") == 7); 410 ATF_REQUIRE(fclose(f) == 0); 411 412 f = fopen(path, "r"); 413 414 ATF_REQUIRE(f != NULL); 415 ATF_REQUIRE(fread(buf, 1, sizeof(buf), f) == 14); 416 ATF_REQUIRE(strncmp(buf, "garbagegarbage", 14) == 0); 417 418 ATF_REQUIRE(fclose(f) == 0); 419 } 420 421 ATF_REQUIRE(unlink(path) == 0); 422 } 423 424 ATF_TC_CLEANUP(freopen_std, tc) 425 { 426 (void)unlink(path); 427 } 428 429 ATF_TP_ADD_TCS(tp) 430 { 431 432 ATF_TP_ADD_TC(tp, fdopen_close); 433 ATF_TP_ADD_TC(tp, fdopen_err); 434 ATF_TP_ADD_TC(tp, fdopen_seek); 435 ATF_TP_ADD_TC(tp, fopen_append); 436 ATF_TP_ADD_TC(tp, fopen_err); 437 ATF_TP_ADD_TC(tp, fopen_mode); 438 ATF_TP_ADD_TC(tp, fopen_perm); 439 #ifdef __NetBSD__ 440 ATF_TP_ADD_TC(tp, fopen_regular); 441 #endif 442 ATF_TP_ADD_TC(tp, fopen_seek); 443 ATF_TP_ADD_TC(tp, freopen_std); 444 445 return atf_no_error(); 446 } 447