+ if (ftruncate(fd, s->old_len) != 0) {
+ fwarn("failtest_write: truncate failed cleaning up %s for %s!",
+ opener->u.open.pathname, caller);
+ }
+
+ if (restore_offset) {
+ trace("Restoring offset of fd %i to %llu\n",
+ fd, (long long)s->off);
+ lseek(fd, s->off, SEEK_SET);
+ }
+}
+
+/* We save/restore most things on demand, but always do mmaped files. */
+static void save_mmapped_files(void)
+{
+ struct failtest_call *i;
+ trace("Saving mmapped files in child\n");
+
+ tlist_for_each_rev(&history, i, list) {
+ struct mmap_call *m = &i->u.mmap;
+ struct saved_mmapped_file *s;
+
+ if (i->type != FAILTEST_MMAP)
+ continue;
+
+ /* FIXME: We only handle mmapped files where fd is still open. */
+ if (m->opener->u.open.closed)
+ continue;
+
+ s = malloc(sizeof *s);
+ s->s = save_contents(m->opener->u.open.pathname,
+ m->fd, m->length, m->offset,
+ "mmapped file before fork");
+ s->opener = m->opener;
+ s->next = saved_mmapped_files;
+ saved_mmapped_files = s;
+ }
+}
+
+static void free_mmapped_files(bool restore)
+{
+ trace("%s mmapped files in child\n",
+ restore ? "Restoring" : "Discarding");
+ while (saved_mmapped_files) {
+ struct saved_mmapped_file *next = saved_mmapped_files->next;
+ if (restore)
+ restore_contents(saved_mmapped_files->opener,
+ saved_mmapped_files->s, false,
+ "saved mmap");
+ free(saved_mmapped_files->s);
+ free(saved_mmapped_files);
+ saved_mmapped_files = next;
+ }
+}
+
+/* Returns a FAILTEST_OPEN, FAILTEST_PIPE or NULL. */
+static struct failtest_call *opener_of(int fd)
+{
+ struct failtest_call *i;
+
+ /* Don't get confused and match genuinely failed opens. */
+ if (fd < 0)
+ return NULL;
+
+ /* Figure out the set of live fds. */
+ tlist_for_each_rev(&history, i, list) {
+ if (i->fail)
+ continue;
+ switch (i->type) {
+ case FAILTEST_CLOSE:
+ if (i->u.close.fd == fd) {
+ return NULL;