1 /*
2 * SPDX-License-Identifier: MIT
3 *
4 * Copyright (c) 2023, Rob Norris <[email protected]>
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 #define DATASIZE (4096)
40 char data[DATASIZE];
41
42 static int
_open_file(int n,int wr)43 _open_file(int n, int wr)
44 {
45 char buf[256];
46 int fd;
47
48 snprintf(buf, sizeof (buf), "testdata_%d_%d", getpid(), n);
49
50 if ((fd = open(buf, wr ? (O_WRONLY | O_CREAT) : O_RDONLY,
51 wr ? (S_IRUSR | S_IWUSR) : 0)) < 0) {
52 fprintf(stderr, "Error: open '%s' (%s): %s\n",
53 buf, wr ? "write" : "read", strerror(errno));
54 exit(1);
55 }
56
57 return (fd);
58 }
59
60 static void
_write_file(int n,int fd)61 _write_file(int n, int fd)
62 {
63 /* write a big ball of stuff */
64 ssize_t nwr = write(fd, data, DATASIZE);
65 if (nwr < 0) {
66 fprintf(stderr, "Error: write '%d_%d': %s\n",
67 getpid(), n, strerror(errno));
68 exit(1);
69 } else if (nwr < DATASIZE) {
70 fprintf(stderr, "Error: write '%d_%d': short write\n", getpid(),
71 n);
72 exit(1);
73 }
74 }
75
76 static int
_seek_file(int n,int fd)77 _seek_file(int n, int fd)
78 {
79 struct stat st;
80 if (fstat(fd, &st) < 0) {
81 fprintf(stderr, "Error: fstat '%d_%d': %s\n", getpid(), n,
82 strerror(errno));
83 exit(1);
84 }
85
86 /*
87 * A zero-sized file correctly has no data, so seeking the file is
88 * pointless.
89 */
90 if (st.st_size == 0)
91 return (0);
92
93 /* size is real, and we only write, so SEEK_DATA must find something */
94 if (lseek(fd, 0, SEEK_DATA) < 0) {
95 if (errno == ENXIO)
96 return (1);
97 fprintf(stderr, "Error: lseek '%d_%d': %s\n",
98 getpid(), n, strerror(errno));
99 exit(2);
100 }
101
102 return (0);
103 }
104
105 int
main(int argc,char ** argv)106 main(int argc, char **argv)
107 {
108 int nfiles = 0;
109 int nthreads = 0;
110
111 if (argc < 3 || (nfiles = atoi(argv[1])) == 0 ||
112 (nthreads = atoi(argv[2])) == 0) {
113 printf("usage: seekflood <nfiles> <threads>\n");
114 exit(1);
115 }
116
117 memset(data, 0x5a, DATASIZE);
118
119 /* fork off some flood threads */
120 for (int i = 0; i < nthreads; i++) {
121 if (!fork()) {
122 /* thread main */
123
124 /* create zero file */
125 int fd = _open_file(0, 1);
126 _write_file(0, fd);
127 close(fd);
128
129 int count = 0;
130
131 int h = 0, i, j, rfd, wfd;
132 for (i = 0; i < nfiles; i += 2, h++) {
133 j = i+1;
134
135 /* seek h, write i */
136 rfd = _open_file(h, 0);
137 wfd = _open_file(i, 1);
138 count += _seek_file(h, rfd);
139 _write_file(i, wfd);
140 close(rfd);
141 close(wfd);
142
143 /* seek i, write j */
144 rfd = _open_file(i, 0);
145 wfd = _open_file(j, 1);
146 count += _seek_file(i, rfd);
147 _write_file(j, wfd);
148 close(rfd);
149 close(wfd);
150 }
151
152 /* return count of failed seeks to parent */
153 exit(count < 256 ? count : 255);
154 }
155 }
156
157 /* wait for threads, take their seek fail counts from exit code */
158 int count = 0, crashed = 0;
159 for (int i = 0; i < nthreads; i++) {
160 int wstatus;
161 wait(&wstatus);
162 if (WIFEXITED(wstatus))
163 count += WEXITSTATUS(wstatus);
164 else
165 crashed++;
166 }
167
168 if (crashed) {
169 fprintf(stderr, "Error: child crashed; test failed\n");
170 exit(1);
171 }
172
173 if (count) {
174 fprintf(stderr, "Error: %d seek failures; test failed\n",
175 count);
176 exit(1);
177 }
178
179 exit(0);
180 }
181