Пример #1
0
/* Implement io_reathenticate as described in <hurd/io.defs>. */
kern_return_t
diskfs_S_io_reauthenticate (struct protid *cred,
			    mach_port_t rend_port)
{
  struct protid *newcred;
  error_t err;
  mach_port_t newright;
  struct iouser *user;

  if (cred == 0)
    return EOPNOTSUPP;

  /* This routine must carefully ignore EINTR because we
     are a simpleroutine, so callers won't know to restart. */

  pthread_mutex_lock (&cred->po->np->lock);
  refcount_ref (&cred->po->refcnt);
  do
    err = diskfs_start_protid (cred->po, &newcred);
  while (err == EINTR);
  if (err)
    {
      refcount_deref (&cred->po->refcnt);
      pthread_mutex_unlock (&cred->po->np->lock);
      return err;
    }

  newright = ports_get_send_right (newcred);
  assert (newright != MACH_PORT_NULL);

  err = iohelp_reauth (&user, diskfs_auth_server_port, rend_port,
		       newright, 1);
  if (! err)
    {
      diskfs_finish_protid (newcred, user);
      iohelp_free_iouser (user);
      mach_port_deallocate (mach_task_self (), rend_port);
    }

  mach_port_deallocate (mach_task_self (), newright);

  pthread_mutex_unlock (&cred->po->np->lock);

  ports_port_deref (newcred);

  return err;
}
Пример #2
0
error_t
fshelp_get_identity (struct port_bucket *bucket,
		     ino_t fileno,
		     mach_port_t *pt)
{
  struct idspec *i;
  error_t err = 0;

  error_t check_port (void *arg)
    {
      struct idspec *i = arg;
      if (i->fileno == fileno)
	{
	  *pt = ports_get_right (i);
	  return 1;
	}
      else
	return 0;
    }

  mutex_lock (&idlock);
  if (!idclass)
    id_initialize ();

  *pt = MACH_PORT_NULL;

  ports_class_iterate (idclass, check_port);

  if (*pt != MACH_PORT_NULL)
    {
      mutex_unlock (&idlock);
      return 0;
    }

  err = ports_create_port (idclass, bucket, sizeof (struct idspec), &i);
  if (err)
    {
      mutex_unlock (&idlock);
      return err;
    }
  i->fileno = fileno;

  *pt = ports_get_right (i);
  ports_port_deref (i);
  mutex_unlock (&idlock);
  return 0;
}
Пример #3
0
/* Return a new connection from a socket previously listened.  */
error_t
S_socket_accept (struct sock_user *user,
                 mach_port_t *port, mach_msg_type_name_t *port_type,
                 mach_port_t *peer_addr_port,
                 mach_msg_type_name_t *peer_addr_port_type)
{
    error_t err;
    struct sock *sock;

    if (!user)
        return EOPNOTSUPP;

    sock = user->sock;

    err = ensure_connq (sock);
    if (!err)
    {
        struct timespec noblock = {0, 0};
        struct sock *peer_sock;

        err = connq_listen (sock->listen_queue,
                            (sock->flags & PFLOCAL_SOCK_NONBLOCK) ? &noblock : NULL,
                            &peer_sock);
        if (!err)
        {
            struct addr *peer_addr;
            *port_type = MACH_MSG_TYPE_MAKE_SEND;
            err = sock_create_port (peer_sock, port);
            if (!err)
                err = sock_get_addr (peer_sock, &peer_addr);
            if (!err)
            {
                *peer_addr_port = ports_get_right (peer_addr);
                *peer_addr_port_type = MACH_MSG_TYPE_MAKE_SEND;
                ports_port_deref (peer_addr);
            }
            else
            {
                /* TEAR DOWN THE CONNECTION XXX */
            }
        }
    }

    return err;
}
Пример #4
0
void
ports_no_senders (void *portstruct,
		  mach_port_mscount_t mscount)
{
  struct port_info *pi = portstruct;
  int dealloc;
  mach_port_t old;

  pthread_mutex_lock (&_ports_lock);
  if ((pi->flags & PORT_HAS_SENDRIGHTS) == 0)
    {
      pthread_mutex_unlock (&_ports_lock);
      return;
    }
  if (mscount >= pi->mscount)
    {
      dealloc = 1;
      pi->flags &= ~PORT_HAS_SENDRIGHTS;
    }
  else
    {
      /* Request a new notification.  The sync value is because
       we might have accounted for a new sender but not actually
       made the send right yet.  */
      mach_port_request_notification (mach_task_self (), pi->port_right,
				      MACH_NOTIFY_NO_SENDERS, pi->mscount,
				      pi->port_right,
				      MACH_MSG_TYPE_MAKE_SEND_ONCE, &old);
      if (old)
	mach_port_deallocate (mach_task_self (), old);
      dealloc = 0;
    }
  pthread_mutex_unlock (&_ports_lock);
  
  if (dealloc)
    {
      ports_interrupt_notified_rpcs (portstruct, pi->port_right,
				     MACH_NOTIFY_NO_SENDERS);
      ports_interrupt_rpcs (pi);
      ports_port_deref (pi);
    }
}
Пример #5
0
/* Find out the name of a socket.  */
error_t
S_socket_name (struct sock_user *user,
               mach_port_t *addr_port, mach_msg_type_name_t *addr_port_type)
{
    error_t err;
    struct addr *addr;

    if (!user)
        return EOPNOTSUPP;

    err = sock_get_addr (user->sock, &addr);
    if (err)
        return err;

    *addr_port = ports_get_right (addr);
    *addr_port_type = MACH_MSG_TYPE_MAKE_SEND;
    ports_port_deref (addr);

    return 0;
}
Пример #6
0
error_t
S_ifsock_getsockaddr (file_t sockfile,
		      mach_port_t *address)
{
  struct trivfs_protid *cred = ports_lookup_port (port_bucket, sockfile,
						  node_class);
  int perms;
  error_t err;

  if (!cred)
    return EOPNOTSUPP;

  err = file_check_access (cred->realnode, &perms);
  if (!err && !(perms & O_READ))
    err = EACCES;

  if (!err)
    *address = address_port;
  ports_port_deref (cred);
  return err;
}
Пример #7
0
error_t
netfs_S_dir_mkfile (struct protid *diruser, int flags, mode_t mode, 
		    mach_port_t *newfile, mach_msg_type_name_t *newfiletype)
{
  error_t err;
  struct node *np;
  struct iouser *user;
  struct protid *newpi;

  pthread_mutex_lock (&diruser->po->np->lock);
  err = netfs_attempt_mkfile (diruser->user, diruser->po->np, mode, &np);

  if (!err)
    {
      /* the dir is now unlocked and NP is locked */
      flags &= OPENONLY_STATE_MODES;
      err = iohelp_dup_iouser (&user, diruser->user);
      if (! err)
        {
          newpi = netfs_make_protid (netfs_make_peropen (np, flags,
							 diruser->po),
				     user);
	  if (newpi)
	    {
	      *newfile = ports_get_right (newpi);
	      *newfiletype = MACH_MSG_TYPE_MAKE_SEND;
	      ports_port_deref (newpi);
	    }
	  else
	    {
	      err = errno;
	      iohelp_free_iouser (user);
	    }
	}
      netfs_nput (np);
    }

  return err;
}
Пример #8
0
error_t
netfs_S_io_restrict_auth (struct protid *user,
			  mach_port_t *newport,
			  mach_msg_type_name_t *newporttype,
			  uid_t *uids,
			  mach_msg_type_number_t nuids,
			  gid_t *gids,
			  mach_msg_type_number_t ngids)
{
  error_t err;
  struct protid *newpi;
  struct iouser *new_user;

  if (!user)
    return EOPNOTSUPP;

  err = iohelp_restrict_iouser (&new_user, user->user,
				uids, nuids, gids, ngids);
  if (err)
    return err;

  refcount_ref (&user->po->refcnt);
  newpi = netfs_make_protid (user->po, new_user);
  if (newpi)
    {
      *newport = ports_get_right (newpi);
      *newporttype = MACH_MSG_TYPE_MAKE_SEND;
    }
  else
    {
      refcount_deref (&user->po->refcnt);
      iohelp_free_iouser (new_user);
      err = ENOMEM;
    }

  ports_port_deref (newpi);
  return err;
}
Пример #9
0
error_t
ports_transfer_right (void *tostruct, 
		      void *fromstruct)
{
  struct port_info *topi = tostruct;
  struct port_info *frompi = fromstruct;
  mach_port_t port;
  int dereffrompi = 0;
  int dereftopi = 0;
  int hassendrights = 0;
  error_t err;

  mutex_lock (&_ports_lock);

  /* Fetch the port in FROMPI and clear its use */
  port = frompi->port_right;
  if (port != MACH_PORT_NULL)
    {
      hurd_ihash_locp_remove (&frompi->bucket->htable, frompi->hentry);
      frompi->port_right = MACH_PORT_NULL;
      if (frompi->flags & PORT_HAS_SENDRIGHTS)
	{
	  frompi->flags &= ~PORT_HAS_SENDRIGHTS;
	  hassendrights = 1;
	  dereffrompi = 1;
	}
    }
  
  /* Destroy the existing right in TOPI. */
  if (topi->port_right != MACH_PORT_NULL)
    {
      hurd_ihash_locp_remove (&topi->bucket->htable, topi->hentry);
      err = mach_port_mod_refs (mach_task_self (), topi->port_right,
				MACH_PORT_RIGHT_RECEIVE, -1);
      assert_perror (err);
      if ((topi->flags & PORT_HAS_SENDRIGHTS) && !hassendrights)
	{
	  dereftopi = 1;
	  topi->flags &= ~PORT_HAS_SENDRIGHTS;
	}
      else if (((topi->flags & PORT_HAS_SENDRIGHTS) == 0) && hassendrights)
	{
	  topi->flags |= PORT_HAS_SENDRIGHTS;
	  topi->refcnt++;
	}
    }
  
  /* Install the new right in TOPI. */
  topi->port_right = port;
  topi->cancel_threshold = frompi->cancel_threshold;
  topi->mscount = frompi->mscount;
  
  if (port)
    {
      hurd_ihash_add (&topi->bucket->htable, port, topi);
      if (topi->bucket != frompi->bucket)
        {
	  err = mach_port_move_member (mach_task_self (), port,
				       topi->bucket->portset);
	  assert_perror (err);
	}
    }
  
  mutex_unlock (&_ports_lock);
  
  /* Take care of any lowered reference counts. */
  if (dereffrompi)
    ports_port_deref (frompi);
  if (dereftopi)
    ports_port_deref (topi);
  return 0;
}
Пример #10
0
kern_return_t
trivfs_S_dir_lookup (struct trivfs_protid *cred,
		     mach_port_t reply, mach_msg_type_name_t reply_type,
		     char *filename,
		     int flags,
		     mode_t mode,
		     retry_type *retry_type,
		     char *retry_name,
		     mach_port_t *retrypt,
		     mach_msg_type_name_t *retrypt_type)
{
  int perms;
  error_t err;
  struct trivfs_protid *newcred;

  if (!cred)
    return EOPNOTSUPP;

  if (filename[0])
    return ENOTDIR;

  /* This is a null-pathname "reopen" call; do the right thing. */

  /* Burn off flags we don't actually implement */
  flags &= O_HURD;
  flags &= ~(O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS);

  /* Validate permissions */
  if (! trivfs_check_access_hook)
    file_check_access (cred->realnode, &perms);
  else
    (*trivfs_check_access_hook) (cred->po->cntl, cred->user,
				 cred->realnode, &perms);
  if ((flags & (O_READ|O_WRITE|O_EXEC) & perms)
      != (flags & (O_READ|O_WRITE|O_EXEC)))
    return EACCES;

  /* Execute the open */
  err = 0;
  if (trivfs_check_open_hook)
    err = (*trivfs_check_open_hook) (cred->po->cntl, cred->user, flags);
  if (!err)
    {
      struct iouser *user;

      err = iohelp_dup_iouser (&user, cred->user);
      if (err)
	return err;

      err = trivfs_open (cred->po->cntl, user, flags,
			 cred->realnode, &newcred);
      if (err)
	iohelp_free_iouser (user);
      else
	mach_port_mod_refs (mach_task_self (), cred->realnode,
			    MACH_PORT_RIGHT_SEND, +1);
    }
  if (err)
    return err;

  *retry_type = FS_RETRY_NORMAL;
  *retry_name = '\0';
  *retrypt = ports_get_right (newcred);
  *retrypt_type = MACH_MSG_TYPE_MAKE_SEND;
  ports_port_deref (newcred);
  return 0;
}
Пример #11
0
/* Return in FILE & FILE_TYPE the file in FSYS corresponding to the NFS file
   handle HANDLE & HANDLE_LEN.  */
