1 /*
2 *
3 * Copyright (c) International Business Machines Corp., 2001
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
13 * the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 /*
21 * NAME
22 * writev01.c
23 *
24 * DESCRIPTION
25 * Testcase to check the basic functionality of writev(2) system call.
26 *
27 * ALGORITHM
28 * Create a IO vector, and attempt to writev various components of it.
29 *
30 * USAGE: <for command-line>
31 * writev01 [-c n] [-e] [-i n] [-I x] [-P x] [-t]
32 * where, -c n : Run n copies concurrently.
33 * -e : Turn on errno logging.
34 * -i n : Execute test n times.
35 * -I x : Execute test for x seconds.
36 * -P x : Pause for x seconds between iterations.
37 * -t : Turn on syscall timing.
38 *
39 * History
40 * 07/2001 John George
41 * -Ported
42 * 04/2002 wjhuie sigset cleanups
43 * 06/2002 Shaobo Li
44 * fix testcase 7, add each testcase comment.
45 *
46 * Restrictions
47 * None
48 */
49
50 #include <stdio.h>
51 #include <sys/types.h>
52 #include <signal.h>
53 #include <sys/uio.h>
54 #include <sys/fcntl.h>
55 #include <memory.h>
56 #include <errno.h>
57 #include "test.h"
58 #include <sys/mman.h>
59
60 #define K_1 1024
61 #define M_1 K_1 * K_1
62 #define G_1 M_1 * K_1
63
64 #define NBUFS 4
65 #define CHUNK 64 /* single chunk */
66 #define MAX_IOVEC 16
67 #define DATA_FILE "writev_data_file"
68
69 char buf1[K_1], buf2[K_1], buf3[K_1];
70
71 struct iovec wr_iovec[MAX_IOVEC] = {
72 /* iov_base *//* iov_len */
73
74 /* testcase# 1 */
75 {buf1, -1},
76 {(buf1 + CHUNK), CHUNK},
77 {(buf1 + CHUNK * 2), CHUNK},
78
79 /* testcase# 2 */
80 {(buf1 + CHUNK * 3), G_1},
81 {(buf1 + CHUNK * 4), G_1},
82 {(buf1 + CHUNK * 5), G_1},
83
84 /* testcase# 3 */
85 {(buf1 + CHUNK * 6), CHUNK},
86 {(caddr_t) - 1, CHUNK},
87 {(buf1 + CHUNK * 8), CHUNK},
88
89 /* testcase# 4 */
90 {(buf1 + CHUNK * 9), CHUNK},
91
92 /* testcase# 5 */
93 {(buf1 + CHUNK * 10), CHUNK},
94
95 /* testcase# 6 */
96 {(buf1 + CHUNK * 11), CHUNK},
97
98 /* testcase# 7 */
99 {(buf1 + CHUNK * 12), CHUNK},
100
101 /* testcase# 8 */
102 {(buf1 + CHUNK * 13), 0},
103
104 /* testcase# 7 */
105 {NULL, 0},
106 {NULL, 0}
107 };
108
109 char name[K_1], f_name[K_1];
110
111 char *bad_addr = 0;
112
113 int fd[4], in_sighandler;
114 int pfd[2]; /* pipe fd's */
115 char *buf_list[NBUFS];
116
117 void sighandler(int);
118 int fill_mem(char *, int, int);
119 void init_buffs(char *[]);
120 void setup(void);
121 void cleanup(void);
122
123 char *TCID = "writev01";
124 int TST_TOTAL = 1;
125
main(int argc,char ** argv)126 int main(int argc, char **argv)
127 {
128 int nbytes, ret;
129
130 int lc;
131
132 tst_parse_opts(argc, argv, NULL, NULL);
133
134 setup();
135
136 for (lc = 0; TEST_LOOPING(lc); lc++) {
137
138 tst_count = 0;
139
140 buf_list[0] = buf1;
141 buf_list[1] = buf2;
142 buf_list[2] = buf3;
143 buf_list[3] = NULL;
144
145 fd[1] = -1; /* Invalid file descriptor */
146
147 if (signal(SIGTERM, sighandler) == SIG_ERR)
148 tst_brkm(TBROK | TERRNO, cleanup,
149 "signal(SIGTERM, ..) failed");
150
151 if (signal(SIGPIPE, sighandler) == SIG_ERR)
152 tst_brkm(TBROK | TERRNO, cleanup,
153 "signal(SIGPIPE, ..) failed");
154
155 init_buffs(buf_list);
156
157 if ((fd[0] = open(f_name, O_WRONLY | O_CREAT, 0666)) == -1)
158 tst_brkm(TBROK | TERRNO, cleanup,
159 "open(.., O_WRONLY|O_CREAT, ..) failed");
160 else if ((nbytes = write(fd[0], buf_list[2], K_1)) != K_1)
161 tst_brkm(TBROK | TERRNO, cleanup, "write failed");
162
163 if (close(fd[0]) == -1)
164 tst_brkm(TBROK | TERRNO, cleanup, "close failed");
165
166 if ((fd[0] = open(f_name, O_RDWR, 0666)) == -1)
167 tst_brkm(TBROK | TERRNO, cleanup, "open failed");
168 //block1: /* given vector length -1, writev return EINVAL. */
169 tst_resm(TPASS, "Enter Block 1");
170
171 TEST(writev(fd[0], wr_iovec, 1));
172 if (TEST_RETURN == -1) {
173 if (TEST_ERRNO == EINVAL)
174 tst_resm(TPASS, "Received EINVAL as expected");
175 else
176 tst_resm(TFAIL, "Expected errno = EINVAL, "
177 "got %d", TEST_ERRNO);
178 } else
179 tst_resm(TFAIL, "writev failed to fail");
180 tst_resm(TINFO, "Exit block 1");
181
182 //block2:
183 /* This testcases doesn't look like what it intent to do
184 * 1. it is not using the wr_iovec initialized
185 * 2. read() and following message is not consistent
186 */
187 tst_resm(TPASS, "Enter block 2");
188
189 if (lseek(fd[0], CHUNK * 6, 0) == -1)
190 tst_resm(TBROK | TERRNO, "block2: 1st lseek failed");
191
192 if ((ret = writev(fd[0], (wr_iovec + 6), 3)) == CHUNK) {
193 if (lseek(fd[0], CHUNK * 6, 0) == -1)
194 tst_brkm(TBROK | TERRNO, cleanup,
195 "block2: 2nd lseek failed");
196 if ((nbytes = read(fd[0], buf_list[0], CHUNK)) != CHUNK)
197 tst_resm(TFAIL, "read failed; expected nbytes "
198 "= 1024, got = %d", nbytes);
199 else if (memcmp((buf_list[0] + CHUNK * 6),
200 (buf_list[2] + CHUNK * 6), CHUNK) != 0)
201 tst_resm(TFAIL, "writev over "
202 "wrote %s", f_name);
203 } else
204 tst_resm(TFAIL | TERRNO, "writev failed unexpectedly");
205 tst_resm(TINFO, "Exit block 2");
206
207 //block3: /* given 1 bad vector buffer with good ones, writev success */
208 tst_resm(TPASS, "Enter block 3");
209
210 if (lseek(fd[0], CHUNK * 6, 0) == -1)
211 tst_brkm(TBROK | TERRNO, cleanup,
212 "block3: 1st lseek failed");
213 if ((nbytes = writev(fd[0], (wr_iovec + 6), 3)) == -1) {
214 if (errno == EFAULT)
215 tst_resm(TFAIL, "Got EFAULT");
216 }
217 if (lseek(fd[0], 0, 0) == -1)
218 tst_brkm(TBROK, cleanup, "block3: 2nd lseek failed");
219 if ((nbytes = read(fd[0], buf_list[0], K_1)) != K_1) {
220 tst_resm(TFAIL | TERRNO,
221 "read failed; expected nbytes = 1024, got = %d",
222 nbytes);
223 } else if (memcmp((buf_list[0] + CHUNK * 6),
224 (buf_list[2] + CHUNK * 6), CHUNK * 3) != 0)
225 tst_resm(TFAIL, "writev overwrote file");
226
227 tst_resm(TINFO, "Exit block 3");
228
229 //block4: /* given bad file discriptor, writev return EBADF. */
230 tst_resm(TPASS, "Enter block 4");
231
232 TEST(writev(fd[1], (wr_iovec + 9), 1));
233 if (TEST_RETURN == -1) {
234 if (TEST_ERRNO == EBADF)
235 tst_resm(TPASS, "Received EBADF as expected");
236 else
237 tst_resm(TFAIL, "expected errno = EBADF, "
238 "got %d", TEST_ERRNO);
239 } else
240 tst_resm(TFAIL, "writev returned a " "positive value");
241
242 tst_resm(TINFO, "Exit block 4");
243
244 //block5: /* given invalid vector count, writev return EINVAL */
245 tst_resm(TPASS, "Enter block 5");
246
247 TEST(writev(fd[0], (wr_iovec + 10), -1));
248 if (TEST_RETURN == -1) {
249 if (TEST_ERRNO == EINVAL)
250 tst_resm(TPASS, "Received EINVAL as expected");
251 else
252 tst_resm(TFAIL, "expected errno = EINVAL, "
253 "got %d", TEST_ERRNO);
254 } else
255 tst_resm(TFAIL, "writev returned a " "positive value");
256
257 tst_resm(TINFO, "Exit block 5");
258
259 //block6: /* given no buffer vector, writev success */
260 tst_resm(TPASS, "Enter block 6");
261
262 TEST(writev(fd[0], (wr_iovec + 11), 0));
263 if (TEST_RETURN == -1)
264 tst_resm(TFAIL | TTERRNO, "writev failed");
265 else
266 tst_resm(TPASS, "writev wrote 0 iovectors");
267
268 tst_resm(TINFO, "Exit block 6");
269
270 //block7:
271 /* given 4 vectors, 2 are NULL, 1 with 0 length and 1 with fixed length,
272 * writev success writing fixed length.
273 */
274 tst_resm(TPASS, "Enter block 7");
275
276 if (lseek(fd[0], CHUNK * 12, 0) == -1)
277 tst_resm(TBROK, "lseek failed");
278 else if ((ret = writev(fd[0], (wr_iovec + 12), 4)) != CHUNK)
279 tst_resm(TFAIL, "writev failed writing %d bytes, "
280 "followed by two NULL vectors", CHUNK);
281 else
282 tst_resm(TPASS, "writev passed writing %d bytes, "
283 "followed by two NULL vectors", CHUNK);
284
285 tst_resm(TINFO, "Exit block 7");
286
287 //block8: /* try to write to a closed pipe, writev return EPIPE. */
288 tst_resm(TPASS, "Enter block 8");
289
290 if (pipe(pfd) == -1)
291 tst_resm(TFAIL | TERRNO, "pipe failed");
292 else {
293 if (close(pfd[0]) == -1)
294 tst_resm(TFAIL | TERRNO, "close failed");
295 else if (writev(pfd[1], (wr_iovec + 12), 1) == -1 &&
296 in_sighandler) {
297 if (errno == EPIPE)
298 tst_resm(TPASS, "Received EPIPE as "
299 "expected");
300 else
301 tst_resm(TFAIL | TERRNO,
302 "didn't get EPIPE");
303 } else
304 tst_resm(TFAIL, "writev returned a positive "
305 "value");
306 }
307 tst_resm(TINFO, "Exit block 8");
308 }
309 cleanup();
310 tst_exit();
311 }
312
setup(void)313 void setup(void)
314 {
315
316 tst_sig(FORK, sighandler, cleanup);
317
318 TEST_PAUSE;
319
320 tst_tmpdir();
321
322 strcpy(name, DATA_FILE);
323 sprintf(f_name, "%s.%d", name, getpid());
324
325 bad_addr = mmap(0, 1, PROT_NONE,
326 MAP_PRIVATE_EXCEPT_UCLINUX | MAP_ANONYMOUS, 0, 0);
327 if (bad_addr == MAP_FAILED)
328 tst_brkm(TBROK | TERRNO, cleanup, "mmap failed");
329 wr_iovec[7].iov_base = bad_addr;
330
331 }
332
cleanup(void)333 void cleanup(void)
334 {
335 if (munmap(bad_addr, 1) == -1)
336 tst_resm(TBROK | TERRNO, "munmap failed");
337
338 close(fd[0]);
339 close(fd[1]);
340
341 if (unlink(f_name) == -1)
342 tst_resm(TBROK | TERRNO, "unlink failed");
343
344 tst_rmdir();
345
346 }
347
init_buffs(char * pbufs[])348 void init_buffs(char *pbufs[])
349 {
350 int i;
351
352 for (i = 0; pbufs[i] != NULL; i++) {
353 switch (i) {
354 case 0:
355
356 case 1:
357 fill_mem(pbufs[i], 0, 1);
358 break;
359
360 case 2:
361 fill_mem(pbufs[i], 1, 0);
362 break;
363
364 default:
365 tst_brkm(TBROK, cleanup, "error detected: init_buffs");
366 }
367 }
368 }
369
fill_mem(char * c_ptr,int c1,int c2)370 int fill_mem(char *c_ptr, int c1, int c2)
371 {
372 int count;
373
374 for (count = 1; count <= K_1 / CHUNK; count++) {
375 if (count & 0x01) { /* if odd */
376 memset(c_ptr, c1, CHUNK);
377 } else { /* if even */
378 memset(c_ptr, c2, CHUNK);
379 }
380 }
381 return 0;
382 }
383
sighandler(int sig)384 void sighandler(int sig)
385 {
386 switch (sig) {
387 case SIGTERM:
388 break;
389
390 case SIGPIPE:
391 in_sighandler++;
392 return;
393
394 default:
395 tst_resm(TFAIL, "sighandler received invalid signal:%d", sig);
396 break;
397 }
398 }
399