#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)
} 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;
} 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;
} 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;
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;
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;
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 */
alloc_util.c \
assert.c \
asynsend.c \
- checkperms.c \
clock_time.c \
closenb.c \
copyfd.c \
sef_ping.c \
sef_signal.c \
sef_st.c \
+ socketpath.c \
sqrt_approx.c \
srv_fork.c \
srv_kill.c \
+++ /dev/null
-#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;
-}
--- /dev/null
+#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;
+}
/* 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;
}
int child, peer;
struct sockaddr_un addr;
int rc, i, j;
+ dev_t dev;
+ ino_t ino;
dprintf(("UDS: do_connect(%d)\n", minor));
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;
/*
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;
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;
}
/* This socket is now listening. */
- uds_fd_table[minor].listening = 1;
+ uds_fd_table[minor].listening = TRUE;
return OK;
}
{
struct sockaddr_un addr;
int rc, i;
+ dev_t dev;
+ ino_t ino;
dprintf(("UDS: do_bind(%d)\n", minor));
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;
{
int rc;
struct sockaddr_un addr;
+ dev_t dev;
+ ino_t ino;
dprintf(("UDS: do_sendto(%d)\n", minor));
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));
* 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,
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;
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++)
{
* 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,
*/
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;
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 *
}
/*===========================================================================*
- * 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);
}
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);
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) */
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