OSDN Git Service

whitespace elimination
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / fork.cc
index 785aabd..97364e3 100644 (file)
@@ -1,7 +1,7 @@
 /* fork.cc
 
-   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2004, 2005
-   Red Hat, Inc.
+   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006,
+   2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
 
 This file is part of Cygwin.
 
@@ -13,9 +13,7 @@ details. */
 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
-#include <stdarg.h>
 #include "cygerrno.h"
-#include "security.h"
 #include "path.h"
 #include "fhandler.h"
 #include "dtable.h"
@@ -24,12 +22,10 @@ details. */
 #include "cygheap.h"
 #include "child_info.h"
 #include "cygtls.h"
-#include "perprocess.h"
+#include "tls_pbuf.h"
 #include "dll_init.h"
-#include "sync.h"
-#include "shared_info.h"
 #include "cygmalloc.h"
-#include "cygthread.h"
+#include "ntdll.h"
 
 #define NPIDS_HELD 4
 
@@ -37,87 +33,103 @@ details. */
 /* FIXME: Once things stabilize, bump up to a few minutes.  */
 #define FORK_WAIT_TIMEOUT (300 * 1000)     /* 300 seconds */
 
-#define dll_data_start &_data_start__
-#define dll_data_end &_data_end__
-#define dll_bss_start &_bss_start__
-#define dll_bss_end &_bss_end__
-
-static void
-stack_base (child_info_fork &ch)
+class frok
 {
-  MEMORY_BASIC_INFORMATION m;
-  memset (&m, 0, sizeof m);
-  if (!VirtualQuery ((LPCVOID) &m, &m, sizeof m))
-    system_printf ("couldn't get memory info, %E");
-
-  ch.stacktop = m.AllocationBase;
-  ch.stackbottom = (LPBYTE) m.BaseAddress + m.RegionSize;
-  ch.stacksize = (DWORD) ch.stackbottom - (DWORD) &m;
-  debug_printf ("bottom %p, top %p, stack %p, size %d, reserve %d",
-               ch.stackbottom, ch.stacktop, &m, ch.stacksize,
-               (DWORD) ch.stackbottom - (DWORD) ch.stacktop);
-}
+  bool load_dlls;
+  child_info_fork ch;
+  const char *errmsg;
+  int child_pid;
+  int this_errno;
+  HANDLE hchild;
+  int __stdcall parent (volatile char * volatile here);
+  int __stdcall child (volatile char * volatile here);
+  bool error (const char *fmt, ...);
+  friend int fork ();
+};
+
+class lock_signals
+{
+  bool worked;
+public:
+  lock_signals ()
+  {
+    worked = sig_send (NULL, __SIGHOLD) == 0;
+  }
+  operator int () const
+  {
+    return worked;
+  }
+  void dont_bother ()
+  {
+    worked = false;
+  }
+  ~lock_signals ()
+  {
+    if (worked)
+      sig_send (NULL, __SIGNOHOLD);
+  }
+};
 
-/* Copy memory from parent to child.
-   The result is a boolean indicating success.  */
+class lock_pthread
+{
+  bool bother;
+public:
+  lock_pthread (): bother (1)
+  {
+    pthread::atforkprepare ();
+  }
+  void dont_bother ()
+  {
+    bother = false;
+  }
+  ~lock_pthread ()
+  {
+    if (bother)
+      pthread::atforkparent ();
+  }
+};
 
-static int
-fork_copy (PROCESS_INFORMATION &pi, const char *what, ...)
+class hold_everything
 {
-  va_list args;
-  char *low;
-  int pass = 0;
+public: /* DELETEME*/
+  bool& ischild;
+  /* Note the order of the locks below.  It is important,
+     to avoid races, that the lock order be preserved.
 
-  va_start (args, what);
+     pthread is first because it serves as a master lock
+     against other forks being attempted while this one is active.
 
-  while ((low = va_arg (args, char *)))
-    {
-      char *high = va_arg (args, char *);
-      DWORD todo = wincap.chunksize () ?: high - low;
-      char *here;
+     signals is next to stop signal processing for the duration
+     of the fork.
 
-      for (here = low; here < high; here += todo)
-       {
-         DWORD done = 0;
-         if (here + todo > high)
-           todo = high - here;
-         int res;
-         if (pi.hThread)
-           res = WriteProcessMemory (pi.hProcess, here, here, todo, &done);
-         else
-           res = ReadProcessMemory (pi.hProcess, here, here, todo, &done);
-         debug_printf ("child handle %p, low %p, high %p, res %d", pi.hProcess,
-                       low, high, res);
-         if (!res || todo != done)
-           {
-             if (!res)
-               __seterrno ();
-             /* If this happens then there is a bug in our fork
-                implementation somewhere. */
-             system_printf ("%s pass %d failed, %p..%p, done %d, windows pid %u, %E",
-                           what, pass, low, high, done, pi.dwProcessId);
-             goto err;
-           }
-       }
+     process is last.  If it is put before signals, then a deadlock
+     could be introduced if the process attempts to exit due to a signal. */
+  lock_pthread pthread;
+  lock_signals signals;
+  lock_process process;
 
