t_fopen.c revision 272343
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 43static const char *path = "fopen"; 44 45ATF_TC_WITH_CLEANUP(fdopen_close); 46ATF_TC_HEAD(fdopen_close, tc) 47{ 48 atf_tc_set_md_var(tc, "descr", "See that descriptors are closed"); 49} 50 51ATF_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 73ATF_TC_CLEANUP(fdopen_close, tc) 74{ 75 (void)unlink(path); 76} 77 78ATF_TC_WITH_CLEANUP(fdopen_err); 79ATF_TC_HEAD(fdopen_err, tc) 80{ 81 atf_tc_set_md_var(tc, "descr", "Test errors from fdopen(3)"); 82} 83 84ATF_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 108ATF_TC_CLEANUP(fdopen_err, tc) 109{ 110 (void)unlink(path); 111} 112 113ATF_TC_WITH_CLEANUP(fdopen_seek); 114ATF_TC_HEAD(fdopen_seek, tc) 115{ 116 atf_tc_set_md_var(tc, "descr", "Test stream position with fdopen(3)"); 117} 118 119ATF_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 143ATF_TC_CLEANUP(fdopen_seek, tc) 144{ 145 (void)unlink(path); 146} 147 148ATF_TC_WITH_CLEANUP(fopen_err); 149ATF_TC_HEAD(fopen_err, tc) 150{ 151 atf_tc_set_md_var(tc, "descr", "Test errors from fopen(3)"); 152} 153 154ATF_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 200ATF_TC_CLEANUP(fopen_err, tc) 201{ 202 (void)unlink(path); 203} 204 205ATF_TC_WITH_CLEANUP(fopen_append); 206ATF_TC_HEAD(fopen_append, tc) 207{ 208 atf_tc_set_md_var(tc, "descr", "Test that append-mode works"); 209} 210 211ATF_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 238ATF_TC_CLEANUP(fopen_append, tc) 239{ 240 (void)unlink(path); 241} 242 243ATF_TC_WITH_CLEANUP(fopen_mode); 244ATF_TC_HEAD(fopen_mode, tc) 245{ 246 atf_tc_set_md_var(tc, "descr", "Test fopen(3) modes"); 247} 248 249ATF_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 284ATF_TC_CLEANUP(fopen_mode, tc) 285{ 286 (void)unlink(path); 287} 288 289ATF_TC(fopen_perm); 290ATF_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 296ATF_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 306ATF_TC(fopen_regular); 307ATF_TC_HEAD(fopen_regular, tc) 308{ 309 atf_tc_set_md_var(tc, "descr", "Test fopen(3) with 'f' mode"); 310} 311 312ATF_TC_BODY(fopen_regular, tc) 313{ 314 static const char *mode[] = { "rf", "r+f", "wf", "w+f", "af", "a+f" }; 315 static const char *devs[] = { _PATH_DEVNULL }; 316 317 size_t i, j; 318 FILE *f; 319 320 for (i = 0; i < __arraycount(devs); i++) { 321 322 for (j = 0; j < __arraycount(mode); j++) { 323 324 errno = 0; 325 f = fopen(devs[i], mode[j]); 326 327 if (f == NULL && errno == EFTYPE) 328 continue; 329 330 if (f != NULL) 331 (void)fclose(f); 332 333 atf_tc_fail_nonfatal("opened %s as %s", 334 devs[i], mode[j]); 335 } 336 } 337} 338 339ATF_TC_WITH_CLEANUP(fopen_seek); 340ATF_TC_HEAD(fopen_seek, tc) 341{ 342 atf_tc_set_md_var(tc, "descr", "Test initial stream position"); 343} 344 345ATF_TC_BODY(fopen_seek, tc) 346{ 347 FILE *f; 348 349 f = fopen(path, "w+"); 350 351 ATF_REQUIRE(f != NULL); 352 ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 353 ATF_REQUIRE(fclose(f) == 0); 354 355 /* 356 * The position of the stream should be 357 * at the start, except for append-mode. 358 */ 359 f = fopen(path, "r"); 360 361 ATF_REQUIRE(f != NULL); 362 ATF_REQUIRE(ftello(f) == 0); 363 ATF_REQUIRE(fclose(f) == 0); 364 365 f = fopen(path, "a"); 366 367 ATF_REQUIRE(f != NULL); 368 ATF_REQUIRE(ftello(f) == 7); 369 ATF_REQUIRE(fclose(f) == 0); 370 ATF_REQUIRE(unlink(path) == 0); 371} 372 373ATF_TC_CLEANUP(fopen_seek, tc) 374{ 375 (void)unlink(path); 376} 377 378ATF_TC_WITH_CLEANUP(freopen_std); 379ATF_TC_HEAD(freopen_std, tc) 380{ 381 atf_tc_set_md_var(tc, "descr", "A basic test of freopen(3)"); 382} 383 384ATF_TC_BODY(freopen_std, tc) 385{ 386 FILE *std[2] = { stdin, stdout }; 387 char buf[15]; 388 size_t i; 389 FILE *f; 390 391 /* 392 * Associate a standard stream with a custom stream. 393 * Then write to the standard stream and verify that 394 * the result now appears in the custom stream. 395 */ 396 for (i = 0; i < __arraycount(std); i++) { 397 398 (void)memset(buf, 'x', sizeof(buf)); 399 400 f = fopen(path, "w+"); 401 ATF_REQUIRE(f != NULL); 402 403 f = freopen(path, "w+", std[i]); 404 ATF_REQUIRE(f != NULL); 405 406 ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 407 ATF_REQUIRE(fprintf(std[i], "garbage") == 7); 408 ATF_REQUIRE(fclose(f) == 0); 409 410 f = fopen(path, "r"); 411 412 ATF_REQUIRE(f != NULL); 413 ATF_REQUIRE(fread(buf, 1, sizeof(buf), f) == 14); 414 ATF_REQUIRE(strncmp(buf, "garbagegarbage", 14) == 0); 415 416 ATF_REQUIRE(fclose(f) == 0); 417 } 418 419 ATF_REQUIRE(unlink(path) == 0); 420} 421 422ATF_TC_CLEANUP(freopen_std, tc) 423{ 424 (void)unlink(path); 425} 426 427ATF_TP_ADD_TCS(tp) 428{ 429 430 ATF_TP_ADD_TC(tp, fdopen_close); 431 ATF_TP_ADD_TC(tp, fdopen_err); 432 ATF_TP_ADD_TC(tp, fdopen_seek); 433 ATF_TP_ADD_TC(tp, fopen_append); 434 ATF_TP_ADD_TC(tp, fopen_err); 435 ATF_TP_ADD_TC(tp, fopen_mode); 436 ATF_TP_ADD_TC(tp, fopen_perm); 437 ATF_TP_ADD_TC(tp, fopen_regular); 438 ATF_TP_ADD_TC(tp, fopen_seek); 439 ATF_TP_ADD_TC(tp, freopen_std); 440 441 return atf_no_error(); 442} 443