xref: /freebsd/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cp_files/seekflood.c (revision ce4dcb97ca433b2a2f03fbae957dae0ff16f6f51)
1 /*
2  * SPDX-License-Identifier: MIT
3  *
4  * Copyright (c) 2023, Rob Norris <robn@despairlabs.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22  * IN THE SOFTWARE.
23  *
24  */
25 
26 #ifndef	_GNU_SOURCE
27 #define	_GNU_SOURCE
28 #endif
29 
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <sys/stat.h>
37 #include <sys/wait.h>
38 
39 /* some older uClibc's lack the defines, so we'll manually define them */
40 #ifdef	__UCLIBC__
41 #ifndef	SEEK_DATA
42 #define	SEEK_DATA 3
43 #endif
44 #endif
45 
46 #define	DATASIZE	(4096)
47 char data[DATASIZE];
48 
49 static int
_open_file(int n,int wr)50 _open_file(int n, int wr)
51 {
52 	char buf[256];
53 	int fd;
54 
55 	snprintf(buf, sizeof (buf), "testdata_%d_%d", getpid(), n);
56 
57 	if ((fd = open(buf, wr ? (O_WRONLY | O_CREAT) : O_RDONLY,
58 	    wr ? (S_IRUSR | S_IWUSR) : 0)) < 0) {
59 		fprintf(stderr, "Error: open '%s' (%s): %s\n",
60 		    buf, wr ? "write" : "read", strerror(errno));
61 		exit(1);
62 	}
63 
64 	return (fd);
65 }
66 
67 static void
_write_file(int n,int fd)68 _write_file(int n, int fd)
69 {
70 	/* write a big ball of stuff */
71 	ssize_t nwr = write(fd, data, DATASIZE);
72 	if (nwr < 0) {
73 		fprintf(stderr, "Error: write '%d_%d': %s\n",
74 		    getpid(), n, strerror(errno));
75 		exit(1);
76 	} else if (nwr < DATASIZE) {
77 		fprintf(stderr, "Error: write '%d_%d': short write\n", getpid(),
78 		    n);
79 		exit(1);
80 	}
81 }
82 
83 static int
_seek_file(int n,int fd)84 _seek_file(int n, int fd)
85 {
86 	struct stat st;
87 	if (fstat(fd, &st) < 0) {
88 		fprintf(stderr, "Error: fstat '%d_%d': %s\n", getpid(), n,
89 		    strerror(errno));
90 		exit(1);
91 	}
92 
93 	/*
94 	 * A zero-sized file correctly has no data, so seeking the file is
95 	 * pointless.
96 	 */
97 	if (st.st_size == 0)
98 		return (0);
99 
100 	/* size is real, and we only write, so SEEK_DATA must find something */
101 	if (lseek(fd, 0, SEEK_DATA) < 0) {
102 		if (errno == ENXIO)
103 			return (1);
104 		fprintf(stderr, "Error: lseek '%d_%d': %s\n",
105 		    getpid(), n, strerror(errno));
106 		exit(2);
107 	}
108 
109 	return (0);
110 }
111 
112 int
main(int argc,char ** argv)113 main(int argc, char **argv)
114 {
115 	int nfiles = 0;
116 	int nthreads = 0;
117 
118 	if (argc < 3 || (nfiles = atoi(argv[1])) == 0 ||
119 	    (nthreads = atoi(argv[2])) == 0) {
120 		printf("usage: seekflood <nfiles> <threads>\n");
121 		exit(1);
122 	}
123 
124 	memset(data, 0x5a, DATASIZE);
125 
126 	/* fork off some flood threads */
127 	for (int i = 0; i < nthreads; i++) {
128 		if (!fork()) {
129 			/* thread main */
130 
131 			/* create zero file */
132 			int fd = _open_file(0, 1);
133 			_write_file(0, fd);
134 			close(fd);
135 
136 			int count = 0;
137 
138 			int h = 0, i, j, rfd, wfd;
139 			for (i = 0; i < nfiles; i += 2, h++) {
140 				j = i+1;
141 
142 				/* seek h, write i */
143 				rfd = _open_file(h, 0);
144 				wfd = _open_file(i, 1);
145 				count += _seek_file(h, rfd);
146 				_write_file(i, wfd);
147 				close(rfd);
148 				close(wfd);
149 
150 				/* seek i, write j */
151 				rfd = _open_file(i, 0);
152 				wfd = _open_file(j, 1);
153 				count += _seek_file(i, rfd);
154 				_write_file(j, wfd);
155 				close(rfd);
156 				close(wfd);
157 			}
158 
159 			/* return count of failed seeks to parent */
160 			exit(count < 256 ? count : 255);
161 		}
162 	}
163 
164 	/* wait for threads, take their seek fail counts from exit code */
165 	int count = 0, crashed = 0;
166 	for (int i = 0; i < nthreads; i++) {
167 		int wstatus;
168 		wait(&wstatus);
169 		if (WIFEXITED(wstatus))
170 			count += WEXITSTATUS(wstatus);
171 		else
172 			crashed++;
173 	}
174 
175 	if (crashed) {
176 		fprintf(stderr, "Error: child crashed; test failed\n");
177 		exit(1);
178 	}
179 
180 	if (count) {
181 		fprintf(stderr, "Error: %d seek failures; test failed\n",
182 		    count);
183 		exit(1);
184 	}
185 
186 	exit(0);
187 }
188