about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog14
-rw-r--r--hurd/Makefile6
-rw-r--r--hurd/hurdlookup.c285
-rw-r--r--hurd/lookup-retry.c324
-rw-r--r--sysdeps/mach/hurd/dl-sysdep.c251
5 files changed, 376 insertions, 504 deletions
diff --git a/ChangeLog b/ChangeLog
index 53b8f046a5..6c6775890e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2001-06-16  Roland McGrath  <roland@frob.com>
+
+	* hurd/Makefile (routines): Add lookup-retry.
+	* hurd/hurdlookup.c (__hurd_file_name_lookup_retry): Broken out ...
+	* hurd/lookup-retry.c: ... into here, new file.
+	* sysdeps/mach/hurd/dl-sysdep.c (open_file): Call that instead
+	using of duplicate code here.
+
+	* hurd/hurdexec.c (_hurd_exec): Be robust for DTABLESIZE==0.
+
 2001-06-17  Ulrich Drepper  <drepper@redhat.com>
 
 	* po/ja.po: Update from translation team.
@@ -2589,7 +2599,7 @@
 
 2001-03-30  Isamu Hasegawa  <isamu@yamato.ibm.com>
 
-	* posix/regex.c: Avoid an access violation if malloc failes.
+	* posix/regex.c: Avoid an access violation if malloc fails.
 
 2001-03-30  Andreas Jaeger  <aj@suse.de>
 
@@ -2606,7 +2616,7 @@
 	* include/endian.h: Define BIG_ENDI, LITTLE_ENDI, HIGH_HALF,
 	and LOW_HALF only if _LIBC is defined and _ISOMAC is not defined.
 	* stdlib/isomac.c (fmt): Define _LIBC and _ISOMAC.
-	.
+
 2001-03-29  Isamu Hasegawa  <isamu@yamato.ibm.com>
 
 	* posix/regex.c: Fix typo and add a sentinel.
diff --git a/hurd/Makefile b/hurd/Makefile
index 0d932e501f..c807fac657 100644
--- a/hurd/Makefile
+++ b/hurd/Makefile
@@ -1,4 +1,5 @@
-# Copyright (C) 1991,92,93,94,95,96,97,98,99 Free Software Foundation, Inc.
+# Copyright (C) 1991,92,93,94,95,96,97,98,99,2001
+#	Free Software Foundation, Inc.
 # This file is part of the GNU C Library.
 
 # The GNU C Library is free software; you can redistribute it and/or
