]> git.ozlabs.org Git - ccan/blob - ccan/failtest/failtest.c
2b78b8e3f8bdc655aaa9234ffa1335fe73c6ac8a
[ccan] / ccan / failtest / failtest.c
1 #include <stdarg.h>
2 #include <string.h>
3 #include <stdio.h>
4 #include <stdarg.h>
5 #include <ctype.h>
6 #include <err.h>
7 #include <unistd.h>
8 #include <poll.h>
9 #include <errno.h>
10 #include <sys/types.h>
11 #include <sys/wait.h>
12 #include <sys/stat.h>
13 #include <assert.h>
14 #include <ccan/read_write_all/read_write_all.h>
15 #include <ccan/failtest/failtest_proto.h>
16 #include <ccan/failtest/failtest.h>
17 #include <ccan/build_assert/build_assert.h>
18
19 bool (*failtest_hook)(struct failtest_call *history, unsigned num)
20 = failtest_default_hook;
21
22 unsigned int failtest_timeout_ms = 20000;
23
24 const char *failpath;
25
26 enum info_type {
27         WRITE,
28         RELEASE_LOCKS,
29         FAILURE,
30         SUCCESS,
31         UNEXPECTED
32 };
33
34 struct write_info_hdr {
35         size_t len;
36         off_t offset;
37         int fd;
38 };
39
40 struct fd_orig {
41         int fd;
42         off_t offset;
43         size_t size;
44         bool dupped;
45 };
46
47 struct write_info {
48         struct write_info_hdr hdr;
49         char *data;
50         size_t oldlen;
51         char *olddata;
52 };
53
54 struct lock_info {
55         int fd;
56         /* end is inclusive: you can't have a 0-byte lock. */
57         off_t start, end;
58         int type;
59 };
60
61 bool (*failtest_exit_check)(struct failtest_call *history, unsigned num);
62
63 static struct failtest_call *history = NULL;
64 static unsigned int history_num = 0;
65 static int control_fd = -1;
66
67 static struct write_info *writes = NULL;
68 static unsigned int writes_num = 0;
69
70 static struct write_info *child_writes = NULL;
71 static unsigned int child_writes_num = 0;
72
73 static struct fd_orig *fd_orig = NULL;
74 static unsigned int fd_orig_num = 0;
75
76 static pid_t lock_owner;
77 static struct lock_info *locks = NULL;
78 static unsigned int lock_num = 0;
79
80 static const char info_to_arg[] = "mceoprwf";
81
82 /* Dummy call used for failtest_undo wrappers. */
83 static struct failtest_call unrecorded_call;
84
85 static struct failtest_call *add_history_(enum failtest_call_type type,
86                                           const char *file,
87                                           unsigned int line,
88                                           const void *elem,
89                                           size_t elem_size)
90 {
91         /* NULL file is how we suppress failure. */
92         if (!file)
93                 return &unrecorded_call;
94
95         history = realloc(history, (history_num + 1) * sizeof(*history));
96         history[history_num].type = type;
97         history[history_num].file = file;
98         history[history_num].line = line;
99         memcpy(&history[history_num].u, elem, elem_size);
100         return &history[history_num++];
101 }
102
103 #define add_history(type, file, line, elem) \
104         add_history_((type), (file), (line), (elem), sizeof(*(elem)))
105
106 static void save_fd_orig(int fd)
107 {
108         unsigned int i;
109
110         for (i = 0; i < fd_orig_num; i++)
111                 if (fd_orig[i].fd == fd)
112                         return;
113
114         fd_orig = realloc(fd_orig, (fd_orig_num + 1) * sizeof(*fd_orig));
115         fd_orig[fd_orig_num].fd = fd;
116         fd_orig[fd_orig_num].dupped = false;
117         fd_orig[fd_orig_num].offset = lseek(fd, 0, SEEK_CUR);
118         fd_orig[fd_orig_num].size = lseek(fd, 0, SEEK_END);
119         lseek(fd, fd_orig[fd_orig_num].offset, SEEK_SET);
120         fd_orig_num++;
121 }
122
123 bool failtest_default_hook(struct failtest_call *history, unsigned num)
124 {
125         return true;
126 }
127
128 static bool read_write_info(int fd)
129 {
130         struct write_info_hdr hdr;
131
132         if (!read_all(fd, &hdr, sizeof(hdr)))
133                 return false;
134
135         child_writes = realloc(child_writes,
136                                (child_writes_num+1) * sizeof(child_writes[0]));
137         child_writes[child_writes_num].hdr = hdr;
138         child_writes[child_writes_num].data = malloc(hdr.len);
139         if (!read_all(fd, child_writes[child_writes_num].data, hdr.len))
140                 return false;
141
142         child_writes_num++;
143         return true;
144 }
145
146 static void print_reproduce(void)
147 {
148         unsigned int i;
149
150         printf("To reproduce: --failpath=");
151         for (i = 0; i < history_num; i++) {
152                 if (history[i].fail)
153                         printf("%c", toupper(info_to_arg[history[i].type]));
154                 else
155                         printf("%c", info_to_arg[history[i].type]);
156         }
157         printf("\n");
158 }
159
160 static void tell_parent(enum info_type type)
161 {
162         if (control_fd != -1)
163                 write_all(control_fd, &type, sizeof(type));
164 }
165
166 static void child_fail(const char *out, size_t outlen, const char *fmt, ...)
167 {
168         va_list ap;
169
170         va_start(ap, fmt);
171         vfprintf(stderr, fmt, ap);
172         va_end(ap);
173
174         fprintf(stderr, "%.*s", (int)outlen, out);
175         print_reproduce();
176         tell_parent(FAILURE);
177         exit(1);
178 }
179
180 static pid_t child;
181
182 static void hand_down(int signal)
183 {
184         kill(child, signal);
185 }
186
187 static void release_locks(void)
188 {
189         /* Locks were never acquired/reacquired? */
190         if (lock_owner == 0)
191                 return;
192
193         /* We own them?  Release them all. */
194         if (lock_owner == getpid()) {
195                 unsigned int i;
196                 struct flock fl;
197                 fl.l_type = F_UNLCK;
198                 fl.l_whence = SEEK_SET;
199                 fl.l_start = 0;
200                 fl.l_len = 0;
201
202                 for (i = 0; i < lock_num; i++)
203                         fcntl(locks[i].fd, F_SETLK, &fl);
204         } else {
205                 /* Our parent must have them; pass request up. */
206                 enum info_type type = RELEASE_LOCKS;
207                 assert(control_fd != -1);
208                 write_all(control_fd, &type, sizeof(type));
209         }
210         lock_owner = 0;
211 }
212
213 /* off_t is a signed type.  Getting its max is non-trivial. */
214 static off_t off_max(void)
215 {
216         BUILD_ASSERT(sizeof(off_t) == 4 || sizeof(off_t) == 8);
217         if (sizeof(off_t) == 4)
218                 return (off_t)0x7FFFFFF;
219         else
220                 return (off_t)0x7FFFFFFFFFFFFFFULL;
221 }
222
223 static void get_locks(void)
224 {
225         unsigned int i;
226         struct flock fl;
227
228         if (lock_owner == getpid())
229                 return;
230
231         if (lock_owner != 0) {
232                 enum info_type type = RELEASE_LOCKS;
233                 assert(control_fd != -1);
234                 write_all(control_fd, &type, sizeof(type));
235         }
236
237         fl.l_whence = SEEK_SET;
238
239         for (i = 0; i < lock_num; i++) {
240                 fl.l_type = locks[i].type;
241                 fl.l_start = locks[i].start;
242                 if (locks[i].end == off_max())
243                         fl.l_len = 0;
244                 else
245                         fl.l_len = locks[i].end - locks[i].start + 1;
246
247                 if (fcntl(locks[i].fd, F_SETLKW, &fl) != 0)
248                         abort();
249         }
250         lock_owner = getpid();
251 }
252
253 static bool should_fail(struct failtest_call *call)
254 {
255         int status;
256         int control[2], output[2];
257         enum info_type type = UNEXPECTED;
258         char *out = NULL;
259         size_t outlen = 0;
260
261         if (call == &unrecorded_call)
262                 return false;
263
264         if (failpath) {
265                 if (tolower(*failpath) != info_to_arg[call->type])
266                         errx(1, "Failpath expected '%c' got '%c'\n",
267                              info_to_arg[call->type], *failpath);
268                 return isupper(*(failpath++));
269         }
270
271         if (!failtest_hook(history, history_num)) {
272                 call->fail = false;
273                 return false;
274         }
275
276         /* We're going to fail in the child. */
277         call->fail = true;
278         if (pipe(control) != 0 || pipe(output) != 0)
279                 err(1, "opening pipe");
280
281         /* Prevent double-printing (in child and parent) */
282         fflush(stdout);
283         child = fork();
284         if (child == -1)
285                 err(1, "forking failed");
286
287         if (child == 0) {
288                 close(control[0]);
289                 close(output[0]);
290                 dup2(output[1], STDOUT_FILENO);
291                 dup2(output[1], STDERR_FILENO);
292                 if (output[1] != STDOUT_FILENO && output[1] != STDERR_FILENO)
293                         close(output[1]);
294                 control_fd = control[1];
295                 return true;
296         }
297
298         signal(SIGUSR1, hand_down);
299
300         close(control[1]);
301         close(output[1]);
302
303         /* We grab output so we can display it; we grab writes so we
304          * can compare. */
305         do {
306                 struct pollfd pfd[2];
307                 int ret;
308
309                 pfd[0].fd = output[0];
310                 pfd[0].events = POLLIN|POLLHUP;
311                 pfd[1].fd = control[0];
312                 pfd[1].events = POLLIN|POLLHUP;
313
314                 if (type == SUCCESS)
315                         ret = poll(pfd, 1, failtest_timeout_ms);
316                 else
317                         ret = poll(pfd, 2, failtest_timeout_ms);
318
319                 if (ret <= 0)
320                         hand_down(SIGUSR1);
321
322                 if (pfd[0].revents & POLLIN) {
323                         ssize_t len;
324
325                         out = realloc(out, outlen + 8192);
326                         len = read(output[0], out + outlen, 8192);
327                         outlen += len;
328                 } else if (type != SUCCESS && (pfd[1].revents & POLLIN)) {
329                         if (read_all(control[0], &type, sizeof(type))) {
330                                 if (type == WRITE) {
331                                         if (!read_write_info(control[0]))
332                                                 break;
333                                 } else if (type == RELEASE_LOCKS) {
334                                         release_locks();
335                                         /* FIXME: Tell them we're done... */
336                                 }
337                         }
338                 } else if (pfd[0].revents & POLLHUP) {
339                         break;
340                 }
341         } while (type != FAILURE);
342
343         close(output[0]);
344         close(control[0]);
345         waitpid(child, &status, 0);
346         if (!WIFEXITED(status))
347                 child_fail(out, outlen, "Killed by signal %u: ",
348                            WTERMSIG(status));
349         /* Child printed failure already, just pass up exit code. */
350         if (type == FAILURE) {
351                 fprintf(stderr, "%.*s", (int)outlen, out);
352                 tell_parent(type);
353                 exit(WEXITSTATUS(status) ? WEXITSTATUS(status) : 1);
354         }
355         if (WEXITSTATUS(status) != 0)
356                 child_fail(out, outlen, "Exited with status %i: ",
357                            WEXITSTATUS(status));
358
359         free(out);
360         signal(SIGUSR1, SIG_DFL);
361
362         /* We continue onwards without failing. */
363         call->fail = false;
364         return false;
365 }
366
367 void *failtest_calloc(size_t nmemb, size_t size,
368                       const char *file, unsigned line)
369 {
370         struct failtest_call *p;
371         struct calloc_call call;
372         call.nmemb = nmemb;
373         call.size = size;
374         p = add_history(FAILTEST_CALLOC, file, line, &call);
375
376         if (should_fail(p)) {
377                 p->u.calloc.ret = NULL;
378                 p->error = ENOMEM;
379         } else {
380                 p->u.calloc.ret = calloc(nmemb, size);
381         }
382         errno = p->error;
383         return p->u.calloc.ret;
384 }
385
386 void *failtest_malloc(size_t size, const char *file, unsigned line)
387 {
388         struct failtest_call *p;
389         struct malloc_call call;
390         call.size = size;
391
392         p = add_history(FAILTEST_MALLOC, file, line, &call);
393         if (should_fail(p)) {
394                 p->u.calloc.ret = NULL;
395                 p->error = ENOMEM;
396         } else {
397                 p->u.calloc.ret = malloc(size);
398         }
399         errno = p->error;
400         return p->u.calloc.ret;
401 }
402
403 void *failtest_realloc(void *ptr, size_t size, const char *file, unsigned line)
404 {
405         struct failtest_call *p;
406         struct realloc_call call;
407         call.size = size;
408         p = add_history(FAILTEST_REALLOC, file, line, &call);
409
410         /* FIXME: Try one child moving allocation, one not. */
411         if (should_fail(p)) {
412                 p->u.realloc.ret = NULL;
413                 p->error = ENOMEM;
414         } else {
415                 p->u.realloc.ret = realloc(ptr, size);
416         }
417         errno = p->error;
418         return p->u.realloc.ret;
419 }
420
421 int failtest_open(const char *pathname, int flags,
422                   const char *file, unsigned line, ...)
423 {
424         struct failtest_call *p;
425         struct open_call call;
426
427         call.pathname = strdup(pathname);
428         call.flags = flags;
429         if (flags & O_CREAT) {
430                 va_list ap;
431                 va_start(ap, line);
432                 call.mode = va_arg(ap, mode_t);
433                 va_end(ap);
434         }
435         p = add_history(FAILTEST_OPEN, file, line, &call);
436         /* Avoid memory leak! */
437         if (p == &unrecorded_call)
438                 free((char *)call.pathname);
439         if (should_fail(p)) {
440                 p->u.open.ret = -1;
441                 /* FIXME: Play with error codes? */
442                 p->error = EACCES;
443         } else {
444                 p->u.open.ret = open(pathname, flags, call.mode);
445         }
446         errno = p->error;
447         return p->u.open.ret;
448 }
449
450 int failtest_pipe(int pipefd[2], const char *file, unsigned line)
451 {
452         struct failtest_call *p;
453         struct pipe_call call;
454
455         p = add_history(FAILTEST_PIPE, file, line, &call);
456         if (should_fail(p)) {
457                 p->u.open.ret = -1;
458                 /* FIXME: Play with error codes? */
459                 p->error = EMFILE;
460         } else {
461                 p->u.pipe.ret = pipe(p->u.pipe.fds);
462         }
463         /* This causes valgrind to notice if they use pipefd[] after failure */
464         memcpy(pipefd, p->u.pipe.fds, sizeof(p->u.pipe.fds));
465         errno = p->error;
466         return p->u.pipe.ret;
467 }
468
469 ssize_t failtest_read(int fd, void *buf, size_t count,
470                       const char *file, unsigned line)
471 {
472         struct failtest_call *p;
473         struct read_call call;
474         call.fd = fd;
475         call.buf = buf;
476         call.count = count;
477         p = add_history(FAILTEST_READ, file, line, &call);
478
479         /* This is going to change seek offset, so save it. */
480         if (control_fd != -1)
481                 save_fd_orig(fd);
482
483         /* FIXME: Try partial read returns. */
484         if (should_fail(p)) {
485                 p->u.read.ret = -1;
486                 p->error = EIO;
487         } else {
488                 p->u.read.ret = read(fd, buf, count);
489         }
490         errno = p->error;
491         return p->u.read.ret;
492 }
493
494 static struct write_info *new_write(void)
495 {
496         writes = realloc(writes, (writes_num + 1) * sizeof(*writes));
497         return &writes[writes_num++];
498 }
499
500 ssize_t failtest_write(int fd, const void *buf, size_t count,
501                        const char *file, unsigned line)
502 {
503         struct failtest_call *p;
504         struct write_call call;
505         off_t offset;
506
507         call.fd = fd;
508         call.buf = buf;
509         call.count = count;
510         p = add_history(FAILTEST_WRITE, file, line, &call);
511
512         offset = lseek(fd, 0, SEEK_CUR);
513
514         /* If we're a child, save contents and tell parent about write. */
515         if (control_fd != -1) {
516                 struct write_info *winfo = new_write();
517                 enum info_type type = WRITE;
518
519                 save_fd_orig(fd);
520
521                 winfo->hdr.len = count;
522                 winfo->hdr.fd = fd;
523                 winfo->data = malloc(count);
524                 memcpy(winfo->data, buf, count);
525                 winfo->hdr.offset = offset;
526                 if (winfo->hdr.offset != (off_t)-1) {
527                         lseek(fd, offset, SEEK_SET);
528                         winfo->olddata = malloc(count);
529                         winfo->oldlen = read(fd, winfo->olddata, count);
530                         if (winfo->oldlen == -1)
531                                 winfo->oldlen = 0;
532                 }
533                 write_all(control_fd, &type, sizeof(type));
534                 write_all(control_fd, &winfo->hdr, sizeof(winfo->hdr));
535                 write_all(control_fd, winfo->data, count);
536         }
537
538         /* FIXME: Try partial write returns. */
539         if (should_fail(p)) {
540                 p->u.write.ret = -1;
541                 p->error = EIO;
542         } else {
543                 /* FIXME: We assume same write order in parent and child */
544                 if (child_writes_num != 0) {
545                         if (child_writes[0].hdr.fd != fd)
546                                 errx(1, "Child wrote to fd %u, not %u?",
547                                      child_writes[0].hdr.fd, fd);
548                         if (child_writes[0].hdr.offset != offset)
549                                 errx(1, "Child wrote to offset %zu, not %zu?",
550                                      (size_t)child_writes[0].hdr.offset,
551                                      (size_t)offset);
552                         if (child_writes[0].hdr.len != count)
553                                 errx(1, "Child wrote length %zu, not %zu?",
554                                      child_writes[0].hdr.len, count);
555                         if (memcmp(child_writes[0].data, buf, count)) {
556                                 child_fail(NULL, 0,
557                                            "Child wrote differently to"
558                                            " fd %u than we did!\n", fd);
559                         }
560                         free(child_writes[0].data);
561                         child_writes_num--;
562                         memmove(&child_writes[0], &child_writes[1],
563                                 sizeof(child_writes[0]) * child_writes_num);
564
565                         /* Is this is a socket or pipe, child wrote it
566                            already. */
567                         if (offset == (off_t)-1) {
568                                 p->u.write.ret = count;
569                                 errno = p->error;
570                                 return p->u.write.ret;
571                         }
572                 }
573                 p->u.write.ret = write(fd, buf, count);
574         }
575         errno = p->error;
576         return p->u.write.ret;
577 }
578
579 static struct lock_info *WARN_UNUSED_RESULT
580 add_lock(struct lock_info *locks, int fd, off_t start, off_t end, int type)
581 {
582         unsigned int i;
583         struct lock_info *l;
584
585         for (i = 0; i < lock_num; i++) {
586                 l = &locks[i];
587
588                 if (l->fd != fd)
589                         continue;
590                 /* Four cases we care about:
591                  * Start overlap:
592                  *      l =    |      |
593                  *      new = |   |
594                  * Mid overlap:
595                  *      l =    |      |
596                  *      new =    |  |
597                  * End overlap:
598                  *      l =    |      |
599                  *      new =      |    |
600                  * Total overlap:
601                  *      l =    |      |
602                  *      new = |         |
603                  */
604                 if (start > l->start && end < l->end) {
605                         /* Mid overlap: trim entry, add new one. */
606                         off_t new_start, new_end;
607                         new_start = end + 1;
608                         new_end = l->end;
609                         l->end = start - 1;
610                         locks = add_lock(locks,
611                                          fd, new_start, new_end, l->type);
612                         l = &locks[i];
613                 } else if (start <= l->start && end >= l->end) {
614                         /* Total overlap: eliminate entry. */
615                         l->end = 0;
616                         l->start = 1;
617                 } else if (end >= l->start && end < l->end) {
618                         /* Start overlap: trim entry. */
619                         l->start = end + 1;
620                 } else if (start > l->start && start <= l->end) {
621                         /* End overlap: trim entry. */
622                         l->end = start-1;
623                 }
624                 /* Nothing left?  Remove it. */
625                 if (l->end < l->start) {
626                         memmove(l, l + 1, (--lock_num - i) * sizeof(l[0]));
627                         i--;
628                 }
629         }
630
631         if (type != F_UNLCK) {
632                 locks = realloc(locks, (lock_num + 1) * sizeof(*locks));
633                 l = &locks[lock_num++];
634                 l->fd = fd;
635                 l->start = start;
636                 l->end = end;
637                 l->type = type;
638         }
639         return locks;
640 }
641
642 /* We only trap this so we can dup fds in case we need to restore. */
643 int failtest_close(int fd)
644 {
645         unsigned int i;
646         int newfd = -1;
647
648         for (i = 0; i < fd_orig_num; i++) {
649                 if (fd_orig[i].fd == fd) {
650                         fd_orig[i].fd = newfd = dup(fd);
651                         fd_orig[i].dupped = true;
652                 }
653         }
654
655         for (i = 0; i < writes_num; i++) {
656                 if (writes[i].hdr.fd == fd)
657                         writes[i].hdr.fd = newfd;
658         }
659
660         locks = add_lock(locks, fd, 0, off_max(), F_UNLCK);
661         return close(fd);
662 }
663
664 /* Zero length means "to end of file" */
665 static off_t end_of(off_t start, off_t len)
666 {
667         if (len == 0)
668                 return off_max();
669         return start + len - 1;
670 }
671
672 /* FIXME: This only handles locks, really. */
673 int failtest_fcntl(int fd, const char *file, unsigned line, int cmd, ...)
674 {
675         struct failtest_call *p;
676         struct fcntl_call call;
677         va_list ap;
678
679         call.fd = fd;
680         call.cmd = cmd;
681
682         /* Argument extraction. */
683         switch (cmd) {
684         case F_SETFL:
685         case F_SETFD:
686                 va_start(ap, cmd);
687                 call.arg.l = va_arg(ap, long);
688                 va_end(ap);
689                 return fcntl(fd, cmd, call.arg.l);
690         case F_GETFD:
691         case F_GETFL:
692                 return fcntl(fd, cmd);
693         case F_GETLK:
694                 get_locks();
695                 va_start(ap, cmd);
696                 call.arg.fl = *va_arg(ap, struct flock *);
697                 va_end(ap);
698                 return fcntl(fd, cmd, &call.arg.fl);
699         case F_SETLK:
700         case F_SETLKW:
701                 va_start(ap, cmd);
702                 call.arg.fl = *va_arg(ap, struct flock *);
703                 va_end(ap);
704                 break;
705         default:
706                 /* This means you need to implement it here. */
707                 err(1, "failtest: unknown fcntl %u", cmd);
708         }
709
710         p = add_history(FAILTEST_FCNTL, file, line, &call);
711         get_locks();
712
713         if (should_fail(p)) {
714                 p->u.fcntl.ret = -1;
715                 if (p->u.fcntl.cmd == F_SETLK)
716                         p->error = EAGAIN;
717                 else
718                         p->error = EDEADLK;
719         } else {
720                 p->u.fcntl.ret = fcntl(p->u.fcntl.fd, p->u.fcntl.cmd,
721                                        &p->u.fcntl.arg.fl);
722                 if (p->u.fcntl.ret == -1)
723                         p->error = errno;
724                 else {
725                         /* We don't handle anything else yet. */
726                         assert(p->u.fcntl.arg.fl.l_whence == SEEK_SET);
727                         locks = add_lock(locks,
728                                          p->u.fcntl.fd,
729                                          p->u.fcntl.arg.fl.l_start,
730                                          end_of(p->u.fcntl.arg.fl.l_start,
731                                                 p->u.fcntl.arg.fl.l_len),
732                                          p->u.fcntl.arg.fl.l_type);
733                 }
734         }
735         errno = p->error;
736         return p->u.fcntl.ret;
737 }
738
739 void failtest_init(int argc, char *argv[])
740 {
741         if (argc == 2
742             && strncmp(argv[1], "--failpath=", strlen("--failpath=")) == 0) {
743                 failpath = argv[1] + strlen("--failpath=");
744         }
745 }
746
747 /* Free up memory, so valgrind doesn't report leaks. */
748 static void free_everything(void)
749 {
750         unsigned int i;
751
752         for (i = 0; i < writes_num; i++) {
753                 free(writes[i].data);
754                 if (writes[i].hdr.offset != (off_t)-1)
755                         free(writes[i].olddata);
756         }
757         free(writes);
758         free(fd_orig);
759         for (i = 0; i < history_num; i++) {
760                 if (history[i].type == FAILTEST_OPEN)
761                         free((char *)history[i].u.open.pathname);
762         }
763         free(history);
764 }
765
766 void failtest_exit(int status)
767 {
768         unsigned int i;
769
770         if (control_fd == -1) {
771                 free_everything();
772                 exit(status);
773         }
774
775         if (failtest_exit_check) {
776                 if (!failtest_exit_check(history, history_num))
777                         child_fail(NULL, 0, "failtest_exit_check failed\n");
778         }
779
780         /* Restore any stuff we overwrote. */
781         for (i = 0; i < writes_num; i++) {
782                 if (writes[i].hdr.offset == (off_t)-1)
783                         continue;
784                 if (writes[i].oldlen != 0) {
785                         lseek(writes[i].hdr.fd, writes[i].hdr.offset,
786                               SEEK_SET);
787                         write(writes[i].hdr.fd, writes[i].olddata,
788                               writes[i].oldlen);
789                 }
790         }
791
792         /* Fix up fd offsets, restore sizes. */
793         for (i = 0; i < fd_orig_num; i++) {
794                 lseek(fd_orig[i].fd, fd_orig[i].offset, SEEK_SET);
795                 ftruncate(fd_orig[i].fd, fd_orig[i].size);
796                 /* Free up any file descriptors we dup'ed. */
797                 if (fd_orig[i].dupped)
798                         close(fd_orig[i].fd);
799         }
800
801         free_everything();
802         tell_parent(SUCCESS);
803         exit(0);
804 }