From: David van Moolenbroek Date: Sun, 27 Dec 2015 19:35:12 +0000 (+0000) Subject: UDS: prepare for socket file creation in bind(2) X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/zpipe.c?a=commitdiff_plain;h=refs%2Fchanges%2F25%2F3425%2F1;p=minix.git UDS: prepare for socket file creation in bind(2) 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 number pair for the created or checked file name, and make returning the canonized path name optional. Change-Id: I892d04b3301d4b911bdc571632ddde65fb747a8a --- diff --git a/minix/include/minix/callnr.h b/minix/include/minix/callnr.h index 759350af6..a88dc1ac6 100644 --- a/minix/include/minix/callnr.h +++ b/minix/include/minix/callnr.h @@ -116,7 +116,7 @@ #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) diff --git a/minix/include/minix/ipc.h b/minix/include/minix/ipc.h index 5bfac14e7..990bb3489 100644 --- a/minix/include/minix/ipc.h +++ b/minix/include/minix/ipc.h @@ -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; diff --git a/minix/include/minix/syslib.h b/minix/include/minix/syslib.h index 1d0611f70..905472cb8 100644 --- a/minix/include/minix/syslib.h +++ b/minix/include/minix/syslib.h @@ -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 */ diff --git a/minix/lib/libsys/Makefile b/minix/lib/libsys/Makefile index 84a5b3aa0..e8702051c 100644 --- a/minix/lib/libsys/Makefile +++ b/minix/lib/libsys/Makefile @@ -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 index 0998e64d8..000000000 --- a/minix/lib/libsys/checkperms.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "syslib.h" - -#include -#include -#include - -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 index 000000000..2473a7815 --- /dev/null +++ b/minix/lib/libsys/socketpath.c @@ -0,0 +1,35 @@ +#include "syslib.h" + +#include +#include +#include + +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; +} diff --git a/minix/net/uds/ioc_uds.c b/minix/net/uds/ioc_uds.c index 3ac44084d..23d8563df 100644 --- a/minix/net/uds/ioc_uds.c +++ b/minix/net/uds/ioc_uds.c @@ -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, diff --git a/minix/net/uds/uds.c b/minix/net/uds/uds.c index 49929548a..baca3c1ed 100644 --- a/minix/net/uds/uds.c +++ b/minix/net/uds/uds.c @@ -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, diff --git a/minix/net/uds/uds.h b/minix/net/uds/uds.h index db9a87940..cfcafdf99 100644 --- a/minix/net/uds/uds.h +++ b/minix/net/uds/uds.h @@ -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; diff --git a/minix/servers/vfs/path.c b/minix/servers/vfs/path.c index 21fab6f1a..5f0191a4b 100644 --- a/minix/servers/vfs/path.c +++ b/minix/servers/vfs/path.c @@ -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); } diff --git a/minix/servers/vfs/proto.h b/minix/servers/vfs/proto.h index d9a3b2068..17f593c1a 100644 --- a/minix/servers/vfs/proto.h +++ b/minix/servers/vfs/proto.h @@ -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); diff --git a/minix/servers/vfs/table.c b/minix/servers/vfs/table.c index f3b24ec96..dde3eb5c4 100644 --- a/minix/servers/vfs/table.c +++ b/minix/servers/vfs/table.c @@ -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) */ diff --git a/minix/tests/test56.c b/minix/tests/test56.c index 9a408911b..4ce66e5f5 100644 --- a/minix/tests/test56.c +++ b/minix/tests/test56.c @@ -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