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 #define DATASIZE (4096) 40 char data[DATASIZE]; 41 42 static int 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 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 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 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