public inbox for cygwin@cygwin.com
 help / color / mirror / Atom feed
* Bug with Fix: mmap(MAP_PRIVATE) region handling in fork()
@ 1997-08-24 19:22 Gary Fuehrer
  0 siblings, 0 replies; 2+ messages in thread
From: Gary Fuehrer @ 1997-08-24 19:22 UTC (permalink / raw)
  To: 'gnu-win32@cygnus.com'

After a fork, a child does not see the same contents in its mmap regions
as the parent does when the region was created with
mmap(,,,MAP_PRIVATE,,) and the parent has written to that regjon prior
to the fork.  This bug is a result of the fork code failing to make the
parent copy into the child process its version of mmap regions that have
the FILE_MAP_COPY bit set.

The following changes to three files (winsup.h, fork.cc, mmap.cc) will
fix this bug.

================
winsup.h
================

Eliminate these declarations:
--------------------------------
/* For mmaps across fork() */

int recreate_mmaps_after_fork(void
*);

void set_child_mmap_ptr(pinfo *);

--------------------------------




================
fork.cc
================

Rename the static "copy" function to the non-static
"copy_memory_to_forkee" function:
--------------------------------
int /* NOTE: not "static" anymore */
copy_memory_to_forkee (HANDLE child, void *low, void *high, int)


:
      rc = copy_memory_to_forkee (pi.hProcess, (char *)proc_data_start,
(char *)proc_data_end, 0);

	:
      rc = copy_memory_to_forkee (pi.hProcess, (char *)proc_bss_start,
(char *)proc_bss_end, 1);
	:
      rc = copy_memory_to_forkee (child->hProcess, u->base, u->ptr, 2);


:
      rc = copy_memory_to_forkee (child->hProcess, &x, u->initial_sp,
3);

--------------------------------



Add these function declarations just before the "cygwin_fork_helper1"
function:
--------------------------------
int copy_mmap_records_to_forkee(pinfo *child);

int
recreate_mmaps_after_fork(void *);

int
copy_copyonwrite_mmaps_to_forkee(HANDLE
hChild);

--------------------------------