error_t
diskfs_S_fsys_getfile (mach_port_t fsys,
		       mach_port_t reply, mach_msg_type_name_t reply_type,
		       uid_t *uids, mach_msg_type_number_t nuids,
		       gid_t *gids, mach_msg_type_number_t ngids,
		       char *handle, mach_msg_type_number_t handle_len,
		       mach_port_t *file, mach_msg_type_name_t *file_type)
{
  int flags;
  error_t err;
  struct node *node;
  const union diskfs_fhandle *f;
  struct protid *new_cred;
  struct peropen *new_po;
  struct iouser *user;
  struct port_info *pt =
    ports_lookup_port (diskfs_port_bucket, fsys, diskfs_control_class);

  if (!pt)
    return EOPNOTSUPP;

  if (handle_len != sizeof *f)
    {
      ports_port_deref (pt);
      return EINVAL;
    }

  f = (const union diskfs_fhandle *) handle;

  err = diskfs_cached_lookup (f->data.cache_id, &node);
  if (err)
    {
      ports_port_deref (pt);
      return err;
    }

  if (node->dn_stat.st_gen != f->data.gen)
    {
      diskfs_nput (node);
      ports_port_deref (pt);
      return ESTALE;
    }

  err = iohelp_create_complex_iouser (&user, uids, nuids, gids, ngids);
  if (err)
    {
      diskfs_nput (node);
      ports_port_deref (pt);
      return err;
    }

  flags = 0;
  if (! fshelp_access (&node->dn_stat, S_IREAD, user))
    flags |= O_READ;
  if (! fshelp_access (&node->dn_stat, S_IEXEC, user))
    flags |= O_EXEC;
  if (! fshelp_access (&node->dn_stat, S_IWRITE, user)
      && ! S_ISDIR (node->dn_stat.st_mode)
      && ! diskfs_check_readonly ())
    flags |= O_WRITE;

  err = diskfs_make_peropen (node, flags, 0, &new_po);
  if (! err)
    {
      err = diskfs_create_protid (new_po, user, &new_cred);
      if (err)
	diskfs_release_peropen (new_po);
    }

  iohelp_free_iouser (user);

  diskfs_nput (node);
  ports_port_deref (pt);

  if (! err)
    {
      *file = ports_get_right (new_cred);
      *file_type = MACH_MSG_TYPE_MAKE_SEND;
    }

  return err;
}
Пример #12
0
/* Send data over a socket, possibly including Mach ports.  */
error_t
S_socket_send (struct sock_user *user, struct addr *dest_addr, int flags,
               char *data, size_t data_len,
               mach_port_t *ports, size_t num_ports,
               char *control, size_t control_len,
               size_t *amount)
{
    error_t err = 0;
    struct pipe *pipe;
    struct sock *sock, *dest_sock;
    struct addr *source_addr;

    if (!user)
        return EOPNOTSUPP;

    sock = user->sock;

    if (flags & MSG_OOB)
        /* BSD local sockets don't support OOB data.  */
        return EOPNOTSUPP;

    if (dest_addr)
    {
        err = addr_get_sock (dest_addr, &dest_sock);
        if (err == EADDRNOTAVAIL)
            /* The server went away.  */
            err = ECONNREFUSED;
        if (err)
            return err;
        if (sock->pipe_class != dest_sock->pipe_class)
            /* Sending to a different type of socket!  */
            err = EINVAL;		/* ? XXX */
    }
    else
        dest_sock = 0;

    /* We could provide a source address for all writes, but we
       only do so for connectionless sockets because that's the
       only place it's required, and it's more efficient not to.  */
    if (!err && sock->pipe_class->flags & PIPE_CLASS_CONNECTIONLESS)
        err = sock_get_addr (sock, &source_addr);
    else
        source_addr = NULL;

    if (!err)
    {
        if (dest_sock)
            /* Grab the destination socket's read pipe directly, and stuff data
               into it.  This is not quite the usage sock_acquire_read_pipe was
               intended for, but it will work, as the only inappropriate errors
               occur on a broken pipe, which shouldn't be possible with the sort of
               sockets with which we can use socket_send...  XXXX */
            err = sock_acquire_read_pipe (dest_sock, &pipe);
        else
            /* No address, must be a connected socket...  */
            err = sock_acquire_write_pipe (sock, &pipe);

        if (!err)
        {
            err = pipe_send (pipe, sock->flags & PFLOCAL_SOCK_NONBLOCK,
                             source_addr, data, data_len,
                             control, control_len, ports, num_ports,
                             amount);
            if (dest_sock)
                pipe_release_reader (pipe);
            else
                pipe_release_writer (pipe);
        }

        if (err)
            /* The send failed, so free any resources it would have consumed
               (mig gets rid of memory, but we have to do everything else). */
        {
            if (source_addr)
                ports_port_deref (source_addr);
            while (num_ports-- > 0)
                mach_port_deallocate (mach_task_self (), *ports++);
        }
    }

    if (dest_sock)
        sock_deref (dest_sock);

    return err;
}
void
ports_manage_port_operations_one_thread (struct port_bucket *bucket,
					 ports_demuxer_type demuxer,
					 int timeout)
{
  error_t err;

  int 
  internal_demuxer (mach_msg_header_t *inp,
		    mach_msg_header_t *outheadp)
    {
      struct port_info *pi;
      struct rpc_info link;
      int status;
      error_t err;
      register mig_reply_header_t *outp = (mig_reply_header_t *) outheadp;
      static const mach_msg_type_t RetCodeType = {
		/* msgt_name = */		MACH_MSG_TYPE_INTEGER_32,
		/* msgt_size = */		32,
		/* msgt_number = */		1,
		/* msgt_inline = */		TRUE,
		/* msgt_longform = */		FALSE,
		/* msgt_deallocate = */		FALSE,
		/* msgt_unused = */		0
	};

      /* Fill in default response. */
      outp->Head.msgh_bits 
	= MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(inp->msgh_bits), 0);
      outp->Head.msgh_size = sizeof *outp;
      outp->Head.msgh_remote_port = inp->msgh_remote_port;
      outp->Head.msgh_local_port = MACH_PORT_NULL;
      outp->Head.msgh_seqno = 0;
      outp->Head.msgh_id = inp->msgh_id + 100;
      outp->RetCodeType = RetCodeType;
      outp->RetCode = MIG_BAD_ID;

      pi = ports_lookup_port (bucket, inp->msgh_local_port, 0);
      if (pi)
	{
	  err = ports_begin_rpc (pi, inp->msgh_id, &link);
	  if (err)
	    {
	      mach_port_deallocate (mach_task_self (), inp->msgh_remote_port);
	      outp->RetCode = err;
	      status = 1;
	    }
	  else
	    {
	      /* No need to check cancel threshhold here, because
		 in a single threaded server the cancel is always
		 handled in order. */
	      status = demuxer (inp, outheadp);
	      ports_end_rpc (pi, &link);
	    }
	  ports_port_deref (pi);
	}
      else
	{
	  outp->RetCode = EOPNOTSUPP;
	  status = 1;
	}

      return status;
    }
  
  do
    err = mach_msg_server_timeout (internal_demuxer, 0, bucket->portset, 
				   timeout ? MACH_RCV_TIMEOUT : 0, timeout);
  while (err != MACH_RCV_TIMED_OUT);
}
Пример #14
0
void
ports_manage_port_operations_one_thread (struct port_bucket *bucket,
					 ports_demuxer_type demuxer,
					 int timeout)
{
  struct ports_thread thread;
  error_t err;

  int 
  internal_demuxer (mach_msg_header_t *inp,
		    mach_msg_header_t *outheadp)
    {
      struct port_info *pi;
      struct rpc_info link;
      int status;
      error_t err;
      register mig_reply_header_t *outp = (mig_reply_header_t *) outheadp;
      static const mach_msg_type_t RetCodeType = {
		/* msgt_name = */		MACH_MSG_TYPE_INTEGER_32,
		/* msgt_size = */		32,
		/* msgt_number = */		1,
		/* msgt_inline = */		TRUE,
		/* msgt_longform = */		FALSE,
		/* msgt_deallocate = */		FALSE,
		/* msgt_unused = */		0
	};

      /* Fill in default response. */
      outp->Head.msgh_bits 
	= MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(inp->msgh_bits), 0);
      outp->Head.msgh_size = sizeof *outp;
      outp->Head.msgh_remote_port = inp->msgh_remote_port;
      outp->Head.msgh_local_port = MACH_PORT_NULL;
      outp->Head.msgh_seqno = 0;
      outp->Head.msgh_id = inp->msgh_id + 100;
      outp->RetCodeType = RetCodeType;
      outp->RetCode = MIG_BAD_ID;

      if (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) ==
	  MACH_MSG_TYPE_PROTECTED_PAYLOAD)
	pi = ports_lookup_payload (bucket, inp->msgh_protected_payload, NULL);
      else
	{
	  pi = ports_lookup_port (bucket, inp->msgh_local_port, 0);
	  if (pi)
	    {
	      /* Store the objects address as the payload and set the
		 message type accordingly.  This prevents us from
		 having to do another hash table lookup in the intran
		 functions if protected payloads are not supported by
		 the kernel.  */
	      inp->msgh_bits =
		MACH_MSGH_BITS_OTHER (inp->msgh_bits)
		| MACH_MSGH_BITS (MACH_MSGH_BITS_REMOTE (inp->msgh_bits),
				  MACH_MSG_TYPE_PROTECTED_PAYLOAD);
	      inp->msgh_protected_payload = (unsigned long) pi;
	    }
	}

      if (pi)
	{
	  err = ports_begin_rpc (pi, inp->msgh_id, &link);
	  if (err)
	    {
	      mach_port_deallocate (mach_task_self (), inp->msgh_remote_port);
	      outp->RetCode = err;
	      status = 1;
	    }
	  else
	    {
	      /* No need to check cancel threshold here, because
		 in a single threaded server the cancel is always
		 handled in order. */
	      status = demuxer (inp, outheadp);
	      ports_end_rpc (pi, &link);
	    }
	  ports_port_deref (pi);
	}
      else
	{
	  outp->RetCode = EOPNOTSUPP;
	  status = 1;
	}

      _ports_thread_quiescent (&bucket->threadpool, &thread);
      return status;
    }

  /* XXX It is currently unsafe for most servers to terminate based on
     inactivity because a request may arrive after a server has
     started shutting down, causing the client to receive an error.
     Prevent the service loop from terminating by setting TIMEOUT to
     zero.  */
  timeout = 0;

  _ports_thread_online (&bucket->threadpool, &thread);
  do
    err = mach_msg_server_timeout (internal_demuxer, 0, bucket->portset, 
				   timeout ? MACH_RCV_TIMEOUT : 0, timeout);
  while (err != MACH_RCV_TIMED_OUT);
  _ports_thread_offline (&bucket->threadpool, &thread);
}
Пример #15
0
/* Receive data from a socket, possibly including Mach ports.  */
error_t
S_socket_recv (struct sock_user *user,
               mach_port_t *addr, mach_msg_type_name_t *addr_type,
               int in_flags,
               char **data, size_t *data_len,
               mach_port_t **ports, mach_msg_type_name_t *ports_type,
               size_t *num_ports,
               char **control, size_t *control_len,
               int *out_flags, size_t amount)
{
    error_t err;
    unsigned flags;
    struct pipe *pipe;
    void *source_addr = NULL;

    if (!user)
        return EOPNOTSUPP;

    if (in_flags & MSG_OOB)
        /* BSD local sockets don't support OOB data.  */
        return EINVAL;		/* XXX */

    /* Fill in the pipe FLAGS from any corresponding ones in IN_FLAGS.  */
    flags = in_flags & MSG_PEEK;

    err = sock_acquire_read_pipe (user->sock, &pipe);
    if (err == EPIPE)
        /* EOF */
    {
        *data_len = 0;
        if (num_ports)
            *num_ports = 0;
        if (control_len)
            *control_len = 0;
    }
    else if (!err)
    {
        err =
            pipe_recv (pipe, user->sock->flags & PFLOCAL_SOCK_NONBLOCK, &flags,
                       &source_addr, data, data_len, amount,
                       control, control_len, ports, num_ports);
        pipe_release_reader (pipe);
    }

    if (!err)
        /* Setup mach ports for return.  */
    {
        *addr_type = MACH_MSG_TYPE_MAKE_SEND;
        *ports_type = MACH_MSG_TYPE_MOVE_SEND;
        if (source_addr)
        {
            *addr = ports_get_right (source_addr);
            ports_port_deref (source_addr); /* since get_right has one too.  */
        }
        else
            *addr = MACH_PORT_NULL;
    }

    *out_flags = 0;

    return err;
}
Пример #16
0
kern_return_t
trivfs_S_fsys_getroot (struct trivfs_control *cntl,
		       mach_port_t reply_port,
		       mach_msg_type_name_t reply_port_type,
		       mach_port_t dotdot,
		       uid_t *uids, size_t nuids,
		       uid_t *gids, size_t ngids,
		       int flags,
		       retry_type *do_retry,
		       char *retry_name,
		       mach_port_t *newpt,
		       mach_msg_type_name_t *newpttype)
{
  int perms;
  error_t err = 0;
  mach_port_t new_realnode;
  struct trivfs_protid *cred;
  struct iouser *user;

  if (!cntl)
    return EOPNOTSUPP;

  if (trivfs_getroot_hook)
    {
      err = (*trivfs_getroot_hook) (cntl, reply_port, reply_port_type, dotdot,
				    uids, nuids, gids, ngids, flags,
				    do_retry, retry_name, newpt, newpttype);
      if (err != EAGAIN)
	return err;
    }

  if ((flags & O_WRITE & trivfs_allow_open) != (flags & O_WRITE))
    return EROFS;
  if ((flags & (O_READ|O_WRITE|O_EXEC) & trivfs_allow_open)
      != (flags & (O_READ|O_WRITE|O_EXEC)))
    return EACCES;

  /* O_CREAT and O_EXCL are not meaningful here; O_NOLINK and O_NOTRANS
     will only be useful when trivfs supports translators (which it doesn't
     now). */
  flags &= O_HURD;
  flags &= ~(O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS);

  struct idvec idvec = {
    .ids = uids,
    .num = nuids,
    .alloced = nuids,
  };

  if (_is_privileged (&idvec))
    /* Privileged users should be given all our rights.  */
    err = io_duplicate (cntl->underlying, &new_realnode);
  else
    /* Non-privileged, restrict rights.  */
    err = io_restrict_auth (cntl->underlying,
			    &new_realnode, uids, nuids, gids, ngids);

  if (err)
    return err;

  err = iohelp_create_complex_iouser (&user, uids, nuids, gids, ngids);
  if (err)
    return err;

  /* Validate permissions.  */
  if (! trivfs_check_access_hook)
    file_check_access (new_realnode, &perms);
  else
    (*trivfs_check_access_hook) (cntl, user, new_realnode, &perms);
  if ((flags & (O_READ|O_WRITE|O_EXEC) & perms)
      != (flags & (O_READ|O_WRITE|O_EXEC)))
    err = EACCES;

  if (!err && trivfs_check_open_hook)
    err = (*trivfs_check_open_hook) (cntl, user, flags);
  if (!err)
    {
      if (! trivfs_open_hook)
	{
	  err = trivfs_open (cntl, user, flags, new_realnode, &cred);
	  if (!err)
	    mach_port_deallocate (mach_task_self (), dotdot);
	}
      else
	err = (*trivfs_open_hook) (cntl, user, dotdot, flags, new_realnode,
				   &cred);
    }

  if (err)
    {
      mach_port_deallocate (mach_task_self (), new_realnode);
      iohelp_free_iouser (user);
    }
  else
    {
      *do_retry = FS_RETRY_NORMAL;
      *retry_name = '\0';
      *newpt = ports_get_right (cred);
      *newpttype = MACH_MSG_TYPE_MAKE_SEND;
      ports_port_deref (cred);
    }

  return err;
}
Пример #17
0
/* Implement pagein callback as described in <mach/memory_object.defs>. */
kern_return_t
_pager_seqnos_memory_object_data_request (mach_port_t object,
					  mach_port_seqno_t seqno,
					  mach_port_t control,
					  vm_offset_t offset,
					  vm_size_t length,
					  vm_prot_t access)
{
  struct pager *p;
  short *pm_entry;
  int doread, doerror;
  error_t err;
  vm_address_t page;
  int write_lock;

  p = ports_lookup_port (0, object, _pager_class);
  if (!p)
    return EOPNOTSUPP;

  /* Acquire the right to meddle with the pagemap */
  mutex_lock (&p->interlock);
  _pager_wait_for_seqno (p, seqno);

  /* sanity checks -- we don't do multi-page requests yet.  */
  if (control != p->memobjcntl)
    {
      printf ("incg data request: wrong control port\n");
      goto release_out;
    }
  if (length != __vm_page_size)
    {
      printf ("incg data request: bad length size %zd\n", length);
      goto release_out;
    }
  if (offset % __vm_page_size)
    {
      printf ("incg data request: misaligned request\n");
      goto release_out;
    }

  _pager_block_termination (p);	/* prevent termination until
				   mark_object_error is done */

  if (p->pager_state != NORMAL)
    {
      printf ("pager in wrong state for read\n");
      goto allow_release_out;
    }

  err = _pager_pagemap_resize (p, offset + length);
  if (err)
    goto allow_release_out;	/* Can't do much about the actual error.  */

  /* If someone is paging this out right now, the disk contents are
     unreliable, so we have to wait.  It is too expensive (right now) to
     find the data and return it, and then interrupt the write, so we just
     mark the page and have the writing thread do m_o_data_supply when it
     gets around to it.  */
  pm_entry = &p->pagemap[offset / __vm_page_size];
  if (*pm_entry & PM_PAGINGOUT)
    {
      doread = 0;
      *pm_entry |= PM_PAGEINWAIT;
    }
  else
    doread = 1;

  if (*pm_entry & PM_INVALID)
    doerror = 1;
  else
    doerror = 0;

  *pm_entry |= PM_INCORE;

  if (PM_NEXTERROR (*pm_entry) != PAGE_NOERR && (access & VM_PROT_WRITE))
    {
      memory_object_data_error (control, offset, length,
				_pager_page_errors[PM_NEXTERROR (*pm_entry)]);
      _pager_mark_object_error (p, offset, length,
				_pager_page_errors[PM_NEXTERROR (*pm_entry)]);
      *pm_entry = SET_PM_NEXTERROR (*pm_entry, PAGE_NOERR);
      doread = 0;
    }

  /* Let someone else in.  */
  _pager_release_seqno (p, seqno);
  mutex_unlock (&p->interlock);

  if (!doread)
    goto allow_term_out;
  if (doerror)
    goto error_read;

  err = pager_read_page (p->upi, offset, &page, &write_lock);
  if (err)
    goto error_read;

  memory_object_data_supply (p->memobjcntl, offset, page, length, 1,
			     write_lock ? VM_PROT_WRITE : VM_PROT_NONE, 0,
			     MACH_PORT_NULL);
  mutex_lock (&p->interlock);
  _pager_mark_object_error (p, offset, length, 0);
  _pager_allow_termination (p);
  mutex_unlock (&p->interlock);
  ports_port_deref (p);
  return 0;

 error_read:
  memory_object_data_error (p->memobjcntl, offset, length, EIO);
  _pager_mark_object_error (p, offset, length, EIO);
 allow_term_out:
  mutex_lock (&p->interlock);
  _pager_allow_termination (p);
  mutex_unlock (&p->interlock);
  ports_port_deref (p);
  return 0;

 allow_release_out:
  _pager_allow_termination (p);
 release_out:
  _pager_release_seqno (p, seqno);
  mutex_unlock (&p->interlock);
  ports_port_deref (p);
  return 0;
}
Пример #18
0
/* Create a new trivfs control port, with underlying node UNDERLYING, and
   return it in CONTROL.  CONTROL_CLASS & CONTROL_BUCKET are passed to
   the ports library to create the control port, and PROTID_CLASS &
   PROTID_BUCKET are used when creating ports representing opens of this
   node.  */
