/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- choose_name
- say
- dorename
- domkdir
- dormdir
- cleanup_rmdir
- rename_proc
- mkdir_proc
- rmdir_proc
- dofork
- run
- setup
- recursive_cleanup
- cleanup
- main
1 /*
2 * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
3 * The President and Fellows of Harvard College.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 /*
31 * Concurrent directory operations test.
32 *
33 * Your system should survive this (without leaving a corrupted file
34 * system behind) once the file system assignment is complete.
35 */
36
37 #include <sys/types.h>
38 #include <sys/wait.h>
39 #include <sys/stat.h>
40 #include <errno.h>
41 #include <stdio.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <stdarg.h>
45 #include <string.h>
46
47 #define NTRIES 100 /* loop count */
48 #define NPROCS 5 /* actually totals 4x this +1 processes */
49
50 #define TESTDIR "dirconc"
51 #define NNAMES 4
52 #define NAMESIZE 32
53
54 ////////////////////////////////////////////////////////////
55
56 static const char *const names[NNAMES] = {
57 "aaaa",
58 "bbbb",
59 "cccc",
60 "dddd",
61 };
62
63 static
64 void
65 choose_name(char *buf, size_t len)
66 {
67 const char *a, *b, *c;
68
69 a = names[random()%NNAMES];
70 if (random()%2==0) {
71 snprintf(buf, len, "%s", a);
72 return;
73 }
74 b = names[random()%NNAMES];
75 if (random()%2==0) {
76 snprintf(buf, len, "%s/%s", a, b);
77 return;
78 }
79 c = names[random()%NNAMES];
80 snprintf(buf, len, "%s/%s/%s", a, b, c);
81 }
82
83 ////////////////////////////////////////////////////////////
84
85 /*
86 * The purpose of this is to be atomic. In our world, straight
87 * printf tends not to be.
88 */
89 static
90 void
91 #ifdef __GNUC__
92 __attribute__((__format__(__printf__, 1, 2)))
93 #endif
94 say(const char *fmt, ...)
95 {
96 char buf[512];
97 va_list ap;
98 va_start(ap, fmt);
99 vsnprintf(buf, sizeof(buf), fmt, ap);
100 va_end(ap);
101 write(STDOUT_FILENO, buf, strlen(buf));
102 }
103
104 ////////////////////////////////////////////////////////////
105
106 static
107 void
108 dorename(const char *name1, const char *name2)
109 {
110 if (rename(name1, name2) < 0) {
111 switch (errno) {
112 case ENOENT:
113 case ENOTEMPTY:
114 case EINVAL:
115 break;
116 default:
117 say("pid %d: rename %s -> %s: %s\n",
118 getpid(), name1, name2, strerror(errno));
119 break;
120 }
121 }
122 }
123
124 static
125 void
126 domkdir(const char *name)
127 {
128 if (mkdir(name, 0775)<0) {
129 switch (errno) {
130 case ENOENT:
131 case EEXIST:
132 break;
133 default:
134 say("pid %d: mkdir %s: %s\n",
135 getpid(), name, strerror(errno));
136 break;
137 }
138 }
139 }
140
141 static
142 void
143 dormdir(const char *name)
144 {
145 if (rmdir(name)<0) {
146 switch (errno) {
147 case ENOENT:
148 case ENOTEMPTY:
149 break;
150 default:
151 say("pid %d: rmdir %s: %s\n",
152 getpid(), name, strerror(errno));
153 break;
154 }
155 }
156 }
157
158 static
159 void
160 cleanup_rmdir(const char *name)
161 {
162 if (rmdir(name)<0) {
163 switch (errno) {
164 case ENOENT:
165 break;
166 default:
167 say("cleanup (pid %d): rmdir %s: %s\n",
168 getpid(), name, strerror(errno));
169 break;
170 }
171 }
172 }
173
174 ////////////////////////////////////////////////////////////
175
176 static
177 void
178 rename_proc(void)
179 {
180 char name1[NAMESIZE], name2[NAMESIZE];
181 int ct;
182
183 for (ct=0; ct<NTRIES; ct++) {
184 choose_name(name1, sizeof(name1));
185 choose_name(name2, sizeof(name2));
186 say("pid %2d: rename %s -> %s\n", (int)getpid(), name1, name2);
187 dorename(name1, name2);
188 }
189 }
190
191 static
192 void
193 mkdir_proc(void)
194 {
195 char name[NAMESIZE];
196 int ct;
197
198 for (ct=0; ct<NTRIES; ct++) {
199 choose_name(name, sizeof(name));
200 say("pid %2d: mkdir %s\n", (int)getpid(), name);
201 domkdir(name);
202 }
203 }
204
205 static
206 void
207 rmdir_proc(void)
208 {
209 char name[NAMESIZE];
210 int ct;
211
212 for (ct=0; ct<NTRIES; ct++) {
213 choose_name(name, sizeof(name));
214 say("pid %2d: rmdir %s\n", (int)getpid(), name);
215 dormdir(name);
216 }
217 }
218
219 ////////////////////////////////////////////////////////////
220
221 static
222 pid_t
223 dofork(void (*func)(void))
224 {
225 pid_t pid;
226
227 pid = fork();
228 if (pid < 0) {
229 say("fork: %s\n", strerror(errno));
230 return -1;
231 }
232 if (pid == 0) {
233 /* child */
234 func();
235 exit(0);
236 }
237 return pid;
238 }
239
240 static
241 void
242 run(void)
243 {
244 pid_t pids[NPROCS*4], wp;
245 int i, status;
246
247 for (i=0; i<NPROCS; i++) {
248 pids[i*4] = dofork(mkdir_proc);
249 pids[i*4+1] = dofork(mkdir_proc);
250 pids[i*4+2] = dofork(rename_proc);
251 pids[i*4+3] = dofork(rmdir_proc);
252 }
253
254 for (i=0; i<NPROCS*4; i++) {
255 if (pids[i]>=0) {
256 wp = waitpid(pids[i], &status, 0);
257 if (wp<0) {
258 say("waitpid %d: %s\n", (int) pids[i],
259 strerror(errno));
260 }
261 else if (WIFSIGNALED(status)) {
262 say("pid %d: signal %d\n", (int) pids[i],
263 WTERMSIG(status));
264 }
265 else if (WIFEXITED(status) && WEXITSTATUS(status)!=0) {
266 say("pid %d: exit %d\n", (int) pids[i],
267 WEXITSTATUS(status));
268 }
269 }
270 }
271 }
272
273 ////////////////////////////////////////////////////////////
274
275 static
276 void
277 setup(const char *fs)
278 {
279 if (chdir(fs)<0) {
280 say("chdir: %s: %s\n", fs, strerror(errno));
281 exit(1);
282 }
283 if (mkdir(TESTDIR, 0775)<0) {
284 say("mkdir: %s: %s\n", TESTDIR, strerror(errno));
285 exit(1);
286 }
287 if (chdir(TESTDIR)<0) {
288 say("chdir: %s: %s\n", TESTDIR, strerror(errno));
289 exit(1);
290 }
291 }
292
293 static
294 void
295 recursive_cleanup(const char *sofar, int depth)
296 {
297 char buf[NAMESIZE*32];
298 int i;
299
300 for (i=0; i<NNAMES; i++) {
301 snprintf(buf, sizeof(buf), "%s/%s", sofar, names[i]);
302 if (rmdir(buf)<0) {
303 if (errno==ENOTEMPTY) {
304 recursive_cleanup(buf, depth+1);
305 cleanup_rmdir(buf);
306 }
307 else if (errno!=ENOENT) {
308 say("cleanup (pid %d): rmdir %s: %s\n",
309 getpid(), buf, strerror(errno));
310 }
311 }
312 }
313 }
314
315 static
316 void
317 cleanup(void)
318 {
319 recursive_cleanup(".", 0);
320
321 chdir("..");
322 cleanup_rmdir(TESTDIR);
323 }
324
325 ////////////////////////////////////////////////////////////
326
327 int
328 main(int argc, char *argv[])
329 {
330 const char *fs;
331 long seed = 0;
332
333 say("Concurrent directory ops test\n");
334
335 if (argc==0 || argv==NULL) {
336 say("Warning: argc is 0 - assuming you mean to run on lhd1: "
337 "with seed 0\n");
338 fs = "lhd1:";
339 }
340 else if (argc==2) {
341 fs = argv[1];
342 }
343 else if (argc==3) {
344 fs = argv[1];
345 seed = atoi(argv[2]);
346 }
347 else {
348 say("Usage: dirconc filesystem [random-seed]\n");
349 exit(1);
350 }
351
352 srandom(seed);
353 setup(fs);
354 say("Starting in %s/%s\n", fs, TESTDIR);
355
356 run();
357
358 say("Cleaning up\n");
359 cleanup();
360
361 return 0;
362 }