Hi,

This patch adds support for SCM_RIGHTS to glibc. It works fine
when sending file descriptors from a socket() or a socketpair() call,
but not from e.g. an open() call, as I've mentioned in another mail.
That's not because of my patch though.

The attached testcase runs fine with the #if set to 1 (i.e. sending
socket fds).

Regards,
Emilio

>From bd70862e18ba6c2a404917bffef72de367a3b132 Mon Sep 17 00:00:00 2001
From: Emilio Pozuelo Monfort <poch...@gmail.com>
Date: Sat, 17 Jul 2010 22:09:13 +0200
Subject: [PATCH] Add support to send file descriptors over Unix sockets

---
 sysdeps/mach/hurd/recvmsg.c |  108 +++++++++++++++++++++++++++++++++++++++++-
 sysdeps/mach/hurd/sendmsg.c |   53 ++++++++++++++++++++-
 2 files changed, 155 insertions(+), 6 deletions(-)

diff --git a/sysdeps/mach/hurd/recvmsg.c b/sysdeps/mach/hurd/recvmsg.c
index 33897b8..cda246e 100644
--- a/sysdeps/mach/hurd/recvmsg.c
+++ b/sysdeps/mach/hurd/recvmsg.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+/* Copyright (C) 2001, 2002, 2010 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
@@ -33,13 +33,37 @@ __libc_recvmsg (int fd, struct msghdr *message, int flags)
   addr_port_t aport;
   char *data = NULL;
   mach_msg_type_number_t len = 0;
-  mach_port_t *ports;
+  mach_port_t *ports, *newports;
   mach_msg_type_number_t nports = 0;
+  struct cmsghdr *cmsg;
   char *cdata = NULL;
   mach_msg_type_number_t clen = 0;
   size_t amount;
   char *buf;
-  int i;
+  int nfds, *fds;
+  int i, j;
+
+  auth_t auth;
+
+  error_t reauthenticate (mach_port_t port, mach_port_t *result)
+    {
+      error_t err;
+      mach_port_t ref;
+      if (*result != MACH_PORT_NULL)
+       return 0;
+      ref = __mach_reply_port ();
+      do
+       err = __io_reauthenticate (port, ref, MACH_MSG_TYPE_MAKE_SEND);
+      while (err == EINTR);
+      if (!err)
+       do
+         err = __auth_user_authenticate (auth,
+                                         ref, MACH_MSG_TYPE_MAKE_SEND,
+                                         result);
+       while (err == EINTR);
+      __mach_port_destroy (__mach_task_self (), ref);
+      return err;
+    }
 
   /* Find the total number of bytes to be read.  */
   amount = 0;
@@ -136,6 +160,84 @@ __libc_recvmsg (int fd, struct msghdr *message, int flags)
     message->msg_controllen = clen;
   memcpy (message->msg_control, cdata, message->msg_controllen);
 
+  /* SCM_RIGHTS ports.  */
+  if (nports > 0)
+    {
+      auth = getauth ();
+      newports = __alloca (nports * sizeof (mach_port_t));
+
+      /* Reauthenticate all ports here.  */
+      for (i = 0; i < nports; i++)
+       {
+         newports[i] = MACH_PORT_NULL;
+         err = reauthenticate (ports[i], &newports[i]);
+         __mach_port_destroy (__mach_task_self (), ports[i]);
+         if (err)
+           {
+             for (j = 0; j < i; j++)
+               __mach_port_destroy (__mach_task_self (), newports[j]);
+             for (j = i+1; j < nports; j++)
+               __mach_port_destroy (__mach_task_self (), ports[j]);
+
+             __vm_deallocate (__mach_task_self (), (vm_address_t) cdata, clen);
+             __hurd_fail (err);
+           }
+       }
+
+      j = 0;
+      for (cmsg = CMSG_FIRSTHDR (message);
+          cmsg;
+          cmsg = CMSG_NXTHDR (message, cmsg))
+       {
+         if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
+           {
+             fds = (int *) CMSG_DATA (cmsg);
+             nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr)))
+                    / sizeof (int);
+
+             for (i = 0; i < nfds && j < nports; i++)
+               {
+                 /* The fd's flags are passed in the control data.  */
+                 if ((fds[i] = _hurd_intern_fd (newports[j++], fds[i], 0))
+                     == -1)
+                   {
+                     err = errno;
+                     goto cleanup;
+                   }
+               }
+           }
+       }
+      if (j != nports)
+       /* Clean up all the file descriptors.  */
+       {
+         err = EGRATUITOUS;
+ cleanup:
+         nports = j;
+         j = 0;
+         for (cmsg = CMSG_FIRSTHDR (message);
+              cmsg;
+              cmsg = CMSG_NXTHDR (message, cmsg))
+           {
+             if (cmsg->cmsg_level == SOL_SOCKET
+                 && cmsg->cmsg_type == SCM_RIGHTS)
+               {
+                 fds = (int *) CMSG_DATA (cmsg);
+                 nfds = (cmsg->cmsg_len
+                         - CMSG_ALIGN (sizeof (struct cmsghdr)))
+                         / sizeof (int);
+                 for (i = 0; i < nfds && j < nports; i++, j++)
+                   _hurd_fd_close (_hurd_fd_get (fds[i]));
+               }
+           }
+
+         for (; j < nports; j++)
+           __mach_port_destroy (__mach_task_self (), newports[j]);
+
+         __vm_deallocate (__mach_task_self (), (vm_address_t) cdata, clen);
+         __hurd_fail (err);
+       }
+    }
+
   __vm_deallocate (__mach_task_self (), (vm_address_t) cdata, clen);
 
   return (buf - data);
