[Pal/{Linux,Linux-SGX}] Refactor db_pipes.c code

Cleanup of the pipes' implementation in both PALs, including
better comments and small fixups (proper `pipe_delete()`, proper
error handling, proper nonblocking option).
This commit is contained in:
Dmitrii Kuvaiskii
2020-03-24 14:37:45 +01:00
committed by Michał Kowalczyk
parent 58c53ad747
commit 350b498fb0
2 changed files with 501 additions and 262 deletions
+226 -98
View File
@@ -21,8 +21,6 @@
* "pipe:" or "pipe.srv:".
*/
#include <linux/types.h>
#include "api.h"
#include "pal.h"
#include "pal_debug.h"
@@ -33,53 +31,87 @@
#include "pal_linux_defs.h"
#include "pal_linux_error.h"
#include "pal_security.h"
typedef __kernel_pid_t pid_t;
#include <asm/fcntl.h>
#include <asm/poll.h>
#include <linux/types.h>
#include <linux/un.h>
static int pipe_path(int pipeid, char* path, int len) {
/* use abstrace UNIX sockets for pipes */
memset(path, 0, len);
return snprintf(path + 1, len - 1, "%s%08x", pal_sec.pipe_prefix, pipeid);
}
static int pipe_addr(int pipeid, struct sockaddr_un* addr) {
/* use abstract UNIX sockets for pipes, with name format "@/graphene/12345678" */
addr->sun_family = AF_UNIX;
return pipe_path(pipeid, (char*)addr->sun_path, sizeof(addr->sun_path));
}
memset(addr->sun_path, 0, sizeof(addr->sun_path));
static inline int pipe_type(int options) {
int type = SOCK_STREAM;
if (options & PAL_OPTION_NONBLOCK)
type |= SOCK_NONBLOCK;
return type;
/* for abstract sockets, first char is NUL */
char* str = (char*)addr->sun_path + 1;
size_t size = sizeof(addr->sun_path) - 1;
/* pipe_prefix already contains a slash at the end, so not needed in the format string */
int ret = snprintf(str, size, "%s%08x", pal_sec.pipe_prefix, pipeid);
return ret >= 0 && (size_t)ret < size ? 0 : -EINVAL;
}
/*!
* \brief Create a listening abstract UNIX socket as preparation for connecting two ends of a pipe.
*
* An abstract UNIX socket with name "@/graphene/<pipeid>" is opened for listening. A corresponding
* PAL handle with type `pipesrv` is created. This PAL handle typically serves only as an
* intermediate step to connect two ends of the pipe (`pipecli` and `pipe`). As soon as the other
* end of the pipe connects to this listening socket, a new accepted socket and the corresponding
* PAL handle are created, and this `pipesrv` handle can be closed.
*
* \param[out] handle PAL handle of type `pipesrv` with abstract UNIX socket opened for listening.
* \param[in] pipeid Integer uniquely identifying the pipe.
* \param[in] options May contain PAL_OPTION_NONBLOCK.
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_listen(PAL_HANDLE* handle, PAL_NUM pipeid, int options) {
struct sockaddr_un addr;
int ret;
if ((ret = pipe_addr(pipeid, &addr)) < 0)
return ret;
struct sockaddr_un addr;
ret = pipe_addr(pipeid, &addr);
if (IS_ERR(ret))
return -PAL_ERROR_DENIED;
unsigned int addrlen = sizeof(struct sockaddr_un);
struct sockopt sock_options;
ret = ocall_listen(AF_UNIX, pipe_type(options), 0, /*ipv6_v6only=*/0,
unsigned int addrlen = sizeof(struct sockaddr_un);
int nonblock = options & PAL_OPTION_NONBLOCK ? SOCK_NONBLOCK : 0;
ret = ocall_listen(AF_UNIX, SOCK_STREAM | nonblock, 0, /*ipv6_v6only=*/0,
(struct sockaddr*)&addr, &addrlen, &sock_options);
if (IS_ERR(ret))
return unix_to_pal_error(ERRNO(ret));
PAL_HANDLE hdl = malloc(HANDLE_SIZE(pipe));
if (!hdl) {
ocall_close(ret);
return -PAL_ERROR_NOMEM;
}
SET_HANDLE_TYPE(hdl, pipesrv);
HANDLE_HDR(hdl)->flags |= RFD(0);
hdl->pipe.fd = ret;
hdl->pipe.pipeid = pipeid;
hdl->pipe.nonblocking = options & PAL_OPTION_NONBLOCK ? PAL_TRUE : PAL_FALSE;
*handle = hdl;
HANDLE_HDR(hdl)->flags |= RFD(0); /* cannot write to a listening socket */
hdl->pipe.fd = ret;
hdl->pipe.pipeid = pipeid;
hdl->pipe.nonblocking = options & PAL_OPTION_NONBLOCK ? PAL_TRUE : PAL_FALSE;
*handle = hdl;
return 0;
}
/*!
* \brief Accept the other end of the pipe and create PAL handle for our end of the pipe.
*
* Caller creates a `pipesrv` PAL handle with the underlying abstract UNIX socket opened for
* listening, and then calls this function to wait for the other end of the pipe to connect.
* When the connection request arrives, a new `pipecli` PAL handle is created with the
* corresponding underlying socket and is returned in `client`. This `pipecli` PAL handle denotes
* our end of the pipe. Typically, `pipesrv` handle is not needed after this and can be closed.
*
* \param[in] handle PAL handle of type `pipesrv` with abstract UNIX socket opened for listening.
* \param[out] client PAL handle of type `pipecli` connected to the other end of the pipe (`pipe`).
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_waitforclient(PAL_HANDLE handle, PAL_HANDLE* client) {
if (!IS_HANDLE_TYPE(handle, pipesrv))
return -PAL_ERROR_NOTSERVER;
@@ -93,64 +125,131 @@ static int pipe_waitforclient(PAL_HANDLE handle, PAL_HANDLE* client) {
return unix_to_pal_error(ERRNO(ret));
PAL_HANDLE clnt = malloc(HANDLE_SIZE(pipe));
if (!clnt) {
ocall_close(ret);
return -PAL_ERROR_NOMEM;
}
SET_HANDLE_TYPE(clnt, pipecli);
HANDLE_HDR(clnt)->flags |= RFD(0) | WFD(0);
clnt->pipe.fd = ret;
clnt->pipe.nonblocking = PAL_FALSE;
clnt->pipe.pipeid = handle->pipe.pipeid;
*client = clnt;
clnt->pipe.fd = ret;
clnt->pipe.nonblocking = PAL_FALSE; /* FIXME: must set nonblocking based on `handle` value */
clnt->pipe.pipeid = handle->pipe.pipeid;
*client = clnt;
return 0;
}
/*!
* \brief Connect to the other end of the pipe and create PAL handle for our end of the pipe.
*
* This function connects to the other end of the pipe, represented as an abstract UNIX socket
* "@/graphene/<pipeid>" opened for listening. When the connection succeeds, a new `pipe` PAL handle
* is created with the corresponding underlying socket and is returned in `handle`. The other end of
* the pipe is typically of type `pipecli`.
*
* \param[out] handle PAL handle of type `pipe` with abstract UNIX socket connected to another end.
* \param[in] pipeid Integer uniquely identifying the pipe.
* \param[in] options May contain PAL_OPTION_NONBLOCK.
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_connect(PAL_HANDLE* handle, PAL_NUM pipeid, int options) {
struct sockaddr_un addr;
int ret;
if ((ret = pipe_addr(pipeid, &addr)) < 0)
return ret;
struct sockaddr_un addr;
ret = pipe_addr(pipeid, &addr);
if (IS_ERR(ret))
return -PAL_ERROR_DENIED;
struct sockopt sock_options;
ret = ocall_connect(AF_UNIX, pipe_type(options), 0, /*ipv6_v6only=*/0, (void*)&addr,
sizeof(struct sockaddr_un), NULL, NULL, &sock_options);
unsigned int addrlen = sizeof(struct sockaddr_un);
int nonblock = options & PAL_OPTION_NONBLOCK ? SOCK_NONBLOCK : 0;
ret = ocall_connect(AF_UNIX, SOCK_STREAM | nonblock, 0, /*ipv6_v6only=*/0,
(const struct sockaddr*)&addr,
addrlen, NULL, NULL, &sock_options);
if (IS_ERR(ret))
return unix_to_pal_error(ERRNO(ret));
PAL_HANDLE hdl = malloc(HANDLE_SIZE(pipe));
if (!hdl) {
ocall_close(ret);
return -PAL_ERROR_NOMEM;
}
SET_HANDLE_TYPE(hdl, pipe);
HANDLE_HDR(hdl)->flags |= RFD(0) | WFD(0);
hdl->pipe.fd = ret;
hdl->pipe.pipeid = pipeid;
hdl->pipe.nonblocking = (options & PAL_OPTION_NONBLOCK) ? PAL_TRUE : PAL_FALSE;
*handle = hdl;
hdl->pipe.fd = ret;
hdl->pipe.pipeid = pipeid;
hdl->pipe.nonblocking = (options & PAL_OPTION_NONBLOCK) ? PAL_TRUE : PAL_FALSE;
*handle = hdl;
return 0;
}
/*!
* \brief Create PAL handle with read and write ends of a pipe.
*
* This function creates a PAL handle of type `pipeprv` (anonymous pipe). In contrast to other types
* of pipes, `pipeprv` encapsulates both ends of the pipe, backed by a host-level socketpair. This
* type of pipe is typically reserved for internal PAL uses, not for LibOS emulation.
*
* \param[out] handle PAL handle of type `pipeprv` backed by a host-level socketpair.
* \param[in] options May contain PAL_OPTION_NONBLOCK.
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_private(PAL_HANDLE* handle, int options) {
int ret, fds[2];
int type = SOCK_STREAM;
if (options & PAL_OPTION_NONBLOCK)
type |= SOCK_NONBLOCK;
int fds[2];
ret = ocall_socketpair(AF_UNIX, type, 0, fds);
int nonblock = options & PAL_OPTION_NONBLOCK ? SOCK_NONBLOCK : 0;
int ret = ocall_socketpair(AF_UNIX, SOCK_STREAM | nonblock, 0, fds);
if (IS_ERR(ret))
return unix_to_pal_error(ERRNO(ret));
PAL_HANDLE hdl = malloc(HANDLE_SIZE(pipeprv));
if (!hdl) {
ocall_close(fds[0]);
ocall_close(fds[1]);
return -PAL_ERROR_NOMEM;
}
SET_HANDLE_TYPE(hdl, pipeprv);
HANDLE_HDR(hdl)->flags |= RFD(0) | WFD(1);
HANDLE_HDR(hdl)->flags |= RFD(0) | WFD(1); /* first FD for reads, second FD for writes */
hdl->pipeprv.fds[0] = fds[0];
hdl->pipeprv.fds[1] = fds[1];
hdl->pipeprv.nonblocking = (options & PAL_OPTION_NONBLOCK) ? PAL_TRUE : PAL_FALSE;
*handle = hdl;
*handle = hdl;
return 0;
}
/* 'open' operation of pipe stream. For each pipe stream, it is
identified by a decimal number in URI. There could be two
types: pipe and pipe.srv. They behave pretty much the same,
except they are two ends of the pipe. */
/*!
* \brief Create PAL handle of type `pipeprv`, `pipesrv`, or `pipe` depending on `type` and `uri`.
*
* Depending on the combination of `type` and `uri`, the following PAL handles are created:
*
* - `type` is URI_TYPE_PIPE and `url` is empty: create `pipeprv` handle (with two connected
* ends of an anonymous pipe).
*
* - `type` is URI_TYPE_PIPE_SRV: create `pipesrv` handle (intermediate listening socket) with
* name in the form of "@/graphene/<pipeid>" where pipeid is
* derived from `uri` via strtol(). Caller is expected to call
* pipe_waitforclient() afterwards.
*
* - `type` is URI_TYPE_PIPE: create `pipe` handle (connecting socket) with name in the form of
* "@/graphene/<pipeid>" where pipeid is derived from `uri` via
* strtol().
*
* \param[out] handle Created PAL handle of type `pipeprv`, `pipesrv`, or `pipe`.
* \param[in] type Can be URI_TYPE_PIPE or URI_TYPE_PIPE_SRV.
* \param[in] uri Content is either NUL (for anonymous pipe) or an integer denoting pipeid.
* \param[in] access Not used.
* \param[in] share Not used.
* \param[in] create Not used.
* \param[in] options May contain PAL_OPTION_NONBLOCK.
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_open(PAL_HANDLE* handle, const char* type, const char* uri, int access, int share,
int create, int options) {
if (!WITHIN_MASK(access, PAL_ACCESS_MASK) || !WITHIN_MASK(share, PAL_SHARE_MASK) ||
@@ -175,7 +274,15 @@ static int pipe_open(PAL_HANDLE* handle, const char* type, const char* uri, int
return -PAL_ERROR_INVAL;
}
/* 'read' operation of pipe stream. offset does not apply here. */
/*!
* \brief Read from pipe (from read end in case of `pipeprv`).
*
* \param[in] handle PAL handle of type `pipeprv`, `pipecli`, or `pipe`.
* \param[in] offset Not used.
* \param[in] len Size of user-supplied buffer.
* \param[out] buffer User-supplied buffer to read data to.
* \return Number of bytes read on success, negative PAL error code otherwise.
*/
static int64_t pipe_read(PAL_HANDLE handle, uint64_t offset, uint64_t len, void* buffer) {
if (offset)
return -PAL_ERROR_INVAL;
@@ -184,12 +291,9 @@ static int64_t pipe_read(PAL_HANDLE handle, uint64_t offset, uint64_t len, void*
!IS_HANDLE_TYPE(handle, pipe))
return -PAL_ERROR_NOTCONNECTION;
if (len != (uint32_t)len)
return -PAL_ERROR_INVAL;
int fd = IS_HANDLE_TYPE(handle, pipeprv) ? handle->pipeprv.fds[0] : handle->pipe.fd;
int fd = IS_HANDLE_TYPE(handle, pipeprv) ? handle->pipeprv.fds[0] : handle->pipe.fd;
ssize_t bytes = ocall_recv(fd, buffer, len, NULL, NULL, NULL, NULL);
if (IS_ERR(bytes))
return unix_to_pal_error(ERRNO(bytes));
@@ -199,7 +303,15 @@ static int64_t pipe_read(PAL_HANDLE handle, uint64_t offset, uint64_t len, void*
return bytes;
}
/* 'write' operation of pipe stream. offset does not apply here. */
/*!
* \brief Write to pipe (to write end in case of `pipeprv`).
*
* \param[in] handle PAL handle of type `pipeprv`, `pipecli`, or `pipe`.
* \param[in] offset Not used.
* \param[in] len Size of user-supplied buffer.
* \param[in] buffer User-supplied buffer to write data from.
* \return Number of bytes written on success, negative PAL error code otherwise.
*/
static int64_t pipe_write(PAL_HANDLE handle, uint64_t offset, uint64_t len, const void* buffer) {
if (offset)
return -PAL_ERROR_INVAL;
@@ -208,9 +320,6 @@ static int64_t pipe_write(PAL_HANDLE handle, uint64_t offset, uint64_t len, cons
!IS_HANDLE_TYPE(handle, pipe))
return -PAL_ERROR_NOTCONNECTION;
if (len != (uint32_t)len)
return -PAL_ERROR_INVAL;
int fd = IS_HANDLE_TYPE(handle, pipeprv) ? handle->pipeprv.fds[1] : handle->pipe.fd;
ssize_t bytes = ocall_send(fd, buffer, len, NULL, 0, NULL, 0);
@@ -220,7 +329,12 @@ static int64_t pipe_write(PAL_HANDLE handle, uint64_t offset, uint64_t len, cons
return bytes;
}
/* 'close' operation of pipe stream. */
/*!
* \brief Close pipe (both ends in case of `pipeprv`).
*
* \param[in] handle PAL handle of type `pipeprv`, `pipesrv`, `pipecli`, or `pipe`.
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_close(PAL_HANDLE handle) {
if (IS_HANDLE_TYPE(handle, pipeprv)) {
if (handle->pipeprv.fds[0] != PAL_IDX_POISON) {
@@ -231,10 +345,7 @@ static int pipe_close(PAL_HANDLE handle) {
ocall_close(handle->pipeprv.fds[1]);
handle->pipeprv.fds[1] = PAL_IDX_POISON;
}
return 0;
}
if (handle->pipe.fd != PAL_IDX_POISON) {
} else if (handle->pipe.fd != PAL_IDX_POISON) {
ocall_close(handle->pipe.fd);
handle->pipe.fd = PAL_IDX_POISON;
}
@@ -242,40 +353,14 @@ static int pipe_close(PAL_HANDLE handle) {
return 0;
}
/* 'delete' operation of pipe stream. */
/*!
* \brief Shut down pipe (one or both ends in case of `pipeprv` depending on `access`).
*
* \param[in] handle PAL handle of type `pipeprv`, `pipesrv`, `pipecli`, or `pipe`.
* \param[in] access May be 0, PAL_DELETE_RD, PAL_DELETE_WR.
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_delete(PAL_HANDLE handle, int access) {
if (IS_HANDLE_TYPE(handle, pipeprv)) {
switch (access) {
case 0:
if (handle->pipeprv.fds[0] != PAL_IDX_POISON) {
ocall_close(handle->pipeprv.fds[0]);
handle->pipeprv.fds[0] = PAL_IDX_POISON;
}
if (handle->pipeprv.fds[1] != PAL_IDX_POISON) {
ocall_close(handle->pipeprv.fds[1]);
handle->pipeprv.fds[1] = PAL_IDX_POISON;
}
break;
case PAL_DELETE_RD:
if (handle->pipeprv.fds[0] != PAL_IDX_POISON) {
ocall_close(handle->pipeprv.fds[0]);
handle->pipeprv.fds[0] = PAL_IDX_POISON;
}
break;
case PAL_DELETE_WR:
if (handle->pipeprv.fds[1] != PAL_IDX_POISON) {
ocall_close(handle->pipeprv.fds[1]);
handle->pipeprv.fds[1] = PAL_IDX_POISON;
}
break;
default:
return -PAL_ERROR_INVAL;
}
}
if (handle->pipe.fd == PAL_IDX_POISON)
return 0;
int shutdown;
switch (access) {
case 0:
@@ -291,10 +376,34 @@ static int pipe_delete(PAL_HANDLE handle, int access) {
return -PAL_ERROR_INVAL;
}
ocall_shutdown(handle->pipe.fd, shutdown);
if (IS_HANDLE_TYPE(handle, pipeprv)) {
/* pipeprv has two underlying FDs, shut down the requested one(s) */
if (handle->pipeprv.fds[0] != PAL_IDX_POISON &&
(shutdown == SHUT_RD || shutdown == SHUT_RDWR)) {
ocall_shutdown(handle->pipeprv.fds[0], SHUT_RD);
}
if (handle->pipeprv.fds[1] != PAL_IDX_POISON &&
(shutdown == SHUT_WR || shutdown == SHUT_RDWR)) {
ocall_shutdown(handle->pipeprv.fds[1], SHUT_WR);
}
} else {
/* other types of pipes have a single underlying FD, shut it down */
if (handle->pipe.fd != PAL_IDX_POISON) {
ocall_shutdown(handle->pipe.fd, shutdown);
}
}
return 0;
}
/*!
* \brief Retrieve attributes of PAL handle.
*
* \param[in] handle PAL handle of type `pipeprv`, `pipesrv`, `pipecli`, or `pipe`.
* \param[out] attr User-supplied buffer to store handle's current attributes.
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_attrquerybyhdl(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) {
int ret;
@@ -347,6 +456,15 @@ static int pipe_attrquerybyhdl(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) {
return 0;
}
/*!
* \brief Set attributes of PAL handle.
*
* Currently only `nonblocking` attribute can be set.
*
* \param[in] handle PAL handle of type `pipeprv`, `pipesrv`, `pipecli`, or `pipe`.
* \param[in] attr User-supplied buffer with new handle's attributes.
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_attrsetbyhdl(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) {
if (handle->generic.fds[0] == PAL_IDX_POISON)
return -PAL_ERROR_BADHANDLE;
@@ -366,8 +484,18 @@ static int pipe_attrsetbyhdl(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) {
return 0;
}
/*!
* \brief Retrieve full URI of PAL handle.
*
* Full URI is composed of the type and pipeid: "<type>:<pipeid>".
*
* \param[in] handle PAL handle of type `pipeprv`, `pipesrv`, `pipecli`, or `pipe`.
* \param[out] buffer User-supplied buffer to write URI to.
* \param[in] count Size of the user-supplied buffer.
* \return Number of bytes written on success, negative PAL error code otherwise.
*/
static int pipe_getname(PAL_HANDLE handle, char* buffer, size_t count) {
int old_count = count;
size_t old_count = count;
int ret;
const char* prefix = NULL;
+275 -164
View File
@@ -21,8 +21,6 @@
* "pipe:" or "pipe.srv:".
*/
#include <linux/types.h>
#include "api.h"
#include "pal.h"
#include "pal_debug.h"
@@ -32,71 +30,105 @@
#include "pal_linux.h"
#include "pal_linux_defs.h"
#include "pal_security.h"
typedef __kernel_pid_t pid_t;
#include <asm/errno.h>
#include <asm/fcntl.h>
#include <asm/poll.h>
#include <linux/time.h>
#include <linux/types.h>
#include <linux/un.h>
#include <sys/socket.h>
static int pipe_path(int pipeid, char* path, int len) {
/* use abstract UNIX sockets for pipes */
memset(path, 0, len);
if (pal_sec.pipe_prefix_id)
return snprintf(path + 1, len - 1, GRAPHENE_UNIX_PREFIX_FMT "/%08x", pal_sec.pipe_prefix_id,
pipeid);
else
return snprintf(path + 1, len - 1, "/graphene/%08x", pipeid);
}
static int pipe_addr(int pipeid, struct sockaddr_un* addr) {
/* use abstract UNIX sockets for pipes, with name format "@/graphene/12345678" */
addr->sun_family = AF_UNIX;
return pipe_path(pipeid, (char*)addr->sun_path, sizeof(addr->sun_path));
memset(addr->sun_path, 0, sizeof(addr->sun_path));
/* for abstract sockets, first char is NUL */
char* str = (char*)addr->sun_path + 1;
size_t size = sizeof(addr->sun_path) - 1;
/* FIXME: Below naming scheme is slightly different from Linux-SGX, not important though */
int ret;
if (pal_sec.pipe_prefix_id) {
ret = snprintf(str, size, GRAPHENE_UNIX_PREFIX_FMT "/%08x",
pal_sec.pipe_prefix_id, pipeid);
} else {
ret = snprintf(str, size, "/graphene/%08x", pipeid);
}
return ret >= 0 && (size_t)ret < size ? 0 : -EINVAL;
}
/*!
* \brief Create a listening abstract UNIX socket as preparation for connecting two ends of a pipe.
*
* An abstract UNIX socket with name "@/graphene/<pipeid>" is opened for listening. A corresponding
* PAL handle with type `pipesrv` is created. This PAL handle typically serves only as an
* intermediate step to connect two ends of the pipe (`pipecli` and `pipe`). As soon as the other
* end of the pipe connects to this listening socket, a new accepted socket and the corresponding
* PAL handle are created, and this `pipesrv` handle can be closed.
*
* \param[out] handle PAL handle of type `pipesrv` with abstract UNIX socket opened for listening.
* \param[in] pipeid Integer uniquely identifying the pipe.
* \param[in] options May contain PAL_OPTION_NONBLOCK.
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_listen(PAL_HANDLE* handle, PAL_NUM pipeid, int options) {
int ret, fd;
fd = INLINE_SYSCALL(socket, 3, AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | options, 0);
if (IS_ERR(fd))
return -PAL_ERROR_DENIED;
int ret;
struct sockaddr_un addr;
if ((ret = pipe_addr(pipeid, &addr)) < 0)
return ret;
ret = INLINE_SYSCALL(bind, 3, fd, &addr, sizeof(addr.sun_path) - 1);
if (IS_ERR(ret)) {
INLINE_SYSCALL(close, 1, fd);
switch (ERRNO(ret)) {
case EINVAL:
return -PAL_ERROR_INVAL;
case EADDRINUSE:
return -PAL_ERROR_STREAMEXIST;
default:
return -PAL_ERROR_DENIED;
}
}
ret = INLINE_SYSCALL(listen, 2, fd, 1);
ret = pipe_addr(pipeid, &addr);
if (IS_ERR(ret))
return -PAL_ERROR_DENIED;
int nonblock = options & PAL_OPTION_NONBLOCK ? SOCK_NONBLOCK : 0;
int fd = INLINE_SYSCALL(socket, 3, AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | nonblock, 0);
if (IS_ERR(fd))
return unix_to_pal_error(ERRNO(fd));
ret = INLINE_SYSCALL(bind, 3, fd, &addr, sizeof(addr.sun_path) - 1);
if (IS_ERR(ret)) {
INLINE_SYSCALL(close, 1, fd);
return unix_to_pal_error(ERRNO(ret));
}
ret = INLINE_SYSCALL(listen, 2, fd, 1);
if (IS_ERR(ret)) {
INLINE_SYSCALL(close, 1, fd);
return unix_to_pal_error(ERRNO(ret));
}
PAL_HANDLE hdl = malloc(HANDLE_SIZE(pipe));
if (!hdl) {
INLINE_SYSCALL(close, 1, fd);
return -PAL_ERROR_NOMEM;
}
SET_HANDLE_TYPE(hdl, pipesrv);
HANDLE_HDR(hdl)->flags |= RFD(0);
hdl->pipe.fd = fd;
hdl->pipe.pipeid = pipeid;
hdl->pipe.nonblocking = options & PAL_OPTION_NONBLOCK ? PAL_TRUE : PAL_FALSE;
*handle = hdl;
HANDLE_HDR(hdl)->flags |= RFD(0); /* cannot write to a listening socket */
hdl->pipe.fd = fd;
hdl->pipe.pipeid = pipeid;
hdl->pipe.nonblocking = options & PAL_OPTION_NONBLOCK ? PAL_TRUE : PAL_FALSE;
*handle = hdl;
return 0;
}
/*!
* \brief Accept the other end of the pipe and create PAL handle for our end of the pipe.
*
* Caller creates a `pipesrv` PAL handle with the underlying abstract UNIX socket opened for
* listening, and then calls this function to wait for the other end of the pipe to connect.
* When the connection request arrives, a new `pipecli` PAL handle is created with the
* corresponding underlying socket and is returned in `client`. This `pipecli` PAL handle denotes
* our end of the pipe. Typically, `pipesrv` handle is not needed after this and can be closed.
*
* \param[in] handle PAL handle of type `pipesrv` with abstract UNIX socket opened for listening.
* \param[out] client PAL handle of type `pipecli` connected to the other end of the pipe (`pipe`).
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_waitforclient(PAL_HANDLE handle, PAL_HANDLE* client) {
if (!IS_HANDLE_TYPE(handle, pipesrv))
return -PAL_ERROR_NOTSERVER;
@@ -105,87 +137,137 @@ static int pipe_waitforclient(PAL_HANDLE handle, PAL_HANDLE* client) {
return -PAL_ERROR_DENIED;
int newfd = INLINE_SYSCALL(accept4, 4, handle->pipe.fd, NULL, NULL, O_CLOEXEC);
if (IS_ERR(newfd))
switch (ERRNO(newfd)) {
case EWOULDBLOCK:
return -PAL_ERROR_TRYAGAIN;
case ECONNABORTED:
return -PAL_ERROR_CONNFAILED;
default:
return -PAL_ERROR_DENIED;
}
return unix_to_pal_error(ERRNO(newfd));
PAL_HANDLE clnt = malloc(HANDLE_SIZE(pipe));
SET_HANDLE_TYPE(clnt, pipecli);
HANDLE_HDR(clnt)->flags |= RFD(0) | WFD(0);
clnt->pipe.fd = newfd;
clnt->pipe.pipeid = handle->pipe.pipeid;
clnt->pipe.nonblocking = PAL_FALSE;
*client = clnt;
return 0;
}
static int pipe_connect(PAL_HANDLE* handle, PAL_NUM pipeid, int options) {
int ret, fd;
fd = INLINE_SYSCALL(socket, 3, AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | options, 0);
if (IS_ERR(fd))
return -PAL_ERROR_DENIED;
struct sockaddr_un addr;
if ((ret = pipe_addr(pipeid, &addr)) < 0)
return ret;
ret = INLINE_SYSCALL(connect, 3, fd, &addr, sizeof(addr.sun_path) - 1);
if (IS_ERR(ret)) {
INLINE_SYSCALL(close, 1, fd);
switch (ERRNO(ret)) {
case ECONNREFUSED:
return -PAL_ERROR_STREAMNOTEXIST;
case EINTR:
return -PAL_ERROR_TRYAGAIN;
default:
return -PAL_ERROR_DENIED;
}
if (!clnt) {
INLINE_SYSCALL(close, 1, newfd);
return -PAL_ERROR_NOMEM;
}
PAL_HANDLE hdl = malloc(HANDLE_SIZE(pipe));
SET_HANDLE_TYPE(hdl, pipe);
HANDLE_HDR(hdl)->flags |= RFD(0) | WFD(0);
hdl->pipe.fd = fd;
hdl->pipe.pipeid = pipeid;
hdl->pipe.nonblocking = (options & PAL_OPTION_NONBLOCK) ? PAL_TRUE : PAL_FALSE;
*handle = hdl;
SET_HANDLE_TYPE(clnt, pipecli);
HANDLE_HDR(clnt)->flags |= RFD(0) | WFD(0);
clnt->pipe.fd = newfd;
clnt->pipe.nonblocking = PAL_FALSE; /* FIXME: must set nonblocking based on `handle` value */
clnt->pipe.pipeid = handle->pipe.pipeid;
*client = clnt;
return 0;
}
static int pipe_private(PAL_HANDLE* handle, int options) {
int ret, fds[2];
/*!
* \brief Connect to the other end of the pipe and create PAL handle for our end of the pipe.
*
* This function connects to the other end of the pipe, represented as an abstract UNIX socket
* "@/graphene/<pipeid>" opened for listening. When the connection succeeds, a new `pipe` PAL handle
* is created with the corresponding underlying socket and is returned in `handle`. The other end of
* the pipe is typically of type `pipecli`.
*
* \param[out] handle PAL handle of type `pipe` with abstract UNIX socket connected to another end.
* \param[in] pipeid Integer uniquely identifying the pipe.
* \param[in] options May contain PAL_OPTION_NONBLOCK.
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_connect(PAL_HANDLE* handle, PAL_NUM pipeid, int options) {
int ret;
ret = INLINE_SYSCALL(socketpair, 4, AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | options, 0, fds);
struct sockaddr_un addr;
ret = pipe_addr(pipeid, &addr);
if (IS_ERR(ret))
return -PAL_ERROR_DENIED;
PAL_HANDLE hdl = malloc(HANDLE_SIZE(pipeprv));
SET_HANDLE_TYPE(hdl, pipeprv);
HANDLE_HDR(hdl)->flags |= RFD(0) | WFD(1);
hdl->pipeprv.fds[0] = fds[0];
hdl->pipeprv.fds[1] = fds[1];
hdl->pipeprv.nonblocking = (options & PAL_OPTION_NONBLOCK) ? PAL_TRUE : PAL_FALSE;
*handle = hdl;
int nonblock = options & PAL_OPTION_NONBLOCK ? SOCK_NONBLOCK : 0;
int fd = INLINE_SYSCALL(socket, 3, AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | nonblock, 0);
if (IS_ERR(fd))
return -PAL_ERROR_DENIED;
ret = INLINE_SYSCALL(connect, 3, fd, &addr, sizeof(addr.sun_path) - 1);
if (IS_ERR(ret)) {
INLINE_SYSCALL(close, 1, fd);
return unix_to_pal_error(ERRNO(ret));
}
PAL_HANDLE hdl = malloc(HANDLE_SIZE(pipe));
if (!hdl) {
INLINE_SYSCALL(close, 1, fd);
return -PAL_ERROR_NOMEM;
}
SET_HANDLE_TYPE(hdl, pipe);
HANDLE_HDR(hdl)->flags |= RFD(0) | WFD(0);
hdl->pipe.fd = fd;
hdl->pipe.pipeid = pipeid;
hdl->pipe.nonblocking = (options & PAL_OPTION_NONBLOCK) ? PAL_TRUE : PAL_FALSE;
*handle = hdl;
return 0;
}
/* 'open' operation of pipe stream. For each pipe stream, it is
identified by a decimal number in URI. There could be two
types: pipe and pipe.srv. They behave pretty much the same,
except they are two ends of the pipe. */
/*!
* \brief Create PAL handle with read and write ends of a pipe.
*
* This function creates a PAL handle of type `pipeprv` (anonymous pipe). In contrast to other types
* of pipes, `pipeprv` encapsulates both ends of the pipe, backed by a host-level socketpair. This
* type of pipe is typically reserved for internal PAL uses, not for LibOS emulation.
*
* \param[out] handle PAL handle of type `pipeprv` backed by a host-level socketpair.
* \param[in] options May contain PAL_OPTION_NONBLOCK.
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_private(PAL_HANDLE* handle, int options) {
int fds[2];
int nonblock = options & PAL_OPTION_NONBLOCK ? SOCK_NONBLOCK : 0;
int ret = INLINE_SYSCALL(socketpair, 4, AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | nonblock, 0, fds);
if (IS_ERR(ret))
return unix_to_pal_error(ERRNO(ret));
PAL_HANDLE hdl = malloc(HANDLE_SIZE(pipeprv));
if (!hdl) {
INLINE_SYSCALL(close, 1, fds[0]);
INLINE_SYSCALL(close, 1, fds[1]);
return -PAL_ERROR_NOMEM;
}
SET_HANDLE_TYPE(hdl, pipeprv);
HANDLE_HDR(hdl)->flags |= RFD(0) | WFD(1); /* first FD for reads, second FD for writes */
hdl->pipeprv.fds[0] = fds[0];
hdl->pipeprv.fds[1] = fds[1];
hdl->pipeprv.nonblocking = (options & PAL_OPTION_NONBLOCK) ? PAL_TRUE : PAL_FALSE;
*handle = hdl;
return 0;
}
/*!
* \brief Create PAL handle of type `pipeprv`, `pipesrv`, or `pipe` depending on `type` and `uri`.
*
* Depending on the combination of `type` and `uri`, the following PAL handles are created:
*
* - `type` is URI_TYPE_PIPE and `url` is empty: create `pipeprv` handle (with two connected
* ends of an anonymous pipe).
*
* - `type` is URI_TYPE_PIPE_SRV: create `pipesrv` handle (intermediate listening socket) with
* name in the form of "@/graphene/<pipeid>" where pipeid is
* derived from `uri` via strtol(). Caller is expected to call
* pipe_waitforclient() afterwards.
*
* - `type` is URI_TYPE_PIPE: create `pipe` handle (connecting socket) with name in the form of
* "@/graphene/<pipeid>" where pipeid is derived from `uri` via
* strtol().
*
* \param[out] handle Created PAL handle of type `pipeprv`, `pipesrv`, or `pipe`.
* \param[in] type Can be URI_TYPE_PIPE or URI_TYPE_PIPE_SRV.
* \param[in] uri Content is either NUL (for anonymous pipe) or an integer denoting pipeid.
* \param[in] access Not used.
* \param[in] share Not used.
* \param[in] create Not used.
* \param[in] options May contain PAL_OPTION_NONBLOCK.
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_open(PAL_HANDLE* handle, const char* type, const char* uri, int access, int share,
int create, int options) {
if (!WITHIN_MASK(access, PAL_ACCESS_MASK) || !WITHIN_MASK(share, PAL_SHARE_MASK) ||
@@ -210,7 +292,15 @@ static int pipe_open(PAL_HANDLE* handle, const char* type, const char* uri, int
return -PAL_ERROR_INVAL;
}
/* 'read' operation of pipe stream. offset does not apply here. */
/*!
* \brief Read from pipe (from read end in case of `pipeprv`).
*
* \param[in] handle PAL handle of type `pipeprv`, `pipecli`, or `pipe`.
* \param[in] offset Not used.
* \param[in] len Size of user-supplied buffer.
* \param[out] buffer User-supplied buffer to read data to.
* \return Number of bytes read on success, negative PAL error code otherwise.
*/
static int64_t pipe_read(PAL_HANDLE handle, uint64_t offset, uint64_t len, void* buffer) {
if (offset)
return -PAL_ERROR_INVAL;
@@ -219,13 +309,11 @@ static int64_t pipe_read(PAL_HANDLE handle, uint64_t offset, uint64_t len, void*
!IS_HANDLE_TYPE(handle, pipe))
return -PAL_ERROR_NOTCONNECTION;
int fd = IS_HANDLE_TYPE(handle, pipeprv) ? handle->pipeprv.fds[0] : handle->pipe.fd;
int64_t bytes = 0;
bytes = INLINE_SYSCALL(read, 3, fd, buffer, len);
int fd = IS_HANDLE_TYPE(handle, pipeprv) ? handle->pipeprv.fds[0] : handle->pipe.fd;
ssize_t bytes = INLINE_SYSCALL(read, 3, fd, buffer, len);
if (IS_ERR(bytes))
bytes = unix_to_pal_error(ERRNO(bytes));
return unix_to_pal_error(ERRNO(bytes));
if (!bytes)
return -PAL_ERROR_ENDOFSTREAM;
@@ -233,7 +321,15 @@ static int64_t pipe_read(PAL_HANDLE handle, uint64_t offset, uint64_t len, void*
return bytes;
}
/* 'write' operation of pipe stream. offset does not apply here. */
/*!
* \brief Write to pipe (to write end in case of `pipeprv`).
*
* \param[in] handle PAL handle of type `pipeprv`, `pipecli`, or `pipe`.
* \param[in] offset Not used.
* \param[in] len Size of user-supplied buffer.
* \param[in] buffer User-supplied buffer to write data from.
* \return Number of bytes written on success, negative PAL error code otherwise.
*/
static int64_t pipe_write(PAL_HANDLE handle, uint64_t offset, size_t len, const void* buffer) {
if (offset)
return -PAL_ERROR_INVAL;
@@ -242,17 +338,21 @@ static int64_t pipe_write(PAL_HANDLE handle, uint64_t offset, size_t len, const
!IS_HANDLE_TYPE(handle, pipe))
return -PAL_ERROR_NOTCONNECTION;
int fd = IS_HANDLE_TYPE(handle, pipeprv) ? handle->pipeprv.fds[1] : handle->pipe.fd;
int64_t bytes = 0;
int fd = IS_HANDLE_TYPE(handle, pipeprv) ? handle->pipeprv.fds[1] : handle->pipe.fd;
bytes = INLINE_SYSCALL(write, 3, fd, buffer, len);
ssize_t bytes = INLINE_SYSCALL(write, 3, fd, buffer, len);
if (IS_ERR(bytes))
bytes = unix_to_pal_error(ERRNO(bytes));
return unix_to_pal_error(ERRNO(bytes));
return bytes;
}
/* 'close' operation of pipe stream. */
/*!
* \brief Close pipe (both ends in case of `pipeprv`).
*
* \param[in] handle PAL handle of type `pipeprv`, `pipesrv`, `pipecli`, or `pipe`.
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_close(PAL_HANDLE handle) {
if (IS_HANDLE_TYPE(handle, pipeprv)) {
if (handle->pipeprv.fds[0] != PAL_IDX_POISON) {
@@ -263,10 +363,7 @@ static int pipe_close(PAL_HANDLE handle) {
INLINE_SYSCALL(close, 1, handle->pipeprv.fds[1]);
handle->pipeprv.fds[1] = PAL_IDX_POISON;
}
return 0;
}
if (handle->pipe.fd != PAL_IDX_POISON) {
} else if (handle->pipe.fd != PAL_IDX_POISON) {
INLINE_SYSCALL(close, 1, handle->pipe.fd);
handle->pipe.fd = PAL_IDX_POISON;
}
@@ -274,40 +371,14 @@ static int pipe_close(PAL_HANDLE handle) {
return 0;
}
/* 'delete' operation of pipe stream. */
/*!
* \brief Shut down pipe (one or both ends in case of `pipeprv` depending on `access`).
*
* \param[in] handle PAL handle of type `pipeprv`, `pipesrv`, `pipecli`, or `pipe`.
* \param[in] access May be 0, PAL_DELETE_RD, PAL_DELETE_WR.
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_delete(PAL_HANDLE handle, int access) {
if (IS_HANDLE_TYPE(handle, pipeprv)) {
switch (access) {
case 0:
if (handle->pipeprv.fds[0] != PAL_IDX_POISON) {
INLINE_SYSCALL(close, 1, handle->pipeprv.fds[0]);
handle->pipeprv.fds[0] = PAL_IDX_POISON;
}
if (handle->pipeprv.fds[1] != PAL_IDX_POISON) {
INLINE_SYSCALL(close, 1, handle->pipeprv.fds[1]);
handle->pipeprv.fds[1] = PAL_IDX_POISON;
}
break;
case PAL_DELETE_RD:
if (handle->pipeprv.fds[0] != PAL_IDX_POISON) {
INLINE_SYSCALL(close, 1, handle->pipeprv.fds[0]);
handle->pipeprv.fds[0] = PAL_IDX_POISON;
}
break;
case PAL_DELETE_WR:
if (handle->pipeprv.fds[1] != PAL_IDX_POISON) {
INLINE_SYSCALL(close, 1, handle->pipeprv.fds[1]);
handle->pipeprv.fds[1] = PAL_IDX_POISON;
}
break;
default:
return -PAL_ERROR_INVAL;
}
}
if (handle->pipe.fd == PAL_IDX_POISON)
return 0;
int shutdown;
switch (access) {
case 0:
@@ -323,14 +394,36 @@ static int pipe_delete(PAL_HANDLE handle, int access) {
return -PAL_ERROR_INVAL;
}
INLINE_SYSCALL(shutdown, 2, handle->pipe.fd, shutdown);
if (IS_HANDLE_TYPE(handle, pipeprv)) {
/* pipeprv has two underlying FDs, shut down the requested one(s) */
if (handle->pipeprv.fds[0] != PAL_IDX_POISON &&
(shutdown == SHUT_RD || shutdown == SHUT_RDWR)) {
INLINE_SYSCALL(shutdown, 2, handle->pipeprv.fds[0], SHUT_RD);
}
if (handle->pipeprv.fds[1] != PAL_IDX_POISON &&
(shutdown == SHUT_WR || shutdown == SHUT_RDWR)) {
INLINE_SYSCALL(shutdown, 2, handle->pipeprv.fds[1], SHUT_WR);
}
} else {
/* other types of pipes have a single underlying FD, shut it down */
if (handle->pipe.fd != PAL_IDX_POISON) {
INLINE_SYSCALL(shutdown, 2, handle->pipe.fd, shutdown);
}
}
return 0;
}
/*!
* \brief Retrieve attributes of PAL handle.
*
* \param[in] handle PAL handle of type `pipeprv`, `pipesrv`, `pipecli`, or `pipe`.
* \param[out] attr User-supplied buffer to store handle's current attributes.
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_attrquerybyhdl(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) {
int ret;
int val;
if (handle->pipe.fd == PAL_IDX_POISON)
return -PAL_ERROR_BADHANDLE;
@@ -343,6 +436,7 @@ static int pipe_attrquerybyhdl(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) {
/* get number of bytes available for reading (doesn't make sense for "listening" pipes) */
attr->pending_size = 0;
if (!IS_HANDLE_TYPE(handle, pipesrv)) {
int val;
ret = INLINE_SYSCALL(ioctl, 3, handle->pipe.fd, FIONREAD, &val);
if (IS_ERR(ret))
return unix_to_pal_error(ERRNO(ret));
@@ -383,19 +477,26 @@ static int pipe_attrquerybyhdl(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) {
return 0;
}
/*!
* \brief Set attributes of PAL handle.
*
* Currently only `nonblocking` attribute can be set.
*
* \param[in] handle PAL handle of type `pipeprv`, `pipesrv`, `pipecli`, or `pipe`.
* \param[in] attr User-supplied buffer with new handle's attributes.
* \return 0 on success, negative PAL error code otherwise.
*/
static int pipe_attrsetbyhdl(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) {
if (handle->generic.fds[0] == PAL_IDX_POISON)
return -PAL_ERROR_BADHANDLE;
int ret;
PAL_BOL* nonblocking = (HANDLE_HDR(handle)->type == pal_type_pipeprv)
? &handle->pipeprv.nonblocking
: &handle->pipe.nonblocking;
if (attr->nonblocking != *nonblocking) {
ret = INLINE_SYSCALL(fcntl, 3, handle->generic.fds[0], F_SETFL,
attr->nonblocking ? O_NONBLOCK : 0);
int ret = INLINE_SYSCALL(fcntl, 3, handle->generic.fds[0], F_SETFL,
attr->nonblocking ? O_NONBLOCK : 0);
if (IS_ERR(ret))
return unix_to_pal_error(ERRNO(ret));
@@ -405,6 +506,16 @@ static int pipe_attrsetbyhdl(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) {
return 0;
}
/*!
* \brief Retrieve full URI of PAL handle.
*
* Full URI is composed of the type and pipeid: "<type>:<pipeid>".
*
* \param[in] handle PAL handle of type `pipeprv`, `pipesrv`, `pipecli`, or `pipe`.
* \param[out] buffer User-supplied buffer to write URI to.
* \param[in] count Size of the user-supplied buffer.
* \return Number of bytes written on success, negative PAL error code otherwise.
*/
static int pipe_getname(PAL_HANDLE handle, char* buffer, size_t count) {
size_t old_count = count;
int ret;