2005-07-11  Marcus Brinkmann  <marcus@gnu.org>

	* hurd/Makefile (routines): Add sysvshm.
	(distribute): Add sysvshm.h.
	* hurd/sysvshm.h: New file.
	* hurd/sysvshm.c: New file.
	* sysdeps/mach/hurd/bits/stat.h (S_IMMAP0): New macro.
	(S_ISPARE): Unset the S_IMMAP0 flag.
	* sysdeps/mach/hurd/ftok.c: New file.
	* sysdeps/mach/hurd/shmat.c: New file.
	* sysdeps/mach/hurd/shmctl.c: New file.
	* sysdeps/mach/hurd/shmdt.c: New file.

diff -rupN libc/hurd/Makefile libc-shm/hurd/Makefile
--- libc/hurd/Makefile	2002-10-14 03:03:09.000000000 +0200
+++ libc-shm/hurd/Makefile	2005-07-12 12:27:06.000000000 +0200
@@ -60,6 +60,7 @@ routines = hurdstartup hurdinit \
 	   vpprintf \
 	   ports-get ports-set hurdports hurdmsg \
 	   errno-loc \
+	   sysvshm \
 	   $(sig) $(dtable) $(inlines) port-cleanup report-wait
 sig	= hurdsig hurdfault siginfo hurd-raise preempt-sig \
 	  trampoline longjmp-ts catch-exc exc2signal hurdkill sigunwind \
@@ -68,7 +69,7 @@ dtable	= dtable port2fd new-fd alloc-fd 
 	  getdport openport \
 	  fd-close fd-read fd-write hurdioctl ctty-input ctty-output
 inlines = $(inline-headers:%.h=%-inlines)
-distribute = hurdstartup.h hurdfault.h hurdhost.h \
+distribute = hurdstartup.h hurdfault.h hurdhost.h sysvshm.h \
 	     faultexc.defs intr-rpc.defs intr-rpc.h intr-msg.h Notes
 
 # XXX this is a temporary hack; see hurdmalloc.h
diff -rupN libc/hurd/sysvshm.c libc-shm/hurd/sysvshm.c
--- libc/hurd/sysvshm.c	1970-01-01 01:00:00.000000000 +0100
+++ libc-shm/hurd/sysvshm.c	2005-07-11 22:57:25.000000000 +0200
@@ -0,0 +1,96 @@
+/* Copyright (C) 2005 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 Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/shm.h>
+
+
+/* Description of an shm attachment.  */
+struct sysvshm_attach
+{
+  /* Linked list.  */
+  struct sysvshm_attach *next;
+
+  /* Map address.  */
+  void *addr;
+
+  /* Map size.  */
+  size_t size;
+};
+
+/* List of attachments.  */
+static struct sysvshm_attach *attach_list;
+
+/* A lock to protect the linked list of shared memory attachments.  */
+static struct mutex sysvshm_lock = MUTEX_INITIALIZER;
+
+
+/* Adds a segment attachment.  */
+error_t
+__sysvshm_add (void *addr, size_t size)
+{
+  struct sysvshm_attach *shm;
+
+  shm = malloc (sizeof (*shm));
+  if (!shm)
+    return errno;
+
+  __mutex_lock (&sysvshm_lock);
+  shm->addr = addr;
+  shm->size = size;
+  shm->next = attach_list;
+  attach_list = shm;
+  __mutex_unlock (&sysvshm_lock);
+
+  return 0;
+}
+
+/* Removes a segment attachment.  Returns its size if found, or EINVAL
+   otherwise.  */
+error_t
+__sysvshm_remove (void *addr, size_t *size)
+{
+  struct sysvshm_attach *shm;
+  struct sysvshm_attach **pshm = &attach_list;
+
+  __mutex_lock (&sysvshm_lock);
+  shm = attach_list;
+  while (shm)
+    {
+      shm = *pshm;
+      if (shm->addr == addr)
+	{
+	  *pshm = shm->next;
+	  *size = shm->size;
+	  __mutex_unlock (&sysvshm_lock);
+	  return 0;
+	}
+      pshm = &shm->next;
+      shm = shm->next;
+    }
+  __mutex_unlock (&sysvshm_lock);
+  return EINVAL;
+}
diff -rupN libc/hurd/sysvshm.h libc-shm/hurd/sysvshm.h
--- libc/hurd/sysvshm.h	1970-01-01 01:00:00.000000000 +0100
+++ libc-shm/hurd/sysvshm.h	2005-07-11 22:57:25.000000000 +0200
@@ -0,0 +1,47 @@
+/* Copyright (C) 2005 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 Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <paths.h>
+#include <hurd.h>
+
+/* The area (from top to bottom) that is used for private keys.  These
+   are all keys that have the second highest bit set.  */
+#define SHM_PRIV_KEY_START INT_MAX
+#define SHM_PRIV_KEY_END ((INT_MAX / 2) + 1)
+
+#define SHM_PREFIX "shm-"
+#define SHM_DIR _PATH_DEV "shm/"
+
+/* The maximum number of characters in a shared memory segment file name.
+   32 is the max number of characters in a 128 bit number in hex.  */
+#if __WORDSIZE > 128
+#error Need to increase SHM_NAMEMAX.
+#else
+#define SHM_NAMEMAX (sizeof (SHM_PREFIX) - 1 + 32 + 1)
+#endif
+
+/* Use this with printf and its variants.  */
+#define SHM_NAMEPRI SHM_PREFIX "%0x"
+
+
+/* Adds a segment attachment.  */
+error_t __sysvshm_add (void *addr, size_t size);
+
+/* Removes a segment attachment.  Returns its size if found, or EINVAL
+   otherwise.  */
+error_t __sysvshm_remove (void *addr, size_t *size);
diff -rupN libc/sysdeps/mach/hurd/bits/stat.h libc-shm/sysdeps/mach/hurd/bits/stat.h
--- libc/sysdeps/mach/hurd/bits/stat.h	2005-07-11 23:39:36.000000000 +0200
+++ libc-shm/sysdeps/mach/hurd/bits/stat.h	2005-07-11 22:58:05.000000000 +0200
@@ -1,4 +1,4 @@
-/* Copyright (C) 1992, 93, 94, 96, 97, 99, 2000 Free Software Foundation, Inc.
+/* Copyright (C) 1992,93,94,96,97,99,2000,2005 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
@@ -182,8 +182,11 @@ struct stat64
 /* All the bits relevant to translators */
 #define S_ITRANS	000070000000
 