Replace the line "set_child_mmap_ptr (child);" with:
--------------------------------
      if (copy_mmap_records_to_forkee(child))

	{

	  small_printf
("fork_helper: copy of mmap_records failed\n");

	  set_errno
(EAGAIN);

	  syscall_printf ("-1 = fork(), copy of mmap_records
failed\n");

	  TerminateProcess (child->hProcess, 1);


u->forkee = 0;

          goto cleanup;


}

--------------------------------


Insert this block of code before the comment "* Now we have started...":
--------------------------------
      if(copy_copyonwrite_mmaps_to_forkee(child->hProcess))

        {


small_printf("fork_helper: copy_copyonwrite_mmaps_to_forkee failed\n");


set_errno (EAGAIN);

	      syscall_printf ("-1 = fork(), copy of
copyonwrite memory failed\n");

	      TerminateProcess (child->hProcess,
1);

              goto cleanup;


}

--------------------------------


Move this block of code so that it comes before the line "/* Tell our
parent we've started."
--------------------------------
      if(recreate_mmaps_after_fork(u->self->mmap_ptr))

        {


small_printf("fork child: recreate_mmaps_after_fork_failed\n");


ExitProcess (1);

        }

--------------------------------



================
mmap.cc
================

Replace the functions "recreate_mmaps_after_fork", and
"set_child_mmap_ptr" with these three functions that follow:
--------------------------------
/*

 * Called by the parent process durring a fork.  At this point, the
child has

 * not yet run.  When it does run and initialize, we want it
to re-establish

 * the same mmap regions that we have.  Because our
heap, where our

 * mmap_records are stored, will not have been copied to
the child at the time

 * it initializes, we can't simply pass it the
"mmapped_areas" pointer.

 * Instead, we must copy the contents of our
"mmapped_areas" into an array of

 * mmap_records where the child process
can read them.  The "child->mmap_ptr"

 * is set to a handle for the
memory where the the child will find the array.

 * After we return, we
will suspend and the child will initialize and call

 *
"recreate_mmaps_after_fork" below.

 */



int
copy_mmap_records_to_forkee(pinfo *child)

{

  /*

   * Count the number
of mmap_record entries are in the "mmapped_areas" map.

   */

  unsigned
count = 0;

  map< int, list<mmap_record> * >::iterator it;

  if
(mmapped_areas != NULL)

  {

    for( it = mmapped_areas->begin(); it !=
mmapped_areas->end(); ++it)

    {

      list<mmap_record> *l =
(*it).second;

      if(l != 0)

      {


list<mmap_record>::iterator li;

        for( li = l->begin(); li !=
l->end(); ++li)

            count++;

      }

    }

  }



  /*

   * Return
with "child->mmap_ptr == NULL"; there are no mmap regions.

   */

  if
(count == 0)

  {

    child->mmap_ptr = NULL;


debug_printf("copy_mmap_records_to_forkee: succeeded\n");

    return 0;


}



  /*

   * Create a file mapping that will be made available to the
child process

   * and will hold an array of all the mmap_records in
"mmapped_areas".

   */

  HANDLE h = CreateFileMapping((HANDLE)-1, NULL,
PAGE_READWRITE, 0,


(count+1)*sizeof(mmap_record), NULL);

  if (h == NULL)

  {


syscall_printf("copy_mmap_records_to_forkee: CreateFileMapping
failed\n");

    return -1;

  }



  mmap_record* pRec =
(mmap_record*)MapViewOfFile(h, FILE_MAP_WRITE, 0, 0, 0);

  if (pRec ==
NULL)

  {

    CloseHandle(h);


syscall_printf("copy_mmap_records_to_forkee: MapViewOfFile failed\n");


return -1;

  }



  /*

   * Now copy all the mmap_records into the mapped
memory array.

   * The count of elements in this array are in the first
record

   * along with some other useful info.

   */

  *pRec =
mmap_record(0, 0, (DWORD)mmapped_areas, count, pRec);

  if
(mmapped_areas != NULL)

  {

    for( it = mmapped_areas->begin(); it !=
mmapped_areas->end(); ++it)

    {

      list<mmap_record> *l =
(*it).second;

      if(l != 0)

      {


list<mmap_record>::iterator li;

        for( li = l->begin(); li !=
l->end(); ++li)

          *(++pRec) = *li;

      }

    }

  }



  /*

   *
Duplicate the handle for use by the child process. The child process


* will use the duplicate handle in recreate_mmaps_after_fork.

   * We
also unmap our view and close our handle, since we're done with it.


*/

  UnmapViewOfFile(pRec - count);


DuplicateHandle(GetCurrentProcess(), h, child->hProcess,


(LPHANDLE)&child->mmap_ptr, FILE_MAP_READ,

                  FALSE,
DUPLICATE_CLOSE_SOURCE);



  debug_printf("copy_mmap_records_to_forkee:
succeeded\n");

  return 0;

}





/*

 * Call to re-create all the file
mappings in a fork()'ed child.  Called from

 * "cygwin_fork_helper1" by
the child durring initialization.  At this point,

 * the parent has
duplicated its .data and .bss, but not its stack or heap.

 * We are
passed a handle to memory via "u->self->mmap_ptr" where an array of

 *
mmap_records can be found.  This was put there by our parent in its
call

 * to "copy_mmap_records_to_forkee".  The first record in the array
contains

 * information like the count of records.  All the HANDLE's in
those records

 * are valid for us (we inherited them), but none of the
mapped areas

 * are in our address space.  We need to iterate through
the array and do the

 * MapViewOfFileEx calls.  Initially, the
"mmapped_areas" pointer is assumed to

 * be NULL, since this process was
just created.  Before returning, both

 * "mmapped_areas" and
"u->self->mmap_ptr" are set to point to the mmap data

 * structure in
the heap.  This pointer is also passed to us in the first

 *
mmap_record.  Remember, that pointer refers to heap memory which the
parent

 * process hasn't yet copied to us.  After we return, we will
suspend while we

 * wait for our parent to copy our heap (so that
"mmaped_areas" is valid).

 * All of our mmap regions, except the ones
that the parent created with

 * FILE_MAP_COPY, will refer to the same
physical memory that the cooresponding

 * region in our parent refers
to.  In the case of FILE_MAP_COPY regions, we

 * will see the contents
of the original file at the time the parent mmapped it.

 * Any changes
made by the parent to these regions will not be seen by us.  So,

 * the
parent will also need to call "copy_copyonwrite_mmaps_to_forkee" below
so

 * that we are in sync with the parent immediately after the fork.


*/



int recreate_mmaps_after_fork(void *param)

{

  if (param != NULL)


{



  HANDLE h = (HANDLE)param;

  mmap_record* pRec =
(mmap_record*)MapViewOfFile(h, FILE_MAP_READ, 0, 0,
                       sizeof(mmap_record));

  if (pRec == NULL)

  {


small_printf("-1 = recreate_mmaps_after_fork(): MapViewOfFile failed "


"with GetLastError = %x\n", GetLastError());

    return -1;

  }



  /*


* Copy the information that was put in the first record

   * (see
set_child_mmap_ptr).

   */

  void* pRecDesired = pRec->get_address();


unsigned count = pRec->get_size();

  mmapped_areas = (map< int,
list<mmap_record> * > *)pRec->get_offset();



  /*

   * We need to have
the array of mmap_records at the address "pRecDesired".

   * Otherwise,
this array may be overlapping one of the mapped regions

   * in the
parent that we are trying to duplicate.  It is unlikely that

   * we are
so lucky as to have "pRec" mapped correctly the first time, so

   *
unmap this view and re-map it using MapViewOfFileEx.

   */


UnmapViewOfFile(pRec);



  if (count > 0)

  {

    pRec =
(mmap_record*)MapViewOfFileEx(h, FILE_MAP_READ, 0, 0, 0, pRecDesired);


if (pRec == NULL)

    {

      small_printf("-1 =
recreate_mmaps_after_fork(): MapViewOfFileEx failed "


"with GetLastError = %x\n", GetLastError());

      return -1;

    }




if (pRec != pRecDesired)

    {

      small_printf("-1 =
recreate_mmaps_after_fork(): MapViewOfFileEx wrong address\n");


return -1;

    }



    /*

     * Copy the "pRec" mmap_record array into
the

     * "mmaped_areas" map of mmap_record lists.

     */

    while
(count-- > 0)

    {

      ++pRec;



      /* Now re-create the
MapViewOfFileEx call */

      void* base =
MapViewOfFileEx(pRec->get_handle(), pRec->get_access(),


0, pRec->get_offset(),


pRec->get_size(), pRec->get_address());

      if(base !=
pRec->get_address())

      {


small_printf("recreate_mmaps_after_fork: base address %x\


fails to match requested address %x\n", base, pRec->get_address());


return -1;

      }



      debug_printf("recreate_mmaps_after_fork: h =
%x, access = %x, offset = %d, \

          size = %d, address = %x\n",
pRec->get_handle(), pRec->get_access(),

          pRec->get_offset(),
pRec->get_size(), pRec->get_address());

    }




UnmapViewOfFile(pRecDesired);

  }

  CloseHandle(h);



 }



  /* Now set
our mmap record in case the child forks. */

  u->self->mmap_ptr =
mmapped_areas;



  debug_printf("recreate_mmaps_after_fork:
succeeded\n");

  return 0;

}





/*

 * Called by the parent process
durring a fork.  At this point,

 * the address space of the child
process has been established;

 * that is, the child has already called
"recreate_mmaps_after_fork".

 * Any mmap region created with
"FILE_MAP_COPY" will need to be

 * copied to the cooresponding memory
addresses in the child process,

 * or else the parent and child may not
see the same contents at those

 * addresses.  They will be different for
those FILE_MAP_COPY regions

 * that the parent has written to (unless
the parent wrote what was

 * there originally).

 */



extern int
copy_memory_to_forkee (HANDLE child, void *low, void *high, int);



int
copy_copyonwrite_mmaps_to_forkee(HANDLE hChild)

{

  /*

   * Iterate
through the array if mmap_records and copy those entries

   * that have
FILE_MAP_COPY set.

   */

  map< int, list<mmap_record> * >::iterator
it;

  if (mmapped_areas != NULL)

  {

    for( it =
mmapped_areas->begin(); it != mmapped_areas->end(); ++it)

    {


list<mmap_record> *l = (*it).second;

      if(l != 0)

      {


list<mmap_record>::iterator li;

          for( li = l->begin(); li !=
l->end(); ++li)

          {

            mmap_record& rec = *li;


char* start = (char*)rec.get_address() + rec.get_offset();




if ((rec.get_access() & FILE_MAP_COPY) &&


!copy_memory_to_forkee(hChild, start, start + rec.get_size(), 0))


{

              syscall_printf("copy_copyonwrite_mmaps_to_forkee:
copy_memory_to_forkee failed\n");

              return -1;


}



	        debug_printf("copy_copyonwrite_mmaps_to_forkee: h = %x,
"

                "access = %x, offset = %d, size = %d, address =
%x\n",

                rec.get_handle(), rec.get_access(),
rec.get_offset(),
                rec.get_size(), rec.get_address());

          }

      }


}

  }



  debug_printf("copy_copyonwrite_mmaps_to_forkee: succeeded\n");


return 0;

}

--------------------------------

-
For help on using this list (especially unsubscribing), send a message to
"gnu-win32-request@cygnus.com" with one line of text: "help".

^ permalink raw reply	[flat|nested] 2+ messages in thread

* RE: Bug with Fix: mmap(MAP_PRIVATE) region handling in fork()
@ 1997-08-27  7:06 Gary Fuehrer
  0 siblings, 0 replies; 2+ messages in thread
From: Gary Fuehrer @ 1997-08-27  7:06 UTC (permalink / raw)
  To: 'gnu-win32@cygnus.com'

[-- Attachment #1: Type: text/plain, Size: 1169 bytes --]

> Download cdk-src.tar.gz from /pub/gnu-win32/gnu-win32-b18/ at
> ftp.cygnus.com.  After expanding it out, you will have a "cdk"
> subdirectory.  The "winsup" directory is in there.
> 
> Attached are my copies of the files (except for "winsup.h" which was
> trivial).  My e-mail about the changes came out crappy because of all
> the gratuitous line wrapping.  So, maybe it would be easier to diff my
> version with the original.
> 
>   
>   
> 
> Gary
> 
> 
> ----------
> From: 	Martin Schrape[SMTP:schrape@atomika.com]
> Sent: 	Tuesday, August 26, 1997 1:56 PM
> To: 	Gary Fuehrer
> Subject: 	RE: Bug with Fix: mmap(MAP_PRIVATE) region handling in
> fork()
> 
> 
> 
> -----Original Message-----
> From:	Gary Fuehrer [SMTP:Fuehrer@seabase.com]
> Sent:	Friday, August 22, 1997 10:42 PM
> To:	'gnu-win32@cygnus.com'
> Subject:	Bug with Fix: mmap(MAP_PRIVATE) region handling in
> fork()
> 
> 
> The following changes to three files (winsup.h, fork.cc, mmap.cc) will
> fix this bug.
> 
> ================
> winsup.h
> ================
> 
> [schrape]  Gary,
> sorry but where did you get the winsup files from. I can't find them
> on the cygnus ftp server.
> --martin
> 

[-- Attachment #2: fork.cc --]
[-- Type: text/x-c, Size: 15836 bytes --]

/* fork.cc: fork for WIN32.

   Copyright 1996, 1997 Cygnus Solutions

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include <stdio.h>
#include <unistd.h>
#include "winsup.h"

extern "C" struct _reent reent_data;

/* Timeout to wait for child to start, parent to init child, etc.  */
/* FIXME: Once things stabilize, bump up to a few minutes.  */
#define FORK_WAIT_TIMEOUT (120 * 1000 /* 120 seconds */)

/* Error return code from WaitForSingleObject.  */
#define WAIT_ERROR_RC 0xffffffff

/* Mutexes and events necessary for child startup.  */
static HANDLE forkee_stopped, forker_stopped;

/* Initialize the fork mechanism.  */

void
fork_init ()
{
  SECURITY_ATTRIBUTES sa;
  sa.nLength = sizeof (sa);
  sa.lpSecurityDescriptor = get_null_sd();
  sa.bInheritHandle = 0;

  forker_stopped = CreateEventA (&sa, TRUE, TRUE, "cygwin.forker-stopped");
  forkee_stopped = CreateEventA (&sa, TRUE, TRUE, "cygwin.forkee-stopped");

  /* If we didn't obtain all the resources we need to fork, allow the program
     to continue, but record the fact that fork won't work.  */
  if (forker_stopped == NULL
      || forkee_stopped == NULL)
    {
      system_printf ("fork_init: unable to allocate fork() resources.\n");
      system_printf ("fork_init: fork() disabled.\n");
    }
}

/* Undo what fork_init does.

   If a task is shutting down, this isn't really necessary, but maybe one day
   we'll want to allow cygwin.dll to be attached to and detached from, so we
   should provide a clean interface to release all resources we obtain.  */

void
fork_terminate ()
{
  CloseHandle (forker_stopped);
  CloseHandle (forkee_stopped);
}

#if 0
void
print_checksum (int idx, register void *low, register void *high)
{
  int pi;
  register  int sum = 0;
  small_printf ("CK %d %x %x ", idx, low, high);

  for (int *pi = (int *)low; pi < (int *)high; pi++)
    {
      sum += *pi;
    }
  small_printf ("%x\n", sum);
}
#endif

/* Copy memory from parent to child.
   The result is a boolean indicating success.  */

int
copy_memory_to_forkee (HANDLE child, void *low, void *high, int)
{
  DWORD done;
  int res;

  debug_printf ("fork copy: child handle %d, low %p, high %p\n",
		child, low, high);

  int lump = 1024*64;
  for (char *p = (char *) low; p < (char *) high; p += lump)
    {
      int todo = MIN ((char *)high - p, lump);

      res = WriteProcessMemory (child, p, p, todo, &done);
      if (!res || todo != done)
	{
	  if (!res)
	    __seterrno ();
	  /* This happens when it shouldn't so there's a bug in our fork
	     implementation somewhere.  Print a message on the console to
	     call people's attention to the failure until we get it
	     straightened out.  */
	  small_printf ("fork copy: failed, %p..%p, res is %d, done is %d\n",
			low, high, res, done);
	  /* Call debug_printf as well to make sure message gets in log
	     file if there is one.  */
	  debug_printf ("fork copy: failed\n");
	  return 0;
	}
    }

#if 0
  print_checksum (idx, low, high);
#endif
  debug_printf ("fork copy: done\n");
  return 1;
}

int copy_mmap_records_to_forkee(pinfo *child);
int recreate_mmaps_after_fork(void *);
int copy_copyonwrite_mmaps_to_forkee(HANDLE hChild);

/* Main guts of fork implementation.
   The result is the standard result of fork.  */

static int
cygwin_fork_helper1 (void *proc_data_start,
		     void *proc_data_end,
		     void *proc_bss_start,
		     void *proc_bss_end)
{
  int res;
  int x, rc;
  hinfo *child_hinfo = 0;

  if (u->self->split_heap_p)
    {
      small_printf ("The heap has been split, CYGWIN can't fork this process.\n");
      small_printf ("Increase the heap_chunk_size in the registry and try again.\n");
      set_errno (ENOMEM);
      syscall_printf ("-1 = fork (), split heap\n");
      return -1;
    }

  /* Don't start the fork until we have the lock.  */
  rc = lock_pinfo_for_update(FORK_WAIT_TIMEOUT);
  switch (rc)
    {
    case WAIT_ERROR_RC:
      small_printf ("fork parent: WaitForSingleObject (mutex) failed, win32 error %d\n",
		    GetLastError ());
      set_errno (EAGAIN);
      syscall_printf ("-1 = fork (), wait failed\n");
      return -1;
    case WAIT_TIMEOUT:
      small_printf ("fork parent: WaitForSingleObject (mutex) timed out\n");
      set_errno (EAGAIN);
      syscall_printf ("-1 = fork (), wait timed out\n");
      return -1;
    default:
      debug_printf ("fork_helper: %d = WaitForSingleObject (...)\n", rc);
      break;
    }

  pinfo *child = s->p.allocate_pid ();

  if (!child)
    {
      set_errno (EAGAIN);
      syscall_printf ("-1 = fork (), process table full\n");
      unlock_pinfo();
      return -1;
    }

  /*
   * We need to save this allocated pointer as the child
   * will be changing it, plus we need to delete [] it later.
   */
  child_hinfo = child->hmap.vec;

  /* This will help some of the confusion.  */
  fflush (stdout);

  debug_printf ("fork_helper: parent pid is %d, child pid is %d\n",
		u->self->get_pid (),
		child->get_pid ());

  /* Initialize things that are done later in dll_crt0_1 that aren't done
     for the forkee.  */
  child->reent_save =  reent_data;
  child->progname = u->self->progname;

  /* Copy all the handles we need in the child.  */
  u->self->hmap.dup_for_fork (&child->hmap);

  PROCESS_INFORMATION pi = {0};

  STARTUPINFO si = {0};
  si.cb = sizeof (STARTUPINFO);

  int c_flags = GetPriorityClass (GetCurrentProcess ()) | CREATE_SUSPENDED; 

  syscall_printf ("CreateProcessA (%s, %s,0,0,1,%x, 0,0,%p,%p)\n",
		  u->self->progname, u->self->progname, c_flags, &si, &pi);

  rc = CreateProcessA (u->self->progname, /* image to run */
		       u->self->progname, /* what we send in arg0 */
		       0,		  /* process security attrs */
		       0,		  /* thread security attrs */
		       TRUE,		  /* inherit handles from parent */
		       c_flags,
		       0,	/* use parent's environment */
		       0,	/* use current drive/directory */
		       &si,
		       &pi);

  if (!rc)
    {
      __seterrno ();
      syscall_printf ("-1 = fork(), CreateProcessA failed\n");
      child->inuse_p = PID_NOT_IN_USE;
      delete [] child_hinfo;
      unlock_pinfo();
      return -1;
    }

  ResetEvent (forker_stopped);
  ResetEvent (forkee_stopped);
  
  debug_printf ("fork_helper: about to call setjmp\n");
  x = setjmp (u->self->restore);
  debug_printf ("fork_helper: setjmp returned %d\n", x);

#if 0
  if (0)
    {
      int k;
      unsigned char *p = (unsigned char *)(u->restore[32/4]);
      for (k = 0; k < 11; k++)
	small_printf ("set reg %d %x\n", k *4 , u->restore[k]);
      for (k = 0; k < 11; k++)
	small_printf ("[%02x]", p[k]);
      small_printf ("the res was %d\n", x);
    }
#endif
  
  if (x == 0)
    {
      /* Parent.  */

      dump_jmp_buf (u->self->restore);

#if 0 /* for debugging */
      s->base[0][0] = proc_data_start;
      s->base[0][1] = proc_data_end;
      s->base[1][0] = proc_bss_start;
      s->base[1][1] = proc_bss_end;
      s->base[2][0] = u->base;
      s->base[2][1] = u->ptr;
      s->base[3][0] = &x;
      s->base[3][1] = u->initial_sp; 
#endif 

      /* Tell the child it's being forked and its pid.
	 Remember, *u gets copied to the child's address space.  */
      u->forkee = child->get_pid ();

      /* Fill in fields in the child's process table entry.  */
      child->ppid = u->self->get_pid ();
      child->hThread = pi.hThread;      
      child->hProcess = pi.hProcess;
      child->dwProcessId = pi.dwProcessId;
      child->uid = u->self->uid;
      child->gid = u->self->gid;
      child->sigs = u->self->sigs;
      child->sig_mask = u->self->sig_mask;
      if (copy_mmap_records_to_forkee(child))
	{
	  small_printf ("fork_helper: copy of mmap_records failed\n");
	  set_errno (EAGAIN);
	  syscall_printf ("-1 = fork(), copy of mmap_records failed\n");
	  TerminateProcess (child->hProcess, 1);
	  u->forkee = 0;
          goto cleanup;
	}

      /* Initialize the child's .data and .bss.  */
      rc = copy_memory_to_forkee (pi.hProcess, (char *)proc_data_start, (char *)proc_data_end, 0);
      if (rc)
	rc = copy_memory_to_forkee (pi.hProcess, (char *)proc_bss_start, (char *)proc_bss_end, 1);
      if (! rc)
	{
	  small_printf ("fork_helper: copy of data/bss failed\n");
	  set_errno (EAGAIN);
	  syscall_printf ("-1 = fork(), data/bss copy failed\n");
	  TerminateProcess (child->hProcess, 1);
	  u->forkee = 0;
          goto cleanup;
	}

      u->forkee = 0;

      /* Need to record that task was started by cygwin32 task.
	 This info will then used by the exit() handling code to know whether
	 to reset inuse_p.  Normally inuse_p isn't reset until wait() is called
	 but if the task wasn't started by a cygwin32 task, wait() will never
	 be called and the process table will fill up.  We remove this
         flag if the parent exits. */

      child->inuse_p |= PID_TO_BE_WAITED_FOR;

      /* FIXME: Exit handling code also needs to record that the task ended
	 so the ps command will know about zombies.  */

      /* Start thread, and wait for it to initialize itself.  */
      rc = ResumeThread (child->hThread);
      if (rc != 1)
	{
	  /* Can't resume the thread.  Not sure why this would happen unless
	     there's a bug in the system.  Things seem to be working OK now
	     though, so flag this with EAGAIN, but print a message on the
	     console.  */
	  small_printf ("fork_helper: ResumeThread failed, rc = %d\n", rc);
	  set_errno (EAGAIN);
	  syscall_printf ("-1 = fork(), ResumeThread failed\n");
	  TerminateProcess (child->hProcess, 1);
          goto cleanup;
	}
      debug_printf ("fork_helper: child started\n");

      /* We don't want to wait forever here.  If there's a problem somewhere
	 it'll hang the entire system (since all forks are mutex'd).  If we
	 time out, set errno = EAGAIN and hope the app tries again.  */

      /* We also add child->hProcess to the wait. If the child fails
         to initialize (eg. because of a missing dll). Then this
         handle will become signalled. This stops a *looong* timeout wait.
      */
      HANDLE wait_array[2];
      wait_array[0] = child->hProcess;
      wait_array[1] = forkee_stopped;
      rc = WaitForMultipleObjects (2, wait_array, FALSE, FORK_WAIT_TIMEOUT);
      if (rc == WAIT_ERROR_RC || rc == WAIT_TIMEOUT)
	{
	  if (rc == WAIT_ERROR_RC)
	    small_printf ("fork_helper: WaitForMultipleObjects failed, win32 error %d\n",
			  GetLastError ());
	  else
	    small_printf ("fork_helper: WaitForMultipleObjects timed out\n");
	  set_errno (EAGAIN);
	  syscall_printf ("-1 = fork(), WaitForMultipleObjects failed\n");
	  TerminateProcess (child->hProcess, 1);
          goto cleanup;
	}
      else if(rc == WAIT_OBJECT_0)
        {
          /* Child died. Clean up and exit. */
          DWORD errcode;
          GetExitCodeProcess(child->hProcess, &errcode);
	  small_printf ("fork_helper: child died before initialization with win32 error %d\n", errcode);
	  set_errno (EAGAIN);
          syscall_printf ("fork_helper: Child died before forkee_stopped signalled\n");
          goto cleanup;
	}
      SuspendThread (child->hThread);

      /* Now fill in the stack and heap - this has to be done after 
	 the child is started.  */
      rc = copy_memory_to_forkee (child->hProcess, u->base, u->ptr, 2);
      if (rc)
	rc = copy_memory_to_forkee (child->hProcess, &x, u->initial_sp, 3);
      if (! rc)
	{
	  small_printf ("fork_helper: copy of stack/heap failed\n");
	  set_errno (EAGAIN);
	  syscall_printf ("-1 = fork(), stack/heap copy failed\n");
	  TerminateProcess (child->hProcess, 1);
          goto cleanup;
	}

      if(copy_copyonwrite_mmaps_to_forkee(child->hProcess))
        {
          small_printf("fork_helper: copy_copyonwrite_mmaps_to_forkee failed\n");
	      set_errno (EAGAIN);
	      syscall_printf ("-1 = fork(), copy of copyonwrite memory failed\n");
	      TerminateProcess (child->hProcess, 1);
              goto cleanup;
        }

      /*
       * Now we have started the child we can get rid of the
       * childs fd table from our address space.
       */
      delete [] child_hinfo;
      /* Start the child up again.  */
      SetEvent (forker_stopped);
      ResumeThread (child->hThread);

      res = child->get_pid ();

      unlock_pinfo();
    }
  else
    {
      /* We arrive here via a longjmp from "crt0".  */
      debug_printf ("fork_helper: child is running\n");

      u->self = s->p[x];
      reent_data = u->self->reent_save;
      debug_printf ("fork child: self %p, pid %d, ppid %d\n",
		    u->self, x, u->self->ppid);

      if(recreate_mmaps_after_fork(u->self->mmap_ptr))
        {
          small_printf("fork child: recreate_mmaps_after_fork_failed\n");
          ExitProcess (1);
        }

      /* Tell our parent we've started.  */
      SetEvent (forkee_stopped);

      /* Wait for the parent to fill in our stack and heap.
	 Don't wait forever here.  If our parent dies we don't want to clog
	 the system.  If the wait fails, we really can't continue so exit.  */
      int rc = WaitForSingleObject (forker_stopped, FORK_WAIT_TIMEOUT);
      switch (rc)
	{
	case WAIT_ERROR_RC:
	  small_printf ("fork child: WaitForSingleObject failed, win32 error %d\n",
			GetLastError ());
	  ExitProcess (1);
	case WAIT_TIMEOUT:
	  small_printf ("fork child: WaitForSingleObject timed out\n");
	  ExitProcess (1);
	default:
	  break;
	}

      /* Ensure winsock is enabled for the child. */
      socket_checkinit();
#if 0
      print_checksum (4,   s->base[0][0], s->base[0][1]);
      print_checksum (5,   s->base[1][0], s->base[1][1]);
      print_checksum (6,   s->base[2][0], s->base[2][1]);
      print_checksum (7,   s->base[3][0], s->base[3][1]);
#endif

      res = 0;
    }

  syscall_printf ("%d = fork()\n", res);
  return res;

/* Common cleanup code for failure cases */
cleanup:
  /* Remember to de-allocate the fd table. */
  delete [] child_hinfo;
  child->inuse_p = PID_NOT_IN_USE;
  unlock_pinfo();
  CloseHandle(child->hProcess);
  CloseHandle(child->hThread);
  return -1;
}

/* This hack uses setjmp/longjmp to ensure that the parent's
   registers are all available in the child. We could use
   GetThreadContext/SetThreadContext instead, but I'm sure this
   is much faster. */

static int
__fork ()
{
  jmp_buf b;
  int r;

  if ((r = setjmp (b)) != 0)
    {
      r = r == -2 ? -1 : r == -1 ? 0 : r;
      return r;
    }

  r = cygwin_fork_helper1 (u->data_start,
			   u->data_end,
			   u->bss_start,
			   u->bss_end);

  /* Must convert result to get it through setjmp ok.  */
  longjmp (b, r == -1 ? -2 : r == 0 ? -1 : r);
}

/* Utility to dump a setjmp buf.  */

void
dump_jmp_buf (jmp_buf buf)
{
#ifdef __i386__
  debug_printf ("jmp_buf: eax 0x%x, ebx 0x%x, ecx 0x%x, edx 0x%x\n",
		buf[0], buf[1], buf[2], buf[3]);
  debug_printf ("jmp_buf: esi 0x%x, edi 0x%x, ebp 0x%x, esp 0x%x\n",
		buf[4], buf[5], buf[6], buf[7]);
  short *s = (short *) &buf[9];
  debug_printf ("jmp_buf: es 0x%x, fs 0x%x, gs 0x%x, ss 0x%x\n",
		s[0], s[1], s[2], s[3]);
  debug_printf ("jmp_buf: eip: 0x%x\n", buf[8]);
#endif
}

extern "C"
int
vfork () 
{
  return fork ();
}

extern "C"
int
fork ()
{
  /* FIXME: Was there a real reason for this?  */
  alloca (100);

  int r = __fork ();
  return r;
}

[-- Attachment #3: mmap.cc --]
[-- Type: text/x-c++, Size: 17516 bytes --]

/* mmap.cc

   Copyright 1996, 1997 Cygnus Solutions

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include <stdlib.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <errno.h>
/* STD defines/includes */
#define times __hide_times
#define __THROW_BAD_ALLOC return 0
#include <list.h>
#include <map.h>
#undef times

#include "winsup.h"
#include "fhandler.h"

/* STL includes */
/*
 * Simple class used to keep a record of all current
 * mmap areas in a process. Needed so that
 * they can be duplicated after a fork().
 */

class mmap_record {

private:

  HANDLE mapping_handle_;
  DWORD access_mode_;
  DWORD offset_;
  DWORD size_to_map_;
  void *base_address_;

public:

  mmap_record(HANDLE h, DWORD ac, DWORD o, DWORD s, void *b)
       : mapping_handle_(h), access_mode_(ac), offset_(o),
         size_to_map_(s), base_address_(b) { ; }

  /* Default Copy constructor/operator=/destructor are ok */

  /* Simple accessors */
  HANDLE get_handle() const { return mapping_handle_; }
  DWORD get_access() const { return access_mode_; }
  DWORD get_offset() const { return offset_; }
  DWORD get_size() const { return size_to_map_; }
  void *get_address() const { return base_address_; }
};

/*
 * Code to keep a record of all mmap'ed area's in a process.
 * Needed to duplicate tham in a child of fork().
 * mmap_record classes are kept in an STL list in an STL map, keyed
 * by file descriptor. This is *NOT* duplicated accross a fork(), it
 * needs to be specially handled by the fork code.
 */

static map< int, list<mmap_record> * > *mmapped_areas;

extern "C" {

caddr_t mmap(caddr_t addr, size_t len, int prot, int flags, int fd, off_t off)
{
  syscall_printf ("mmap: addr = %x, len = %d, prot = %x, flags = %x, fd = %d, off = %d\n",
                   addr, len, prot, flags, fd, off);

  BOOL is_win95 = windows_95();

  /* No fixed addresses on windows 95 - loser OS */
  if(is_win95 && (flags & MAP_FIXED)) {
    set_errno(EINVAL);
    syscall_printf ("-1 = mmap(): win95 and MAP_FIXED\n");
    return (caddr_t)-1;
  }

  if(mmapped_areas == 0) {
    /* First mmap call, create STL map */
    mmapped_areas = new map< int, list<mmap_record> * >;
    if(mmapped_areas == 0) {
      set_errno(ENOMEM);
      syscall_printf ("-1 = mmap(): ENOMEM\n");
      return (caddr_t)-1;
    }
  }

  DWORD access = (prot & PROT_WRITE) ? FILE_MAP_WRITE : FILE_MAP_READ;
  if (flags & MAP_PRIVATE) access = FILE_MAP_COPY;
  DWORD protect;
  
  if( access & FILE_MAP_COPY )
    protect = PAGE_WRITECOPY;
  else if( access & FILE_MAP_WRITE)
    protect = PAGE_READWRITE;
  else
    protect = PAGE_READONLY;

  SECURITY_ATTRIBUTES sa;
  sa.nLength = sizeof (SECURITY_ATTRIBUTES);
  sa.bInheritHandle = TRUE;
  sa.lpSecurityDescriptor = 0;

  HANDLE hFile;
  if(fd == -1)
    hFile = (HANDLE)0xFFFFFFFF;
  else {
    /* Ensure that fd is open */
    if(NOT_OPEN_FD (fd)) {
      set_errno(EBADF);
      syscall_printf ("-1 = mmap(): EBADF\n");
      return (caddr_t)-1;
    }
    hinfo_vec *hvec = &u->self->hmap;
    hFile = (*hvec)[fd].h->get_handle();
  }
  HANDLE h = CreateFileMapping( hFile, &sa, protect, 0, len, NULL);
  if(h == 0) {
    __seterrno();
    syscall_printf ("-1 = mmap(): CreateFileMapping failed with %d\n", GetLastError());
    return (caddr_t)-1;
  }

  void *base;

  if(flags & MAP_FIXED) {
    base = MapViewOfFileEx( h, access, 0, off, len, addr);
    if(base != addr) {
      __seterrno();
      syscall_printf ("-1 = mmap(): MapViewOfFileEx failed with %d\n", GetLastError());
      CloseHandle(h);
      return (caddr_t)-1;
    }
  } else {
    base = MapViewOfFile( h, access, 0, off, len);
    if(base == 0) {
      __seterrno();
      syscall_printf ("-1 = mmap(): MapViewOfFile failed with %d\n", GetLastError());
      CloseHandle(h);
      return (caddr_t)-1;
    }
  }

  /* Ok, so we have a successfully mmap'ed area.
     Now save it so that a fork'ed child can reproduce it.
  */

  mmap_record mmap_rec(h, access, off, len, base);

  /* Get the list of mmapped areas for this fd, create a new
     one if one doesn't exist yet. 
  */

  list<mmap_record> *l = (*mmapped_areas)[fd];
  if(l == 0) {
    /* Create a new one */
    l = new list<mmap_record>;
    if(l == 0) {
      UnmapViewOfFile(base);
      CloseHandle(h);
      set_errno(ENOMEM);
      syscall_printf ("-1 = mmap(): ENOMEM\n");
      return (caddr_t)-1;
    }
    (*mmapped_areas)[fd] = l;
  }

  /* Insert into the list */
  l->push_front(mmap_rec);

  syscall_printf ("%x = mmap() succeeded\n", base);
  return (caddr_t)base;
}

/*
 * Call to remove an mmap'ed area. Insists that base requested
 * is the same as that mmap'ed, error if not.
 */

int munmap( caddr_t addr, size_t len)
{
  syscall_printf ("munmap(addr = %x, len = %d)\n", addr, len);

  /*
   * Check if a mmap'ed area was ever created.
   */
  if(mmapped_areas == 0) {
    syscall_printf ("-1 = munmap(): mmapped_areas == 0\n");
    set_errno(EINVAL);
    return -1;
  }

  /*
   * Iterate through the map, looking for the mmap'ed area.
   * Error if not found.
   */

  map< int, list<mmap_record> * >::iterator it;

  for( it = mmapped_areas->begin(); it != mmapped_areas->end(); ++it)
    {
      list<mmap_record> *l = (*it).second;
      if(l != 0)
        {
          list<mmap_record>::iterator li;
          for( li = l->begin(); li != l->end(); ++li)
            {
              mmap_record rec = *li;
              if(rec.get_address() == addr)
                {
                  /* Unmap the area */
                  UnmapViewOfFile(addr);
                  CloseHandle(rec.get_handle());
                  /* Delete the entry. */
                  l->erase(li);
                  syscall_printf ("0 = munmap(): %x\n", addr);
                  return 0;
                }
             }
         }
     }
  set_errno(EINVAL);

  syscall_printf ("-1 = munmap(): EINVAL\n");

  return -1;
}                

/*
 * Sync file with memory. Ignore flags for now.
 */

int msync( caddr_t addr, size_t len, int flags)
{
  syscall_printf("msync: addr = %x, len = %d, flags = %x\n",
                  addr, len, flags);

  if(FlushViewOfFile(addr, len) == 0)
    {
      syscall_printf("-1 = msync: LastError = %x\n", GetLastError());
      __seterrno();
      return -1;
    }
  syscall_printf("0 = msync\n");
  return 0;
}

/*
 * Set memory protection.
 */

int mprotect(caddr_t addr, size_t len, int prot)
{
  DWORD old_prot;
  DWORD new_prot = 0;

  syscall_printf("mprotect(addr = %x, len = %d, prot = %x)\n",
                  addr, len, prot);

  if(prot & PROT_NONE)
    new_prot = PAGE_NOACCESS;
  else 
    {
      switch(prot)
        {
          case PROT_READ|PROT_WRITE|PROT_EXEC:
            new_prot = PAGE_EXECUTE_READWRITE;
            break;
          case PROT_READ|PROT_WRITE:
            new_prot = PAGE_READWRITE;
            break;
          case PROT_READ|PROT_EXEC:
            new_prot = PAGE_EXECUTE_READ;
            break;
          case PROT_READ:
            new_prot = PAGE_READONLY;
            break;
          default:
            syscall_printf("-1 = mprotect(): invalid prot value\n");
            set_errno(EINVAL);
            return -1;
         }
     }
  if(VirtualProtect(addr, len, prot, &old_prot)== 0)
    {
      __seterrno();
      syscall_printf("-1 = mprotect(): lasterror = %x\n", GetLastError());
      return -1;
    }
  syscall_printf("0 = mprotect()\n");
  return 0;
}

};


/*
 * Called by the parent process durring a fork.  At this point, the child has
 * not yet run.  When it does run and initialize, we want it to re-establish
 * the same mmap regions that we have.  Because our heap, where our
 * mmap_records are stored, will not have been copied to the child at the time
 * it initializes, we can't simply pass it the "mmapped_areas" pointer.
 * Instead, we must copy the contents of our "mmapped_areas" into an array of
 * mmap_records where the child process can read them.  The "child->mmap_ptr"
 * is set to a handle for the memory where the the child will find the array.
 * After we return, we will suspend and the child will initialize and call
 * "recreate_mmaps_after_fork" below.
 */

int copy_mmap_records_to_forkee(pinfo *child)
{
  /*
   * Count the number of mmap_record entries are in the "mmapped_areas" map.
   */
  unsigned count = 0;
  map< int, list<mmap_record> * >::iterator it;
  if (mmapped_areas != NULL)
  {
    for( it = mmapped_areas->begin(); it != mmapped_areas->end(); ++it)
    {
      list<mmap_record> *l = (*it).second;
      if(l != 0)
      {
        list<mmap_record>::iterator li;
        for( li = l->begin(); li != l->end(); ++li)
            count++;
      }
    }
  }

  /*
   * Return with "child->mmap_ptr == NULL"; there are no mmap regions.
   */
  if (count == 0)
  {
    child->mmap_ptr = NULL;
    debug_printf("copy_mmap_records_to_forkee: succeeded\n");
    return 0;
  }

  /*
   * Create a file mapping that will be made available to the child process
   * and will hold an array of all the mmap_records in "mmapped_areas".
   */
  HANDLE h = CreateFileMapping((HANDLE)-1, NULL, PAGE_READWRITE, 0,
                               (count+1)*sizeof(mmap_record), NULL);
  if (h == NULL)
  {
    syscall_printf("copy_mmap_records_to_forkee: CreateFileMapping failed\n");
    return -1;
  }

  mmap_record* pRec = (mmap_record*)MapViewOfFile(h, FILE_MAP_WRITE, 0, 0, 0);
  if (pRec == NULL)
  {
    CloseHandle(h);
    syscall_printf("copy_mmap_records_to_forkee: MapViewOfFile failed\n");
    return -1;
  }

  /*
   * Now copy all the mmap_records into the mapped memory array.
   * The count of elements in this array are in the first record
   * along with some other useful info.
   */
  *pRec = mmap_record(0, 0, (DWORD)mmapped_areas, count, pRec);
  if (mmapped_areas != NULL)
  {
    for( it = mmapped_areas->begin(); it != mmapped_areas->end(); ++it)
    {
      list<mmap_record> *l = (*it).second;
      if(l != 0)
      {
        list<mmap_record>::iterator li;
        for( li = l->begin(); li != l->end(); ++li)
          *(++pRec) = *li;
      }
    }
  }

  /*
   * Duplicate the handle for use by the child process. The child process
   * will use the duplicate handle in recreate_mmaps_after_fork.
   * We also unmap our view and close our handle, since we're done with it.
   */
  UnmapViewOfFile(pRec - count);
  DuplicateHandle(GetCurrentProcess(), h, child->hProcess,
                  (LPHANDLE)&child->mmap_ptr, FILE_MAP_READ,
                  FALSE, DUPLICATE_CLOSE_SOURCE);

  debug_printf("copy_mmap_records_to_forkee: succeeded\n");
  return 0;
}


/*
 * Call to re-create all the file mappings in a fork()'ed child.  Called from
 * "cygwin_fork_helper1" by the child durring initialization.  At this point,
 * the parent has duplicated its .data and .bss, but not its stack or heap.
 * We are passed a handle to memory via "u->self->mmap_ptr" where an array of
 * mmap_records can be found.  This was put there by our parent in its call
 * to "copy_mmap_records_to_forkee".  The first record in the array contains
 * information like the count of records.  All the HANDLE's in those records
 * are valid for us (we inherited them), but none of the mapped areas
 * are in our address space.  We need to iterate through the array and do the
 * MapViewOfFileEx calls.  Initially, the "mmapped_areas" pointer is assumed to
 * be NULL, since this process was just created.  Before returning, both
 * "mmapped_areas" and "u->self->mmap_ptr" are set to point to the mmap data
 * structure in the heap.  This pointer is also passed to us in the first
 * mmap_record.  Remember, that pointer refers to heap memory which the parent
 * process hasn't yet copied to us.  After we return, we will suspend while we
 * wait for our parent to copy our heap (so that "mmaped_areas" is valid).
 * All of our mmap regions, except the ones that the parent created with
 * FILE_MAP_COPY, will refer to the same physical memory that the cooresponding
 * region in our parent refers to.  In the case of FILE_MAP_COPY regions, we
 * will see the contents of the original file at the time the parent mmapped it.
 * Any changes made by the parent to these regions will not be seen by us.  So,
 * the parent will also need to call "copy_copyonwrite_mmaps_to_forkee" below so
 * that we are in sync with the parent immediately after the fork.
 */

int recreate_mmaps_after_fork(void *param)
{
  if (param != NULL)
{

  HANDLE h = (HANDLE)param;
  mmap_record* pRec = (mmap_record*)MapViewOfFile(h, FILE_MAP_READ, 0, 0,
                       sizeof(mmap_record));
  if (pRec == NULL)
  {
    small_printf("-1 = recreate_mmaps_after_fork(): MapViewOfFile failed "
                 "with GetLastError = %x\n", GetLastError());
    return -1;
  }

  /*
   * Copy the information that was put in the first record
   * (see set_child_mmap_ptr).
   */
  void* pRecDesired = pRec->get_address();
  unsigned count = pRec->get_size();
  mmapped_areas = (map< int, list<mmap_record> * > *)pRec->get_offset();

  /*
   * We need to have the array of mmap_records at the address "pRecDesired".
   * Otherwise, this array may be overlapping one of the mapped regions
   * in the parent that we are trying to duplicate.  It is unlikely that
   * we are so lucky as to have "pRec" mapped correctly the first time, so
   * unmap this view and re-map it using MapViewOfFileEx.
   */
  UnmapViewOfFile(pRec);

  if (count > 0)
  {
    pRec = (mmap_record*)MapViewOfFileEx(h, FILE_MAP_READ, 0, 0, 0, pRecDesired);
    if (pRec == NULL)
    {
      small_printf("-1 = recreate_mmaps_after_fork(): MapViewOfFileEx failed "
                   "with GetLastError = %x\n", GetLastError());
      return -1;
    }

    if (pRec != pRecDesired)
    {
      small_printf("-1 = recreate_mmaps_after_fork(): MapViewOfFileEx wrong address\n");
      return -1;
    }

    /*
     * Copy the "pRec" mmap_record array into the
     * "mmaped_areas" map of mmap_record lists.
     */
    while (count-- > 0)
    {
      ++pRec;

      /* Now re-create the MapViewOfFileEx call */
      void* base = MapViewOfFileEx(pRec->get_handle(), pRec->get_access(),
                                   0, pRec->get_offset(),
                                   pRec->get_size(), pRec->get_address());
      if(base != pRec->get_address())
      {
        small_printf("recreate_mmaps_after_fork: base address %x\
            fails to match requested address %x\n", base, pRec->get_address());
        return -1;
      }

      debug_printf("recreate_mmaps_after_fork: h = %x, access = %x, offset = %d, \
          size = %d, address = %x\n", pRec->get_handle(), pRec->get_access(),
          pRec->get_offset(), pRec->get_size(), pRec->get_address());
    }

    UnmapViewOfFile(pRecDesired);
  }
  CloseHandle(h);

}

  /* Now set our mmap record in case the child forks. */
  u->self->mmap_ptr = mmapped_areas;

  debug_printf("recreate_mmaps_after_fork: succeeded\n");
  return 0;
}


/*
 * Called by the parent process durring a fork.  At this point,
 * the address space of the child process has been established;
 * that is, the child has already called "recreate_mmaps_after_fork".
 * Any mmap region created with "FILE_MAP_COPY" will need to be
 * copied to the cooresponding memory addresses in the child process,
 * or else the parent and child may not see the same contents at those
 * addresses.  They will be different for those FILE_MAP_COPY regions
 * that the parent has written to (unless the parent wrote what was
 * there originally).
 */

extern int copy_memory_to_forkee (HANDLE child, void *low, void *high, int);

int copy_copyonwrite_mmaps_to_forkee(HANDLE hChild)
{
  /*
   * Iterate through the array if mmap_records and copy those entries
   * that have FILE_MAP_COPY set.
   */
  map< int, list<mmap_record> * >::iterator it;
  if (mmapped_areas != NULL)
  {
    for( it = mmapped_areas->begin(); it != mmapped_areas->end(); ++it)
    {
      list<mmap_record> *l = (*it).second;
      if(l != 0)
      {
          list<mmap_record>::iterator li;
          for( li = l->begin(); li != l->end(); ++li)
          {
            mmap_record& rec = *li;
            char* start = (char*)rec.get_address() + rec.get_offset();

            if ((rec.get_access() & FILE_MAP_COPY) &&
                !copy_memory_to_forkee(hChild, start, start + rec.get_size(), 0))
            {
              syscall_printf("copy_copyonwrite_mmaps_to_forkee: copy_memory_to_forkee failed\n");
              return -1;
            }

	        debug_printf("copy_copyonwrite_mmaps_to_forkee: h = %x, "
                "access = %x, offset = %d, size = %d, address = %x\n",
                rec.get_handle(), rec.get_access(), rec.get_offset(),
                rec.get_size(), rec.get_address());
          }
      }
    }
  }

  debug_printf("copy_copyonwrite_mmaps_to_forkee: succeeded\n");
  return 0;
}

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~1997-08-27  7:06 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1997-08-24 19:22 Bug with Fix: mmap(MAP_PRIVATE) region handling in fork() Gary Fuehrer
1997-08-27  7:06 Gary Fuehrer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).