+static void hand_down(int signum)
+{
+ kill(child, signum);
+}
+
+static void release_locks(void)
+{
+ /* Locks were never acquired/reacquired? */
+ if (lock_owner == 0)
+ return;
+
+ /* We own them? Release them all. */
+ if (lock_owner == getpid()) {
+ unsigned int i;
+ struct flock fl;
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+
+ trace("Releasing %u locks\n", lock_num);
+ for (i = 0; i < lock_num; i++)
+ fcntl(locks[i].fd, F_SETLK, &fl);
+ } else {
+ /* Our parent must have them; pass request up. */
+ enum info_type type = RELEASE_LOCKS;
+ assert(control_fd != -1);
+ write_all(control_fd, &type, sizeof(type));
+ }
+ lock_owner = 0;
+}
+
+/* off_t is a signed type. Getting its max is non-trivial. */
+static off_t off_max(void)
+{
+ BUILD_ASSERT(sizeof(off_t) == 4 || sizeof(off_t) == 8);
+ if (sizeof(off_t) == 4)
+ return (off_t)0x7FFFFFF;
+ else
+ return (off_t)0x7FFFFFFFFFFFFFFULL;
+}
+
+static void get_locks(void)
+{
+ unsigned int i;
+ struct flock fl;
+
+ if (lock_owner == getpid())
+ return;
+
+ if (lock_owner != 0) {
+ enum info_type type = RELEASE_LOCKS;
+ assert(control_fd != -1);
+ trace("Asking parent to release locks\n");
+ write_all(control_fd, &type, sizeof(type));
+ }
+
+ fl.l_whence = SEEK_SET;
+
+ for (i = 0; i < lock_num; i++) {
+ fl.l_type = locks[i].type;
+ fl.l_start = locks[i].start;
+ if (locks[i].end == off_max())
+ fl.l_len = 0;
+ else
+ fl.l_len = locks[i].end - locks[i].start + 1;
+
+ if (fcntl(locks[i].fd, F_SETLKW, &fl) != 0)
+ abort();
+ }
+ trace("Acquired %u locks\n", lock_num);
+ lock_owner = getpid();
+}
+
+
+static struct contents_saved *save_contents(const char *filename,
+ int fd, size_t count, off_t off,
+ const char *why)
+{
+ struct contents_saved *s = malloc(sizeof(*s) + count);
+ ssize_t ret;
+
+ s->off = off;
+
+ ret = pread(fd, s->contents, count, off);
+ if (ret < 0) {
+ fwarn("failtest_write: failed to save old contents!");
+ s->count = 0;
+ } else
+ s->count = ret;
+
+ /* Use lseek to get the size of file, but we have to restore
+ * file offset */
+ off = lseek(fd, 0, SEEK_CUR);
+ s->old_len = lseek(fd, 0, SEEK_END);
+ lseek(fd, off, SEEK_SET);
+
+ trace("Saving %p %s %zu@%llu after %s (filelength %llu) via fd %i\n",
+ s, filename, s->count, (long long)s->off, why,
+ (long long)s->old_len, fd);
+ return s;
+}
+
+static void restore_contents(struct failtest_call *opener,
+ struct contents_saved *s,
+ bool restore_offset,
+ const char *caller)
+{
+ int fd;
+
+ /* The top parent doesn't need to restore. */
+ if (control_fd == -1)
+ return;
+
+ /* Has the fd been closed? */
+ if (opener->u.open.closed) {
+ /* Reopen, replace fd, close silently as we clean up. */
+ fd = open(opener->u.open.pathname, O_RDWR);
+ if (fd < 0) {
+ fwarn("failtest: could not reopen %s to clean up %s!",
+ opener->u.open.pathname, caller);
+ return;
+ }
+ /* Make it clearly distinguisable from a "normal" fd. */
+ fd = move_fd_to_high(fd);
+ trace("Reopening %s to restore it (was fd %i, now %i)\n",
+ opener->u.open.pathname, opener->u.open.ret, fd);
+ opener->u.open.ret = fd;
+ opener->u.open.closed = false;
+ }
+ fd = opener->u.open.ret;
+
+ trace("Restoring %p %s %zu@%llu after %s (filelength %llu) via fd %i\n",
+ s, opener->u.open.pathname, s->count, (long long)s->off, caller,
+ (long long)s->old_len, fd);
+ if (pwrite(fd, s->contents, s->count, s->off) != s->count) {
+ fwarn("failtest: write failed cleaning up %s for %s!",
+ opener->u.open.pathname, caller);
+ }
+
+ 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)