/* Copyright (C) 1992 Free Software Foundation, Inc.
This file is part of the GNU Hurd.

The GNU Hurd 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 1, or (at your option)
any later version.

The GNU Hurd 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 the GNU Hurd; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* Written by Roland McGrath.  */

#include <hurd.h>
#include <string.h>

/* Receive right for the bootstrap port of the translator we just started.  */
static mach_port_t waiting_bootstrap;
static file_t waiting_realnode;	/* Port to give it.  */
/* Reply port for fsys_getroot call that started the translator.  */
static mig_reply_port_t waiting_getroot;
static int waiting_getroot_flags; /* Flags from that call.  */

/* Control port for active translator.  */
mach_port_t tfs_translator_cntl = MACH_PORT_NULL;

error_t
tfs_fsys_getroot (fsys_t fsys,
		  mig_reply_port_t reply,
		  int flags,
		  file_t auth_realnode,
		  file_t *file)
{
  file_t rootnode;
  error_t err;

  if (err = tfs_create_user (flags, auth_realnode, &rootnode))
    return err;

  if (!(flags & FS_LOOKUP_NOTRANS))
    {
      if (tfs_translator_cntl != MACH_PORT_NULL)
	/* There is already a translator running.  */
	err = fsys_getroot (tfs_translator_cntl,
			    flags,
			    rootnode,
			    &rootnode);
      else
	{
	  /* See if we need to start up a translator.  */

	  char buf[2048], *bufp = buf, *p;
	  size_t len = sizeof (buf);

	  /* XXX Assumes tfs_create_user keeps AUTH_REALNODE alive.  */
	  err = file_get_translator (auth_realnode, &bufp, &len);
	  if (!err)
	    {
	      p = memchr (bufp, '\0', len);
	      if (p != NULL && ++p - bufp < len)
		{
		  /* There is a translator.  Start it up.  */
		  
		  file_t translator;
		  char *end, *transname;
		  mach_port_t ports[INIT_PORT_MAX], proc;
		  int ints[INIT_INT];
		  
		  end = memchr (p, '\0', bufp + len - p);
		  if (end == NULL)
		    {
		      transname = alloca (bufp + len - p + 1);
		      memcpy (transname, p, bufp + len - p);
		      transname[bufp + len - p] = '\0';
		    }
		  else
		    {
		      transname = p;
		      ++end;
		    }
		  
		  translator = path_lookup (transname, FS_LOOKUP_EXEC, 0);
		  if (translator == MACH_PORT_NULL)
		    err = errno;
		  else
		    {
		      ports[INIT_PORT_CCDIR] = getccdir ();
		      ports[INIT_PORT_CWDIR] = getcwdir ();
		      ports[INIT_PORT_CRDIR] = getcrdir ();
		      ports[INIT_PORT_AUTH] = getauth ();
		      ports[INIT_PORT_PROC] = MACH_PORT_NULL;
		      mach_port_allocate (mach_task_self (),
					  MACH_PORT_RIGHT_RECEIVE,
					  &waiting_bootstrap);
		      mach_port_insert_right (mach_task_self (),
					      MACH_PORT_RIGHT_SEND,
					      waiting_bootstrap);
		      ports[INIT_PORT_BOOTSTRAP] = waiting_bootstrap;
		      /* XXX logincoll */
		      ints[INIT_UMASK] = getumask ();
		      ints[INIT_CTTY_FSTYPE] = 0;
		      ints[INIT_CTTY_FSID1] = 0;
		      ints[INIT_CTTY_FSID2] = 0;
		      ints[INIT_CTTY_FILEID] = 0;
		      ints[INIT_SIGMASK] = sigblock (0);
		      sigignored ((sigset_t *) &ints[INIT_SIGIGN]); /* XXX */
		      err = file_exec (translator,
				       MACH_PORT_NULL, EXEC_NEWTASK,
				       p, bufp + len - p,
				       NULL, 0, /* No environment.  */
				       NULL, 0, /* No descriptor table.  */
				       ports, INIT_PORT_MAX,
				       ints, INIT_INT_MAX);
		      mach_port_deallocate (mach_task_self (),
					    ports[INIT_PORT_CCDIR]);
		      mach_port_deallocate (mach_task_self (),
					    ports[INIT_PORT_CWDIR]);
		      mach_port_deallocate (mach_task_self (),
					    ports[INIT_PORT_CRDIR]);
		      mach_port_deallocate (mach_task_self (),
					    ports[INIT_PORT_AUTH]);
		      mach_port_deallocate (mach_task_self (),
					    ports[INIT_PORT_BOOTSTRAP]);
		      if (!err)
			{
			  /* We will reply when the translator sends us
			     fsys_startup on its bootstrap port.  */
			  
			  waiting_realnode = rootnode;
			  waiting_getroot = reply;
			  waiting_getroot_flags = flags & ~FS_LOOKUP_NOTRANS;
			  
			  mach_port_request_notification
			    (mach_task_self (),
			     waiting_bootstrap,
			     MACH_NOTIFY_NO_SENDERS,
			     0, /* ? */
			     waiting_bootstrap,
			     NULL);
			  mach_port_move_member (mach_task_self (),
						 waiting_bootstrap,
						 tfs_request_portset);
			  return MIG_NO_REPLY;
			}
		    }
		}
	    }
	}
    }

  if (err)
    mach_port_deallocate (mach_task_self (), rootnode);
  else
    *file = rootnode;
  return err;
}

error_t
tfs_fsys_startup (mach_port_t bootstrap,
		  fsys_t cntl,
		  file_t *realnode,
		  file_t *dotdot)
{
  error_t err;

  if (bootstrap != waiting_bootstrap)
    return EOPNOTSUPP;

  if (cntl == MACH_PORT_NULL)
    return EINVAL;

  if (err = tfs_create_user (~0, MACH_PORT_NULL, realnode))
    return err;
  *dotdot = tfs_dotdot;

  /* We now have an active translator.  */
  tfs_translator_cntl = cntl;

  /* Send the pending fsys_getroot on to the translator;
     it will get the request after we return to it.  */
  fsys_getroot (cntl,
		waiting_getroot,
		waiting_getroot_flags,
		waiting_realnode,
		NULL);

  return 0;
}

/* XXX should be called when we get a no-senders notification for
   `waiting_bootstrap'.  */
void
tfs_waiting_bootstrap_no_senders ()
{
  mach_port_destroy (mach_task_self (), waiting_bootstrap);
  mach_port_deallocate (mach_task_self (), waiting_realnode);
  mach_port_deallocate (mach_task_self (), waiting_getroot);
}