diff --git a/sysdeps/mach/hurd/sendmsg.c b/sysdeps/mach/hurd/sendmsg.c
index 118fd59..7c6c666 100644
--- a/sysdeps/mach/hurd/sendmsg.c
+++ b/sysdeps/mach/hurd/sendmsg.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001,2002,2004 Free Software Foundation, Inc.
+/* Copyright (C) 2001,2002,2004,2010 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
@@ -32,6 +32,10 @@ ssize_t
 __libc_sendmsg (int fd, const struct msghdr *message, int flags)
 {
   error_t err = 0;
+  struct cmsghdr *cmsg;
+  mach_port_t *ports = NULL;
+  mach_msg_type_number_t nports = 0;
+  int *fds, nfds;
   struct sockaddr_un *addr = message->msg_name;
   socklen_t addr_len = message->msg_namelen;
   addr_port_t aport = MACH_PORT_NULL;
@@ -101,6 +105,48 @@ __libc_sendmsg (int fd, const struct msghdr *message, int 
flags)
        }
     }
 
+  /* SCM_RIGHTS support: get the number of fds to send.  */
+  for (cmsg = CMSG_FIRSTHDR (message); cmsg; cmsg = CMSG_NXTHDR (message, 
cmsg))
+    if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
+      nports += (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr)))
+               / sizeof (int);
+
+  if (nports)
+    ports = __alloca (nports * sizeof (mach_port_t));
+
+  nports = 0;
+  for (cmsg = CMSG_FIRSTHDR (message); cmsg; cmsg = CMSG_NXTHDR (message, 
cmsg))
+    {
+      if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
+       {
+         fds = (int *) CMSG_DATA (cmsg);
+         nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr)))
+                / sizeof (int);
+
+         for (i = 0; i < nfds; i++)
+           {
+             err = HURD_DPORT_USE
+               (fds[i],
+                ({
+                  err = __io_restrict_auth (port, &ports[nports++],
+                                            0, 0, 0, 0);
+                  /* We pass the flags in the control data.  */
+                  fds[i] = descriptor->flags;
+                }));
+
+             if (err)
+               {
+                 for (i = 0; i < nports - 1; i++)
+                   __mach_port_deallocate (__mach_task_self (), ports[i]);
+
+                 if (dealloc)
+                   __vm_deallocate (__mach_task_self (), data.addr, len);
+                 __hurd_fail (err);
+               }
+           }
+       }
+    }
+
   if (addr)
     {
       if (addr->sun_family == AF_LOCAL)
@@ -143,8 +189,9 @@ __libc_sendmsg (int fd, const struct msghdr *message, int 
flags)
                              /* Send the data.  */
                              err = __socket_send (port, aport,
                                                   flags, data.ptr, len,
-                                                  NULL,
-                                                  MACH_MSG_TYPE_COPY_SEND, 0,
+                                                  ports,
+                                                  MACH_MSG_TYPE_MOVE_SEND,
+                                                  nports,
                                                   message->msg_control,
                                                   message->msg_controllen,
                                                   &amount);
-- 
1.7.1

#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

int
main (int argc, char **argv)
{
  struct msghdr msg1  = { 0 }, msg2 = { 0 };
  struct cmsghdr *cmsg;
  struct iovec iov;
  int sv1[2], sv2[2], *fds, dummy;
  char buf[CMSG_SPACE(sizeof sv2)], buf2[2048];

  if (socketpair (PF_UNIX, SOCK_STREAM, 0, sv1) == -1)
    {
      perror ("socketpair");
      return 1;
    }
#if 1
  if (socketpair (PF_UNIX, SOCK_STREAM, 0, sv2) == -1)
    {
      perror ("socketpair");
      return 1;
    }
#else
  if ((sv2[0] = open ("/tmp/file1", O_RDONLY)) == -1)
    {
      perror ("open");
      return 1;
    }
  if ((sv2[1] = open ("/tmp/file2", O_RDONLY)) == -1)
    {
      perror ("open");
      return 1;
    }
#endif

  iov.iov_base = &dummy;
  iov.iov_len = 1;

  msg1.msg_iov = &iov;
  msg1.msg_iovlen = 1;
  msg1.msg_control = buf;
  msg1.msg_controllen = sizeof (buf);

  cmsg = CMSG_FIRSTHDR (&msg1);
  cmsg->cmsg_level = SOL_SOCKET;
  cmsg->cmsg_type = SCM_RIGHTS;
  cmsg->cmsg_len = CMSG_LEN (sizeof (sv2));
  memcpy (CMSG_DATA (cmsg), sv2, sizeof (sv2));

  if (sendmsg (sv1[0], &msg1, 0) < 0)
    {
      perror ("sendmsg");
      return 1;
    }

  assert (close (sv2[0]) == 0);
  assert (close (sv2[1]) == 0);

  msg2.msg_name = NULL;
  msg2.msg_namelen = 0;
  msg2.msg_iov = &iov;
  msg2.msg_iovlen = 1;
  msg2.msg_controllen = sizeof (buf2);
  msg2.msg_control = buf2;

  if (recvmsg (sv1[1], &msg2, msg2.msg_flags) < 0)
    {
      perror ("recvmsg");
      return 1;
    }

  cmsg = CMSG_FIRSTHDR (&msg2);
  assert (cmsg->cmsg_level == SOL_SOCKET);
  assert (cmsg->cmsg_type == SCM_RIGHTS);
  assert (sizeof (sv2) == (cmsg->cmsg_len - ((char *)CMSG_DATA (cmsg) - (char *)cmsg)));

  fds = (int*) CMSG_DATA (cmsg);

  assert (close (fds[0]) == 0);
  assert (close (fds[1]) == 0);

  return 0;
}

Reply via email to