]> Zhao Yanbai Git Server - minix.git/commitdiff
UDS: prepare for socket file creation in bind(2) 25/3425/1
authorDavid van Moolenbroek <david@minix3.org>
Sun, 27 Dec 2015 19:35:12 +0000 (19:35 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Thu, 9 Mar 2017 23:39:53 +0000 (23:39 +0000)
This patch prepares for moving of the creation of socket files on the
file system from the libc bind(2) stub into the UDS service.  This
change is necessary for the socket type agnostic libc implementation.
The change is not yet activated - the code that is not yet used is
enclosed in "#if NOT_YET" blocks.  The activation needs to be atomic
with UDS's switch to libsockdriver; otherwise, user applications may
break.

As part of the change, various UDS bind(2) semantics are changed to
match the POSIX standard and other operating systems.  In
implementation terms, the service-only VFS API checkperms(2) is
renamed to socketpath(2), and extended with a new subcall which
creates a new socket file.  An extension to test56 checks the new
bind(2) semantics of UDS, although most new tests are still disabled
until activation as well.

Finally, as further preparation for a more structural redesign of the
UDS service, also return the <device,inode> number pair for the
created or checked file name, and make returning the canonized path
name optional.

Change-Id: I892d04b3301d4b911bdc571632ddde65fb747a8a

13 files changed:
minix/include/minix/callnr.h
minix/include/minix/ipc.h
minix/include/minix/syslib.h
minix/lib/libsys/Makefile
minix/lib/libsys/checkperms.c [deleted file]
minix/lib/libsys/socketpath.c [new file with mode: 0644]
minix/net/uds/ioc_uds.c
minix/net/uds/uds.c
minix/net/uds/uds.h
minix/servers/vfs/path.c
minix/servers/vfs/proto.h
minix/servers/vfs/table.c
minix/tests/test56.c

index 759350af6c788ac815cc28054b369e3889e59f52..a88dc1ac64447f39fbb8f75ba80ae72033d237d2 100644 (file)
 #define VFS_GCOV_FLUSH         (VFS_BASE + 44)
 #define VFS_MAPDRIVER          (VFS_BASE + 45)
 #define VFS_COPYFD             (VFS_BASE + 46)
-#define VFS_CHECKPERMS         (VFS_BASE + 47)
+#define VFS_SOCKETPATH         (VFS_BASE + 47)
 #define VFS_GETSYSINFO         (VFS_BASE + 48)
 #define VFS_SOCKET             (VFS_BASE + 49)
 #define VFS_SOCKETPAIR         (VFS_BASE + 50)
index 5bfac14e73c0943d6f81725c1cdc69b85633c0a9..990bb3489ff9585807809e8391abc556664215ac 100644 (file)
@@ -1451,15 +1451,6 @@ typedef struct {
 } mess_lsys_tty_fkey_ctl;
 _ASSERT_MSG_SIZE(mess_lsys_tty_fkey_ctl);
 
-typedef struct {
-       endpoint_t endpt;
-       cp_grant_id_t grant;
-       size_t count;
-
-       uint8_t padding[44];
-} mess_lsys_vfs_checkperms;
-_ASSERT_MSG_SIZE(mess_lsys_vfs_checkperms);
-
 typedef struct {
        endpoint_t endpt;
        int fd;
@@ -1480,6 +1471,16 @@ typedef struct {
 } mess_lsys_vfs_mapdriver;
 _ASSERT_MSG_SIZE(mess_lsys_vfs_mapdriver);
 
+typedef struct {
+       endpoint_t endpt;
+       cp_grant_id_t grant;
+       size_t count;
+       int what;
+
+       uint8_t padding[40];
+} mess_lsys_vfs_socketpath;
+_ASSERT_MSG_SIZE(mess_lsys_vfs_socketpath);
+
 typedef struct {
        endpoint_t      endpt;
        void            *addr;
@@ -2266,6 +2267,14 @@ typedef struct {
 } mess_vfs_lsys_gcov;
 _ASSERT_MSG_SIZE(mess_vfs_lsys_gcov);
 
+typedef struct {
+       dev_t device;
+       ino_t inode;
+
+       uint8_t padding[40];
+} mess_vfs_lsys_socketpath;
+_ASSERT_MSG_SIZE(mess_vfs_lsys_socketpath);
+
 typedef struct {
        time_t atime;
        time_t mtime;
@@ -2481,9 +2490,9 @@ typedef struct noxfer_message {
                mess_lsys_sched_scheduling_start m_lsys_sched_scheduling_start;
                mess_lsys_sched_scheduling_stop m_lsys_sched_scheduling_stop;
                mess_lsys_tty_fkey_ctl  m_lsys_tty_fkey_ctl;
-               mess_lsys_vfs_checkperms m_lsys_vfs_checkperms;
                mess_lsys_vfs_copyfd    m_lsys_vfs_copyfd;
                mess_lsys_vfs_mapdriver m_lsys_vfs_mapdriver;
+               mess_lsys_vfs_socketpath m_lsys_vfs_socketpath;
                mess_lsys_vm_getref     m_lsys_vm_getref;
                mess_lsys_vm_info       m_lsys_vm_info;
                mess_lsys_vm_map_phys   m_lsys_vm_map_phys;
@@ -2567,6 +2576,7 @@ typedef struct noxfer_message {
                mess_vfs_lsockdriver_simple     m_vfs_lsockdriver_simple;
                mess_vfs_lsockdriver_socket     m_vfs_lsockdriver_socket;
                mess_vfs_lsys_gcov      m_vfs_lsys_gcov;
+               mess_vfs_lsys_socketpath        m_vfs_lsys_socketpath;
                mess_vfs_utimens        m_vfs_utimens;
                mess_vm_vfs_mmap        m_vm_vfs_mmap;
                mess_vmmcp              m_vmmcp;
index 1d0611f7066e9ff7653f748d218cebb745033d54..905472cb8868490949ee95983664daeba0dbf7be 100644 (file)
@@ -269,7 +269,11 @@ int mapdriver(const char *label, devmajor_t major, const int *domains,
 pid_t getnpid(endpoint_t proc_ep);
 uid_t getnuid(endpoint_t proc_ep);
 gid_t getngid(endpoint_t proc_ep);
-int checkperms(endpoint_t endpt, char *path, size_t size);
+int socketpath(endpoint_t endpt, char *path, size_t size, int what, dev_t *dev,
+       ino_t *ino);
+#define SPATH_CHECK    0       /* check user permissions on socket path */
+#define SPATH_CREATE   1       /* create socket file at given path */
+#define SPATH_CANONIZE 0x8000  /* copy back canonized path (legacy support) */
 int copyfd(endpoint_t endpt, int fd, int what);
 #define COPYFD_FROM    0       /* copy file descriptor from remote process */
 #define COPYFD_TO      1       /* copy file descriptor to remote process */
index 84a5b3aa0ac87d75c9777f9b9f21d5d284c1fcf4..e8702051ca031bc7e65a23e8ab832b48550f9305 100644 (file)
@@ -13,7 +13,6 @@ SRCS+=  \
        alloc_util.c \
        assert.c \
        asynsend.c \
-       checkperms.c \
        clock_time.c \
        closenb.c \
        copyfd.c \
@@ -49,6 +48,7 @@ SRCS+=  \
        sef_ping.c \
        sef_signal.c \
        sef_st.c \
+       socketpath.c \
        sqrt_approx.c \
        srv_fork.c \
        srv_kill.c \
diff --git a/minix/lib/libsys/checkperms.c b/minix/lib/libsys/checkperms.c
deleted file mode 100644 (file)
index 0998e64..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "syslib.h"
-
-#include <unistd.h>
-#include <string.h>
-#include <minix/safecopies.h>
-
-int
-checkperms(endpoint_t endpt, char *path, size_t size)
-{
-       cp_grant_id_t grant;
-       message m;
-       int r;
-
-       if ((grant = cpf_grant_direct(VFS_PROC_NR, (vir_bytes) path, size,
-           CPF_READ | CPF_WRITE)) == GRANT_INVALID)
-               return ENOMEM;
-
-       memset(&m, 0, sizeof(m));
-       m.m_lsys_vfs_checkperms.endpt = endpt;
-       m.m_lsys_vfs_checkperms.grant = grant;
-       m.m_lsys_vfs_checkperms.count = size;
-
-       r = _taskcall(VFS_PROC_NR, VFS_CHECKPERMS, &m);
-
-       cpf_revoke(grant);
-
-       return r;
-}
diff --git a/minix/lib/libsys/socketpath.c b/minix/lib/libsys/socketpath.c
new file mode 100644 (file)
index 0000000..2473a78
--- /dev/null
@@ -0,0 +1,35 @@
+#include "syslib.h"
+
+#include <unistd.h>
+#include <string.h>
+#include <minix/safecopies.h>
+
+int
+socketpath(endpoint_t endpt, char * path, size_t size, int what, dev_t * dev,
+       ino_t * ino)
+{
+       cp_grant_id_t grant;
+       message m;
+       int r;
+
+       if ((grant = cpf_grant_direct(VFS_PROC_NR, (vir_bytes)path, size,
+           CPF_READ | CPF_WRITE)) == GRANT_INVALID)
+               return ENOMEM;
+
+       memset(&m, 0, sizeof(m));
+       m.m_lsys_vfs_socketpath.endpt = endpt;
+       m.m_lsys_vfs_socketpath.grant = grant;
+       m.m_lsys_vfs_socketpath.count = size;
+       m.m_lsys_vfs_socketpath.what = what | SPATH_CANONIZE;
+
+       r = _taskcall(VFS_PROC_NR, VFS_SOCKETPATH, &m);
+
+       cpf_revoke(grant);
+
+       if (r == OK) {
+               *dev = m.m_vfs_lsys_socketpath.device;
+               *ino = m.m_vfs_lsys_socketpath.inode;
+       }
+
+       return r;
+}
index 3ac44084d76b45eb21c4bc555aa4a847e96eedbd..23d8563dff58472d3794e1f3464cb887afbaa816 100644 (file)
@@ -67,10 +67,11 @@ do_accept(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
 
        /* Locate the server socket. */
        for (i = 0; i < NR_FDS; i++) {
-               if (uds_fd_table[i].addr.sun_family == AF_UNIX &&
+               if (uds_fd_table[i].stale == FALSE &&
+                   uds_fd_table[i].listening == TRUE &&
+                   uds_fd_table[i].addr.sun_family == AF_UNIX &&
                    !strncmp(addr.sun_path, uds_fd_table[i].addr.sun_path,
-                   sizeof(uds_fd_table[i].addr.sun_path)) &&
-                       uds_fd_table[i].listening == 1)
+                   sizeof(uds_fd_table[i].addr.sun_path)))
                        break;
        }
 
@@ -146,6 +147,8 @@ do_connect(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
        int child, peer;
        struct sockaddr_un addr;
        int rc, i, j;
+       dev_t dev;
+       ino_t ino;
 
        dprintf(("UDS: do_connect(%d)\n", minor));
 
@@ -167,8 +170,8 @@ do_connect(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
            sizeof(struct sockaddr_un))) != OK)
                return rc;
 
-       if ((rc = checkperms(uds_fd_table[minor].owner, addr.sun_path,
-           sizeof(addr.sun_path))) != OK)
+       if ((rc = socketpath(uds_fd_table[minor].owner, addr.sun_path,
+           sizeof(addr.sun_path), SPATH_CHECK, &dev, &ino)) != OK)
                return rc;
 
        /*
@@ -178,7 +181,9 @@ do_connect(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
        for (i = 0; i < NR_FDS; i++) {
                if (uds_fd_table[minor].type != uds_fd_table[i].type)
                        continue;
-               if (!uds_fd_table[i].listening)
+               if (uds_fd_table[i].listening == FALSE)
+                       continue;
+               if (uds_fd_table[i].stale == TRUE)
                        continue;
                if (uds_fd_table[i].addr.sun_family != AF_UNIX)
                        continue;
@@ -276,7 +281,7 @@ do_listen(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
            sizeof(backlog_size))) != OK)
                return rc;
 
-       if (uds_fd_table[minor].listening == 0) {
+       if (uds_fd_table[minor].listening == FALSE) {
                /* Set the backlog size to a reasonable value. */
                if (backlog_size <= 0 || backlog_size > UDS_SOMAXCONN)
                        backlog_size = UDS_SOMAXCONN;
@@ -295,7 +300,7 @@ do_listen(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
        }
 
        /* This socket is now listening. */
-       uds_fd_table[minor].listening = 1;
+       uds_fd_table[minor].listening = TRUE;
 
        return OK;
 }
@@ -334,6 +339,8 @@ do_bind(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
 {
        struct sockaddr_un addr;
        int rc, i;
+       dev_t dev;
+       ino_t ino;
 
        dprintf(("UDS: do_bind(%d)\n", minor));
 
@@ -356,21 +363,41 @@ do_bind(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
        if (addr.sun_path[0] == '\0')
                return ENOENT;
 
-       if ((rc = checkperms(uds_fd_table[minor].owner, addr.sun_path,
-               sizeof(addr.sun_path))) != OK)
+       /* Attempt to create the socket file. */
+       if ((rc = socketpath(uds_fd_table[minor].owner, addr.sun_path,
+#if NOT_YET
+           sizeof(addr.sun_path), SPATH_CREATE, &dev, &ino)) != OK)
+#else
+           sizeof(addr.sun_path), SPATH_CHECK, &dev, &ino)) != OK)
+#endif
                return rc;
 
-       /* Make sure the address isn't already in use by another socket. */
+       /*
+        * It is possible that the socket path name was already in use as
+        * address by another socket.  This means that the socket file was
+        * prematurely unlinked.  In that case, mark the old socket as stale,
+        * so that its path name will not be matched and only the newly bound
+        * socket will be found in address-based searches.  For now, we leave
+        * the old socket marked as stale for as long as it is bound to the
+        * same address.  A more advanced implementation could establish an
+        * order between the sockets so that the most recently bound socket is
+        * found at any time, but it is doubtful whether that would be useful.
+        */
        for (i = 0; i < NR_FDS; i++) {
-               if (uds_fd_table[i].addr.sun_family == AF_UNIX &&
+               if (uds_fd_table[i].stale == FALSE &&
+                   uds_fd_table[i].addr.sun_family == AF_UNIX &&
                    !strncmp(addr.sun_path, uds_fd_table[i].addr.sun_path,
                    sizeof(uds_fd_table[i].addr.sun_path))) {
-                       /* Another socket is bound to this sun_path. */
+#if NOT_YET
+                       uds_fd_table[i].stale = TRUE;
+#else
                        return EADDRINUSE;
+#endif
                }
        }
 
        /* Looks good, perform the bind(). */
+       uds_fd_table[minor].stale = FALSE;
        memcpy(&uds_fd_table[minor].addr, &addr, sizeof(struct sockaddr_un));
 
        return OK;
@@ -597,6 +624,8 @@ do_sendto(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
 {
        int rc;
        struct sockaddr_un addr;
+       dev_t dev;
+       ino_t ino;
 
        dprintf(("UDS: do_sendto(%d)\n", minor));
 
@@ -612,8 +641,8 @@ do_sendto(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
        if (addr.sun_family != AF_UNIX || addr.sun_path[0] == '\0')
                return EINVAL;
 
-       if ((rc = checkperms(uds_fd_table[minor].owner, addr.sun_path,
-           sizeof(addr.sun_path))) != OK)
+       if ((rc = socketpath(uds_fd_table[minor].owner, addr.sun_path,
+           sizeof(addr.sun_path), SPATH_CHECK, &dev, &ino)) != OK)
                return rc;
 
        memcpy(&uds_fd_table[minor].target, &addr, sizeof(struct sockaddr_un));
@@ -825,6 +854,7 @@ do_sendmsg(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
                         * target address.
                         */
                        if (uds_fd_table[i].type == SOCK_DGRAM &&
+                           uds_fd_table[i].stale == FALSE &&
                            uds_fd_table[i].addr.sun_family == AF_UNIX &&
                            !strncmp(uds_fd_table[minor].target.sun_path,
                            uds_fd_table[i].addr.sun_path,
index 49929548afdcb28ed0798de57fa998fc80b5147c..baca3c1ed5b697095f2b2cd5fd322c28fd837118 100644 (file)
@@ -94,7 +94,8 @@ uds_open(devminor_t UNUSED(orig_minor), int access,
        for (i = 0; i < OPEN_MAX; i++)
                uds_fd_table[minor].ancillary_data.fds[i] = -1;
 
-       uds_fd_table[minor].listening = 0;
+       uds_fd_table[minor].stale = FALSE;
+       uds_fd_table[minor].listening = FALSE;
        uds_fd_table[minor].peer = -1;
        uds_fd_table[minor].child = -1;
 
@@ -203,7 +204,7 @@ uds_select(devminor_t minor, unsigned int ops, endpoint_t endpt)
                bytes = uds_perform_read(minor, NONE, GRANT_INVALID, 1, 1);
                if (bytes > 0) {
                        ready_ops |= CDEV_OP_RD;        /* data available */
-               } else if (uds_fd_table[minor].listening == 1) {
+               } else if (uds_fd_table[minor].listening == TRUE) {
                        /* Check for pending connections. */
                        for (i = 0; i < uds_fd_table[minor].backlog_size; i++)
                        {
@@ -389,6 +390,7 @@ uds_perform_write(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant,
                         * the target address.
                         */
                        if (uds_fd_table[i].type == SOCK_DGRAM &&
+                           uds_fd_table[i].stale == FALSE &&
                            uds_fd_table[i].addr.sun_family == AF_UNIX &&
                            !strncmp(uds_fd_table[minor].target.sun_path,
                            uds_fd_table[i].addr.sun_path,
index db9a879403123f78b6362e02d0966aae9d4ad425..cfcafdf9901544a09c073ac00e98737c5bf64d30 100644 (file)
@@ -121,8 +121,14 @@ struct uds_fd {
         */
        struct sockaddr_un source;
 
-       /* Flag (1 or 0) - listening for incoming connections.
-        * Default to 0. Set to 1 by do_listen()
+       /* Flag (TRUE or FALSE) - address overridden by newer socket.
+        * Default to FALSE.  Set to TRUE by do_bind() on another socket with
+        * the same path but its on-disk socket file removed in the meantime.
+        */
+       int stale;
+
+       /* Flag (TRUE or FALSE) - listening for incoming connections.
+        * Default to FALSE.  Set to TRUE by do_listen().
         */
        int listening;
 
index 21fab6f1a725589b825c38ebea00c324d77902cd..5f0191a4b71eda9f87ae368392b1897d485c6687 100644 (file)
@@ -33,8 +33,6 @@
 
 static int lookup(struct vnode *dirp, struct lookup *resolve,
        node_details_t *node, struct fproc *rfp);
-static int check_perms(endpoint_t ep, cp_grant_id_t io_gr, size_t
-       pathlen);
 
 /*===========================================================================*
  *                             advance                                      *
@@ -801,64 +799,145 @@ canonical_path(char orig_path[PATH_MAX], struct fproc *rfp)
 }
 
 /*===========================================================================*
- *                             check_perms                                  *
+ *                             do_socketpath                                *
  *===========================================================================*/
-static int check_perms(ep, io_gr, pathlen)
-endpoint_t ep;
-cp_grant_id_t io_gr;
-size_t pathlen;
+int do_socketpath(void)
 {
-  int r, slot;
-  struct vnode *vp;
-  struct vmnt *vmp;
+/*
+ * Perform a path action on an on-disk socket file.  This call may be performed
+ * by the UDS service only.  The action is always on behalf of a user process
+ * that is currently making a socket call to the UDS service, and thus, VFS may
+ * rely on the fact that the user process is blocked.  TODO: there should be
+ * checks in place to prevent (even accidental) abuse of this function, though.
+ */
+  int r, what, slot;
+  endpoint_t ep;
+  cp_grant_id_t io_gr;
+  size_t pathlen;
+  struct vnode *dirp, *vp;
+  struct vmnt *vmp, *vmp2;
   struct fproc *rfp;
-  char canon_path[PATH_MAX];
-  struct lookup resolve;
+  char path[PATH_MAX];
+  struct lookup resolve, resolve2;
   struct sockaddr_un sun;
+  mode_t bits;
+
+  /* This should be replaced by an ACL check. */
+  if (!super_user) return EPERM;
+
+  ep = job_m_in.m_lsys_vfs_socketpath.endpt;
+  io_gr = job_m_in.m_lsys_vfs_socketpath.grant;
+  pathlen = job_m_in.m_lsys_vfs_socketpath.count;
+  what = job_m_in.m_lsys_vfs_socketpath.what;
 
   if (isokendpt(ep, &slot) != OK) return(EINVAL);
   if (pathlen < sizeof(sun.sun_path) || pathlen >= PATH_MAX) return(EINVAL);
 
   rfp = &(fproc[slot]);
-  r = sys_safecopyfrom(who_e, io_gr, (vir_bytes) 0, (vir_bytes) canon_path,
-       pathlen);
+  r = sys_safecopyfrom(who_e, io_gr, (vir_bytes)0, (vir_bytes)path, pathlen);
   if (r != OK) return(r);
-  canon_path[pathlen] = '\0';
+  path[pathlen] = '\0';
 
-  /* Turn path into canonical path to the socket file */
-  if ((r = canonical_path(canon_path, rfp)) != OK) return(r);
-  if (strlen(canon_path) >= pathlen) return(ENAMETOOLONG);
+  /* If requested, turn path into canonical path to the socket file */
+  if (what & SPATH_CANONIZE) {
+       if ((r = canonical_path(path, rfp)) != OK) return(r);
+       if (strlen(path) >= pathlen) return(ENAMETOOLONG);
 
-  /* copy canon_path back to the caller */
-  r = sys_safecopyto(who_e, (cp_grant_id_t) io_gr, (vir_bytes) 0,
-       (vir_bytes) canon_path, pathlen);
-  if (r != OK) return(r);
+       /* copy path back to the caller */
+       r = sys_safecopyto(who_e, (cp_grant_id_t)io_gr, (vir_bytes)0,
+           (vir_bytes)path, pathlen);
+       if (r != OK) return(r);
+  }
 
-  /* Now do permissions checking */
-  lookup_init(&resolve, canon_path, PATH_NOFLAGS, &vmp, &vp);
-  resolve.l_vmnt_lock = VMNT_READ;
-  resolve.l_vnode_lock = VNODE_READ;
-  if ((vp = eat_path(&resolve, rfp)) == NULL) return(err_code);
+  /* Now perform the requested action.  For the SPATH_CHECK action, a socket
+   * file is expected to exist already, and we should check whether the given
+   * user process has access to it.  For the SPATH_CREATE action, no file is
+   * expected to exist yet, and a socket file should be created on behalf of
+   * the user process.  In both cases, on success, return the socket file's
+   * device and inode numbers to the caller.
+   *
+   * Since the above canonicalization releases all locks once done, we need to
+   * recheck absolutely everything now.  TODO: do not release locks in between.
+   */
+  switch (what & ~SPATH_CANONIZE) {
+  case SPATH_CHECK:
+       lookup_init(&resolve, path, PATH_NOFLAGS, &vmp, &vp);
+       resolve.l_vmnt_lock = VMNT_READ;
+       resolve.l_vnode_lock = VNODE_READ;
+       if ((vp = eat_path(&resolve, rfp)) == NULL) return(err_code);
 
-  /* check permissions */
-  r = forbidden(rfp, vp, (R_BIT | W_BIT));
+       /* Check file type and permissions. */
+       if (!S_ISSOCK(vp->v_mode))
+               r = ENOTSOCK; /* not in POSIX spec; this is what NetBSD does */
+       else
+               r = forbidden(rfp, vp, R_BIT | W_BIT);
 
-  unlock_vnode(vp);
-  unlock_vmnt(vmp);
+       if (r == OK) {
+               job_m_out.m_vfs_lsys_socketpath.device = vp->v_dev;
+               job_m_out.m_vfs_lsys_socketpath.inode = vp->v_inode_nr;
+       }
 
-  put_vnode(vp);
-  return(r);
-}
+       unlock_vnode(vp);
+       unlock_vmnt(vmp);
+       put_vnode(vp);
+       break;
 
-/*===========================================================================*
- *                             do_checkperms                                *
- *===========================================================================*/
-int do_checkperms(void)
-{
-  /* This should be replaced by an ACL check. */
-  if (!super_user) return EPERM;
+  case SPATH_CREATE:
+       /* This is effectively simulating a mknod(2) call by the user process,
+        * including the application of its umask to the file permissions.
+        */
+       lookup_init(&resolve, path, PATH_RET_SYMLINK, &vmp, &dirp);
+       resolve.l_vmnt_lock = VMNT_WRITE;
+       resolve.l_vnode_lock = VNODE_WRITE;
+
+       if ((dirp = last_dir(&resolve, rfp)) == NULL) return(err_code);
+
+       bits = S_IFSOCK | (ACCESSPERMS & rfp->fp_umask);
+
+       if (!S_ISDIR(dirp->v_mode))
+               r = ENOTDIR;
+       else if ((r = forbidden(rfp, dirp, W_BIT | X_BIT)) == OK) {
+               r = req_mknod(dirp->v_fs_e, dirp->v_inode_nr, path,
+                   rfp->fp_effuid, rfp->fp_effgid, bits, NO_DEV);
+               if (r == OK) {
+                       /* Now we need to find out the device and inode number
+                        * of the socket file we just created.  The vmnt lock
+                        * should prevent any trouble here.
+                        */
+                       lookup_init(&resolve2, resolve.l_path,
+                           PATH_RET_SYMLINK, &vmp2, &vp);
+                       resolve2.l_vmnt_lock = VMNT_READ;
+                       resolve2.l_vnode_lock = VNODE_READ;
+                       vp = advance(dirp, &resolve2, rfp);
+                       assert(vmp2 == NULL);
+                       if (vp != NULL) {
+                               job_m_out.m_vfs_lsys_socketpath.device =
+                                   vp->v_dev;
+                               job_m_out.m_vfs_lsys_socketpath.inode =
+                                   vp->v_inode_nr;
+                               unlock_vnode(vp);
+                               put_vnode(vp);
+                       } else {
+                               /* Huh.  This should never happen.  If it does,
+                                * we assume the socket file has somehow been
+                                * lost, so we do not try to unlink it.
+                                */
+                               printf("VFS: socketpath did not find created "
+                                   "node at %s (%d)\n", path, err_code);
+                               r = err_code;
+                       }
+               } else if (r == EEXIST)
+                       r = EADDRINUSE;
+       }
+
+       unlock_vnode(dirp);
+       unlock_vmnt(vmp);
+       put_vnode(dirp);
+       break;
 
-  return check_perms(job_m_in.m_lsys_vfs_checkperms.endpt,
-       job_m_in.m_lsys_vfs_checkperms.grant,
-       job_m_in.m_lsys_vfs_checkperms.count);
+  default:
+       r = ENOSYS;
+  }
+
+  return(r);
 }
index d9a3b2068e8732f6120361c333532d4ea733e090..17f593c1a9e48679d7171e0d8f7b77f6c2bedd35 100644 (file)
@@ -170,7 +170,7 @@ void lookup_init(struct lookup *resolve, char *path, int flags, struct
        vmnt **vmp, struct vnode **vp);
 int get_name(struct vnode *dirp, struct vnode *entry, char *_name);
 int canonical_path(char *orig_path, struct fproc *rfp);
-int do_checkperms(void);
+int do_socketpath(void);
 
 /* pipe.c */
 int do_pipe2(void);
index f3b24ec96abb301a1e7acfb6035d661e4cff9d43..dde3eb5c42d7cd82a1fbbeb222edde6cb89497e8 100644 (file)
@@ -62,7 +62,7 @@ int (* const call_vec[NR_VFS_CALLS])(void) = {
        CALL(VFS_GCOV_FLUSH)    = do_gcov_flush,        /* gcov_flush(2) */
        CALL(VFS_MAPDRIVER)     = do_mapdriver,         /* mapdriver(2) */
        CALL(VFS_COPYFD)        = do_copyfd,            /* copyfd(2) */
-       CALL(VFS_CHECKPERMS)    = do_checkperms,        /* checkperms(2) */
+       CALL(VFS_SOCKETPATH)    = do_socketpath,        /* socketpath(2) */
        CALL(VFS_GETSYSINFO)    = do_getsysinfo,        /* getsysinfo(2) */
        CALL(VFS_SOCKET)        = do_socket,            /* socket(2) */
        CALL(VFS_SOCKETPAIR)    = do_socketpair,        /* socketpair(2) */
index 9a408911bac9302098ace35c275728287f356191..4ce66e5f591c59dd228f7d988cf513a52d5cffc1 100644 (file)
@@ -1390,14 +1390,197 @@ static void
 test_file(void)
 {
        struct sockaddr_un addr;
-       int sd;
+#if NOT_YET
+       struct sockaddr_un saddr, saddr2;
+       char buf[1];
+       socklen_t len;
+       struct stat st;
+       mode_t omask;
+       int, csd, fd;
+#endif
+       int sd, sd2;
 
+       /*
+        * If the provided socket path exists on the file system, the bind(2)
+        * call must fail, regardless of whether there is a socket that is
+        * bound to that address.
+        */
        UNLINK(TEST_SUN_PATH);
 
        memset(&addr, 0, sizeof(addr));
        addr.sun_family = AF_UNIX;
        strlcpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path));
 
+       if ((sd = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1)
+               test_fail("Can't open socket");
+       if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
+               test_fail("Can't bind socket");
+
+       if ((sd2 = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1)
+               test_fail("Can't open socket");
+       if (bind(sd2, (struct sockaddr *)&addr, sizeof(addr)) != -1)
+               test_fail("Binding socket unexpectedly succeeded");
+       if (errno != EADDRINUSE)
+               test_fail("Binding socket failed with wrong error");
+
+       CLOSE(sd);
+
+#if NOT_YET
+       if (bind(sd2, (struct sockaddr *)&addr, sizeof(addr)) != -1)
+               test_fail("Binding socket unexpectedly succeeded");
+       if (errno != EADDRINUSE)
+               test_fail("Binding socket failed with wrong error");
+
+       CLOSE(sd2);
+
+       UNLINK(TEST_SUN_PATH);
+
+       /*
+        * If the socket is removed from the file system while there is still a
+        * socket bound to it, it should be possible to bind a new socket to
+        * the address.  The old socket should then become unreachable in terms
+        * of connections and data directed to the address, even though it
+        * should still appear to be bound to the same address.
+        */
+       if ((sd = socket(PF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1)
+               test_fail("Can't open socket");
+       if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
+               test_fail("Can't bind socket");
+       memset(&saddr, 0, sizeof(saddr));
+       len = sizeof(saddr);
+       if (getsockname(sd, (struct sockaddr *)&saddr, &len) != 0)
+               test_fail("Can't get socket address");
+
+       if ((csd = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1)
+               test_fail("Can't open client socket");
+
+       memset(buf, 'X', sizeof(buf));
+       if (sendto(csd, buf, sizeof(buf), 0, (struct sockaddr *)&addr,
+           sizeof(addr)) != sizeof(buf))
+               test_fail("Can't send to socket");
+       if (recvfrom(sd, buf, sizeof(buf), 0, NULL, 0) != sizeof(buf))
+               test_fail("Can't receive from socket");
+       if (buf[0] != 'X')
+               test_fail("Transmission failure");
+
+       if (unlink(TEST_SUN_PATH) != 0)
+               test_fail("Can't unlink socket");
+
+       if ((sd2 = socket(PF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1)
+               test_fail("Can't open socket");
+       if (bind(sd2, (struct sockaddr *)&addr, sizeof(addr)) != 0)
+               test_fail("Can't bind socket");
+       memset(&saddr2, 0, sizeof(saddr2));
+       len = sizeof(saddr2);
+       if (getsockname(sd2, (struct sockaddr *)&saddr2, &len) != 0)
+               test_fail("Can't get socket address");
+       if (memcmp(&saddr, &saddr2, sizeof(saddr)))
+               test_fail("Unexpected socket address");
+
+       memset(buf, 'Y', sizeof(buf));
+       if (sendto(csd, buf, sizeof(buf), 0, (struct sockaddr *)&addr,
+           sizeof(addr)) != sizeof(buf))
+               test_fail("Can't send to socket");
+       if (recvfrom(sd, buf, sizeof(buf), 0, NULL, 0) != -1)
+               test_fail("Unexpectedly received from old socket");
+       if (errno != EWOULDBLOCK)
+               test_fail("Wrong receive failure from old socket");
+       if (recvfrom(sd2, buf, sizeof(buf), 0, NULL, 0) != sizeof(buf))
+               test_fail("Can't receive from new socket");
+       if (buf[0] != 'Y')
+               test_fail("Transmission failure");
+
+       len = sizeof(saddr2);
+       if (getsockname(sd, (struct sockaddr *)&saddr2, &len) != 0)
+               test_fail("Can't get old socket address");
+       if (memcmp(&saddr, &saddr2, sizeof(saddr)))
+               test_fail("Unexpected old socket address");
+
+       /*
+        * Currently, our implementation "hides" the old socket even if the new
+        * socket is closed, but since this is not standard behavior and may be
+        * changed later, we do not test for it.  However, in any case,
+        * rebinding the hidden socket should make it "visible" again.
+        */
+       strlcpy(saddr2.sun_path, TEST_SUN_PATHB, sizeof(saddr2.sun_path));
+       if (bind(sd, (struct sockaddr *)&saddr2, sizeof(saddr2)) != 0)
+               test_fail("Can't rebind socket");
+
+       memset(buf, 'Z', sizeof(buf));
+       if (sendto(csd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr2,
+           sizeof(saddr2)) != sizeof(buf))
+               test_fail("Can't send to socket");
+       if (recvfrom(sd, buf, sizeof(buf), 0, NULL, 0) != sizeof(buf))
+               test_fail("Can't receive from socket");
+       if (buf[0] != 'Z')
+               test_fail("Transmission failure");
+
+       if (unlink(TEST_SUN_PATH) != 0)
+               test_fail("Can't unlink socket");
+       if (unlink(TEST_SUN_PATHB) != 0)
+               test_fail("Can't unlink other socket");
+
+       CLOSE(sd);
+       CLOSE(sd2);
+       CLOSE(csd);
+
+       /*
+        * If the socket path identifies a file that is not a socket, bind(2)
+        * should still fail with EADDRINUSE, but connect(2) and sendto(2)
+        * should fail with ENOTSOCK (the latter is not specified by POSIX, so
+        * we follow NetBSD here).
+        */
+       if ((fd = open(TEST_SUN_PATH, O_WRONLY | O_CREAT | O_EXCL, 0700)) < 0)
+               test_fail("Can't create regular file");
+       CLOSE(fd);
+
+       if ((sd = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1)
+               test_fail("Can't open socket");
+       if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != -1)
+               test_fail("Binding socket unexpectedly succeeded");
+       if (errno != EADDRINUSE)
+               test_fail("Binding socket failed with wrong error");
+       if (sendto(sd, buf, sizeof(buf), 0, (struct sockaddr *)&addr,
+           sizeof(addr)) != -1)
+               test_fail("Sending to socket unexpectedly succeeded");
+       if (errno != ENOTSOCK)
+               test_fail("Sending to socket failed with wrong error");
+       CLOSE(sd);
+
+       if ((sd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
+               test_fail("Can't open socket");
+       if (connect(sd, (struct sockaddr *)&addr, sizeof(addr)) != -1)
+               test_fail("Connecting socket unexpectedly succeeded");
+       if (errno != ENOTSOCK)
+               test_fail("Connecting socket failed with wrong error");
+       CLOSE(sd);
+
+       UNLINK(TEST_SUN_PATH);
+
+       /*
+        * The created socket file should have an access mode of 0777 corrected
+        * with the calling process's file mode creation mask.
+        */
+       omask = umask(045123);
+
+       if ((sd = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1)
+               test_fail("Can't open socket");
+       if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
+               test_fail("Can't bind socket");
+
+       if (stat(TEST_SUN_PATH, &st) != 0)
+               test_fail("Can't stat socket");
+       if (!S_ISSOCK(st.st_mode))
+               test_fail("File is not a socket");
+       if ((st.st_mode & ALLPERMS) != (ACCESSPERMS & ~0123))
+               test_fail("Unexpected file permission mask");
+
+       CLOSE(sd);
+       UNLINK(TEST_SUN_PATH);
+
+       umask(omask);
+#endif
+
        /*
         * Only socket(2), socketpair(2), and accept(2) may be used to obtain
         * new file descriptors to sockets (or "sockets"); open(2) on a socket