-      pass++;
-    }
+public:
+  hold_everything (bool& x): ischild (x) {}
+  operator int () const {return signals;}
 
-  debug_printf ("done");
-  return 1;
+  ~hold_everything()
+  {
+    if (ischild)
+      {
+       pthread.dont_bother ();
+       process.dont_bother ();
+       signals.dont_bother ();
+      }
+  }
 
- err:
-  TerminateProcess (pi.hProcess, 1);
-  set_errno (EAGAIN);
-  return 0;
-}
+};
 
-static int
+static void
 resume_child (HANDLE forker_finished)
 {
   SetEvent (forker_finished);
   debug_printf ("signalled child");
-  return 1;
+  return;
 }
 
 /* Notify parent that it is time for the next step. */
@@ -153,22 +165,50 @@ sync_with_parent (const char *s, bool hang_self)
     }
 }
 
-static int __stdcall
-fork_child (HANDLE& hParent, dll *&first_dll, bool& load_dlls)
+bool
+frok::error (const char *fmt, ...)
+{
+  DWORD exit_code = ch.exit_code;
+  if (!exit_code && hchild)
+    {
+      exit_code = ch.proc_retry (hchild);
+      if (!exit_code)
+       return false;
+    }
+  if (exit_code != EXITCODE_FORK_FAILED)
+    {
+      va_list ap;
+      static char buf[NT_MAX_PATH + 256];
+      va_start (ap, fmt);
+      __small_vsprintf (buf, fmt, ap);
+      errmsg = buf;
+    }
+  return true;
+}
+
+int __stdcall
+frok::child (volatile char * volatile here)
 {
+  HANDLE& hParent = ch.parent;
+  extern void fixup_lockf_after_fork ();
   extern void fixup_hooks_after_fork ();
   extern void fixup_timers_after_fork ();
   debug_printf ("child is running.  pid %d, ppid %d, stack here %p",
                myself->pid, myself->ppid, __builtin_frame_address (0));
 
+  /* NOTE: Logically this belongs in dll_list::load_after_fork, but by
+     doing it here, before the first sync_with_parent, we can exploit
+     the existing retry mechanism in hopes of getting a more favorable
+     address space layout next time. */
+  dlls.reserve_space ();
+
   sync_with_parent ("after longjmp", true);
-  sigproc_printf ("hParent %p, child 1 first_dll %p, load_dlls %d", hParent,
-                 first_dll, load_dlls);
+  sigproc_printf ("hParent %p, load_dlls %d", hParent, load_dlls);
 
   /* If we've played with the stack, stacksize != 0.  That means that
      fork() was invoked from other than the main thread.  Make sure that
      the threadinfo information is properly set up.  */
-  if (fork_info->stacksize)
+  if (fork_info->stackaddr)
     {
       _main_tls = &_my_tls;
       _main_tls->init_thread (NULL, NULL);
@@ -176,34 +216,25 @@ fork_child (HANDLE& hParent, dll *&first_dll, bool& load_dlls)
       _impure_ptr = &_main_tls->local_clib;
     }
 
-  if (wincap.has_security ())
-    {
-      set_cygwin_privileges (hProcImpToken);
-      cygheap->user.reimpersonate ();
-    }
+  set_cygwin_privileges (hProcToken);
+  clear_procimptoken ();
+  cygheap->user.reimpersonate ();
 
 #ifdef DEBUGGING
-  char c;
-  if (GetEnvironmentVariable ("FORKDEBUG", &c, 1))
+  if (GetEnvironmentVariableA ("FORKDEBUG", NULL, 0))
     try_to_debug ();
   char buf[80];
   /* This is useful for debugging fork problems.  Use gdb to attach to
      the pid reported here. */
-  if (GetEnvironmentVariable ("CYGWIN_FORK_SLEEP", buf, sizeof (buf)))
+  if (GetEnvironmentVariableA ("CYGWIN_FORK_SLEEP", buf, sizeof (buf)))
     {
       small_printf ("Sleeping %d after fork, pid %u\n", atoi (buf), GetCurrentProcessId ());
       Sleep (atoi (buf));
     }
 #endif
 
-  set_file_api_mode (current_codepage);
-
   MALLOC_CHECK;
 
-  if (fixup_mmaps_after_fork (hParent))
-    api_fatal ("recreate_mmaps_after_fork_failed");
-
-#ifdef USE_SERVER
   /* Incredible but true:  If we use sockets and SYSV IPC shared memory,
      there's a good chance that a duplicated socket in the child occupies
      memory which is needed to duplicate shared memory from the parent
@@ -213,7 +244,6 @@ fork_child (HANDLE& hParent, dll *&first_dll, bool& load_dlls)
      fdtab before fixing up shared memory. */
   if (fixup_shms_after_fork ())
     api_fatal ("recreate_shm areas after fork failed");
-#endif
 
   MALLOC_CHECK;
 
@@ -228,25 +258,25 @@ fork_child (HANDLE& hParent, dll *&first_dll, bool& load_dlls)
     }
   else
     {
-      dlls.load_after_fork (hParent, first_dll);
+      dlls.load_after_fork (hParent);
       cygheap->fdtab.fixup_after_fork (hParent);
       sync_with_parent ("loaded dlls", true);
     }
 
