1 /*
2  *
3  *   Copyright (c) International Business Machines  Corp., 2002
4  *   Copyright (c) Cyril Hrubis chrubis@suse.cz 2009
5  *
6  *   This program is free software;  you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
14  *   the GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program;  if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /*
22  * NAME
23  *	ftest07.c -- test file I/O with readv and writev (ported from SPIE,
24  *		    section2/filesuite/ftest9.c
25  *
26  * 	this is the same as ftest4, except that it uses lseek64
27  *
28  * CALLS
29  *	lseek64, readv, writev,
30  *	truncate, ftruncate, fsync, sync, fstat
31  *
32  * ALGORITHM
33  *	A bitmap is used to map pieces of a file.
34  *      Loop: pick a random piece of the file
35  *            if we haven't seen it before make sure it is zero,
36  *            write pattern
37  *            if we have seen it before make sure correct pattern.
38  *
39  *      This was originally written by rbk - was program tfio.c
40  *	Modified by dale to integrate with test suites.
41  *	Modified by G. Stevens to use readv and writev.
42  *	Modofied by K. Hakim to integrate with SPIES.
43  *
44  * RESTRICTIONS
45  *  1.  Runs a long time with default args - can take others on input
46  *	line.  Use with "term mode".
47  *	If run on vax the ftruncate will not be random - will always go to
48  *	start of file.  NOTE: produces a very high load average!!
49  *
50  *  2.  The "csize" argument must be evenly divisible by MAXIOVCNT.
51  *
52  * CAUTION!!
53  *	If a file is supplied to this program with the "-f" option
54  *	it will be removed with a system("rm -rf filename") call.
55  *
56  *
57  */
58 
59 #define _XOPEN_SOURCE 500
60 #define _LARGEFILE64_SOURCE 1
61 #include <sys/types.h>
62 #include <sys/param.h>
63 #include <sys/wait.h>
64 #include <sys/stat.h>
65 #include <errno.h>
66 #include <sys/uio.h>
67 #include <fcntl.h>
68 #include <signal.h>
69 #include <stdio.h>
70 #include <unistd.h>
71 #include <inttypes.h>
72 #include "test.h"
73 #include "libftest.h"
74 
75 char *TCID = "ftest07";
76 int TST_TOTAL = 1;
77 
78 #define PASSED 1
79 #define FAILED 0
80 
81 #define MAXCHILD	25
82 #define K_1		1024
83 #define K_2		2048
84 #define K_4		4096
85 #define	MAXIOVCNT	16
86 
87 static void setup(void);
88 static void runtest(void);
89 static void dotest(int, int, int);
90 static void domisc(int, int, char *);
91 static void term(int sig);
92 
93 static int csize;		/* chunk size */
94 static int iterations;		/* # total iterations */
95 static off64_t max_size;	/* max file size */
96 static int misc_intvl;		/* for doing misc things; 0 ==> no */
97 static int nchild;		/* how many children */
98 static int fd;			/* file descriptor used by child */
99 static int parent_pid;
100 static int pidlist[MAXCHILD];
101 static char test_name[2];	/* childs test directory name */
102 
103 static char fuss[MAXPATHLEN];	/* directory to do this in */
104 static char homedir[MAXPATHLEN];	/* where we started */
105 
106 static int local_flag;
107 
main(int ac,char * av[])108 int main(int ac, char *av[])
109 {
110 	int lc;
111 
112 	tst_parse_opts(ac, av, NULL, NULL);
113 
114 	setup();
115 
116 	for (lc = 0; TEST_LOOPING(lc); lc++) {
117 
118 		local_flag = PASSED;
119 
120 		runtest();
121 
122 		if (local_flag == PASSED)
123 			tst_resm(TPASS, "Test passed.");
124 		else
125 			tst_resm(TFAIL, "Test failed.");
126 
127 		tst_rmdir();
128 		tst_exit();
129 
130 	}
131 
132 	tst_exit();
133 }
134 
setup(void)135 static void setup(void)
136 {
137 	char wdbuf[MAXPATHLEN], *cwd;
138 
139 	/*
140 	 * Make a directory to do this in; ignore error if already exists.
141 	 * Save starting directory.
142 	 */
143 	if ((cwd = getcwd(homedir, sizeof(homedir))) == NULL) {
144 		tst_brkm(TBROK, NULL, "Failed to get corrent directory");
145 	}
146 
147 	parent_pid = getpid();
148 	tst_tmpdir();
149 	if (!fuss[0])
150 		sprintf(fuss, "%s/ftest07.%d", getcwd(wdbuf, sizeof(wdbuf)),
151 			getpid());
152 
153 	mkdir(fuss, 0755);
154 
155 	if (chdir(fuss) < 0) {
156 		tst_brkm(TBROK, NULL, "\tCan't chdir(%s), error %d.", fuss,
157 			 errno);
158 	}
159 
160 	/*
161 	 * Default values for run conditions.
162 	 */
163 
164 	iterations = 10;
165 	nchild = 5;
166 	csize = K_2;		/* should run with 1, 2, and 4 K sizes */
167 	max_size = K_1 * K_1;
168 	misc_intvl = 10;
169 
170 	if (sigset(SIGTERM, term) == SIG_ERR) {
171 		tst_brkm(TBROK | TERRNO, NULL, "sigset (signo=SIGTERM) failed");
172 	}
173 
174 }
175 
runtest(void)176 static void runtest(void)
177 {
178 	pid_t pid;
179 	int child, count, i, nwait, status;
180 
181 	nwait = 0;
182 
183 	for (i = 0; i < nchild; i++) {
184 		test_name[0] = 'a' + i;
185 		test_name[1] = '\0';
186 		fd = open(test_name, O_RDWR | O_CREAT | O_TRUNC, 0666);
187 		if (fd < 0) {
188 			tst_brkm(TBROK, NULL, "\tError %d creating %s/%s.",
189 				 errno,
190 				 fuss, test_name);
191 		}
192 
193 		if ((child = fork()) == 0) {
194 			dotest(nchild, i, fd);
195 			tst_exit();
196 		}
197 
198 		close(fd);
199 
200 		if (child < 0) {
201 			tst_brkm(TBROK | TERRNO, NULL, "fork failed");
202 		} else {
203 			pidlist[i] = child;
204 			nwait++;
205 		}
206 	}
207 
208 	/*
209 	 * Wait for children to finish.
210 	 */
211 	count = 0;
212 	while (1) {
213 		if ((child = wait(&status)) >= 0) {
214 			//tst_resm(TINFO, "\tTest{%d} exited status = 0x%x", child, status);
215 			if (status) {
216 				tst_resm(TFAIL,
217 					 "\tTest{%d} failed, expected 0 exit.",
218 					 child);
219 				local_flag = FAILED;
220 			}
221 			++count;
222 		} else {
223 			if (errno != EINTR)
224 				break;
225 		}
226 	}
227 
228 	/*
229 	 * Should have collected all children.
230 	 */
231 
232 	if (count != nwait) {
233 		tst_resm(TFAIL, "\tWrong # children waited on, count = %d",
234 			 count);
235 		local_flag = FAILED;
236 	}
237 
238 	chdir(homedir);
239 
240 	pid = fork();
241 	if (pid < 0) {
242 		tst_brkm(TBROK | TERRNO, NULL, "fork failed");
243 	}
244 
245 	if (pid == 0) {
246 		execl("/bin/rm", "rm", "-rf", fuss, NULL);
247 		exit(1);
248 	} else
249 		wait(&status);
250 	if (status) {
251 		tst_resm(TINFO, "CAUTION - ftest07, '%s' may not be removed",
252 			 fuss);
253 	}
254 
255 	sync();
256 }
257 
258 /*
259  * dotest()
260  *	Children execute this.
261  *
262  * Randomly read/mod/write chunks with known pattern and check.
263  * When fill sectors, iterate.
264  */
265 
266 #define	NMISC	4
267 enum m_type { m_fsync, m_trunc, m_sync, m_fstat };
268 char *m_str[] = { "fsync", "trunc", "sync", "fstat" };
269 
270 int misc_cnt[NMISC];			/* counts # of each kind of misc */
271 long long unsigned int file_max;	/* file-max size */
272 int nchunks;
273 int last_trunc = -1;
274 int tr_flag;
275 enum m_type type = m_fsync;
276 
CHUNK(off64_t i)277 static inline long long unsigned int CHUNK(off64_t i)
278 {
279 	return (long long unsigned int) i * csize;
280 }
281 #define	NEXTMISC	((rand() % misc_intvl) + 5)
282 
dotest(int testers,int me,int fd)283 static void dotest(int testers, int me, int fd)
284 {
285 	char *bits, *hold_bits;
286 	char val;
287 	int count, collide, chunk, whenmisc, xfr, i;
288 
289 	/* Stuff for the readv call */
290 	struct iovec r_iovec[MAXIOVCNT];
291 	int r_ioveclen;
292 
293 	/* Stuff for the writev call */
294 	struct iovec val_iovec[MAXIOVCNT];
295 
296 	struct iovec zero_iovec[MAXIOVCNT];
297 	int w_ioveclen;
298 	struct stat stat;
299 
300 	nchunks = max_size / csize;
301 	whenmisc = 0;
302 
303 	if ((bits = malloc((nchunks + 7) / 8)) == NULL) {
304 		tst_brkm(TBROK, NULL, "\tmalloc failed(bits)");
305 	}
306 	if ((hold_bits = malloc((nchunks + 7) / 8)) == NULL) {
307 		tst_brkm(TBROK, NULL, "\tmalloc failed(hlod_bits)");
308 	}
309 
310 	/*Allocate memory for the iovec buffers and init the iovec arrays
311 	 */
312 	r_ioveclen = w_ioveclen = csize / MAXIOVCNT;
313 
314 	/* Please note that the above statement implies that csize
315 	 * be evenly divisible by MAXIOVCNT.
316 	 */
317 
318 	for (i = 0; i < MAXIOVCNT; i++) {
319 		if ((r_iovec[i].iov_base = calloc(r_ioveclen, 1)) == NULL) {
320 			tst_brkm(TFAIL, NULL,
321 				 "\tmalloc failed(r_iovec[i].iov_base)");
322 		}
323 		r_iovec[i].iov_len = r_ioveclen;
324 
325 		/* Allocate unused memory areas between all the buffers to
326 		 * make things more diffult for the OS.
327 		 */
328 
329 		if (malloc((i + 1) * 8) == NULL) {
330 			tst_brkm(TBROK, NULL, "\tmalloc failed((i+1)*8)");
331 		}
332 		if ((val_iovec[i].iov_base = calloc(w_ioveclen, 1)) == NULL) {
333 			tst_resm(TBROK, "\tmalloc failed(val_iovec[i]");
334 			exit(1);
335 		}
336 		val_iovec[i].iov_len = w_ioveclen;
337 
338 		if (malloc((i + 1) * 8) == NULL) {
339 			tst_brkm(TBROK, NULL, "\tmalloc failed((i+1)*8)");
340 		}
341 		if ((zero_iovec[i].iov_base = calloc(w_ioveclen, 1)) == NULL) {
342 			tst_brkm(TBROK, NULL, "\tmalloc failed(zero_iover)");
343 		}
344 		zero_iovec[i].iov_len = w_ioveclen;
345 
346 		if (malloc((i + 1) * 8) == NULL) {
347 			tst_brkm(TBROK, NULL, "\tmalloc failed((i+1)*8)");
348 		}
349 	}
350 	/*
351 	 * No init sectors; allow file to be sparse.
352 	 */
353 	val = (64 / testers) * me + 1;
354 
355 	/*
356 	 * For each iteration:
357 	 *      zap bits array
358 	 *      loop
359 	 *              pick random chunk, read it.
360 	 *              if corresponding bit off {
361 	 *                      verify = 0. (sparse file)
362 	 *                      ++count;
363 	 *              } else
364 	 *                      verify = val.
365 	 *              write "val" on it.
366 	 *              repeat unitl count = nchunks.
367 	 *      ++val.
368 	 */
369 
370 	srand(getpid());
371 	if (misc_intvl)
372 		whenmisc = NEXTMISC;
373 
374 	while (iterations-- > 0) {
375 		for (i = 0; i < NMISC; i++)
376 			misc_cnt[i] = 0;
377 		ftruncate(fd, 0);
378 		file_max = 0;
379 		memset(bits, 0, (nchunks + 7) / 8);
380 		memset(hold_bits, 0, (nchunks + 7) / 8);
381 
382 		/* Have to fill the val and zero iov buffers in a different manner
383 		 */
384 		for (i = 0; i < MAXIOVCNT; i++) {
385 			memset(val_iovec[i].iov_base, val,
386 			       val_iovec[i].iov_len);
387 			memset(zero_iovec[i].iov_base, 0,
388 			       zero_iovec[i].iov_len);
389 
390 		}
391 		count = 0;
392 		collide = 0;
393 		while (count < nchunks) {
394 			chunk = rand() % nchunks;
395 			/*
396 			 * Read it.
397 			 */
398 			if (lseek64(fd, CHUNK(chunk), 0) < 0) {
399 				tst_brkm(TFAIL,
400 					 NULL,
401 					 "\tTest[%d]: lseek64(0) fail at %Lx, errno = %d.",
402 					 me, CHUNK(chunk), errno);
403 			}
404 			if ((xfr = readv(fd, &r_iovec[0], MAXIOVCNT)) < 0) {
405 				tst_brkm(TFAIL,
406 					 NULL,
407 					 "\tTest[%d]: readv fail at %Lx, errno = %d.",
408 					 me, CHUNK(chunk), errno);
409 			}
410 			/*
411 			 * If chunk beyond EOF just write on it.
412 			 * Else if bit off, haven't seen it yet.
413 			 * Else, have.  Verify values.
414 			 */
415 			if (CHUNK(chunk) >= file_max) {
416 				bits[chunk / 8] |= (1 << (chunk % 8));
417 				++count;
418 			} else if ((bits[chunk / 8] & (1 << (chunk % 8))) == 0) {
419 				if (xfr != csize) {
420 					tst_brkm(TFAIL,
421 						 NULL,
422 						 "\tTest[%d]: xfr=%d != %d, zero read.",
423 						 me, xfr, csize);
424 				}
425 				for (i = 0; i < MAXIOVCNT; i++) {
426 					if (memcmp
427 					    (r_iovec[i].iov_base,
428 					     zero_iovec[i].iov_base,
429 					     r_iovec[i].iov_len)) {
430 						tst_resm(TFAIL,
431 							 "\tTest[%d] bad verify @ 0x%Lx for val %d count %d xfr %d file_max 0x%llx, should be 0.",
432 							 me, CHUNK(chunk), val,
433 							 count, xfr, file_max);
434 						tst_resm(TINFO,
435 							 "\tTest[%d]: last_trunc = 0x%x",
436 							 me, last_trunc);
437 						fstat(fd, &stat);
438 						tst_resm(TINFO,
439 							 "\tStat: size=%llx, ino=%x",
440 							 stat.st_size, (unsigned)stat.st_ino);
441 						sync();
442 						ft_dumpiov(&r_iovec[i]);
443 						ft_dumpbits(bits,
444 							    (nchunks + 7) / 8);
445 						ft_orbits(hold_bits, bits,
446 							  (nchunks + 7) / 8);
447 						tst_resm(TINFO, "\tHold ");
448 						ft_dumpbits(hold_bits,
449 							    (nchunks + 7) / 8);
450 						tst_exit();
451 					}
452 				}
453 				bits[chunk / 8] |= (1 << (chunk % 8));
454 				++count;
455 			} else {
456 				if (xfr != csize) {
457 					tst_brkm(TFAIL,
458 						 NULL,
459 						 "\tTest[%d]: xfr=%d != %d, val read.",
460 						 me, xfr, csize);
461 				}
462 				++collide;
463 				for (i = 0; i < MAXIOVCNT; i++) {
464 					if (memcmp
465 					    (r_iovec[i].iov_base,
466 					     val_iovec[i].iov_base,
467 					     r_iovec[i].iov_len)) {
468 						tst_resm(TFAIL,
469 							 "\tTest[%d] bad verify @ 0x%Lx for val %d count %d xfr %d file_max 0x%llx.",
470 							 me, CHUNK(chunk), val,
471 							 count, xfr, file_max);
472 						tst_resm(TINFO,
473 							 "\tTest[%d]: last_trunc = 0x%x",
474 							 me, last_trunc);
475 						fstat(fd, &stat);
476 						tst_resm(TINFO,
477 							 "\tStat: size=%llx, ino=%x",
478 							 stat.st_size, (unsigned)stat.st_ino);
479 						sync();
480 						ft_dumpiov(&r_iovec[i]);
481 						ft_dumpbits(bits,
482 							    (nchunks + 7) / 8);
483 						ft_orbits(hold_bits, bits,
484 							  (nchunks + 7) / 8);
485 						tst_resm(TINFO, "\tHold ");
486 						ft_dumpbits(hold_bits,
487 							    (nchunks + 7) / 8);
488 						tst_exit();
489 					}
490 				}
491 			}
492 			/*
493 			 * Writev it.
494 			 */
495 			if (lseek64(fd, -((off64_t) xfr), 1) < 0) {
496 				tst_brkm(TFAIL,
497 					 NULL,
498 					 "\tTest[%d]: lseek64(1) fail at %Lx, errno = %d.",
499 					 me, CHUNK(chunk), errno);
500 			}
501 			if ((xfr =
502 			     writev(fd, &val_iovec[0], MAXIOVCNT)) < csize) {
503 				if (errno == ENOSPC) {
504 					tst_resm(TFAIL,
505 						 "\tTest[%d]: no space, exiting.",
506 						 me);
507 					fsync(fd);
508 					tst_exit();
509 				}
510 				tst_brkm(TFAIL,
511 					 NULL,
512 					 "\tTest[%d]: writev fail at %Lx xfr %d, errno = %d.",
513 					 me, CHUNK(chunk), xfr, errno);
514 			}
515 			if (CHUNK(chunk) + csize > file_max)
516 				file_max = CHUNK(chunk) + csize;
517 			/*
518 			 * If hit "misc" interval, do it.
519 			 */
520 			if (misc_intvl && --whenmisc <= 0) {
521 				ft_orbits(hold_bits, bits, (nchunks + 7) / 8);
522 				domisc(me, fd, bits);
523 				whenmisc = NEXTMISC;
524 			}
525 			if (count + collide > 2 * nchunks)
526 				break;
527 		}
528 
529 		/*
530 		 * End of iteration, maybe before doing all chunks.
531 		 */
532 		fsync(fd);
533 		++misc_cnt[m_fsync];
534 		//tst_resm(TINFO, "\tTest{%d} val %d done, count = %d, collide = {%d}",
535 		//              me, val, count, collide);
536 		//for (i = 0; i < NMISC; i++)
537 		//      tst_resm(TINFO, "\t\tTest{%d}: {%d} %s's.", me, misc_cnt[i], m_str[i]);
538 		++val;
539 	}
540 }
541 
542 /*
543  * domisc()
544  *	Inject misc syscalls into the thing.
545  */
domisc(int me,int fd,char * bits)546 static void domisc(int me, int fd, char *bits)
547 {
548 	int chunk;
549 	struct stat sb;
550 
551 	if (type > m_fstat)
552 		type = m_fsync;
553 	switch (type) {
554 	case m_fsync:
555 		if (fsync(fd) < 0) {
556 			tst_brkm(TFAIL, NULL, "\tTest[%d]: fsync error %d.",
557 				 me,
558 				 errno);
559 		}
560 		break;
561 	case m_trunc:
562 		chunk = rand() % (file_max / csize);
563 		file_max = CHUNK(chunk);
564 		last_trunc = file_max;
565 		if (tr_flag) {
566 			if (ftruncate(fd, file_max) < 0) {
567 				tst_brkm(TFAIL,
568 					 NULL,
569 					 "\tTest[%d]: ftruncate error %d @ 0x%llx.",
570 					 me, errno, file_max);
571 			}
572 			tr_flag = 0;
573 		} else {
574 			if (truncate(test_name, file_max) < 0) {
575 				tst_brkm(TFAIL,
576 					 NULL,
577 					 "\tTest[%d]: truncate error %d @ 0x%llx.",
578 					 me, errno, file_max);
579 			}
580 			tr_flag = 1;
581 		}
582 		for (; chunk % 8 != 0; chunk++)
583 			bits[chunk / 8] &= ~(1 << (chunk % 8));
584 		for (; chunk < nchunks; chunk += 8)
585 			bits[chunk / 8] = 0;
586 		break;
587 	case m_sync:
588 		sync();
589 		break;
590 	case m_fstat:
591 		if (fstat(fd, &sb) < 0) {
592 			tst_brkm(TFAIL, NULL, "\tTest[%d]: fstat() error %d.",
593 				 me,
594 				 errno);
595 		}
596 		if (sb.st_size != file_max) {
597 			tst_brkm(TFAIL,
598 				 NULL, "\tTest[%d]: fstat() mismatch; st_size=%"
599 				 PRIx64 ",file_max=%llx.", me,
600 				 (int64_t) sb.st_size, file_max);
601 		}
602 		break;
603 	}
604 
605 	++misc_cnt[type];
606 	++type;
607 }
608 
609 /* term()
610  *
611  *	This is called when a SIGTERM signal arrives.
612  */
term(int sig LTP_ATTRIBUTE_UNUSED)613 static void term(int sig LTP_ATTRIBUTE_UNUSED)
614 {
615 	int i;
616 
617 	tst_resm(TINFO, "\tterm -[%d]- got sig term.", getpid());
618 
619 	/*
620 	 * If run by hand we like to have the parent send the signal to
621 	 * the child processes.  This makes life easy.
622 	 */
623 	if (parent_pid == getpid()) {
624 		for (i = 0; i < nchild; i++)
625 			if (pidlist[i])
626 				kill(pidlist[i], SIGTERM);
627 		return;
628 	}
629 
630 	tst_resm(TINFO, "\tunlinking '%s'", test_name);
631 
632 	close(fd);
633 	if (unlink(test_name))
634 		tst_resm(TBROK, "Unlink of '%s' failed, errno = %d.",
635 			 test_name, errno);
636 	else
637 		tst_resm(TINFO, "Unlink of '%s' successful.", test_name);
638 
639 	tst_exit();
640 }
641