Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 56 additions & 11 deletions erts/emulator/beam/erl_bif_port.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#define HALLOC_EXTRA 200

static Port *open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump);
static int parse_packet_option(SysDriverOpts *opts, Eterm option);
static int merge_global_environment(erts_osenv_t *env, Eterm key_value_pairs);
static char **convert_args(Eterm);
static void free_args(char **);
Expand Down Expand Up @@ -782,6 +783,58 @@ Eterm erts_port_data_read(Port* prt)
}


static int
parse_packet_option(SysDriverOpts *opts, Eterm option)
{
Sint packet_bytes;
Eterm packet_endian = am_big;

if (is_small(option)) {
packet_bytes = signed_val(option);
} else if (is_tuple_arity(option, 2)) {
Eterm *tp = tuple_val(option);

if (is_not_small(tp[1])) {
return 0;
}

packet_bytes = signed_val(tp[1]);
packet_endian = tp[2];

if (packet_bytes != 2 && packet_bytes != 4) {
return 0;
}
} else {
return 0;
}

switch (packet_bytes) {
case 1:
case 2:
case 4:
break;
default:
return 0;
}

if (packet_endian == am_big) {
opts->packet_endian = ERTS_SYS_DRIVER_PACKET_ENDIAN_BIG;
} else if (packet_endian == am_little) {
opts->packet_endian = ERTS_SYS_DRIVER_PACKET_ENDIAN_LITTLE;
} else if (packet_endian == am_native) {
#ifdef WORDS_BIGENDIAN
opts->packet_endian = ERTS_SYS_DRIVER_PACKET_ENDIAN_BIG;
#else
opts->packet_endian = ERTS_SYS_DRIVER_PACKET_ENDIAN_LITTLE;
#endif
} else {
return 0;
}

opts->packet_bytes = packet_bytes;
return 1;
}

/*
* Open a port. Most of the work is not done here but rather in
* the file io.c.
Expand Down Expand Up @@ -811,6 +864,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump)

/* These are the defaults */
opts.packet_bytes = 0;
opts.packet_endian = ERTS_SYS_DRIVER_PACKET_ENDIAN_BIG;
opts.use_stdio = 1;
opts.redir_stderr = 0;
opts.read_write = 0;
Expand Down Expand Up @@ -847,18 +901,9 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump)
arity = *tp++;
option = *tp++;
if (option == am_packet) {
if (is_not_small(*tp)) {
if (!parse_packet_option(&opts, *tp)) {
goto bad_settings;
}
opts.packet_bytes = signed_val(*tp);
switch (opts.packet_bytes) {
case 1:
case 2:
case 4:
break;
default:
goto bad_settings;
}
} else if (option == am_line) {
if (is_not_small(*tp)) {
goto bad_settings;
Expand Down Expand Up @@ -978,6 +1023,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump)
}
} else if (*nargs == am_stream) {
opts.packet_bytes = 0;
opts.packet_endian = ERTS_SYS_DRIVER_PACKET_ENDIAN_BIG;
} else if (*nargs == am_use_stdio) {
opts.use_stdio = 1;
} else if (*nargs == am_stderr_to_stdout) {
Expand Down Expand Up @@ -1615,4 +1661,3 @@ BIF_RETTYPE decode_packet_3(BIF_ALIST_3)

BIF_RET(res);
}

83 changes: 82 additions & 1 deletion erts/emulator/beam/erl_sys_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ typedef struct _SysDriverOpts SysDriverOpts;

#include "erl_driver.h"

typedef enum {
ERTS_SYS_DRIVER_PACKET_ENDIAN_BIG,
ERTS_SYS_DRIVER_PACKET_ENDIAN_LITTLE
} ErtsSysDriverPacketEndian;

