コード例 #1
0
ファイル: trans-callback.c プロジェクト: Larhard/hurd
/* Callback function needed for calls to fshelp_fetch_root.  See
   <hurd/fshelp.h> for the interface description.  */
static error_t
_netfs_translator_callback2_fn (void *cookie1, void *cookie2, int flags,
				mach_port_t *underlying,
				mach_msg_type_name_t *underlying_type)
{
  error_t err;
  struct protid *cred;
  struct node *node = cookie1;
  struct iouser *user;

  err = iohelp_create_simple_iouser (&user, node->nn_stat.st_uid,
				   node->nn_stat.st_gid);
  if (err)
    return err;

  cred = netfs_make_protid (netfs_make_peropen (node, flags, cookie2),
			    user);
  if (cred)
    {
      *underlying = ports_get_right (cred);
      *underlying_type = MACH_MSG_TYPE_MAKE_SEND;
      ports_port_deref (cred);
      return 0;
    }
  else
    {
      iohelp_free_iouser (user);
      return errno;
    }
}
コード例 #2
0
ファイル: io-reauthenticate.c プロジェクト: Larhard/hurd
/* 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;
}
コード例 #3
0
error_t
netfs_S_fsys_syncfs (mach_port_t cntl,
		     mach_port_t reply,
		     mach_msg_type_name_t reply_type,
		     int wait,
		     int children)
{
  struct iouser *cred;
  error_t err;

  err = iohelp_create_simple_iouser (&cred, 0, 0);
  if (err)
    return err;
  err = netfs_attempt_syncfs (cred, wait);
  iohelp_free_iouser (cred);
  return err;
}
コード例 #4
0
ファイル: dir-mkfile.c プロジェクト: Larhard/hurd
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;
}
コード例 #5
0
ファイル: io-restrict-auth.c プロジェクト: GNUHurdTR/hurd
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;
}
コード例 #6
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;
}
コード例 #7
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;
}
コード例 #8
0
ファイル: fsys-getroot.c プロジェクト: GNUHurdTR/hurd
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;
}
コード例 #9
0
ファイル: dir-lookup.c プロジェクト: GNUHurdTR/hurd
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;
}
コード例 #10
0
ファイル: dir-lookup.c プロジェクト: Larhard/hurd
/* 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);