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