error_t
trivfs_create_control (mach_port_t underlying,
		       struct port_class *control_class,
		       struct port_bucket *control_bucket,
		       struct port_class *protid_class,
		       struct port_bucket *protid_bucket,
		       struct trivfs_control **control)
{
  error_t err;

  /* Perhaps allocate, and perhaps add the specified port classes the ones
     recognized by trivfs.  */
  err = trivfs_add_control_port_class (&control_class);
  if (! err)
    err = trivfs_add_protid_port_class (&protid_class);
  else
    protid_class = 0;

  /* Perhaps allocate new port buckets.  */
  if (! err)
    err = trivfs_add_port_bucket (&control_bucket);
  else
    control_bucket = 0;
  if (! err)
    {
      if (! protid_bucket)
	/* By default, use the same port bucket for both.  */
	protid_bucket = control_bucket;
      err = trivfs_add_port_bucket (&protid_bucket);
    }
  else
    protid_bucket = 0;

  if (! err)
    err = ports_create_port (control_class, control_bucket, 
			     sizeof (struct trivfs_control), control);

  if (! err)
    {
      (*control)->underlying = underlying;
      (*control)->protid_class = protid_class;
      (*control)->protid_bucket = protid_bucket;
      err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
				&(*control)->filesys_id);
      if (err)
	{
	  ports_port_deref (*control);
	  goto out;
	}
      