+  init_console_handler (myself->ctty > 0);
   ForceCloseHandle1 (fork_info->forker_finished, forker_finished);
 
-  _my_tls.fixup_after_fork ();
-  sigproc_init ();
-
   pthread::atforkchild ();
   fixup_timers_after_fork ();
-  fixup_hooks_after_fork ();
   cygbench ("fork-child");
   ld_preload ();
+  fixup_hooks_after_fork ();
+  _my_tls.fixup_after_fork ();
   cygwin_finished_initializing = true;
   return 0;
 }
 
+#define NO_SLOW_PID_REUSE
 #ifndef NO_SLOW_PID_REUSE
 static void
 slow_pid_reuse (HANDLE h)
@@ -262,8 +292,9 @@ slow_pid_reuse (HANDLE h)
      windows from reusing the same pid.  */
   if (last_fork_procs[nfork_procs])
     ForceCloseHandle1 (last_fork_procs[nfork_procs], fork_stupidity);
-  if (DuplicateHandle (hMainProc, h, hMainProc, &last_fork_procs[nfork_procs],
-                       0, FALSE, DUPLICATE_SAME_ACCESS))
+  if (DuplicateHandle (GetCurrentProcess (), h,
+                      GetCurrentProcess (), &last_fork_procs[nfork_procs],
+                      0, FALSE, DUPLICATE_SAME_ACCESS))
     ProtectHandle1 (last_fork_procs[nfork_procs], fork_stupidity);
   else
     {
@@ -274,18 +305,21 @@ slow_pid_reuse (HANDLE h)
 }
 #endif
 
