/* * Author: Manoj Ampalam * * Implementation of POSIX APIs * * Copyright (c) 2015 Microsoft Corp. * All rights reserved * * Microsoft openssh win32 port * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "inc\sys\socket.h" #include "inc\sys\select.h" #include "inc\sys\uio.h" #include "inc\sys\types.h" #include "inc\sys\stat.h" #include "inc\unistd.h" #include "inc\fcntl.h" #include "inc\sys\un.h" #include "inc\utf.h" #include "inc\stdio.h" #include "w32fd.h" #include "signal_internal.h" #include #include #include #include #include #include #include "Shlwapi.h" #include #include "misc_internal.h" #include "debug.h" /* internal table that stores the fd to w32_io mapping*/ struct w32fd_table { w32_fd_set occupied; /*bit map for tracking occipied table entries*/ struct w32_io* w32_ios[MAX_FDS];/*array of references to mapped w32_io objects*/ }; /* mapping table*/ static struct w32fd_table fd_table; /* main thread handle*/ HANDLE main_thread; void fd_table_set(struct w32_io* pio, int index); void fd_decode_state(char*); #define POSIX_FD_STATE "c28fc6f98a2c44abbbd89d6a3037d0d9_POSIX_FD_STATE" #define POSIX_CHROOTW L"c28fc6f98a2c44abbbd89d6a3037d0d9_POSIX_CHROOT" /* __progname */ char* __progname = ""; /* __progdir */ char* __progdir = ""; wchar_t* __wprogdir = L""; /* __progdata */ char* __progdata = ""; wchar_t* __wprogdata = L""; /* initializes mapping table*/ static int fd_table_initialize() { struct w32_io *pio; HANDLE wh; /* table entries representing std in, out and error*/ DWORD wh_index[] = { STD_INPUT_HANDLE , STD_OUTPUT_HANDLE , STD_ERROR_HANDLE }; int fd_num = 0; memset(&fd_table, 0, sizeof(fd_table)); /* prepare std io fds */ for (fd_num = STDIN_FILENO; fd_num <= STDERR_FILENO; fd_num++) { wh = GetStdHandle(wh_index[fd_num]); if (wh != NULL && wh != INVALID_HANDLE_VALUE) { pio = malloc(sizeof(struct w32_io)); if (!pio) { errno = ENOMEM; return -1; } memset(pio, 0, sizeof(struct w32_io)); pio->type = NONSOCK_SYNC_FD; pio->handle = wh; fd_table_set(pio, fd_num); } } /* decode fd state if any */ { char *posix_fd_state; _dupenv_s(&posix_fd_state, NULL, POSIX_FD_STATE); /*TODO - validate parent process - to accomodate these scenarios - * A posix parent process launches a regular process that inturn launches a posix child process * In this case the posix child process may misinterpret POSIX_FD_STATE set by grand parent */ if (NULL != posix_fd_state) { fd_decode_state(posix_fd_state); free(posix_fd_state); _putenv_s(POSIX_FD_STATE, ""); } } /* decode chroot if any */ { _wdupenv_s(&chroot_pathw, NULL, POSIX_CHROOTW); if (chroot_pathw != NULL) { if ((chroot_path = utf16_to_utf8(chroot_pathw)) == NULL) return -1; chroot_path_len = (int) strlen(chroot_path); } } return 0; } /* get a free slot in mapping table with least index*/ static int fd_table_get_min_index() { int min_index = 0; unsigned char* bitmap = fd_table.occupied.bitmap; unsigned char tmp; while (*bitmap == 0xff) { bitmap++; min_index += 8; if (min_index >= MAX_FDS) { errno = EMFILE; debug3("ERROR: MAX_FDS limit reached"); return -1; } } tmp = *bitmap; while (tmp & 0x80) { tmp <<= 1; min_index++; } return min_index; } /* maps pio to fd (specified by index)*/ static void fd_table_set(struct w32_io* pio, int index) { fd_table.w32_ios[index] = pio; pio->table_index = index; assert(pio->type != UNKNOWN_FD); FD_SET(index, &(fd_table.occupied)); } /* removes entry at index from mapping table*/ static void fd_table_clear(int index) { fd_table.w32_ios[index] = NULL; FD_CLR(index, &(fd_table.occupied)); } void init_prog_paths() { wchar_t* wpgmptr; static int processed = 0; if (processed) return; if (_get_wpgmptr(&wpgmptr) != 0) fatal("unable to retrieve wpgmptr"); if ((__wprogdir = _wcsdup(wpgmptr)) == NULL || (__progdir = utf16_to_utf8(__wprogdir)) == NULL) fatal("%s out of memory", __func__); __progname = strrchr(__progdir, '\\') + 1; /* TODO: retain trailing \ at the end of progdir* variants ? */ *(strrchr(__progdir, '\\')) = '\0'; *(wcsrchr(__wprogdir, L'\\')) = L'\0'; /* strip .exe off __progname */ *(__progname + strlen(__progname) - 4) = '\0'; /* get %programdata% value */ size_t len = 0; _dupenv_s(&__progdata, &len, "ProgramData"); if (!__progdata) fatal("couldn't find ProgramData environment variable"); if(!(__wprogdata = utf8_to_utf16(__progdata))) fatal("%s out of memory", __func__, __LINE__); processed = 1; } void w32posix_initialize() { init_prog_paths(); if ((fd_table_initialize() != 0) || (socketio_initialize() != 0)) debug_assert_internal(); main_thread = OpenThread(THREAD_SET_CONTEXT | SYNCHRONIZE, FALSE, GetCurrentThreadId()); if (main_thread == NULL || sw_initialize() != 0 ) { debug_assert_internal(); fatal("failed to initialize w32posix wrapper"); } } void w32posix_done() { socketio_done(); } /* Check if the corresponding fd is set blocking */ BOOL w32_io_is_blocking(struct w32_io* pio) { return (pio->fd_status_flags & O_NONBLOCK) ? FALSE : TRUE; } /* * Check if io is ready/available. This function is primarily used by select() * as it decides on what fds can be set. */ BOOL w32_io_is_io_available(struct w32_io* pio, BOOL rd) { if (pio->type == SOCK_FD) return socketio_is_io_available(pio, rd); else return fileio_is_io_available(pio, rd); } void w32_io_on_select(struct w32_io* pio, BOOL rd) { if ((pio->type == SOCK_FD)) socketio_on_select(pio, rd); else fileio_on_select(pio, rd); } #define CHECK_FD(fd) do { \ errno = 0; \ if ((fd < 0) || (fd > MAX_FDS - 1) || fd_table.w32_ios[fd] == NULL) { \ errno = EBADF; \ debug3("%s ERROR: bad fd: %d", __FUNCTION__, fd); \ return -1; \ } \ } while (0) #define CHECK_SOCK_IO(pio) do { \ errno = 0; \ if (pio->type != SOCK_FD) { \ errno = ENOTSOCK; \ debug3("%s ERROR: not sock :%d", __FUNCTION__, pio->type); \ return -1; \ } \ } while (0) int w32_socket(int domain, int type, int protocol) { int min_index = fd_table_get_min_index(); struct w32_io* pio = NULL; errno = 0; if (min_index == -1) return -1; if (domain == AF_UNIX && type == SOCK_STREAM) { pio = fileio_afunix_socket(); if (pio == NULL) return -1; pio->type = NONSOCK_FD; } else { pio = socketio_socket(domain, type, protocol); if (pio == NULL) return -1; pio->type = SOCK_FD; } fd_table_set(pio, min_index); debug4("socket:%d, socktype:%d, io:%p, fd:%d ", pio->sock, type, pio, min_index); return min_index; } int w32_accept(int fd, struct sockaddr* addr, int* addrlen) { CHECK_FD(fd); CHECK_SOCK_IO(fd_table.w32_ios[fd]); int min_index = fd_table_get_min_index(); struct w32_io* pio = NULL; if (min_index == -1) return -1; if (fd_table.w32_ios[fd]->type == NONSOCK_FD) { errno = ENOTSUP; verbose("Unix domain server sockets are not supported"); return -1; } pio = socketio_accept(fd_table.w32_ios[fd], addr, addrlen); if (!pio) return -1; pio->type = SOCK_FD; fd_table_set(pio, min_index); debug4("socket:%d, io:%p, fd:%d ", pio->sock, pio, min_index); return min_index; } int w32_setsockopt(int fd, int level, int optname, const void* optval, int optlen) { CHECK_FD(fd); CHECK_SOCK_IO(fd_table.w32_ios[fd]); return socketio_setsockopt(fd_table.w32_ios[fd], level, optname, (const char*)optval, optlen); } int w32_getsockopt(int fd, int level, int optname, void* optval, int* optlen) { CHECK_FD(fd); CHECK_SOCK_IO(fd_table.w32_ios[fd]); return socketio_getsockopt(fd_table.w32_ios[fd], level, optname, (char*)optval, optlen); } int w32_getsockname(int fd, struct sockaddr* name, int* namelen) { CHECK_FD(fd); CHECK_SOCK_IO(fd_table.w32_ios[fd]); return socketio_getsockname(fd_table.w32_ios[fd], name, namelen); } int w32_getpeername(int fd, struct sockaddr* name, int* namelen) { CHECK_FD(fd); CHECK_SOCK_IO(fd_table.w32_ios[fd]); return socketio_getpeername(fd_table.w32_ios[fd], name, namelen); } int w32_listen(int fd, int backlog) { CHECK_FD(fd); if (fd_table.w32_ios[fd]->type == NONSOCK_FD) { errno = ENOTSUP; verbose("Unix domain server sockets are not supported"); return -1; } CHECK_SOCK_IO(fd_table.w32_ios[fd]); return socketio_listen(fd_table.w32_ios[fd], backlog); } int w32_bind(int fd, const struct sockaddr *name, int namelen) { CHECK_FD(fd); if (fd_table.w32_ios[fd]->type == NONSOCK_FD) { errno = ENOTSUP; verbose("Unix domain server sockets are not supported"); return -1; } CHECK_SOCK_IO(fd_table.w32_ios[fd]); return socketio_bind(fd_table.w32_ios[fd], name, namelen); } int w32_connect(int fd, const struct sockaddr* name, int namelen) { CHECK_FD(fd); if (fd_table.w32_ios[fd]->type == NONSOCK_FD) { struct sockaddr_un* addr = (struct sockaddr_un*)name; return fileio_connect(fd_table.w32_ios[fd], addr->sun_path); } CHECK_SOCK_IO(fd_table.w32_ios[fd]); return socketio_connect(fd_table.w32_ios[fd], name, namelen); } int w32_recv(int fd, void *buf, size_t len, int flags) { CHECK_FD(fd); CHECK_SOCK_IO(fd_table.w32_ios[fd]); return socketio_recv(fd_table.w32_ios[fd], buf, len, flags); } int w32_send(int fd, const void *buf, size_t len, int flags) { CHECK_FD(fd); CHECK_SOCK_IO(fd_table.w32_ios[fd]); return socketio_send(fd_table.w32_ios[fd], buf, len, flags); } int w32_shutdown(int fd, int how) { debug4("shutdown - fd:%d how:%d", fd, how); CHECK_FD(fd); CHECK_SOCK_IO(fd_table.w32_ios[fd]); return socketio_shutdown(fd_table.w32_ios[fd], how); } int w32_socketpair(int domain, int type, int protocol, int sv[2]) { int p0, p1; struct w32_io* pio[2]; errno = 0; p0 = fd_table_get_min_index(); if (p0 == -1) return -1; /*temporarily set occupied bit*/ FD_SET(p0, &fd_table.occupied); p1 = fd_table_get_min_index(); FD_CLR(p0, &fd_table.occupied); if (p1 == -1) return -1; if (-1 == fileio_pipe(pio, 1)) return -1; pio[0]->type = NONSOCK_FD; pio[1]->type = NONSOCK_FD; fd_table_set(pio[0], p0); fd_table_set(pio[1], p1); sv[0] = p0; sv[1] = p1; debug4("socketpair - r-h:%d,io:%p,fd:%d w-h:%d,io:%p,fd:%d", pio[0]->handle, pio[0], p0, pio[1]->handle, pio[1], p1); return 0; } int w32_pipe(int *pfds) { int read_index, write_index; struct w32_io* pio[2]; errno = 0; read_index = fd_table_get_min_index(); if (read_index == -1) return -1; /*temporarily set occupied bit*/ FD_SET(read_index, &fd_table.occupied); write_index = fd_table_get_min_index(); FD_CLR(read_index, &fd_table.occupied); if (write_index == -1) return -1; if (-1 == fileio_pipe(pio, 0)) return -1; pio[0]->type = NONSOCK_FD; pio[1]->type = NONSOCK_FD; fd_table_set(pio[0], read_index); fd_table_set(pio[1], write_index); pfds[0] = read_index; pfds[1] = write_index; debug4("pipe - r-h:%d,io:%p,fd:%d w-h:%d,io:%p,fd:%d", pio[0]->handle, pio[0], read_index, pio[1]->handle, pio[1], write_index); return 0; } int w32_open(const char *pathname, int flags, ... /* arg */) { int min_index = fd_table_get_min_index(); struct w32_io* pio; va_list valist; mode_t mode = 0; errno = 0; if (min_index == -1) return -1; if (flags & O_CREAT) { va_start(valist, flags); mode = va_arg(valist, mode_t); va_end(valist); } pio = fileio_open(pathname, flags, mode); if (pio == NULL) return -1; pio->type = NONSOCK_FD; fd_table_set(pio, min_index); debug4("open - handle:%p, io:%p, fd:%d", pio->handle, pio, min_index); debug5("open - path:%s", pathname); return min_index; } int w32_read(int fd, void *dst, size_t max) { CHECK_FD(fd); if (fd_table.w32_ios[fd]->type == SOCK_FD) return socketio_recv(fd_table.w32_ios[fd], dst, max, 0); return fileio_read(fd_table.w32_ios[fd], dst, max); } int w32_write(int fd, const void *buf, size_t max) { CHECK_FD(fd); if (fd_table.w32_ios[fd]->type == SOCK_FD) return socketio_send(fd_table.w32_ios[fd], buf, max, 0); return fileio_write(fd_table.w32_ios[fd], buf, max); } int w32_writev(int fd, const struct iovec *iov, int iovcnt) { int written = 0; int i = 0; CHECK_FD(fd); for (i = 0; i < iovcnt; i++) { int ret = w32_write(fd, iov[i].iov_base, iov[i].iov_len); if (ret > 0) written += ret; } return written; } int w32_fstat(int fd, struct w32_stat *buf) { CHECK_FD(fd); return fileio_fstat(fd_table.w32_ios[fd], (struct _stat64*)buf); } long w32_lseek(int fd, unsigned __int64 offset, int origin) { CHECK_FD(fd); return fileio_lseek(fd_table.w32_ios[fd], offset, origin); } int w32_isatty(int fd) { struct w32_io* pio; if ((fd < 0) || (fd > MAX_FDS - 1) || fd_table.w32_ios[fd] == NULL) { errno = EBADF; return 0; } pio = fd_table.w32_ios[fd]; if (FILETYPE(pio) == FILE_TYPE_CHAR) return 1; else { errno = EINVAL; return 0; } } FILE* w32_fdopen(int fd, const char *mode) { errno = 0; if ((fd < 0) || (fd > MAX_FDS - 1) || fd_table.w32_ios[fd] == NULL) { errno = EBADF; debug3("fdopen - ERROR bad fd: %d", fd); return NULL; } return fileio_fdopen(fd_table.w32_ios[fd], mode); } int w32_close(int fd) { struct w32_io* pio; int r; if ((fd < 0) || (fd > MAX_FDS - 1) || fd_table.w32_ios[fd] == NULL) { errno = EBADF; return -1; } pio = fd_table.w32_ios[fd]; debug4("close - io:%p, type:%d, fd:%d, table_index:%d", pio, pio->type, fd, pio->table_index); if (pio->type == SOCK_FD) r = socketio_close(pio); else r = fileio_close(pio); fd_table_clear(fd); return r; } static int w32_io_process_fd_flags(struct w32_io* pio, int flags) { DWORD shi_flags; if (flags & ~FD_CLOEXEC) { debug3("fcntl - ERROR unsupported flags %d, io:%p", flags, pio); errno = ENOTSUP; return -1; } shi_flags = (flags & FD_CLOEXEC) ? 0 : HANDLE_FLAG_INHERIT; HANDLE h = WINHANDLE(pio); /* * Ignore if handle is not valid yet. It will not be valid for * UF_UNIX sockets that are not connected yet */ if (IS_VALID_HANDLE(h) && (SetHandleInformation(h, HANDLE_FLAG_INHERIT, shi_flags) == FALSE)) { debug3("fcntl - SetHandleInformation failed with error:%d, io:%p", GetLastError(), pio); errno = EOTHER; return -1; } pio->fd_flags = flags; return 0; } int w32_fcntl(int fd, int cmd, ... /* arg */) { va_list valist; va_start(valist, cmd); int ret = 0; CHECK_FD(fd); switch (cmd) { case F_GETFL: ret = fd_table.w32_ios[fd]->fd_status_flags; break; case F_SETFL: fd_table.w32_ios[fd]->fd_status_flags = va_arg(valist, int); ret = 0; break; case F_GETFD: ret = fd_table.w32_ios[fd]->fd_flags; break; case F_SETFD: ret = w32_io_process_fd_flags(fd_table.w32_ios[fd], va_arg(valist, int)); break; default: errno = EINVAL; debug3("fcntl - ERROR not supported cmd:%d", cmd); ret = -1; break; } va_end(valist); return ret; } #define SELECT_EVENT_LIMIT 512 int w32_select(int fds, w32_fd_set* readfds, w32_fd_set* writefds, w32_fd_set* exceptfds, const struct timeval *timeout) { ULONGLONG ticks_start = GetTickCount64(), ticks_spent; w32_fd_set read_ready_fds, write_ready_fds; HANDLE events[SELECT_EVENT_LIMIT]; int num_events = 0; int in_set_fds = 0, out_ready_fds = 0, i; unsigned int timeout_ms = 0, time_rem = 0; errno = 0; /* TODO - the size of these can be reduced based on fds */ memset(&read_ready_fds, 0, sizeof(w32_fd_set)); memset(&write_ready_fds, 0, sizeof(w32_fd_set)); if (timeout) timeout_ms = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; if (fds > MAX_FDS) { errno = EINVAL; debug3("select - ERROR: invalid fds: %d", fds); return -1; } if (!readfds && !writefds) { errno = EINVAL; debug3("select - ERROR: null fd_sets"); return -1; } /* TODO - see if this needs to be supported */ if (exceptfds) { for (i = 0; i < fds; i++) FD_CLR(i, exceptfds); } if (readfds) { for (i = 0; i < fds; i++) if (FD_ISSET(i, readfds)) { CHECK_FD(i); in_set_fds++; } } if (writefds) { for (i = 0; i < fds; i++) if (FD_ISSET(i, writefds)) { CHECK_FD(i); in_set_fds++; } } /* if none of input fds are set return error */ if (in_set_fds == 0) { errno = EINVAL; debug3("select - ERROR: empty fd_sets"); return -1; } debug5("Total in fds:%d", in_set_fds); /* * start async io on selected fds if needed and pick up any events * that select needs to listen on */ for (int i = 0; i < fds; i++) { if (readfds && FD_ISSET(i, readfds)) { w32_io_on_select(fd_table.w32_ios[i], TRUE); if ((fd_table.w32_ios[i]->type == SOCK_FD) && (fd_table.w32_ios[i]->internal.state == SOCK_LISTENING)) { if (num_events == SELECT_EVENT_LIMIT) { debug3("select - ERROR: max #events breach"); errno = ENOMEM; return -1; } events[num_events++] = fd_table.w32_ios[i]->read_overlapped.hEvent; } } if (writefds && FD_ISSET(i, writefds)) { w32_io_on_select(fd_table.w32_ios[i], FALSE); if ((fd_table.w32_ios[i]->type == SOCK_FD) && (fd_table.w32_ios[i]->internal.state == SOCK_CONNECTING)) { if (num_events == SELECT_EVENT_LIMIT) { debug3("select - ERROR: max #events reached for select"); errno = ENOMEM; return -1; } events[num_events++] = fd_table.w32_ios[i]->write_overlapped.hEvent; } } } /* excute any scheduled APCs */ if (0 != wait_for_any_event(NULL, 0, 0)) return -1; /* see if any io is ready */ for (i = 0; i < fds; i++) { if (readfds && FD_ISSET(i, readfds)) { if (w32_io_is_io_available(fd_table.w32_ios[i], TRUE)) { FD_SET(i, &read_ready_fds); out_ready_fds++; } } if (writefds && FD_ISSET(i, writefds)) { if (w32_io_is_io_available(fd_table.w32_ios[i], FALSE)) { FD_SET(i, &write_ready_fds); out_ready_fds++; } } } /* timeout specified and both fields are 0 - polling mode*/ /* proceed with further wait if not in polling mode*/ if ((timeout == NULL) || (timeout_ms != 0)) /* wait for io until any is ready */ while (out_ready_fds == 0) { ticks_spent = GetTickCount64() - ticks_start; time_rem = 0; if (timeout != NULL) { if (timeout_ms < ticks_spent) { debug4("select - timing out"); break; } time_rem = timeout_ms - (ticks_spent & 0xffffffff); } else time_rem = INFINITE; if (0 != wait_for_any_event(events, num_events, time_rem)) return -1; /* check on fd status */ out_ready_fds = 0; for (int i = 0; i < fds; i++) { if (readfds && FD_ISSET(i, readfds)) { if (w32_io_is_io_available(fd_table.w32_ios[i], TRUE)) { FD_SET(i, &read_ready_fds); out_ready_fds++; } } if (writefds && FD_ISSET(i, writefds)) { if (w32_io_is_io_available(fd_table.w32_ios[i], FALSE)) { FD_SET(i, &write_ready_fds); out_ready_fds++; } } } if (out_ready_fds == 0) debug5("select - wait ended without any IO completion, looping again"); } /* clear out fds that are not ready yet */ if (readfds) for (i = 0; i < fds; i++) if (FD_ISSET(i, readfds)) { if (FD_ISSET(i, &read_ready_fds)) { /* for connect() initiated sockets finish WSA connect process*/ if ((fd_table.w32_ios[i]->type == SOCK_FD) && ((fd_table.w32_ios[i]->internal.state == SOCK_CONNECTING))) if (socketio_finish_connect(fd_table.w32_ios[i]) != 0) { /* async connect failed, error will be picked up by recv or send */ errno = 0; } } else FD_CLR(i, readfds); } if (writefds) for (i = 0; i < fds; i++) if (FD_ISSET(i, writefds)) { if (FD_ISSET(i, &write_ready_fds)) { /* for connect() initiated sockets finish WSA connect process*/ if ((fd_table.w32_ios[i]->type == SOCK_FD) && ((fd_table.w32_ios[i]->internal.state == SOCK_CONNECTING))) if (socketio_finish_connect(fd_table.w32_ios[i]) != 0) { /* async connect failed, error will be picked up by recv or send */ errno = 0; } } else FD_CLR(i, writefds); } debug5("select - returning %d", out_ready_fds); return out_ready_fds; } static HANDLE dup_handle(int fd) { HANDLE h = fd_table.w32_ios[fd]->handle; int is_sock = fd_table.w32_ios[fd]->type == SOCK_FD; if (is_sock) { SOCKET dup_sock; SOCKET sock = (SOCKET)h; WSAPROTOCOL_INFOW info; if (WSADuplicateSocketW(sock, GetCurrentProcessId(), &info) != 0) { errno = EOTHER; error("WSADuplicateSocket failed, WSALastError: %d", WSAGetLastError()); return NULL; } dup_sock = WSASocketW(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, &info, 0, 0); if (dup_sock == INVALID_SOCKET) { errno = EOTHER; error("WSASocketW failed, WSALastError: %d", WSAGetLastError()); return NULL; } return (HANDLE)dup_sock; } else { HANDLE dup_handle; if (!DuplicateHandle(GetCurrentProcess(), h, GetCurrentProcess(), &dup_handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) { errno = EOTHER; error("dup - ERROR: DuplicatedHandle() :%d", GetLastError()); } return dup_handle; } } int w32_dup2(int oldfd, int newfd) { struct w32_io* pio; CHECK_FD(oldfd); if (fd_table.w32_ios[newfd]) w32_close(newfd); pio = malloc(sizeof(struct w32_io)); if (pio == NULL) { errno = ENOMEM; return -1; } memset(pio, 0, sizeof(struct w32_io)); if ((pio->handle = dup_handle(oldfd)) == 0) { free(pio); return -1; } pio->type = fd_table.w32_ios[oldfd]->type; if (pio->type == SOCK_FD) pio->internal.state = SOCK_READY; fd_table_set(pio, newfd); return 0; } int w32_dup(int oldfd) { int min_index, r; CHECK_FD(oldfd); if ((min_index = fd_table_get_min_index()) == -1) return -1; if ((r = w32_dup2(oldfd, min_index)) != 0) return r; return min_index; } HANDLE w32_fd_to_handle(int fd) { return fd_table.w32_ios[fd]->handle; } int w32_ftruncate(int fd, off_t length) { LARGE_INTEGER new_postion; CHECK_FD(fd); new_postion.QuadPart = length; if (!SetFilePointerEx(w32_fd_to_handle(fd), new_postion, 0, FILE_BEGIN)) return -1; if (!SetEndOfFile(w32_fd_to_handle(fd))) return -1; return 0; } int w32_fchmod(int fd, mode_t mode) { wchar_t *file_path; char *file_path_utf8 = NULL; int ret = -1; CHECK_FD(fd); file_path = get_final_path_by_handle(fd_table.w32_ios[fd]->handle); if (!file_path) goto cleanup; if ((file_path_utf8 = utf16_to_utf8(file_path)) == NULL) goto cleanup; ret = w32_chmod(file_path_utf8, mode); cleanup: if (file_path_utf8) free(file_path_utf8); return ret; } int w32_fsync(int fd) { CHECK_FD(fd); return FlushFileBuffers(w32_fd_to_handle(fd)); } int fork() { verbose("fork is not supported"); return -1; } char * build_commandline_string(const char* cmd, char *const argv[], BOOLEAN prepend_module_path); /* * spawn a child process * - specified by cmd with agruments argv * - with std handles set to in, out, err * - flags are passed to CreateProcess call * spawned child will run as as_user if its not NULL */ static int spawn_child_internal(char* cmd, char *const argv[], HANDLE in, HANDLE out, HANDLE err, unsigned long flags, HANDLE* as_user, BOOLEAN prepend_module_path) { PROCESS_INFORMATION pi; STARTUPINFOW si; BOOL b; char *cmdline; wchar_t * cmdline_utf16 = NULL; int ret = -1; if ((cmdline = build_commandline_string(cmd, argv, prepend_module_path)) == NULL) { errno = ENOMEM; goto cleanup; } if ((cmdline_utf16 = utf8_to_utf16(cmdline)) == NULL) { errno = ENOMEM; goto cleanup; } memset(&si, 0, sizeof(STARTUPINFOW)); si.cb = sizeof(STARTUPINFOW); si.hStdInput = in; si.hStdOutput = out; si.hStdError = err; si.dwFlags = STARTF_USESTDHANDLES; wchar_t * t = cmdline_utf16; do { debug3("spawning %ls", t); if (as_user) b = CreateProcessAsUserW(as_user, NULL, t, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi); else b = CreateProcessW(NULL, t, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi); if(b || GetLastError() != ERROR_FILE_NOT_FOUND || (argv != NULL && *argv != NULL) || cmd[0] == '\"') break; t++; *(cmdline_utf16 + wcslen(cmdline_utf16) - 1) = L'\0'; } while (t == (cmdline_utf16 + 1)); if (b) { if (register_child(pi.hProcess, pi.dwProcessId) == -1) { TerminateProcess(pi.hProcess, 0); CloseHandle(pi.hProcess); goto cleanup; } CloseHandle(pi.hThread); ret = pi.dwProcessId; } else { errno = GetLastError(); error("%s failed error:%d", (as_user ? "CreateProcessAsUserW" : "CreateProcessW"), GetLastError()); } cleanup: if (cmdline) free(cmdline); if (cmdline_utf16) free(cmdline_utf16); return ret; } #include "inc\spawn.h" /* structures defining binary layout of fd info to be transmitted between parent and child processes*/ struct std_fd_state { int num_inherited; char in_type; char out_type; char err_type; char padding; }; struct inh_fd_state { int handle; short index; char type; char padding; }; /* encodes the fd info into a base64 encoded binary blob */ static char* fd_encode_state(const posix_spawn_file_actions_t *file_actions, HANDLE aux_h[]) { char *buf, *encoded; struct std_fd_state *std_fd_state; struct inh_fd_state *c; DWORD len_req = 0; BOOL b; int i; int fd_in = file_actions->stdio_redirect[STDIN_FILENO]; int fd_out = file_actions->stdio_redirect[STDOUT_FILENO]; int fd_err = file_actions->stdio_redirect[STDERR_FILENO]; int num_aux_fds = file_actions->num_aux_fds; const int *parent_aux_fds = file_actions->aux_fds_info.parent_fd; const int *child_aux_fds = file_actions->aux_fds_info.child_fd; buf = malloc(8 * (1 + num_aux_fds)); if (!buf) { errno = ENOMEM; return NULL; } std_fd_state = (struct std_fd_state *)buf; std_fd_state->num_inherited = num_aux_fds; std_fd_state->in_type = fd_table.w32_ios[fd_in]->type; std_fd_state->out_type = fd_table.w32_ios[fd_out]->type; std_fd_state->err_type = fd_table.w32_ios[fd_err]->type; c = (struct inh_fd_state*)(buf + 8); for (i = 0; i < num_aux_fds; i++) { c->handle = (int)(intptr_t)aux_h[i]; c->index = child_aux_fds[i]; c->type = fd_table.w32_ios[parent_aux_fds[i]]->type; c++; } b = CryptBinaryToStringA(buf, 8 * (1 + num_aux_fds), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &len_req); encoded = malloc(len_req); if (!encoded) { free(buf); errno = ENOMEM; return NULL; } b = CryptBinaryToStringA(buf, 8 * (1 + num_aux_fds), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, encoded, &len_req); free(buf); return encoded; } /* decodes fd info from an encoded binary blob */ static void fd_decode_state(char* enc_buf) { char* buf; DWORD req = 0, skipped, out_flags; struct std_fd_state *std_fd_state; struct inh_fd_state *c; int num_inherited = 0; CryptStringToBinary(enc_buf, 0, CRYPT_STRING_BASE64 | CRYPT_STRING_STRICT, NULL, &req, &skipped, &out_flags); buf = malloc(req); if (!buf) fatal("out of memory"); CryptStringToBinary(enc_buf, 0, CRYPT_STRING_BASE64 | CRYPT_STRING_STRICT, buf, &req, &skipped, &out_flags); std_fd_state = (struct std_fd_state *)buf; fd_table.w32_ios[0]->type = std_fd_state->in_type; if (fd_table.w32_ios[0]->type == SOCK_FD) fd_table.w32_ios[0]->internal.state = SOCK_READY; fd_table.w32_ios[1]->type = std_fd_state->out_type; if (fd_table.w32_ios[1]->type == SOCK_FD) fd_table.w32_ios[1]->internal.state = SOCK_READY; fd_table.w32_ios[2]->type = std_fd_state->err_type; if (fd_table.w32_ios[2]->type == SOCK_FD) fd_table.w32_ios[2]->internal.state = SOCK_READY; num_inherited = std_fd_state->num_inherited; c = (struct inh_fd_state*)(buf + 8); while (num_inherited--) { struct w32_io* pio = malloc(sizeof(struct w32_io)); if (!pio) fatal("out of memory"); ZeroMemory(pio, sizeof(struct w32_io)); pio->handle = (void*)(INT_PTR)c->handle; pio->type = c->type; if (pio->type == SOCK_FD) pio->internal.state = SOCK_READY; fd_table_set(pio, c->index); c++; } free(buf); return; } int posix_spawn_internal(pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], HANDLE user_token, BOOLEAN prepend_module_path) { int i, ret = -1; int sc_flags = 0; char* fd_info = NULL; HANDLE aux_handles[MAX_INHERITED_FDS]; HANDLE stdio_handles[STDERR_FILENO + 1]; if (file_actions == NULL || envp) { errno = ENOTSUP; return -1; } if (attrp && attrp->flags == POSIX_SPAWN_SETPGROUP) sc_flags = CREATE_NEW_PROCESS_GROUP; /* prepare handles */ memset(stdio_handles, 0, sizeof(stdio_handles)); memset(aux_handles, 0, sizeof(aux_handles)); stdio_handles[STDIN_FILENO] = dup_handle(file_actions->stdio_redirect[STDIN_FILENO]); stdio_handles[STDOUT_FILENO] = dup_handle(file_actions->stdio_redirect[STDOUT_FILENO]); stdio_handles[STDERR_FILENO] = dup_handle(file_actions->stdio_redirect[STDERR_FILENO]); if (!stdio_handles[STDIN_FILENO] || !stdio_handles[STDOUT_FILENO] || !stdio_handles[STDERR_FILENO]) goto cleanup; for (i = 0; i < file_actions->num_aux_fds; i++) { aux_handles[i] = dup_handle(file_actions->aux_fds_info.parent_fd[i]); if (aux_handles[i] == NULL) goto cleanup; } /* set fd info */ if ((fd_info = fd_encode_state(file_actions, aux_handles)) == NULL) goto cleanup; if (_putenv_s(POSIX_FD_STATE, fd_info) != 0) goto cleanup; i = spawn_child_internal(argv[0], argv + 1, stdio_handles[STDIN_FILENO], stdio_handles[STDOUT_FILENO], stdio_handles[STDERR_FILENO], sc_flags, user_token, prepend_module_path); if (i == -1) goto cleanup; if (pidp) *pidp = i; ret = 0; cleanup: _putenv_s(POSIX_FD_STATE, ""); for (i = 0; i <= STDERR_FILENO; i++) { if (stdio_handles[i] != NULL) { if (fd_table.w32_ios[file_actions->stdio_redirect[i]]->type == SOCK_FD) closesocket((SOCKET)stdio_handles[i]); else CloseHandle(stdio_handles[i]); } } for (i = 0; i < file_actions->num_aux_fds; i++) { if (aux_handles[i] != NULL) { if (fd_table.w32_ios[file_actions->aux_fds_info.parent_fd[i]]->type == SOCK_FD) closesocket((SOCKET)aux_handles[i]); else CloseHandle(aux_handles[i]); } } if (fd_info) free(fd_info); return ret; } int posix_spawn(pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) { return posix_spawn_internal(pidp, path, file_actions, attrp, argv, envp, NULL, TRUE); } int posix_spawnp(pid_t *pidp, const char *file, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) { return posix_spawn_internal(pidp, file, file_actions, attrp, argv, envp, NULL, FALSE); }