writev01.c revision 0722a2ba05fbb7f320e3f3cc63f86c97393bdd71
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., 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
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 <usctest.h>
59#include <sys/mman.h>
60
61#define	K_1	1024
62#define	M_1	K_1 * K_1
63#define	G_1	M_1 * K_1
64
65#define	NBUFS		4
66#define	CHUNK		64		/* single chunk */
67#define	MAX_IOVEC	16
68#define	DATA_FILE	"writev_data_file"
69
70char buf1[K_1], buf2[K_1], buf3[K_1];
71
72struct iovec wr_iovec[MAX_IOVEC] = {
73	/* iov_base */		/* iov_len */
74
75	/* testcase# 1 */
76	{buf1,			-1},
77	{(buf1 + CHUNK),	CHUNK},
78	{(buf1 + CHUNK * 2),	CHUNK},
79
80	/* testcase# 2 */
81	{(buf1 + CHUNK * 3),	G_1},
82	{(buf1 + CHUNK * 4),	G_1},
83	{(buf1 + CHUNK * 5),	G_1},
84
85	/* testcase# 3 */
86	{(buf1 + CHUNK * 6),	CHUNK},
87	{(caddr_t)-1,		CHUNK},
88	{(buf1 + CHUNK * 8),	CHUNK},
89
90	/* testcase# 4 */
91	{(buf1 + CHUNK * 9),	CHUNK},
92
93	/* testcase# 5 */
94	{(buf1 + CHUNK * 10),	CHUNK},
95
96	/* testcase# 6 */
97	{(buf1 + CHUNK * 11),	CHUNK},
98
99	/* testcase# 7 */
100	{(buf1 + CHUNK * 12),	CHUNK},
101
102	/* testcase# 8 */
103	{(buf1 + CHUNK * 13),	0},
104
105	/* testcase# 7 */
106	{(caddr_t)NULL,		0},
107	{(caddr_t)NULL,		0}
108};
109
110char name[K_1], f_name[K_1];
111
112char * bad_addr = 0;
113
114/* 0 terminated list of expected errnos */
115int exp_enos[] = {14, 22, 32, 77, 0};
116
117int fd[4], in_sighandler;
118int pfd[2];				/* pipe fd's */
119char *buf_list[NBUFS];
120int fail;
121
122void sighandler(int);
123long l_seek(int, long, int);
124int fill_mem(char *, int, int);
125int init_buffs(char *[]);
126void setup(void);
127void cleanup(void);
128
129char *TCID = "writev01";
130int TST_TOTAL = 1;
131extern int Tst_count;
132
133int main(int argc, char **argv)
134{
135	int nbytes, ret;
136
137	int lc;				/* loop counter */
138	char *msg;			/* message returned from parse_opts */
139
140	/* parse standard options */
141	if ((msg = parse_opts(argc, argv, (option_t *)NULL, NULL)) !=
142	    (char *) NULL) {
143		tst_brkm(TBROK, cleanup, "OPTION PARSING ERROR - %s", msg);
144		/*NOTREACHED*/
145	}
146
147	/* set "tstdir", and "testfile" vars */
148	setup();
149
150	/* The following loop checks looping state if -i option given */
151	for (lc = 0; TEST_LOOPING(lc); lc++) {
152
153
154		/* reset Tst_count in case we are looping */
155		Tst_count = 0;
156
157		buf_list[0] = buf1;
158		buf_list[1] = buf2;
159		buf_list[2] = buf3;
160		buf_list[3] = (char *)NULL;
161
162		fd[1] = -1;		/* Invalid file descriptor  */
163
164		if (signal(SIGTERM, sighandler) == SIG_ERR) {
165			perror("signal: SIGTERM");
166			cleanup();
167			/*NOTREACHED*/
168		}
169
170		if (signal(SIGPIPE, sighandler) == SIG_ERR) {
171			perror("signal: SIGPIPE");
172			cleanup();
173			/*NOTREACHED*/
174		}
175
176		init_buffs(buf_list);
177
178		if ((fd[0] = open(f_name, O_WRONLY | O_CREAT, 0666)) < 0) {
179			tst_resm(TFAIL, "open failed: fname = %s, errno = %d",
180				 f_name, errno);
181			cleanup();
182			/*NOTREACHED*/
183		} else if ((nbytes = write(fd[0], buf_list[2], K_1)) != K_1) {
184			tst_resm(TFAIL, "write failed: nbytes = %d, "
185				 "errno = %d", nbytes, errno);
186			cleanup();
187			/*NOTREACHED*/
188		}
189
190		if (close(fd[0]) < 0) {
191			tst_resm(TFAIL, "close failed: errno: %d", errno);
192			cleanup();
193			/*NOTREACHED*/
194		}
195
196		if ((fd[0] = open(f_name, O_RDWR, 0666)) < 0) {
197			tst_resm(TFAIL, "open failed: fname = %s, errno = %d",
198				 f_name, errno);
199			cleanup();
200			/*NOTREACHED*/
201		}
202
203//block1: /* given vector length -1, writev() return EINVAL. */
204		tst_resm(TINFO, "Enter Block 1");
205		fail = 0;
206
207		TEST(writev(fd[0], wr_iovec, 1));
208		if (TEST_RETURN < 0) {
209			TEST_ERROR_LOG(TEST_ERRNO);
210			if (TEST_ERRNO == EINVAL) {
211				tst_resm(TINFO, "Received EINVAL as expected");
212			} else {
213				tst_resm(TFAIL, "Expected errno = EINVAL, "
214					 "got %d", TEST_ERRNO);
215				fail = 1;
216			}
217		} else {
218			tst_resm(TFAIL, "writev() failed to fail");
219			fail = 1;
220		}
221		if (fail) {
222			tst_resm(TINFO, "block 1 FAILED");
223		} else {
224			tst_resm(TINFO, "block 1 PASSED");
225		}
226		tst_resm(TINFO, "Exit block 1");
227
228//block2:
229	/* 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        */
233		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
272//block3: /* given 1 bad vector buffer with good ones, writev() success */
273		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
313//block4: /* given bad file discriptor, writev() return EBADF. */
314		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
340//block5: /* given invalid vector count, writev() return EINVAL */
341		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
367//block6: /* given no buffer vector, writev() success */
368		tst_resm(TINFO, "Enter block 6");
369		fail = 0;
370
371		TEST(writev(fd[0], (wr_iovec + 11), 0));
372		if (TEST_RETURN < 0) {
373			TEST_ERROR_LOG(TEST_ERRNO);
374			tst_resm(TFAIL, "writev() failed with unexpected errno "
375				 "%d", TEST_ERRNO);
376			fail = 1;
377		} else {
378			tst_resm(TPASS, "writev() wrote 0 iovectors");
379		}
380
381		if (fail) {
382			tst_resm(TINFO, "block 6 FAILED");
383		} else {
384			tst_resm(TINFO, "block 6 PASSED");
385		}
386		tst_resm(TINFO, "Exit block 6");
387
388//block7:
389	 /* given 4 vectors, 2 are NULL, 1 with 0 length and 1 with fixed length,
390         * writev() success writing fixed length.
391         */
392		tst_resm(TINFO, "Enter block 7");
393		fail = 0;
394
395		l_seek(fd[0], CHUNK * 12, 0);
396   		if ((ret = writev(fd[0], (wr_iovec + 12), 4)) != CHUNK) {
397			tst_resm(TFAIL, "writev() failed writing %d bytes, "
398				 "followed by two NULL vectors", CHUNK);
399			fail = 1;
400		} else {
401			tst_resm(TPASS, "writev passed writing %d bytes, "
402				 "followed by two NULL vectors", CHUNK);
403		}
404
405		if (fail) {
406			tst_resm(TINFO, "block 7 FAILED");
407		} else {
408			tst_resm(TINFO, "block 7 PASSED");
409		}
410		tst_resm(TINFO, "Exit block 7");
411
412//block8: /* try to write to a closed pipe, writev() return EPIPE. */
413		tst_resm(TINFO, "Enter block 8");
414		fail = 0;
415
416		if (pipe(pfd) < 0) {
417			TEST_ERROR_LOG(errno);
418			perror("pipe");
419			tst_resm(TFAIL, "pipe failed: errno = %d", errno);
420			fail = 1;
421		} else {
422			if (close(pfd[0]) < 0) {
423				TEST_ERROR_LOG(errno);
424				perror("close");
425				tst_resm(TFAIL, "close failed: errno = %d",
426					 errno);
427				fail = 1;
428			} else if ((writev(pfd[1], (wr_iovec + 12), 1)
429					< 0) && in_sighandler) {
430				TEST_ERROR_LOG(errno);
431				if (errno == EPIPE) {
432					tst_resm(TINFO, "Received EPIPE as "
433						"expected");
434				} else {
435					tst_resm(TFAIL, "expected errno = "
436						 "EPIPE, got %d", errno);
437					fail = 1;
438				}
439			} else {
440				tst_resm(TFAIL, "Error: writev() returned a "
441					 "positive value");
442				fail = 1;
443			}
444		}
445		if (fail) {
446			tst_resm(TINFO, "block 8 FAILED");
447		} else {
448			tst_resm(TINFO, "block 8 PASSED");
449		}
450		tst_resm(TINFO, "Exit block 8");
451	}
452	cleanup();
453	/*NOTREACHED*/
454	return(0);
455}
456
457/*
458 * setup()
459 *	performs all ONE TIME setup for this test
460 */
461void
462setup(void)
463{
464	/* capture signals */
465	tst_sig(FORK, DEF_HANDLER, cleanup);
466
467	/* Set up the expected error numbers for -e option */
468	TEST_EXP_ENOS(exp_enos);
469
470	/* Pause if that option was specified.
471	 * TEST_PAUSE contains the code to fork the test with the -i option.
472	 * You want to make sure you do this before you create your temporary
473	 * directory.
474	 */
475	TEST_PAUSE;
476
477	/* Create a unique temporary directory and chdir() to it. */
478	tst_tmpdir();
479
480	strcpy(name, DATA_FILE);
481	sprintf(f_name, "%s.%d", name, getpid());
482
483        bad_addr = mmap(0, 1, PROT_NONE,
484			MAP_PRIVATE_EXCEPT_UCLINUX|MAP_ANONYMOUS, 0, 0);
485        if (bad_addr == MAP_FAILED) {
486            printf("mmap failed\n");
487        }
488        wr_iovec[7].iov_base = bad_addr;
489
490}
491
492/*
493 * cleanup()
494 *	performs all ONE TIME cleanup for this test at
495 *	completion or premature exit
496 */
497void
498cleanup(void)
499{
500	/*
501	 * print timing stats if that option was specified.
502	 * print errno log if that option was specified.
503	 */
504	TEST_CLEANUP;
505
506	if (unlink(f_name) < 0) {
507		tst_resm(TFAIL, "unlink Failed--file = %s, errno = %d",
508			 f_name, errno);
509	}
510	tst_rmdir();
511
512	tst_exit();
513}
514
515int
516init_buffs(char *pbufs[])
517{
518	int i;
519
520	for (i = 0; pbufs[i] != (char *)NULL; i++) {
521		switch (i) {
522		case 0:
523
524		case 1:	fill_mem(pbufs[i], 0, 1);
525			break;
526
527		case 2:	fill_mem(pbufs[i], 1, 0);
528			break;
529
530		default: tst_resm(TFAIL, "Error detected: init_buffs()");
531			 cleanup();
532			 /*NOTREACHED*/
533		}
534	}
535	return(0);
536}
537
538int
539fill_mem(char *c_ptr, int c1, int c2)
540{
541	int count;
542
543	for (count = 1; count <= K_1 / CHUNK; count++) {
544		if (count & 0x01) {		/* if odd */
545			memset(c_ptr, c1, CHUNK);
546		} else {			/* if even */
547			memset(c_ptr, c2, CHUNK);
548		}
549	}
550	return(0);
551}
552
553void
554sighandler(int sig)
555{
556	switch (sig) {
557	case SIGTERM:	break;
558
559	case SIGPIPE:	++in_sighandler;
560			return;
561
562	default:	tst_resm(TFAIL, "sighandler() received invalid "
563				 "signal:%d", sig);
564			break;
565	}
566
567	if (unlink(f_name) < 0) {
568		tst_resm(TFAIL, "unlink Failed--file = %s, errno = %d",
569			 f_name, errno);
570		tst_exit();
571		/*NOTREACHED*/
572	}
573	exit(sig);
574}
575
576long
577l_seek(int fdesc, long offset, int whence)
578{
579	if (lseek(fdesc, offset, whence) < 0) {
580		perror("lseek");
581		tst_resm(TFAIL, "lseek Failed : errno = %d", errno);
582		fail = 1;
583	}
584	return(0);
585}
586