1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2020 Robert Mustacchi
14  */
15 
16 /*
17  * Tests to verify fileno(3C) behavior. This test explicitly leaks fds and FILE
18  * structures to make it easier to verify the subsequent fd behavior works and
19  * is apparent through the FILE *.
20  */
21 
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <err.h>
25 #include <stdlib.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <wchar.h>
30 
31 #define	FNO_DUPFD	150
32 
33 static uint_t fno_nfails;
34 static uint_t fno_ntests;
35 static int fno_nextfd;
36 
37 const char *
_umem_debug_init(void)38 _umem_debug_init(void)
39 {
40 	return ("default,verbose");
41 }
42 
43 const char *
_umem_logging_init(void)44 _umem_logging_init(void)
45 {
46 	return ("fail,contents");
47 }
48 
49 
50 static void
check_file(FILE * fp,int fd,const char * msg)51 check_file(FILE *fp, int fd, const char *msg)
52 {
53 	int act = fileno(fp);
54 	if (act != fd) {
55 		(void) printf("TEST FAILED: %s: expected fd %d, found %d\n",
56 		    msg, fd, act);
57 		fno_nfails++;
58 	} else {
59 		(void) printf("TEST PASSED: %s\n", msg);
60 	}
61 	fno_ntests++;
62 }
63 
64 static void
check_open_n(int n)65 check_open_n(int n)
66 {
67 	int fdbase;
68 	uint_t i;
69 
70 	for (i = 0, fdbase = fno_nextfd; i < n; i++, fdbase++) {
71 		FILE *f = fopen("/dev/null", "w+");
72 		if (f == NULL) {
73 			err(EXIT_FAILURE, "failed to open /dev/null");
74 		}
75 		check_file(f, fdbase, "Consecutive FDs");
76 	}
77 }
78 
79 static void
check_memstream(void)80 check_memstream(void)
81 {
82 	FILE *fmem, *omem, *wmem;
83 	char *buf;
84 	wchar_t *wbuf;
85 	size_t size;
86 
87 	fmem = fmemopen(NULL, 10, "w+");
88 	if (fmem == NULL) {
89 		err(EXIT_FAILURE, "failed to fmemopen()");
90 	}
91 
92 	omem = open_memstream(&buf, &size);
93 	if (omem == NULL) {
94 		err(EXIT_FAILURE, "failed to open_memstream()");
95 	}
96 
97 	wmem = open_wmemstream(&wbuf, &size);
98 	if (wmem == NULL) {
99 		err(EXIT_FAILURE, "failed to open_wmemstream()");
100 	}
101 
102 	check_file(fmem, -1, "basic fmemopen()");
103 	check_file(omem, -1, "basic open_memstream()");
104 	check_file(wmem, -1, "basic open_wmemstream()");
105 }
106 
107 static void
check_fdopen(void)108 check_fdopen(void)
109 {
110 	int fd, dupfd;
111 	FILE *f;
112 
113 	fd = open("/dev/null", O_RDWR);
114 	if (fd < 0) {
115 		err(EXIT_FAILURE, "failed to open /dev/null");
116 	}
117 	fno_nextfd = fd + 1;
118 
119 	f = fdopen(fd, "r+");
120 	if (f == NULL) {
121 		err(EXIT_FAILURE, "failed to fdopen /dev/null");
122 	}
123 	check_file(f, fd, "fdopen");
124 
125 	if ((dupfd = dup2(fd, FNO_DUPFD)) != FNO_DUPFD) {
126 		err(EXIT_FAILURE, "failed to dup2 /dev/null");
127 	}
128 	f = fdopen(dupfd, "r+");
129 	if (f == NULL) {
130 		err(EXIT_FAILURE, "failed to fdopen dup2'd /dev/null");
131 	}
132 	check_file(f, dupfd, "fdopen of dup2'd file");
133 
134 	f = freopen("/dev/zero", "r+", f);
135 	if (f == NULL) {
136 		err(EXIT_FAILURE, "failed to freopen dup2'd FILE *");
137 	}
138 	check_file(f, fno_nextfd, "freopen dup2'd FILE *");
139 	fno_nextfd++;
140 }
141 
142 static void
check_alternate(void)143 check_alternate(void)
144 {
145 	wchar_t *c;
146 	size_t s, i;
147 
148 	for (i = 0; i < 10; i++) {
149 		FILE *f, *save;
150 		f = fmemopen(NULL, 10, "a+");
151 		if (f == NULL) {
152 			err(EXIT_FAILURE, "failed to create fmemopen stream");
153 		}
154 		check_file(f, -1, "alternating memstream, fopen (fmemopen)");
155 
156 		save = f;
157 		f = fopen("/dev/zero", "r+");
158 		if (f == NULL) {
159 			err(EXIT_FAILURE, "failed to open /dev/zero");
160 		}
161 		check_file(f, fno_nextfd, "alternating memstream, fopen "
162 		    "(file)");
163 		fno_nextfd++;
164 
165 		f = open_wmemstream(&c, &s);
166 		if (f == NULL) {
167 			err(EXIT_FAILURE, "failed to create open_wmemstream() "
168 			    "stream");
169 		}
170 		check_file(f, -1, "alternating memstream, fopen (wmemstream)");
171 
172 		f = freopen("/dev/null", "r+", save);
173 		if (f == NULL) {
174 			err(EXIT_FAILURE, "failed to freopen /dev/null from "
175 			    "fmemopen()");
176 		}
177 		check_file(f, fno_nextfd, "alternating memstream, fopen "
178 		    "(reopen)");
179 
180 		f = freopen("/dev/zero", "a+", f);
181 		check_file(f, fno_nextfd, "alternating memstream, fopen "
182 		    "(reopen file)");
183 		fno_nextfd++;
184 	}
185 }
186 
187 int
main(void)188 main(void)
189 {
190 	check_file(stdin, STDIN_FILENO, "default stdin fd is correct");
191 	check_file(stdout, STDOUT_FILENO, "default stdout fd is correct");
192 	check_file(stderr, STDERR_FILENO, "default stderr fd is correct");
193 
194 	/*
195 	 * Establish our base fd. The test runner can open files on our behalf.
196 	 */
197 	fno_nextfd = open("/dev/null", O_RDONLY);
198 	if (fno_nextfd < 0) {
199 		err(EXIT_FAILURE, "failed to open /dev/null");
200 	}
201 	fno_nextfd++;
202 	check_open_n(10);
203 	fno_nextfd += 10;
204 	check_memstream();
205 	check_fdopen();
206 	check_alternate();
207 
208 	printf("%d/%d tests passed\n", fno_ntests - fno_nfails, fno_ntests);
209 	return (fno_nfails > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
210 }
211