writev01.c revision 2c28215423293e443469a07ae7011135d058b671
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, NULL, NULL)) !=
142	    NULL) {
143		tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
144	 }
145
146	/* set "tstdir", and "testfile" vars */
147	setup();
148
149	/* The following loop checks looping state if -i option given */
150	for (lc = 0; TEST_LOOPING(lc); lc++) {
151
152		/* reset Tst_count in case we are looping */
153		Tst_count = 0;
154
155		buf_list[0] = buf1;
156		buf_list[1] = buf2;
157		buf_list[2] = buf3;
158		buf_list[3] = NULL;
159
160		fd[1] = -1;	/* Invalid file descriptor  */
161
162		if (signal(SIGTERM, sighandler) == SIG_ERR) {
163			perror("signal: SIGTERM");
164			cleanup();
165		 }
166
167		if (signal(SIGPIPE, sighandler) == SIG_ERR) {
168			perror("signal: SIGPIPE");
169			cleanup();
170		 }
171
172		init_buffs(buf_list);
173
174		if ((fd[0] = open(f_name, O_WRONLY | O_CREAT, 0666)) < 0) {
175			tst_resm(TFAIL, "open failed: fname = %s, errno = %d",
176				 f_name, errno);
177			cleanup();
178		 } else
179		    if ((nbytes = write(fd[0], buf_list[2], K_1)) != K_1) {
180			tst_resm(TFAIL,
181				 "write failed: nbytes = %d, " "errno = %d",
182				 nbytes, errno);
183			cleanup();
184		 }
185
186		if (close(fd[0]) < 0) {
187			tst_resm(TFAIL, "close failed: errno: %d", errno);
188			cleanup();
189		 }
190
191		if ((fd[0] = open(f_name, O_RDWR, 0666)) < 0) {
192			tst_resm(TFAIL, "open failed: fname = %s, errno = %d",
193				 f_name, errno);
194			cleanup();
195		 }
196//block1: /* given vector length -1, writev() return EINVAL. */
197		tst_resm(TINFO, "Enter Block 1");
198		fail = 0;
199
200		TEST(writev(fd[0], wr_iovec, 1));
201		if (TEST_RETURN < 0) {
202			TEST_ERROR_LOG(TEST_ERRNO);
203			if (TEST_ERRNO == EINVAL) {
204				tst_resm(TINFO, "Received EINVAL as expected");
205			} else {
206				tst_resm(TFAIL, "Expected errno = EINVAL, "
207					 "got %d", TEST_ERRNO);
208				fail = 1;
209			}
210		} else {
211			tst_resm(TFAIL, "writev() failed to fail");
212			fail = 1;
213		}
214		if (fail) {
215			tst_resm(TINFO, "block 1 FAILED");
216		} else {
217			tst_resm(TINFO, "block 1 PASSED");
218		}
219		tst_resm(TINFO, "Exit block 1");
220
221//block2:
222		/* This testcases doesn't look like what it intent to do
223		 * 1. it is not using the wr_iovec initialized
224		 * 2. read() and following message is not consistent
225		 */
226		tst_resm(TINFO, "Enter block 2");
227		fail = 0;
228
229		if (l_seek(fd[0], CHUNK * 6, 0) < 0) {
230			TEST_ERROR_LOG(errno);
231			tst_resm(TBROK, "block2: 1st lseek failed");
232			fail = 1;
233		}
234
235		if ((ret = writev(fd[0], (wr_iovec + 6), 3)) == CHUNK) {
236			if (l_seek(fd[0], CHUNK * 6, 0) < 0) {
237				TEST_ERROR_LOG(errno);
238				tst_resm(TFAIL, "block2: 2nd lseek failed");
239				fail = 1;
240			}
241			if ((nbytes = read(fd[0], buf_list[0], CHUNK)) != CHUNK) {
242				perror("read error");
243				tst_resm(TFAIL, "expected nbytes = 1024, "
244					 "got = %d", nbytes);
245				fail = 1;
246			} else if (memcmp((buf_list[0] + CHUNK * 6),
247					  (buf_list[2] + CHUNK * 6),
248					  CHUNK) != 0) {
249				tst_resm(TFAIL, "Error: writev() over "
250					 "wrote %s", f_name);
251				fail = 1;
252			}
253		} else {
254			tst_resm(TFAIL, "writev() failed unexpectedly");
255			fail = 1;
256		}
257		if (fail) {
258			tst_resm(TINFO, "block 2 FAILED");
259		} else {
260			tst_resm(TINFO, "block 2 PASSED");
261		}
262		tst_resm(TINFO, "Exit block 2");
263
264//block3: /* given 1 bad vector buffer with good ones, writev() success */
265		tst_resm(TINFO, "Enter block 3");
266		fail = 0;
267
268		if (lseek(fd[0], CHUNK * 6, 0) < 0) {
269			TEST_ERROR_LOG(errno);
270			tst_resm(TFAIL, "block3: 1st lseek failed");
271			fail = 1;
272		}
273		if ((nbytes = writev(fd[0], (wr_iovec + 6), 3)) < 0) {
274			TEST_ERROR_LOG(errno);
275			if (errno == EFAULT) {
276				tst_resm(TFAIL, "Got EFAULT");
277				fail = 1;
278			}
279		}
280		if (l_seek(fd[0], 0, 0) < 0) {
281			TEST_ERROR_LOG(errno);
282			tst_resm(TFAIL, "block3: 2nd lseek failed");
283			fail = 1;
284		}
285		if ((nbytes = read(fd[0], buf_list[0], K_1)) != K_1) {
286			perror("read error");
287			tst_resm(TFAIL, "expected nbytes = 1024, got = %d",
288				 nbytes);
289			fail = 1;
290		} else if (memcmp((buf_list[0] + CHUNK * 6),
291				  (buf_list[2] + CHUNK * 6), CHUNK * 3) != 0) {
292			tst_resm(TFAIL, "Error: writev() over wrote %s",
293				 f_name);
294			fail = 1;
295		}
296
297		if (fail) {
298			tst_resm(TINFO, "block 3 FAILED");
299		} else {
300			tst_resm(TINFO, "block 3 PASSED");
301		}
302		tst_resm(TINFO, "Exit block 3");
303
304//block4: /* given bad file discriptor, writev() return EBADF. */
305		tst_resm(TINFO, "Enter block 4");
306		fail = 0;
307
308		TEST(writev(fd[1], (wr_iovec + 9), 1));
309		if (TEST_RETURN < 0) {
310			TEST_ERROR_LOG(TEST_ERRNO);
311			if (TEST_ERRNO == EBADF) {
312				tst_resm(TINFO, "Received EBADF as expected");
313			} else {
314				tst_resm(TFAIL, "expected errno = EBADF, "
315					 "got %d", TEST_ERRNO);
316				fail = 1;
317			}
318		} else {
319			tst_resm(TFAIL, "Error: writev() returned a "
320				 "positive value");
321			fail = 1;
322		}
323
324		if (fail) {
325			tst_resm(TINFO, "block 4 FAILED");
326		} else {
327			tst_resm(TINFO, "block 4 PASSED");
328		}
329		tst_resm(TINFO, "Exit block 4");
330
331//block5: /* given invalid vector count, writev() return EINVAL */
332		tst_resm(TINFO, "Enter block 5");
333		fail = 0;
334
335		TEST(writev(fd[0], (wr_iovec + 10), -1));
336		if (TEST_RETURN < 0) {
337			TEST_ERROR_LOG(TEST_ERRNO);
338			if (TEST_ERRNO == EINVAL) {
339				tst_resm(TINFO, "Received EINVAL as expected");
340			} else {
341				tst_resm(TFAIL, "expected errno = EINVAL, "
342					 "got %d", TEST_ERRNO);
343				fail = 1;
344			}
345		} else {
346			tst_resm(TFAIL, "Error: writev() returned a "
347				 "positive value");
348			fail = 1;
349		}
350
351		if (fail) {
352			tst_resm(TINFO, "block 5 FAILED");
353		} else {
354			tst_resm(TINFO, "block 5 PASSED");
355		}
356		tst_resm(TINFO, "Exit block 5");
357
358//block6: /* given no buffer vector, writev() success */
359		tst_resm(TINFO, "Enter block 6");
360		fail = 0;
361
362		TEST(writev(fd[0], (wr_iovec + 11), 0));
363		if (TEST_RETURN < 0) {
364			TEST_ERROR_LOG(TEST_ERRNO);
365			tst_resm(TFAIL, "writev() failed with unexpected errno "
366				 "%d", TEST_ERRNO);
367			fail = 1;
368		} else {
369			tst_resm(TPASS, "writev() wrote 0 iovectors");
370		}
371
372		if (fail) {
373			tst_resm(TINFO, "block 6 FAILED");
374		} else {
375			tst_resm(TINFO, "block 6 PASSED");
376		}
377		tst_resm(TINFO, "Exit block 6");
378
379//block7:
380		/* given 4 vectors, 2 are NULL, 1 with 0 length and 1 with fixed length,
381		 * writev() success writing fixed length.
382		 */
383		tst_resm(TINFO, "Enter block 7");
384		fail = 0;
385
386		l_seek(fd[0], CHUNK * 12, 0);
387		if ((ret = writev(fd[0], (wr_iovec + 12), 4)) != CHUNK) {
388			tst_resm(TFAIL, "writev() failed writing %d bytes, "
389				 "followed by two NULL vectors", CHUNK);
390			fail = 1;
391		} else {
392			tst_resm(TPASS, "writev passed writing %d bytes, "
393				 "followed by two NULL vectors", CHUNK);
394		}
395
396		if (fail) {
397			tst_resm(TINFO, "block 7 FAILED");
398		} else {
399			tst_resm(TINFO, "block 7 PASSED");
400		}
401		tst_resm(TINFO, "Exit block 7");
402
403//block8: /* try to write to a closed pipe, writev() return EPIPE. */
404		tst_resm(TINFO, "Enter block 8");
405		fail = 0;
406
407		if (pipe(pfd) < 0) {
408			TEST_ERROR_LOG(errno);
409			perror("pipe");
410			tst_resm(TFAIL, "pipe failed: errno = %d", errno);
411			fail = 1;
412		} else {
413			if (close(pfd[0]) < 0) {
414				TEST_ERROR_LOG(errno);
415				perror("close");
416				tst_resm(TFAIL, "close failed: errno = %d",
417					 errno);
418				fail = 1;
419			} else if ((writev(pfd[1], (wr_iovec + 12), 1)
420				    < 0) && in_sighandler) {
421				TEST_ERROR_LOG(errno);
422				if (errno == EPIPE) {
423					tst_resm(TINFO, "Received EPIPE as "
424						 "expected");
425				} else {
426					tst_resm(TFAIL, "expected errno = "
427						 "EPIPE, got %d", errno);
428					fail = 1;
429				}
430			} else {
431				tst_resm(TFAIL, "Error: writev() returned a "
432					 "positive value");
433				fail = 1;
434			}
435		}
436		if (fail) {
437			tst_resm(TINFO, "block 8 FAILED");
438		} else {
439			tst_resm(TINFO, "block 8 PASSED");
440		}
441		tst_resm(TINFO, "Exit block 8");
442	}
443	close(fd[0]);
444	close(fd[1]);
445	cleanup();
446	  return 0;
447}
448
449/*
450 * setup()
451 *	performs all ONE TIME setup for this test
452 */
453void setup(void)
454{
455
456	tst_sig(FORK, DEF_HANDLER, cleanup);
457
458	/* Set up the expected error numbers for -e option */
459	TEST_EXP_ENOS(exp_enos);
460
461	/* Pause if that option was specified.
462	 * TEST_PAUSE contains the code to fork the test with the -i option.
463	 * You want to make sure you do this before you create your temporary
464	 * directory.
465	 */
466	TEST_PAUSE;
467
468	/* Create a unique temporary directory and chdir() to it. */
469	tst_tmpdir();
470
471	strcpy(name, DATA_FILE);
472	sprintf(f_name, "%s.%d", name, getpid());
473
474	bad_addr = mmap(0, 1, PROT_NONE,
475			MAP_PRIVATE_EXCEPT_UCLINUX | MAP_ANONYMOUS, 0, 0);
476	if (bad_addr == MAP_FAILED) {
477		printf("mmap failed\n");
478	}
479	wr_iovec[7].iov_base = bad_addr;
480
481}
482
483/*
484 * cleanup()
485 *	performs all ONE TIME cleanup for this test at
486 *	completion or premature exit
487 */
488void cleanup(void)
489{
490	/*
491	 * print timing stats if that option was specified.
492	 * print errno log if that option was specified.
493	 */
494	TEST_CLEANUP;
495
496	if (unlink(f_name) < 0) {
497		tst_resm(TFAIL, "unlink Failed--file = %s, errno = %d",
498			 f_name, errno);
499	}
500	tst_rmdir();
501
502}
503
504int init_buffs(char *pbufs[])
505{
506	int i;
507
508	for (i = 0; pbufs[i] != NULL; i++) {
509		switch (i) {
510		case 0:
511
512		case 1:
513			fill_mem(pbufs[i], 0, 1);
514			break;
515
516		case 2:
517			fill_mem(pbufs[i], 1, 0);
518			break;
519
520		default:
521			tst_resm(TFAIL, "Error detected: init_buffs()");
522			cleanup();
523		 }
524	}
525	return 0;
526}
527
528int fill_mem(char *c_ptr, int c1, int c2)
529{
530	int count;
531
532	for (count = 1; count <= K_1 / CHUNK; count++) {
533		if (count & 0x01) {	/* if odd */
534			memset(c_ptr, c1, CHUNK);
535		} else {	/* if even */
536			memset(c_ptr, c2, CHUNK);
537		}
538	}
539	return 0;
540}
541
542void sighandler(int sig)
543{
544	switch (sig) {
545	case SIGTERM:
546		break;
547
548	case SIGPIPE:
549		++in_sighandler;
550		return;
551
552	default:
553		tst_resm(TFAIL, "sighandler() received invalid "
554			 "signal:%d", sig);
555		break;
556	}
557
558	if (unlink(f_name) < 0) {
559		tst_resm(TFAIL, "unlink Failed--file = %s, errno = %d",
560			 f_name, errno);
561		tst_exit();
562	 }
563	exit(sig);
564}
565
566long l_seek(int fdesc, long offset, int whence)
567{
568	if (lseek(fdesc, offset, whence) < 0) {
569		perror("lseek");
570		tst_resm(TFAIL, "lseek Failed : errno = %d", errno);
571		fail = 1;
572	}
573	return 0;
574}