      err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
				&(*control)->file_id);
      if (err)
	{
	  mach_port_destroy (mach_task_self (), (*control)->filesys_id);
	  ports_port_deref (*control);
	  goto out;
	}

      (*control)->hook = 0;
      mutex_init (&(*control)->lock);
    }

out:
  if (err)
    {
      trivfs_remove_control_port_class (control_class);
      trivfs_remove_protid_port_class (protid_class);
      trivfs_remove_port_bucket (control_bucket);
      trivfs_remove_port_bucket (protid_bucket);
    }

  return err;
}
Пример #19
0
/* Implement the object termination call from the kernel as described
   in <mach/memory_object.defs>. */
kern_return_t
_pager_seqnos_memory_object_terminate (mach_port_t object, 
				       mach_port_seqno_t seqno,
				       mach_port_t control,
				       mach_port_t name)
{
  struct pager *p;
  
  p = ports_lookup_port (0, object, _pager_class);
  if (!p)
    return EOPNOTSUPP;

  mutex_lock (&p->interlock);
  _pager_wait_for_seqno (p, seqno);
  
  if (control != p->memobjcntl)
    {
      printf ("incg terminate: wrong control port");
      goto out;
    }
  if (name != p->memobjname)
    {
      printf ("incg terminate: wrong name port");
      goto out;
    }

  while (p->noterm)
    {
      p->termwaiting = 1;
      condition_wait (&p->wakeup, &p->interlock);
    }

  /* Destry the ports we received; mark that in P so that it doesn't bother
     doing it again. */
  mach_port_destroy (mach_task_self (), control);
  mach_port_destroy (mach_task_self (), name);
  p->memobjcntl = p->memobjname = MACH_PORT_NULL;

  _pager_free_structure (p);

#ifdef KERNEL_INIT_RACE
  if (p->init_head)
    {
      struct pending_init *i = p->init_head;
      p->init_head = i->next;
      if (!i->next)
	p->init_tail = 0;
      p->memobjcntl = i->control;
      p->memobjname = i->name;
      memory_object_ready (i->control, p->may_cache, p->copy_strategy);
      p->pager_state = NORMAL;
      free (i);
    }
#endif

 out:
  _pager_release_seqno (p, seqno);
  mutex_unlock (&p->interlock);
  ports_port_deref (p);

  return 0;
}
Пример #20
0
int
ethernet_demuxer (mach_msg_header_t *inp,
		  mach_msg_header_t *outp)
{
  struct net_rcv_msg *msg = (struct net_rcv_msg *) inp;
  struct sk_buff *skb;
  int datalen;
  struct ether_device *edev;
  struct device *dev = 0;
  mach_port_t local_port;

  if (inp->msgh_id != NET_RCV_MSG_ID)
    return 0;

  if (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) ==
      MACH_MSG_TYPE_PROTECTED_PAYLOAD)
    {
      struct port_info *pi = ports_lookup_payload (NULL,
						   inp->msgh_protected_payload,
						   NULL);
      if (pi)
	{
	  local_port = pi->port_right;
	  ports_port_deref (pi);
	}
      else
	local_port = MACH_PORT_NULL;
    }
  else
    local_port = inp->msgh_local_port;

  for (edev = ether_dev; edev; edev = edev->next)
    if (local_port == edev->readptname)
      dev = &edev->dev;

  if (! dev)
    {
      if (inp->msgh_remote_port != MACH_PORT_NULL)
	mach_port_deallocate (mach_task_self (), inp->msgh_remote_port);
      return 1;
    }

  datalen = ETH_HLEN
    + msg->packet_type.msgt_number - sizeof (struct packet_header);

  pthread_mutex_lock (&net_bh_lock);
  skb = alloc_skb (datalen, GFP_ATOMIC);
  skb_put (skb, datalen);
  skb->dev = dev;

  /* Copy the two parts of the frame into the buffer. */
  memcpy (skb->data, msg->header, ETH_HLEN);
  memcpy (skb->data + ETH_HLEN,
	  msg->packet + sizeof (struct packet_header),
	  datalen - ETH_HLEN);

  /* Drop it on the queue. */
  skb->protocol = eth_type_trans (skb, dev);
  netif_rx (skb);
  pthread_mutex_unlock (&net_bh_lock);

  return 1;
}
Пример #21
0
kern_return_t
trivfs_S_io_restrict_auth (struct trivfs_protid *cred,
			   mach_port_t reply,
			   mach_msg_type_name_t replytype,
			   mach_port_t *newport,
			   mach_msg_type_name_t *newporttype,
			   uid_t *uids, size_t nuids,
			   uid_t *gids, size_t ngids)
{
  int i;
  error_t err;
  struct trivfs_protid *newcred;
  struct idvec *uvec, *gvec;
  struct iouser *user;

  if (!cred)
    return EOPNOTSUPP;

  if (cred->isroot)
    /* CRED has root access, and so may use any ids.  */
    {
      err = iohelp_create_complex_iouser (&user, uids, nuids, gids, ngids);
      if (err)
        return err;
    }
  else
    {
      uvec = make_idvec ();
      if (! uvec)
        return ENOMEM;

      gvec = make_idvec ();
      if (! gvec)
        {
	  idvec_free (uvec);
	  return ENOMEM;
	}

      /* Otherwise, use any of the requested ids that CRED already has.  */
      for (i = 0; i < cred->user->uids->num; i++)
	if (listmember (uids, cred->user->uids->ids[i], nuids))
	  {
	    err = idvec_add (uvec, cred->user->uids->ids[i]);
	    if (err)
	      goto out;
	  }

      for (i = 0; i < cred->user->gids->num; i++)
	if (listmember (gids, cred->user->gids->ids[i], ngids))
	  {
	    err = idvec_add (gvec, cred->user->gids->ids[i]);
	    if (err)
	      goto out;
	  }

      err = iohelp_create_iouser (&user, uvec, gvec);
      if (err)
        {
	out:
	  idvec_free (uvec);
	  idvec_free (gvec);
	  return err;
	}
    }

  err = ports_create_port (cred->po->cntl->protid_class,
			   cred->po->cntl->protid_bucket,
			   sizeof (struct trivfs_protid),
			   &newcred);
  if (err)
    {
      iohelp_free_iouser (user);
      return err;
    }

  newcred->isroot = 0;
  mutex_lock (&cred->po->cntl->lock);
  newcred->po = cred->po;
  newcred->po->refcnt++;
  mutex_unlock (&cred->po->cntl->lock);
  if (cred->isroot && idvec_contains (user->uids, 0))
    newcred->isroot = 1;
  newcred->user = user;
  newcred->hook = cred->hook;

  err = io_restrict_auth (cred->realnode, &newcred->realnode,
			  user->uids->ids, user->uids->num,
			  user->gids->ids, user->gids->num);
  if (!err && trivfs_protid_create_hook)
    {
      err = (*trivfs_protid_create_hook) (newcred);
      if (err)
	mach_port_deallocate (mach_task_self (), newcred->realnode);
    }

  if (err)
    /* Signal that the user destroy hook shouldn't be called on NEWCRED.  */
    newcred->realnode = MACH_PORT_NULL;
  else
    {
      *newport = ports_get_right (newcred);
      *newporttype = MACH_MSG_TYPE_MAKE_SEND;
    }

  /* This will destroy NEWCRED if we got an error and didn't do the
     ports_get_right above.  */
  ports_port_deref (newcred);

  return 0;
}
Пример #22
0
/* Implement dir_lookup as described in <hurd/fs.defs>. */
kern_return_t
diskfs_S_dir_lookup (struct protid *dircred,
		     char *path,
		     int flags,
		     mode_t mode,
		     enum retry_type *retry,
		     char *retryname,
		     file_t *returned_port,
		     mach_msg_type_name_t *returned_port_poly)
{
  struct node *dnp;
  struct node *np;
  int nsymlink = 0;
  char *nextname;
  char *relpath;
  int nextnamelen;
  error_t error = 0;
  char *pathbuf = 0;
  int pathbuflen = 0;
  int newnamelen;
  int create, excl;
  int lastcomp = 0;
  int newnode = 0;
  struct dirstat *ds = 0;
  int mustbedir = 0;
  size_t amt;
  int type;
  struct protid *newpi = 0;
  struct peropen *newpo = 0;

  if (!dircred)
    return EOPNOTSUPP;

  flags &= O_HURD;

  create = (flags & O_CREAT);
  excl = (flags & O_EXCL);

  /* Skip leading slashes */
  while (path[0] == '/')
    path++;

  /* Preserve the path relative to diruser->po->path.  */
  relpath = strdup (path);
  if (! relpath)
    return ENOMEM;

  /* Keep a pointer to the start of the path for length
     calculations.  */
  char *path_start = path;

  *returned_port_poly = MACH_MSG_TYPE_MAKE_SEND;
  *retry = FS_RETRY_NORMAL;
  retryname[0] = '\0';

  if (path[0] == '\0')
    {
      /* Set things up in the state expected by the code from gotit: on. */
      dnp = 0;
      np = dircred->po->np;
      pthread_mutex_lock (&np->lock);
      diskfs_nref (np);
      goto gotit;
    }

  dnp = dircred->po->np;

  pthread_mutex_lock (&dnp->lock);
  np = 0;

  diskfs_nref (dnp);		/* acquire a reference for later diskfs_nput */

  do
    {
      assert (!lastcomp);

      /* Find the name of the next pathname component */
      nextname = index (path, '/');

      if (nextname)
	{
	  *nextname++ = '\0';
	  while (*nextname == '/')
	    nextname++;
	  if (*nextname == '\0')
	    {
	      /* These are the rules for filenames ending in /. */
	      nextname = 0;
	      lastcomp = 1;
	      mustbedir = 1;
	      create = 0;
	    }
	  else
	    lastcomp = 0;
	}
      else
	lastcomp = 1;

      np = 0;

      /* diskfs_lookup the next pathname component */
      if (lastcomp && create)
	{
	  if (!ds)
	    ds = alloca (diskfs_dirstat_size);
	  error = diskfs_lookup (dnp, path, CREATE, &np, ds, dircred);
	}
      else
	error = diskfs_lookup (dnp, path, LOOKUP, &np, 0, dircred);

      if (lastcomp && create && excl && (!error || error == EAGAIN))
	error = EEXIST;

      /* If we get an error we're done */
      if (error == EAGAIN)
	{
	  if (dnp == dircred->po->shadow_root)
	    /* We're at the root of a shadow tree.  */
	    {
	      if (dircred->po->shadow_root_parent == MACH_PORT_NULL)
		{
		  /* This is a shadow root with no parent, meaning
		     we should treat it as a virtual root disconnected
		  from its real .. directory.  */
		  error = 0;
		  np = dnp;
		  diskfs_nref (np);
		}
	      else
		{
		  /* Punt the client up to the shadow root parent.  */
		  *retry = FS_RETRY_REAUTH;
		  *returned_port = dircred->po->shadow_root_parent;
		  *returned_port_poly = MACH_MSG_TYPE_COPY_SEND;
		  if (! lastcomp)
		    strcpy (retryname, nextname);
		  error = 0;
		  goto out;
		}
	    }
	  else if (dircred->po->root_parent != MACH_PORT_NULL)
	    /* We're at a real translator root; even if DIRCRED->po has a
	       shadow root, we can get here if its in a directory that was
	    renamed out from under it...  */
	    {
	      *retry = FS_RETRY_REAUTH;
	      *returned_port = dircred->po->root_parent;
	      *returned_port_poly = MACH_MSG_TYPE_COPY_SEND;
	      if (!lastcomp)
		strcpy (retryname, nextname);
	      error = 0;
	      goto out;
	    }
	  else
	    /* We're at a REAL root, as in there's no way up from here.  */
	    {
	      error = 0;
	      np = dnp;
	      diskfs_nref (np);
	    }
	}

      /* Create the new node if necessary */
      if (lastcomp && create)
	{
	  if (error == ENOENT)
	    {
	      mode &= ~(S_IFMT | S_ISPARE | S_ISVTX | S_ITRANS);
	      mode |= S_IFREG;
	      error = diskfs_create_node (dnp, path, mode, &np, dircred, ds);
	      if (diskfs_synchronous)
		{
		  diskfs_file_update (dnp, 1);
		  diskfs_file_update (np, 1);
		}
	      newnode = 1;
	    }
	  else
	    diskfs_drop_dirstat (dnp, ds);
	}

      if (error)
	goto out;

      /* If this is translated, start the translator (if necessary)
	 and return.  */
      if ((((flags & O_NOTRANS) == 0) || !lastcomp)
	  && ((np->dn_stat.st_mode & S_IPTRANS)
	      || S_ISFIFO (np->dn_stat.st_mode)
	      || S_ISCHR (np->dn_stat.st_mode)
	      || S_ISBLK (np->dn_stat.st_mode)
	      || fshelp_translated (&np->transbox)))
	{
	  mach_port_t dirport;
	  struct iouser *user;

	  /* A callback function for short-circuited translators.
	     Symlink & ifsock are handled elsewhere.  */
	  error_t short_circuited_callback1 (void *cookie1, void *cookie2,
					     uid_t *uid, gid_t *gid,
					     char **argz, size_t *argz_len)
	    {
	      struct node *node = cookie1;

	      switch (node->dn_stat.st_mode & S_IFMT)
		{
		case S_IFCHR:
		case S_IFBLK:
		  asprintf (argz, "%s%c%d%c%d",
			    (S_ISCHR (node->dn_stat.st_mode)
			     ? _HURD_CHRDEV : _HURD_BLKDEV),
			    0, major (node->dn_stat.st_rdev),
			    0, minor (node->dn_stat.st_rdev));
		  *argz_len = strlen (*argz) + 1;
		  *argz_len += strlen (*argz + *argz_len) + 1;
		  *argz_len += strlen (*argz + *argz_len) + 1;
		  break;
		case S_IFIFO:
		  asprintf (argz, "%s", _HURD_FIFO);
		  *argz_len = strlen (*argz) + 1;
		  break;
		default:
		  return ENOENT;
		}

	      *uid = node->dn_stat.st_uid;
	      *gid = node->dn_stat.st_gid;

	      return 0;
	    }

	  /* Create an unauthenticated port for DNP, and then
	     unlock it. */
	  error = iohelp_create_empty_iouser (&user);
	  if (! error)
	    {
	      error = diskfs_make_peropen (dnp, 0, dircred->po, &newpo);
	      if (! error)
		{
		  error = diskfs_create_protid (newpo, user, &newpi);
		  if (! error)
		    newpo = 0;
		}

	      iohelp_free_iouser (user);
	    }

	  if (error)
	    goto out;

	  dirport = ports_get_send_right (newpi);
	  if (np != dnp)
	    pthread_mutex_unlock (&dnp->lock);

	  /* Check if an active translator is currently running.  If
	     not, fshelp_fetch_root will start one.  In that case, we
	     need to register it in the list of active
	     translators.  */
	  boolean_t register_translator =
	    np->transbox.active == MACH_PORT_NULL;

	  error = fshelp_fetch_root (&np->transbox, dircred->po,
				     dirport, dircred->user,
				     lastcomp ? flags : 0,
				     ((np->dn_stat.st_mode & S_IPTRANS)
				      ? _diskfs_translator_callback1
				      : short_circuited_callback1),
				     _diskfs_translator_callback2,
				     retry, retryname, returned_port);

	  /* fetch_root copies DIRPORT for success, so we always should
	     deallocate our send right.  */
	  mach_port_deallocate (mach_task_self (), dirport);

	  if (error != ENOENT)
	    {
	      *returned_port_poly = MACH_MSG_TYPE_MOVE_SEND;
	      if (!lastcomp && !error)
		{
		  char *end = strchr (retryname, '\0');
		  *end++ = '/';
		  strcpy (end, nextname);
		}

	      if (register_translator)
		{
		  char *translator_path = strdupa (relpath);
		  char *complete_path;
		  if (nextname != NULL)
		    {
		      /* This was not the last path component.
			 NEXTNAME points to the next component, locate
			 the end of the current component and use it
			 to trim TRANSLATOR_PATH.  */
		      char *end = nextname;
		      while (*end != 0)
			end--;
		      translator_path[end - path_start] = '\0';
		    }

		  if (dircred->po->path == NULL || !strcmp (dircred->po->path,"."))
		      /* dircred is the root directory.  */
		      complete_path = translator_path;
		  else
		      asprintf (&complete_path, "%s/%s", dircred->po->path, translator_path);

		  error = fshelp_set_active_translator (&newpi->pi,
							complete_path,
							np->transbox.active);
		  if (complete_path != translator_path)
		    free(complete_path);
		  if (error)
		    goto out;
		}

	      goto out;
	    }

	  ports_port_deref (newpi);
	  newpi = NULL;

	  /* ENOENT means there was a hiccup, and the translator
	     vanished while NP was unlocked inside fshelp_fetch_root.
	     Reacquire the locks, and continue as normal. */
	  error = 0;
	  if (np != dnp)
	    {
	      if (!strcmp (path, ".."))
		pthread_mutex_lock (&dnp->lock);
	      else
		{
		  if (pthread_mutex_trylock (&dnp->lock))
		    {
		      pthread_mutex_unlock (&np->lock);
		      pthread_mutex_lock (&dnp->lock);
		      pthread_mutex_lock (&np->lock);
		    }
		}
	    }
	}

      if (S_ISLNK (np->dn_stat.st_mode)
	  && (!lastcomp
	      || mustbedir	/* "foo/" must see that foo points to a dir */
	      || !(flags & (O_NOLINK|O_NOTRANS))))
	{
	  /* Handle symlink interpretation */

	  if (nsymlink++ > diskfs_maxsymlinks)
	    {
	      error = ELOOP;
	      goto out;
	    }

	  nextnamelen = nextname ? strlen (nextname) + 1 : 0;
	  newnamelen = nextnamelen + np->dn_stat.st_size + 1 + 1;
	  if (pathbuflen < newnamelen)
	    {
	      pathbuf = alloca (newnamelen);
	      pathbuflen = newnamelen;
	    }

	  if (diskfs_read_symlink_hook)
	    error = (*diskfs_read_symlink_hook)(np, pathbuf);
	  if (!diskfs_read_symlink_hook || error == EINVAL)
	    {
	      error = diskfs_node_rdwr (np, pathbuf,
					0, np->dn_stat.st_size, 0,
					dircred, &amt);
	      if (!error)
		assert (amt == np->dn_stat.st_size);
	    }
	  if (error)
	    goto out;

	  if (np->dn_stat.st_size == 0)	/* symlink to "" */
	    path = nextname;
	  else
	    {
	      if (nextname)
		{
		  pathbuf[np->dn_stat.st_size] = '/';
		  memcpy (pathbuf + np->dn_stat.st_size + 1,
			  nextname, nextnamelen - 1);
		}
	      pathbuf[nextnamelen + np->dn_stat.st_size] = '\0';

	      if (pathbuf[0] == '/')
		{
		  /* Punt to the caller.  */
		  *retry = FS_RETRY_MAGICAL;
		  *returned_port = MACH_PORT_NULL;
		  memcpy (retryname, pathbuf,
			  nextnamelen + np->dn_stat.st_size + 1);
		  if (mustbedir)
		    {
		      retryname[nextnamelen + np->dn_stat.st_size] = '/';
		      retryname[nextnamelen + np->dn_stat.st_size + 1] = '\0';
		    }
		  goto out;
		}

	      path = pathbuf;
	    }

	  if (lastcomp)
	    lastcomp = 0;

	  diskfs_nput (np);
	  np = 0;

	  if (path == 0)	/* symlink to "" was the last component */
	    {
	      np = dnp;
	      dnp = 0;
	      break;
	    }
	}
      else
	{
	  /* Handle normal nodes */
	  path = nextname;
	  if (np == dnp)
	    diskfs_nrele (dnp);
	  else
	    diskfs_nput (dnp);
	  if (!lastcomp)
	    {
	      dnp = np;
	      np = 0;
	    }
	  else
	    dnp = 0;
	}
    } while (path && *path);