-static int __stdcall
-fork_parent (HANDLE&, dll *&first_dll, bool& load_dlls, void *stack_here, child_info_fork &ch)
+int __stdcall
+frok::parent (volatile char * volatile stack_here)
 {
   HANDLE forker_finished;
   DWORD rc;
-  PROCESS_INFORMATION pi = {0, NULL, 0, 0};
+  child_pid = -1;
+  this_errno = 0;
+  bool fix_impersonation = false;
+  pinfo child;
 
-  pthread::atforkprepare ();
+  int c_flags = GetPriorityClass (GetCurrentProcess ());
+  debug_printf ("priority class %d", c_flags);
 
-  int c_flags = GetPriorityClass (hMainProc) /*|
-               CREATE_NEW_PROCESS_GROUP*/;
-  STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
+  errmsg = NULL;
+  hchild = NULL;
 
   /* If we don't have a console, then don't create a console for the
      child either.  */
@@ -306,20 +340,16 @@ fork_parent (HANDLE&, dll *&first_dll, bool& load_dlls, void *stack_here, child_
   if (cygheap->fdtab.need_fixup_before ())
     c_flags |= CREATE_SUSPENDED;
 
-  /* Remember the address of the first loaded dll and decide
-     if we need to load dlls.  We do this here so that this
-     information will be available in the parent and, when
-     the stack is copied, in the child. */
-  first_dll = dlls.start.next;
+  /* Remember if we need to load dynamically linked dlls.
+     We do this here so that this information will be available
+     in the parent and, when the stack is copied, in the child. */
   load_dlls = dlls.reload_on_fork && dlls.loaded_dlls;
 
-  /* This will help some of the confusion.  */
-  fflush (stdout);
-
   forker_finished = CreateEvent (&sec_all, FALSE, FALSE, NULL);
   if (forker_finished == NULL)
     {
-      system_printf ("unable to allocate forker_finished event, %E");
+      this_errno = geterrno_from_win_error ();
+      error ("unable to allocate forker_finished event");
       return -1;
     }
 
@@ -327,77 +357,127 @@ fork_parent (HANDLE&, dll *&first_dll, bool& load_dlls, void *stack_here, child_
 
   ch.forker_finished = forker_finished;
 
-  stack_base (ch);
+  ch.stackbottom = _tlsbase;
+  ch.stacktop = (void *) _tlstop;
+  ch.stackaddr = 0;
+  ch.guardsize = 0;
+  if (&_my_tls != _main_tls)
+    {
+      /* We have not been started from the main thread.  Fetch the
+        information required to set up the thread stack identically
+        in the child. */
+      PTEB teb = NtCurrentTeb ();
+      if (!teb->DeallocationStack)
+       {
+         /* Pthread with application-provided stack.  Don't set up a
+            PAGE_GUARD page.  guardsize == -1 is used in alloc_stack_hard_way
+            to recognize this type of stack. */
+         ch.stackaddr = _my_tls.tid->attr.stackaddr;
+         ch.guardsize = (size_t) -1;
+       }
+      else
+       {
+         ch.stackaddr = teb->DeallocationStack;
+         /* If it's a pthread, fetch guardsize from thread attributes. */
+         if (_my_tls.tid)
+           ch.guardsize = _my_tls.tid->attr.guardsize;
+       }
+    }
+  debug_printf ("stack - bottom %p, top %p, addr %p, guardsize %p",
+               ch.stackbottom, ch.stacktop, ch.stackaddr, ch.guardsize);
+
+  PROCESS_INFORMATION pi;
+  STARTUPINFOW si;
+
+  memset (&si, 0, sizeof (si));
+  si.cb = sizeof si;
 
-  si.cb = sizeof (STARTUPINFO);
   si.lpReserved2 = (LPBYTE) &ch;
   si.cbReserved2 = sizeof (ch);
 
-  /* Remove impersonation */
-  cygheap->user.deimpersonate ();
-
-  syscall_printf ("CreateProcess (%s, %s, 0, 0, 1, %x, 0, 0, %p, %p)",
+  syscall_printf ("CreateProcessW (%W, %W, 0, 0, 1, %p, 0, 0, %p, %p)",
                  myself->progname, myself->progname, c_flags, &si, &pi);
   bool locked = __malloc_lock ();
-  rc = CreateProcess (myself->progname, /* image to run */
-                     myself->progname, /* what we send in arg0 */
-                     &sec_none_nih,
-                     &sec_none_nih,
-                     TRUE,       /* inherit handles from parent */
-                     c_flags,
-                     NULL,       /* environment filled in later */
-                     0,          /* use current drive/directory */
-                     &si,
-                     &pi);
+  time_t start_time = time (NULL);
 
-  if (!rc)
-    {
-      __seterrno ();
-      syscall_printf ("CreateProcessA failed, %E");
-      ForceCloseHandle (forker_finished);
-      /* Restore impersonation */
-      cygheap->user.reimpersonate ();
-      __malloc_unlock ();
-      return -1;
-    }
+  /* Remove impersonation */
+  cygheap->user.deimpersonate ();
+  fix_impersonation = true;
+  ch.refresh_cygheap ();
 
-  /* Fixup the parent datastructure if needed and resume the child's
-     main thread. */
-  if (cygheap->fdtab.need_fixup_before ())
+  while (1)
     {
-      cygheap->fdtab.fixup_before_fork (pi.dwProcessId);
-      ResumeThread (pi.hThread);
+      hchild = NULL;
+      rc = CreateProcessW (myself->progname, /* image to run */
+                          myself->progname, /* what we send in arg0 */
+                          &sec_none_nih,
+                          &sec_none_nih,
+                          TRUE,          /* inherit handles from parent */
+                          c_flags,
+                          NULL,          /* environment filled in later */
+                          0,     /* use current drive/directory */
+                          &si,
+                          &pi);
+
+      if (!rc)
+       {
+         this_errno = geterrno_from_win_error ();
+         error ("CreateProcessW failed for '%W'", myself->progname);
+         memset (&pi, 0, sizeof (pi));
+         goto cleanup;
+       }
+
+      if (cygheap->fdtab.need_fixup_before ())
+       {
+         cygheap->fdtab.fixup_before_fork (pi.dwProcessId);
+         ResumeThread (pi.hThread);
+       }
+
+      CloseHandle (pi.hThread);
+      hchild = pi.hProcess;
+
+      /* Protect the handle but name it similarly to the way it will
+        be called in subproc handling. */
+      ProtectHandle1 (hchild, childhProc);
+
+      strace.write_childpid (ch, pi.dwProcessId);
+
+      /* Wait for subproc to initialize itself. */
+      if (!ch.sync (pi.dwProcessId, hchild, FORK_WAIT_TIMEOUT))
+       {
+         if (!error ("forked process died unexpectedly, retry %d, exit code %d",
+                     ch.retry, ch.exit_code))
+           continue;
+         this_errno = EAGAIN;
+         goto cleanup;
+       }
+      break;
     }
 
-  int child_pid = cygwin_pid (pi.dwProcessId);
-  pinfo child (child_pid, 1);
+  /* Restore impersonation */
+  cygheap->user.reimpersonate ();
+  fix_impersonation = false;
+
+  child_pid = cygwin_pid (pi.dwProcessId);
+  child.init (child_pid, 1, NULL);
 
   if (!child)
     {
+      this_errno = get_errno () == ENOMEM ? ENOMEM : EAGAIN;
       syscall_printf ("pinfo failed");
-      if (get_errno () != ENOMEM)
-       set_errno (EAGAIN);
       goto cleanup;
     }
 
-  child->start_time = time (NULL); /* Register child's starting time. */
+  child->start_time = start_time; /* Register child's starting time. */
   child->nice = myself->nice;
 
   /* Initialize things that are done later in dll_crt0_1 that aren't done
      for the forkee.  */
-  strcpy (child->progname, myself->progname);
-
-  /* Restore impersonation */
-  cygheap->user.reimpersonate ();
-
-  ProtectHandle (pi.hThread);
-  /* Protect the handle but name it similarly to the way it will
-     be called in subproc handling. */
-  ProtectHandle1 (pi.hProcess, childhProc);
+  wcscpy (child->progname, myself->progname);
 
   /* Fill in fields in the child's process table entry.  */
   child->dwProcessId = pi.dwProcessId;
-  child.hProcess = pi.hProcess;
+  child.hProcess = hchild;
 
   /* Hopefully, this will succeed.  The alternative to doing things this
      way is to reserve space prior to calling CreateProcess and then fill
@@ -406,70 +486,74 @@ fork_parent (HANDLE&, dll *&first_dll, bool& load_dlls, void *stack_here, child_
      we can't actually record the pid in the internal table. */
   if (!child.remember (false))
     {
-      TerminateProcess (pi.hProcess, 1);
-      set_errno (EAGAIN);
+      TerminateProcess (hchild, 1);
+      this_errno = EAGAIN;
+#ifdef DEBUGGING0
+      error ("child remember failed");
+#endif
       goto cleanup;
     }
 
 #ifndef NO_SLOW_PID_REUSE
-  slow_pid_reuse (pi.hProcess);
+  slow_pid_reuse (hchild);
 #endif
 
-  /* Wait for subproc to initialize itself. */
-  if (!ch.sync (child->pid, pi.hProcess, FORK_WAIT_TIMEOUT))
-    {
-      system_printf ("child %d died waiting for longjmp before initialization", child_pid);
-      goto cleanup;
-    }
-
   /* CHILD IS STOPPED */
   debug_printf ("child is alive (but stopped)");
 
-  /* Initialize, in order: data, bss, heap, stack, dll data, dll bss
-     Note: variables marked as NO_COPY will not be copied
-     since they are placed in a protected segment. */
-
+  /* Initialize, in order: stack, dll data, dll bss.
+     data, bss, heap were done earlier (in dcrt0.cc)
+     Note: variables marked as NO_COPY will not be copied since they are
+     placed in a protected segment.  */
 
   MALLOC_CHECK;
-  void *impure_beg;
-  void *impure_end;
+  const void *impure_beg;
+  const void *impure_end;
+  const char *impure;
   if (&_my_tls == _main_tls)
-    impure_beg = impure_end = NULL;
+    impure_beg = impure_end = impure = NULL;
   else
     {
+      impure = "impure";
       impure_beg = _impure_ptr;
       impure_end = _impure_ptr + 1;
     }
-  rc = fork_copy (pi, "user/cygwin data",
-                 user_data->data_start, user_data->data_end,
-                 user_data->bss_start, user_data->bss_end,
-                 cygheap->user_heap.base, cygheap->user_heap.ptr,
-                 stack_here, ch.stackbottom,
-                 dll_data_start, dll_data_end,
-                 dll_bss_start, dll_bss_end, impure_beg, impure_end, NULL);
+  rc = child_copy (hchild, true,
+                  "stack", stack_here, ch.stackbottom,
+                  impure, impure_beg, impure_end,
+                  NULL);
 
   __malloc_unlock ();
   locked = false;
   MALLOC_CHECK;
   if (!rc)
-    goto cleanup;
+    {
+      this_errno = get_errno ();
+      error ("pid %u, exitval %p", pi.dwProcessId, ch.exit_code);
+      goto cleanup;
+    }
 
   /* Now fill data/bss of any DLLs that were linked into the program. */
   for (dll *d = dlls.istart (DLL_LINK); d; d = dlls.inext ())
     {
       debug_printf ("copying data/bss of a linked dll");
-      if (!fork_copy (pi, "linked dll data/bss", d->p.data_start, d->p.data_end,
-                                                d->p.bss_start, d->p.bss_end,
-                                                NULL))
-       goto cleanup;
+      if (!child_copy (hchild, true,
+                      "linked dll data", d->p.data_start, d->p.data_end,
+                      "linked dll bss", d->p.bss_start, d->p.bss_end,
+                      NULL))
+       {
+         this_errno = get_errno ();
+         error ("couldn't copy linked dll data/bss");
+         goto cleanup;
+       }
     }
 
-  /* Start thread, and wait for it to reload dlls.  */
-  if (!resume_child (forker_finished))
-    goto cleanup;
-  else if (!ch.sync (child->pid, pi.hProcess, FORK_WAIT_TIMEOUT))
+  /* Start thread, and then wait for it to reload dlls.  */
+  resume_child (forker_finished);
+  if (!ch.sync (child->pid, hchild, FORK_WAIT_TIMEOUT))
     {
-      system_printf ("child %d died waiting for dll loading", child_pid);
+      this_errno = EAGAIN;
+      error ("died waiting for dll loading");
       goto cleanup;
     }
 
@@ -483,56 +567,53 @@ fork_parent (HANDLE&, dll *&first_dll, bool& load_dlls, void *stack_here, child_
       for (dll *d = dlls.istart (DLL_LOAD); d; d = dlls.inext ())
        {
          debug_printf ("copying data/bss for a loaded dll");
-         if (!fork_copy (pi, "loaded dll data/bss", d->p.data_start, d->p.data_end,
-                                                    d->p.bss_start, d->p.bss_end,
-                                                    NULL))
-           goto cleanup;
+         if (!child_copy (hchild, true,
+                          "loaded dll data", d->p.data_start, d->p.data_end,
+                          "loaded dll bss", d->p.bss_start, d->p.bss_end,
+                          NULL))
+           {
+             this_errno = get_errno ();
+#ifdef DEBUGGING
+             error ("copying data/bss for a loaded dll");
+#endif
+             goto cleanup;
+           }
        }
       /* Start the child up again. */
       resume_child (forker_finished);
     }
 
-  ForceCloseHandle (pi.hThread);
   ForceCloseHandle (forker_finished);
   forker_finished = NULL;
-  pi.hThread = NULL;
-  pthread::atforkparent ();
 
   return child_pid;
 
 /* Common cleanup code for failure cases */
- cleanup:
+cleanup:
+  if (fix_impersonation)
+    cygheap->user.reimpersonate ();
   if (locked)
     __malloc_unlock ();
 
   /* Remember to de-allocate the fd table. */
-  if (pi.hProcess)
-    ForceCloseHandle1 (pi.hProcess, childhProc);
-  if (pi.hThread)
-    ForceCloseHandle (pi.hThread);
+  if (hchild && !child.hProcess)
+    ForceCloseHandle1 (hchild, childhProc);
   if (forker_finished)
     ForceCloseHandle (forker_finished);
+  debug_printf ("returning -1");
   return -1;
 }
 
 extern "C" int
 fork ()
 {
-  struct
-  {
-    dll *first_dll;
-    bool load_dlls;
-    child_info_fork ch;
-  } grouped;
-
-  MALLOC_CHECK;
+  frok grouped;
 
   debug_printf ("entering");
-  grouped.first_dll = NULL;
   grouped.load_dlls = 0;
 
-  void *esp;
-  __asm__ volatile ("movl %%esp,%0": "=r" (esp));
+  int res;
+  bool ischild = false;
 
   myself->set_has_pgid_children ();
 
@@ -544,15 +625,63 @@ fork ()
       return -1;
     }
 
-  sig_send (NULL, __SIGHOLD);
-  int res = setjmp (grouped.ch.jmp);
-  if (res)
-    res = fork_child (grouped.ch.parent, grouped.first_dll, grouped.load_dlls);
-  else
-    res = fork_parent (grouped.ch.parent, grouped.first_dll, grouped.load_dlls, esp, grouped.ch);
-  sig_send (NULL, __SIGNOHOLD);
+  {
+    hold_everything held_everything (ischild);
+    /* This tmp_pathbuf constructor is required here because the below setjmp
+       magic will otherwise not restore the original buffer count values in
+       the thread-local storage.  A process forking too deeply will run into
+       the problem to be out of temporary TLS path buffers. */
+    tmp_pathbuf tp;
+
+    if (!held_everything)
+      {
+       if (exit_state)
+         Sleep (INFINITE);
+       set_errno (EAGAIN);
+       return -1;
+      }
+
+    /* Put the dll list in topological dependency ordering, in
+       hopes that the child will have a better shot at loading dlls
+       properly if it only has to deal with one at a time.  */
+    dlls.topsort ();
+
+    ischild = !!setjmp (grouped.ch.jmp);
+
+    volatile char * volatile esp;
+    __asm__ volatile ("movl %%esp,%0": "=r" (esp));
+
+    if (!ischild)
+      res = grouped.parent (esp);
+    else
+      {
+       res = grouped.child (esp);
+       in_forkee = false;
+       ischild = true; /* might have been reset by fork mem copy */
+      }
+  }
 
   MALLOC_CHECK;
+  if (ischild)
+    {
+      myself->process_state |= PID_ACTIVE;
+      myself->process_state &= ~(PID_INITIALIZING | PID_EXITED | PID_REAPED);
+    }
+  else if (res < 0)
+    {
+      if (!grouped.errmsg)
+       syscall_printf ("fork failed - child pid %d, errno %d", grouped.child_pid, grouped.this_errno);
+      else
+       {
+         char buf[strlen (grouped.errmsg) + sizeof ("child %d - , errno 4294967295  ")];
+         strcpy (buf, "child %d - ");
+         strcat (buf, grouped.errmsg);
+         strcat (buf, ", errno %d");
+         system_printf (buf, grouped.child_pid, grouped.this_errno);
+       }
+
+      set_errno (grouped.this_errno);
+    }
   syscall_printf ("%d = fork()", res);
   return res;
 }
@@ -563,94 +692,62 @@ fork_init ()
 }
 #endif /*DEBUGGING*/
 
-#ifdef NEWVFORK
-/* Dummy function to force second assignment below to actually be
-   carried out */
-static vfork_save *
-get_vfork_val ()
-{
-  return vfork_storage.val ();
-}
-#endif
 
 extern "C" int
 vfork ()
 {
-#ifndef NEWVFORK
   debug_printf ("stub called");
   return fork ();
-#else
-  vfork_save *vf = get_vfork_val ();
-  char **esp, **pp;
-
-  if (vf == NULL)
-    vf = vfork_storage.create ();
-  else if (vf->pid)
-    return fork ();
-
-  // FIXME the tls stuff could introduce a signal race if a child process
-  // exits quickly.
-  if (!setjmp (vf->j))
-    {
-      vf->pid = -1;
-      __asm__ volatile ("movl %%esp,%0": "=r" (vf->vfork_esp):);
-      __asm__ volatile ("movl %%ebp,%0": "=r" (vf->vfork_ebp):);
-      for (pp = (char **) vf->frame, esp = vf->vfork_esp;
-          esp <= vf->vfork_ebp + 2; pp++, esp++)
-       *pp = *esp;
-      vf->ctty = myself->ctty;
-      vf->sid = myself->sid;
-      vf->pgid = myself->pgid;
-      cygheap->ctty_on_hold = cygheap->ctty;
-      vf->open_fhs = cygheap->open_fhs;
-      debug_printf ("cygheap->ctty_on_hold %p, cygheap->open_fhs %d", cygheap->ctty_on_hold, cygheap->open_fhs);
-      int res = cygheap->fdtab.vfork_child_dup () ? 0 : -1;
-      debug_printf ("%d = vfork()", res);
-      _my_tls.call_signal_handler ();  // FIXME: racy
-      vf->tls = _my_tls;
-      return res;
-    }
-
-  vf = get_vfork_val ();
-
-  for (pp = (char **) vf->frame, esp = vf->vfork_esp;
-       esp <= vf->vfork_ebp + 2; pp++, esp++)
-    *esp = *pp;
+}
 
-  cygheap->fdtab.vfork_parent_restore ();
+/* Copy memory from one process to another. */
 
-  myself->ctty = vf->ctty;
-  myself->sid = vf->sid;
-  myself->pgid = vf->pgid;
-  termios_printf ("cygheap->ctty %p, cygheap->ctty_on_hold %p", cygheap->ctty, cygheap->ctty_on_hold);
-  cygheap->open_fhs = vf->open_fhs;
+bool
+child_copy (HANDLE hp, bool write, ...)
+{
+  va_list args;
+  va_start (args, write);
+  static const char *huh[] = {"read", "write"};
 
-  if (vf->pid < 0)
+  char *what;
+  while ((what = va_arg (args, char *)))
     {
-      int exitval = vf->exitval;
-      vf->pid = 0;
-      if ((vf->pid = fork ()) == 0)
-       exit (exitval);
-    }
+      char *low = va_arg (args, char *);
+      char *high = va_arg (args, char *);
+      DWORD todo = high - low;
+      char *here;
 
-  int pid = vf->pid;
-  vf->pid = 0;
-  debug_printf ("exiting vfork, pid %d", pid);
-  sig_dispatch_pending ();
+      for (here = low; here < high; here += todo)
+       {
+         DWORD done = 0;
+         if (here + todo > high)
+           todo = high - here;
+         int res;
+         if (write)
+           res = WriteProcessMemory (hp, here, here, todo, &done);
+         else
+           res = ReadProcessMemory (hp, here, here, todo, &done);
+         debug_printf ("%s - hp %p low %p, high %p, res %d", what, hp, low, high, res);
+         if (!res || todo != done)
+           {
+             if (!res)
+               __seterrno ();
+             /* If this happens then there is a bug in our fork
+                implementation somewhere. */
+             system_printf ("%s %s copy failed, %p..%p, done %d, windows pid %u, %E",
+                           what, huh[write], low, high, done, myself->dwProcessId);
+             goto err;
+           }
+       }
+    }
 
-  _my_tls.call_signal_handler ();      // FIXME: racy
-  _my_tls = vf->tls;
-  return pid;
-#endif
-}
+  va_end (args);
+  debug_printf ("done");
+  return true;
 
-int
-child_copy (HANDLE h, DWORD pid, const char *what, void *child_start, void *child_end)
-{
-  PROCESS_INFORMATION pi;
-  pi.hProcess = h;
-  pi.dwProcessId = pid;
-  pi.hThread = NULL;
-  debug_printf ("%s, start %p, end %p", what, child_start, child_end);
-  return fork_copy (pi, what, child_start, child_end, NULL);
+ err:
+  va_end (args);
+  TerminateProcess (hp, 1);
+  set_errno (EAGAIN);
+  return false;
 }