blob: fd00c1948738eabd5edac11e5ca6977da365a201 [file] [log] [blame]
plars865695b2001-08-27 22:15:12 +00001/*
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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
robbiew4644c7e2002-04-26 14:33:32 +000042 * 04/2002 wjhuie sigset cleanups
robbiew51281002002-06-20 17:02:19 +000043 * 06/2002 Shaobo Li
44 * fix testcase 7, add each testcase comment.
plars865695b2001-08-27 22:15:12 +000045 *
46 * Restrictions
47 * None
48 */
49
50#include <stdio.h>
plars29bcde82002-10-07 17:21:48 +000051#include <stdlib.h>
plars865695b2001-08-27 22:15:12 +000052#include <signal.h>
plars29bcde82002-10-07 17:21:48 +000053#include <sys/types.h>
plars865695b2001-08-27 22:15:12 +000054#include <sys/uio.h>
55#include <sys/fcntl.h>
plars29bcde82002-10-07 17:21:48 +000056#include <sys/utsname.h>
57#include <linux/version.h>
plars865695b2001-08-27 22:15:12 +000058#include <memory.h>
59#include <errno.h>
60#include <test.h>
61#include <usctest.h>
62
63#define K_1 1024
64#define M_1 K_1 * K_1
65#define G_1 M_1 * K_1
66
67#define NBUFS 4
68#define CHUNK 64 /* single chunk */
69#define MAX_IOVEC 16
70#define DATA_FILE "writev_data_file"
71
72char buf1[K_1], buf2[K_1], buf3[K_1];
plars29bcde82002-10-07 17:21:48 +000073int newwritev;
plars865695b2001-08-27 22:15:12 +000074
75struct iovec wr_iovec[MAX_IOVEC] = {
76 /* iov_base */ /* iov_len */
77
78 /* testcase# 1 */
79 buf1, -1,
80 (buf1 + CHUNK), CHUNK,
81 (buf1 + CHUNK * 2), CHUNK,
82
83 /* testcase# 2 */
84 (buf1 + CHUNK * 3), G_1,
85 (buf1 + CHUNK * 4), G_1,
86 (buf1 + CHUNK * 5), G_1,
87
88 /* testcase# 3 */
89 (buf1 + CHUNK * 6), CHUNK,
90 (caddr_t)-1, CHUNK,
91 (buf1 + CHUNK * 8), CHUNK,
92
93 /* testcase# 4 */
94 (buf1 + CHUNK * 9), CHUNK,
95
96 /* testcase# 5 */
97 (buf1 + CHUNK * 10), CHUNK,
98
99 /* testcase# 6 */
100 (buf1 + CHUNK * 11), CHUNK,
101
102 /* testcase# 7 */
103 (buf1 + CHUNK * 12), CHUNK,
104
105 /* testcase# 8 */
106 (buf1 + CHUNK * 13), 0,
107
robbiew51281002002-06-20 17:02:19 +0000108 /* testcase# 7 */
plars865695b2001-08-27 22:15:12 +0000109 (caddr_t)NULL, 0,
plars865695b2001-08-27 22:15:12 +0000110 (caddr_t)NULL, 0,
111};
112
113char name[K_1], f_name[K_1];
114
115/* 0 terminated list of expected errnos */
116int exp_enos[] = {14, 22, 32, 77, 0};
117
118int fd[4], in_sighandler;
119int pfd[2]; /* pipe fd's */
120char *buf_list[NBUFS];
121int fail;
122
123void sighandler(int);
124long l_seek(int, long, int);
125int fill_mem(char *, int, int);
126int init_buffs(char *[]);
127void setup(void);
128void cleanup(void);
129
130char *TCID = "writev01";
131int TST_TOTAL = 1;
132extern int Tst_count;
133
134main(int argc, char **argv)
135{
136 int nbytes, ret;
137
138 int lc; /* loop counter */
139 char *msg; /* message returned from parse_opts */
140
141 /* parse standard options */
142 if ((msg = parse_opts(argc, argv, (option_t *)NULL, NULL)) !=
143 (char *) NULL) {
144 tst_brkm(TBROK, cleanup, "OPTION PARSING ERROR - %s", msg);
145 /*NOTREACHED*/
146 }
147
148 /* set "tstdir", and "testfile" vars */
149 setup();
150
151 /* The following loop checks looping state if -i option given */
152 for (lc = 0; TEST_LOOPING(lc); lc++) {
153
154
155 /* reset Tst_count in case we are looping */
156 Tst_count = 0;
157
158 buf_list[0] = buf1;
159 buf_list[1] = buf2;
160 buf_list[2] = buf3;
161 buf_list[3] = (char *)NULL;
162
163 fd[1] = -1; /* Invalid file descriptor */
164
robbiew4644c7e2002-04-26 14:33:32 +0000165 if (signal(SIGTERM, sighandler) == SIG_ERR) {
166 perror("signal: SIGTERM");
plars865695b2001-08-27 22:15:12 +0000167 cleanup();
168 /*NOTREACHED*/
169 }
170
robbiew4644c7e2002-04-26 14:33:32 +0000171 if (signal(SIGPIPE, sighandler) == SIG_ERR) {
172 perror("signal: SIGPIPE");
plars865695b2001-08-27 22:15:12 +0000173 cleanup();
174 /*NOTREACHED*/
175 }
176
177 init_buffs(buf_list);
178
179 if ((fd[0] = open(f_name, O_WRONLY | O_CREAT, 0666)) < 0) {
180 tst_resm(TFAIL, "open failed: fname = %s, errno = %d",
181 f_name, errno);
182 cleanup();
183 /*NOTREACHED*/
184 } else if ((nbytes = write(fd[0], buf_list[2], K_1)) != K_1) {
185 tst_resm(TFAIL, "write failed: nbytes = %d, "
186 "errno = %d", nbytes, errno);
187 cleanup();
188 /*NOTREACHED*/
189 }
190
191 if (close(fd[0]) < 0) {
192 tst_resm(TFAIL, "close failed: errno: %d", errno);
193 cleanup();
194 /*NOTREACHED*/
195 }
196
197 if ((fd[0] = open(f_name, O_RDWR, 0666)) < 0) {
198 tst_resm(TFAIL, "open failed: fname = %s, errno = %d",
199 f_name, errno);
200 cleanup();
201 /*NOTREACHED*/
202 }
203
robbiew51281002002-06-20 17:02:19 +0000204block1: /* given vector length -1, writev() return EINVAL. */
plars865695b2001-08-27 22:15:12 +0000205 tst_resm(TINFO, "Enter Block 1");
206 fail = 0;
207
208 TEST(writev(fd[0], wr_iovec, 1));
209 if (TEST_RETURN < 0) {
210 TEST_ERROR_LOG(TEST_ERRNO);
211 if (TEST_ERRNO == EINVAL) {
212 tst_resm(TINFO, "Received EINVAL as expected");
213 } else {
214 tst_resm(TFAIL, "Expected errno = EINVAL, "
215 "got %d", TEST_ERRNO);
216 fail = 1;
217 }
218 } else {
219 tst_resm(TFAIL, "writev() failed to fail");
220 fail = 1;
221 }
222 if (fail) {
223 tst_resm(TINFO, "block 1 FAILED");
224 } else {
225 tst_resm(TINFO, "block 1 PASSED");
226 }
227 tst_resm(TINFO, "Exit block 1");
228
robbiew51281002002-06-20 17:02:19 +0000229block2: /* This testcases doesn't look like what it intent to do
230 * 1. it is not using the wr_iovec initialized
231 * 2. read() and following message is not consistent
232 */
plars865695b2001-08-27 22:15:12 +0000233 tst_resm(TINFO, "Enter block 2");
234 fail = 0;
235
236 if (l_seek(fd[0], CHUNK * 6, 0) < 0) {
237 TEST_ERROR_LOG(errno);
238 tst_resm(TBROK, "block2: 1st lseek failed");
239 fail = 1;
240 }
241
242 if ((ret = writev(fd[0], (wr_iovec + 6), 3)) == CHUNK) {
243 if (l_seek(fd[0], CHUNK * 6, 0) < 0) {
244 TEST_ERROR_LOG(errno);
245 tst_resm(TFAIL, "block2: 2nd lseek failed");
246 fail = 1;
247 }
248 if ((nbytes = read(fd[0], buf_list[0], CHUNK)) !=
249 CHUNK) {
250 perror("read error");
251 tst_resm(TFAIL, "expected nbytes = 1024, "
252 "got = %d", nbytes);
253 fail = 1;
254 } else if (memcmp((buf_list[0] + CHUNK * 6),
255 (buf_list[2] + CHUNK * 6),
256 CHUNK) != 0) {
257 tst_resm(TFAIL, "Error: writev() over "
258 "wrote %s", f_name);
259 fail = 1;
260 }
261 } else {
262 tst_resm(TFAIL, "writev() failed unexpectedly");
263 fail = 1;
264 }
265 if (fail) {
266 tst_resm(TINFO, "block 2 FAILED");
267 } else {
268 tst_resm(TINFO, "block 2 PASSED");
269 }
270 tst_resm(TINFO, "Exit block 2");
271
robbiew51281002002-06-20 17:02:19 +0000272block3: /* given 1 bad vector buffer with good ones, writev() success */
plars865695b2001-08-27 22:15:12 +0000273 tst_resm(TINFO, "Enter block 3");
274 fail = 0;
275
276 if (lseek(fd[0], CHUNK * 6, 0) < 0) {
277 TEST_ERROR_LOG(errno);
278 tst_resm(TFAIL, "block3: 1st lseek failed");
279 fail = 1;
280 }
281 if ((nbytes = writev(fd[0], (wr_iovec + 6), 3)) < 0) {
282 TEST_ERROR_LOG(errno);
283 if (errno == EFAULT) {
284 tst_resm(TFAIL, "Got EFAULT");
285 fail = 1;
286 }
287 }
288 if (l_seek(fd[0], 0, 0) < 0) {
289 TEST_ERROR_LOG(errno);
290 tst_resm(TFAIL, "block3: 2nd lseek failed");
291 fail = 1;
292 }
293 if ((nbytes = read(fd[0], buf_list[0], K_1)) != K_1) {
294 perror("read error");
295 tst_resm(TFAIL, "expected nbytes = 1024, got = %d",
296 nbytes);
297 fail = 1;
298 } else if (memcmp((buf_list[0]+ CHUNK * 6),
299 (buf_list[2] + CHUNK * 6),
300 CHUNK * 3) != 0) {
301 tst_resm(TFAIL, "Error: writev() over wrote %s",
302 f_name);
303 fail = 1;
304 }
305
306 if (fail) {
307 tst_resm(TINFO, "block 3 FAILED");
308 } else {
309 tst_resm(TINFO, "block 3 PASSED");
310 }
311 tst_resm(TINFO, "Exit block 3");
312
robbiew51281002002-06-20 17:02:19 +0000313block4: /* given bad file discriptor, writev() return EBADF. */
plars865695b2001-08-27 22:15:12 +0000314 tst_resm(TINFO, "Enter block 4");
315 fail = 0;
316
317 TEST(writev(fd[1], (wr_iovec + 9), 1));
318 if (TEST_RETURN < 0) {
319 TEST_ERROR_LOG(TEST_ERRNO);
320 if (TEST_ERRNO == EBADF) {
321 tst_resm(TINFO, "Received EBADF as expected");
322 } else {
323 tst_resm(TFAIL, "expected errno = EBADF, "
324 "got %d", TEST_ERRNO);
325 fail = 1;
326 }
327 } else {
328 tst_resm(TFAIL, "Error: writev() returned a "
329 "positive value");
330 fail = 1;
331 }
332
333 if (fail) {
334 tst_resm(TINFO, "block 4 FAILED");
335 } else {
336 tst_resm(TINFO, "block 4 PASSED");
337 }
338 tst_resm(TINFO, "Exit block 4");
339
robbiew51281002002-06-20 17:02:19 +0000340block5: /* given invalid vector count, writev() return EINVAL */
plars865695b2001-08-27 22:15:12 +0000341 tst_resm(TINFO, "Enter block 5");
342 fail = 0;
343
344 TEST(writev(fd[0], (wr_iovec + 10), -1));
345 if (TEST_RETURN < 0) {
346 TEST_ERROR_LOG(TEST_ERRNO);
347 if (TEST_ERRNO == EINVAL) {
348 tst_resm(TINFO, "Received EINVAL as expected");
349 } else {
350 tst_resm(TFAIL, "expected errno = EINVAL, "
351 "got %d", TEST_ERRNO);
352 fail = 1;
353 }
354 } else {
355 tst_resm(TFAIL, "Error: writev() returned a "
356 "positive value");
357 fail = 1;
358 }
359
360 if (fail) {
361 tst_resm(TINFO, "block 5 FAILED");
362 } else {
363 tst_resm(TINFO, "block 5 PASSED");
364 }
365 tst_resm(TINFO, "Exit block 5");
366
robbiew51281002002-06-20 17:02:19 +0000367block6: /* given no buffer vector, writev() success */
plars29bcde82002-10-07 17:21:48 +0000368 if(newwritev) {
369
plars865695b2001-08-27 22:15:12 +0000370 tst_resm(TINFO, "Enter block 6");
371 fail = 0;
372
373 TEST(writev(fd[0], (wr_iovec + 11), 0));
plars29bcde82002-10-07 17:21:48 +0000374 if ((TEST_RETURN == -1) && (TEST_ERRNO == EINVAL)) {
375 tst_resm(TPASS, "writev() failed with expected EINVAL "
376 "when count == 0");
plars865695b2001-08-27 22:15:12 +0000377 } else {
plars29bcde82002-10-07 17:21:48 +0000378 tst_resm(TFAIL, "writev() did not fail with EINVAL "
379 "when count == 0");
380 fail = 1;
plars865695b2001-08-27 22:15:12 +0000381 }
382
383 if (fail) {
384 tst_resm(TINFO, "block 6 FAILED");
385 } else {
386 tst_resm(TINFO, "block 6 PASSED");
387 }
388 tst_resm(TINFO, "Exit block 6");
plars29bcde82002-10-07 17:21:48 +0000389 }
plars865695b2001-08-27 22:15:12 +0000390
robbiew51281002002-06-20 17:02:19 +0000391block7: /* given 4 vectors, 2 are NULL, 1 with 0 length and 1 with fixed length,
392 * writev() success writing fixed length.
393 */
plars865695b2001-08-27 22:15:12 +0000394 tst_resm(TINFO, "Enter block 7");
395 fail = 0;
396
397 l_seek(fd[0], CHUNK * 12, 0);
robbiew51281002002-06-20 17:02:19 +0000398 if ((ret = writev(fd[0], (wr_iovec + 12), 4)) != CHUNK) {
plars865695b2001-08-27 22:15:12 +0000399 tst_resm(TFAIL, "writev() failed writing %d bytes, "
400 "followed by two NULL vectors", CHUNK);
401 fail = 1;
402 } else {
403 tst_resm(TPASS, "writev passed writing %d bytes, "
404 "followed by two NULL vectors", CHUNK);
405 }
406
407 if (fail) {
408 tst_resm(TINFO, "block 7 FAILED");
409 } else {
410 tst_resm(TINFO, "block 7 PASSED");
411 }
412 tst_resm(TINFO, "Exit block 7");
413
robbiew51281002002-06-20 17:02:19 +0000414block8: /* try to write to a closed pipe, writev() return EPIPE. */
plars865695b2001-08-27 22:15:12 +0000415 tst_resm(TINFO, "Enter block 8");
416 fail = 0;
417
418 if (pipe(pfd) < 0) {
419 TEST_ERROR_LOG(errno);
420 perror("pipe");
421 tst_resm(TFAIL, "pipe failed: errno = %d", errno);
422 fail = 1;
423 } else {
424 if (close(pfd[0]) < 0) {
425 TEST_ERROR_LOG(errno);
426 perror("close");
427 tst_resm(TFAIL, "close failed: errno = %d",
428 errno);
429 fail = 1;
430 } else if ((writev(pfd[1], (wr_iovec + 12), 1)
431 < 0) && in_sighandler) {
432 TEST_ERROR_LOG(errno);
433 if (errno == EPIPE) {
434 tst_resm(TINFO, "Received EPIPE as "
435 "expected");
436 } else {
437 tst_resm(TFAIL, "expected errno = "
438 "EPIPE, got %d", errno);
439 fail = 1;
440 }
441 } else {
442 tst_resm(TFAIL, "Error: writev() returned a "
443 "positive value");
444 fail = 1;
445 }
446 }
447 if (fail) {
448 tst_resm(TINFO, "block 8 FAILED");
449 } else {
450 tst_resm(TINFO, "block 8 PASSED");
451 }
452 tst_resm(TINFO, "Exit block 8");
453 }
454 cleanup();
455 /*NOTREACHED*/
456}
457
458/*
459 * setup()
460 * performs all ONE TIME setup for this test
461 */
462void
463setup(void)
464{
plars29bcde82002-10-07 17:21:48 +0000465 struct utsname utsbuf;
466 char *r1, *r2, *r3;
467
468 newwritev=0;
plars865695b2001-08-27 22:15:12 +0000469 /* capture signals */
470 tst_sig(FORK, DEF_HANDLER, cleanup);
471
472 /* Set up the expected error numbers for -e option */
473 TEST_EXP_ENOS(exp_enos);
474
475 /* Pause if that option was specified.
476 * TEST_PAUSE contains the code to fork the test with the -i option.
477 * You want to make sure you do this before you create your temporary
478 * directory.
479 */
480 TEST_PAUSE;
481
482 /* Create a unique temporary directory and chdir() to it. */
483 tst_tmpdir();
484
485 strcpy(name, DATA_FILE);
486 sprintf(f_name, "%s.%d", name, getpid());
plars29bcde82002-10-07 17:21:48 +0000487
488 /* kernels after 2.5.35 implement a new readv/writev behaviour
489 * * that returns EINVAL when count == 0
490 * */
491 uname(&utsbuf);
492 r1 = strtok(utsbuf.release,".");
493 r2 = strtok(NULL,".");
494 r3 = strtok(NULL,".");
495 if (KERNEL_VERSION(atoi(r1),atoi(r2),atoi(r3)) > KERNEL_VERSION(2,5,35))
496 newwritev = 1;
plars865695b2001-08-27 22:15:12 +0000497}
498
499/*
500 * cleanup()
501 * performs all ONE TIME cleanup for this test at
502 * completion or premature exit
503 */
504void
505cleanup(void)
506{
507 /*
508 * print timing stats if that option was specified.
509 * print errno log if that option was specified.
510 */
511 TEST_CLEANUP;
512
513 if (unlink(f_name) < 0) {
514 tst_resm(TFAIL, "unlink Failed--file = %s, errno = %d",
515 f_name, errno);
516 }
517 tst_rmdir();
518
519 tst_exit();
520}
521
522int
523init_buffs(char *pbufs[])
524{
525 int i;
526
527 for (i = 0; pbufs[i] != (char *)NULL; i++) {
528 switch (i) {
529 case 0:
530
531 case 1: fill_mem(pbufs[i], 0, 1);
532 break;
533
534 case 2: fill_mem(pbufs[i], 1, 0);
535 break;
536
537 default: tst_resm(TFAIL, "Error detected: init_buffs()");
538 cleanup();
539 /*NOTREACHED*/
540 }
541 }
542 return(0);
543}
544
545int
546fill_mem(char *c_ptr, int c1, int c2)
547{
548 int count;
549
550 for (count = 1; count <= K_1 / CHUNK; count++) {
551 if (count & 0x01) { /* if odd */
552 memset(c_ptr, c1, CHUNK);
553 } else { /* if even */
554 memset(c_ptr, c2, CHUNK);
555 }
556 }
557 return(0);
558}
559
560void
561sighandler(int sig)
562{
563 switch (sig) {
564 case SIGTERM: break;
565
566 case SIGPIPE: ++in_sighandler;
567 return;
568
569 default: tst_resm(TFAIL, "sighandler() received invalid "
570 "signal:%d", sig);
571 break;
572 }
573
574 if (unlink(f_name) < 0) {
575 tst_resm(TFAIL, "unlink Failed--file = %s, errno = %d",
576 f_name, errno);
577 tst_exit();
578 /*NOTREACHED*/
579 }
580 exit(sig);
581}
582
583long
584l_seek(int fdesc, long offset, int whence)
585{
586 if (lseek(fdesc, offset, whence) < 0) {
587 perror("lseek");
588 tst_resm(TFAIL, "lseek Failed : errno = %d", errno);
589 fail = 1;
590 }
591 return(0);
592}