about summary refs log tree commit diff
path: root/sysdeps/mach/hurd/dup2.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/mach/hurd/dup2.c')
-rw-r--r--sysdeps/mach/hurd/dup2.c40
1 files changed, 30 insertions, 10 deletions
diff --git a/sysdeps/mach/hurd/dup2.c b/sysdeps/mach/hurd/dup2.c
index f4ec623b05..c4b16698b8 100644
--- a/sysdeps/mach/hurd/dup2.c
+++ b/sysdeps/mach/hurd/dup2.c
@@ -58,8 +58,7 @@ DEFUN(__dup2, (fd, fd2), int fd AND int fd2)
       io_t ctty = _hurd_port_get (&d->ctty, &ctty_ulink);
       io_t port = _hurd_port_locked_get (&d->port, &ulink); /* Unlocks D.  */
 
-      __mutex_lock (&_hurd_dtable_lock);
-      if (fd2 < 0 || fd2 >= _hurd_dtablesize)
+      if (fd2 < 0)
 	{
 	  errno = EBADF;
 	  fd2 = -1;
@@ -67,16 +66,37 @@ DEFUN(__dup2, (fd, fd2), int fd AND int fd2)
       else
 	{
 	  /* Get a hold of the destination descriptor.  */
-	  struct hurd_fd *d2 = _hurd_dtable[fd2];
-	  if (d2 == NULL)
+	  struct hurd_fd *d2;
+
+	  if (fd2 >= _hurd_dtablesize)
+	    {
+	      /* The table is not large enough to hold the destination
+		 descriptor.  Enlarge it as necessary to allocate this
+		 descriptor.  */
+	      __mutex_unlock (&_hurd_dtable_lock);
+	      /* We still hold FD1's lock, but this is safe because
+		 _hurd_alloc_fd will only examine the cells starting
+		 at FD2.  */
+	      d2 = _hurd_alloc_fd (NULL, fd2);
+	      if (d2)
+		__spin_unlock (&d2->port.lock);
+	      __mutex_lock (&_hurd_dtable_lock);
+	    }
+	  else
 	    {
-	      /* Must allocate a new one.  We don't initialize the port cells
-		 with this call so that if it fails (out of memory), we will
-		 not have already added user references for the ports, which we
-		 would then have to deallocate.  */
-	      d2 = _hurd_dtable[fd2] = _hurd_new_fd (MACH_PORT_NULL,
-						     MACH_PORT_NULL);
+	      d2 = _hurd_dtable[fd2];
+	      if (d2 == NULL)
+		{
+		  /* Must allocate a new one.  We don't initialize the port
+		     cells with this call so that if it fails (out of
+		     memory), we will not have already added user
+		     references for the ports, which we would then have to
+		     deallocate.  */
+		  d2 = _hurd_dtable[fd2] = _hurd_new_fd (MACH_PORT_NULL,
+							 MACH_PORT_NULL);
+		}
 	    }
+
 	  if (d2 == NULL)
 	    {
 	      fd2 = -1;