/*
* This structure contains options to all built in drivers.
* None of the drivers use all of the fields.
Expand All @@ -48,6 +53,7 @@ struct _SysDriverOpts {
Uint ifd; /* Input file descriptor (fd driver). */
Uint ofd; /* Outputfile descriptor (fd driver). */
int packet_bytes; /* Number of bytes in packet header. */
ErtsSysDriverPacketEndian packet_endian; /* Byte order of packet header. */
int read_write; /* Read and write bits. */
int use_stdio; /* Use standard I/O: TRUE or FALSE. */
int redir_stderr; /* Redirect stderr to stdout: TRUE/FALSE. */
Expand All @@ -68,8 +74,83 @@ struct _SysDriverOpts {
char msgq_watermarks_set;
};

#endif
ERTS_GLB_INLINE Uint32
erts_sys_driver_get_packet_size(const char *header,
int packet_bytes,
ErtsSysDriverPacketEndian packet_endian);

ERTS_GLB_INLINE int
erts_sys_driver_packet_size_fits(ErlDrvSizeT packet_size, int packet_bytes);

ERTS_GLB_INLINE void
erts_sys_driver_put_packet_size(ErlDrvSizeT packet_size,
char *header,
int packet_bytes,
ErtsSysDriverPacketEndian packet_endian);

#if ERTS_GLB_INLINE_INCL_FUNC_DEF

ERTS_GLB_INLINE Uint32
erts_sys_driver_get_packet_size(const char *header,
int packet_bytes,
ErtsSysDriverPacketEndian packet_endian)
{
Uint32 packet_size = 0;
int i;

for (i = 0; i < packet_bytes; i++) {
int index = packet_endian == ERTS_SYS_DRIVER_PACKET_ENDIAN_LITTLE
? packet_bytes - i - 1
: i;

packet_size = (packet_size << 8) |
((const unsigned char *) header)[index];
}

return packet_size;
}

ERTS_GLB_INLINE int
erts_sys_driver_packet_size_fits(ErlDrvSizeT packet_size, int packet_bytes)
{
if (packet_bytes < 0 ||
packet_size > ((ErlDrvSizeT) -1) - (ErlDrvSizeT) packet_bytes) {
return 0;
}

switch (packet_bytes) {
case 0:
return 1;
case 1:
return packet_size <= 0xff;
case 2:
return packet_size <= 0xffff;
case 4:
return (Uint64) packet_size <= ERTS_UINT32_MAX;
default:
return 0;
}
}

ERTS_GLB_INLINE void
erts_sys_driver_put_packet_size(ErlDrvSizeT packet_size,
char *header,
int packet_bytes,
ErtsSysDriverPacketEndian packet_endian)
{
unsigned char *bytes = (unsigned char *) header;
int i;

for (i = 0; i < packet_bytes; i++) {
int index = packet_endian == ERTS_SYS_DRIVER_PACKET_ENDIAN_LITTLE
? i
: packet_bytes - i - 1;

bytes[index] = packet_size & 0xff;
packet_size >>= 8;
}
}

#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */

#endif
41 changes: 21 additions & 20 deletions erts/emulator/sys/unix/sys_drivers.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <sys/uio.h>
#include <termios.h>
#include <ctype.h>
#include <limits.h>
#include <sys/utsname.h>
#include <sys/select.h>
#include <arpa/inet.h>
Expand Down Expand Up @@ -110,6 +111,7 @@ typedef struct driver_data {
ErtsSysFdData *ofd;
ErtsSysFdData *ifd;
int packet_bytes;
ErtsSysDriverPacketEndian packet_endian;
int pid;
int alive;
int status;
Expand Down Expand Up @@ -336,7 +338,6 @@ static ErtsSysDriverData *
create_driver_data(ErlDrvPort port_num,
int ifd,
int ofd,
int packet_bytes,
int read_write,
int exit_status,
int pid,
Expand Down Expand Up @@ -365,7 +366,8 @@ create_driver_data(ErlDrvPort port_num,
if (prt != ERTS_INVALID_ERL_DRV_PORT)
prt->os_pid = pid;

driver_data->packet_bytes = packet_bytes;
driver_data->packet_bytes = opts->packet_bytes;
driver_data->packet_endian = opts->packet_endian;
driver_data->port_num = port_num;
driver_data->pid = pid;
driver_data->alive = exit_status ? 1 : 0;
Expand Down Expand Up @@ -691,8 +693,8 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,

erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);

dd = create_driver_data(port_num, ifd[0], ofd[1], opts->packet_bytes,
DO_WRITE | DO_READ, opts->exit_status,
dd = create_driver_data(port_num, ifd[0], ofd[1],
DO_WRITE | DO_READ, opts->exit_status,
0, 0, opts);

{
Expand Down Expand Up @@ -974,7 +976,6 @@ static ErlDrvData fd_start(ErlDrvPort port_num, char* name,
}
}
return (ErlDrvData)create_driver_data(port_num, opts->ifd, opts->ofd,
opts->packet_bytes,
opts->read_write, 0, -1,
!non_blocking, opts);
}
Expand Down Expand Up @@ -1060,22 +1061,21 @@ static void outputv(ErlDrvData e, ErlIOVec* ev)
ErtsSysDriverData *dd = (ErtsSysDriverData*)e;
ErlDrvPort ix = dd->port_num;
int pb = dd->packet_bytes;
ErtsSysDriverPacketEndian packet_endian = dd->packet_endian;
int ofd = dd->ofd ? dd->ofd->fd : -1;
ssize_t n;
char lb[4];
char* lbp;
ErlDrvSizeT len = ev->size;
ErlDrvSizeT qsz;

/* (len > ((unsigned long)-1 >> (4-pb)*8)) */
/* if (pb >= 0 && (len & (((ErlDrvSizeT)1 << (pb*8))) - 1) != len) {*/
if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) {
if (!erts_sys_driver_packet_size_fits(len, pb)) {
driver_failure_posix(ix, EINVAL);
return; /* -1; */
}
/* Handles 0 <= pb <= 4 only */
put_int32((Uint32) len, lb);
lbp = lb + (4-pb);
erts_sys_driver_put_packet_size(len, lb, pb, packet_endian);
lbp = lb;

ev->iov[0].iov_base = lbp;
ev->iov[0].iov_len = pb;
Expand Down Expand Up @@ -1142,22 +1142,21 @@ static void output(ErlDrvData e, char* buf, ErlDrvSizeT len)
ErtsSysDriverData *dd = (ErtsSysDriverData*)e;
ErlDrvPort ix = dd->port_num;
int pb = dd->packet_bytes;
ErtsSysDriverPacketEndian packet_endian = dd->packet_endian;
int ofd = dd->ofd ? dd->ofd->fd : -1;
ssize_t n;
ErlDrvSizeT qsz;
char lb[4];
char* lbp;
struct iovec iv[2];

/* (len > ((unsigned long)-1 >> (4-pb)*8)) */
if (((pb == 2) && (len > 0xffff))
|| (pb == 1 && len > 0xff)
if (!erts_sys_driver_packet_size_fits(len, pb)
|| dd->pid == 0 /* Attempt at output before port is ready */) {
driver_failure_posix(ix, EINVAL);
return; /* -1; */
}
put_int32(len, lb);
lbp = lb + (4-pb);
erts_sys_driver_put_packet_size(len, lb, pb, packet_endian);
lbp = lb;

qsz = driver_sizeq(ix);
if (qsz) {
Expand Down Expand Up @@ -1405,11 +1404,13 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd)
}
dd->ifd->psz = 0;

switch (packet_bytes) {
case 1: h = get_int8(dd->ifd->pbuf); break;
case 2: h = get_int16(dd->ifd->pbuf); break;
case 4: h = get_uint32(dd->ifd->pbuf); break;
default: ASSERT(0); return; /* -1; */
h = erts_sys_driver_get_packet_size(dd->ifd->pbuf,
packet_bytes,
dd->packet_endian);
if (h > (Uint) INT_MAX) {
errno = EINVAL;
port_inp_failure(dd, -1);
break;
}

if (h <= (bytes_left)) {
Expand Down
Loading