1272343Sngie/* $NetBSD: t_fopen.c,v 1.3 2011/09/14 14:34:37 martin Exp $ */ 2272343Sngie 3272343Sngie/*- 4272343Sngie * Copyright (c) 2011 The NetBSD Foundation, Inc. 5272343Sngie * All rights reserved. 6272343Sngie * 7272343Sngie * This code is derived from software contributed to The NetBSD Foundation 8272343Sngie * by Jukka Ruohonen. 9272343Sngie * 10272343Sngie * Redistribution and use in source and binary forms, with or without 11272343Sngie * modification, are permitted provided that the following conditions 12272343Sngie * are met: 13272343Sngie * 1. Redistributions of source code must retain the above copyright 14272343Sngie * notice, this list of conditions and the following disclaimer. 15272343Sngie * 2. Redistributions in binary form must reproduce the above copyright 16272343Sngie * notice, this list of conditions and the following disclaimer in the 17272343Sngie * documentation and/or other materials provided with the distribution. 18272343Sngie * 19272343Sngie * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20272343Sngie * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21272343Sngie * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22272343Sngie * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23272343Sngie * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24272343Sngie * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25272343Sngie * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26272343Sngie * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27272343Sngie * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28272343Sngie * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29272343Sngie * POSSIBILITY OF SUCH DAMAGE. 30272343Sngie */ 31272343Sngie#include <sys/cdefs.h> 32272343Sngie__RCSID("$NetBSD: t_fopen.c,v 1.3 2011/09/14 14:34:37 martin Exp $"); 33272343Sngie 34272343Sngie#include <atf-c.h> 35272343Sngie#include <errno.h> 36272343Sngie#include <fcntl.h> 37272343Sngie#include <limits.h> 38272343Sngie#include <paths.h> 39272343Sngie#include <stdio.h> 40272343Sngie#include <string.h> 41272343Sngie#include <unistd.h> 42272343Sngie 43272343Sngiestatic const char *path = "fopen"; 44272343Sngie 45272343SngieATF_TC_WITH_CLEANUP(fdopen_close); 46272343SngieATF_TC_HEAD(fdopen_close, tc) 47272343Sngie{ 48272343Sngie atf_tc_set_md_var(tc, "descr", "See that descriptors are closed"); 49272343Sngie} 50272343Sngie 51272343SngieATF_TC_BODY(fdopen_close, tc) 52272343Sngie{ 53272343Sngie FILE *f; 54272343Sngie int fd; 55272343Sngie 56272343Sngie /* 57272343Sngie * Check that the file descriptor 58272343Sngie * used to fdopen(3) a stream is 59272343Sngie * closed once the stream is closed. 60272343Sngie */ 61272343Sngie fd = open(path, O_RDWR | O_CREAT); 62272343Sngie 63272343Sngie ATF_REQUIRE(fd >= 0); 64272343Sngie 65272343Sngie f = fdopen(fd, "w+"); 66272343Sngie 67272343Sngie ATF_REQUIRE(f != NULL); 68272343Sngie ATF_REQUIRE(fclose(f) == 0); 69272343Sngie ATF_REQUIRE(close(fd) == -1); 70272343Sngie ATF_REQUIRE(unlink(path) == 0); 71272343Sngie} 72272343Sngie 73272343SngieATF_TC_CLEANUP(fdopen_close, tc) 74272343Sngie{ 75272343Sngie (void)unlink(path); 76272343Sngie} 77272343Sngie 78272343SngieATF_TC_WITH_CLEANUP(fdopen_err); 79272343SngieATF_TC_HEAD(fdopen_err, tc) 80272343Sngie{ 81272343Sngie atf_tc_set_md_var(tc, "descr", "Test errors from fdopen(3)"); 82272343Sngie} 83272343Sngie 84272343SngieATF_TC_BODY(fdopen_err, tc) 85272343Sngie{ 86272343Sngie int fd; 87272343Sngie 88272343Sngie fd = open(path, O_RDONLY | O_CREAT); 89272343Sngie ATF_REQUIRE(fd >= 0); 90272343Sngie 91272343Sngie errno = 0; 92272343Sngie ATF_REQUIRE_ERRNO(EINVAL, fdopen(fd, "w") == NULL); 93272343Sngie 94272343Sngie errno = 0; 95272343Sngie ATF_REQUIRE_ERRNO(EINVAL, fdopen(fd, "a") == NULL); 96272343Sngie 97272343Sngie ATF_REQUIRE(close(fd) == 0); 98272343Sngie 99272343Sngie errno = 0; 100272343Sngie ATF_REQUIRE_ERRNO(EBADF, fdopen(fd, "r") == NULL); 101272343Sngie 102272343Sngie errno = 0; 103272343Sngie ATF_REQUIRE_ERRNO(EBADF, fdopen(-1, "w+") == NULL); 104272343Sngie 105272343Sngie (void)unlink(path); 106272343Sngie} 107272343Sngie 108272343SngieATF_TC_CLEANUP(fdopen_err, tc) 109272343Sngie{ 110272343Sngie (void)unlink(path); 111272343Sngie} 112272343Sngie 113272343SngieATF_TC_WITH_CLEANUP(fdopen_seek); 114272343SngieATF_TC_HEAD(fdopen_seek, tc) 115272343Sngie{ 116272343Sngie atf_tc_set_md_var(tc, "descr", "Test stream position with fdopen(3)"); 117272343Sngie} 118272343Sngie 119272343SngieATF_TC_BODY(fdopen_seek, tc) 120272343Sngie{ 121272343Sngie FILE *f; 122272343Sngie int fd; 123272343Sngie 124272343Sngie /* 125272343Sngie * Verify that the file position associated 126272343Sngie * with the stream corresponds with the offset 127272343Sngie * set earlier for the file descriptor. 128272343Sngie */ 129272343Sngie fd = open(path, O_RDWR | O_CREAT); 130272343Sngie 131272343Sngie ATF_REQUIRE(fd >= 0); 132272343Sngie ATF_REQUIRE(write(fd, "garbage", 7) == 7); 133272343Sngie ATF_REQUIRE(lseek(fd, 3, SEEK_SET) == 3); 134272343Sngie 135272343Sngie f = fdopen(fd, "r+"); 136272343Sngie 137272343Sngie ATF_REQUIRE(f != NULL); 138272343Sngie ATF_REQUIRE(ftell(f) == 3); 139272343Sngie ATF_REQUIRE(fclose(f) == 0); 140272343Sngie ATF_REQUIRE(unlink(path) == 0); 141272343Sngie} 142272343Sngie 143272343SngieATF_TC_CLEANUP(fdopen_seek, tc) 144272343Sngie{ 145272343Sngie (void)unlink(path); 146272343Sngie} 147272343Sngie 148272343SngieATF_TC_WITH_CLEANUP(fopen_err); 149272343SngieATF_TC_HEAD(fopen_err, tc) 150272343Sngie{ 151272343Sngie atf_tc_set_md_var(tc, "descr", "Test errors from fopen(3)"); 152272343Sngie} 153272343Sngie 154272343SngieATF_TC_BODY(fopen_err, tc) 155272343Sngie{ 156272343Sngie static const char *mode[] = { 157272343Sngie "x", "xr", "xr", "+r+", "R", "W+", " aXX", "Xr", " r+", "" }; 158272343Sngie 159272343Sngie char buf[PATH_MAX + 1]; 160272343Sngie size_t i; 161272343Sngie FILE *f; 162272343Sngie 163272343Sngie f = fopen(path, "w+"); 164272343Sngie 165272343Sngie ATF_REQUIRE(f != NULL); 166272343Sngie ATF_REQUIRE(fclose(f) == 0); 167272343Sngie 168272343Sngie /* 169272343Sngie * Note that also "invalid" characters 170272343Sngie * may follow the mode-string whenever 171272343Sngie * the first character is valid. 172272343Sngie */ 173272343Sngie for (i = 0; i < __arraycount(mode); i++) { 174272343Sngie 175272343Sngie errno = 0; 176272343Sngie f = fopen(path, mode[i]); 177272343Sngie 178272343Sngie if (f == NULL && errno == EINVAL) 179272343Sngie continue; 180272343Sngie 181272343Sngie if (f != NULL) 182272343Sngie (void)fclose(f); 183272343Sngie 184272343Sngie atf_tc_fail_nonfatal("opened file as '%s'", mode[i]); 185272343Sngie } 186272343Sngie 187272343Sngie (void)unlink(path); 188272343Sngie (void)memset(buf, 'x', sizeof(buf)); 189272343Sngie 190272343Sngie errno = 0; 191272343Sngie ATF_REQUIRE_ERRNO(EISDIR, fopen("/usr/bin", "w") == NULL); 192272343Sngie 193272343Sngie errno = 0; 194272343Sngie ATF_REQUIRE_ERRNO(ENOENT, fopen("/a/b/c/d/e/f", "r") == NULL); 195272343Sngie 196272343Sngie errno = 0; 197272343Sngie ATF_REQUIRE_ERRNO(ENAMETOOLONG, fopen(buf, "r+") == NULL); 198272343Sngie} 199272343Sngie 200272343SngieATF_TC_CLEANUP(fopen_err, tc) 201272343Sngie{ 202272343Sngie (void)unlink(path); 203272343Sngie} 204272343Sngie 205272343SngieATF_TC_WITH_CLEANUP(fopen_append); 206272343SngieATF_TC_HEAD(fopen_append, tc) 207272343Sngie{ 208272343Sngie atf_tc_set_md_var(tc, "descr", "Test that append-mode works"); 209272343Sngie} 210272343Sngie 211272343SngieATF_TC_BODY(fopen_append, tc) 212272343Sngie{ 213272343Sngie char buf[15]; 214272343Sngie FILE *f; 215272343Sngie 216272343Sngie (void)memset(buf, 'x', sizeof(buf)); 217272343Sngie 218272343Sngie f = fopen(path, "w+"); 219272343Sngie 220272343Sngie ATF_REQUIRE(f != NULL); 221272343Sngie ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 222272343Sngie ATF_REQUIRE(fclose(f) == 0); 223272343Sngie 224272343Sngie f = fopen(path, "a"); 225272343Sngie 226272343Sngie ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 227272343Sngie ATF_REQUIRE(fclose(f) == 0); 228272343Sngie 229272343Sngie f = fopen(path, "r"); 230272343Sngie 231272343Sngie ATF_REQUIRE(fread(buf, 1, sizeof(buf), f) == 14); 232272343Sngie ATF_REQUIRE(strncmp(buf, "garbagegarbage", 14) == 0); 233272343Sngie 234272343Sngie ATF_REQUIRE(fclose(f) == 0); 235272343Sngie ATF_REQUIRE(unlink(path) == 0); 236272343Sngie} 237272343Sngie 238272343SngieATF_TC_CLEANUP(fopen_append, tc) 239272343Sngie{ 240272343Sngie (void)unlink(path); 241272343Sngie} 242272343Sngie 243272343SngieATF_TC_WITH_CLEANUP(fopen_mode); 244272343SngieATF_TC_HEAD(fopen_mode, tc) 245272343Sngie{ 246272343Sngie atf_tc_set_md_var(tc, "descr", "Test fopen(3) modes"); 247272343Sngie} 248272343Sngie 249272343SngieATF_TC_BODY(fopen_mode, tc) 250272343Sngie{ 251272343Sngie size_t i; 252272343Sngie FILE *f; 253272343Sngie 254272343Sngie static const char *mode[] = { 255272343Sngie "r", "r+", "w", "w+", "a", "a+", 256272343Sngie "rb", "r+b", "wb", "w+b", "ab", "a+b" 257272343Sngie "re", "r+e", "we", "w+e", "ae", "a+e" 258272343Sngie "rf", "r+f", "wf", "w+f", "af", "a+f" 259272343Sngie }; 260272343Sngie 261272343Sngie f = fopen(path, "w+"); 262272343Sngie 263272343Sngie ATF_REQUIRE(f != NULL); 264272343Sngie ATF_REQUIRE(fclose(f) == 0); 265272343Sngie 266272343Sngie /* 267272343Sngie * Verify that various modes work. 268272343Sngie */ 269272343Sngie for (i = 0; i < __arraycount(mode); i++) { 270272343Sngie 271272343Sngie f = fopen(path, mode[i]); 272272343Sngie 273272343Sngie if (f != NULL) { 274272343Sngie ATF_REQUIRE(fclose(f) == 0); 275272343Sngie continue; 276272343Sngie } 277272343Sngie 278272343Sngie atf_tc_fail_nonfatal("failed to open file as %s", mode[i]); 279272343Sngie } 280272343Sngie 281272343Sngie (void)unlink(path); 282272343Sngie} 283272343Sngie 284272343SngieATF_TC_CLEANUP(fopen_mode, tc) 285272343Sngie{ 286272343Sngie (void)unlink(path); 287272343Sngie} 288272343Sngie 289272343SngieATF_TC(fopen_perm); 290272343SngieATF_TC_HEAD(fopen_perm, tc) 291272343Sngie{ 292272343Sngie atf_tc_set_md_var(tc, "descr", "Test permissions with fopen(3)"); 293272343Sngie atf_tc_set_md_var(tc, "require.user", "unprivileged"); 294272343Sngie} 295272343Sngie 296272343SngieATF_TC_BODY(fopen_perm, tc) 297272343Sngie{ 298272343Sngie 299272343Sngie errno = 0; 300272343Sngie ATF_REQUIRE_ERRNO(EACCES, fopen("/bin/ls", "a+") == NULL); 301272343Sngie 302272343Sngie errno = 0; 303272343Sngie ATF_REQUIRE_ERRNO(EACCES, fopen("/bin/ls", "w+") == NULL); 304272343Sngie} 305272343Sngie 306272343SngieATF_TC(fopen_regular); 307272343SngieATF_TC_HEAD(fopen_regular, tc) 308272343Sngie{ 309272343Sngie atf_tc_set_md_var(tc, "descr", "Test fopen(3) with 'f' mode"); 310272343Sngie} 311272343Sngie 312272343SngieATF_TC_BODY(fopen_regular, tc) 313272343Sngie{ 314272343Sngie static const char *mode[] = { "rf", "r+f", "wf", "w+f", "af", "a+f" }; 315272343Sngie static const char *devs[] = { _PATH_DEVNULL }; 316272343Sngie 317272343Sngie size_t i, j; 318272343Sngie FILE *f; 319272343Sngie 320272343Sngie for (i = 0; i < __arraycount(devs); i++) { 321272343Sngie 322272343Sngie for (j = 0; j < __arraycount(mode); j++) { 323272343Sngie 324272343Sngie errno = 0; 325272343Sngie f = fopen(devs[i], mode[j]); 326272343Sngie 327272343Sngie if (f == NULL && errno == EFTYPE) 328272343Sngie continue; 329272343Sngie 330272343Sngie if (f != NULL) 331272343Sngie (void)fclose(f); 332272343Sngie 333272343Sngie atf_tc_fail_nonfatal("opened %s as %s", 334272343Sngie devs[i], mode[j]); 335272343Sngie } 336272343Sngie } 337272343Sngie} 338272343Sngie 339272343SngieATF_TC_WITH_CLEANUP(fopen_seek); 340272343SngieATF_TC_HEAD(fopen_seek, tc) 341272343Sngie{ 342272343Sngie atf_tc_set_md_var(tc, "descr", "Test initial stream position"); 343272343Sngie} 344272343Sngie 345272343SngieATF_TC_BODY(fopen_seek, tc) 346272343Sngie{ 347272343Sngie FILE *f; 348272343Sngie 349272343Sngie f = fopen(path, "w+"); 350272343Sngie 351272343Sngie ATF_REQUIRE(f != NULL); 352272343Sngie ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 353272343Sngie ATF_REQUIRE(fclose(f) == 0); 354272343Sngie 355272343Sngie /* 356272343Sngie * The position of the stream should be 357272343Sngie * at the start, except for append-mode. 358272343Sngie */ 359272343Sngie f = fopen(path, "r"); 360272343Sngie 361272343Sngie ATF_REQUIRE(f != NULL); 362272343Sngie ATF_REQUIRE(ftello(f) == 0); 363272343Sngie ATF_REQUIRE(fclose(f) == 0); 364272343Sngie 365272343Sngie f = fopen(path, "a"); 366272343Sngie 367272343Sngie ATF_REQUIRE(f != NULL); 368272343Sngie ATF_REQUIRE(ftello(f) == 7); 369272343Sngie ATF_REQUIRE(fclose(f) == 0); 370272343Sngie ATF_REQUIRE(unlink(path) == 0); 371272343Sngie} 372272343Sngie 373272343SngieATF_TC_CLEANUP(fopen_seek, tc) 374272343Sngie{ 375272343Sngie (void)unlink(path); 376272343Sngie} 377272343Sngie 378272343SngieATF_TC_WITH_CLEANUP(freopen_std); 379272343SngieATF_TC_HEAD(freopen_std, tc) 380272343Sngie{ 381272343Sngie atf_tc_set_md_var(tc, "descr", "A basic test of freopen(3)"); 382272343Sngie} 383272343Sngie 384272343SngieATF_TC_BODY(freopen_std, tc) 385272343Sngie{ 386272343Sngie FILE *std[2] = { stdin, stdout }; 387272343Sngie char buf[15]; 388272343Sngie size_t i; 389272343Sngie FILE *f; 390272343Sngie 391272343Sngie /* 392272343Sngie * Associate a standard stream with a custom stream. 393272343Sngie * Then write to the standard stream and verify that 394272343Sngie * the result now appears in the custom stream. 395272343Sngie */ 396272343Sngie for (i = 0; i < __arraycount(std); i++) { 397272343Sngie 398272343Sngie (void)memset(buf, 'x', sizeof(buf)); 399272343Sngie 400272343Sngie f = fopen(path, "w+"); 401272343Sngie ATF_REQUIRE(f != NULL); 402272343Sngie 403272343Sngie f = freopen(path, "w+", std[i]); 404272343Sngie ATF_REQUIRE(f != NULL); 405272343Sngie 406272343Sngie ATF_REQUIRE(fwrite("garbage", 1, 7, f) == 7); 407272343Sngie ATF_REQUIRE(fprintf(std[i], "garbage") == 7); 408272343Sngie ATF_REQUIRE(fclose(f) == 0); 409272343Sngie 410272343Sngie f = fopen(path, "r"); 411272343Sngie 412272343Sngie ATF_REQUIRE(f != NULL); 413272343Sngie ATF_REQUIRE(fread(buf, 1, sizeof(buf), f) == 14); 414272343Sngie ATF_REQUIRE(strncmp(buf, "garbagegarbage", 14) == 0); 415272343Sngie 416272343Sngie ATF_REQUIRE(fclose(f) == 0); 417272343Sngie } 418272343Sngie 419272343Sngie ATF_REQUIRE(unlink(path) == 0); 420272343Sngie} 421272343Sngie 422272343SngieATF_TC_CLEANUP(freopen_std, tc) 423272343Sngie{ 424272343Sngie (void)unlink(path); 425272343Sngie} 426272343Sngie 427272343SngieATF_TP_ADD_TCS(tp) 428272343Sngie{ 429272343Sngie 430272343Sngie ATF_TP_ADD_TC(tp, fdopen_close); 431272343Sngie ATF_TP_ADD_TC(tp, fdopen_err); 432272343Sngie ATF_TP_ADD_TC(tp, fdopen_seek); 433272343Sngie ATF_TP_ADD_TC(tp, fopen_append); 434272343Sngie ATF_TP_ADD_TC(tp, fopen_err); 435272343Sngie ATF_TP_ADD_TC(tp, fopen_mode); 436272343Sngie ATF_TP_ADD_TC(tp, fopen_perm); 437276478Sngie#ifdef __NetBSD__ 438272343Sngie ATF_TP_ADD_TC(tp, fopen_regular); 439276478Sngie#endif 440272343Sngie ATF_TP_ADD_TC(tp, fopen_seek); 441272343Sngie ATF_TP_ADD_TC(tp, freopen_std); 442272343Sngie 443272343Sngie return atf_no_error(); 444272343Sngie} 445