+/* Definitely no mmaps to this.  */
+#define S_IMMAP0	000100000000
+
 /* ALL the unused bits.  */
-#define	S_ISPARE	(~(S_IFMT|S_ITRANS|S_INOCACHE|    \
+#define	S_ISPARE	(~(S_IFMT|S_ITRANS|S_INOCACHE|S_IMMAP0|    \
 			   S_IUSEUNK|S_IUNKNOWN|07777))
 #endif
 
diff -rupN libc/sysdeps/mach/hurd/ftok.c libc-shm/sysdeps/mach/hurd/ftok.c
--- libc/sysdeps/mach/hurd/ftok.c	1970-01-01 01:00:00.000000000 +0100
+++ libc-shm/sysdeps/mach/hurd/ftok.c	2005-07-11 22:57:25.000000000 +0200
@@ -0,0 +1,43 @@
+/* Copyright (C) 1995, 1996, 2000, 2005 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, August 1995.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <sys/ipc.h>
+#include <sys/stat.h>
+
+
+/* In the Hurd, we use the second-to-most-significant bit as flag for
+   private keys.  We use a different order of the components so that
+   the biggest one---the inode number---is affected by this.  */
+
+key_t
+ftok (pathname, proj_id)
+     const char *pathname;
+     int proj_id;
+{
+  struct stat64 st;
+  key_t key;
+
+  if (__xstat64 (_STAT_VER, pathname, &st) < 0)
+    return (key_t) -1;
+
+  key = ((st.st_dev & 0xff) | ((proj_id & 0xff) << 8)
+	 | ((st.st_ino & 0x3fff) << 16));
+
+  return key;
+}
diff -rupN libc/sysdeps/mach/hurd/shmat.c libc-shm/sysdeps/mach/hurd/shmat.c
--- libc/sysdeps/mach/hurd/shmat.c	1970-01-01 01:00:00.000000000 +0100
+++ libc-shm/sysdeps/mach/hurd/shmat.c	2005-07-12 12:28:20.000000000 +0200
@@ -0,0 +1,78 @@
+/* Copyright (C) 2005 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 Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <errno.h>
+#include <utime.h>
+#include <stdio.h>
+#include <assert.h>
+#include <sys/mman.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "sysvshm.h"
+
+/* Attach the shared memory segment associated with SHMID to the data
+   segment of the calling process.  SHMADDR and SHMFLG determine how
+   and where the segment is attached.  */
+void *
+__shmat (int shmid, const void *shmaddr, int shmflg)
+{
+  error_t err;
+  char filename[sizeof (SHM_DIR) - 1 + SHM_NAMEMAX];
+  int fd;
+  void *addr;
+  struct stat statbuf;
+  int res;
+
+  sprintf (filename, SHM_DIR SHM_NAMEPRI, shmid);
+  fd = __open (filename, (shmflg & SHM_RDONLY) ? O_RDONLY : O_RDWR);
+  if (fd < 0)
+    {
+      if (errno == ENOENT)
+	errno = EINVAL;
+      return (void *) -1;
+    }
+
+  res = __fstat (fd, &statbuf);
+  if (res < 0)
+    {
+      __close (fd);
+      return (void *) -1;
+    }
+
+  addr = __mmap ((void *) shmaddr, statbuf.st_size,
+		 PROT_READ | ((shmflg & SHM_RDONLY) ? 0 : PROT_WRITE),
+		 MAP_SHARED, fd, 0);
+  __close (fd);
+  if (addr == MAP_FAILED)
+    return (void *) -1;
+
+  err = __sysvshm_add (addr, statbuf.st_size);
+  if (err)
+    {
+      munmap (addr, statbuf.st_size);
+      return (void *) -1;
+    }
+
+  return addr;
+}
+
+weak_alias(__shmat, shmat)
diff -rupN libc/sysdeps/mach/hurd/shmctl.c libc-shm/sysdeps/mach/hurd/shmctl.c
--- libc/sysdeps/mach/hurd/shmctl.c	1970-01-01 01:00:00.000000000 +0100
+++ libc-shm/sysdeps/mach/hurd/shmctl.c	2005-07-12 12:28:25.000000000 +0200
@@ -0,0 +1,132 @@
+/* Copyright (C) 2005 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 Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "sysvshm.h"
+
+/* Provide operations to control over shared memory segments.  */
+int
+__shmctl (int id, int cmd, struct shmid_ds *buf)
+{
+  error_t err = 0;
+  int fd;
+  int res;
+  char filename[sizeof (SHM_DIR) - 1 + SHM_NAMEMAX];
+  struct stat statbuf;
+
+  sprintf (filename, SHM_DIR SHM_NAMEPRI, id);
+  /* SysV requires read access for IPC_STAT.  */
+  fd = __open (filename, O_NORW);
+  if (fd < 0)
+    {
+      if (errno == ENOENT)
+	errno = EINVAL;
+      return -1;
+    }
+
+  res = __fstat (fd, &statbuf);
+  if (res < 0)
+    {
+      err = errno;
+      __close (fd);
+      errno = err;
+      return -1;
+    }
+  
+  switch (cmd)
+    {
+    case IPC_STAT:
+
+      buf->shm_perm.__key = id;
+      buf->shm_perm.uid = statbuf.st_uid;
+      buf->shm_perm.gid = statbuf.st_gid;
+
+      /* We do not support the creator.  */
+      buf->shm_perm.cuid = statbuf.st_uid;
+      buf->shm_perm.cgid = statbuf.st_gid;
+
+      /* We just want the protection bits.  */
+      buf->shm_perm.mode = statbuf.st_mode & 0777;
+      /* Hopeless.  We do not support a sequence number.  */
+      buf->shm_perm.__seq = statbuf.st_ino;
+      buf->shm_segsz = statbuf.st_size;
+
+      /* Hopeless.  We do not support any of these.  */
+      buf->shm_atime = statbuf.st_atime;
+      buf->shm_dtime = statbuf.st_mtime;
+      /* Well, this comes at least close.  */
+      buf->shm_ctime = statbuf.st_ctime;
+
+      /* We do not support the PID.  */
+      buf->shm_cpid = 0;
+      buf->shm_lpid = 0;
+
+      if (statbuf.st_mode & S_IMMAP0)
+        buf->shm_nattch = 0;
+      else
+        /* 42 is the answer.  Of course this is bogus, but for most
+	   applications, this should be fine.  */
+        buf->shm_nattch = 42;
+
+      break;
+
+    case IPC_SET:
+      if (statbuf.st_uid != buf->shm_perm.uid
+	  || statbuf.st_gid != buf->shm_perm.gid)
+	{
+	  res = __fchown (fd,
+			  (statbuf.st_uid != buf->shm_perm.uid)
+			  ? buf->shm_perm.uid : -1,
+			  (statbuf.st_gid != buf->shm_perm.gid)
+			  ? buf->shm_perm.gid : -1);
+	  if (res < 0)
+	    err = errno;
+	}
+
+      if (!err && statbuf.st_mode & 0777 != buf->shm_perm.mode & 0777)
+	{
+	  res = __fchmod (fd, (statbuf.st_mode & ~0777)
+			  | (buf->shm_perm.mode & 0777));
+	  if (res < 0)
+	    err = errno;
+	}
+      break;
+
+    case IPC_RMID:
+      res = __unlink (filename);
+      /* FIXME: Check error (mapping ENOENT to EINVAL).  */
+      break;
+
+    default:
+      err = EINVAL;
+    }
+
+  __close (fd);
+  errno = err;
+  return err ? -1 : 0;
+}
+
+weak_alias(__shmctl, shmctl)
diff -rupN libc/sysdeps/mach/hurd/shmdt.c libc-shm/sysdeps/mach/hurd/shmdt.c
--- libc/sysdeps/mach/hurd/shmdt.c	1970-01-01 01:00:00.000000000 +0100
+++ libc-shm/sysdeps/mach/hurd/shmdt.c	2005-07-11 22:57:25.000000000 +0200
@@ -0,0 +1,51 @@
+/* Copyright (C) 2005 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 Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+#include <sys/mman.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include "sysvshm.h"
+
+/* Detach shared memory segment starting at address specified by
+   SHMADDR from the caller's data segment.  */
+int
+__shmdt (const void *shmaddr)
+{
+  error_t err;
+  size_t size;
+
+  err = __sysvshm_remove ((void *) shmaddr, &size);
+  if (err)
+    {
+      errno = err;
+      return -1;
+    }
+
+  __munmap ((void *) shmaddr, size);
+  return 0;
+}
+
+weak_alias(__shmdt, shmdt)
diff -rupN libc/sysdeps/mach/hurd/shmget.c libc-shm/sysdeps/mach/hurd/shmget.c
--- libc/sysdeps/mach/hurd/shmget.c	1970-01-01 01:00:00.000000000 +0100
+++ libc-shm/sysdeps/mach/hurd/shmget.c	2005-07-11 22:57:25.000000000 +0200
@@ -0,0 +1,245 @@
+/* Copyright (C) 2005 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 Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/shm.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <hurd/fd.h>
+
+#include "sysvshm.h"
+
+/* Create a new shared memory segment file without linking it into the
+   filesystem.  Return the directory and file ports in R_DIR and R_FILE.  */
+static error_t
+create_shm_file (size_t size, int flags, file_t *r_dir, file_t *r_file)
+{
+  error_t err;
+  file_t dir;
+  file_t file;
+
+  flags &= 0777;
+
+  /* Get a port to the directory that will contain the file.  */
+  dir = __file_name_lookup (SHM_DIR, 0, 0);
+  if (dir == MACH_PORT_NULL)
+    return errno;
+
+  /* Create an unnamed file in the directory.  */
+  err = __dir_mkfile (dir, O_RDWR, flags, &file);
+  if (err)
+    {
+      __mach_port_deallocate (__mach_task_self (), dir);
+      return err;
+    }
+
+  err = __file_set_size (file, size);
+  if (err)
+    {
+      __mach_port_deallocate (__mach_task_self (), file);
+      __mach_port_deallocate (__mach_task_self (), dir);
+
+      return err;
+    }
+
+  *r_dir = dir;
+  *r_file = file;
+
+  return 0;
+}
+  
+
+/* Open the shared memory segment *R_KEY and return a file descriptor
+   to it in R_FD.  If KEY is IPC_PRIVATE, use a private key and return
+   it in R_KEY.  */
+static error_t
+get_exclusive (int shmflags, size_t size, key_t *r_key, int *r_fd)
+{
+  error_t err;
+  file_t dir;
+  file_t file;
+  char filename[SHM_NAMEMAX];
+  key_t key = *r_key;
+  bool is_private;
+
+  /* Create the shared memory segment.  */
+  err = create_shm_file (size, shmflags, &dir, &file);
+  if (err)
+    return err;
+
+  if (key == IPC_PRIVATE)
+    {
+      is_private = true;
+      key = SHM_PRIV_KEY_START;
+
+      /* Try to link the shared memory segment into the filesystem
+	 (exclusively).  Private segments have negative keys.  */
+      do
+	{
+	  sprintf (filename, SHM_NAMEPRI, key);
+	  err = __dir_link (dir, file, filename, 1);
+	  if (!err)
+	    {
+	      /* We are done.  */
+	      *r_key = key;
+	      break;
+	    }
+	  else if (err == EEXIST)
+	    {
+	      /* Check if we ran out of keys.  If not, try again with new
+		 key.  */
+	      if (key == SHM_PRIV_KEY_END)
+		err = ENOSPC;
+	      else
+		err = 0;
+
+	      key--;
+	    }
+	}
+      while (!err);
+    }
+  else
+    {
+      /* Try to link the shared memory segment into the filesystem
+	 (exclusively) under the given key.  */
+      sprintf (filename, SHM_NAMEPRI, key);
+      err = __dir_link (dir, file, filename, 1);
+    }
+
+  __mach_port_deallocate (__mach_task_self (), dir);
+
+  if (!err)
+    {
+      int fd;
+
+      /* Get a file descriptor for that port.  */
+      fd = _hurd_intern_fd (file, O_RDWR, 1); /* dealloc on error */
+      if (fd < 0)
+	err = errno;
+      else
+	*r_fd = fd;
+    }
+
+  return err;
+}
+
+
+/* Open the shared memory segment KEY (creating it if it doesn't yet
+   exist) and return a file descriptor to it in R_FD.  */
+static error_t
+get_shared (int shmflags, size_t size, key_t key, int *r_fd)
+{
+  error_t err = 0;
+  char filename[sizeof (SHM_DIR) - 1 + SHM_NAMEMAX];
+  int fd = -1;
+  int create_flag;
+
+  create_flag = (shmflags & IPC_CREAT) ? O_CREAT : 0;
+  sprintf (filename, SHM_DIR SHM_NAMEPRI, key);
+
+  do
+    {
+      fd = __open (filename, O_NORW | create_flag, shmflags & 0777);
+
+      if (fd < 0 && errno != ENOENT)
+	/* We give up.  */
+	return errno;
+      else if (fd >= 0)
+	{
+	  int res;
+	  struct stat statbuf;
+
+	  /* Check the size (we only need to do this if we did not
+	     create the shared memory segment file ourselves).  */
+	  res = __fstat (fd, &statbuf);
+	  if (res < 0)
+	    {
+	      err = errno;
+	      __close (fd);
+	      return err;
+	    }
+
+	  if (statbuf.st_size < size)
+	    {
+	      __close (fd);
+	      return EINVAL;
+	    }
+	}	  
+      else
+	{
+	  /* The memory segment doesn't exist.  */
+	  if (create_flag)
+	    {
+	      /* Try to create it exclusively.  */
+	      err = get_exclusive (shmflags, size, &key, &fd);
+	      if (err == EEXIST)
+		/* If somebody created it in the meanwhile, just try again.  */
+		err = 0;
+	    }
+	  else
+	    err = ENOENT;
+	}
+    }
+  while (fd < 0 && !err);
+
+  if (!err)
+    *r_fd = fd;
+  else
+    *r_fd = -1;
+
+  return err;
+}
+
+/* Return an identifier for an shared memory segment of at least size
+   SIZE which is associated with KEY.  */
+int
+__shmget (key_t key, size_t size, int shmflags)
+{
+  error_t err;
+  int fd;
+
+  if (key == IPC_PRIVATE || shmflags & IPC_EXCL)
+    /* An exclusive shared memory segment must be created.  */
+    err = get_exclusive (shmflags, size, &key, &fd);
+  else
+    err = get_shared (shmflags, size, key, &fd);
+
+  if (err)
+    {
+      errno = err;
+      return -1;
+    }
+
+  /* From here, we can't fail.  That's important, as otherwise we
+     would need to unlink the file if we created it (in that case, the
+     code above would have to be changed to pass a "created" flag down
+     to the caller).  */
+
+  __close (fd);
+
+  return key;
+}
+
+weak_alias(__shmget, shmget)