@@ -44,7 +45,8 @@ user-interfaces		:= $(addprefix hurd/,\
 server-interfaces	:= hurd/msg faultexc
 
 routines = hurdstartup hurdinit \
-	   hurdid hurdlookup hurdpid hurdrlimit hurdprio hurdexec hurdselect \
+	   hurdid hurdpid hurdrlimit hurdprio hurdexec hurdselect \
+	   hurdlookup lookup-retry \
 	   get-host set-host \
 	   path-lookup \
 	   setauth \
diff --git a/hurd/hurdlookup.c b/hurd/hurdlookup.c
index 5a54cfb9d8..a2db898235 100644
--- a/hurd/hurdlookup.c
+++ b/hurd/hurdlookup.c
@@ -19,11 +19,7 @@
 #include <hurd.h>
 #include <hurd/lookup.h>
 #include <string.h>
-#include <limits.h>
 #include <fcntl.h>
-#include "stdio-common/_itoa.h"
-#include <hurd/term.h>
-#include <hurd/paths.h>
 
 
 /* Translate the error from dir_lookup into the error the user sees.  */
@@ -103,287 +99,6 @@ __hurd_file_name_lookup (error_t (*use_init_port)
 weak_alias (__hurd_file_name_lookup, hurd_file_name_lookup)
 
 error_t
-__hurd_file_name_lookup_retry (error_t (*use_init_port)
-			         (int which, error_t (*operate) (file_t)),
-			       file_t (*get_dtable_port) (int fd),
-			       error_t (*lookup)
-			         (file_t dir, char *name,
-				  int flags, mode_t mode,
-				  retry_type *do_retry, string_t retry_name,
-				  mach_port_t *result),
-			       enum retry_type doretry,
-			       char retryname[1024],
-			       int flags, mode_t mode,
-			       file_t *result)
-{
-  error_t err;
-  char *file_name;
-  int nloops;
-
-  error_t lookup_op (file_t startdir)
-    {
-      while (file_name[0] == '/')
-	file_name++;
-
-      return lookup_error ((*lookup) (startdir, file_name, flags, mode,
-				      &doretry, retryname, result));
-    }
-  error_t reauthenticate (file_t unauth)
-    {
-      error_t err;
-      mach_port_t ref = __mach_reply_port ();
-      error_t reauth (auth_t auth)
-	{
-	  return __auth_user_authenticate (auth, ref,
-					   MACH_MSG_TYPE_MAKE_SEND,
-					   result);
-	}
-      err = __io_reauthenticate (unauth, ref, MACH_MSG_TYPE_MAKE_SEND);
-      if (! err)
-	err = (*use_init_port) (INIT_PORT_AUTH, &reauth);
-      __mach_port_destroy (__mach_task_self (), ref);
-      __mach_port_deallocate (__mach_task_self (), unauth);
-      return err;
-    }
-
-  if (! lookup)
-    lookup = __dir_lookup;
-
-  nloops = 0;
-  err = 0;
-  do
-    {
-      file_t startdir = MACH_PORT_NULL;
-      int dirport = INIT_PORT_CWDIR;
-
-      switch (doretry)
-	{
-	case FS_RETRY_REAUTH:
-	  if (err = reauthenticate (*result))
-	    return err;
-	  /* Fall through.  */
-
-	case FS_RETRY_NORMAL:
-	  if (nloops++ >= SYMLOOP_MAX)
-	    {
-	      __mach_port_deallocate (__mach_task_self (), *result);
-	      return ELOOP;
-	    }
-
-	  /* An empty RETRYNAME indicates we have the final port.  */
-	  if (retryname[0] == '\0' &&
-	      /* If reauth'd, we must do one more retry on "" to give the new
-		 translator a chance to make a new port for us.  */
-	      doretry == FS_RETRY_NORMAL)
-	    {
-	      if (flags & O_NOFOLLOW)
-		{
-		  /* In Linux, O_NOFOLLOW means to reject symlinks.  If we
-		     did an O_NOLINK lookup above and io_stat here to check
-		     for S_IFLNK, a translator like firmlink could easily
-		     spoof this check by not showing S_IFLNK, but in fact
-		     redirecting the lookup to some other name
-		     (i.e. opening the very same holes a symlink would).
-
-		     Instead we do an O_NOTRANS lookup above, and stat the
-		     underlying node: if it has a translator set, and its
-		     owner is not root (st_uid 0) then we reject it.
-		     Since the motivation for this feature is security, and
-		     that security presumes we trust the containing
-		     directory, this check approximates the security of
-		     refusing symlinks while accepting mount points.
-		     Note that we actually permit something Linux doesn't:
-		     we follow root-owned symlinks; if that is deemed
-		     undesireable, we can add a final check for that
-		     one exception to our general translator-based rule.  */
-		  struct stat st;
-		  err = __io_stat (*result, &st);
-		  if (!err
-		      && (st.st_mode & (S_IPTRANS|S_IATRANS)))
-		    {
-		      if (st.st_uid != 0)
-			err = ENOENT;
-		      else if (st.st_mode & S_IPTRANS)
-			{
-			  char buf[1024];
-			  char *trans = buf;
-			  size_t translen = sizeof buf;
-			  err = __file_get_translator (*result,
-						       &trans, &translen);
-			  if (!err
-			      && translen > sizeof _HURD_SYMLINK
-			      && !memcmp (trans,
-					  _HURD_SYMLINK, sizeof _HURD_SYMLINK))
-			    err = ENOENT;
-			}
-		    }
-		}
-
-	      /* We got a successful translation.  Now apply any open-time
-		 action flags we were passed.  */
-
-	      if (!err && (flags & O_TRUNC)) /* Asked to truncate the file.  */
-		err = __file_set_size (*result, 0);
-
-	      if (err)
-		__mach_port_deallocate (__mach_task_self (), *result);
-	      return err;
-	    }
-
-	  startdir = *result;
-	  file_name = retryname;
-	  break;
-
-	case FS_RETRY_MAGICAL:
-	  switch (retryname[0])
-	    {
-	    case '/':
-	      dirport = INIT_PORT_CRDIR;
-	      if (*result != MACH_PORT_NULL)
-		__mach_port_deallocate (__mach_task_self (), *result);
-	      if (nloops++ >= SYMLOOP_MAX)
-		return ELOOP;
-	      file_name = &retryname[1];
-	      break;
-
-	    case 'f':
-	      if (retryname[1] == 'd' && retryname[2] == '/')
-		{
-		  int fd;
-		  char *end;
-		  int save = errno;
-		  errno = 0;
-		  fd = (int) strtol (&retryname[3], &end, 10);
-		  if (end == NULL || errno || /* Malformed number.  */
-		      /* Check for excess text after the number.  A slash
-			 is valid; it ends the component.  Anything else
-			 does not name a numeric file descriptor.  */
-		      (*end != '/' && *end != '\0'))
-		    {
-		      errno = save;
-		      return ENOENT;
-		    }
-		  if (! get_dtable_port)
-		    err = EGRATUITOUS;
-		  else
-		    {
-		      *result = (*get_dtable_port) (fd);
-		      if (*result == MACH_PORT_NULL)
-			{
-			  /* If the name was a proper number, but the file
-			     descriptor does not exist, we return EBADF instead
-			     of ENOENT.  */
-			  err = errno;
-			  errno = save;
-			}
-		    }
-		  errno = save;
-		  if (err)
-		    return err;
-		  if (*end == '\0')
-		    return 0;
-		  else
-		    {
-		      /* Do a normal retry on the remaining components.  */
-		      startdir = *result;
-		      file_name = end + 1; /* Skip the slash.  */
-		      break;
-		    }
-		}
-	      else
-		goto bad_magic;
-	      break;
-
-	    case 'm':
-	      if (retryname[1] == 'a' && retryname[2] == 'c' &&
-		  retryname[3] == 'h' && retryname[4] == 't' &&
-		  retryname[5] == 'y' && retryname[6] == 'p' &&
-		  retryname[7] == 'e')
-		{
-		  error_t err;
-		  struct host_basic_info hostinfo;
-		  mach_msg_type_number_t hostinfocnt = HOST_BASIC_INFO_COUNT;
-		  char *p;
-		  /* XXX want client's host */
-		  if (err = __host_info (__mach_host_self (), HOST_BASIC_INFO,
-					 (natural_t *) &hostinfo,
-					 &hostinfocnt))
-		    return err;
-		  if (hostinfocnt != HOST_BASIC_INFO_COUNT)
-		    return EGRATUITOUS;
-		  p = _itoa (hostinfo.cpu_subtype, &retryname[8], 10, 0);
-		  *--p = '/';
-		  p = _itoa (hostinfo.cpu_type, &retryname[8], 10, 0);
-		  if (p < retryname)
-		    abort ();	/* XXX write this right if this ever happens */
-		  if (p > retryname)
-		    strcpy (retryname, p);
-		  startdir = *result;
-		}
-	      else
-		goto bad_magic;
-	      break;
-
-	    case 't':
-	      if (retryname[1] == 't' && retryname[2] == 'y')
-		switch (retryname[3])
-		  {
-		    error_t opentty (file_t *result)
-		      {
-			error_t err;
-			error_t ctty_open (file_t port)
-			  {
-			    if (port == MACH_PORT_NULL)
-			      return ENXIO; /* No controlling terminal.  */
-			    return __termctty_open_terminal (port,
-							     flags,
-							     result);
-			  }
-			err = (*use_init_port) (INIT_PORT_CTTYID, &ctty_open);
-			if (! err)
-			  err = reauthenticate (*result);
-			return err;
-		      }
-
-		  case '\0':
-		    return opentty (result);
-		  case '/':
-		    if (err = opentty (&startdir))
-		      return err;
-		    strcpy (retryname, &retryname[4]);
-		    break;
-		  default:
-		    goto bad_magic;
-		  }
-	      else
-		goto bad_magic;
-	      break;
-
-	    default:
-	    bad_magic:
-	      return EGRATUITOUS;
-	    }
-	  break;
-
-	default:
-	  return EGRATUITOUS;
-	}
-
-      if (startdir != MACH_PORT_NULL)
-	{
-	  err = lookup_op (startdir);
-	  __mach_port_deallocate (__mach_task_self (), startdir);
-	  startdir = MACH_PORT_NULL;
-	}
-      else
-	err = (*use_init_port) (dirport, &lookup_op);
-    } while (! err);
-
-  return err;
-}
-weak_alias (__hurd_file_name_lookup_retry, hurd_file_name_lookup_retry)
-
-error_t
 __hurd_file_name_split (error_t (*use_init_port)
 			  (int which, error_t (*operate) (file_t)),
 			file_t (*get_dtable_port) (int fd),
diff --git a/hurd/lookup-retry.c b/hurd/lookup-retry.c
new file mode 100644
index 0000000000..34b0674561
--- /dev/null
+++ b/hurd/lookup-retry.c
@@ -0,0 +1,324 @@
+/* hairy bits of Hurd file name lookup
+   Copyright (C) 1992,93,94,95,96,97,99,2001 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <hurd.h>
+#include <hurd/lookup.h>
+#include <hurd/term.h>
+#include <hurd/paths.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <string.h>
+#include "stdio-common/_itoa.h"
+
+/* Translate the error from dir_lookup into the error the user sees.  */
+static inline error_t
+lookup_error (error_t error)
+{
+  switch (error)
+    {
+    case EOPNOTSUPP:
+    case MIG_BAD_ID:
+      /* These indicate that the server does not understand dir_lookup
+	 at all.  If it were a directory, it would, by definition.  */
+      return ENOTDIR;
+    default:
+      return error;
+    }
+}
+
+error_t
+__hurd_file_name_lookup_retry (error_t (*use_init_port)
+			         (int which, error_t (*operate) (file_t)),
+			       file_t (*get_dtable_port) (int fd),
+			       error_t (*lookup)
+			         (file_t dir, char *name,
+				  int flags, mode_t mode,
+				  retry_type *do_retry, string_t retry_name,
+				  mach_port_t *result),
+			       enum retry_type doretry,
+			       char retryname[1024],
+			       int flags, mode_t mode,
+			       file_t *result)
+{
+  error_t err;
+  char *file_name;
+  int nloops;
+
+  error_t lookup_op (file_t startdir)
+    {
+      while (file_name[0] == '/')
+	file_name++;
+
+      return lookup_error ((*lookup) (startdir, file_name, flags, mode,
+				      &doretry, retryname, result));
+    }
+  error_t reauthenticate (file_t unauth)
+    {
+      error_t err;
+      mach_port_t ref = __mach_reply_port ();
+      error_t reauth (auth_t auth)
+	{
+	  return __auth_user_authenticate (auth, ref,
+					   MACH_MSG_TYPE_MAKE_SEND,
+					   result);
+	}
+      err = __io_reauthenticate (unauth, ref, MACH_MSG_TYPE_MAKE_SEND);
+      if (! err)
+	err = (*use_init_port) (INIT_PORT_AUTH, &reauth);
+      __mach_port_destroy (__mach_task_self (), ref);
+      __mach_port_deallocate (__mach_task_self (), unauth);
+      return err;
+    }
+
+  if (! lookup)
+    lookup = __dir_lookup;
+
+  nloops = 0;
+  err = 0;
+  do
+    {
+      file_t startdir = MACH_PORT_NULL;
+      int dirport = INIT_PORT_CWDIR;
+
+      switch (doretry)
+	{
+	case FS_RETRY_REAUTH:
+	  if (err = reauthenticate (*result))
+	    return err;
+	  /* Fall through.  */
+
+	case FS_RETRY_NORMAL:
+	  if (nloops++ >= SYMLOOP_MAX)
+	    {
+	      __mach_port_deallocate (__mach_task_self (), *result);
+	      return ELOOP;
+	    }
+
+	  /* An empty RETRYNAME indicates we have the final port.  */
+	  if (retryname[0] == '\0' &&
+	      /* If reauth'd, we must do one more retry on "" to give the new
+		 translator a chance to make a new port for us.  */
+	      doretry == FS_RETRY_NORMAL)
+	    {
+	      if (flags & O_NOFOLLOW)
+		{
+		  /* In Linux, O_NOFOLLOW means to reject symlinks.  If we
+		     did an O_NOLINK lookup above and io_stat here to check
+		     for S_IFLNK, a translator like firmlink could easily
+		     spoof this check by not showing S_IFLNK, but in fact
+		     redirecting the lookup to some other name
+		     (i.e. opening the very same holes a symlink would).
+
+		     Instead we do an O_NOTRANS lookup above, and stat the
+		     underlying node: if it has a translator set, and its
+		     owner is not root (st_uid 0) then we reject it.
+		     Since the motivation for this feature is security, and
+		     that security presumes we trust the containing
+		     directory, this check approximates the security of
+		     refusing symlinks while accepting mount points.
+		     Note that we actually permit something Linux doesn't:
+		     we follow root-owned symlinks; if that is deemed
+		     undesireable, we can add a final check for that
+		     one exception to our general translator-based rule.  */
+		  struct stat st;
+		  err = __io_stat (*result, &st);
+		  if (!err
+		      && (st.st_mode & (S_IPTRANS|S_IATRANS)))
+		    {
+		      if (st.st_uid != 0)
+			err = ENOENT;
+		      else if (st.st_mode & S_IPTRANS)
+			{
+			  char buf[1024];
+			  char *trans = buf;
+			  size_t translen = sizeof buf;
+			  err = __file_get_translator (*result,
+						       &trans, &translen);
+			  if (!err
+			      && translen > sizeof _HURD_SYMLINK
+			      && !memcmp (trans,
+					  _HURD_SYMLINK, sizeof _HURD_SYMLINK))
+			    err = ENOENT;
+			}
+		    }
+		}
+
+	      /* We got a successful translation.  Now apply any open-time
+		 action flags we were passed.  */
+
+	      if (!err && (flags & O_TRUNC)) /* Asked to truncate the file.  */
+		err = __file_set_size (*result, 0);
+
+	      if (err)
+		__mach_port_deallocate (__mach_task_self (), *result);
+	      return err;
+	    }
+
+	  startdir = *result;
+	  file_name = retryname;
+	  break;
+
+	case FS_RETRY_MAGICAL:
+	  switch (retryname[0])
+	    {
+	    case '/':
+	      dirport = INIT_PORT_CRDIR;
+	      if (*result != MACH_PORT_NULL)
+		__mach_port_deallocate (__mach_task_self (), *result);
+	      if (nloops++ >= SYMLOOP_MAX)
+		return ELOOP;
+	      file_name = &retryname[1];
+	      break;
+
+	    case 'f':
+	      if (retryname[1] == 'd' && retryname[2] == '/')
+		{
+		  int fd;
+		  char *end;
+		  int save = errno;
+		  errno = 0;
+		  fd = (int) strtol (&retryname[3], &end, 10);
+		  if (end == NULL || errno || /* Malformed number.  */
+		      /* Check for excess text after the number.  A slash
+			 is valid; it ends the component.  Anything else
+			 does not name a numeric file descriptor.  */
+		      (*end != '/' && *end != '\0'))
+		    {
+		      errno = save;
+		      return ENOENT;
+		    }
+		  if (! get_dtable_port)
+		    err = EGRATUITOUS;
+		  else
+		    {
+		      *result = (*get_dtable_port) (fd);
+		      if (*result == MACH_PORT_NULL)
+			{
+			  /* If the name was a proper number, but the file
+			     descriptor does not exist, we return EBADF instead
+			     of ENOENT.  */
+			  err = errno;
+			  errno = save;
+			}
+		    }
+		  errno = save;
+		  if (err)
+		    return err;
+		  if (*end == '\0')
+		    return 0;
+		  else
+		    {
+		      /* Do a normal retry on the remaining components.  */
+		      startdir = *result;
+		      file_name = end + 1; /* Skip the slash.  */
+		      break;
+		    }
+		}
+	      else
+		goto bad_magic;
+	      break;
+
+	    case 'm':
+	      if (retryname[1] == 'a' && retryname[2] == 'c' &&
+		  retryname[3] == 'h' && retryname[4] == 't' &&
+		  retryname[5] == 'y' && retryname[6] == 'p' &&
+		  retryname[7] == 'e')
+		{
+		  error_t err;
+		  struct host_basic_info hostinfo;
+		  mach_msg_type_number_t hostinfocnt = HOST_BASIC_INFO_COUNT;
+		  char *p;
+		  /* XXX want client's host */
+		  if (err = __host_info (__mach_host_self (), HOST_BASIC_INFO,
+					 (natural_t *) &hostinfo,
+					 &hostinfocnt))
+		    return err;
+		  if (hostinfocnt != HOST_BASIC_INFO_COUNT)
+		    return EGRATUITOUS;
+		  p = _itoa (hostinfo.cpu_subtype, &retryname[8], 10, 0);
+		  *--p = '/';
+		  p = _itoa (hostinfo.cpu_type, &retryname[8], 10, 0);
+		  if (p < retryname)
+		    abort ();	/* XXX write this right if this ever happens */
+		  if (p > retryname)
+		    strcpy (retryname, p);
+		  startdir = *result;
+		}
+	      else
+		goto bad_magic;
+	      break;
+
+	    case 't':
+	      if (retryname[1] == 't' && retryname[2] == 'y')
+		switch (retryname[3])
+		  {
+		    error_t opentty (file_t *result)
+		      {
+			error_t err;
+			error_t ctty_open (file_t port)
+			  {
+			    if (port == MACH_PORT_NULL)
+			      return ENXIO; /* No controlling terminal.  */
+			    return __termctty_open_terminal (port,
+							     flags,
+							     result);
+			  }
+			err = (*use_init_port) (INIT_PORT_CTTYID, &ctty_open);
+			if (! err)
+			  err = reauthenticate (*result);
+			return err;
+		      }
+
+		  case '\0':
+		    return opentty (result);
+		  case '/':
+		    if (err = opentty (&startdir))
+		      return err;
+		    strcpy (retryname, &retryname[4]);
+		    break;
+		  default:
+		    goto bad_magic;
+		  }
+	      else
+		goto bad_magic;
+	      break;
+
+	    default:
+	    bad_magic:
+	      return EGRATUITOUS;
+	    }
+	  break;
+
+	default:
+	  return EGRATUITOUS;
+	}
+
+      if (startdir != MACH_PORT_NULL)
+	{
+	  err = lookup_op (startdir);
+	  __mach_port_deallocate (__mach_task_self (), startdir);
+	  startdir = MACH_PORT_NULL;
+	}
+      else
+	err = (*use_init_port) (dirport, &lookup_op);
+    } while (! err);
+
+  return err;
+}
+weak_alias (__hurd_file_name_lookup_retry, hurd_file_name_lookup_retry)
diff --git a/sysdeps/mach/hurd/dl-sysdep.c b/sysdeps/mach/hurd/dl-sysdep.c
index 8bcce13588..d30717b197 100644
--- a/sysdeps/mach/hurd/dl-sysdep.c
+++ b/sysdeps/mach/hurd/dl-sysdep.c
@@ -29,8 +29,7 @@
 #include <sysdep.h>
 #include <mach/mig_support.h>
 #include "hurdstartup.h"
-#include <mach/host_info.h>
-#include <stdio-common/_itoa.h>
+#include <hurd/lookup.h>
 #include <hurd/auth.h>
 #include <hurd/term.h>
 #include <stdarg.h>
@@ -266,20 +265,37 @@ _dl_sysdep_start_cleanup (void)
    dynamic linker re-relocates itself to be user-visible (for -ldl),
    it will get the user's definition (i.e. usually libc's).  */
 
-/* Open FILE_NAME and return a Hurd I/O for it in *PORT, or
-   return an error.  If STAT is non-zero, stat the file into that stat buffer.  */
+/* Open FILE_NAME and return a Hurd I/O for it in *PORT, or return an
+   error.  If STAT is non-zero, stat the file into that stat buffer.  */
 static error_t
-open_file (const char *file_name, int mode,
+open_file (const char *file_name, int flags,
 	   mach_port_t *port, struct stat *stat)
 {
   enum retry_type doretry;
   char retryname[1024];		/* XXX string_t LOSES! */
-  file_t startdir, newpt, fileport;
-  int dealloc_dir;
-  int nloops;
+  file_t startdir;
   error_t err;
 
-  assert (!(mode & ~O_READ));
+  error_t use_init_port (int which, error_t (*operate) (file_t))
+    {
+      return (which < _dl_hurd_data->portarraysize
+	      ? ((*operate) (_dl_hurd_data->portarray[which]))
+	      : EGRATUITOUS);
+    }
+  file_t get_dtable_port (int fd)
+    {
+      if ((unsigned int) fd < _dl_hurd_data->dtablesize
+	  && _dl_hurd_data->dtable[fd] != MACH_PORT_NULL)
+	{
+	  __mach_port_mod_refs (__mach_task_self (), _dl_hurd_data->dtable[fd],
+				MACH_PORT_RIGHT_SEND, +1);
+	  return _dl_hurd_data->dtable[fd];
+	}
+      errno = EBADF;
+      return MACH_PORT_NULL;
+    }
+
+  assert (!(flags & ~O_READ));
 
   startdir = _dl_hurd_data->portarray[file_name[0] == '/' ?
 				      INIT_PORT_CRDIR : INIT_PORT_CWDIR];
@@ -287,216 +303,21 @@ open_file (const char *file_name, int mode,
   while (file_name[0] == '/')
     file_name++;
 
-  if (err = __dir_lookup (startdir, (char *)file_name, mode, 0,
-			  &doretry, retryname, &fileport))
-    return err;
+  err = __dir_lookup (startdir, (char *)file_name, O_RDONLY, 0,
+		      &doretry, retryname, port);
 
-  dealloc_dir = 0;
-  nloops = 0;
-
-  while (1)
+  if (!err)
+    err = __hurd_file_name_lookup_retry (use_init_port, get_dtable_port,
+					 __dir_lookup, doretry, retryname,
+					 O_RDONLY, 0, port);
+  if (!err && stat)
     {
-      if (dealloc_dir)
-	__mach_port_deallocate (__mach_task_self (), startdir);
+      err = __io_stat (*port, stat);
       if (err)
-	return err;
-
-      switch (doretry)
-	{
-	case FS_RETRY_REAUTH:
-	  {
-	    mach_port_t ref = __mach_reply_port ();
-	    err = __io_reauthenticate (fileport, ref, MACH_MSG_TYPE_MAKE_SEND);
-	    if (! err)
-	      err = __auth_user_authenticate
-		(_dl_hurd_data->portarray[INIT_PORT_AUTH],
-		 ref, MACH_MSG_TYPE_MAKE_SEND,
-		 &newpt);
-	    __mach_port_destroy (__mach_task_self (), ref);
-	  }
-	  __mach_port_deallocate (__mach_task_self (), fileport);
-	  if (err)
-	    return err;
-	  fileport = newpt;
-	  /* Fall through.  */
-
-	case FS_RETRY_NORMAL:
-#ifdef SYMLOOP_MAX
-	  if (nloops++ >= SYMLOOP_MAX)
-	    return ELOOP;
-#endif
-
-	  /* An empty RETRYNAME indicates we have the final port.  */
-	  if (retryname[0] == '\0')
-	    {
-	      dealloc_dir = 1;
-	    opened:
-	      /* We have the file open.  Now map it.  */
-	      if (stat)
-		err = __io_stat (fileport, stat);
-
-	      if (err)
-		{
-		  if (dealloc_dir)
-		    __mach_port_deallocate (__mach_task_self (), fileport);
-		}
-	      else
-		{
-		  if (!dealloc_dir)
-		    __mach_port_mod_refs (__mach_task_self (), fileport,
-					  MACH_PORT_RIGHT_SEND, 1);
-		  *port = fileport;
-		}
-
-	      return err;
-	    }
-
-	  startdir = fileport;
-	  dealloc_dir = 1;
-	  file_name = retryname;
-	  break;
-
-	case FS_RETRY_MAGICAL:
-	  switch (retryname[0])
-	    {
-	    case '/':
-	      startdir = _dl_hurd_data->portarray[INIT_PORT_CRDIR];
-	      dealloc_dir = 0;
-	      if (fileport != MACH_PORT_NULL)
-		__mach_port_deallocate (__mach_task_self (), fileport);
-	      file_name = &retryname[1];
-	      break;
-
-	    case 'f':
-	      if (retryname[1] == 'd' && retryname[2] == '/' &&
-		  isdigit (retryname[3]))
-		{
-		  /* We can't use strtol for the decoding here
-		     because it brings in hairy locale bloat.  */
-		  char *p;
-		  int fd = 0;
-		  for (p = &retryname[3]; isdigit (*p); ++p)
-		    fd = (fd * 10) + (*p - '0');
-		  /* Check for excess text after the number.  A slash is
-		     valid; it ends the component.  Anything else does not
-		     name a numeric file descriptor.  */
-		  if (*p != '/' && *p != '\0')
-		    return ENOENT;
-		  if (fd < 0 || fd >= _dl_hurd_data->dtablesize ||
-		      _dl_hurd_data->dtable[fd] == MACH_PORT_NULL)
-		    /* If the name was a proper number, but the file
-		       descriptor does not exist, we return EBADF instead
-		       of ENOENT.  */
-		    return EBADF;
-		  fileport = _dl_hurd_data->dtable[fd];
-		  if (*p == '\0')
-		    {
-		      /* This descriptor is the file port we want.  */
-		      dealloc_dir = 0;
-		      goto opened;
-		    }
-		  else
-		    {
-		      /* Do a normal retry on the remaining components.  */
-		      startdir = fileport;
-		      dealloc_dir = 1;
-		      file_name = p + 1; /* Skip the slash.  */
-		      break;
-		    }
-		}
-	      else
-		goto bad_magic;
-	      break;
-
-	    case 'm':
-	      if (retryname[1] == 'a' && retryname[2] == 'c' &&
-		  retryname[3] == 'h' && retryname[4] == 't' &&
-		  retryname[5] == 'y' && retryname[6] == 'p' &&
-		  retryname[7] == 'e')
-		{
-		  error_t err;
-		  struct host_basic_info hostinfo;
-		  mach_msg_type_number_t hostinfocnt = HOST_BASIC_INFO_COUNT;
-		  char *p;
-		  if (err = __host_info (__mach_host_self (), HOST_BASIC_INFO,
-					 (natural_t *) &hostinfo,
-					 &hostinfocnt))
-		    return err;
-		  if (hostinfocnt != HOST_BASIC_INFO_COUNT)
-		    return EGRATUITOUS;
-		  p = _itoa (hostinfo.cpu_subtype, &retryname[8], 10, 0);
-		  *--p = '/';
-		  p = _itoa (hostinfo.cpu_type, &retryname[8], 10, 0);
-		  if (p < retryname)
-		    abort ();	/* XXX write this right if this ever happens */
-		  if (p > retryname)
-		    strcpy (retryname, p);
-		  startdir = fileport;
-		  dealloc_dir = 1;
-		}
-	      else
-		goto bad_magic;
-	      break;
-
-	    case 't':
-	      if (retryname[1] == 't' && retryname[2] == 'y')
-		switch (retryname[3])
-		  {
-		    error_t opentty (file_t *result)
-		      {
-			error_t err;
-			file_t unauth;
-			err = __termctty_open_terminal
-			  (_dl_hurd_data->portarray[INIT_PORT_CTTYID],
-			   mode, &unauth);
-			if (! err)
-			  {
-			    mach_port_t ref = __mach_reply_port ();
-			    err = __io_reauthenticate
-			      (unauth, ref, MACH_MSG_TYPE_MAKE_SEND);
-			    if (! err)
-			      err = __auth_user_authenticate
-				(_dl_hurd_data->portarray[INIT_PORT_AUTH],
-				 ref, MACH_MSG_TYPE_MAKE_SEND,
-				 result);
-			    __mach_port_deallocate (__mach_task_self (),
-						    unauth);
-			    __mach_port_destroy (__mach_task_self (), ref);
-			  }
-			return err;
-		      }
-
-		  case '\0':
-		    if (err = opentty (&fileport))
-		      return err;
-		    dealloc_dir = 1;
-		    goto opened;
-		  case '/':
-		    if (err = opentty (&startdir))
-		      return err;
-		    dealloc_dir = 1;
-		    strcpy (retryname, &retryname[4]);
-		    break;
-		  default:
-		    goto bad_magic;
-		  }
-	      else
-		goto bad_magic;
-	      break;
-
-	    default:
-	    bad_magic:
-	      return EGRATUITOUS;
-	    }
-	  break;
-
-	default:
-	  return EGRATUITOUS;
-	}
-
-      err = __dir_lookup (startdir, (char *)file_name, mode, 0,
-			  &doretry, retryname, &fileport);
+	__mach_port_deallocate (__mach_task_self (), *port);
     }
+
+  return err;
 }
 
 int weak_function