diff options
Diffstat (limited to 'erts/emulator/drivers')
-rw-r--r-- | erts/emulator/drivers/common/efile_drv.c | 467 | ||||
-rw-r--r-- | erts/emulator/drivers/common/erl_efile.h | 22 | ||||
-rw-r--r-- | erts/emulator/drivers/common/gzio.c | 84 | ||||
-rw-r--r-- | erts/emulator/drivers/common/gzio.h | 16 | ||||
-rw-r--r-- | erts/emulator/drivers/common/gzio_zutil.h | 9 | ||||
-rw-r--r-- | erts/emulator/drivers/common/inet_drv.c | 1736 | ||||
-rw-r--r-- | erts/emulator/drivers/common/ram_file_drv.c | 8 | ||||
-rw-r--r-- | erts/emulator/drivers/common/zlib_drv.c | 7 | ||||
-rw-r--r-- | erts/emulator/drivers/ose/ose_efile.c | 1124 | ||||
-rw-r--r-- | erts/emulator/drivers/ose/ose_signal_drv.c | 896 | ||||
-rw-r--r-- | erts/emulator/drivers/ose/ttsl_drv.c | 68 | ||||
-rw-r--r-- | erts/emulator/drivers/unix/ttsl_drv.c | 69 | ||||
-rw-r--r-- | erts/emulator/drivers/unix/unix_efile.c | 689 | ||||
-rw-r--r-- | erts/emulator/drivers/win32/registry_drv.c | 4 | ||||
-rw-r--r-- | erts/emulator/drivers/win32/ttsl_drv.c | 6 | ||||
-rw-r--r-- | erts/emulator/drivers/win32/win_efile.c | 805 | ||||
-rw-r--r-- | erts/emulator/drivers/win32/winsock_func.h | 102 |
17 files changed, 4734 insertions, 1378 deletions
diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 912f5d3d8b..b62e9a0306 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2012. All Rights Reserved. + * Copyright Ericsson AB 1996-2013. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -56,7 +56,8 @@ #define FILE_FDATASYNC 30 #define FILE_FADVISE 31 #define FILE_SENDFILE 32 - +#define FILE_FALLOCATE 33 +#define FILE_CLOSE_ON_PORT_EXIT 34 /* Return codes */ #define FILE_RESP_OK 0 @@ -98,7 +99,16 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif + +#ifndef __OSE__ +#include <ctype.h> +#include <sys/types.h> #include <stdlib.h> +#else +#include "ctype.h" +#include "sys/types.h" +#include "stdlib.h" +#endif /* Need (NON)BLOCKING macros for sendfile */ #ifndef WANT_NONBLOCKING @@ -110,11 +120,9 @@ #include "erl_driver.h" #include "erl_efile.h" #include "erl_threads.h" -#include "zlib.h" #include "gzio.h" #include "dtrace-wrapper.h" -#include <ctype.h> -#include <sys/types.h> + void erl_exit(int n, char *fmt, ...); @@ -167,7 +175,7 @@ dt_private *get_dt_private(int); #ifdef USE_THREADS -#define IF_THRDS if (sys_info.async_threads > 0) +#define THRDS_AVAILABLE (sys_info.async_threads > 0) #ifdef HARDDEBUG /* HARDDEBUG in io.c is expected too */ #define TRACE_DRIVER fprintf(stderr, "Efile: ") #else @@ -177,23 +185,26 @@ dt_private *get_dt_private(int); #define MUTEX_LOCK(m) do { IF_THRDS { TRACE_DRIVER; driver_pdl_lock(m); } } while (0) #define MUTEX_UNLOCK(m) do { IF_THRDS { TRACE_DRIVER; driver_pdl_unlock(m); } } while (0) #else +#define THRDS_AVAILABLE (0) #define MUTEX_INIT(m, p) #define MUTEX_LOCK(m) #define MUTEX_UNLOCK(m) #endif +#define IF_THRDS if (THRDS_AVAILABLE) +#define SENDFILE_FLGS_USE_THREADS (1 << 0) /** * On DARWIN sendfile can deadlock with close if called in * different threads. So until Apple fixes so that sendfile * is not buggy we disable usage of the async pool for * DARWIN. The testcase t_sendfile_crashduring reproduces - * this error when using +A 10. + * this error when using +A 10 and enabling SENDFILE_FLGS_USE_THREADS. */ #if defined(__APPLE__) && defined(__MACH__) -#define USE_THRDS_FOR_SENDFILE 0 +#define USE_THRDS_FOR_SENDFILE(DATA) 0 #else -#define USE_THRDS_FOR_SENDFILE (sys_info.async_threads > 0) +#define USE_THRDS_FOR_SENDFILE(DATA) (DATA->flags & SENDFILE_FLGS_USE_THREADS) #endif /* defined(__APPLE__) && defined(__MACH__) */ @@ -299,7 +310,7 @@ static void file_stop_select(ErlDrvEvent event, void* _); enum e_timer {timer_idle, timer_again, timer_write}; #ifdef HAVE_SENDFILE enum e_sendfile {sending, not_sending}; -static void free_sendfile(void *data); +#define SENDFILE_USE_THREADS (1 << 0) #endif /* HAVE_SENDFILE */ struct t_data; @@ -395,7 +406,6 @@ struct t_pwritev { ErlDrvPort port; ErlDrvPDL q_mtx; size_t size; - size_t free_size; unsigned cnt; unsigned n; struct t_pbuf_spec specs[1]; @@ -428,6 +438,7 @@ struct t_data int level; void (*invoke)(void *); void (*free)(void *); + void *data_to_free; /* used by FILE_CLOSE_ON_PORT_EXIT only */ int again; int reply; #ifdef USE_VM_PROBES @@ -439,6 +450,7 @@ struct t_data Efile_error errInfo; int flags; SWord fd; + int is_fd_unused; /**/ Efile_info info; EFILE_DIR_HANDLE dir_handle; /* Handle to open directory. */ @@ -458,7 +470,6 @@ struct t_data ErlDrvPort port; ErlDrvPDL q_mtx; size_t size; - size_t free_size; size_t reply_size; } writev; struct t_pwritev pwritev; @@ -503,6 +514,10 @@ struct t_data Uint64 written; } sendfile; #endif /* HAVE_SENDFILE */ + struct { + Sint64 offset; + Sint64 length; + } fallocate; } c; char b[1]; }; @@ -536,57 +551,85 @@ static void *ef_safe_realloc(void *op, Uint s) */ /* char EV_CHAR_P(ErlIOVec *ev, int p, int q) */ -#define EV_CHAR_P(ev, p, q) \ - (((char *)(ev)->iov[(q)].iov_base) + (p)) +#define EV_CHAR_P(ev, p, q) \ + (((char *)(ev)->iov[q].iov_base) + (p)) /* int EV_GET_CHAR(ErlIOVec *ev, char *p, int *pp, int *qp) */ -#define EV_GET_CHAR(ev, p, pp, qp) \ - (*(pp)+1 <= (ev)->iov[*(qp)].iov_len \ - ? (*(p) = *EV_CHAR_P(ev, *(pp), *(qp)), \ - *(pp) = ( *(pp)+1 < (ev)->iov[*(qp)].iov_len \ - ? *(pp)+1 \ - : ((*(qp))++, 0)), \ - !0) \ - : 0) +#define EV_GET_CHAR(ev, p, pp, qp) efile_ev_get_char(ev, p ,pp, qp) +static int +efile_ev_get_char(ErlIOVec *ev, char *p, size_t *pp, size_t *qp) { + if (*pp + 1 <= ev->iov[*qp].iov_len) { + *p = *EV_CHAR_P(ev, *pp, *qp); + if (*pp + 1 < ev->iov[*qp].iov_len) + *pp += 1; + else { + *qp += 1; + *pp = 0; + } + return !0; + } + return 0; +} /* Uint32 EV_UINT32(ErlIOVec *ev, int p, int q)*/ -#define EV_UINT32(ev, p, q) \ - ((Uint32) *(((unsigned char *)(ev)->iov[(q)].iov_base) + (p))) +#define EV_UINT32(ev, p, q) \ + ((Uint32) ((unsigned char *)(ev)->iov[q].iov_base)[p]) /* int EV_GET_UINT32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) */ -#define EV_GET_UINT32(ev, p, pp, qp) \ - (*(pp)+4 <= (ev)->iov[*(qp)].iov_len \ - ? (*(p) = (EV_UINT32(ev, *(pp), *(qp)) << 24) \ - | (EV_UINT32(ev, *(pp)+1, *(qp)) << 16) \ - | (EV_UINT32(ev, *(pp)+2, *(qp)) << 8) \ - | (EV_UINT32(ev, *(pp)+3, *(qp))), \ - *(pp) = ( *(pp)+4 < (ev)->iov[*(qp)].iov_len \ - ? *(pp)+4 \ - : ((*(qp))++, 0)), \ - !0) \ - : 0) +#define EV_GET_UINT32(ev, p, pp, qp) efile_ev_get_uint32(ev, p, pp, qp) +static int +efile_ev_get_uint32(ErlIOVec *ev, Uint32 *p, size_t *pp, size_t *qp) { + if (*pp + 4 <= ev->iov[*qp].iov_len) { + *p = (EV_UINT32(ev, *pp, *qp) << 24) + | (EV_UINT32(ev, *pp + 1, *qp) << 16) + | (EV_UINT32(ev, *pp + 2, *qp) << 8) + | (EV_UINT32(ev, *pp + 3, *qp)); + if (*pp + 4 < ev->iov[*qp].iov_len) + *pp += 4; + else { + *qp += 1; + *pp = 0; + } + return !0; + } + return 0; +} /* Uint64 EV_UINT64(ErlIOVec *ev, int p, int q)*/ -#define EV_UINT64(ev, p, q) \ - ((Uint64) *(((unsigned char *)(ev)->iov[(q)].iov_base) + (p))) - -/* int EV_GET_UINT64(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) */ -#define EV_GET_UINT64(ev, p, pp, qp) \ - (*(pp)+8 <= (ev)->iov[*(qp)].iov_len \ - ? (*(p) = (EV_UINT64(ev, *(pp), *(qp)) << 56) \ - | (EV_UINT64(ev, *(pp)+1, *(qp)) << 48) \ - | (EV_UINT64(ev, *(pp)+2, *(qp)) << 40) \ - | (EV_UINT64(ev, *(pp)+3, *(qp)) << 32) \ - | (EV_UINT64(ev, *(pp)+4, *(qp)) << 24) \ - | (EV_UINT64(ev, *(pp)+5, *(qp)) << 16) \ - | (EV_UINT64(ev, *(pp)+6, *(qp)) << 8) \ - | (EV_UINT64(ev, *(pp)+7, *(qp))), \ - *(pp) = ( *(pp)+8 < (ev)->iov[*(qp)].iov_len \ - ? *(pp)+8 \ - : ((*(qp))++, 0)), \ - !0) \ - : 0) +#define EV_UINT64(ev, p, q) \ + ((Uint64) ((unsigned char *)(ev)->iov[q].iov_base)[p]) + +/* int EV_GET_UINT64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) */ +#define EV_GET_UINT64(ev, p, pp, qp) efile_ev_get_uint64(ev, p, pp, qp) +static int +efile_ev_get_uint64(ErlIOVec *ev, Uint64 *p, size_t *pp, size_t *qp) { + if (*pp + 8 <= ev->iov[*qp].iov_len) { + *p = (EV_UINT64(ev, *pp, *qp) << 56) + | (EV_UINT64(ev, *pp + 1, *qp) << 48) + | (EV_UINT64(ev, *pp + 2, *qp) << 40) + | (EV_UINT64(ev, *pp + 3, *qp) << 32) + | (EV_UINT64(ev, *pp + 4, *qp) << 24) + | (EV_UINT64(ev, *pp + 5, *qp) << 16) + | (EV_UINT64(ev, *pp + 6, *qp) << 8) + | (EV_UINT64(ev, *pp + 7, *qp)); + if (*pp + 8 < ev->iov[*qp].iov_len) + *pp += 8; + else { + *qp += 1; + *pp = 0; + } + return !0; + } + return 0; +} +/* int EV_GET_SINT64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) */ +#define EV_GET_SINT64(ev, p, pp, qp) efile_ev_get_sint64(ev, p, pp, qp) +static int +efile_ev_get_sint64(ErlIOVec *ev, Sint64 *p, size_t *pp, size_t *qp) { + Uint64 *tmp = (Uint64*)p; + return EV_GET_UINT64(ev, tmp, pp, qp); +} #if 0 @@ -730,6 +773,9 @@ file_init(void) : 0); driver_system_info(&sys_info, sizeof(ErlDrvSysInfo)); + /* run initiation of efile_driver if needed */ + efile_init(); + #ifdef USE_VM_PROBES erts_mtx_init(&dt_driver_mutex, "efile_drv dtrace mutex"); pthread_key_create(&dt_driver_key, NULL); @@ -738,6 +784,7 @@ file_init(void) return 0; } + /********************************************************************* * Driver entry point -> start */ @@ -754,7 +801,7 @@ file_start(ErlDrvPort port, char* command) } desc->fd = FILE_FD_INVALID; desc->port = port; - desc->key = (unsigned int) (UWord) port; + desc->key = driver_async_port_key(port); desc->flags = 0; desc->invoke = NULL; desc->d = NULL; @@ -781,14 +828,9 @@ file_start(ErlDrvPort port, char* command) return (ErlDrvData) desc; } -static void free_data(void *data) -{ - EF_FREE(data); -} - static void do_close(int flags, SWord fd) { if (flags & EFILE_COMPRESSED) { - erts_gzclose((gzFile)(fd)); + erts_gzclose((ErtsGzFile)(fd)); } else { efile_closefile((int) fd); } @@ -803,25 +845,27 @@ static void invoke_close(void *data) DTRACE_INVOKE_RETURN(FILE_CLOSE); } -/********************************************************************* - * Driver entry point -> stop - */ -static void -file_stop(ErlDrvData e) +static void free_data(void *data) { - file_descriptor* desc = (file_descriptor*)e; - - TRACE_C('p'); + struct t_data *d = (struct t_data *) data; - if (desc->fd != FILE_FD_INVALID) { - do_close(desc->flags, desc->fd); - desc->fd = FILE_FD_INVALID; - desc->flags = 0; - } - if (desc->read_binp) { - driver_free_binary(desc->read_binp); + switch (d->command) { + case FILE_OPEN: + if (d->is_fd_unused && d->fd != FILE_FD_INVALID) { + /* This is OK to do in scheduler thread because there can be no async op + ongoing for this fd here, as we exited during async open. + Ideally, this close should happen in an async thread too, but that would + require a substantial rewrite, as we are here because of a dead port and + cannot schedule async jobs for that port any more... */ + do_close(d->flags, d->fd); + } + break; + case FILE_CLOSE_ON_PORT_EXIT: + EF_FREE(d->data_to_free); + break; } - EF_FREE(desc); + + EF_FREE(data); } @@ -878,6 +922,7 @@ static void reply_Uint_posix_error(file_descriptor *desc, Uint num, driver_output2(desc->port, response, t-response, NULL, 0); } +#ifdef HAVE_SENDFILE static void reply_string_error(file_descriptor *desc, char* str) { char response[256]; /* Response buffer. */ char* s; @@ -888,6 +933,7 @@ static void reply_string_error(file_descriptor *desc, char* str) { *t = tolower(*s); driver_output2(desc->port, response, t-response, NULL, 0); } +#endif static int reply_error(file_descriptor *desc, Efile_error *errInfo) /* The error codes. */ @@ -1104,10 +1150,10 @@ static void invoke_read(void *data) } read_size = size; if (d->flags & EFILE_COMPRESSED) { - read_size = erts_gzread((gzFile)d->fd, + read_size = erts_gzread((ErtsGzFile)d->fd, d->c.read.binp->orig_bytes + d->c.read.bin_offset, size); - status = (read_size != -1); + status = (read_size != (size_t) -1); if (!status) { d->errInfo.posix_errno = EIO; } @@ -1144,7 +1190,7 @@ static void invoke_read_line(void *data) { struct t_data *d = (struct t_data *) data; int status; - size_t read_size; + size_t read_size = 0; int local_loop = (d->again == 0); DTRACE_INVOKE_SETUP(FILE_READ_LINE); @@ -1155,7 +1201,14 @@ static void invoke_read_line(void *data) /* Need more place */ ErlDrvSizeT need = (d->c.read_line.read_size >= DEFAULT_LINEBUF_SIZE) ? d->c.read_line.read_size + DEFAULT_LINEBUF_SIZE : DEFAULT_LINEBUF_SIZE; - ErlDrvBinary *newbin = driver_alloc_binary(need); + ErlDrvBinary *newbin; +#if !ALWAYS_READ_LINE_AHEAD + /* Use read_ahead size if need does not exceed it */ + if (need < (d->c.read_line.binp)->orig_size && + d->c.read_line.read_ahead) + need = (d->c.read_line.binp)->orig_size; +#endif + newbin = driver_alloc_binary(need); if (newbin == NULL) { d->result_ok = 0; d->errInfo.posix_errno = ENOMEM; @@ -1170,11 +1223,11 @@ static void invoke_read_line(void *data) size = need - d->c.read_line.read_size; } if (d->flags & EFILE_COMPRESSED) { - read_size = erts_gzread((gzFile)d->fd, + read_size = erts_gzread((ErtsGzFile)d->fd, d->c.read_line.binp->orig_bytes + d->c.read_line.read_offset + d->c.read_line.read_size, size); - status = (read_size != -1); + status = (read_size != (size_t) -1); if (!status) { d->errInfo.posix_errno = EIO; } @@ -1211,7 +1264,7 @@ static void invoke_read_line(void *data) d->c.read_line.read_size -= too_much; ASSERT(d->c.read_line.read_size >= 0); if (d->flags & EFILE_COMPRESSED) { - Sint64 location = erts_gzseek((gzFile)d->fd, + Sint64 location = erts_gzseek((ErtsGzFile)d->fd, -((Sint64) too_much), EFILE_SEEK_CUR); if (location == -1) { d->result_ok = 0; @@ -1334,7 +1387,7 @@ static void invoke_preadv(void *data) = efile_pread(&d->errInfo, (int) d->fd, c->offsets[c->cnt] + c->size, - ev->iov[1 + c->cnt].iov_base + c->size, + ((char *)ev->iov[1 + c->cnt].iov_base) + c->size, read_size, &bytes_read))) { bytes_read_so_far += bytes_read; @@ -1496,7 +1549,7 @@ static void invoke_writev(void *data) { */ errno = EINVAL; if (! (status = - erts_gzwrite((gzFile)d->fd, + erts_gzwrite((ErtsGzFile)d->fd, iov[i].iov_base, iov[i].iov_len)) == iov[i].iov_len) { d->errInfo.posix_errno = @@ -1520,26 +1573,24 @@ static void invoke_writev(void *data) { } EF_FREE(iov); - d->c.writev.free_size = size; - d->c.writev.size -= size; if (! d->result_ok) { d->again = 0; + MUTEX_LOCK(d->c.writev.q_mtx); + driver_deq(d->c.writev.port, d->c.writev.size); + MUTEX_UNLOCK(d->c.writev.q_mtx); } else { if (! segment) { d->again = 0; } + d->c.writev.size -= size; TRACE_F(("w%lu", (unsigned long)size)); - + MUTEX_LOCK(d->c.writev.q_mtx); + driver_deq(d->c.writev.port, size); + MUTEX_UNLOCK(d->c.writev.q_mtx); } - DTRACE_INVOKE_RETURN(FILE_WRITE); -} -static void free_writev(void *data) { - struct t_data *d = data; - MUTEX_LOCK(d->c.writev.q_mtx); - driver_deq(d->c.writev.port, d->c.writev.size + d->c.writev.free_size); - MUTEX_UNLOCK(d->c.writev.q_mtx); - EF_FREE(d); + + DTRACE_INVOKE_RETURN(FILE_WRITE); } static void invoke_pwd(void *data) @@ -1590,7 +1641,7 @@ static void invoke_pwritev(void *data) { struct t_pwritev *c = &d->c.pwritev; size_t p; int segment; - size_t size, write_size; + size_t size, write_size, written; DTRACE_INVOKE_SETUP(FILE_PWRITEV); segment = d->again && c->size >= 2*FILE_SEGMENT_WRITE; @@ -1610,39 +1661,35 @@ static void invoke_pwritev(void *data) { if (iovlen < 0) goto error; /* Port terminated */ - for (iovcnt = 0, c->free_size = 0; - c->cnt < c->n && iovcnt < iovlen && c->free_size < size; + for (iovcnt = 0, written = 0; + c->cnt < c->n && iovcnt < iovlen && written < size; c->cnt++) { int chop; write_size = c->specs[c->cnt].size; if (iov[iovcnt].iov_len - p < write_size) { - /* Mismatch between pos/size spec and what is queued */ - d->errInfo.posix_errno = EINVAL; - d->result_ok = 0; - d->again = 0; - goto done; + goto error; } - chop = segment && c->free_size + write_size >= 2*FILE_SEGMENT_WRITE; + chop = segment && written + write_size >= 2*FILE_SEGMENT_WRITE; if (chop) { - ASSERT(c->free_size < FILE_SEGMENT_WRITE); + ASSERT(written < FILE_SEGMENT_WRITE); write_size = FILE_SEGMENT_WRITE + FILE_SEGMENT_WRITE/2 - - c->free_size; + - written; } d->result_ok = efile_pwrite(&d->errInfo, (int) d->fd, - iov[iovcnt].iov_base + p, + (char *)(iov[iovcnt].iov_base) + p, write_size, c->specs[c->cnt].offset); if (! d->result_ok) { d->again = 0; - goto done; + goto deq_error; } - c->free_size += write_size; + written += write_size; c->size -= write_size; if (chop) { c->specs[c->cnt].offset += write_size; c->specs[c->cnt].size -= write_size; /* Schedule out (d->again != 0) */ - goto done; + break; } /* Move forward in buffer */ p += write_size; @@ -1664,25 +1711,29 @@ static void invoke_pwritev(void *data) { d->errInfo.posix_errno = EINVAL; d->result_ok = 0; d->again = 0; + deq_error: + MUTEX_LOCK(d->c.writev.q_mtx); + driver_deq(d->c.pwritev.port, c->size); + MUTEX_UNLOCK(d->c.writev.q_mtx); + + goto done; } else { - ASSERT(c->free_size == size); + ASSERT(written == size); d->again = 0; } + } else { + ASSERT(written >= FILE_SEGMENT_WRITE); } + + MUTEX_LOCK(d->c.writev.q_mtx); + driver_deq(d->c.pwritev.port, written); + MUTEX_UNLOCK(d->c.writev.q_mtx); done: EF_FREE(iov); /* Free our copy of the vector, nothing to restore */ + DTRACE_INVOKE_RETURN(FILE_PWRITEV); } -static void free_pwritev(void *data) { - struct t_data *d = data; - - MUTEX_LOCK(d->c.writev.q_mtx); - driver_deq(d->c.pwritev.port, d->c.pwritev.free_size + d->c.pwritev.size); - MUTEX_UNLOCK(d->c.writev.q_mtx); - EF_FREE(d); -} - static void invoke_flstat(void *data) { struct t_data *d = (struct t_data *) data; @@ -1760,7 +1811,7 @@ static void invoke_lseek(void *data) d->errInfo.posix_errno = EINVAL; status = 0; } else { - d->c.lseek.location = erts_gzseek((gzFile)d->fd, + d->c.lseek.location = erts_gzseek((ErtsGzFile)d->fd, offset, d->c.lseek.origin); if (d->c.lseek.location == -1) { d->errInfo.posix_errno = errno; @@ -1848,7 +1899,7 @@ static void invoke_open(void *data) if (status || (d->errInfo.posix_errno != EISDIR)) { mode = (d->flags & EFILE_MODE_READ) ? "rb" : "wb"; d->fd = (SWord) erts_gzopen(d->b, mode); - if ((gzFile)d->fd) { + if ((ErtsGzFile)d->fd) { status = 1; } else { if (errno == 0) { @@ -1862,6 +1913,9 @@ static void invoke_open(void *data) } d->result_ok = status; + if (!status) { + d->fd = FILE_FD_INVALID; + } DTRACE_INVOKE_RETURN(FILE_OPEN); } @@ -1893,7 +1947,7 @@ static void invoke_sendfile(void *data) d->c.sendfile.written += nbytes; - if (result == 1 || (result == 0 && USE_THRDS_FOR_SENDFILE)) { + if (result == 1 || (result == 0 && USE_THRDS_FOR_SENDFILE(d))) { d->result_ok = 0; } else if (result == 0 && (d->errInfo.posix_errno == EAGAIN || d->errInfo.posix_errno == EINTR)) { @@ -1910,7 +1964,7 @@ static void invoke_sendfile(void *data) static void free_sendfile(void *data) { struct t_data *d = (struct t_data *)data; - if (USE_THRDS_FOR_SENDFILE) { + if (USE_THRDS_FOR_SENDFILE(d)) { SET_NONBLOCKING(d->c.sendfile.out_fd); } else { MUTEX_LOCK(d->c.sendfile.q_mtx); @@ -1953,6 +2007,17 @@ static int flush_sendfile(file_descriptor *desc,void *_) { #endif /* HAVE_SENDFILE */ +static void invoke_fallocate(void *data) +{ + struct t_data *d = (struct t_data *) data; + int fd = (int) d->fd; + Sint64 offset = d->c.fallocate.offset; + Sint64 length = d->c.fallocate.length; + + d->again = 0; + d->result_ok = efile_fallocate(&d->errInfo, fd, offset, length); +} + static void free_readdir(void *data) { struct t_data *d = (struct t_data *) data; @@ -1982,21 +2047,8 @@ static void try_free_read_bin(file_descriptor *desc) { static int try_again(file_descriptor *desc, struct t_data *d) { - if (! d->again) { + if (! d->again) return 0; - } - switch (d->command) { - case FILE_WRITE: - MUTEX_LOCK(d->c.writev.q_mtx); - driver_deq(d->c.writev.port, d->c.writev.free_size); - MUTEX_UNLOCK(d->c.writev.q_mtx); - break; - case FILE_PWRITEV: - MUTEX_LOCK(d->c.writev.q_mtx); - driver_deq(d->c.pwritev.port, d->c.pwritev.free_size); - MUTEX_UNLOCK(d->c.writev.q_mtx); - break; - } if (desc->timer_state != timer_idle) { driver_cancel_timer(desc->port); } @@ -2052,10 +2104,9 @@ static struct t_data *async_write(file_descriptor *desc, int *errp, } #endif d->reply = reply; - d->c.writev.free_size = 0; d->c.writev.reply_size = reply_size; d->invoke = invoke_writev; - d->free = free_writev; + d->free = free_data; d->level = 1; cq_enq(desc, d); desc->write_buffered = 0; @@ -2216,6 +2267,49 @@ static int lseek_flush_read(file_descriptor *desc, int *errp } +/********************************************************************* + * Driver entry point -> stop + * The close has to be scheduled on async thread, so that currently active + * async operation does not suddenly have the ground disappearing under their feet... + */ +static void +file_stop(ErlDrvData e) +{ + file_descriptor* desc = (file_descriptor*)e; + + TRACE_C('p'); + + IF_THRDS { + flush_read(desc); + if (desc->fd != FILE_FD_INVALID) { + struct t_data *d = EF_SAFE_ALLOC(sizeof(struct t_data)); + d->command = FILE_CLOSE_ON_PORT_EXIT; + d->reply = !0; + d->fd = desc->fd; + d->flags = desc->flags; + d->invoke = invoke_close; + d->free = free_data; + d->level = 2; + d->data_to_free = (void *) desc; + cq_enq(desc, d); + desc->fd = FILE_FD_INVALID; + desc->flags = 0; + cq_execute(desc); + } else { + EF_FREE(desc); + } + } else { + if (desc->fd != FILE_FD_INVALID) { + do_close(desc->flags, desc->fd); + desc->fd = FILE_FD_INVALID; + desc->flags = 0; + } + if (desc->read_binp) { + driver_free_binary(desc->read_binp); + } + EF_FREE(desc); + } +} /********************************************************************* * Driver entry point -> ready_async @@ -2325,7 +2419,7 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) desc->write_errInfo = d->errInfo; } } - free_writev(data); + free_data(data); break; case FILE_LSEEK: if (d->reply) { @@ -2348,6 +2442,7 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) case FILE_RENAME: case FILE_WRITE_INFO: case FILE_FADVISE: + case FILE_FALLOCATE: reply(desc, d->result_ok, &d->errInfo); free_data(data); break; @@ -2373,8 +2468,10 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) if (!d->result_ok) { reply_error(desc, &d->errInfo); } else { + ASSERT(d->is_fd_unused); desc->fd = d->fd; desc->flags = d->flags; + d->is_fd_unused = 0; reply_Uint(desc, d->fd); } free_data(data); @@ -2436,7 +2533,6 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) } free_readdir(data); break; - /* See file_stop */ case FILE_CLOSE: if (d->reply) { TRACE_C('K'); @@ -2453,7 +2549,7 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) } else { reply_Uint(desc, d->c.pwritev.n); } - free_pwritev(data); + free_data(data); break; case FILE_PREADV: if (!d->result_ok) { @@ -2488,7 +2584,7 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) reply_Sint64(desc, d->c.sendfile.written); desc->sendfile_state = not_sending; free_sendfile(data); - } else if (d->result_ok == 1) { // If we are using select to send the rest of the data + } else if (d->result_ok == 1) { /* If we are using select to send the rest of the data */ desc->sendfile_state = sending; desc->d = d; driver_select(desc->port, (ErlDrvEvent)(long)d->c.sendfile.out_fd, @@ -2496,16 +2592,26 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) } break; #endif + case FILE_CLOSE_ON_PORT_EXIT: + /* See file_stop. However this is never invoked after the port is killed. */ + free_data(data); + EF_FREE(desc); + desc = NULL; + /* This is it for this port, so just send dtrace and return, avoid doing anything to the freed data */ + DTRACE6(efile_drv_return, sched_i1, sched_i2, sched_utag, + command, result_ok, posix_errno); + return; default: abort(); } DTRACE6(efile_drv_return, sched_i1, sched_i2, sched_utag, command, result_ok, posix_errno); - if (desc->write_buffered != 0 && desc->timer_state == timer_idle) { + if (desc->write_buffered != 0 && desc->timer_state == timer_idle ) { desc->timer_state = timer_write; driver_set_timer(desc->port, desc->write_delay); } cq_execute(desc); + } @@ -2745,6 +2851,7 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d->invoke = invoke_open; d->free = free_data; d->level = 2; + d->is_fd_unused = 1; goto done; } @@ -2958,6 +3065,20 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) goto done; } + case FILE_FALLOCATE: + { + d = EF_SAFE_ALLOC(sizeof(struct t_data)); + + d->fd = fd; + d->command = command; + d->invoke = invoke_fallocate; + d->free = free_data; + d->level = 2; + d->c.fallocate.offset = get_int64((uchar*) buf); + d->c.fallocate.length = get_int64(((uchar*) buf) + sizeof(Sint64)); + goto done; + } + } /* @@ -3028,25 +3149,25 @@ file_flush(ErlDrvData e) { /********************************************************************* * Driver entry point -> control + * Only debug functionality... */ static ErlDrvSSizeT file_control(ErlDrvData e, unsigned int command, char* buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen) { - /* - * warning: variable ‘desc’ set but not used - * [-Wunused-but-set-variable] - * ... no kidding ... - * - * file_descriptor *desc = (file_descriptor *)e; switch (command) { + case 'K' : + if (rlen < 4) { + *rbuf = EF_ALLOC(4); + } + (*rbuf)[0] = ((desc->key) >> 24) & 0xFF; + (*rbuf)[1] = ((desc->key) >> 16) & 0xFF; + (*rbuf)[2] = ((desc->key) >> 8) & 0xFF; + (*rbuf)[3] = (desc->key) & 0xFF; + return 4; default: return 0; - } - ASSERT(0); - desc = NULL; - */ - return 0; + } } /********************************************************************* @@ -3099,7 +3220,7 @@ static void file_outputv(ErlDrvData e, ErlIOVec *ev) { file_descriptor* desc = (file_descriptor*)e; char command; - int p, q; + size_t p, q; int err; struct t_data *d = NULL; #ifdef USE_VM_PROBES @@ -3527,7 +3648,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { for(i = 0; i < n; i++) { Uint32 sizeH, sizeL; size_t size; - if ( !EV_GET_UINT64(ev, &d->c.pwritev.specs[i].offset, &p, &q) + if ( !EV_GET_SINT64(ev, &d->c.pwritev.specs[i].offset, &p, &q) || !EV_GET_UINT32(ev, &sizeH, &p, &q) || !EV_GET_UINT32(ev, &sizeL, &p, &q)) { /* Misalignment in buffer */ @@ -3555,7 +3676,6 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { #ifdef USE_VM_PROBES dt_i3 = d->c.pwritev.size; #endif - d->c.pwritev.free_size = 0; if (j == 0) { /* Trivial case - nothing to write */ EF_FREE(d); @@ -3579,7 +3699,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { MUTEX_UNLOCK(desc->q_mtx); /* Execute the command */ d->invoke = invoke_pwritev; - d->free = free_pwritev; + d->free = free_data; d->level = 1; cq_enq(desc, d); } @@ -3670,7 +3790,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { for (i = 1; i < 1+n; i++) { Uint32 sizeH, sizeL; size_t size; - if ( !EV_GET_UINT64(ev, &d->c.preadv.offsets[i-1], &p, &q) + if ( !EV_GET_SINT64(ev, &d->c.preadv.offsets[i-1], &p, &q) || !EV_GET_UINT32(ev, &sizeH, &p, &q) || !EV_GET_UINT32(ev, &sizeL, &p, &q)) { reply_posix_error(desc, EINVAL); @@ -3717,7 +3837,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { res_ev->iov[0].iov_base = res_ev->binv[0]->orig_bytes; /* Fill in the number of buffers in the header */ put_int32(0, res_ev->iov[0].iov_base); - put_int32(n, res_ev->iov[0].iov_base+4); + put_int32(n, (char *)(res_ev->iov[0].iov_base) + 4); /**/ res_ev->size = res_ev->iov[0].iov_len; if (n == 0) { @@ -3738,7 +3858,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { Uint32 origin; /* Origin of seek. */ if (ev->size < 1+8+4 - || !EV_GET_UINT64(ev, &offset, &p, &q) + || !EV_GET_SINT64(ev, &offset, &p, &q) || !EV_GET_UINT32(ev, &origin, &p, &q)) { /* Wrong length of buffer to contain offset and origin */ reply_posix_error(desc, EINVAL); @@ -3851,7 +3971,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { goto done; } if (ev->size < 1+1+8+4 - || !EV_GET_UINT64(ev, &hdr_offset, &p, &q) + || !EV_GET_SINT64(ev, &hdr_offset, &p, &q) || !EV_GET_UINT32(ev, &max_size, &p, &q)) { /* Buffer too short to contain * the header offset and max size spec */ @@ -4017,8 +4137,16 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { goto done; } - if (hd_len != 0 || tl_len != 0 || flags != 0) { - // We do not allow header, trailers and/or flags right now + if (hd_len != 0 || tl_len != 0) { + /* We do not allow header, trailers */ + reply_posix_error(desc, EINVAL); + goto done; + } + + + if (flags & SENDFILE_FLGS_USE_THREADS && !THRDS_AVAILABLE) { + /* We do not allow use_threads flag on a system where + no threads are available. */ reply_posix_error(desc, EINVAL); goto done; } @@ -4028,6 +4156,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->command = command; d->invoke = invoke_sendfile; d->free = free_sendfile; + d->flags = flags; d->level = 2; d->c.sendfile.out_fd = (int) out_fd; @@ -4047,7 +4176,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->c.sendfile.nbytes = nbytes; - if (USE_THRDS_FOR_SENDFILE) { + if (USE_THRDS_FOR_SENDFILE(d)) { SET_BLOCKING(d->c.sendfile.out_fd); } else { /** diff --git a/erts/emulator/drivers/common/erl_efile.h b/erts/emulator/drivers/common/erl_efile.h index 69ad02633c..5a8e3bc5db 100644 --- a/erts/emulator/drivers/common/erl_efile.h +++ b/erts/emulator/drivers/common/erl_efile.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2011. All Rights Reserved. + * Copyright Ericsson AB 1997-2013. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -34,6 +34,7 @@ #define EFILE_COMPRESSED 8 #define EFILE_MODE_EXCL 16 #define EFILE_NO_TRUNCATE 32 /* Special for reopening on VxWorks */ +#define EFILE_MODE_SYNC 64 /* * Seek modes for efile_seek(). @@ -67,7 +68,7 @@ #define FILENAMES_16BIT 1 #endif -// We use sendfilev if it exist on solaris +/* We use sendfilev if it exist on solaris */ #if !defined(HAVE_SENDFILE) && defined(HAVE_SENDFILEV) #define HAVE_SENDFILE #endif @@ -88,20 +89,6 @@ typedef struct _Efile_error { } Efile_error; /* - * This structure contains date and time. - */ - -//typedef struct _Efile_time { -// unsigned year; /* (4 digits). */ -// unsigned month; /* (1..12). */ -// unsigned day; /* (1..31). */ -// unsigned hour; /* (0..23). */ -// unsigned minute; /* (0..59). */ -// unsigned second; /* (0..59). */ -//} Efile_time; - - -/* * Describes what is returned by file:file_info/1. */ @@ -140,7 +127,7 @@ struct t_sendfile_hdtl { /* * Functions. */ - +int efile_init(void); int efile_mkdir(Efile_error* errInfo, char* name); int efile_rmdir(Efile_error* errInfo, char* name); int efile_delete_file(Efile_error* errInfo, char* name); @@ -185,3 +172,4 @@ int efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length, int efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd, off_t *offset, Uint64 *nbytes, struct t_sendfile_hdtl *hdtl); #endif /* HAVE_SENDFILE */ +int efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length); diff --git a/erts/emulator/drivers/common/gzio.c b/erts/emulator/drivers/common/gzio.c index a9303d55bc..1ef1602ec9 100644 --- a/erts/emulator/drivers/common/gzio.c +++ b/erts/emulator/drivers/common/gzio.c @@ -13,19 +13,16 @@ # include "config.h" #endif #include <stdio.h> +#include <string.h> /* ssize_t on Mac OS X */ #include <errno.h> #ifdef HAVE_UNISTD_H #include <unistd.h> #endif #include <ctype.h> #include "erl_driver.h" +#include "erl_efile.h" #include "sys.h" -#ifdef VXWORKS -/* pull in FOPEN from zutil.h instead */ -#undef F_OPEN -#endif - #ifdef __WIN32__ #ifndef HAVE_CONFLICTING_FREAD_DECLARATION #define HAVE_CONFLICTING_FREAD_DECLARATION @@ -77,15 +74,15 @@ typedef struct gz_stream { int transparent; /* 1 if input file is not a .gz file */ char mode; /* 'w' or 'r' */ int position; /* Position (for seek) */ - int (*destroy)OF((struct gz_stream*)); /* Function to destroy + int (*destroy)(struct gz_stream*); /* Function to destroy * this structure. */ } gz_stream; -local gzFile gz_open OF((const char *path, const char *mode)); -local int get_byte OF((gz_stream *s)); -local void check_header OF((gz_stream *s)); -local int destroy OF((gz_stream *s)); -local uLong getLong OF((gz_stream *s)); +local ErtsGzFile gz_open (const char *path, const char *mode); +local int get_byte (gz_stream *s); +local void check_header (gz_stream *s); +local int destroy (gz_stream *s); +local uLong getLong (gz_stream *s); #ifdef UNIX /* @@ -148,7 +145,7 @@ local uLong getLong OF((gz_stream *s)); can be checked to distinguish the two cases (if errno is zero, the zlib error is Z_MEM_ERROR). */ -local gzFile gz_open (path, mode) +local ErtsGzFile gz_open (path, mode) const char *path; const char *mode; { @@ -183,7 +180,7 @@ local gzFile gz_open (path, mode) s->path = (char*)ALLOC(FILENAME_BYTELEN(path)+FILENAME_CHARSIZE); if (s->path == NULL) { - return s->destroy(s), (gzFile)Z_NULL; + return s->destroy(s), (ErtsGzFile)Z_NULL; } FILENAME_COPY(s->path, path); /* do this early for debugging */ @@ -201,7 +198,7 @@ local gzFile gz_open (path, mode) } while (*p++ && m < fmode + sizeof(fmode) - 1); *m = '\0'; if (s->mode == '\0') - return s->destroy(s), (gzFile)Z_NULL; + return s->destroy(s), (ErtsGzFile)Z_NULL; if (s->mode == 'w') { err = deflateInit2(&(s->stream), level, @@ -211,7 +208,7 @@ local gzFile gz_open (path, mode) s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); if (err != Z_OK || s->outbuf == Z_NULL) { - return s->destroy(s), (gzFile)Z_NULL; + return s->destroy(s), (ErtsGzFile)Z_NULL; } } else { /* @@ -225,7 +222,7 @@ local gzFile gz_open (path, mode) s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); if (err != Z_OK || s->inbuf == Z_NULL) { - return s->destroy(s), (gzFile)Z_NULL; + return s->destroy(s), (ErtsGzFile)Z_NULL; } } s->stream.avail_out = Z_BUFSIZE; @@ -233,17 +230,17 @@ local gzFile gz_open (path, mode) errno = 0; #if defined(FILENAMES_16BIT) { - char wfmode[160]; - int i=0,j; - for(j=0;fmode[j] != '\0';++j) { - wfmode[i++]=fmode[j]; - wfmode[i++]='\0'; + FILE* efile_wfopen(const WCHAR* name, const WCHAR* mode); + WCHAR wfmode[80]; + int i = 0; + int j; + for(j = 0; fmode[j] != '\0'; ++j) { + wfmode[i++] = (WCHAR) fmode[j]; } - wfmode[i++] = '\0'; - wfmode[i++] = '\0'; - s->file = F_OPEN(path, wfmode); + wfmode[i++] = L'\0'; + s->file = efile_wfopen((WCHAR *)path, wfmode); if (s->file == NULL) { - return s->destroy(s), (gzFile)Z_NULL; + return s->destroy(s), (ErtsGzFile)Z_NULL; } } #elif defined(UNIX) @@ -253,18 +250,18 @@ local gzFile gz_open (path, mode) s->file = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666); } if (s->file == -1) { - return s->destroy(s), (gzFile)Z_NULL; + return s->destroy(s), (ErtsGzFile)Z_NULL; } #else - s->file = F_OPEN(path, fmode); + s->file = fopen(path, fmode); if (s->file == NULL) { - return s->destroy(s), (gzFile)Z_NULL; + return s->destroy(s), (ErtsGzFile)Z_NULL; } #endif if (s->mode == 'r') { check_header(s); /* skip the .gz header */ } - return (gzFile)s; + return (ErtsGzFile)s; } /* =========================================================================== @@ -300,7 +297,7 @@ local int gz_rewind (gz_stream *s) /* =========================================================================== Opens a gzip (.gz) file for reading or writing. */ -gzFile erts_gzopen (path, mode) +ErtsGzFile erts_gzopen (path, mode) const char *path; const char *mode; { @@ -319,7 +316,7 @@ local int get_byte(s) if (s->z_eof) return EOF; if (s->stream.avail_in == 0) { #ifdef UNIX - size_t res; + ssize_t res; errno = 0; res = ERTS_GZREAD(s->file, s->inbuf, Z_BUFSIZE); if (res == 0) { @@ -451,7 +448,7 @@ local int destroy (s) gzread returns the number of bytes actually read (0 for end of file). */ int -erts_gzread(gzFile file, voidp buf, unsigned len) +erts_gzread(ErtsGzFile file, voidp buf, unsigned len) { gz_stream *s = (gz_stream*)file; Bytef *start = buf; /* starting point for crc computation */ @@ -492,7 +489,7 @@ erts_gzread(gzFile file, voidp buf, unsigned len) } if (s->stream.avail_in == 0 && !s->z_eof) { #ifdef UNIX - size_t res; + ssize_t res; errno = 0; res = ERTS_GZREAD(s->file, s->inbuf, Z_BUFSIZE); if (res == 0) { @@ -561,7 +558,7 @@ erts_gzread(gzFile file, voidp buf, unsigned len) gzwrite returns the number of bytes actually written (0 in case of error). */ int -erts_gzwrite(gzFile file, voidp buf, unsigned len) +erts_gzwrite(ErtsGzFile file, voidp buf, unsigned len) { gz_stream *s = (gz_stream*)file; @@ -597,11 +594,20 @@ erts_gzwrite(gzFile file, voidp buf, unsigned len) */ int -erts_gzseek(gzFile file, int offset, int whence) +erts_gzseek(ErtsGzFile file, int offset, int whence) { int pos; gz_stream* s = (gz_stream *) file; + switch (whence) { + case EFILE_SEEK_SET: whence = SEEK_SET; break; + case EFILE_SEEK_CUR: whence = SEEK_CUR; break; + case EFILE_SEEK_END: whence = SEEK_END; break; + default: + errno = EINVAL; + return -1; + } + if (s == NULL) { errno = EINVAL; return -1; @@ -659,7 +665,7 @@ erts_gzseek(gzFile file, int offset, int whence) degrade compression. */ int -erts_gzflush(gzFile file, int flush) +erts_gzflush(ErtsGzFile file, int flush) { uInt len; int done = 0; @@ -718,7 +724,7 @@ local uLong getLong (s) and deallocates all the (de)compression state. */ int -erts_gzclose(gzFile file) +erts_gzclose(ErtsGzFile file) { int err; gz_stream *s = (gz_stream*)file; @@ -727,9 +733,9 @@ erts_gzclose(gzFile file) if (s->mode == 'w') { err = erts_gzflush (file, Z_FINISH); - if (err != Z_OK) return s->destroy(file); + if (err != Z_OK) return s->destroy(s); } - return s->destroy(file); + return s->destroy(s); } diff --git a/erts/emulator/drivers/common/gzio.h b/erts/emulator/drivers/common/gzio.h index 3f1e546140..ea50d922ec 100644 --- a/erts/emulator/drivers/common/gzio.h +++ b/erts/emulator/drivers/common/gzio.h @@ -17,11 +17,15 @@ * %CopyrightEnd% */ -gzFile erts_gzopen (const char *path, const char *mode); -int erts_gzread(gzFile file, voidp buf, unsigned len); -int erts_gzwrite(gzFile file, voidp buf, unsigned len); -int erts_gzseek(gzFile, int, int); -int erts_gzflush(gzFile file, int flush); -int erts_gzclose(gzFile file); +#include "zlib.h" + +typedef struct erts_gzFile* ErtsGzFile; + +ErtsGzFile erts_gzopen (const char *path, const char *mode); +int erts_gzread(ErtsGzFile file, voidp buf, unsigned len); +int erts_gzwrite(ErtsGzFile file, voidp buf, unsigned len); +int erts_gzseek(ErtsGzFile, int, int); +int erts_gzflush(ErtsGzFile file, int flush); +int erts_gzclose(ErtsGzFile file); ErlDrvBinary* erts_gzinflate_buffer(char*, uLong); ErlDrvBinary* erts_gzdeflate_buffer(char*, uLong); diff --git a/erts/emulator/drivers/common/gzio_zutil.h b/erts/emulator/drivers/common/gzio_zutil.h index 00eccc80fc..854205cc2c 100644 --- a/erts/emulator/drivers/common/gzio_zutil.h +++ b/erts/emulator/drivers/common/gzio_zutil.h @@ -23,12 +23,6 @@ * that may change or not exist at all. */ -#ifndef HAVE_LIBZ -/* Use our "real" copy of zutil.h if we don't use shared zlib */ -#include "zutil.h" - -#else /* HAVE_LIBZ: Shared zlib is used */ - #define local static #define DEF_MEM_LEVEL 8 #define zmemcpy sys_memcpy @@ -77,6 +71,3 @@ # define OS_CODE 0x03 /* assume Unix */ #endif - -#endif /* HAVE_LIBZ */ - diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 8f4fff0f40..09d90f4984 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2012. All Rights Reserved. + * Copyright Ericsson AB 1997-2013. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -86,6 +86,17 @@ #endif typedef unsigned long long llu_t; +#ifndef INT16_MIN +#define INT16_MIN (-32768) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif + +#ifdef __OSE__ +#include "inet.h" +#endif + #ifdef __WIN32__ #define STRNCASECMP strncasecmp @@ -189,6 +200,7 @@ typedef unsigned long long llu_t; #define HAVE_MULTICAST_SUPPORT +#define HAVE_UDP #define ERRNO_BLOCK WSAEWOULDBLOCK @@ -282,29 +294,145 @@ static BOOL (WINAPI *fpSetHandleInformation)(HANDLE,DWORD,DWORD); static unsigned long zero_value = 0; static unsigned long one_value = 1; -#else +#elif defined (__OSE__) + +/* + * Some notes about how inet (currently only tcp) works on OSE. + * The driver uses OSE signals to communicate with the one_inet + * process. Because of the difference in how signals and file descriptors + * work the whole select/deselect mechanic is very different. + * In ose when a sock_select is done a function is called. That function + * notes the changes that the driver want to do, but does not act on it. + * later when the function returns the new desired state is compared + * to the previous state and the apprioriate actions are taken. The action + * is usually to either request more data from the stack or stop requesting + * data. + * + * One thing to note is that the driver never does select/deselect. It always + * listens for the signals. Flow of data is regulated by sending or not sending + * signals to the ose inet process. + * + * The interesting functions to look at are: + * * inet_driver_select : called when sock_select is called + * * tcp_inet_ose_dispatch_signal : checks state changes and sends new signals + * * tcp_inet_drv_output_ose : ready output callback, reads signals and calls + * dispatch_signal + * * tcp_inet_drv_input_ose : ready input callback. + */ + +#include "efs.h" +#include "sys/socket.h" +#include "sys/uio.h" +#include "sfk/sys/sfk_uio.h" +#include "netinet/in.h" +#include "netinet/tcp.h" +#include "netdb.h" +#include "ose_spi/socket.sig" + + +static ssize_t writev_fallback(int fd, const struct iovec *iov, int iovcnt, int max_sz); + +#define INVALID_SOCKET -1 +#define INVALID_EVENT -1 +#define SOCKET_ERROR -1 + +#define SOCKET int +#define HANDLE int +#define FD_READ ERL_DRV_READ +#define FD_WRITE ERL_DRV_WRITE +#define FD_CLOSE 0 +#define FD_CONNECT (1<<4) +#define FD_ACCEPT (1<<5) +#define SOCK_FD_ERROR (1<<6) + +#define sock_connect(s, addr, len) connect((s), (addr), (len)) +#define sock_listen(s, b) listen((s), (b)) +#define sock_bind(s, addr, len) bind((s), (addr), (len)) +#define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l)) +#define sock_setopt(s,t,n,v,l) setsockopt((s),(t),(n),(v),(l)) +#define sock_name(s, addr, len) getsockname((s), (addr), (len)) +#define sock_peer(s, addr, len) getpeername((s), (addr), (len)) +#define sock_ntohs(x) ntohs((x)) +#define sock_ntohl(x) ntohl((x)) +#define sock_htons(x) htons((x)) +#define sock_htonl(x) htonl((x)) + +#define sock_accept(s, addr, len) accept((s), (addr), (len)) +#define sock_send(s,buf,len,flag) inet_send((s),(buf),(len),(flag)) +#define sock_sendto(s,buf,blen,flag,addr,alen) \ + sendto((s),(buf),(blen),(flag),(addr),(alen)) +#define sock_sendv(s, vec, size, np, flag) \ + (*(np) = writev_fallback((s), (struct iovec*)(vec), (size), (*(np)))) +#define sock_sendmsg(s,msghdr,flag) sendmsg((s),(msghdr),(flag)) + +#define sock_open(af, type, proto) socket((af), (type), (proto)) +#define sock_close(s) close((s)) +#define sock_dup(s) dup((s)) +#define sock_shutdown(s, how) shutdown((s), (how)) + +#define sock_hostname(buf, len) gethostname((buf), (len)) +#define sock_getservbyname(name,proto) getservbyname((name), (proto)) +#define sock_getservbyport(port,proto) getservbyport((port), (proto)) + +#define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag)) +#define sock_recvfrom(s,buf,blen,flag,addr,alen) \ + recvfrom((s),(buf),(blen),(flag),(addr),(alen)) +#define sock_recvmsg(s,msghdr,flag) recvmsg((s),(msghdr),(flag)) + +#define sock_errno() errno +#define sock_create_event(d) ((d)->s) /* return file descriptor */ +#define sock_close_event(e) /* do nothing */ + +#ifndef WANT_NONBLOCKING +#define WANT_NONBLOCKING +#endif +#include "sys.h" + +typedef unsigned long u_long; +#define IN_CLASSA(a) ((((in_addr_t)(a)) & 0x80000000) == 0) +#define IN_CLASSA_NET 0xff000000 +#define IN_CLASSA_NSHIFT 24 +#define IN_CLASSA_HOST (0xffffffff & ~IN_CLASSA_NET) +#define IN_CLASSA_MAX 128 + +#define IN_CLASSB(a) ((((in_addr_t)(a)) & 0xc0000000) == 0x80000000) +#define IN_CLASSB_NET 0xffff0000 +#define IN_CLASSB_NSHIFT 16 +#define IN_CLASSB_HOST (0xffffffff & ~IN_CLASSB_NET) +#define IN_CLASSB_MAX 65536 + +#define IN_CLASSC(a) ((((in_addr_t)(a)) & 0xe0000000) == 0xc0000000) +#define IN_CLASSC_NET 0xffffff00 +#define IN_CLASSC_NSHIFT 8 +#define IN_CLASSC_HOST (0xffffffff & ~IN_CLASSC_NET) + +#define IN_CLASSD(a) ((((in_addr_t)(a)) & 0xf0000000) == 0xe0000000) +#define IN_MULTICAST(a) IN_CLASSD(a) + +#define IN_EXPERIMENTAL(a) ((((in_addr_t)(a)) & 0xe0000000) == 0xe0000000) +#define IN_BADCLASS(a) ((((in_addr_t)(a)) & 0xf0000000) == 0xf0000000) + +#define sock_select(d, flags, onoff) do { \ + ASSERT(!(d)->is_ignored); \ + (d)->event_mask = (onoff) ? \ + ((d)->event_mask | (flags)) : \ + ((d)->event_mask & ~(flags)); \ + DEBUGF(("(%s / %d) sock_select(%ld): flags=%02X, onoff=%d, event_mask=%02lX, s=%d\r\n", \ + __FILE__, __LINE__, (long) (d)->port, (flags), (onoff), (unsigned long) (d)->event_mask, (d)->s)); \ + inet_driver_select((d), (flags), (onoff)); \ + } while(0) + +#else /* !__OSE__ && !__WIN32__ */ -#ifdef VXWORKS -#include <sockLib.h> -#include <sys/times.h> -#include <iosLib.h> -#include <taskLib.h> -#include <selectLib.h> -#include <ioLib.h> -#else #include <sys/time.h> #ifdef NETDB_H_NEEDS_IN_H #include <netinet/in.h> #endif #include <netdb.h> -#endif #include <sys/socket.h> #include <netinet/in.h> -#ifdef VXWORKS -#include <rpc/rpctypes.h> -#endif #ifdef DEF_INADDR_LOOPBACK_IN_RPC_TYPES_H #include <rpc/types.h> #endif @@ -312,12 +440,10 @@ static unsigned long one_value = 1; #include <netinet/tcp.h> #include <arpa/inet.h> -#if (!defined(VXWORKS)) #include <sys/param.h> #ifdef HAVE_ARPA_NAMESER_H #include <arpa/nameser.h> #endif -#endif #ifdef HAVE_SYS_SOCKIO_H #include <sys/sockio.h> @@ -329,9 +455,19 @@ static unsigned long one_value = 1; #include <net/if.h> +#ifdef HAVE_SCHED_H +#include <sched.h> +#endif + +#ifdef HAVE_SETNS_H +#include <setns.h> +#endif + +#define HAVE_UDP + /* SCTP support -- currently for UNIX platforms only: */ #undef HAVE_SCTP -#if (!defined(VXWORKS) && !defined(__WIN32__) && defined(HAVE_SCTP_H)) +#if defined(HAVE_SCTP_H) #include <netinet/sctp.h> @@ -423,16 +559,47 @@ static unsigned long one_value = 1; # define sctp_adaptation_layer_event sctp_adaption_layer_event #endif -#ifdef __GNUC__ +#if defined(__GNUC__) && defined(HAVE_SCTP_BINDX) static typeof(sctp_bindx) *p_sctp_bindx = NULL; +#else +static int (*p_sctp_bindx) + (int sd, struct sockaddr *addrs, int addrcnt, int flags) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_PEELOFF) static typeof(sctp_peeloff) *p_sctp_peeloff = NULL; #else -static int (*p_sctp_bindx)(int sd, struct sockaddr *addrs, - int addrcnt, int flags) = NULL; -static int (*p_sctp_peeloff)(int sd, sctp_assoc_t assoc_id) = NULL; +static int (*p_sctp_peeloff) + (int sd, sctp_assoc_t assoc_id) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_GETLADDRS) +static typeof(sctp_getladdrs) *p_sctp_getladdrs = NULL; +#else +static int (*p_sctp_getladdrs) + (int sd, sctp_assoc_t assoc_id, struct sockaddr **ss) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_FREELADDRS) +static typeof(sctp_freeladdrs) *p_sctp_freeladdrs = NULL; +#else +static void (*p_sctp_freeladdrs)(struct sockaddr *addrs) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_GETPADDRS) +static typeof(sctp_getpaddrs) *p_sctp_getpaddrs = NULL; +#else +static int (*p_sctp_getpaddrs) + (int sd, sctp_assoc_t assoc_id, struct sockaddr **ss) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_FREEPADDRS) +static typeof(sctp_freepaddrs) *p_sctp_freepaddrs = NULL; +#else +static void (*p_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #endif -#endif /* SCTP supported */ +#endif /* #if defined(HAVE_SCTP_H) */ #ifndef WANT_NONBLOCKING #define WANT_NONBLOCKING @@ -478,15 +645,8 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define sock_connect(s, addr, len) connect((s), (addr), (len)) #define sock_listen(s, b) listen((s), (b)) #define sock_bind(s, addr, len) bind((s), (addr), (len)) -#ifdef VXWORKS -#define sock_getopt(s,t,n,v,l) wrap_sockopt(&getsockopt,\ - s,t,n,v,(unsigned int)(l)) -#define sock_setopt(s,t,n,v,l) wrap_sockopt(&setsockopt,\ - s,t,n,v,(unsigned int)(l)) -#else #define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l)) #define sock_setopt(s,t,n,v,l) setsockopt((s),(t),(n),(v),(l)) -#endif #define sock_name(s, addr, len) getsockname((s), (addr), (len)) #define sock_peer(s, addr, len) getpeername((s), (addr), (len)) #define sock_ntohs(x) ntohs((x)) @@ -527,13 +687,19 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) (d)->event_mask = (onoff) ? \ ((d)->event_mask | (flags)) : \ ((d)->event_mask & ~(flags)); \ - DEBUGF(("sock_select(%ld): flags=%02X, onoff=%d, event_mask=%02lX\r\n", \ - (long) (d)->port, (flags), (onoff), (unsigned long) (d)->event_mask)); \ + DEBUGF(("(%s / %d) sock_select(%ld): flags=%02X, onoff=%d, event_mask=%02lX\r\n", \ + __FILE__, __LINE__, (long) (d)->port, (flags), (onoff), (unsigned long) (d)->event_mask)); \ inet_driver_select((d)->port, (ErlDrvEvent)(long)(d)->event, (flags), (onoff)); \ } while(0) -#endif /* __WIN32__ */ +#endif /* !__WIN32__ && !__OSE__ */ + +#ifdef HAVE_SOCKLEN_T +# define SOCKLEN_T socklen_t +#else +# define SOCKLEN_T int +#endif #include "packet_parser.h" @@ -588,6 +754,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_PASSIVE 0 /* false */ #define INET_ACTIVE 1 /* true */ #define INET_ONCE 2 /* true; active once then passive */ +#define INET_MULTI 3 /* true; active N then passive */ /* INET_REQ_GETSTATUS enumeration */ #define INET_F_OPEN 0x0001 @@ -600,7 +767,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_F_BUSY 0x0080 #define INET_F_MULTI_CLIENT 0x0100 /* Multiple clients for one descriptor, i.e. multi-accept */ -/* One numberspace for *_REC_* so if an e.g UDP request is issued +/* One numberspace for *_REQ_* so if an e.g UDP request is issued ** for a TCP socket, the driver can protest. */ #define INET_REQ_OPEN 1 @@ -631,6 +798,8 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_REQ_ACCEPT 26 #define INET_REQ_LISTEN 27 #define INET_REQ_IGNOREFD 28 +#define INET_REQ_GETLADDRS 29 +#define INET_REQ_GETPADDRS 30 /* TCP requests */ /* #define TCP_REQ_ACCEPT 40 MOVED */ @@ -675,6 +844,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define UDP_OPT_MULTICAST_LOOP 13 /* set/get IP multicast loopback */ #define UDP_OPT_ADD_MEMBERSHIP 14 /* add an IP group membership */ #define UDP_OPT_DROP_MEMBERSHIP 15 /* drop an IP group membership */ +#define INET_OPT_IPV6_V6ONLY 16 /* IPv6 only socket, no mapped v4 addrs */ /* LOPT is local options */ #define INET_LOPT_BUFFER 20 /* min buffer size hint */ #define INET_LOPT_HEADER 21 /* list header size */ @@ -685,13 +855,16 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_LOPT_EXITONCLOSE 26 /* exit port on active close or not ! */ #define INET_LOPT_TCP_HIWTRMRK 27 /* set local high watermark */ #define INET_LOPT_TCP_LOWTRMRK 28 /* set local low watermark */ -#define INET_LOPT_BIT8 29 /* set 8 bit detection */ + /* 29 unused */ #define INET_LOPT_TCP_SEND_TIMEOUT 30 /* set send timeout */ #define INET_LOPT_TCP_DELAY_SEND 31 /* Delay sends until next poll */ #define INET_LOPT_PACKET_SIZE 32 /* Max packet size */ #define INET_LOPT_UDP_READ_PACKETS 33 /* Number of packets to read */ #define INET_OPT_RAW 34 /* Raw socket options */ #define INET_LOPT_TCP_SEND_TIMEOUT_CLOSE 35 /* auto-close on send timeout or not */ +#define INET_LOPT_MSGQ_HIWTRMRK 36 /* set local msgq high watermark */ +#define INET_LOPT_MSGQ_LOWTRMRK 37 /* set local msgq low watermark */ +#define INET_LOPT_NETNS 38 /* Network namespace pathname */ /* SCTP options: a separate range, from 100: */ #define SCTP_OPT_RTOINFO 100 #define SCTP_OPT_ASSOCINFO 101 @@ -720,12 +893,6 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_IFOPT_FLAGS 6 #define INET_IFOPT_HWADDR 7 -/* INET_LOPT_BIT8 options */ -#define INET_BIT8_CLEAR 0 -#define INET_BIT8_SET 1 -#define INET_BIT8_ON 2 -#define INET_BIT8_OFF 3 - /* INET_REQ_GETSTAT enumeration */ #define INET_STAT_RECV_CNT 1 #define INET_STAT_RECV_MAX 2 @@ -808,6 +975,8 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_HIGH_WATERMARK (1024*8) /* 8k pending high => busy */ #define INET_LOW_WATERMARK (1024*4) /* 4k pending => allow more */ +#define INET_HIGH_MSGQ_WATERMARK (1024*8) /* 8k pending high => busy */ +#define INET_LOW_MSGQ_WATERMARK (1024*4) /* 4k pending => allow more */ #define INET_INFINITY 0xffffffff /* infinity value */ @@ -820,9 +989,10 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_IFNAMSIZ 16 /* INET Ignore states */ -#define INET_IGNORE_NONE 0 -#define INET_IGNORE_READ 1 -#define INET_IGNORE_WRITE 1 << 1 +#define INET_IGNORE_NONE 0 +#define INET_IGNORE_READ (1 << 0) +#define INET_IGNORE_WRITE (1 << 1) +#define INET_IGNORE_PASSIVE (1 << 2) /* Max length of Erlang Term Buffer (for outputting structured terms): */ #ifdef HAVE_SCTP @@ -899,7 +1069,7 @@ typedef struct subs_list_ { #define NO_PROCESS 0 #define NO_SUBSCRIBERS(SLP) ((SLP)->subscriber == NO_PROCESS) -static void send_to_subscribers(ErlDrvPort, subs_list *, int, +static void send_to_subscribers(ErlDrvTermData, subs_list *, int, ErlDrvTermData [], int); static void free_subscribers(subs_list*); static int save_subscriber(subs_list *, ErlDrvTermData); @@ -921,7 +1091,6 @@ typedef struct { int mode; /* BINARY | LIST (affect how to interpret hsz) */ int exitf; /* exit port on close or not */ - int bit8f; /* check if data has bit number 7 set */ int deliver; /* Delivery mode, TERM or PORT */ ErlDrvTermData caller; /* recipient of sync reply */ @@ -933,6 +1102,7 @@ typedef struct { inet_async_op op_queue[INET_MAX_ASYNC]; /* call queue */ int active; /* 0 = passive, 1 = active, 2 = active once */ + Sint16 active_count; /* counter for {active,N} */ int stype; /* socket type: SOCK_STREAM/SOCK_DGRAM/SOCK_SEQPACKET */ int sprotocol; /* socket protocol: @@ -940,8 +1110,6 @@ typedef struct { int sfamily; /* address family */ enum PacketParseType htype; /* header type (TCP only?) */ unsigned int psize; /* max packet size (TCP only?) */ - int bit8; /* set if bit8f==true and data some data - seen had the 7th bit set */ inet_address remote; /* remote address for connected sockets */ inet_address peer_addr; /* fake peer address */ inet_address name_addr; /* fake local address */ @@ -952,12 +1120,20 @@ typedef struct { int bufsz; /* minimum buffer constraint */ unsigned int hsz; /* the list header size, -1 is large !!! */ /* statistics */ - unsigned long recv_oct[2]; /* number of received octets >= 64 bits */ +#ifdef ARCH_64 + Uint64 recv_oct; /* number of received octets, 64 bits */ +#else + Uint32 recv_oct[2]; /* number of received octets, 64 bits */ +#endif unsigned long recv_cnt; /* number of packets received */ unsigned long recv_max; /* maximum packet size received */ double recv_avg; /* average packet size received */ double recv_dvi; /* avarage deviation from avg_size */ - unsigned long send_oct[2]; /* number of octets sent >= 64 bits */ +#ifdef ARCH_64 + Uint64 send_oct; /* number of octets sent, 64 bits */ +#else + Uint32 send_oct[2]; /* number of octets sent, 64 bits */ +#endif unsigned long send_cnt; /* number of packets sent */ unsigned long send_max; /* maximum packet send */ double send_avg; /* average packet size sent */ @@ -966,6 +1142,17 @@ typedef struct { int is_ignored; /* if a fd is ignored by the inet_drv. This flag should be set to true when the fd is used outside of inet_drv. */ +#ifdef HAVE_SETNS + char *netns; /* Socket network namespace name + as full file path */ +#endif +#ifdef __OSE__ + int select_state; /* state to keep track of whether we + should trigger another read/write + request at end of ready_input/output */ + ErlDrvEvent events[6]; +#endif + } inet_descriptor; @@ -981,8 +1168,10 @@ static void tcp_inet_stop(ErlDrvData); static void tcp_inet_command(ErlDrvData, char*, ErlDrvSizeT); static void tcp_inet_commandv(ErlDrvData, ErlIOVec*); static void tcp_inet_flush(ErlDrvData drv_data); +#ifndef __OSE__ static void tcp_inet_drv_input(ErlDrvData, ErlDrvEvent); static void tcp_inet_drv_output(ErlDrvData data, ErlDrvEvent event); +#endif static ErlDrvData tcp_inet_start(ErlDrvPort, char* command); static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData, unsigned int, char*, ErlDrvSizeT, char**, ErlDrvSizeT); @@ -994,7 +1183,72 @@ static void tcp_inet_event(ErlDrvData, ErlDrvEvent); static void find_dynamic_functions(void); #endif -static struct erl_drv_entry tcp_inet_driver_entry = +#ifdef __OSE__ +/* The structure of the signal used for requesting asynchronous + * notification from the stack. Under normal circumstances the network stack + * shouldn't overwrite the value set in the fd field by the sender + * of the request */ +struct OseAsyncSig { + struct FmEvent event; + int fd; +}; + +union SIGNAL { + SIGSELECT signo; + struct OseAsyncSig async; +}; + +static ErlDrvSSizeT tcp_inet_ctl_ose(ErlDrvData e, unsigned int cmd, + char* buf, ErlDrvSizeT len, + char** rbuf, ErlDrvSizeT rsize); +static void tcp_inet_commandv_ose(ErlDrvData e, ErlIOVec* ev); +static void tcp_inet_drv_output_ose(ErlDrvData data, ErlDrvEvent event); +static void tcp_inet_drv_input_ose(ErlDrvData data, ErlDrvEvent event); +static ErlDrvOseEventId inet_resolve_signal(union SIGNAL *sig); + +#ifdef INET_DRV_DEBUG + +static char *read_req = "SO_EVENT_READ_REQUEST"; +static char *read_rep = "SO_EVENT_READ_REPLY"; +static char *write_req = "SO_EVENT_WRITE_REQUEST"; +static char *write_rep = "SO_EVENT_WRITE_REPLY"; +static char *eof_req = "SO_EVENT_EOF_REQUEST"; +static char *eof_rep = "SO_EVENT_EOF_REPLY"; +static char *accept_req = "SO_EVENT_ACCEPT_REQUEST"; +static char *accept_rep = "SO_EVENT_ACCEPT_REPLY"; +static char *connect_req = "SO_EVENT_CONNECT_REQUEST"; +static char *connect_rep = "SO_EVENT_CONNECT_REPLY"; +static char *error_req = "SO_EVENT_ERROR_REQUEST"; +static char *error_rep = "SO_EVENT_ERROR_REPLY"; +static char signo_tmp[32]; + +static char *signo_to_string(SIGSELECT signo) { + switch (signo) { + case SO_EVENT_READ_REQUEST: { return read_req; } + case SO_EVENT_READ_REPLY: { return read_rep; } + case SO_EVENT_WRITE_REQUEST: { return write_req; } + case SO_EVENT_WRITE_REPLY: { return write_rep; } + case SO_EVENT_EOF_REQUEST: { return eof_req; } + case SO_EVENT_EOF_REPLY: { return eof_rep; } + case SO_EVENT_ACCEPT_REQUEST: { return accept_req; } + case SO_EVENT_ACCEPT_REPLY: { return accept_rep; } + case SO_EVENT_CONNECT_REQUEST: { return connect_req; } + case SO_EVENT_CONNECT_REPLY: { return connect_rep; } + case SO_EVENT_ERROR_REQUEST: { return error_req; } + case SO_EVENT_ERROR_REPLY: { return error_rep; } + } + + snprintf(signo_tmp,32,"0x%x",signo); + + return signo_tmp; +} + +#endif + +#endif /* __OSE__ */ + + +static struct erl_drv_entry tcp_inet_driver_entry = { tcp_inet_init, /* inet_init will add this driver !! */ tcp_inet_start, @@ -1003,6 +1257,9 @@ static struct erl_drv_entry tcp_inet_driver_entry = #ifdef __WIN32__ tcp_inet_event, NULL, +#elif defined(__OSE__) + tcp_inet_drv_input_ose, /*ready_input*/ + tcp_inet_drv_output_ose, /*ready_output*/ #else tcp_inet_drv_input, tcp_inet_drv_output, @@ -1010,9 +1267,17 @@ static struct erl_drv_entry tcp_inet_driver_entry = "tcp_inet", NULL, NULL, +#ifdef __OSE__ + tcp_inet_ctl_ose, +#else tcp_inet_ctl, +#endif tcp_inet_timeout, +#ifdef __OSE__ + tcp_inet_commandv_ose, +#else tcp_inet_commandv, +#endif NULL, tcp_inet_flush, NULL, @@ -1028,6 +1293,7 @@ static struct erl_drv_entry tcp_inet_driver_entry = +#ifdef HAVE_UDP static int packet_inet_init(void); static void packet_inet_stop(ErlDrvData); static void packet_inet_command(ErlDrvData, char*, ErlDrvSizeT); @@ -1077,6 +1343,7 @@ static struct erl_drv_entry udp_inet_driver_entry = NULL, inet_stop_select }; +#endif #ifdef HAVE_SCTP static struct erl_drv_entry sctp_inet_driver_entry = @@ -1140,6 +1407,7 @@ static int tcp_deliver(tcp_descriptor* desc, int len); static int tcp_inet_output(tcp_descriptor* desc, HANDLE event); static int tcp_inet_input(tcp_descriptor* desc, HANDLE event); +#ifdef HAVE_UDP typedef struct { inet_descriptor inet; /* common data structure (DON'T MOVE) */ int read_packets; /* Number of packets to read per invocation */ @@ -1151,29 +1419,54 @@ typedef struct { static int packet_inet_input(udp_descriptor* udesc, HANDLE event); static int packet_inet_output(udp_descriptor* udesc, HANDLE event); +#endif /* convert descriptor poiner to inet_descriptor pointer */ #define INETP(d) (&(d)->inet) +#ifdef __OSE__ +static void inet_driver_select(inet_descriptor* desc, + int flags, int onoff); +static void tcp_inet_ose_dispatch_signals(tcp_descriptor *desc, + int prev_select_state, + union SIGNAL *sig); +#endif + static int async_ref = 0; /* async reference id generator */ #define NEW_ASYNC_ID() ((async_ref++) & 0xffff) +/* check for transition from active to passive */ +#define INET_CHECK_ACTIVE_TO_PASSIVE(inet) \ + do { \ + if ((inet)->active == INET_ONCE) \ + (inet)->active = INET_PASSIVE; \ + else if ((inet)->active == INET_MULTI && --((inet)->active_count) == 0) { \ + (inet)->active = INET_PASSIVE; \ + packet_passive_message(inet); \ + } \ + } while (0) static ErlDrvTermData am_ok; static ErlDrvTermData am_tcp; -static ErlDrvTermData am_udp; static ErlDrvTermData am_error; +static ErlDrvTermData am_einval; static ErlDrvTermData am_inet_async; static ErlDrvTermData am_inet_reply; static ErlDrvTermData am_timeout; static ErlDrvTermData am_closed; +static ErlDrvTermData am_tcp_passive; static ErlDrvTermData am_tcp_closed; static ErlDrvTermData am_tcp_error; -static ErlDrvTermData am_udp_error; static ErlDrvTermData am_empty_out_q; static ErlDrvTermData am_ssl_tls; +#ifdef HAVE_UDP +static ErlDrvTermData am_udp; +static ErlDrvTermData am_udp_passive; +static ErlDrvTermData am_udp_error; +#endif #ifdef HAVE_SCTP static ErlDrvTermData am_sctp; +static ErlDrvTermData am_sctp_passive; static ErlDrvTermData am_sctp_error; static ErlDrvTermData am_true; static ErlDrvTermData am_false; @@ -1183,6 +1476,7 @@ static ErlDrvTermData am_list; static ErlDrvTermData am_binary; static ErlDrvTermData am_active; static ErlDrvTermData am_once; +static ErlDrvTermData am_multi; static ErlDrvTermData am_buffer; static ErlDrvTermData am_linger; static ErlDrvTermData am_recbuf; @@ -1191,6 +1485,8 @@ static ErlDrvTermData am_reuseaddr; static ErlDrvTermData am_dontroute; static ErlDrvTermData am_priority; static ErlDrvTermData am_tos; +static ErlDrvTermData am_ipv6_v6only; +static ErlDrvTermData am_netns; #endif /* speical errors for bad ports and sequences */ @@ -1375,6 +1671,7 @@ static void *realloc_wrapper(void *current, ErlDrvSizeT size){ # define SCTP_ANC_BUFF_SIZE INET_DEF_BUFFER/2 /* XXX: not very good... */ #endif +#ifdef HAVE_UDP static int load_ip_port(ErlDrvTermData* spec, int i, char* buf) { spec[i++] = ERL_DRV_INT; @@ -1409,12 +1706,12 @@ static int load_ip_address(ErlDrvTermData* spec, int i, int family, char* buf) } return i; } +#endif #ifdef HAVE_SCTP /* For SCTP, we often need to return {IP, Port} tuples: */ -static int inet_get_address - (int family, char* dst, inet_address* src, unsigned int* len); +static int inet_get_address(char* dst, inet_address* src, unsigned int* len); #define LOAD_IP_AND_PORT_CNT \ (8*LOAD_INT_CNT + LOAD_TUPLE_CNT + LOAD_INT_CNT + LOAD_TUPLE_CNT) @@ -1429,10 +1726,9 @@ static int load_ip_and_port unsigned int len = sizeof(struct sockaddr_storage); unsigned int alen = len; char abuf [len]; - int res = - inet_get_address(desc->sfamily, abuf, (inet_address*) addr, &alen); - ASSERT(res==0); - res = 0; + int res = inet_get_address(abuf, (inet_address*) addr, &alen); + ASSERT(res==0); (void)res; + /* Now "abuf" contains: Family(1b), Port(2b), IP(4|16b) */ /* NB: the following functions are safe to use, as they create tuples @@ -1577,10 +1873,12 @@ static void release_buffer(ErlDrvBinary* buf) } } +#ifdef HAVE_UDP static ErlDrvBinary* realloc_buffer(ErlDrvBinary* buf, ErlDrvSizeT newsz) { return driver_realloc_binary(buf, newsz); } +#endif /* use a TRICK, access the refc field to see if any one else has * a ref to this buffer then call driver_free_binary else @@ -1895,8 +2193,7 @@ static int deq_async(inet_descriptor* desc, int* ap, ErlDrvTermData* cp, int* rp ** {inet_async, Port, Ref, ok} */ static int -send_async_ok(ErlDrvPort port, ErlDrvTermData Port, int Ref, - ErlDrvTermData recipient) +send_async_ok(ErlDrvTermData Port, int Ref,ErlDrvTermData recipient) { ErlDrvTermData spec[2*LOAD_ATOM_CNT + LOAD_PORT_CNT + LOAD_INT_CNT + LOAD_TUPLE_CNT]; @@ -1910,14 +2207,14 @@ send_async_ok(ErlDrvPort port, ErlDrvTermData Port, int Ref, ASSERT(i == sizeof(spec)/sizeof(*spec)); - return driver_send_term(port, recipient, spec, i); + return erl_drv_send_term(Port, recipient, spec, i); } /* send message: ** {inet_async, Port, Ref, {ok,Port2}} */ static int -send_async_ok_port(ErlDrvPort port, ErlDrvTermData Port, int Ref, +send_async_ok_port(ErlDrvTermData Port, int Ref, ErlDrvTermData recipient, ErlDrvTermData Port2) { ErlDrvTermData spec[2*LOAD_ATOM_CNT + 2*LOAD_PORT_CNT + @@ -1936,14 +2233,14 @@ send_async_ok_port(ErlDrvPort port, ErlDrvTermData Port, int Ref, ASSERT(i == sizeof(spec)/sizeof(*spec)); - return driver_send_term(port, recipient, spec, i); + return erl_drv_send_term(Port, recipient, spec, i); } /* send message: ** {inet_async, Port, Ref, {error,Reason}} */ static int -send_async_error(ErlDrvPort port, ErlDrvTermData Port, int Ref, +send_async_error(ErlDrvTermData Port, int Ref, ErlDrvTermData recipient, ErlDrvTermData Reason) { ErlDrvTermData spec[3*LOAD_ATOM_CNT + LOAD_PORT_CNT + @@ -1961,7 +2258,7 @@ send_async_error(ErlDrvPort port, ErlDrvTermData Port, int Ref, i = LOAD_TUPLE(spec, i, 4); ASSERT(i == sizeof(spec)/sizeof(*spec)); DEBUGF(("send_async_error %ld %ld\r\n", recipient, Reason)); - return driver_send_term(port, recipient, spec, i); + return erl_drv_send_term(Port, recipient, spec, i); } @@ -1973,7 +2270,7 @@ static int async_ok(inet_descriptor* desc) if (deq_async(desc, &aid, &caller, &req) < 0) return -1; - return send_async_ok(desc->port, desc->dport, aid, caller); + return send_async_ok(desc->dport, aid, caller); } static int async_ok_port(inet_descriptor* desc, ErlDrvTermData Port2) @@ -1984,7 +2281,7 @@ static int async_ok_port(inet_descriptor* desc, ErlDrvTermData Port2) if (deq_async(desc, &aid, &caller, &req) < 0) return -1; - return send_async_ok_port(desc->port, desc->dport, aid, caller, Port2); + return send_async_ok_port(desc->dport, aid, caller, Port2); } static int async_error_am(inet_descriptor* desc, ErlDrvTermData reason) @@ -1995,8 +2292,7 @@ static int async_error_am(inet_descriptor* desc, ErlDrvTermData reason) if (deq_async(desc, &aid, &caller, &req) < 0) return -1; - return send_async_error(desc->port, desc->dport, aid, caller, - reason); + return send_async_error(desc->dport, aid, caller, reason); } /* dequeue all operations */ @@ -2007,8 +2303,7 @@ static int async_error_am_all(inet_descriptor* desc, ErlDrvTermData reason) ErlDrvTermData caller; while (deq_async(desc, &aid, &caller, &req) == 0) { - send_async_error(desc->port, desc->dport, aid, caller, - reason); + send_async_error(desc->dport, aid, caller, reason); } return 0; } @@ -2036,7 +2331,7 @@ static int inet_reply_ok(inet_descriptor* desc) ASSERT(i == sizeof(spec)/sizeof(*spec)); desc->caller = 0; - return driver_send_term(desc->port, caller, spec, i); + return erl_drv_send_term(desc->dport, caller, spec, i); } #ifdef HAVE_SCTP @@ -2055,7 +2350,7 @@ static int inet_reply_ok_port(inet_descriptor* desc, ErlDrvTermData dport) ASSERT(i == sizeof(spec)/sizeof(*spec)); desc->caller = 0; - return driver_send_term(desc->port, caller, spec, i); + return erl_drv_send_term(desc->dport, caller, spec, i); } #endif @@ -2078,7 +2373,7 @@ static int inet_reply_error_am(inet_descriptor* desc, ErlDrvTermData reason) desc->caller = 0; DEBUGF(("inet_reply_error_am %ld %ld\r\n", caller, reason)); - return driver_send_term(desc->port, caller, spec, i); + return erl_drv_send_term(desc->dport, caller, spec, i); } /* send: @@ -2187,12 +2482,12 @@ static int http_response_inetdrv(void *arg, int major, int minor, i = LOAD_TUPLE(spec, i, 2); i = LOAD_TUPLE(spec, i, 4); ASSERT(i<=27); - return driver_send_term(desc->inet.port, caller, spec, i); + return erl_drv_send_term(desc->inet.dport, caller, spec, i); } else { i = LOAD_TUPLE(spec, i, 3); ASSERT(i<=27); - return driver_output_term(desc->inet.port, spec, i); + return erl_drv_output_term(desc->inet.dport, spec, i); } } @@ -2284,12 +2579,12 @@ http_request_inetdrv(void* arg, const http_atom_t* meth, const char* meth_ptr, i = LOAD_TUPLE(spec, i, 2); i = LOAD_TUPLE(spec, i, 4); ASSERT(i <= 43); - return driver_send_term(desc->inet.port, caller, spec, i); + return erl_drv_send_term(desc->inet.dport, caller, spec, i); } else { i = LOAD_TUPLE(spec, i, 3); ASSERT(i <= 43); - return driver_output_term(desc->inet.port, spec, i); + return erl_drv_output_term(desc->inet.dport, spec, i); } } @@ -2338,12 +2633,12 @@ http_header_inetdrv(void* arg, const http_atom_t* name, const char* name_ptr, i = LOAD_TUPLE(spec, i, 2); i = LOAD_TUPLE(spec, i, 4); ASSERT(i <= 26); - return driver_send_term(desc->inet.port, caller, spec, i); + return erl_drv_send_term(desc->inet.dport, caller, spec, i); } else { i = LOAD_TUPLE(spec, i, 3); ASSERT(i <= 26); - return driver_output_term(desc->inet.port, spec, i); + return erl_drv_output_term(desc->inet.dport, spec, i); } } @@ -2369,7 +2664,7 @@ static int http_eoh_inetdrv(void* arg) i = LOAD_TUPLE(spec, i, 2); i = LOAD_TUPLE(spec, i, 4); ASSERT(i <= 14); - return driver_send_term(desc->inet.port, caller, spec, i); + return erl_drv_send_term(desc->inet.dport, caller, spec, i); } else { /* {http, S, http_eoh} */ @@ -2378,7 +2673,7 @@ static int http_eoh_inetdrv(void* arg) i = LOAD_ATOM(spec, i, am_http_eoh); i = LOAD_TUPLE(spec, i, 3); ASSERT(i <= 14); - return driver_output_term(desc->inet.port, spec, i); + return erl_drv_output_term(desc->inet.dport, spec, i); } } @@ -2406,7 +2701,7 @@ static int http_error_inetdrv(void* arg, const char* buf, int len) i = LOAD_TUPLE(spec, i, 2); i = LOAD_TUPLE(spec, i, 4); ASSERT(i <= 19); - return driver_send_term(desc->inet.port, caller, spec, i); + return erl_drv_send_term(desc->inet.dport, caller, spec, i); } else { /* {http, S, {http_error,Line} */ @@ -2417,7 +2712,7 @@ static int http_error_inetdrv(void* arg, const char* buf, int len) i = LOAD_TUPLE(spec, i, 2); i = LOAD_TUPLE(spec, i, 3); ASSERT(i <= 19); - return driver_output_term(desc->inet.port, spec, i); + return erl_drv_output_term(desc->inet.dport, spec, i); } } @@ -2470,11 +2765,11 @@ int ssl_tls_inetdrv(void* arg, unsigned type, unsigned major, unsigned minor, i = LOAD_TUPLE(spec, i, 2); i = LOAD_TUPLE(spec, i, 4); ASSERT(i <= 28); - ret = driver_send_term(desc->inet.port, caller, spec, i); + ret = erl_drv_send_term(desc->inet.dport, caller, spec, i); } else { ASSERT(i <= 28); - ret = driver_output_term(desc->inet.port, spec, i); + ret = erl_drv_output_term(desc->inet.dport, spec, i); } done: driver_free_binary(bin); @@ -2524,7 +2819,7 @@ static int inet_async_data(inet_descriptor* desc, const char* buf, int len) i = LOAD_TUPLE(spec, i, 4); ASSERT(i == 15); desc->caller = 0; - return driver_send_term(desc->port, caller, spec, i); + return erl_drv_send_term(desc->dport, caller, spec, i); } else { /* INET_MODE_BINARY => [H1,H2,...HSz | Binary] */ @@ -2538,7 +2833,7 @@ static int inet_async_data(inet_descriptor* desc, const char* buf, int len) i = LOAD_TUPLE(spec, i, 4); ASSERT(i <= 20); desc->caller = 0; - code = driver_send_term(desc->port, caller, spec, i); + code = erl_drv_send_term(desc->dport, caller, spec, i); return code; } } @@ -3131,7 +3426,7 @@ inet_async_binary_data ASSERT(i <= PACKET_ERL_DRV_TERM_DATA_LEN); desc->caller = 0; - return driver_send_term(desc->port, caller, spec, i); + return erl_drv_send_term(desc->dport, caller, spec, i); } /* @@ -3154,7 +3449,7 @@ static int tcp_message(inet_descriptor* desc, const char* buf, int len) i = LOAD_STRING(spec, i, buf, len); /* => [H1,H2,...Hn] */ i = LOAD_TUPLE(spec, i, 3); ASSERT(i <= 20); - return driver_output_term(desc->port, spec, i); + return erl_drv_output_term(desc->dport, spec, i); } else { /* INET_MODE_BINARY => [H1,H2,...HSz | Binary] */ @@ -3166,7 +3461,7 @@ static int tcp_message(inet_descriptor* desc, const char* buf, int len) i = LOAD_STRING_CONS(spec, i, buf, hsz); i = LOAD_TUPLE(spec, i, 3); ASSERT(i <= 20); - code = driver_output_term(desc->port, spec, i); + code = erl_drv_output_term(desc->dport, spec, i); return code; } } @@ -3201,7 +3496,7 @@ tcp_binary_message(inet_descriptor* desc, ErlDrvBinary* bin, int offs, int len) } i = LOAD_TUPLE(spec, i, 3); ASSERT(i <= 20); - return driver_output_term(desc->port, spec, i); + return erl_drv_output_term(desc->dport, spec, i); } /* @@ -3220,7 +3515,7 @@ static int tcp_closed_message(tcp_descriptor* desc) i = LOAD_PORT(spec, i, desc->inet.dport); i = LOAD_TUPLE(spec, i, 2); ASSERT(i <= 6); - return driver_output_term(desc->inet.port, spec, i); + return erl_drv_output_term(desc->inet.dport, spec, i); } return 0; } @@ -3241,9 +3536,10 @@ static int tcp_error_message(tcp_descriptor* desc, int err) i = LOAD_ATOM(spec, i, am_err); i = LOAD_TUPLE(spec, i, 3); ASSERT(i <= 8); - return driver_output_term(desc->inet.port, spec, i); + return erl_drv_output_term(desc->inet.dport, spec, i); } +#ifdef HAVE_UDP /* ** active mode message: ** {udp, S, IP, Port, [H1,...Hsz | Data]} or @@ -3332,9 +3628,43 @@ static int packet_binary_message /* Close up the outer 5-tuple: */ i = LOAD_TUPLE(spec, i, 5); ASSERT(i <= PACKET_ERL_DRV_TERM_DATA_LEN); - return driver_output_term(desc->port, spec, i); + return erl_drv_output_term(desc->dport, spec, i); } +#endif + +/* +** active mode message: send active-to-passive transition message +** {tcp_passive, S} or +** {udp_passive, S} or +** {sctp_passive, S} +*/ + static int packet_passive_message(inet_descriptor* desc) + { + ErlDrvTermData spec[6]; + int i = 0; + + DEBUGF(("packet_passive_message(%ld):\r\n", (long)desc->port)); + +#if !defined(HAVE_UDP) && !defined(HAVE_SCTP) + i = LOAD_ATOM(spec, i, am_tcp_passive); +#else + if (desc->sprotocol == IPPROTO_TCP) + i = LOAD_ATOM(spec, i, am_tcp_passive); + else { +#ifdef HAVE_SCTP + i = LOAD_ATOM(spec, i, IS_SCTP(desc) ? am_sctp_passive : am_udp_passive); +#else + i = LOAD_ATOM(spec, i, am_udp_passive); +#endif + } +#endif + i = LOAD_PORT(spec, i, desc->dport); + i = LOAD_TUPLE(spec, i, 2); + ASSERT(i <= 6); + return erl_drv_output_term(desc->dport, spec, i); + } +#ifdef HAVE_UDP /* ** send active message {udp_error|sctp_error, S, Error} */ @@ -3359,20 +3689,9 @@ static int packet_error_message(udp_descriptor* udesc, int err) i = LOAD_ATOM(spec, i, am_err); i = LOAD_TUPLE(spec, i, 3); ASSERT(i == sizeof(spec)/sizeof(*spec)); - return driver_output_term(desc->port, spec, i); -} - - -/* scan buffer for bit 7 */ -static void scanbit8(inet_descriptor* desc, const char* buf, int len) -{ - int c; - - if (!desc->bit8f || desc->bit8) return; - c = 0; - while(len--) c |= *buf++; - desc->bit8 = ((c & 0x80) != 0); + return erl_drv_output_term(desc->dport, spec, i); } +#endif /* ** active=TRUE: @@ -3388,10 +3707,8 @@ static int tcp_reply_data(tcp_descriptor* desc, char* buf, int len) int code; const char* body = buf; int bodylen = len; - - packet_get_body(desc->inet.htype, &body, &bodylen); - scanbit8(INETP(desc), body, bodylen); + packet_get_body(desc->inet.htype, &body, &bodylen); if (desc->inet.deliver == INET_DELIVER_PORT) { code = inet_port_data(INETP(desc), body, bodylen); @@ -3408,8 +3725,7 @@ static int tcp_reply_data(tcp_descriptor* desc, char* buf, int len) if (code < 0) return code; - if (desc->inet.active == INET_ONCE) - desc->inet.active = INET_PASSIVE; + INET_CHECK_ACTIVE_TO_PASSIVE(INETP(desc)); return code; } @@ -3424,8 +3740,6 @@ tcp_reply_binary_data(tcp_descriptor* desc, ErlDrvBinary* bin, int offs, int len packet_get_body(desc->inet.htype, &body, &bodylen); offs = body - bin->orig_bytes; /* body offset now */ - scanbit8(INETP(desc), body, bodylen); - if (desc->inet.deliver == INET_DELIVER_PORT) code = inet_port_binary_data(INETP(desc), bin, offs, bodylen); else if ((code=packet_parse(desc->inet.htype, buf, len, &desc->http_state, @@ -3438,12 +3752,11 @@ tcp_reply_binary_data(tcp_descriptor* desc, ErlDrvBinary* bin, int offs, int len } if (code < 0) return code; - if (desc->inet.active == INET_ONCE) - desc->inet.active = INET_PASSIVE; + INET_CHECK_ACTIVE_TO_PASSIVE(INETP(desc)); return code; } - +#ifdef HAVE_UDP static int packet_reply_binary_data(inet_descriptor* desc, unsigned int hsz, ErlDrvBinary * bin, int offs, int len, @@ -3451,8 +3764,6 @@ packet_reply_binary_data(inet_descriptor* desc, unsigned int hsz, { int code; - scanbit8(desc, bin->orig_bytes+offs, len); - if (desc->active == INET_PASSIVE) /* "inet" is actually for both UDP and SCTP, as well as TCP! */ return inet_async_binary_data(desc, hsz, bin, offs, len, extra); @@ -3464,11 +3775,11 @@ packet_reply_binary_data(inet_descriptor* desc, unsigned int hsz, code = packet_binary_message(desc, bin, offs, len, extra); if (code < 0) return code; - if (desc->active == INET_ONCE) - desc->active = INET_PASSIVE; + INET_CHECK_ACTIVE_TO_PASSIVE(desc); return code; } } +#endif /* ---------------------------------------------------------------------------- @@ -3510,6 +3821,7 @@ sock_init(void) /* May be called multiple times. */ #ifdef HAVE_SCTP static void inet_init_sctp(void) { INIT_ATOM(sctp); + INIT_ATOM(sctp_passive); INIT_ATOM(sctp_error); INIT_ATOM(true); INIT_ATOM(false); @@ -3519,6 +3831,7 @@ static void inet_init_sctp(void) { INIT_ATOM(binary); INIT_ATOM(active); INIT_ATOM(once); + INIT_ATOM(multi); INIT_ATOM(buffer); INIT_ATOM(linger); INIT_ATOM(recbuf); @@ -3527,6 +3840,8 @@ static void inet_init_sctp(void) { INIT_ATOM(dontroute); INIT_ATOM(priority); INIT_ATOM(tos); + INIT_ATOM(ipv6_v6only); + INIT_ATOM(netns); /* Option names */ INIT_ATOM(sctp_rtoinfo); @@ -3640,15 +3955,22 @@ static int inet_init() INIT_ATOM(ok); INIT_ATOM(tcp); +#ifdef HAVE_UDP INIT_ATOM(udp); +#endif INIT_ATOM(error); + INIT_ATOM(einval); INIT_ATOM(inet_async); INIT_ATOM(inet_reply); INIT_ATOM(timeout); INIT_ATOM(closed); + INIT_ATOM(tcp_passive); INIT_ATOM(tcp_closed); INIT_ATOM(tcp_error); +#ifdef HAVE_UDP + INIT_ATOM(udp_passive); INIT_ATOM(udp_error); +#endif INIT_ATOM(empty_out_q); INIT_ATOM(ssl_tls); @@ -3667,14 +3989,35 @@ static int inet_init() /* add TCP, UDP and SCTP drivers */ add_driver_entry(&tcp_inet_driver_entry); +#ifdef HAVE_UDP add_driver_entry(&udp_inet_driver_entry); +#endif + #ifdef HAVE_SCTP /* Check the size of SCTP AssocID -- currently both this driver and the Erlang part require 32 bit: */ ASSERT(sizeof(sctp_assoc_t)==ASSOC_ID_LEN); -# if defined(HAVE_SCTP_BINDX) && defined (HAVE_SCTP_PEELOFF) +# if defined(HAVE_SCTP_BINDX) p_sctp_bindx = sctp_bindx; +# if defined(HAVE_SCTP_PEELOFF) p_sctp_peeloff = sctp_peeloff; +# else + p_sctp_peeloff = NULL; +# endif +# if defined(HAVE_SCTP_GETLADDRS) && defined(HAVE_SCTP_FREELADDRS) + p_sctp_getladdrs = sctp_getladdrs; + p_sctp_freeladdrs = sctp_freeladdrs; +# else + p_sctp_getladdrs = NULL; + p_sctp_freeladdrs = NULL; +# endif +# if defined(HAVE_SCTP_GETPADDRS) && defined(HAVE_SCTP_FREEPADDRS) + p_sctp_getpaddrs = sctp_getpaddrs; + p_sctp_freepaddrs = sctp_freepaddrs; +# else + p_sctp_getpaddrs = NULL; + p_sctp_freepaddrs = NULL; +# endif inet_init_sctp(); add_driver_entry(&sctp_inet_driver_entry); # else @@ -3689,12 +4032,36 @@ static int inet_init() void *ptr; if (erts_sys_ddll_sym(h_libsctp, "sctp_bindx", &ptr) == 0) { p_sctp_bindx = ptr; - inet_init_sctp(); - add_driver_entry(&sctp_inet_driver_entry); if (erts_sys_ddll_sym(h_libsctp, "sctp_peeloff", &ptr) == 0) { p_sctp_peeloff = ptr; } + else p_sctp_peeloff = NULL; + if (erts_sys_ddll_sym(h_libsctp, "sctp_getladdrs", &ptr) == 0) { + p_sctp_getladdrs = ptr; + } + else p_sctp_getladdrs = NULL; + if (erts_sys_ddll_sym(h_libsctp, "sctp_freeladdrs", &ptr) == 0) { + p_sctp_freeladdrs = ptr; + } + else { + p_sctp_freeladdrs = NULL; + p_sctp_getladdrs = NULL; + } + if (erts_sys_ddll_sym(h_libsctp, "sctp_getpaddrs", &ptr) == 0) { + p_sctp_getpaddrs = ptr; + } + else p_sctp_getpaddrs = NULL; + if (erts_sys_ddll_sym(h_libsctp, "sctp_freepaddrs", &ptr) == 0) { + p_sctp_freepaddrs = ptr; + } + else { + p_sctp_freepaddrs = NULL; + p_sctp_getpaddrs = NULL; + } + inet_init_sctp(); + add_driver_entry(&sctp_inet_driver_entry); } + else p_sctp_bindx = NULL; } } # endif @@ -3849,10 +4216,12 @@ static char *inet_set_faddress(int family, inet_address* dst, ** and *len is the length of dst on return ** (suitable to deliver to erlang) */ -static int inet_get_address(int family, char* dst, inet_address* src, unsigned int* len) +static int inet_get_address(char* dst, inet_address* src, unsigned int* len) { + int family; short port; + family = src->sa.sa_family; if ((family == AF_INET) && (*len >= sizeof(struct sockaddr_in))) { dst[0] = INET_AF_INET; port = sock_ntohs(src->sai.sin_port); @@ -3874,6 +4243,75 @@ static int inet_get_address(int family, char* dst, inet_address* src, unsigned i return -1; } +/* Same as the above, but take family from the address structure, +** and advance the address pointer to the next address +** according to the size of the current, +** and return the resulting encoded size +*/ +static int inet_address_to_erlang(char *dst, inet_address **src) { + short port; + + switch ((*src)->sa.sa_family) { + case AF_INET: + if (dst) { + dst[0] = INET_AF_INET; + port = sock_ntohs((*src)->sai.sin_port); + put_int16(port, dst+1); + sys_memcpy(dst+1+2, (char *) &(*src)->sai.sin_addr, 4); + } + (*src) = (inet_address *) (&(*src)->sai + 1); + return 1 + 2 + 4; +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + if (dst) { + dst[0] = INET_AF_INET6; + port = sock_ntohs((*src)->sai6.sin6_port); + put_int16(port, dst+1); + VALGRIND_MAKE_MEM_DEFINED(&(*src)->sai6.sin6_addr,16); /* false undefs from syscall sctp_get[lp]addrs */ + sys_memcpy(dst+1+2, (char *) &(*src)->sai6.sin6_addr, 16); + } + (*src) = (inet_address *) (&(*src)->sai6 + 1); + return 1 + 2 + 16; +#endif + default: + return -1; + } +} + +/* Encode n encoded addresses from addrs in the result buffer +*/ +static ErlDrvSizeT reply_inet_addrs +(int n, inet_address *addrs, char **rbuf, ErlDrvSizeT rsize) { + inet_address *ia; + int i, s; + ErlDrvSizeT rlen; + + if (IS_SOCKET_ERROR(n)) return ctl_error(sock_errno(), rbuf, rsize); + if (n == 0) return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); + + /* Calculate result length */ + rlen = 1; + ia = addrs; + for (i = 0; i < n; i++) { + s = inet_address_to_erlang(NULL, &ia); + if (s < 0) break; + rlen += s; + } + + if (rlen > rsize) (*rbuf) = ALLOC(rlen); + + (*rbuf)[0] = INET_REP_OK; + rlen = 1; + ia = addrs; + for (i = 0; i < n; i++) { + s = inet_address_to_erlang((*rbuf)+rlen, &ia); + if (s < 0) break; + rlen += s; + } + + return rlen; +} + static void desc_close(inet_descriptor* desc) { if (desc->s != INVALID_SOCKET) { @@ -3883,13 +4321,26 @@ static void desc_close(inet_descriptor* desc) desc->forced_events = 0; desc->send_would_block = 0; #endif - // We should close the fd here, but the other driver might still - // be selecting on it. +#ifdef __OSE__ + if (desc->events[0]) { + driver_select(desc->port,desc->events[0],FD_READ|FD_WRITE|ERL_DRV_USE,0); + driver_select(desc->port,desc->events[1],FD_READ|FD_WRITE|ERL_DRV_USE,0); + driver_select(desc->port,desc->events[2],FD_READ|FD_WRITE|ERL_DRV_USE,0); + driver_select(desc->port,desc->events[3],FD_READ|FD_WRITE|ERL_DRV_USE,0); + driver_select(desc->port,desc->events[4],FD_READ|FD_WRITE|ERL_DRV_USE,0); + driver_select(desc->port,desc->events[5],FD_READ|FD_WRITE|ERL_DRV_USE,0); + } +#else + /* + * We should close the fd here, but the other driver might still + * be selecting on it. + */ if (!desc->is_ignored) driver_select(desc->port,(ErlDrvEvent)(long)desc->event, ERL_DRV_USE, 0); else inet_stop_select((ErlDrvEvent)(long)desc->event,NULL); +#endif desc->event = INVALID_EVENT; /* closed by stop_select callback */ desc->s = INVALID_SOCKET; desc->event_mask = 0; @@ -3921,7 +4372,7 @@ static int erl_inet_close(inet_descriptor* desc) desc_close(desc); desc->state = INET_STATE_CLOSED; } else if (desc->prebound && (desc->s != INVALID_SOCKET)) { - sock_select(desc, FD_READ | FD_WRITE | FD_CLOSE, 0); + sock_select(desc, FD_READ | FD_WRITE | FD_CLOSE | ERL_DRV_USE_NO_CALLBACK, 0); desc->event_mask = 0; #ifdef __WIN32__ desc->forced_events = 0; @@ -3931,20 +4382,151 @@ static int erl_inet_close(inet_descriptor* desc) return 0; } +#ifdef __OSE__ +static void inet_select_init(inet_descriptor* desc) +{ + desc->events[0] = + erl_drv_ose_event_alloc(SO_EVENT_READ_REPLY, + desc->s, + inet_resolve_signal, + NULL); + driver_select(desc->port, desc->events[0], + ERL_DRV_READ|ERL_DRV_USE, 1); + + desc->events[1] = + erl_drv_ose_event_alloc(SO_EVENT_EOF_REPLY, + desc->s, + inet_resolve_signal, + NULL); + driver_select(desc->port, desc->events[1], + ERL_DRV_READ|ERL_DRV_USE, 1); + + desc->events[2] = + erl_drv_ose_event_alloc(SO_EVENT_ACCEPT_REPLY, + desc->s, + inet_resolve_signal, + NULL); + driver_select(desc->port, desc->events[2], + ERL_DRV_READ|ERL_DRV_USE, 1); + + /* trigger tcp_inet_input */ + desc->events[3] = + erl_drv_ose_event_alloc(SO_EVENT_WRITE_REPLY, + desc->s, + inet_resolve_signal, + NULL); + driver_select(desc->port, desc->events[3], + ERL_DRV_WRITE|ERL_DRV_USE, 1); + + desc->events[4] = + erl_drv_ose_event_alloc(SO_EVENT_CONNECT_REPLY, + desc->s, + inet_resolve_signal, + NULL); + driver_select(desc->port, desc->events[4], + ERL_DRV_WRITE|ERL_DRV_USE, 1); + + desc->events[5] = + erl_drv_ose_event_alloc(SO_EVENT_ERROR_REPLY, + desc->s, + inet_resolve_signal, + NULL); + driver_select(desc->port, desc->events[5], + ERL_DRV_WRITE|ERL_DRV_USE, 1); + + /* Issue a select on error event before any other select to be sure we are + prepared to receive error notifications from the stack, even in the + situations when select isn't issued */ + sock_select(desc, SOCK_FD_ERROR, 1); +} +#endif static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, char** rbuf, ErlDrvSizeT rsize) { + int save_errno; +#ifdef HAVE_SETNS + int current_ns, new_ns; + current_ns = new_ns = 0; +#endif + save_errno = 0; + if (desc->state != INET_STATE_CLOSED) return ctl_xerror(EXBADSEQ, rbuf, rsize); + +#ifdef HAVE_SETNS + if (desc->netns != NULL) { + /* Temporarily change network namespace for this thread + * while creating the socket + */ + current_ns = open("/proc/self/ns/net", O_RDONLY); + if (current_ns == INVALID_SOCKET) + return ctl_error(sock_errno(), rbuf, rsize); + new_ns = open(desc->netns, O_RDONLY); + if (new_ns == INVALID_SOCKET) { + save_errno = sock_errno(); + while (close(current_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + return ctl_error(save_errno, rbuf, rsize); + } + if (setns(new_ns, CLONE_NEWNET) != 0) { + save_errno = sock_errno(); + while (close(new_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + while (close(current_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + return ctl_error(save_errno, rbuf, rsize); + } + else { + while (close(new_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + } + } +#endif if ((desc->s = sock_open(domain, type, desc->sprotocol)) == INVALID_SOCKET) - return ctl_error(sock_errno(), rbuf, rsize); - if ((desc->event = sock_create_event(desc)) == INVALID_EVENT) - return ctl_error(sock_errno(), rbuf, rsize); + save_errno = sock_errno(); +#ifdef HAVE_SETNS + if (desc->netns != NULL) { + /* Restore network namespace */ + if (setns(current_ns, CLONE_NEWNET) != 0) { + /* XXX Failed to restore network namespace. + * What to do? Tidy up and return an error... + * Note that the thread now might still be in the namespace. + * Can this even happen? Should the emulator be aborted? + */ + if (desc->s != INVALID_SOCKET) + save_errno = sock_errno(); + while (close(desc->s) == INVALID_SOCKET && + sock_errno() == EINTR); + desc->s = INVALID_SOCKET; + while (close(current_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + return ctl_error(save_errno, rbuf, rsize); + } + else { + while (close(current_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + } + } +#endif + if (desc->s == INVALID_SOCKET) + return ctl_error(save_errno, rbuf, rsize); + + if ((desc->event = sock_create_event(desc)) == INVALID_EVENT) { + save_errno = sock_errno(); + while (close(desc->s) == INVALID_SOCKET && + sock_errno() == EINTR); + desc->s = INVALID_SOCKET; + return ctl_error(save_errno, rbuf, rsize); + } SET_NONBLOCKING(desc->s); #ifdef __WIN32__ driver_select(desc->port, desc->event, ERL_DRV_READ, 1); #endif +#ifdef __OSE__ + inet_select_init(desc); +#endif + desc->state = INET_STATE_OPEN; desc->stype = type; desc->sfamily = domain; @@ -3954,7 +4536,8 @@ static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, /* as inet_open but pass in an open socket (MUST BE OF RIGHT TYPE) */ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, - SOCKET s, char** rbuf, ErlDrvSizeT rsize) + SOCKET s, Uint32 bound, + char** rbuf, ErlDrvSizeT rsize) { inet_address name; unsigned int sz = sizeof(name); @@ -3964,18 +4547,37 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, return ctl_error(sock_errno(), rbuf, rsize); if (name.sa.sa_family != domain) return ctl_error(EINVAL, rbuf, rsize); +#ifdef __OSE__ + /* for fdopen duplicating the sd will allow to uniquely identify + the signal from OSE with erlang port */ + desc->s = sock_dup(s); +#else desc->s = s; +#endif + if ((desc->event = sock_create_event(desc)) == INVALID_EVENT) return ctl_error(sock_errno(), rbuf, rsize); SET_NONBLOCKING(desc->s); #ifdef __WIN32__ driver_select(desc->port, desc->event, ERL_DRV_READ, 1); #endif - desc->state = INET_STATE_BOUND; /* assume bound */ + + if (bound) + desc->state = INET_STATE_BOUND; + else + desc->state = INET_STATE_OPEN; + if (type == SOCK_STREAM) { /* check if connected */ sz = sizeof(name); - if (!IS_SOCKET_ERROR(sock_peer(s, (struct sockaddr*) &name, &sz))) + if (!IS_SOCKET_ERROR(sock_peer(s, (struct sockaddr*) &name, &sz))) { desc->state = INET_STATE_CONNECTED; +#ifdef __OSE__ + /* since we are dealing with different descriptors (i.e. inet and + socket) the select part should be initialized with the right + values */ + inet_select_init(desc); +#endif + } } desc->prebound = 1; /* used to prevent a real close since @@ -4001,8 +4603,7 @@ struct addr_if { #ifndef SIOCGIFNETMASK -static struct in_addr net_mask(in) -struct in_addr in; +static struct in_addr net_mask(struct in_addr in) { register u_long i = sock_ntohl(in.s_addr); @@ -4376,7 +4977,7 @@ static ErlDrvSSizeT inet_ctl_getiflist(inet_descriptor* desc, case AF_INET6: #endif case AF_INET: - ASSERT(sp+IFNAMSIZ+1 < sbuf+ifc.ifc_len+1) + ASSERT(sp+IFNAMSIZ+1 < sbuf+ifc.ifc_len+1); strncpy(sp, ifrp->ifr_name, IFNAMSIZ); sp[IFNAMSIZ] = '\0'; sp += strlen(sp), ++sp; @@ -5313,50 +5914,6 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p, #endif - - -#ifdef VXWORKS -/* -** THIS is a terrible creature, a bug in the TCP part -** of the old VxWorks stack (non SENS) created a race. -** If (and only if?) a socket got closed from the other -** end and we tried a set/getsockopt on the TCP level, -** the task would generate a bus error... -*/ -static STATUS wrap_sockopt(STATUS (*function)() /* Yep, no parameter - check */, - int s, int level, int optname, - char *optval, unsigned int optlen - /* optlen is a pointer if function - is getsockopt... */) -{ - fd_set rs; - struct timeval timeout; - int to_read; - int ret; - - FD_ZERO(&rs); - FD_SET(s,&rs); - memset(&timeout,0,sizeof(timeout)); - if (level == IPPROTO_TCP) { - taskLock(); - if (select(s+1,&rs,NULL,NULL,&timeout)) { - if (ioctl(s,FIONREAD,(int)&to_read) == ERROR || - to_read == 0) { /* End of file, other end closed? */ - sock_errno() = EBADF; - taskUnlock(); - return ERROR; - } - } - ret = (*function)(s,level,optname,optval,optlen); - taskUnlock(); - } else { - ret = (*function)(s,level,optname,optval,optlen); - } - return ret; -} -#endif - /* Per H @ Tail-f: The original code here had problems that possibly only occur if you abuse it for non-INET sockets, but anyway: a) If the getsockopt for SO_PRIORITY or IP_TOS failed, the actual @@ -5382,13 +5939,8 @@ static int setopt_prio_tos_trick int res; int res_prio; int res_tos; -#ifdef HAVE_SOCKLEN_T - socklen_t -#else - int -#endif - tmp_arg_sz_prio = sizeof(tmp_ival_prio), - tmp_arg_sz_tos = sizeof(tmp_ival_tos); + SOCKLEN_T tmp_arg_sz_prio = sizeof(tmp_ival_prio); + SOCKLEN_T tmp_arg_sz_tos = sizeof(tmp_ival_tos); res_prio = sock_getopt(fd, SOL_SOCKET, SO_PRIORITY, (char *) &tmp_ival_prio, &tmp_arg_sz_prio); @@ -5500,8 +6052,25 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) case INET_LOPT_ACTIVE: DEBUGF(("inet_set_opts(%ld): s=%d, ACTIVE=%d\r\n", - (long)desc->port, desc->s,ival)); + (long)desc->port, desc->s, ival)); desc->active = ival; + if (desc->active == INET_MULTI) { + long ac = desc->active_count; + Sint16 nval = get_int16(ptr); + ptr += 2; + len -= 2; + ac += nval; + if (ac > INT16_MAX || ac < INT16_MIN) + return -1; + desc->active_count += nval; + if (desc->active_count < 0) + desc->active_count = 0; + if (desc->active_count == 0) { + desc->active = INET_PASSIVE; + packet_passive_message(desc); + } + } else + desc->active_count = 0; if ((desc->stype == SOCK_STREAM) && (desc->active != INET_PASSIVE) && (desc->state == INET_STATE_CLOSED)) { tcp_closed_message((tcp_descriptor *) desc); @@ -5532,29 +6101,6 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) desc->exitf = ival; continue; - case INET_LOPT_BIT8: - DEBUGF(("inet_set_opts(%ld): s=%d, BIT8=%d\r\n", - (long)desc->port, desc->s, ival)); - switch(ival) { - case INET_BIT8_ON: - desc->bit8f = 1; - desc->bit8 = 0; - break; - case INET_BIT8_OFF: - desc->bit8f = 0; - desc->bit8 = 0; - break; - case INET_BIT8_CLEAR: - desc->bit8f = 1; - desc->bit8 = 0; - break; - case INET_BIT8_SET: - desc->bit8f = 1; - desc->bit8 = 1; - break; - } - continue; - case INET_LOPT_TCP_HIWTRMRK: if (desc->stype == SOCK_STREAM) { tcp_descriptor* tdesc = (tcp_descriptor*) desc; @@ -5575,6 +6121,26 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) } continue; + case INET_LOPT_MSGQ_HIWTRMRK: { + ErlDrvSizeT high; + if (ival < ERL_DRV_BUSY_MSGQ_LIM_MIN + || ERL_DRV_BUSY_MSGQ_LIM_MAX < ival) + return -1; + high = (ErlDrvSizeT) ival; + erl_drv_busy_msgq_limits(desc->port, NULL, &high); + continue; + } + + case INET_LOPT_MSGQ_LOWTRMRK: { + ErlDrvSizeT low; + if (ival < ERL_DRV_BUSY_MSGQ_LIM_MIN + || ERL_DRV_BUSY_MSGQ_LIM_MAX < ival) + return -1; + low = (ErlDrvSizeT) ival; + erl_drv_busy_msgq_limits(desc->port, &low, NULL); + continue; + } + case INET_LOPT_TCP_SEND_TIMEOUT: if (desc->stype == SOCK_STREAM) { tcp_descriptor* tdesc = (tcp_descriptor*) desc; @@ -5600,6 +6166,7 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) } continue; +#ifdef HAVE_UDP case INET_LOPT_UDP_READ_PACKETS: if (desc->stype == SOCK_DGRAM) { udp_descriptor* udesc = (udp_descriptor*) desc; @@ -5607,6 +6174,21 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) udesc->read_packets = ival; } continue; +#endif + +#ifdef HAVE_SETNS + case INET_LOPT_NETNS: + /* It is annoying that ival and len are both (signed) int */ + if (ival < 0) return -1; + if (len < ival) return -1; + if (desc->netns != NULL) FREE(desc->netns); + desc->netns = ALLOC(((unsigned int) ival) + 1); + memcpy(desc->netns, ptr, ival); + desc->netns[ival] = '\0'; + ptr += ival; + len -= ival; + continue; +#endif case INET_OPT_REUSEADDR: #ifdef __WIN32__ @@ -5636,23 +6218,11 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) case INET_OPT_SNDBUF: type = SO_SNDBUF; DEBUGF(("inet_set_opts(%ld): s=%d, SO_SNDBUF=%d\r\n", (long)desc->port, desc->s, ival)); - /* - * Setting buffer sizes in VxWorks gives unexpected results - * our workaround is to leave it at default. - */ -#ifdef VXWORKS - goto skip_os_setopt; -#else break; -#endif case INET_OPT_RCVBUF: type = SO_RCVBUF; DEBUGF(("inet_set_opts(%ld): s=%d, SO_RCVBUF=%d\r\n", (long)desc->port, desc->s, ival)); -#ifdef VXWORKS - goto skip_os_setopt; -#else break; -#endif case INET_OPT_LINGER: type = SO_LINGER; if (len < 4) return -1; @@ -5743,6 +6313,23 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) #endif /* HAVE_MULTICAST_SUPPORT */ + case INET_OPT_IPV6_V6ONLY: +#if HAVE_DECL_IPV6_V6ONLY + proto = IPPROTO_IPV6; + type = IPV6_V6ONLY; + propagate = 1; + DEBUGF(("inet_set_opts(%ld): s=%d, IPV6_V6ONLY=%d\r\n", + (long)desc->port, desc->s, ival)); + break; +#elif defined(__WIN32__) && defined(HAVE_IN6) && defined(AF_INET6) + /* Fake a'la OpenBSD; set to 'true' is fine but 'false' invalid. */ + if (ival != 0) continue; + else return -1; + break; +#else + continue; +#endif + case INET_OPT_RAW: if (len < 8) { return -1; @@ -5774,9 +6361,6 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) } DEBUGF(("inet_set_opts(%ld): s=%d returned %d\r\n", (long)desc->port, desc->s, res)); -#ifdef VXWORKS -skip_os_setopt: -#endif if (type == SO_RCVBUF) { /* make sure we have desc->bufsz >= SO_RCVBUF */ if (ival > desc->bufsz) @@ -5799,7 +6383,8 @@ skip_os_setopt: /* XXX fprintf(stderr,"desc->htype == %d, old_htype == %d, desc->active == %d, old_active == %d\r\n",(int)desc->htype, (int) old_htype, (int) desc->active, (int) old_active );*/ - return 1+(desc->htype == old_htype && desc->active == INET_ONCE); + return 1+(desc->htype == old_htype && + (desc->active == INET_ONCE || desc->active == INET_MULTI)); } return 0; } @@ -5932,9 +6517,39 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len) case INET_LOPT_ACTIVE: desc->active = get_int32(curr); curr += 4; + if (desc->active == INET_MULTI) { + long ac = desc->active_count; + Sint16 nval = get_int16(curr); curr += 2; + ac += nval; + if (ac > INT16_MAX || ac < INT16_MIN) + return -1; + desc->active_count += nval; + if (desc->active_count < 0) + desc->active_count = 0; + if (desc->active_count == 0) { + desc->active = INET_PASSIVE; + packet_passive_message(desc); + } + } else + desc->active_count = 0; res = 0; continue; +#ifdef HAVE_SETNS + case INET_LOPT_NETNS: + { + size_t ns_len; + ns_len = get_int32(curr); curr += 4; + CHKLEN(curr, ns_len); + if (desc->netns != NULL) FREE(desc->netns); + desc->netns = ALLOC(ns_len + 1); + memcpy(desc->netns, curr, ns_len); + desc->netns[ns_len] = '\0'; + curr += ns_len; + } + continue; +#endif + /* SCTP options and applicable generic INET options: */ case SCTP_OPT_RTOINFO: @@ -6075,6 +6690,22 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len) continue; /* Option not supported -- ignore it */ # endif + case INET_OPT_IPV6_V6ONLY: +# if HAVE_DECL_IPV6_V6ONLY + { + arg.ival= get_int32 (curr); curr += 4; + proto = IPPROTO_IPV6; + type = IPV6_V6ONLY; + arg_ptr = (char*) (&arg.ival); + arg_sz = sizeof ( arg.ival); + break; + } +# elif defined(__WIN32__) && defined(HAVE_IN6) && defined(AF_INET6) +# error Here is a fix for Win IPv6 SCTP missing +# else + continue; /* Option not supported -- ignore it */ +# endif + case SCTP_OPT_AUTOCLOSE: { arg.ival= get_int32 (curr); curr += 4; @@ -6423,6 +7054,11 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, case INET_LOPT_ACTIVE: *ptr++ = opt; put_int32(desc->active, ptr); + if (desc->active == INET_MULTI) { + PLACE_FOR(2,ptr); + put_int16(desc->active_count, ptr); + ptr += 2; + } continue; case INET_LOPT_PACKET: *ptr++ = opt; @@ -6437,15 +7073,6 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, put_int32(desc->exitf, ptr); continue; - case INET_LOPT_BIT8: - *ptr++ = opt; - if (desc->bit8f) { - put_int32(desc->bit8, ptr); - } else { - put_int32(INET_BIT8_OFF, ptr); - } - continue; - case INET_LOPT_TCP_HIWTRMRK: if (desc->stype == SOCK_STREAM) { *ptr++ = opt; @@ -6466,6 +7093,24 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, } continue; + case INET_LOPT_MSGQ_HIWTRMRK: { + ErlDrvSizeT high = ERL_DRV_BUSY_MSGQ_READ_ONLY; + *ptr++ = opt; + erl_drv_busy_msgq_limits(desc->port, NULL, &high); + ival = high > INT_MAX ? INT_MAX : (int) high; + put_int32(ival, ptr); + continue; + } + + case INET_LOPT_MSGQ_LOWTRMRK: { + ErlDrvSizeT low = ERL_DRV_BUSY_MSGQ_READ_ONLY; + *ptr++ = opt; + erl_drv_busy_msgq_limits(desc->port, &low, NULL); + ival = low > INT_MAX ? INT_MAX : (int) low; + put_int32(ival, ptr); + continue; + } + case INET_LOPT_TCP_SEND_TIMEOUT: if (desc->stype == SOCK_STREAM) { *ptr++ = opt; @@ -6496,6 +7141,7 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, } continue; +#ifdef HAVE_UDP case INET_LOPT_UDP_READ_PACKETS: if (desc->stype == SOCK_DGRAM) { *ptr++ = opt; @@ -6505,6 +7151,23 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, TRUNCATE_TO(0,ptr); } continue; +#endif + +#ifdef HAVE_SETNS + case INET_LOPT_NETNS: + if (desc->netns != NULL) { + size_t netns_len; + netns_len = strlen(desc->netns); + *ptr++ = opt; + put_int32(netns_len, ptr); + PLACE_FOR(netns_len, ptr); + memcpy(ptr, desc->netns, netns_len); + ptr += netns_len; + } else { + TRUNCATE_TO(0,ptr); + } + continue; +#endif case INET_OPT_PRIORITY: #ifdef SO_PRIORITY @@ -6572,6 +7235,22 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, break; #endif /* HAVE_MULTICAST_SUPPORT */ + case INET_OPT_IPV6_V6ONLY: +#if HAVE_DECL_IPV6_V6ONLY + proto = IPPROTO_IPV6; + type = IPV6_V6ONLY; + break; +#elif defined(__WIN32__) && defined(HAVE_IN6) && defined(AF_INET6) + /* Fake reading 'true' */ + *ptr++ = opt; + put_int32(1, ptr); + ptr += 4; + continue; +#else + TRUNCATE_TO(0,ptr); + continue; /* skip - no result */ +#endif + case INET_OPT_RAW: { int data_provided; @@ -6754,7 +7433,10 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, } case INET_LOPT_ACTIVE: { - PLACE_FOR(spec, i, 2*LOAD_ATOM_CNT + LOAD_TUPLE_CNT); + if (desc->active == INET_MULTI) + PLACE_FOR(spec, i, LOAD_ATOM_CNT + LOAD_INT_CNT + LOAD_TUPLE_CNT); + else + PLACE_FOR(spec, i, 2*LOAD_ATOM_CNT + LOAD_TUPLE_CNT); i = LOAD_ATOM (spec, i, am_active); switch (desc->active) { @@ -6767,12 +7449,31 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, case INET_ONCE : { i = LOAD_ATOM (spec, i, am_once); break; } + case INET_MULTI : + { i = LOAD_INT(spec, i, desc->active_count); break; } + default: ASSERT (0); } i = LOAD_TUPLE (spec, i, 2); break; } +#ifdef HAVE_SETNS + case INET_LOPT_NETNS: + if (desc->netns != NULL) { + PLACE_FOR + (spec, i, + LOAD_ATOM_CNT + LOAD_BUF2BINARY_CNT + LOAD_TUPLE_CNT); + i = LOAD_ATOM (spec, i, am_netns); + i = LOAD_BUF2BINARY + (spec, i, desc->netns, strlen(desc->netns)); + i = LOAD_TUPLE (spec, i, 2); + break; + } + else + continue; /* Ignore */ +#endif + /* SCTP and generic INET options: */ case SCTP_OPT_RTOINFO: @@ -6876,6 +7577,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, case INET_OPT_DONTROUTE: case INET_OPT_PRIORITY : case INET_OPT_TOS : + case INET_OPT_IPV6_V6ONLY: case SCTP_OPT_AUTOCLOSE: case SCTP_OPT_MAXSEG : /* The following options return true or false: */ @@ -6948,6 +7650,20 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, continue; # endif } + case INET_OPT_IPV6_V6ONLY: +# if HAVE_DECL_IPV6_V6ONLY + { + proto = IPPROTO_IPV6; + type = IPV6_V6ONLY; + tag = am_ipv6_v6only; + break; + } +# elif defined(__WIN32__) && defined(HAVE_IN6) && defined(AF_INET6) +# error Here is a fix for Win IPv6 SCTP needed +# else + /* Not supported -- ignore */ + continue; +# endif case SCTP_OPT_AUTOCLOSE: { proto = IPPROTO_SCTP; @@ -7348,7 +8064,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, i = LOAD_TUPLE(spec, i, 3); /* Now, convert "spec" into the returnable term: */ - driver_send_term(desc->port, driver_caller(desc->port), spec, i); + erl_drv_send_term(desc->dport, driver_caller(desc->port), spec, i); FREE(spec); (*dest)[0] = INET_REP; @@ -7398,13 +8114,21 @@ static ErlDrvSSizeT inet_fill_stat(inet_descriptor* desc, val = (unsigned long) driver_sizeq(desc->port); break; case INET_STAT_RECV_OCT: +#ifdef ARCH_64 + put_int64(desc->recv_oct, dst); /* write it all */ +#else put_int32(desc->recv_oct[1], dst); /* write high 32bit */ put_int32(desc->recv_oct[0], dst+4); /* write low 32bit */ +#endif dst += 8; continue; case INET_STAT_SEND_OCT: +#ifdef ARCH_64 + put_int64(desc->send_oct, dst); /* write it all */ +#else put_int32(desc->send_oct[1], dst); /* write high 32bit */ put_int32(desc->send_oct[0], dst+4); /* write low 32bit */ +#endif dst += 8; continue; default: return -1; /* invalid argument */ @@ -7430,7 +8154,7 @@ send_empty_out_q_msgs(inet_descriptor* desc) ASSERT(msg_len == sizeof(msg)/sizeof(*msg)); - send_to_subscribers(desc->port, + send_to_subscribers(desc->dport, &desc->empty_out_q_subs, 1, msg, @@ -7471,9 +8195,27 @@ static ErlDrvSSizeT inet_subscribe(inet_descriptor* desc, static void inet_stop(inet_descriptor* desc) { erl_inet_close(desc); +#ifdef HAVE_SETNS + if (desc->netns != NULL) + FREE(desc->netns); +#endif FREE(desc); } +static void set_default_msgq_limits(ErlDrvPort port) +{ + ErlDrvSizeT q_high = INET_HIGH_MSGQ_WATERMARK; + ErlDrvSizeT q_low = INET_LOW_MSGQ_WATERMARK; + if (q_low < ERL_DRV_BUSY_MSGQ_LIM_MIN) + q_low = ERL_DRV_BUSY_MSGQ_LIM_MIN; + else if (q_low > ERL_DRV_BUSY_MSGQ_LIM_MAX) + q_low = ERL_DRV_BUSY_MSGQ_LIM_MAX; + if (q_high < ERL_DRV_BUSY_MSGQ_LIM_MIN) + q_high = ERL_DRV_BUSY_MSGQ_LIM_MIN; + else if (q_high > ERL_DRV_BUSY_MSGQ_LIM_MAX) + q_high = ERL_DRV_BUSY_MSGQ_LIM_MAX; + erl_drv_busy_msgq_limits(port, &q_low, &q_high); +} /* Allocate descriptor */ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol) @@ -7504,22 +8246,29 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol) desc->mode = INET_MODE_LIST; /* list mode */ desc->exitf = 1; /* exit port when close on active socket */ - desc->bit8f = 0; - desc->bit8 = 0; desc->deliver = INET_DELIVER_TERM; /* standard term format */ desc->active = INET_PASSIVE; /* start passive */ + desc->active_count = 0; desc->oph = NULL; desc->opt = NULL; desc->peer_ptr = NULL; desc->name_ptr = NULL; +#ifdef ARCH_64 + desc->recv_oct = 0; +#else desc->recv_oct[0] = desc->recv_oct[1] = 0; +#endif desc->recv_cnt = 0; desc->recv_max = 0; desc->recv_avg = 0.0; desc->recv_dvi = 0.0; +#ifdef ARCH_64 + desc->send_oct = 0; +#else desc->send_oct[0] = desc->send_oct[1] = 0; +#endif desc->send_cnt = 0; desc->send_max = 0; desc->send_avg = 0.0; @@ -7530,6 +8279,19 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol) desc->is_ignored = 0; +#ifdef HAVE_SETNS + desc->netns = NULL; +#endif +#ifdef __OSE__ + desc->select_state = 0; + desc->events[0] = NULL; + desc->events[1] = NULL; + desc->events[2] = NULL; + desc->events[3] = NULL; + desc->events[4] = NULL; + desc->events[5] = NULL; +#endif + return (ErlDrvData)desc; } @@ -7719,6 +8481,39 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, return ctl_reply(INET_REP_OK, tbuf, strlen(tbuf), rbuf, rsize); } + case INET_REQ_GETPADDRS: { + DEBUGF(("inet_ctl(%ld): INET_GETPADDRS\r\n", (long)desc->port)); + + if (len != 4) return ctl_error(EINVAL, rbuf, rsize); + + if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); + if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize); + +#ifdef HAVE_SCTP + if (IS_SCTP(desc) && p_sctp_getpaddrs) { + struct sockaddr *sa; + Uint32 assoc_id; + int n; + ErlDrvSizeT rlen; + + assoc_id = get_int32(buf); + n = p_sctp_getpaddrs(desc->s, assoc_id, &sa); + rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize); + if (n > 0) p_sctp_freepaddrs(sa); + return rlen; + } +#endif + { /* Fallback to sock_peer */ + inet_address addr; + unsigned int sz; + int i; + + sz = sizeof(addr); + i = sock_peer(desc->s, (struct sockaddr *) &addr, &sz); + return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize); + } + } + case INET_REQ_PEER: { /* get peername */ char tbuf[sizeof(inet_address)]; inet_address peer; @@ -7734,7 +8529,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (IS_SOCKET_ERROR(sock_peer(desc->s, (struct sockaddr*)ptr,&sz))) return ctl_error(sock_errno(), rbuf, rsize); } - if (inet_get_address(desc->sfamily, tbuf, ptr, &sz) < 0) + if (inet_get_address(tbuf, ptr, &sz) < 0) return ctl_error(EINVAL, rbuf, rsize); return ctl_reply(INET_REP_OK, tbuf, sz, rbuf, rsize); } @@ -7755,6 +8550,39 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, } } + case INET_REQ_GETLADDRS: { + DEBUGF(("inet_ctl(%ld): INET_GETLADDRS\r\n", (long)desc->port)); + + if (len != 4) return ctl_error(EINVAL, rbuf, rsize); + + if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); + if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize); + +#ifdef HAVE_SCTP + if (IS_SCTP(desc) && p_sctp_getladdrs) { + struct sockaddr *sa; + Uint32 assoc_id; + int n; + ErlDrvSizeT rlen; + + assoc_id = get_int32(buf); + n = p_sctp_getladdrs(desc->s, assoc_id, &sa); + rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize); + if (n > 0) p_sctp_freeladdrs(sa); + return rlen; + } +#endif + { /* Fallback to sock_name */ + inet_address addr; + unsigned int sz; + int i; + + sz = sizeof(addr); + i = sock_name(desc->s, (struct sockaddr *) &addr, &sz); + return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize); + } + } + case INET_REQ_NAME: { /* get sockname */ char tbuf[sizeof(inet_address)]; inet_address name; @@ -7771,7 +8599,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (IS_SOCKET_ERROR(sock_name(desc->s, (struct sockaddr*)ptr, &sz))) return ctl_error(sock_errno(), rbuf, rsize); } - if (inet_get_address(desc->sfamily, tbuf, ptr, &sz) < 0) + if (inet_get_address(tbuf, ptr, &sz) < 0) return ctl_error(EINVAL, rbuf, rsize); return ctl_reply(INET_REP_OK, tbuf, sz, rbuf, rsize); } @@ -7813,8 +8641,8 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, desc->state = INET_STATE_BOUND; if ((port = inet_address_port(&local)) == 0) { - len = sizeof(local); - sock_name(desc->s, (struct sockaddr*) &local, (unsigned int*)&len); + SOCKLEN_T adrlen = sizeof(local); + sock_name(desc->s, &local.sa, &adrlen); port = inet_address_port(&local); } port = sock_ntohs(port); @@ -7833,24 +8661,30 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (!IS_CONNECTED(desc)) return ctl_error(ENOTCONN, rbuf, rsize); - if (!desc->stype == SOCK_STREAM) + if (desc->stype != SOCK_STREAM) return ctl_error(EINVAL, rbuf, rsize); if (*buf == 1 && !desc->is_ignored) { sock_select(desc, (FD_READ|FD_WRITE|FD_CLOSE|ERL_DRV_USE_NO_CALLBACK), 0); - desc->is_ignored = INET_IGNORE_READ; + if (desc->active) + desc->is_ignored = INET_IGNORE_READ; + else + desc->is_ignored = INET_IGNORE_PASSIVE; } else if (*buf == 0 && desc->is_ignored) { - int flags = (FD_READ|FD_CLOSE|((desc->is_ignored & INET_IGNORE_WRITE)?FD_WRITE:0)); + int flags = FD_CLOSE; + if (desc->is_ignored & INET_IGNORE_READ) + flags |= FD_READ; + if (desc->is_ignored & INET_IGNORE_WRITE) + flags |= FD_WRITE; desc->is_ignored = INET_IGNORE_NONE; - sock_select(desc, flags, 1); + if (flags != FD_CLOSE) + sock_select(desc, flags, 1); } else return ctl_error(EINVAL, rbuf, rsize); return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } -#ifndef VXWORKS - case INET_REQ_GETSERVBYNAME: { /* L1 Name-String L2 Proto-String */ char namebuf[256]; char protobuf[256]; @@ -7901,8 +8735,6 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, return ctl_reply(INET_REP_OK, srv->s_name, len, rbuf, rsize); } -#endif /* !VXWORKS */ - default: return ctl_xerror(EXBADPORT, rbuf, rsize); } @@ -7912,14 +8744,19 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, static void inet_output_count(inet_descriptor* desc, ErlDrvSizeT len) { unsigned long n = desc->send_cnt + 1; - unsigned long t = desc->send_oct[0] + len; +#ifndef ARCH_64 + Uint32 t = desc->send_oct[0] + len; int c = (t < desc->send_oct[0]); +#endif double avg = desc->send_avg; - /* at least 64 bit octet count */ +#ifdef ARCH_64 + desc->send_oct += len; +#else + /* 64 bit octet count in 32 bit words */ desc->send_oct[0] = t; desc->send_oct[1] += c; - +#endif if (n == 0) /* WRAP, use old avg as input to a new sequence */ n = 1; desc->send_avg += (len - avg) / n; @@ -7932,14 +8769,20 @@ static void inet_output_count(inet_descriptor* desc, ErlDrvSizeT len) static void inet_input_count(inet_descriptor* desc, ErlDrvSizeT len) { unsigned long n = desc->recv_cnt + 1; - unsigned long t = desc->recv_oct[0] + len; +#ifndef ARCH_64 + Uint32 t = (desc->recv_oct[0] + len); int c = (t < desc->recv_oct[0]); +#endif double avg = desc->recv_avg; double dvi; - /* at least 64 bit octet count */ +#ifdef ARCH_64 + desc->recv_oct += len; +#else + /* 64 bit octet count in 32 bit words */ desc->recv_oct[0] = t; desc->recv_oct[1] += c; +#endif if (n == 0) /* WRAP */ n = 1; @@ -8081,7 +8924,7 @@ static int tcp_inet_init(void) /* initialize the TCP descriptor */ -static ErlDrvData tcp_inet_start(ErlDrvPort port, char* args) +static ErlDrvData prep_tcp_inet_start(ErlDrvPort port, char* args) { tcp_descriptor* desc; DEBUGF(("tcp_inet_start(%ld) {\r\n", (long)port)); @@ -8108,6 +8951,12 @@ static ErlDrvData tcp_inet_start(ErlDrvPort port, char* args) return (ErlDrvData) desc; } +static ErlDrvData tcp_inet_start(ErlDrvPort port, char* args) +{ + ErlDrvData data = prep_tcp_inet_start(port, args); + set_default_msgq_limits(port); + return data; +} /* Copy a descriptor, by creating a new port with same settings * as the descriptor desc. * return NULL on error (SYSTEM_LIMIT no ports avail) @@ -8115,10 +8964,11 @@ static ErlDrvData tcp_inet_start(ErlDrvPort port, char* args) static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s, ErlDrvTermData owner, int* err) { + ErlDrvSizeT q_low, q_high; ErlDrvPort port = desc->inet.port; tcp_descriptor* copy_desc; - copy_desc = (tcp_descriptor*) tcp_inet_start(port, NULL); + copy_desc = (tcp_descriptor*) prep_tcp_inet_start(port, NULL); /* Setup event if needed */ if ((copy_desc->inet.s = s) != INVALID_SOCKET) { @@ -8133,7 +8983,6 @@ static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s, /* Some flags must be inherited at this point */ copy_desc->inet.mode = desc->inet.mode; copy_desc->inet.exitf = desc->inet.exitf; - copy_desc->inet.bit8f = desc->inet.bit8f; copy_desc->inet.deliver = desc->inet.deliver; copy_desc->inet.htype = desc->inet.htype; copy_desc->inet.psize = desc->inet.psize; @@ -8153,8 +9002,20 @@ static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s, FREE(copy_desc); return NULL; } + + /* Read busy msgq limits of parent */ + q_low = q_high = ERL_DRV_BUSY_MSGQ_READ_ONLY; + erl_drv_busy_msgq_limits(desc->inet.port, &q_low, &q_high); + /* Write same busy msgq limits to child */ + erl_drv_busy_msgq_limits(port, &q_low, &q_high); + copy_desc->inet.port = port; copy_desc->inet.dport = driver_mk_port(port); + +#ifdef __OSE__ + inet_select_init(©_desc->inet); +#endif + *err = 0; return copy_desc; } @@ -8185,7 +9046,7 @@ static void tcp_close_check(tcp_descriptor* desc) desc->inet.state = INET_STATE_LISTENING; while (deq_multi_op(desc,&id,&req,&caller,NULL,&monitor) == 0) { driver_demonitor_process(desc->inet.port, &monitor); - send_async_error(desc->inet.port, desc->inet.dport, id, caller, am_closed); + send_async_error(desc->inet.dport, id, caller, am_closed); } clean_multi_timers(&(desc->mtd), desc->inet.port); } @@ -8216,8 +9077,22 @@ static void tcp_inet_stop(ErlDrvData e) inet_stop(INETP(desc)); } +#ifdef __OSE__ - +static ErlDrvSSizeT tcp_inet_ctl_ose(ErlDrvData e, unsigned int cmd, + char* buf, ErlDrvSizeT len, + char** rbuf, ErlDrvSizeT rsize) { + + tcp_descriptor* desc = (tcp_descriptor*)e; + int prev_select_state = INETP(desc)->select_state; + + ErlDrvSSizeT res = tcp_inet_ctl(e,cmd,buf,len,rbuf,rsize); + + tcp_inet_ose_dispatch_signals((tcp_descriptor*)e,prev_select_state,NULL); + + return res; +} +#endif /* TCP requests from Erlang */ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, @@ -8252,10 +9127,11 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, break; } - case INET_REQ_FDOPEN: { /* pass in an open socket */ + case INET_REQ_FDOPEN: { /* pass in an open (and optionally bound) socket */ int domain; + int bound; DEBUGF(("tcp_inet_ctl(%ld): FDOPEN\r\n", (long)desc->inet.port)); - if (len != 6) return ctl_error(EINVAL, rbuf, rsize); + if (len != 6 && len != 10) return ctl_error(EINVAL, rbuf, rsize); switch(buf[0]) { case INET_AF_INET: domain = AF_INET; @@ -8273,8 +9149,13 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_error(EINVAL, rbuf, rsize); } if (buf[1] != INET_TYPE_STREAM) return ctl_error(EINVAL, rbuf, rsize); + + if (len == 6) bound = 1; + else bound = get_int32(buf+2+4); + return inet_ctl_fdopen(INETP(desc), domain, SOCK_STREAM, - (SOCKET) get_int32(buf+2), rbuf, rsize); + (SOCKET) get_int32(buf+2), + bound, rbuf, rsize); break; } @@ -8433,7 +9314,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, ErlDrvTermData caller = driver_caller(desc->inet.port); tcp_descriptor* accept_desc; int err; - + if ((accept_desc = tcp_inet_copy(desc,s,caller,&err)) == NULL) { sock_close(s); return ctl_error(err, rbuf, rsize); @@ -8467,7 +9348,8 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, char tbuf[2]; int n; - DEBUGF(("tcp_inet_ctl(%ld): RECV\r\n", (long)desc->inet.port)); + DEBUGF(("tcp_inet_ctl(%ld): RECV (s=%d)\r\n", + (long)desc->inet.port, desc->inet.s)); /* INPUT: Timeout(4), Length(4) */ if (!IS_CONNECTED(INETP(desc))) { if (desc->tcp_add_flags & TCP_ADDF_DELAYED_CLOSE_RECV) { @@ -8499,6 +9381,8 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, driver_set_timer(desc->inet.port, timeout); if (!INETP(desc)->is_ignored) sock_select(INETP(desc),(FD_READ|FD_CLOSE),1); + else + INETP(desc)->is_ignored |= INET_IGNORE_READ; } } return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize); @@ -8609,7 +9493,7 @@ static void tcp_inet_multi_timeout(ErlDrvData e, ErlDrvTermData caller) sock_select(INETP(desc),FD_ACCEPT,0); desc->inet.state = INET_STATE_LISTENING; /* restore state */ } - send_async_error(desc->inet.port, desc->inet.dport, id, caller, am_timeout); + send_async_error(desc->inet.dport, id, caller, am_timeout); } @@ -8637,6 +9521,16 @@ static void tcp_inet_command(ErlDrvData e, char *buf, ErlDrvSizeT len) DEBUGF(("tcp_inet_command(%ld) }\r\n", (long)desc->inet.port)); } +#ifdef __OSE__ + +static void tcp_inet_commandv_ose(ErlDrvData e, ErlIOVec* ev) { + int prev_select_state = INETP((tcp_descriptor*)e)->select_state; + tcp_inet_commandv(e, ev); + tcp_inet_ose_dispatch_signals((tcp_descriptor*)e,prev_select_state,NULL); +} + +#endif + static void tcp_inet_commandv(ErlDrvData e, ErlIOVec* ev) { @@ -8700,6 +9594,22 @@ static void inet_stop_select(ErlDrvEvent event, void* _) { #ifdef __WIN32__ WSACloseEvent((HANDLE)event); +#elif defined(__OSE__) + ErlDrvOseEventId id; + union SIGNAL *sig; + erl_drv_ose_event_fetch(event, NULL, &id,NULL); + DEBUGF(("inet_stop_select(?#?) {s=%d\n",id)); + sock_close((int)id); + /* On socket close all the signals waiting to be processed as part of the + select should be deallocated */ + while((sig = erl_drv_ose_get_signal(event))) { + DEBUGF(("inet_stop_select(?#?): Freeing signal %s\n", + signo_to_string(sig->signo))); + free_buf(&sig); + } + erl_drv_ose_event_free(event); + DEBUGF(("inet_stop_select(?#?) }\n")); + #else sock_close((SOCKET)(long)event); #endif @@ -8789,7 +9699,7 @@ static int tcp_recv_error(tcp_descriptor* desc, int err) if (desc->inet.exitf) driver_exit(desc->inet.port, err); else - desc_close(INETP(desc)); + desc_close_read(INETP(desc)); } return -1; } @@ -8819,12 +9729,13 @@ static int tcp_remain(tcp_descriptor* desc, int* len) int n = desc->i_ptr - ptr; /* number of bytes read */ int tlen; - DEBUGF(("tcp_remain(%ld): s=%d, n=%d, nfill=%d nsz=%d\r\n", - (long)desc->inet.port, desc->inet.s, n, nfill, nsz)); - tlen = packet_get_length(desc->inet.htype, ptr, n, desc->inet.psize, desc->i_bufsz, &desc->http_state); + + DEBUGF(("tcp_remain(%ld): s=%d, n=%d, nfill=%d nsz=%d, tlen %d\r\n", + (long)desc->inet.port, desc->inet.s, n, nfill, nsz, tlen)); + if (tlen > 0) { if (tlen <= n) { /* got a packet */ *len = tlen; @@ -9232,7 +10143,146 @@ static void tcp_inet_event(ErlDrvData e, ErlDrvEvent event) return; } -#endif /* WIN32 */ +#elif defined(__OSE__) /* !__WIN32__ */ +/* The specific resolve signal function. It will return the socket descriptor + for which the select was issued */ +static ErlDrvOseEventId inet_resolve_signal(union SIGNAL *sig) { + DEBUGF(("%s(?#?): s=%d got signal %s, status = %d, extra = %d, sender = 0x%x\n", + __FUNCTION__,sig->async.fd,signo_to_string(sig->signo), + sig->async.event.status, + sig->async.event.extra,sender(&sig))); + if (sig->signo == SO_EVENT_READ_REPLY || + sig->signo == SO_EVENT_ACCEPT_REPLY || + sig->signo == SO_EVENT_EOF_REPLY || + sig->signo == SO_EVENT_WRITE_REPLY || + sig->signo == SO_EVENT_ERROR_REPLY || + sig->signo == SO_EVENT_CONNECT_REPLY ) { + return sig->async.fd; + } + + return -1; +} + +static void inet_driver_select(inet_descriptor* desc, + int flags, int onoff) { + ASSERT(!desc->is_ignored); + + if(onoff) { + desc->select_state |= flags; + } else { + desc->select_state &= ~flags; + } +} + +static ssize_t writev_fallback(int fd, const struct iovec *iov, int iovcnt, int max_sz) +{ + size_t data_len = 0; + size_t sent = 0; + ssize_t n; + int i; + + for(i = 0; i < iovcnt; i++) + { + data_len = iov[i].iov_len; +tryagain: + n = sock_send(fd, iov[i].iov_base, data_len, 0); + if (IS_SOCKET_ERROR(n)) { + /* If buffer length is greater than the amount stack is able to + * send out then try to send at least max_sz (this comes with + * SO_EVENT_WRITE_REPLY signal*/ + if ((errno == EMSGSIZE) && (max_sz > 0) && (data_len > max_sz)) { + data_len = max_sz; + goto tryagain; + } + break; + } + sent += n; + } + return sent; +} + +#define OSE_EVENT_REQ(TCP_DESC,EVENT) do { \ + union SIGNAL *sig = alloc(sizeof(struct OseAsyncSig), EVENT); \ + sig->async.fd = INETP(TCP_DESC)->s; \ + ose_request_event(INETP(TCP_DESC)->s, &sig, 1); \ + DEBUGF(("%s(%ld): s=%d sent %s\r\n",__FUNCTION__, \ + INETP(TCP_DESC)->port,INETP(TCP_DESC)->s,signo_to_string(EVENT))); \ + } while(0) + +static void tcp_inet_ose_dispatch_signals(tcp_descriptor *desc, + int prev_select_state, + union SIGNAL *sig) { + if (sig) { + DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) {s=%d resend\r\n", + (long)INETP(desc)->port,INETP(desc)->s)); + /* We are reacting to a signal, which means that if + the select_state for that signal is still activated + we should send a new signal */ + switch (sig->signo) { + case SO_EVENT_READ_REPLY: { + if (INETP(desc)->select_state & FD_READ) + OSE_EVENT_REQ(desc,SO_EVENT_READ_REQUEST); + break; + } + case SO_EVENT_WRITE_REPLY: { + if (INETP(desc)->select_state & FD_WRITE) + OSE_EVENT_REQ(desc,SO_EVENT_WRITE_REQUEST); + break; + } + case SO_EVENT_CONNECT_REPLY: { + if (INETP(desc)->select_state & FD_CONNECT) + OSE_EVENT_REQ(desc,SO_EVENT_CONNECT_REQUEST); + break; + } + case SO_EVENT_ACCEPT_REPLY: { + if (INETP(desc)->select_state & FD_ACCEPT) + OSE_EVENT_REQ(desc,SO_EVENT_ACCEPT_REQUEST); + break; + } + case SO_EVENT_ERROR_REPLY: { + if (INETP(desc)->select_state & SOCK_FD_ERROR) + OSE_EVENT_REQ(desc,SO_EVENT_ERROR_REQUEST); + break; + } + + } + DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) }\r\n", + (long)INETP(desc)->port)); + } + + if (INETP(desc)->select_state != prev_select_state) { + /* If the select state has changed we have to issue signals for + the state parts that have changed. */ + int xor_select_state = INETP(desc)->select_state ^ prev_select_state; + DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) {s=%d select change\r\n", + (long)INETP(desc)->port,INETP(desc)->s)); + if ((xor_select_state & FD_READ) && + (INETP(desc)->select_state & FD_READ)) { + OSE_EVENT_REQ(desc,SO_EVENT_READ_REQUEST); + } + if ((xor_select_state & FD_WRITE) && + (INETP(desc)->select_state & FD_WRITE)) { + OSE_EVENT_REQ(desc,SO_EVENT_WRITE_REQUEST); + } + if ((xor_select_state & FD_CONNECT) && + (INETP(desc)->select_state & FD_CONNECT)) { + OSE_EVENT_REQ(desc,SO_EVENT_CONNECT_REQUEST); + } + if ((xor_select_state & FD_ACCEPT) && + (INETP(desc)->select_state & FD_ACCEPT)) { + OSE_EVENT_REQ(desc,SO_EVENT_ACCEPT_REQUEST); + } + if ((xor_select_state & SOCK_FD_ERROR) && + (INETP(desc)->select_state & SOCK_FD_ERROR)) { + OSE_EVENT_REQ(desc,SO_EVENT_ERROR_REQUEST); + } + + DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) }\r\n", + (long)INETP(desc)->port)); + } +} + +#endif /* __OSE__ */ /* socket has input: @@ -9253,7 +10303,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) unsigned int len; inet_address remote; inet_async_op *this_op = desc->inet.opt; - + len = sizeof(desc->inet.remote); s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &len); if (s == INVALID_SOCKET && sock_errno() == ERRNO_BLOCK) { @@ -9322,7 +10372,6 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) while (desc->inet.state == INET_STATE_MULTI_ACCEPTING) { len = sizeof(desc->inet.remote); s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &len); - if (s == INVALID_SOCKET && sock_errno() == ERRNO_BLOCK) { /* Just try again, no real error, keep the last return code */ goto done; @@ -9350,7 +10399,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) if (s == INVALID_SOCKET) { /* Not ERRNO_BLOCK, that's handled right away */ - ret = send_async_error(desc->inet.port, desc->inet.dport, + ret = send_async_error(desc->inet.dport, id, caller, error_atom(sock_errno())); goto done; } @@ -9360,7 +10409,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) if ((accept_desc = tcp_inet_copy(desc,s,caller,&err)) == NULL) { sock_close(s); - ret = send_async_error(desc->inet.port, desc->inet.dport, + ret = send_async_error(desc->inet.dport, id, caller, error_atom(err)); goto done; } @@ -9371,7 +10420,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) ERL_DRV_READ, 1); #endif accept_desc->inet.state = INET_STATE_CONNECTED; - ret = send_async_ok_port(desc->inet.port, desc->inet.dport, + ret = send_async_ok_port(desc->inet.dport, id, caller, accept_desc->inet.dport); } } @@ -9648,6 +10697,49 @@ static int tcp_send(tcp_descriptor* desc, char* ptr, ErlDrvSizeT len) return 0; } +#ifdef __OSE__ + +static void tcp_inet_drv_output_ose(ErlDrvData data, ErlDrvEvent event) +{ + union SIGNAL *event_sig = erl_drv_ose_get_signal(event); + + while (event_sig) { + int prev_select_state = INETP((tcp_descriptor*)data)->select_state; + int res = tcp_inet_output((tcp_descriptor*)data, (HANDLE)event_sig); + if (res != -1) { + tcp_inet_ose_dispatch_signals((tcp_descriptor*)data, + prev_select_state,event_sig); + free_buf(&event_sig); + event_sig = erl_drv_ose_get_signal(event); + } else { + /* NOTE: here the event object could have been deallocated!!!! + inet_stop_select is called when doing driver_select(ERL_DRV_USE,0) + */ + free_buf(&event_sig); + return; + } + } +} + +static void tcp_inet_drv_input_ose(ErlDrvData data, ErlDrvEvent event) +{ + union SIGNAL *event_sig = erl_drv_ose_get_signal(event); + + while (event_sig) { + int prev_select_state = INETP((tcp_descriptor*)data)->select_state; + int res = tcp_inet_input((tcp_descriptor*)data, (HANDLE)event); + if (res != -1) { + tcp_inet_ose_dispatch_signals((tcp_descriptor*)data, prev_select_state, + event_sig); + free_buf(&event_sig); + event_sig = erl_drv_ose_get_signal(event); + } else { + free_buf(&event_sig); + return; + } + } +} +#else static void tcp_inet_drv_output(ErlDrvData data, ErlDrvEvent event) { (void)tcp_inet_output((tcp_descriptor*)data, (HANDLE)event); @@ -9657,6 +10749,7 @@ static void tcp_inet_drv_input(ErlDrvData data, ErlDrvEvent event) { (void)tcp_inet_input((tcp_descriptor*)data, (HANDLE)event); } +#endif /* socket ready for ouput: ** 1. INET_STATE_CONNECTING => non block connect ? @@ -9722,6 +10815,13 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) ssize_t n; SysIOVec* iov; +#ifdef __OSE__ + /* For large size buffers case the amount of data that the stack is + able to send out (received in the .extra field) should be passed + down to writev_fallback */ + n = event ? ((union SIGNAL*)event)->async.event.extra : 0; +#endif + if ((iov = driver_peekq(ix, &vsize)) == NULL) { sock_select(INETP(desc), FD_WRITE, 0); send_empty_out_q_msgs(INETP(desc)); @@ -9731,9 +10831,10 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) DEBUGF(("tcp_inet_output(%ld): s=%d, About to send %d items\r\n", (long)desc->inet.port, desc->inet.s, vsize)); if (IS_SOCKET_ERROR(sock_sendv(desc->inet.s, iov, vsize, &n, 0))) { + write_error: if ((sock_errno() != ERRNO_BLOCK) && (sock_errno() != EINTR)) { - DEBUGF(("tcp_inet_output(%ld): sock_sendv(%d) errno = %d\r\n", - (long)desc->inet.port, vsize, sock_errno())); + DEBUGF(("tcp_inet_output(%ld): sock_sendv(%d) errno = %d (errno %d)\r\n", + (long)desc->inet.port, vsize, sock_errno(), errno)); ret = tcp_send_error(desc, sock_errno()); goto done; } @@ -9741,6 +10842,28 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) desc->inet.send_would_block = 1; #endif goto done; + } else if (n == 0) { /* Workaround for redhat/CentOS 6.3 returning + 0 when sending packets with + sizes > (max 32 bit signed int) */ + size_t howmuch = 0x7FFFFFFF; /* max signed 32 bit */ + int x; +#ifdef __OSE__ + /* For EWOULDBLOCK sock_sendv returns 0 so we have to be sure it + wasn't the case */ + if(sock_errno() == ERRNO_BLOCK) + goto done; +#endif + for(x = 0; x < vsize && iov[x].iov_len == 0; ++x) + ; + if (x < vsize) { + if (howmuch > iov[x].iov_len) { + howmuch = iov[x].iov_len; + } + n = sock_send(desc->inet.s, iov[x].iov_base,howmuch,0); + if (IS_SOCKET_ERROR(n)) { + goto write_error; + } + } } if (driver_deq(ix, n) <= desc->low) { if (IS_BUSY(INETP(desc))) { @@ -9818,12 +10941,15 @@ static int should_use_so_bsdcompat(void) * as the descriptor desc. * return NULL on error (ENFILE no ports avail) */ +static ErlDrvData packet_inet_start(ErlDrvPort port, char* args, int protocol); + static udp_descriptor* sctp_inet_copy(udp_descriptor* desc, SOCKET s, int* err) { + ErlDrvSizeT q_low, q_high; ErlDrvPort port = desc->inet.port; udp_descriptor* copy_desc; - copy_desc = (udp_descriptor*) sctp_inet_start(port, NULL); + copy_desc = (udp_descriptor*) packet_inet_start(port, NULL, IPPROTO_SCTP); /* Setup event if needed */ if ((copy_desc->inet.s = s) != INVALID_SOCKET) { @@ -9838,7 +10964,6 @@ static udp_descriptor* sctp_inet_copy(udp_descriptor* desc, SOCKET s, int* err) /* Some flags must be inherited at this point */ copy_desc->inet.mode = desc->inet.mode; copy_desc->inet.exitf = desc->inet.exitf; - copy_desc->inet.bit8f = desc->inet.bit8f; copy_desc->inet.deliver = desc->inet.deliver; copy_desc->inet.htype = desc->inet.htype; copy_desc->inet.psize = desc->inet.psize; @@ -9855,15 +10980,24 @@ static udp_descriptor* sctp_inet_copy(udp_descriptor* desc, SOCKET s, int* err) FREE(copy_desc); return NULL; } + + /* Read busy msgq limits of parent */ + q_low = q_high = ERL_DRV_BUSY_MSGQ_READ_ONLY; + erl_drv_busy_msgq_limits(desc->inet.port, &q_low, &q_high); + /* Write same busy msgq limits to child */ + erl_drv_busy_msgq_limits(port, &q_low, &q_high); + copy_desc->inet.port = port; copy_desc->inet.dport = driver_mk_port(port); *err = 0; + return copy_desc; } #endif +#ifdef HAVE_UDP static int packet_inet_init() { return 0; @@ -9890,16 +11024,22 @@ static ErlDrvData packet_inet_start(ErlDrvPort port, char* args, int protocol) static ErlDrvData udp_inet_start(ErlDrvPort port, char *args) { - return packet_inet_start(port, args, IPPROTO_UDP); + ErlDrvData data = packet_inet_start(port, args, IPPROTO_UDP); + set_default_msgq_limits(port); + return data; } +#endif #ifdef HAVE_SCTP static ErlDrvData sctp_inet_start(ErlDrvPort port, char *args) { - return packet_inet_start(port, args, IPPROTO_SCTP); + ErlDrvData data = packet_inet_start(port, args, IPPROTO_SCTP); + set_default_msgq_limits(port); + return data; } #endif +#ifdef HAVE_UDP static void packet_inet_stop(ErlDrvData e) { /* There should *never* be any "empty out q" subscribers on @@ -9988,10 +11128,11 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return replen; - case INET_REQ_FDOPEN: { /* pass in an open (and bound) socket */ + case INET_REQ_FDOPEN: { /* pass in an open (and optionally bound) socket */ SOCKET s; + int bound; DEBUGF(("packet inet_ctl(%ld): FDOPEN\r\n", (long)desc->port)); - if (len != 6) { + if (len != 6 && len != 10) { return ctl_error(EINVAL, rbuf, rsize); } switch (buf[0]) { @@ -10016,7 +11157,11 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return ctl_error(EINVAL, rbuf, rsize); } s = (SOCKET)get_int32(buf+2); - replen = inet_ctl_fdopen(desc, af, type, s, rbuf, rsize); + + if (len == 6) bound = 1; + else bound = get_int32(buf+2+4); + + replen = inet_ctl_fdopen(desc, af, type, s, bound, rbuf, rsize); if ((*rbuf)[0] != INET_REP_ERROR) { if (desc->active) @@ -10240,6 +11385,7 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, } new_udesc->inet.state = INET_STATE_CONNECTED; new_udesc->inet.stype = SOCK_STREAM; + SET_NONBLOCKING(new_udesc->inet.s); inet_reply_ok_port(desc, new_udesc->inet.dport); (*rbuf)[0] = INET_REP; @@ -10409,7 +11555,7 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) else inet_reply_ok(desc); } - +#endif #ifdef __WIN32__ static void packet_inet_event(ErlDrvData e, ErlDrvEvent event) @@ -10431,6 +11577,7 @@ static void packet_inet_event(ErlDrvData e, ErlDrvEvent event) #endif +#ifdef HAVE_UDP static void packet_inet_drv_input(ErlDrvData e, ErlDrvEvent event) { (void) packet_inet_input((udp_descriptor*)e, (HANDLE)event); @@ -10558,7 +11705,7 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event) inet_input_count(desc, n); udesc->i_ptr += n; - inet_get_address(desc->sfamily, abuf, &other, &len); + inet_get_address(abuf, &other, &len); /* Copy formatted address to the buffer allocated; "len" is the actual length which must be <= than the original reserved. This means that the addr + data in the buffer are contiguous, @@ -10687,6 +11834,7 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event) DEBUGF(("packet_inet_output(%ld) }\r\n", (long)desc->port)); return ret; } +#endif /*---------------------------------------------------------------------------*/ @@ -11013,7 +12161,7 @@ subs_list *subs; static void send_to_subscribers ( - ErlDrvPort port, + ErlDrvTermData port, subs_list *subs, int free_subs, ErlDrvTermData msg[], @@ -11030,7 +12178,7 @@ static void send_to_subscribers this = subs; while(this) { - (void) driver_send_term(port, this->subscriber, msg, msg_len); + (void) erl_drv_send_term(port, this->subscriber, msg, msg_len); if(free_subs && !first) { next = this->next; diff --git a/erts/emulator/drivers/common/ram_file_drv.c b/erts/emulator/drivers/common/ram_file_drv.c index a109e40333..7f7cd7cd91 100644 --- a/erts/emulator/drivers/common/ram_file_drv.c +++ b/erts/emulator/drivers/common/ram_file_drv.c @@ -48,6 +48,7 @@ #define RAM_FILE_SIZE 37 /* get file size */ #define RAM_FILE_ADVISE 38 /* predeclare the access * pattern for file data */ +#define RAM_FILE_ALLOCATE 39 /* allocate space for a file */ /* possible new operations include: DES_ENCRYPT DES_DECRYPT @@ -720,6 +721,13 @@ static void rfile_command(ErlDrvData e, char* buf, ErlDrvSizeT count) else reply(f, 1, 0); break; + + case RAM_FILE_ALLOCATE: + if (f->flags == 0) + error_reply(f, EBADF); + else + reply(f, 1, 0); + break; } /* * Ignore anything else -- let the caller hang. diff --git a/erts/emulator/drivers/common/zlib_drv.c b/erts/emulator/drivers/common/zlib_drv.c index 60394b610b..3143e4511d 100644 --- a/erts/emulator/drivers/common/zlib_drv.c +++ b/erts/emulator/drivers/common/zlib_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2012. All Rights Reserved. + * Copyright Ericsson AB 2003-2013. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -21,6 +21,9 @@ * ZLib interface for erlang * */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif #include <stdio.h> #include <zlib.h> #include <errno.h> @@ -119,7 +122,7 @@ static int zlib_deflate(ZLibData* d, int flush); #if defined(__WIN32__) static int i32(char* buf) #else -static inline int i32(char* buf) +static __inline__ int i32(char* buf) #endif { return (int) ( diff --git a/erts/emulator/drivers/ose/ose_efile.c b/erts/emulator/drivers/ose/ose_efile.c new file mode 100644 index 0000000000..035ff81a9b --- /dev/null +++ b/erts/emulator/drivers/ose/ose_efile.c @@ -0,0 +1,1124 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Purpose: Provides file and directory operations for OSE. + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if defined(HAVE_POSIX_FALLOCATE) && !defined(__sun) && !defined(__sun__) +#define _XOPEN_SOURCE 600 +#endif +#if !defined(_GNU_SOURCE) && defined(HAVE_LINUX_FALLOC_H) +#define _GNU_SOURCE +#endif +#include "sys.h" +#include "erl_driver.h" +#include "erl_efile.h" +#if defined(DARWIN) || defined(HAVE_LINUX_FALLOC_H) || defined(HAVE_POSIX_FALLOCATE) +#include "fcntl.h" +#endif +#include "ose.h" +#include "unistd.h" +#include "sys/stat.h" +#include "dirent.h" +#include "sys/time.h" +#include "time.h" +#include "assert.h" + +/* Find a definition of MAXIOV, that is used in the code later. */ +#if defined IOV_MAX +#define MAXIOV IOV_MAX +#elif defined UIO_MAXIOV +#define MAXIOV UIO_MAXIOV +#else +#define MAXIOV 16 +#endif + +/* + * Macros for testing file types. + */ + +#define ISDIR(st) (((st).st_mode & S_IFMT) == S_IFDIR) +#define ISREG(st) (((st).st_mode & S_IFMT) == S_IFREG) +#define ISDEV(st) \ + (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK) +#define ISLNK(st) (((st).st_mode & S_IFLNK) == S_IFLNK) +#ifdef NO_UMASK +#define FILE_MODE 0644 +#define DIR_MODE 0755 +#else +#define FILE_MODE 0666 +#define DIR_MODE 0777 +#endif + +#define IS_DOT_OR_DOTDOT(s) \ + (s[0] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0'))) + +/* + * Macros for handling local file descriptors + * and mutexes. + * + * Handling of files like this is necessary because OSE + * does not allow seeking after the end of a file. So + * what we do it emulate this by keeping track of the size + * of the file and where the file's positions is. If a + * write happens after eof then we pad it. + * + * Given time this should be rewritten to get rid of the + * mutex and use the port lock to protect the data. This + * could be done be done by adapting the efile api for some + * calls to allow some meta-data to be associated with the + * open file. + */ + +#define L_FD_IS_VALID(fd_data) ((fd_data)->beyond_eof > 0) +#define L_FD_INVALIDATE(fd_data) (fd_data)->beyond_eof = 0 +#define L_FD_CUR(fd_data) (fd_data)->pos +#define L_FD_OFFS_BEYOND_EOF(fd_data, offs) \ + (((fd_data)->size > offs) ? 0 : 1) + +#define L_FD_FAIL -1 +#define L_FD_SUCCESS 1 +#define L_FD_PAD_SIZE 255 + +struct fd_meta { + ErlDrvMutex *meta_mtx; + struct fd_data *fd_data_list; +}; + +struct fd_data { + int fd; + struct fd_data *next; + struct fd_data *prev; + int pos; + int beyond_eof; + size_t size; +#ifdef DEBUG + PROCESS owner; +#endif +}; + +static int l_invalidate_local_fd(int fd); +static int l_pad_file(struct fd_data *fd_data, off_t offset); +static int check_error(int result, Efile_error* errInfo); +static struct fd_data* l_new_fd(void); +static int l_remove_local_fd(int fd); +static struct fd_data* l_find_local_fd(int fd); +static int l_update_local_fd(int fd, int pos, int size); + +static struct fd_meta* fdm = NULL; + + +/***************************************************************************/ + +static int +l_remove_local_fd(int fd) +{ + struct fd_data *fd_data; + fd_data = l_find_local_fd(fd); + + if (fd_data == NULL) { + return L_FD_FAIL; + } +#ifdef DEBUG + assert(fd_data->owner == current_process()); +#endif + erl_drv_mutex_lock(fdm->meta_mtx); + /* head ? */ + if (fd_data == fdm->fd_data_list) { + if (fd_data->next != NULL) { + /* remove link to head */ + fd_data->next->prev = NULL; + /* set new head */ + fdm->fd_data_list = fd_data->next; + } + else { + /* head is lonely */ + fdm->fd_data_list = NULL; + } + } + else { /* not head */ + if (fd_data->prev == NULL) { + erl_drv_mutex_unlock(fdm->meta_mtx); + return L_FD_FAIL; + } + else { + if (fd_data->next != NULL) { + fd_data->next->prev = fd_data->prev; + fd_data->prev->next = fd_data->next; + } + else { + fd_data->prev->next = NULL; + } + } + } + + /* scramble values */ + fd_data->beyond_eof = -1; + fd_data->next = NULL; + fd_data->prev = NULL; + fd_data->fd = -1; + + /* unlock and clean */ + driver_free(fd_data); + erl_drv_mutex_unlock(fdm->meta_mtx); + + return L_FD_SUCCESS; +} + +/***************************************************************************/ + +static int +l_invalidate_local_fd(int fd) { + struct fd_data *fd_data; + + if ((fd_data = l_find_local_fd(fd)) == NULL) { + return L_FD_FAIL; + } + + fd_data->beyond_eof = 0; + return L_FD_SUCCESS; +} + +/****************************************************************************/ + +static struct fd_data* +l_find_local_fd(int fd) { + struct fd_data *fd_data; + + fd_data = NULL; + erl_drv_mutex_lock(fdm->meta_mtx); + for (fd_data = fdm->fd_data_list; fd_data != NULL; ) { + if (fd_data->fd == fd) { +#ifdef DEBUG + assert(fd_data->owner == current_process()); +#endif + break; + } + fd_data = fd_data->next; + } + erl_drv_mutex_unlock(fdm->meta_mtx); + return fd_data; +} + +/***************************************************************************/ + +static struct fd_data* +l_new_fd(void) { + struct fd_data *fd_data; + + fd_data = driver_alloc(sizeof(struct fd_data)); + if (fd_data == NULL) { + return NULL; + } + erl_drv_mutex_lock(fdm->meta_mtx); + if (fdm->fd_data_list == NULL) { + fdm->fd_data_list = fd_data; + fdm->fd_data_list->prev = NULL; + fdm->fd_data_list->next = NULL; + } + else { + fd_data->next = fdm->fd_data_list; + fdm->fd_data_list = fd_data; + fdm->fd_data_list->prev = NULL; + } +#ifdef DEBUG + fd_data->owner = current_process(); +#endif + erl_drv_mutex_unlock(fdm->meta_mtx); + return fd_data; +} + +/***************************************************************************/ + +static int +l_update_local_fd(int fd, int pos, int size) { + struct fd_data *fd_data = NULL; + + fd_data = l_find_local_fd(fd); + /* new fd to handle? */ + if (fd_data == NULL) { + fd_data = l_new_fd(); + if (fd_data == NULL) { + /* out of memory */ + return L_FD_FAIL; + } + } + fd_data->size = size; + fd_data->pos = pos; + fd_data->fd = fd; + fd_data->beyond_eof = 1; + + return L_FD_SUCCESS; +} + +/***************************************************************************/ + +static int +l_pad_file(struct fd_data *fd_data, off_t offset) { + int size_dif; + int written = 0; + int ret_val = L_FD_SUCCESS; + char padding[L_FD_PAD_SIZE]; + + size_dif = (offset - fd_data->size); + memset(&padding, '\0', L_FD_PAD_SIZE); + + while (size_dif > 0) { + written = write(fd_data->fd, padding, + (size_dif < L_FD_PAD_SIZE) ? + size_dif : L_FD_PAD_SIZE); + if (written < 0 && errno != EINTR && errno != EAGAIN) { + ret_val = -1; + break; + } + size_dif -= written; + } + L_FD_INVALIDATE(fd_data); + return ret_val; +} + +/***************************************************************************/ + +static int +check_error(int result, Efile_error *errInfo) { + if (result < 0) { + errInfo->posix_errno = errInfo->os_errno = errno; + return 0; + } + return 1; +} + +/***************************************************************************/ + +int +efile_init() { + fdm = driver_alloc(sizeof(struct fd_meta)); + if (fdm == NULL) { + return L_FD_FAIL; + } + fdm->meta_mtx = erl_drv_mutex_create("ose_efile local fd mutex\n"); + erl_drv_mutex_lock(fdm->meta_mtx); + fdm->fd_data_list = NULL; + erl_drv_mutex_unlock(fdm->meta_mtx); + return L_FD_SUCCESS; +} + +/***************************************************************************/ + +int +efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */ + char* name) /* Name of directory to create. */ +{ +#ifdef NO_MKDIR_MODE + return check_error(mkdir(name), errInfo); +#else + int res = mkdir(name, DIR_MODE); + if (res < 0 && errno == EINVAL) { + errno = ENOENT; + } + return check_error(res, errInfo); +#endif +} + +/***************************************************************************/ + +int +efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ + char* name) /* Name of directory to delete. */ +{ + if (rmdir(name) == 0) { + return 1; + } + if (errno == ENOTEMPTY) { + errno = EEXIST; + } + if (errno == EEXIST || errno == EINVAL) { + int saved_errno = errno; + struct stat file_stat; + struct stat cwd_stat; + + if(stat(name, &file_stat) != 0) { + errno = ENOENT; + return check_error(-1, errInfo); + } + /* + * The error code might be wrong if this is the current directory. + */ + if (stat(name, &file_stat) == 0 && stat(".", &cwd_stat) == 0 && + file_stat.st_ino == cwd_stat.st_ino && + file_stat.st_dev == cwd_stat.st_dev) { + saved_errno = EACCES; + } + errno = saved_errno; + } + return check_error(-1, errInfo); +} + +/***************************************************************************/ + +int +efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ + char* name) /* Name of file to delete. */ +{ + struct stat statbuf; + + if (stat(name, &statbuf) >= 0) { + /* Do not let unlink() remove directories */ + if (ISDIR(statbuf)) { + errno = EPERM; + return check_error(-1, errInfo); + } + + if (unlink(name) == 0) { + return 1; + } + + if (errno == EISDIR) { + errno = EPERM; + return check_error(-1, errInfo); + } + } + else { + if (errno == EINVAL) { + errno = ENOENT; + return check_error(-1, errInfo); + } + } + return check_error(-1, errInfo); +} + +/* + *--------------------------------------------------------------------------- + * + * Changes the name of an existing file or directory, from src to dst. + * If src and dst refer to the same file or directory, does nothing + * and returns success. Otherwise if dst already exists, it will be + * deleted and replaced by src subject to the following conditions: + * If src is a directory, dst may be an empty directory. + * If src is a file, dst may be a file. + * In any other situation where dst already exists, the rename will + * fail. + * + * Results: + * If the directory was successfully created, returns 1. + * Otherwise the return value is 0 and errno is set to + * indicate the error. Some possible values for errno are: + * + * EACCES: src or dst parent directory can't be read and/or written. + * EEXIST: dst is a non-empty directory. + * EINVAL: src is a root directory or dst is a subdirectory of src. + * EISDIR: dst is a directory, but src is not. + * ENOENT: src doesn't exist, or src or dst is "". + * ENOTDIR: src is a directory, but dst is not. + * EXDEV: src and dst are on different filesystems. + * + * Side effects: + * The implementation of rename may allow cross-filesystem renames, + * but the caller should be prepared to emulate it with copy and + * delete if errno is EXDEV. + * + *--------------------------------------------------------------------------- + */ + +int +efile_rename(Efile_error* errInfo, /* Where to return error codes. */ + char* src, /* Original name. */ + char* dst) /* New name. */ +{ + + /* temporary fix AFM does not recognize ./<file name> + * in destination remove pending on adaption of AFM fix + */ + + char *dot_str; + if (dst != NULL) { + dot_str = strchr(dst, '.'); + if (dot_str && dot_str == dst && dot_str[1] == '/') { + dst = dst+2; + } + } + + if (rename(src, dst) == 0) { + return 1; + } + if (errno == ENOTEMPTY) { + errno = EEXIST; + } + if (errno == EINVAL) { + struct stat file_stat; + + if (stat(dst, &file_stat)== 0) { + if (ISDIR(file_stat)) { + errno = EISDIR; + } + else if (ISREG(file_stat)) { + errno = ENOTDIR; + } + else { + errno = EINVAL; + } + } + else { + errno = EINVAL; + } + } + + if (strcmp(src, "/") == 0) { + errno = EINVAL; + } + return check_error(-1, errInfo); +} + +/***************************************************************************/ + +int +efile_chdir(Efile_error* errInfo, /* Where to return error codes. */ + char* name) /* Name of directory to make current. */ +{ + return check_error(chdir(name), errInfo); +} + +/***************************************************************************/ + +int +efile_getdcwd(Efile_error* errInfo, /* Where to return error codes. */ + int drive, /* 0 - current, 1 - A, 2 - B etc. */ + char* buffer, /* Where to return the current + directory. */ + size_t size) /* Size of buffer. */ +{ + if (drive == 0) { + if (getcwd(buffer, size) == NULL) + return check_error(-1, errInfo); + + return 1; + } + + /* + * Drives other than 0 is not supported on Unix. + */ + + errno = ENOTSUP; + return check_error(-1, errInfo); +} + +/***************************************************************************/ + +int +efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ + char* name, /* Name of directory to open. */ + EFILE_DIR_HANDLE* p_dir_handle, /* Pointer to directory + handle of + open directory.*/ + char* buffer, /* Pointer to buffer for + one filename. */ + size_t *size) /* in-out Size of buffer, length + of name. */ +{ + DIR *dp; /* Pointer to directory structure. */ + struct dirent* dirp; /* Pointer to directory entry. */ + + /* + * If this is the first call, we must open the directory. + */ + + if (*p_dir_handle == NULL) { + dp = opendir(name); + if (dp == NULL) + return check_error(-1, errInfo); + *p_dir_handle = (EFILE_DIR_HANDLE) dp; + } + + /* + * Retrieve the name of the next file using the directory handle. + */ + + dp = *((DIR **)((void *)p_dir_handle)); + for (;;) { + dirp = readdir(dp); + if (dirp == NULL) { + closedir(dp); + return 0; + } + if (IS_DOT_OR_DOTDOT(dirp->d_name)) + continue; + buffer[0] = '\0'; + strncat(buffer, dirp->d_name, (*size)-1); + *size = strlen(dirp->d_name); + return 1; + } +} + +/***************************************************************************/ + +int +efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ + char* name, /* Name of directory to open. */ + int flags, /* Flags to user for opening. */ + int* pfd, /* Where to store the file + descriptor. */ + Sint64 *pSize) /* Where to store the size of the + file. */ +{ + struct stat statbuf; + int fd; + int mode; /* Open mode. */ + + if (stat(name, &statbuf) >= 0 && !ISREG(statbuf)) { + errno = EISDIR; + return check_error(-1, errInfo); + } + + switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) { + case EFILE_MODE_READ: + mode = O_RDONLY; + break; + case EFILE_MODE_WRITE: + if (flags & EFILE_NO_TRUNCATE) + mode = O_WRONLY | O_CREAT; + else + mode = O_WRONLY | O_CREAT | O_TRUNC; + break; + case EFILE_MODE_READ_WRITE: + mode = O_RDWR | O_CREAT; + break; + default: + errno = EINVAL; + return check_error(-1, errInfo); + } + + + if (flags & EFILE_MODE_APPEND) { + mode &= ~O_TRUNC; + mode |= O_APPEND; + } + + if (flags & EFILE_MODE_EXCL) { + mode |= O_EXCL; + } + + fd = open(name, mode, FILE_MODE); + + if (!check_error(fd, errInfo)) + return 0; + + *pfd = fd; + if (pSize) { + *pSize = statbuf.st_size; + } + return 1; +} + +/***************************************************************************/ + +int +efile_may_openfile(Efile_error* errInfo, char *name) { + struct stat statbuf; /* Information about the file */ + int result; + + result = stat(name, &statbuf); + if (!check_error(result, errInfo)) + return 0; + if (!ISREG(statbuf)) { + errno = EISDIR; + return check_error(-1, errInfo); + } + return 1; +} + +/***************************************************************************/ + +void +efile_closefile(int fd) +{ + if (l_find_local_fd(fd) != NULL) { + l_remove_local_fd(fd); + } + close(fd); +} + +/***************************************************************************/ + +int +efile_fdatasync(Efile_error *errInfo, /* Where to return error codes. */ + int fd) /* File descriptor for file to sync data. */ +{ + return efile_fsync(errInfo, fd); +} + +/***************************************************************************/ + +int +efile_fsync(Efile_error *errInfo, /* Where to return error codes. */ + int fd) /* File descriptor for file to sync. */ +{ + return check_error(fsync(fd), errInfo); +} + +/***************************************************************************/ + +int +efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, + char* name, int info_for_link) +{ + struct stat statbuf; /* Information about the file */ + int result; + + result = stat(name, &statbuf); + if (!check_error(result, errInfo)) { + return 0; + } + +#if SIZEOF_OFF_T == 4 + pInfo->size_high = 0; +#else + pInfo->size_high = (Uint32)(statbuf.st_size >> 32); +#endif + pInfo->size_low = (Uint32)statbuf.st_size; + +#ifdef NO_ACCESS + /* Just look at read/write access for owner. */ + + pInfo->access = ((statbuf.st_mode >> 6) & 07) >> 1; + +#else + pInfo->access = FA_NONE; + if (access(name, R_OK) == 0) + pInfo->access |= FA_READ; + if (access(name, W_OK) == 0) + pInfo->access |= FA_WRITE; + +#endif + + if (ISDEV(statbuf)) + pInfo->type = FT_DEVICE; + else if (ISDIR(statbuf)) + pInfo->type = FT_DIRECTORY; + else if (ISREG(statbuf)) + pInfo->type = FT_REGULAR; + else if (ISLNK(statbuf)) + pInfo->type = FT_SYMLINK; + else + pInfo->type = FT_OTHER; + + pInfo->accessTime = statbuf.st_atime; + pInfo->modifyTime = statbuf.st_mtime; + pInfo->cTime = statbuf.st_ctime; + + pInfo->mode = statbuf.st_mode; + pInfo->links = statbuf.st_nlink; + pInfo->major_device = statbuf.st_dev; + pInfo->inode = statbuf.st_ino; + pInfo->uid = statbuf.st_uid; + pInfo->gid = statbuf.st_gid; + + return 1; +} + +/***************************************************************************/ + +int +efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name) +{ + /* + * On some systems chown will always fail for a non-root user unless + * POSIX_CHOWN_RESTRICTED is not set. Others will succeed as long as + * you don't try to chown a file to someone besides youself. + */ + if (pInfo->mode != -1) { + mode_t newMode = pInfo->mode & (S_ISUID | S_ISGID | + S_IRWXU | S_IRWXG | S_IRWXO); + if (chmod(name, newMode)) { + newMode &= ~(S_ISUID | S_ISGID); + if (chmod(name, newMode)) { + return check_error(-1, errInfo); + } + } + } + + return 1; +} + +/***************************************************************************/ + +int +efile_write(Efile_error* errInfo, /* Where to return error codes. */ + int flags, /* Flags given when file was + opened. */ + int fd, /* File descriptor to write to. */ + char* buf, /* Buffer to write. */ + size_t count) /* Number of bytes to write. */ +{ + ssize_t written; /* Bytes written in last operation. */ + struct fd_data *fd_data; + + if ((fd_data = l_find_local_fd(fd)) != NULL) { + if (L_FD_IS_VALID(fd_data)) { + /* we are beyond eof and need to pad*/ + if (l_pad_file(fd_data, L_FD_CUR(fd_data)) < 0) { + return check_error(-1, errInfo); + } + } + } + + while (count > 0) { + if ((written = write(fd, buf, count)) < 0) { + if (errno != EINTR) { + return check_error(-1, errInfo); + } + else { + written = 0; + } + } + ASSERT(written <= count); + buf += written; + count -= written; + } + return 1; +} + +/***************************************************************************/ + +int +efile_writev(Efile_error* errInfo, /* Where to return error codes */ + int flags, /* Flags given when file was + * opened */ + int fd, /* File descriptor to write to */ + SysIOVec* iov, /* Vector of buffer structs. + * The structs may be changed i.e. + * due to incomplete writes */ + int iovcnt) /* Number of structs in vector */ +{ + struct fd_data *fd_data; + int cnt = 0; /* Buffers so far written */ + + ASSERT(iovcnt >= 0); + if ((fd_data = l_find_local_fd(fd)) != NULL) { + if (L_FD_IS_VALID(fd_data)) { + /* we are beyond eof and need to pad*/ + if (l_pad_file(fd_data, L_FD_CUR(fd_data)) < 0) { + return check_error(-1, errInfo); + } + } + } + while (cnt < iovcnt) { + if ((! iov[cnt].iov_base) || (iov[cnt].iov_len <= 0)) { + /* Empty buffer - skip */ + cnt++; + } + else { /* Non-empty buffer */ + ssize_t w; /* Bytes written in this call */ + do { + w = write(fd, iov[cnt].iov_base, iov[cnt].iov_len); + } while (w < 0 && errno == EINTR); + + ASSERT(w <= iov[cnt].iov_len || w == -1); + + if (w < 0) { + return check_error(-1, errInfo); + } + /* Move forward to next buffer to write */ + for (; cnt < iovcnt && w > 0; cnt++) { + if (iov[cnt].iov_base && iov[cnt].iov_len > 0) { + if (w < iov[cnt].iov_len) { + /* Adjust the buffer for next write */ + iov[cnt].iov_len -= w; + iov[cnt].iov_base += w; + w = 0; + break; + } + else { + w -= iov[cnt].iov_len; + } + } + } + ASSERT(w == 0); + } /* else Non-empty buffer */ + } /* while (cnt< iovcnt) */ + return 1; +} + +/***************************************************************************/ + +int +efile_read(Efile_error* errInfo, /* Where to return error codes. */ + int flags, /* Flags given when file was opened. */ + int fd, /* File descriptor to read from. */ + char* buf, /* Buffer to read into. */ + size_t count, /* Number of bytes to read. */ + size_t *pBytesRead) /* Where to return number of + bytes read. */ +{ + ssize_t n; + struct fd_data *fd_data; + + if ((fd_data = l_find_local_fd(fd)) != NULL) { + if (L_FD_IS_VALID(fd_data)) { + *pBytesRead = 0; + return 1; + } + } + for (;;) { + if ((n = read(fd, buf, count)) >= 0) { + break; + } + else if (errno != EINTR) { + return check_error(-1, errInfo); + } + } + if (fd_data != NULL && L_FD_IS_VALID(fd_data)) { + L_FD_INVALIDATE(fd_data); + } + *pBytesRead = (size_t) n; + return 1; +} + +/* pread() and pwrite() */ +/* Some unix systems, notably Solaris has these syscalls */ +/* It is especially nice for i.e. the dets module to have support */ +/* for this, even if the underlying OS dosn't support it, it is */ +/* reasonably easy to work around by first calling seek, and then */ +/* calling read(). */ +/* This later strategy however changes the file pointer, which pread() */ +/* does not do. We choose to ignore this and say that the location */ +/* of the file pointer is undefined after a call to any of the p functions*/ + + +int +efile_pread(Efile_error* errInfo, /* Where to return error codes. */ + int fd, /* File descriptor to read from. */ + Sint64 offset, /* Offset in bytes from BOF. */ + char* buf, /* Buffer to read into. */ + size_t count, /* Number of bytes to read. */ + size_t *pBytesRead) /* Where to return + number of bytes read. */ +{ + int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); + if (res) { + return efile_read(errInfo, 0, fd, buf, count, pBytesRead); + } else { + return res; + } +} + + +/***************************************************************************/ + +int +efile_pwrite(Efile_error* errInfo, /* Where to return error codes. */ + int fd, /* File descriptor to write to. */ + char* buf, /* Buffer to write. */ + size_t count, /* Number of bytes to write. */ + Sint64 offset) /* where to write it */ +{ + int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); + + if (res) { + return efile_write(errInfo, 0, fd, buf, count); + } else { + return res; + } +} + +/***************************************************************************/ + +int +efile_seek(Efile_error* errInfo, /* Where to return error codes. */ + int fd, /* File descriptor to do the seek on. */ + Sint64 offset, /* Offset in bytes from the given + origin. */ + int origin, /* Origin of seek (SEEK_SET, SEEK_CUR, + SEEK_END). */ + Sint64 *new_location) /* Resulting new location in file. */ +{ + off_t off, result; + off = (off_t) offset; + + switch (origin) { + case EFILE_SEEK_SET: + origin = SEEK_SET; + break; + case EFILE_SEEK_CUR: + origin = SEEK_CUR; + break; + case EFILE_SEEK_END: + origin = SEEK_END; + break; + default: + errno = EINVAL; + return check_error(-1, errInfo); + } + + if (off != offset) { + errno = EINVAL; + return check_error(-1, errInfo); + } + + errno = 0; + result = lseek(fd, off, origin); + + if (result >= 0) { + l_invalidate_local_fd(fd); + } + + if (result < 0) + { + if (errno == ENOSYS) { + int size, cur_pos; + + if (off < 0) { + errno = EINVAL; + return check_error(-1, errInfo); + } + + cur_pos = lseek(fd, 0, SEEK_CUR); + size = lseek(fd, 0, SEEK_END); + + if (origin == SEEK_SET) { + result = offset; + } + else if (origin == SEEK_CUR) { + result = offset + cur_pos; + } + else if (origin == SEEK_END) { + result = size + offset; + } + + /* sanity check our result */ + if (size > result) { + return check_error(-1, errInfo); + } + + /* store the data localy */ + l_update_local_fd(fd, result, size); + + /* reset the original file position */ + if (origin != SEEK_END) { + lseek(fd, cur_pos, SEEK_SET); + } + } + else if (errno == 0) { + errno = EINVAL; + } + } + + if (new_location) { + *new_location = result; + } + + return 1; +} + +/***************************************************************************/ + +int +efile_truncate_file(Efile_error* errInfo, int *fd, int flags) +{ + off_t offset; + struct fd_data *fd_data; + + if ((fd_data = l_find_local_fd(*fd)) != NULL && L_FD_IS_VALID(fd_data)) { + offset = L_FD_CUR(fd_data); + } + else { + offset = lseek(*fd, 0, SEEK_CUR); + } + + return check_error(((offset >= 0) && + (ftruncate(*fd, offset) == 0)) ? 1 : -1, errInfo); +} + +/***************************************************************************/ + +int +efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size) +{ + errno = ENOTSUP; + return check_error(-1, errInfo); +} + +/***************************************************************************/ + +int +efile_altname(Efile_error* errInfo, char* name, char* buffer, size_t size) +{ + errno = ENOTSUP; + return check_error(-1, errInfo); +} + +/***************************************************************************/ + +int +efile_link(Efile_error* errInfo, char* old, char* new) +{ + errno = ENOTSUP; + return check_error(-1, errInfo); +} + +/***************************************************************************/ + +int +efile_symlink(Efile_error* errInfo, char* old, char* new) +{ + errno = ENOTSUP; + return check_error(-1, errInfo); +} + +/***************************************************************************/ + +int +efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, + Sint64 length, int advise) +{ + return check_error(posix_fadvise(fd, offset, length, advise), errInfo); +} + +/***************************************************************************/ + +static int +call_posix_fallocate(int fd, Sint64 offset, Sint64 length) +{ + int ret; + + /* + * On Linux and Solaris for example, posix_fallocate() returns + * a positive error number on error and it does not set errno. + * On FreeBSD however (9.0 at least), it returns -1 on error + * and it sets errno. + */ + do { + ret = posix_fallocate(fd, (off_t) offset, (off_t) length); + if (ret > 0) { + errno = ret; + ret = -1; + } + } while (ret != 0 && errno == EINTR); + + return ret; +} + +/***************************************************************************/ + +int +efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length) +{ + return check_error(call_posix_fallocate(fd, offset, length), errInfo); +} diff --git a/erts/emulator/drivers/ose/ose_signal_drv.c b/erts/emulator/drivers/ose/ose_signal_drv.c new file mode 100644 index 0000000000..4929b53856 --- /dev/null +++ b/erts/emulator/drivers/ose/ose_signal_drv.c @@ -0,0 +1,896 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2013-2013. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "errno.h" +#include "stdio.h" +#include "string.h" +#include "stddef.h" + +#include "sys.h" +#include "erl_driver.h" +#include "ose.h" + + +#ifdef HAVE_OSE_SPI_H +#include "ose_spi/ose_spi.h" +#endif + +#define DEBUG_ATTACH 0 +#define DEBUG_HUNT 0 +#define DEBUG_SEND 0 +#define DEBUG_LISTEN 0 + +#if 0 +#define DEBUGP(FMT,...) printf(FMT, __VA_ARGS__) +#else +#define DEBUGP(FMT,...) +#endif + +#if DEBUG_ATTACH +#define DEBUGP_ATTACH(...) DEBUGP( __VA_ARGS__) +#else +#define DEBUGP_ATTACH(...) +#endif + +#if DEBUG_HUNT +#define DEBUGP_HUNT(...) DEBUGP( __VA_ARGS__) +#else +#define DEBUGP_HUNT(...) +#endif + +#if DEBUG_LISTEN +#define DEBUGP_LISTEN(...) DEBUGP( __VA_ARGS__) +#else +#define DEBUGP_LISTEN(...) +#endif + +#if DEBUG_SEND +#define DEBUGP_SEND(...) DEBUGP( __VA_ARGS__) +#else +#define DEBUGP_SEND(...) +#endif + + +#define DRIVER_NAME "ose_signal_drv" +#define GET_SPID 1 +#define GET_NAME 2 +#define HUNT 100 +#define DEHUNT 101 +#define ATTACH 102 +#define DETACH 103 +#define SEND 104 +#define SEND_W_S 105 +#define LISTEN 106 +#define OPEN 200 + +#define REF_SEGMENT_SIZE 8 + +struct async { + SIGSELECT signo; + ErlDrvTermData port; + ErlDrvTermData proc; + PROCESS spid; + PROCESS target; + Uint32 ref; +}; + +/** + * OSE signals + **/ +union SIGNAL { + SIGSELECT signo; + struct async async; +}; + +/** + * The driver's context + **/ +typedef struct _driver_context { + ErlDrvPort port; + PROCESS spid; + ErlDrvEvent perm_events[2]; + ErlDrvEvent *events; + Uint32 event_cnt; + Uint32 ref; + Uint32 *outstanding_refs; + Uint32 outstanding_refs_max; + Uint32 outstanding_refs_cnt; +} driver_context_t; + +/** + * Global variables + **/ +static ErlDrvTermData a_ok; +static ErlDrvTermData a_error; +static ErlDrvTermData a_enomem; +static ErlDrvTermData a_enoent; +static ErlDrvTermData a_badarg; +static ErlDrvTermData a_mailbox_up; +static ErlDrvTermData a_mailbox_down; +static ErlDrvTermData a_ose_drv_reply; +static ErlDrvTermData a_message; +static PROCESS proxy_proc; + + +/** + * Serialize/unserialize unsigned 32-bit values + **/ +static char *put_u32(unsigned int value, char *ptr) { + *ptr++ = (value & 0xff000000) >> 24; + *ptr++ = (value & 0x00ff0000) >> 16; + *ptr++ = (value & 0x0000ff00) >> 8; + *ptr++ = (value & 0xff); + + return ptr; +} + +static unsigned int get_u32(char *ptr) { + unsigned int result = 0; + result += (ptr[0] & 0xff) << 24; + result += (ptr[1] & 0xff) << 16; + result += (ptr[2] & 0xff) << 8; + result += (ptr[3] & 0xff); + + return result; +} + + +/* Stolen from efile_drv.c */ + +/* char EV_CHAR_P(ErlIOVec *ev, int p, int q) */ +#define EV_CHAR_P(ev, p, q) \ + (((char *)(ev)->iov[(q)].iov_base) + (p)) + +/* int EV_GET_CHAR(ErlIOVec *ev, char *p, int *pp, int *qp) */ +#define EV_GET_CHAR(ev, p, pp, qp) ev_get_char(ev, p ,pp, qp) +static int +ev_get_char(ErlIOVec *ev, char *p, int *pp, int *qp) { + if (*(pp)+1 <= (ev)->iov[*(qp)].iov_len) { + *(p) = *EV_CHAR_P(ev, *(pp), *(qp)); + if (*(pp)+1 < (ev)->iov[*(qp)].iov_len) + *(pp) = *(pp)+1; + else { + (*(qp))++; + *pp = 0; + } + return !0; + } + return 0; +} + +/* Uint32 EV_UINT32(ErlIOVec *ev, int p, int q)*/ +#define EV_UINT32(ev, p, q) \ + ((Uint32) *(((unsigned char *)(ev)->iov[(q)].iov_base) + (p))) + +/* int EV_GET_UINT32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) */ +#define EV_GET_UINT32(ev, p, pp, qp) ev_get_uint32(ev,(Uint32*)(p),pp,qp) +static int +ev_get_uint32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) { + if (*(pp)+4 <= (ev)->iov[*(qp)].iov_len) { + *(p) = (EV_UINT32(ev, *(pp), *(qp)) << 24) + | (EV_UINT32(ev, *(pp)+1, *(qp)) << 16) + | (EV_UINT32(ev, *(pp)+2, *(qp)) << 8) + | (EV_UINT32(ev, *(pp)+3, *(qp))); + if (*(pp)+4 < (ev)->iov[*(qp)].iov_len) + *(pp) = *(pp)+4; + else { + (*(qp))++; + *pp = 0; + } + return !0; + } + return 0; +} + +/** + * Convinience macros + **/ +#define send_response(port,output) erl_drv_send_term(driver_mk_port(port),\ + driver_caller(port), output, sizeof(output) / sizeof(output[0])); + +void iov_memcpy(void *dest,ErlIOVec *ev,int ind,int off); +void iov_memcpy(void *dest,ErlIOVec *ev,int ind,int off) { + int i; + memcpy(dest,ev->iov[ind].iov_base+off,ev->iov[ind].iov_len-off); + for (i = ind+1; i < ev->vsize; i++) + memcpy(dest,ev->iov[i].iov_base,ev->iov[i].iov_len); +} + +/** + * Reference handling + **/ + +static int add_reference(driver_context_t *ctxt, Uint32 ref) { + + /* + * Premature optimizations may be evil, but they sure are fun. + */ + + if (ctxt->outstanding_refs == NULL) { + /* First ref to be ignored */ + ctxt->outstanding_refs = driver_alloc(REF_SEGMENT_SIZE*sizeof(Uint32)); + if (!ctxt->outstanding_refs) + return 1; + + memset(ctxt->outstanding_refs,0,REF_SEGMENT_SIZE*sizeof(Uint32)); + ctxt->outstanding_refs_max += REF_SEGMENT_SIZE; + ctxt->outstanding_refs[ctxt->outstanding_refs_cnt++] = ref; + } else if (ctxt->outstanding_refs_cnt == ctxt->outstanding_refs_max) { + /* Expand ref array */ + Uint32 *new_array; + ctxt->outstanding_refs_max += REF_SEGMENT_SIZE; + new_array = driver_realloc(ctxt->outstanding_refs, + ctxt->outstanding_refs_max*sizeof(Uint32)); + + if (!new_array) { + ctxt->outstanding_refs_max -= REF_SEGMENT_SIZE; + return 1; + } + + ctxt->outstanding_refs = new_array; + + memset(ctxt->outstanding_refs+ctxt->outstanding_refs_cnt,0, + REF_SEGMENT_SIZE*sizeof(Uint32)); + ctxt->outstanding_refs[ctxt->outstanding_refs_cnt++] = ref; + + } else { + /* Find an empty slot: + * First we try current index, + * then we scan for a slot. + */ + if (!ctxt->outstanding_refs[ctxt->outstanding_refs_cnt]) { + ctxt->outstanding_refs[ctxt->outstanding_refs_cnt++] = ref; + } else { + int i; + ASSERT(ctxt->outstanding_refs_cnt < ctxt->outstanding_refs_max); + for (i = 0; i < ctxt->outstanding_refs_max; i++) + if (!ctxt->outstanding_refs[i]) + break; + ASSERT(ctxt->outstanding_refs[i] == 0); + ctxt->outstanding_refs[i] = ref; + ctxt->outstanding_refs_cnt++; + } + } + return 0; +} + +/* Return 0 if removed, 1 if does not exist, */ +static int remove_reference(driver_context_t *ctxt, Uint32 ref) { + int i,j; + + if (ctxt->outstanding_refs_max == 0 && ctxt->outstanding_refs_cnt == 0) { + ASSERT(ctxt->outstanding_refs == NULL); + return 1; + } + + for (i = 0; i < ctxt->outstanding_refs_max; i++) { + if (ctxt->outstanding_refs[i] == ref) { + ctxt->outstanding_refs[i] = 0; + ctxt->outstanding_refs_cnt--; + i = -1; + break; + } + } + + if (i != -1) + return 1; + + if (ctxt->outstanding_refs_cnt == 0) { + driver_free(ctxt->outstanding_refs); + ctxt->outstanding_refs = NULL; + ctxt->outstanding_refs_max = 0; + } else if (ctxt->outstanding_refs_cnt == (ctxt->outstanding_refs_max - REF_SEGMENT_SIZE)) { + Uint32 *new_array; + for (i = 0, j = 0; i < ctxt->outstanding_refs_cnt; i++) { + if (ctxt->outstanding_refs[i] == 0) { + for (j = i+1; j < ctxt->outstanding_refs_max; j++) + if (ctxt->outstanding_refs[j]) { + ctxt->outstanding_refs[i] = ctxt->outstanding_refs[j]; + ctxt->outstanding_refs[j] = 0; + break; + } + } + } + ctxt->outstanding_refs_max -= REF_SEGMENT_SIZE; + new_array = driver_realloc(ctxt->outstanding_refs, + ctxt->outstanding_refs_max*sizeof(Uint32)); + if (!new_array) { + ctxt->outstanding_refs_max += REF_SEGMENT_SIZE; + return 2; + } + + ctxt->outstanding_refs = new_array; + + } + + return 0; +} + +/** + * The OSE proxy process. This only handles ERTS_SIGNAL_OSE_DRV_ATTACH. + * The process is needed because signals triggered by attach ignore + * redir tables. + * + * We have one global proxy process to save memory. An attempt to make each + * port phantom into a proxy was made, but that used way to much memory. + */ +static OS_PROCESS(driver_proxy_process) { + SIGSELECT sigs[] = {1,ERTS_SIGNAL_OSE_DRV_ATTACH}; + PROCESS master = 0; + + while (1) { + union SIGNAL *sig = receive(sigs); + + if (sig->signo == ERTS_SIGNAL_OSE_DRV_ATTACH) { + + /* The first message is used to determine who to send messages to. */ + if (master == 0) + master = sender(&sig); + + if (sig->async.target == 0) { + PROCESS from = sender(&sig); + restore(sig); + DEBUGP_ATTACH("0x%x: got attach 0x%x, sending to 0x%x\n", + current_process(),from,master); + sig->async.target = from; + send(&sig,master); + } else { + PROCESS target = sig->async.target; + restore(sig); + sig->async.target = 0; + DEBUGP_ATTACH("0x%x: doing attach on 0x%x\n",current_process(),target); + attach(&sig,target); + } + } + } +} + + +/** + * Init routine for the driver + **/ +static int drv_init(void) { + + a_ok = driver_mk_atom("ok"); + a_error = driver_mk_atom("error"); + a_enomem = driver_mk_atom("enomem"); + a_enoent = driver_mk_atom("enoent"); + a_badarg = driver_mk_atom("badarg"); + a_mailbox_up = driver_mk_atom("mailbox_up"); + a_mailbox_down = driver_mk_atom("mailbox_down"); + a_ose_drv_reply = driver_mk_atom("ose_drv_reply"); + a_message = driver_mk_atom("message"); + + proxy_proc = create_process(get_ptype(current_process()), + "ose_signal_driver_proxy", + driver_proxy_process, 10000, + get_pri(current_process()), + 0, 0, NULL, 0, 0); + +#ifdef DEBUG + efs_clone(proxy_proc); +#endif + start(proxy_proc); + + return 0; +} + +/* Signal resolution callback */ +static ErlDrvOseEventId resolve_signal(union SIGNAL* osig) { + union SIGNAL *sig = osig; + if (sig->signo == ERTS_SIGNAL_OSE_DRV_HUNT || + sig->signo == ERTS_SIGNAL_OSE_DRV_ATTACH) { + return sig->async.spid; + } + DEBUGP("%p: Got signal %d sent to %p from 0x%p\n", + current_process(),sig->signo,addressee(&sig),sender(&sig)); + return addressee(&sig); +} + + +/** + * Start routine for the driver + **/ +static ErlDrvData drv_start(ErlDrvPort port, char *command) +{ + driver_context_t *ctxt = driver_alloc(sizeof(driver_context_t)); + + ctxt->perm_events[0] = NULL; + ctxt->perm_events[1] = NULL; + + ctxt->spid = 0; + ctxt->port = port; + ctxt->event_cnt = 0; + ctxt->events = NULL; + ctxt->ref = 0; + ctxt->outstanding_refs = NULL; + ctxt->outstanding_refs_max = 0; + ctxt->outstanding_refs_cnt = 0; + + + /* Set the communication protocol to Erlang to be binary */ + set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY); + + /* Everything ok */ + return (ErlDrvData)ctxt; +} + +/** + * Stop routine for the driver + **/ +static void drv_stop(ErlDrvData driver_data) +{ + driver_context_t *ctxt = (driver_context_t *)driver_data; + int i; + + /* HUNT + ATTACH */ + if (ctxt->perm_events[0]) + driver_select(ctxt->port, ctxt->perm_events[0], + ERL_DRV_USE|ERL_DRV_READ, 0); + if (ctxt->perm_events[1]) + driver_select(ctxt->port, ctxt->perm_events[1], + ERL_DRV_USE|ERL_DRV_READ, 0); + + for (i = 0; i < ctxt->event_cnt; i++) { + driver_select(ctxt->port, ctxt->events[i], ERL_DRV_USE|ERL_DRV_READ, 0); + } + + if (ctxt->spid != 0) + kill_proc(ctxt->spid); + DEBUGP("0x%x: stopped\n",ctxt->spid); + if (ctxt->events) + driver_free(ctxt->events); + if (ctxt->outstanding_refs) + driver_free(ctxt->outstanding_refs); + + driver_free(ctxt); +} + +/** + * Output from Erlang + **/ +static void outputv(ErlDrvData driver_data, ErlIOVec *ev) +{ + driver_context_t *ctxt = (driver_context_t *)driver_data; + int p = 0, q = 1; + char cmd; + + if (! EV_GET_CHAR(ev,&cmd,&p,&q)) { + ErlDrvTermData output[] = { + ERL_DRV_ATOM, a_ose_drv_reply, + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_ATOM, a_badarg, + ERL_DRV_TUPLE, 3}; + send_response(ctxt->port, output); + return; + } + + /* Command is in the buffer's first byte */ + switch(cmd) { + + case OPEN: { + char *name = driver_alloc(ev->size - 1+1); + struct OS_redir_entry redir[2]; + + redir[0].sig = 1; + redir[0].pid = current_process(); + + iov_memcpy(name,ev,q,p); + name[ev->size-1] = '\0'; + + ctxt->spid = create_process(OS_PHANTOM, name, NULL, 0, + 0, 0, 0, redir, 0, 0); + + DEBUGP("0x%x: open\n",ctxt->spid); + + ctxt->perm_events[1] = + erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_ATTACH,(int)ctxt->spid, + resolve_signal, NULL); + driver_select(ctxt->port,ctxt->perm_events[1],ERL_DRV_READ|ERL_DRV_USE,1); + + ctxt->perm_events[0] = + erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_HUNT,(int)ctxt->spid, + resolve_signal, NULL); + driver_select(ctxt->port,ctxt->perm_events[0],ERL_DRV_READ|ERL_DRV_USE,1); + + start(ctxt->spid); + + { + ErlDrvTermData output[] = { + ERL_DRV_ATOM, a_ose_drv_reply, + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_ATOM, a_ok, + ERL_DRV_TUPLE, 3}; + + send_response(ctxt->port, output); + } + + break; + + } + + case ATTACH: + case HUNT: + { + union SIGNAL *sig = alloc(sizeof(union SIGNAL), + cmd == HUNT ? ERTS_SIGNAL_OSE_DRV_HUNT:ERTS_SIGNAL_OSE_DRV_ATTACH); + + sig->async.port = driver_mk_port(ctxt->port); + sig->async.proc = driver_caller(ctxt->port); + sig->async.spid = ctxt->spid; + sig->async.ref = ++ctxt->ref; + + if (add_reference(ctxt,ctxt->ref)) { + ErlDrvTermData output[] = { + ERL_DRV_ATOM, a_ose_drv_reply, + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_ATOM, a_enomem, + ERL_DRV_TUPLE, 3}; + send_response(ctxt->port, output); + free_buf(&sig); + } else { + ErlDrvTermData output[] = { + ERL_DRV_ATOM, a_ose_drv_reply, + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_INT, (ErlDrvUInt)ctxt->ref, + ERL_DRV_TUPLE, 2, + ERL_DRV_TUPLE, 3}; + send_response(ctxt->port, output); + + if (cmd == HUNT) { + char *huntname = driver_alloc(sizeof(char)*((ev->size-1)+1)); + + iov_memcpy(huntname,ev,q,p); + huntname[ev->size-1] = '\0'; + + DEBUGP_HUNT("0x%x: hunt %s -> %u (%u,%u)\n", + ctxt->spid,huntname,ctxt->ref, + ctxt->outstanding_refs_cnt, + ctxt->outstanding_refs_max); + + hunt(huntname, 0, NULL, &sig); + + driver_free(huntname); + } else { + EV_GET_UINT32(ev,&sig->async.target,&p,&q); + DEBUGP_ATTACH("0x%x: attach %u -> %u (%u,%u)\n", + ctxt->spid,sig->async.target, + ctxt->ref, + ctxt->outstanding_refs_cnt, + ctxt->outstanding_refs_max); + + send(&sig,proxy_proc); + } + + } + + break; + } + + case DETACH: + case DEHUNT: + { + + Uint32 ref; + + EV_GET_UINT32(ev,&ref,&p,&q); + if (cmd == DETACH) { + DEBUGP_ATTACH("0x%x: detach %u (%u,%u)\n",ctxt->spid,ref, + ctxt->outstanding_refs_cnt, + ctxt->outstanding_refs_max); + } else { + DEBUGP_HUNT("0x%x: dehunt %u (%u,%u)\n",ctxt->spid,ref, + ctxt->outstanding_refs_cnt, + ctxt->outstanding_refs_max); + } + + if (remove_reference(ctxt,ref)) { + ErlDrvTermData output[] = { + ERL_DRV_ATOM, a_ose_drv_reply, + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_ATOM, a_error, + ERL_DRV_ATOM, a_enoent, + ERL_DRV_TUPLE, 2, + ERL_DRV_TUPLE, 3}; + + send_response(ctxt->port, output); + } else { + ErlDrvTermData output[] = { + ERL_DRV_ATOM, a_ose_drv_reply, + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_ATOM, a_ok, + ERL_DRV_TUPLE, 3}; + + send_response(ctxt->port, output); + } + + break; + } + + case SEND: + case SEND_W_S: + { + PROCESS spid; + PROCESS sender; + SIGSELECT signo; + OSBUFSIZE size = ev->size-9; + union SIGNAL *sig; + + EV_GET_UINT32(ev,&spid,&p,&q); + + if (cmd == SEND_W_S) { + EV_GET_UINT32(ev,&sender,&p,&q); + size -= 4; + } else { + sender = ctxt->spid; + } + + EV_GET_UINT32(ev,&signo,&p,&q); + + sig = alloc(size + sizeof(SIGSELECT),signo); + + if (cmd == SEND_W_S) { + DEBUGP_SEND("0x%x: send_w_s(%u,%u,%u)\n",ctxt->spid,spid,signo,sender); + } else { + DEBUGP_SEND("0x%x: send(%u,%u)\n",ctxt->spid,spid,signo); + } + + iov_memcpy(((char *)&sig->signo) + sizeof(SIGSELECT),ev,q,p); + + send_w_s(&sig, sender, spid); + + break; + } + + case LISTEN: + { + int i,j,event_cnt = (ev->size - 1)/4; + ErlDrvEvent *events = NULL; + SIGSELECT signo,tmp_signo; + + if (event_cnt == 0) { + for (i = 0; i < ctxt->event_cnt; i++) + driver_select(ctxt->port,ctxt->events[i],ERL_DRV_READ|ERL_DRV_USE,0); + if (ctxt->events) + driver_free(ctxt->events); + } else { + events = driver_alloc(sizeof(ErlDrvEvent)*event_cnt); + EV_GET_UINT32(ev,&signo,&p,&q); + for (i = 0, j = 0; i < event_cnt || j < ctxt->event_cnt; ) { + + if (ctxt->events) + erl_drv_ose_event_fetch(ctxt->events[j],&tmp_signo,NULL,NULL); + + if (signo == tmp_signo) { + events[i++] = ctxt->events[j++]; + EV_GET_UINT32(ev,&signo,&p,&q); + } else if (signo < tmp_signo || !ctxt->events) { + /* New signal to select on */ + events[i] = erl_drv_ose_event_alloc(signo,(int)ctxt->spid, + resolve_signal, NULL); + driver_select(ctxt->port,events[i++],ERL_DRV_READ|ERL_DRV_USE,1); + EV_GET_UINT32(ev,&signo,&p,&q); + } else { + /* Remove old signal to select on */ + driver_select(ctxt->port,ctxt->events[j++],ERL_DRV_READ|ERL_DRV_USE,0); + } + } + if (ctxt->events) + driver_free(ctxt->events); + } + ctxt->events = events; + ctxt->event_cnt = event_cnt; + + { + ErlDrvTermData output[] = { + ERL_DRV_ATOM, a_ose_drv_reply, + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_ATOM, a_ok, + ERL_DRV_TUPLE, 3}; + send_response(ctxt->port, output); + } + break; + } + + default: + { + DEBUGP("Warning: 'ose_signal_drv' unknown command '%d'\n", cmd); + break; + } + } +} + +/** + * Handler for when OSE signal arrives + **/ +static void ready_input(ErlDrvData driver_data, ErlDrvEvent event) +{ + driver_context_t *ctxt = (driver_context_t *)driver_data; + union SIGNAL *sig = erl_drv_ose_get_signal(event); + + while (sig != NULL) { + + switch(sig->signo) + { + /* Remote process is available */ + case ERTS_SIGNAL_OSE_DRV_HUNT: + { + const PROCESS spid = sender(&sig); + + if (remove_reference(ctxt,sig->async.ref)) { + DEBUGP_HUNT("0x%x: Got hunt from 0x%x -> %u (CANCELLED) (%u,%u)\n", + ctxt->spid,spid,sig->async.ref, + ctxt->outstanding_refs_cnt, + ctxt->outstanding_refs_max); + /* Already removed by dehunt */ + } else { + ErlDrvTermData reply[] = { + ERL_DRV_ATOM, a_mailbox_up, + ERL_DRV_PORT, sig->async.port, + ERL_DRV_PORT, sig->async.port, + ERL_DRV_UINT, (ErlDrvUInt)sig->async.ref, + ERL_DRV_TUPLE, 2, + ERL_DRV_UINT, (ErlDrvUInt)spid, + ERL_DRV_TUPLE, 4}; + DEBUGP_HUNT("0x%x: Got hunt from 0x%x -> %u (%u,%u)\n", + ctxt->spid,spid,sig->async.ref, + ctxt->outstanding_refs_cnt, + ctxt->outstanding_refs_max); + erl_drv_send_term(sig->async.port, sig->async.proc, reply, + sizeof(reply) / sizeof(reply[0])); + } + break; + } + + /* Remote process is down */ + case ERTS_SIGNAL_OSE_DRV_ATTACH: + { + PROCESS spid = sig->async.target; + + if (remove_reference(ctxt,sig->async.ref)) { + DEBUGP_ATTACH("0x%x: Got attach from 0x%x -> %u (CANCELLED) (%u,%u)\n", + ctxt->spid,spid,sig->async.ref, + ctxt->outstanding_refs_cnt, + ctxt->outstanding_refs_max); + /* Already removed by detach */ + } else { + ErlDrvTermData reply[] = { + ERL_DRV_ATOM, a_mailbox_down, + ERL_DRV_PORT, sig->async.port, + ERL_DRV_PORT, sig->async.port, + ERL_DRV_UINT, sig->async.ref, + ERL_DRV_TUPLE, 2, + ERL_DRV_UINT, (ErlDrvUInt)spid, + ERL_DRV_TUPLE, 4}; + DEBUGP_ATTACH("0x%x: Got attach from 0x%x -> %u (%u,%u)\n", + ctxt->spid,spid,sig->async.ref, + ctxt->outstanding_refs_cnt, + ctxt->outstanding_refs_max); + erl_drv_send_term(sig->async.port, sig->async.proc, reply, + sizeof(reply) / sizeof(reply[0])); + } + break; + } + + /* Received user defined signal */ + default: + { + const PROCESS spid = sender(&sig); + const OSBUFSIZE size = sigsize(&sig) - sizeof(SIGSELECT); + const char *sig_data = ((char *)&sig->signo) + sizeof(SIGSELECT); + + ErlDrvTermData reply[] = { + ERL_DRV_ATOM, a_message, + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_UINT, (ErlDrvUInt)spid, + ERL_DRV_UINT, (ErlDrvUInt)ctxt->spid, + ERL_DRV_UINT, (ErlDrvUInt)sig->signo, + ERL_DRV_BUF2BINARY, (ErlDrvTermData)sig_data, (ErlDrvUInt)size, + ERL_DRV_TUPLE, 4, + ERL_DRV_TUPLE, 3}; + + DEBUGP_SEND("0x%x: Got 0x%u\r\n", spid, sig->signo); + + erl_drv_output_term(driver_mk_port(ctxt->port), reply, + sizeof(reply) / sizeof(reply[0])); + break; + } + } + + free_buf(&sig); + sig = erl_drv_ose_get_signal(event); + } +} + +/** + * Handler for 'port_control' + **/ +static ErlDrvSSizeT control(ErlDrvData driver_data, unsigned int cmd, + char *buf, ErlDrvSizeT len, + char **rbuf, ErlDrvSizeT rlen) +{ + driver_context_t *ctxt = (driver_context_t *)driver_data; + + switch(cmd) + { + case GET_SPID: + { + const PROCESS spid = ctxt->spid; + put_u32(spid, *rbuf); + return sizeof(PROCESS); + } + +#ifdef HAVE_OSE_SPI_H + case GET_NAME: + { + const PROCESS spid = get_u32(buf); + char *name = (char*)get_pid_info(spid,OSE_PI_NAME); + int n; + if (!name) { + *rbuf = NULL; + return 0; + } + + if (rlen < (n = strlen(name))) { + ErlDrvBinary *bin = driver_alloc_binary(n); + strncpy(bin->orig_bytes,name,n); + *rbuf = (char*)bin; + } else + strncpy(*rbuf,name,n); + free_buf((union SIGNAL**)&name); + + return n; + } +#endif + default: + { + /* Unknown command */ + return (ErlDrvSSizeT)ERL_DRV_ERROR_GENERAL; + break; + } + } +} + +static void stop_select(ErlDrvEvent event, void *reserved) +{ + erl_drv_ose_event_free(event); +} + +/** + * Setup the driver entry for the Erlang runtime + **/ +ErlDrvEntry ose_signal_driver_entry = { + .init = drv_init, + .start = drv_start, + .stop = drv_stop, + .outputv = outputv, + .ready_input = ready_input, + .driver_name = DRIVER_NAME, + .control = control, + .extended_marker = ERL_DRV_EXTENDED_MARKER, + .major_version = ERL_DRV_EXTENDED_MAJOR_VERSION, + .minor_version = ERL_DRV_EXTENDED_MINOR_VERSION, + .driver_flags = ERL_DRV_FLAG_USE_PORT_LOCKING, + .stop_select = stop_select +}; + diff --git a/erts/emulator/drivers/ose/ttsl_drv.c b/erts/emulator/drivers/ose/ttsl_drv.c new file mode 100644 index 0000000000..8af2ce6af3 --- /dev/null +++ b/erts/emulator/drivers/ose/ttsl_drv.c @@ -0,0 +1,68 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Stub tty driver because group/user depend on this. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "erl_driver.h" + +static int ttysl_init(void); +static ErlDrvData ttysl_start(ErlDrvPort, char*); + +/* Define the driver table entry. */ +struct erl_drv_entry ttsl_driver_entry = { + ttysl_init, + ttysl_start, + NULL, + NULL, + NULL, + NULL, + "tty_sl", + NULL, + NULL, + NULL, + NULL, /* timeout */ + NULL, /* outputv */ + NULL, /* ready_async */ + NULL, /* flush */ + NULL, /* call */ + NULL, /* event */ + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + 0, /* ERL_DRV_FLAGs */ + NULL, + NULL, /* process_exit */ + NULL +}; + + +static int ttysl_init(void) +{ + return 0; +} + +static ErlDrvData ttysl_start(ErlDrvPort port, char* buf) +{ + return ERL_DRV_ERROR_GENERAL; +} diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c index b29f80a8ba..491e0a090e 100644 --- a/erts/emulator/drivers/unix/ttsl_drv.c +++ b/erts/emulator/drivers/unix/ttsl_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2011. All Rights Reserved. + * Copyright Ericsson AB 1996-2013. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -42,6 +42,9 @@ static ErlDrvData ttysl_start(ErlDrvPort, char*); #include <locale.h> #include <unistd.h> #include <termios.h> +#ifdef HAVE_WCWIDTH +#include <wchar.h> +#endif #ifdef HAVE_FCNTL_H #include <fcntl.h> #endif @@ -95,6 +98,9 @@ static int lpos; /* The current "cursor position" in the line buf */ #define CONTROL_TAG 0x10000000U /* Control character, value in first position */ #define ESCAPED_TAG 0x01000000U /* Escaped character, value in first position */ +#ifdef HAVE_WCWIDTH +#define WIDE_TAG 0x02000000U /* Wide character, value in first position */ +#endif #define TAG_MASK 0xFF000000U #define MAXSIZE (1 << 16) @@ -597,12 +603,20 @@ static int check_buf_size(byte *s, int n) } if (utf8_mode) { /* That is, terminal is UTF8 compliant */ if (ch >= 128 || isprint(ch)) { - DEBUGLOG(("Printable(UTF-8:%d):%d",(pos - opos),ch)); - size++; /* Buffer contains wide characters... */ +#ifdef HAVE_WCWIDTH + int width; +#endif + DEBUGLOG(("Printable(UTF-8:%d):%d",pos,ch)); + size++; +#ifdef HAVE_WCWIDTH + if ((width = wcwidth(ch)) > 1) { + size += width - 1; + } +#endif } else if (ch == '\t') { size += 8; } else { - DEBUGLOG(("Magic(UTF-8:%d):%d",(pos - opos),ch)); + DEBUGLOG(("Magic(UTF-8:%d):%d",pos,ch)); size += 2; } } else { @@ -731,7 +745,7 @@ static Sint16 get_sint16(char *s) { return ((*s << 8) | ((byte*)s)[1]); } - + static int start_lbuf(void) { if (!lbuf && !(lbuf = ( Uint32*) driver_alloc(lbuf_size * sizeof(Uint32)))) @@ -823,7 +837,7 @@ static int del_chars(int n) r = llen - lpos - l; /* Characters after deleted */ /* Fix up buffer and buffer pointers. */ if (r > 0) - memcpy(lbuf + lpos, lbuf + pos, r * sizeof(Uint32)); + memmove(lbuf + lpos, lbuf + pos, r * sizeof(Uint32)); llen -= l; /* Write out characters after, blank the tail and jump back to lpos. */ write_buf(lbuf + lpos, r); @@ -842,7 +856,7 @@ static int del_chars(int n) move_cursor(lpos, lpos-l); /* Move back */ /* Fix up buffer and buffer pointers. */ if (r > 0) - memcpy(lbuf + pos, lbuf + lpos, r * sizeof(Uint32)); + memmove(lbuf + pos, lbuf + lpos, r * sizeof(Uint32)); lpos -= l; llen -= l; /* Write out characters after, blank the tail and jump back to lpos. */ @@ -868,12 +882,22 @@ static int step_over_chars(int n) end = lbuf + llen; c = lbuf + lpos; for ( ; n > 0 && c < end; --n) { +#ifdef HAVE_WCWIDTH + while (*c & WIDE_TAG) { + c++; + } +#endif c++; while (c < end && (*c & TAG_MASK) && ((*c & ~TAG_MASK) == 0)) c++; } for ( ; n < 0 && c > beg; n++) { --c; +#ifdef HAVE_WCWIDTH + while (c > beg + 1 && (c[-1] & WIDE_TAG)) { + --c; + } +#endif while (c > beg && (*c & TAG_MASK) && ((*c & ~TAG_MASK) == 0)) --c; } @@ -899,6 +923,15 @@ static int insert_buf(byte *s, int n) ++pos; } if ((utf8_mode && (ch >= 128 || isprint(ch))) || (ch <= 255 && isprint(ch))) { +#ifdef HAVE_WCWIDTH + int width; + if ((width = wcwidth(ch)) > 1) { + while (--width) { + DEBUGLOG(("insert_buf: Wide(UTF-8):%d,%d",width,ch)); + lbuf[lpos++] = (WIDE_TAG | ((Uint32) ch)); + } + } +#endif DEBUGLOG(("insert_buf: Printable(UTF-8):%d",ch)); lbuf[lpos++] = (Uint32) ch; } else if (ch >= 128) { /* not utf8 mode */ @@ -912,11 +945,15 @@ static int insert_buf(byte *s, int n) lbuf[lpos++] = (CONTROL_TAG | ((Uint32) ch)); ch = 0; } while (lpos % 8); - } else if (ch == '\n' || ch == '\r') { + } else if (ch == '\e' || ch == '\n' || ch == '\r') { write_buf(lbuf + buffpos, lpos - buffpos); - outc('\r'); - if (ch == '\n') - outc('\n'); + if (ch == '\e') { + outc('\e'); + } else { + outc('\r'); + if (ch == '\n') + outc('\n'); + } if (llen > lpos) { memcpy(lbuf, lbuf + lpos, llen - lpos); } @@ -1002,6 +1039,10 @@ static int write_buf(Uint32 *s, int n) if (octbuff != octtmp) { driver_free(octbuff); } +#ifdef HAVE_WCWIDTH + } else if (*s & WIDE_TAG) { + --n; s++; +#endif } else { DEBUGLOG(("Very unexpected character %d",(int) *s)); ++n; @@ -1050,7 +1091,7 @@ static int move_cursor(int from, int to) move_left(-dc); return TRUE; } - + static int start_termcap(void) { int eres; @@ -1146,7 +1187,7 @@ static int move_down(int n) tputs(down, 1, outc); return TRUE; } - + /* * Updates cols if terminal has resized (SIGWINCH). Should be called @@ -1168,7 +1209,7 @@ static void update_cols(void) cols = width; } } - + /* * Put a terminal device into non-canonical mode with ECHO off. diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index b250bac4dc..878beb055b 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2012. All Rights Reserved. + * Copyright Ericsson AB 1997-2013. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -22,6 +22,12 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif +#if defined(HAVE_POSIX_FALLOCATE) && !defined(__sun) && !defined(__sun__) +#define _XOPEN_SOURCE 600 +#endif +#if !defined(_GNU_SOURCE) && defined(HAVE_LINUX_FALLOC_H) +#define _GNU_SOURCE +#endif #include "sys.h" #include "erl_driver.h" #include "erl_efile.h" @@ -41,25 +47,12 @@ #define DARWIN 1 #endif -#ifdef DARWIN +#if defined(DARWIN) || defined(HAVE_LINUX_FALLOC_H) || defined(HAVE_POSIX_FALLOCATE) #include <fcntl.h> -#endif /* DARWIN */ +#endif -#ifdef VXWORKS -#include <ioLib.h> -#include <dosFsLib.h> -#include <nfsLib.h> -#include <sys/stat.h> -/* -** Not nice to include usrLib.h as MANY normal variable names get reported -** as shadowing globals, like 'i' for example. -** Instead we declare the only function we use here -*/ -/* - * #include <usrLib.h> - */ -extern STATUS copy(char *, char *); -#include <errno.h> +#ifdef HAVE_LINUX_FALLOC_H +#include <linux/falloc.h> #endif #ifdef SUNOS4 @@ -93,276 +86,32 @@ extern STATUS copy(char *, char *); #define DIR_MODE 0777 #endif -#ifdef VXWORKS /* Currently only used on vxworks */ - -#define EF_ALLOC(S) driver_alloc((S)) -#define EF_REALLOC(P, S) driver_realloc((P), (S)) -#define EF_SAFE_ALLOC(S) ef_safe_alloc((S)) -#define EF_SAFE_REALLOC(P, S) ef_safe_realloc((P), (S)) -#define EF_FREE(P) do { if((P)) driver_free((P)); } while(0) - -void erl_exit(int n, char *fmt, ...); - -static void *ef_safe_alloc(Uint s) -{ - void *p = EF_ALLOC(s); - if (!p) erl_exit(1, - "unix efile drv: Can't allocate %lu bytes of memory\n", - (unsigned long)s); - return p; -} - -#if 0 /* Currently not used */ - -static void *ef_safe_realloc(void *op, Uint s) -{ - void *p = EF_REALLOC(op, s); - if (!p) erl_exit(1, - "unix efile drv: Can't reallocate %lu bytes of memory\n", - (unsigned long)s); - return p; -} - -#endif /* #if 0 */ -#endif /* #ifdef VXWORKS */ - #define IS_DOT_OR_DOTDOT(s) \ (s[0] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0'))) -#ifdef VXWORKS -static int vxworks_to_posix(int vx_errno); -#endif - -/* -** VxWorks (not) strikes again. Too long RESULTING paths -** may give the infamous bus error. Have to check ALL -** filenames and pathnames. No wonder the emulator is slow on -** these cards... -*/ -#ifdef VXWORKS -#define CHECK_PATHLEN(Name, ErrInfo) \ - if (path_size(Name) > PATH_MAX) { \ - errno = ENAMETOOLONG; \ - return check_error(-1, ErrInfo); \ - } -#else -#define CHECK_PATHLEN(X,Y) /* Nothing */ -#endif - static int check_error(int result, Efile_error* errInfo); static int check_error(int result, Efile_error *errInfo) { if (result < 0) { -#ifdef VXWORKS - errInfo->posix_errno = errInfo->os_errno = vxworks_to_posix(errno); -#else errInfo->posix_errno = errInfo->os_errno = errno; -#endif return 0; } return 1; } -#ifdef VXWORKS - -/* - * VxWorks has different error codes for different file systems. - * We map those to POSIX ones. - */ -static int -vxworks_to_posix(int vx_errno) -{ - DEBUGF(("[vxworks_to_posix] vx_errno: %08x\n", vx_errno)); - switch (vx_errno) { - /* dosFsLib mapping */ -#ifdef S_dosFsLib_FILE_ALREADY_EXISTS - case S_dosFsLib_FILE_ALREADY_EXISTS: return EEXIST; -#else - case S_dosFsLib_FILE_EXISTS: return EEXIST; -#endif -#ifdef S_dosFsLib_BAD_DISK - case S_dosFsLib_BAD_DISK: return EIO; -#endif -#ifdef S_dosFsLib_CANT_CHANGE_ROOT - case S_dosFsLib_CANT_CHANGE_ROOT: return EINVAL; -#endif -#ifdef S_dosFsLib_NO_BLOCK_DEVICE - case S_dosFsLib_NO_BLOCK_DEVICE: return ENOTBLK; -#endif -#ifdef S_dosFsLib_BAD_SEEK - case S_dosFsLib_BAD_SEEK: return ESPIPE; -#endif - case S_dosFsLib_VOLUME_NOT_AVAILABLE: return ENXIO; - case S_dosFsLib_DISK_FULL: return ENOSPC; - case S_dosFsLib_FILE_NOT_FOUND: return ENOENT; - case S_dosFsLib_NO_FREE_FILE_DESCRIPTORS: return ENFILE; - case S_dosFsLib_INVALID_NUMBER_OF_BYTES: return EINVAL; - case S_dosFsLib_ILLEGAL_NAME: return EINVAL; - case S_dosFsLib_CANT_DEL_ROOT: return EACCES; - case S_dosFsLib_NOT_FILE: return EISDIR; - case S_dosFsLib_NOT_DIRECTORY: return ENOTDIR; - case S_dosFsLib_NOT_SAME_VOLUME: return EXDEV; - case S_dosFsLib_READ_ONLY: return EACCES; - case S_dosFsLib_ROOT_DIR_FULL: return ENOSPC; - case S_dosFsLib_DIR_NOT_EMPTY: return EEXIST; - case S_dosFsLib_NO_LABEL: return ENXIO; - case S_dosFsLib_INVALID_PARAMETER: return EINVAL; - case S_dosFsLib_NO_CONTIG_SPACE: return ENOSPC; - case S_dosFsLib_FD_OBSOLETE: return EBADF; - case S_dosFsLib_DELETED: return EINVAL; - case S_dosFsLib_INTERNAL_ERROR: return EIO; - case S_dosFsLib_WRITE_ONLY: return EACCES; - /* nfsLib mapping - is needed since Windriver has used */ - /* inconsistent error codes (errno.h/nfsLib.h). */ - case S_nfsLib_NFS_OK: return 0; - case S_nfsLib_NFSERR_PERM: return EPERM; - case S_nfsLib_NFSERR_NOENT: return ENOENT; - case S_nfsLib_NFSERR_IO: return EIO; - case S_nfsLib_NFSERR_NXIO: return ENXIO; -#ifdef S_nfsLib_NFSERR_ACCES - case S_nfsLib_NFSERR_ACCES: return EACCES; -#else - case S_nfsLib_NFSERR_ACCESS: return EACCES; -#endif - case S_nfsLib_NFSERR_EXIST: return EEXIST; - case S_nfsLib_NFSERR_NODEV: return ENODEV; - case S_nfsLib_NFSERR_NOTDIR: return ENOTDIR; - case S_nfsLib_NFSERR_ISDIR: return EISDIR; - case S_nfsLib_NFSERR_FBIG: return EFBIG; - case S_nfsLib_NFSERR_NOSPC: return ENOSPC; - case S_nfsLib_NFSERR_ROFS: return EROFS; - case S_nfsLib_NFSERR_NAMETOOLONG: return ENAMETOOLONG; - case S_nfsLib_NFSERR_NOTEMPTY: return EEXIST; - case S_nfsLib_NFSERR_DQUOT: return ENOSPC; - case S_nfsLib_NFSERR_STALE: return EINVAL; - case S_nfsLib_NFSERR_WFLUSH: return ENXIO; - /* And sometimes (...) the error codes are from ioLib (as in the */ - /* case of the (for nfsLib) unimplemented rename function) */ - case S_ioLib_DISK_NOT_PRESENT: return EIO; -#if S_ioLib_DISK_NOT_PRESENT != S_ioLib_NO_DRIVER - case S_ioLib_NO_DRIVER: return ENXIO; -#endif - case S_ioLib_UNKNOWN_REQUEST: return ENOSYS; - case S_ioLib_DEVICE_TIMEOUT: return EIO; -#ifdef S_ioLib_UNFORMATED - /* Added (VxWorks 5.2 -> 5.3.1) */ - #if S_ioLib_UNFORMATED != S_ioLib_DEVICE_TIMEOUT - case S_ioLib_UNFORMATED: return EIO; - #endif -#endif -#if S_ioLib_DEVICE_TIMEOUT != S_ioLib_DEVICE_ERROR - case S_ioLib_DEVICE_ERROR: return ENXIO; -#endif - case S_ioLib_WRITE_PROTECTED: return EACCES; - case S_ioLib_NO_FILENAME: return EINVAL; - case S_ioLib_CANCELLED: return EINTR; - case S_ioLib_NO_DEVICE_NAME_IN_PATH: return EINVAL; - case S_ioLib_NAME_TOO_LONG: return ENAMETOOLONG; -#ifdef S_objLib_OBJ_UNAVAILABLE - case S_objLib_OBJ_UNAVAILABLE: return ENOENT; -#endif - - /* Temporary workaround for a weird error in passFs - (VxWorks Simsparc only). File operation fails because of - ENOENT, but errno is not set. */ -#ifdef SIMSPARCSOLARIS - case 0: return ENOENT; -#endif - - } - /* If the error code matches none of the above, assume */ - /* it is a POSIX one already. The upper bits (>=16) are */ - /* cleared since VxWorks uses those bits to indicate in */ - /* what module the error occured. */ - return vx_errno & 0xffff; -} - -static int -vxworks_enotsup(Efile_error *errInfo) -{ - errInfo->posix_errno = errInfo->os_errno = ENOTSUP; - return 0; -} - -static int -count_path_length(char *pathname, char *pathname2) -{ - static int stack[PATH_MAX / 2 + 1]; - int sp = 0; - char *tmp; - char *cpy = NULL; - int i; - int sum; - for(i = 0;i < 2;++i) { - if (!i) { - cpy = EF_SAFE_ALLOC(strlen(pathname)+1); - strcpy(cpy, pathname); - } else if (pathname2 != NULL) { - EF_FREE(cpy); - cpy = EF_SAFE_ALLOC(strlen(pathname2)+1); - strcpy(cpy, pathname2); - } else - break; - - for (tmp = strtok(cpy,"/"); tmp != NULL; tmp = strtok(NULL,"/")) { - if (!strcmp(tmp,"..") && sp > 0) - --sp; - else if (strcmp(tmp,".")) - stack[sp++] = strlen(tmp); - } - } - if (cpy != NULL) - EF_FREE(cpy); - sum = 0; - for(i = 0;i < sp; ++i) - sum += stack[i]+1; - return (sum) ? sum : 1; -} - -static int -path_size(char *pathname) -{ - static char currdir[PATH_MAX+2]; - if (*pathname == '/') - return count_path_length(pathname,NULL); - ioDefPathGet(currdir); - strcat(currdir,"/"); - return count_path_length(currdir,pathname); +int +efile_init() { + return 1; } - -#endif /* VXWORKS */ int efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of directory to create. */ { - CHECK_PATHLEN(name,errInfo); #ifdef NO_MKDIR_MODE -#ifdef VXWORKS - /* This is a VxWorks/nfs workaround for erl_tar to create - * non-existant directories. (of some reason (...) VxWorks - * returns, the *non-module-prefixed*, 0xd code when - * trying to create a directory in a directory that doesn't exist). - * (see efile_openfile) - */ - if (mkdir(name) < 0) { - struct stat sb; - if (name[0] == '\0') { - /* Return the correct error code enoent */ - errno = S_nfsLib_NFSERR_NOENT; - } else if (stat(name, &sb) == OK) { - errno = S_nfsLib_NFSERR_EXIST; - } else if((strchr(name, '/') != NULL) && (errno == 0xd)) { - /* Return the correct error code enoent */ - errno = S_nfsLib_NFSERR_NOENT; - } - return check_error(-1, errInfo); - } else return 1; -#else return check_error(mkdir(name), errInfo); -#endif #else return check_error(mkdir(name, DIR_MODE), errInfo); #endif @@ -372,16 +121,9 @@ int efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of directory to delete. */ { - CHECK_PATHLEN(name, errInfo); if (rmdir(name) == 0) { return 1; } -#ifdef VXWORKS - if (name[0] == '\0') { - /* Return the correct error code enoent */ - errno = S_nfsLib_NFSERR_NOENT; - } -#else if (errno == ENOTEMPTY) { errno = EEXIST; } @@ -401,7 +143,6 @@ efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ } errno = saved_errno; } -#endif return check_error(-1, errInfo); } @@ -409,7 +150,6 @@ int efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of file to delete. */ { - CHECK_PATHLEN(name,errInfo); if (unlink(name) == 0) { return 1; } @@ -457,32 +197,13 @@ efile_rename(Efile_error* errInfo, /* Where to return error codes. */ char* src, /* Original name. */ char* dst) /* New name. */ { - CHECK_PATHLEN(src,errInfo); - CHECK_PATHLEN(dst,errInfo); -#ifdef VXWORKS - - /* First check if src == dst, if so, just return. */ - /* VxWorks dos file system destroys the file otherwise, */ - /* VxWorks nfs file system rename doesn't work at all. */ - if(strcmp(src, dst) == 0) - return 1; -#endif if (rename(src, dst) == 0) { return 1; } -#ifdef VXWORKS - /* nfs for VxWorks doesn't support rename. We try to emulate it */ - /* (by first copying src to dst and then deleting src). */ - if(errno == S_ioLib_UNKNOWN_REQUEST && /* error code returned - by ioLib (!) */ - copy(src, dst) == OK && - unlink(src) == OK) - return 1; -#endif if (errno == ENOTEMPTY) { errno = EEXIST; } -#if defined (sparc) && !defined(VXWORKS) +#if defined (sparc) /* * SunOS 4.1.4 reports overwriting a non-empty directory with a * directory as EINVAL instead of EEXIST (first rule out the correct @@ -543,7 +264,6 @@ int efile_chdir(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of directory to make current. */ { - CHECK_PATHLEN(name, errInfo); return check_error(chdir(name), errInfo); } @@ -600,8 +320,6 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ * If this is the first call, we must open the directory. */ - CHECK_PATHLEN(name, errInfo); - if (*p_dir_handle == NULL) { dp = opendir(name); if (dp == NULL) @@ -641,26 +359,13 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ struct stat statbuf; int fd; int mode; /* Open mode. */ -#ifdef VXWORKS - char pathbuff[PATH_MAX+2]; - char sbuff[PATH_MAX*2]; - char *totbuff = sbuff; - int nameneed; -#endif - - - CHECK_PATHLEN(name, errInfo); -#ifdef VXWORKS - /* Have to check that it's not a directory. */ - if (stat(name,&statbuf) != ERROR && ISDIR(statbuf)) { - errno = EISDIR; - return check_error(-1, errInfo); - } -#endif - - if (stat(name, &statbuf) >= 0 && !ISREG(statbuf)) { -#if !defined(VXWORKS) && !defined(OSE) + if (stat(name, &statbuf) < 0) { + /* statbuf is undefined: if the caller depends on it, + i.e. invoke_read_file(), fail the call immediately */ + if (pSize && flags == EFILE_MODE_READ) + return check_error(-1, errInfo); + } else if (!ISREG(statbuf)) { /* * For UNIX only, here is some ugly code to allow * /dev/null to be opened as a file. @@ -677,12 +382,9 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ } } if (!(dev_null_ino && statbuf.st_ino == dev_null_ino)) { -#endif errno = EISDIR; return check_error(-1, errInfo); -#if !defined(VXWORKS) && !defined(OSE) } -#endif } switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) { @@ -706,49 +408,23 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ if (flags & EFILE_MODE_APPEND) { mode &= ~O_TRUNC; -#ifndef VXWORKS - mode |= O_APPEND; /* Dont make VxWorks think things it shouldn't */ -#endif + mode |= O_APPEND; } if (flags & EFILE_MODE_EXCL) { mode |= O_EXCL; } -#ifdef VXWORKS - if (*name != '/') { - /* Make sure it is an absolute pathname, because ftruncate needs it */ - ioDefPathGet(pathbuff); - strcat(pathbuff,"/"); - nameneed = strlen(pathbuff) + strlen(name) + 1; - if (nameneed > PATH_MAX*2) - totbuff = EF_SAFE_ALLOC(nameneed); - strcpy(totbuff,pathbuff); - strcat(totbuff,name); - fd = open(totbuff, mode, FILE_MODE); - if (totbuff != sbuff) - EF_FREE(totbuff); - } else { - fd = open(name, mode, FILE_MODE); - } + if (flags & EFILE_MODE_SYNC) { +#ifdef O_SYNC + mode |= O_SYNC; #else - fd = open(name, mode, FILE_MODE); -#endif - -#ifdef VXWORKS - - /* This is a VxWorks/nfs workaround for erl_tar to create - * non-existant directories. (of some reason (...) VxWorks - * returns, the *non-module-prefixed*, 0xd code when - * trying to write a file in a directory that doesn't exist). - * (see efile_mkdir) - */ - if ((fd < 0) && (strchr(name, '/') != NULL) && (errno == 0xd)) { - /* Return the correct error code enoent */ - errno = S_nfsLib_NFSERR_NOENT; + errno = ENOTSUP; return check_error(-1, errInfo); - } #endif + } + + fd = open(name, mode, FILE_MODE); if (!check_error(fd, errInfo)) return 0; @@ -797,11 +473,7 @@ efile_fsync(Efile_error *errInfo, /* Where to return error codes. */ int fd) /* File descriptor for file to sync. */ { #ifdef NO_FSYNC -#ifdef VXWORKS - return check_error(ioctl(fd, FIOSYNC, 0), errInfo); -#else - undefined fsync -#endif /* VXWORKS */ + undefined fsync /* XXX: Really? */ #else #if defined(DARWIN) && defined(F_FULLFSYNC) return check_error(fcntl(fd, F_FULLFSYNC), errInfo); @@ -818,21 +490,8 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, struct stat statbuf; /* Information about the file */ int result; -#ifdef VXWORKS - if (*name == '\0') { - errInfo->posix_errno = errInfo->os_errno = ENOENT; - return 0; - } -#endif - - CHECK_PATHLEN(name, errInfo); - if (info_for_link) { -#if (defined(VXWORKS)) - result = stat(name, &statbuf); -#else result = lstat(name, &statbuf); -#endif } else { result = stat(name, &statbuf); } @@ -849,19 +508,9 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, #ifdef NO_ACCESS /* Just look at read/write access for owner. */ -#ifdef VXWORKS - - pInfo->access = FA_NONE; - if(statbuf.st_mode & S_IRUSR) - pInfo->access |= FA_READ; - if(statbuf.st_mode & S_IWUSR) - pInfo->access |= FA_WRITE; - -#else pInfo->access = ((statbuf.st_mode >> 6) & 07) >> 1; -#endif /* VXWORKS */ #else pInfo->access = FA_NONE; if (access(name, R_OK) == 0) @@ -902,35 +551,6 @@ efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name) { struct utimbuf tval; - CHECK_PATHLEN(name, errInfo); - -#ifdef VXWORKS - - if (pInfo->mode != -1) { - int fd; - struct stat statbuf; - - fd = open(name, O_RDONLY, 0); - if (!check_error(fd, errInfo)) - return 0; - if (fstat(fd, &statbuf) < 0) { - close(fd); - return check_error(-1, errInfo); - } - if (pInfo->mode & S_IWUSR) { - /* clear read only bit */ - statbuf.st_attrib &= ~DOS_ATTR_RDONLY; - } else { - /* set read only bit */ - statbuf.st_attrib |= DOS_ATTR_RDONLY; - } - /* This should work for dos files but not for nfs ditos, so don't - * report errors (to avoid problems when running e.g. erl_tar) - */ - ioctl(fd, FIOATTRIBSET, statbuf.st_attrib); - close(fd); - } -#else /* * On some systems chown will always fail for a non-root user unless * POSIX_CHOWN_RESTRICTED is not set. Others will succeed as long as @@ -952,20 +572,10 @@ efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name) } } -#endif /* !VXWORKS */ - tval.actime = pInfo->accessTime; tval.modtime = pInfo->modifyTime; -#ifdef VXWORKS - /* VxWorks' utime doesn't work when the file is a nfs mounted - * one, don't report error if utime fails. - */ - utime(name, &tval); - return 1; -#else return check_error(utime(name, &tval), errInfo); -#endif } @@ -979,11 +589,6 @@ efile_write(Efile_error* errInfo, /* Where to return error codes. */ { ssize_t written; /* Bytes written in last operation. */ -#ifdef VXWORKS - if (flags & EFILE_MODE_APPEND) { - lseek(fd, 0, SEEK_END); /* Naive append emulation on VXWORKS */ - } -#endif while (count > 0) { if ((written = write(fd, buf, count)) < 0) { if (errno != EINTR) @@ -1012,12 +617,6 @@ efile_writev(Efile_error* errInfo, /* Where to return error codes */ ASSERT(iovcnt >= 0); -#ifdef VXWORKS - if (flags & EFILE_MODE_APPEND) { - lseek(fd, 0, SEEK_END); /* Naive append emulation on VXWORKS */ - } -#endif - while (cnt < iovcnt) { if ((! iov[cnt].iov_base) || (iov[cnt].iov_len <= 0)) { /* Empty buffer - skip */ @@ -1040,7 +639,8 @@ efile_writev(Efile_error* errInfo, /* Where to return error codes */ do { w = write(fd, iov[cnt].iov_base, iov[cnt].iov_len); } while (w < 0 && errno == EINTR); - ASSERT(w <= iov[cnt].iov_len); + ASSERT(w <= iov[cnt].iov_len || + (w == -1 && errno != EINTR)); } if (w < 0) return check_error(-1, errInfo); /* Move forward to next buffer to write */ @@ -1049,7 +649,7 @@ efile_writev(Efile_error* errInfo, /* Where to return error codes */ if (w < iov[cnt].iov_len) { /* Adjust the buffer for next write */ iov[cnt].iov_len -= w; - iov[cnt].iov_base += w; + iov[cnt].iov_base = ((char *)iov[cnt].iov_base) + w; w = 0; break; } else { @@ -1226,118 +826,6 @@ efile_seek(Efile_error* errInfo, /* Where to return error codes. */ int efile_truncate_file(Efile_error* errInfo, int *fd, int flags) { -#ifdef VXWORKS - off_t offset; - char namebuf[PATH_MAX+1]; - char namebuf2[PATH_MAX+10]; - int new; - int dummy; - int i; - int left; - static char buff[1024]; - struct stat st; - Efile_error tmperr; - - if ((offset = lseek(*fd, 0, 1)) < 0) { - return check_error((int) offset,errInfo); - } - if (ftruncate(*fd, offset) < 0) { - if (vxworks_to_posix(errno) != EINVAL) { - return check_error(-1, errInfo); - } - /* - ** Kludge - */ - if(ioctl(*fd,FIOGETNAME,(int) namebuf) < 0) { - return check_error(-1, errInfo); - } - for(i=0;i<1000;++i) { - sprintf(namebuf2,"%s%d",namebuf,i); - CHECK_PATHLEN(namebuf2,errInfo); - if (stat(namebuf2,&st) < 0) { - break; - } - } - if (i > 1000) { - errno = EINVAL; - return check_error(-1, errInfo); - } - if (close(*fd) < 0) { - return check_error(-1, errInfo); - } - if (efile_rename(&tmperr,namebuf,namebuf2) < 0) { - i = check_error(-1,&tmperr); - if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE, - fd,&dummy)) { - *fd = -1; - } else { - *errInfo = tmperr; - } - return i; - } - if ((*fd = open(namebuf2, O_RDONLY, 0)) < 0) { - i = check_error(-1,errInfo); - efile_rename(&tmperr,namebuf2,namebuf); /* at least try */ - if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE, - fd,&dummy)) { - *fd = -1; - } else { - lseek(*fd,offset,SEEK_SET); - } - return i; - } - /* Point of no return... */ - - if ((new = open(namebuf,O_RDWR | O_CREAT, FILE_MODE)) < 0) { - close(*fd); - *fd = -1; - return 0; - } - left = offset; - - while (left) { - if ((i = read(*fd,buff,(left > 1024) ? 1024 : left)) < 0) { - i = check_error(-1,errInfo); - close(new); - close(*fd); - unlink(namebuf); - efile_rename(&tmperr,namebuf2,namebuf); /* at least try */ - if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE, - fd,&dummy)) { - *fd = -1; - } else { - lseek(*fd,offset,SEEK_SET); - } - return i; - } - left -= i; - if (write(new,buff,i) < 0) { - i = check_error(-1,errInfo); - close(new); - close(*fd); - unlink(namebuf); - rename(namebuf2,namebuf); /* at least try */ - if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE, - fd,&dummy)) { - *fd = -1; - } else { - lseek(*fd,offset,SEEK_SET); - } - return i; - } - } - close(*fd); - unlink(namebuf2); - close(new); - i = efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,fd, - &dummy); - if (i) { - lseek(*fd,offset,SEEK_SET); - } - return i; - } - return 1; -#else #ifndef NO_FTRUNCATE off_t offset; @@ -1347,15 +835,11 @@ efile_truncate_file(Efile_error* errInfo, int *fd, int flags) #else return 1; #endif -#endif } int efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size) { -#ifdef VXWORKS - return vxworks_enotsup(errInfo); -#else int len; ASSERT(size > 0); len = readlink(name, buffer, size-1); @@ -1364,7 +848,6 @@ efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size) } buffer[len] = '\0'; return 1; -#endif } int @@ -1377,21 +860,13 @@ efile_altname(Efile_error* errInfo, char* name, char* buffer, size_t size) int efile_link(Efile_error* errInfo, char* old, char* new) { -#ifdef VXWORKS - return vxworks_enotsup(errInfo); -#else return check_error(link(old, new), errInfo); -#endif } int efile_symlink(Efile_error* errInfo, char* old, char* new) { -#ifdef VXWORKS - return vxworks_enotsup(errInfo); -#else return check_error(symlink(old, new), errInfo); -#endif } int @@ -1406,8 +881,8 @@ efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, } #ifdef HAVE_SENDFILE -// For some reason the maximum size_t cannot be used as the max size -// 3GB seems to work on all platforms +/* For some reason the maximum size_t cannot be used as the max size + 3GB seems to work on all platforms */ #define SENDFILE_CHUNK_SIZE ((1UL << 30) -1) /* @@ -1444,7 +919,7 @@ efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd, #if defined(__linux__) ssize_t retval; do { - // check if *nbytes is 0 or greater than chunk size + /* check if *nbytes is 0 or greater than chunk size */ if (*nbytes == 0 || *nbytes > SENDFILE_CHUNK_SIZE) retval = sendfile(out_fd, in_fd, offset, SENDFILE_CHUNK_SIZE); else @@ -1455,7 +930,7 @@ efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd, } } while (retval == SENDFILE_CHUNK_SIZE); if (written != 0) { - // -1 is not returned by the linux API so we have to simulate it + /* -1 is not returned by the linux API so we have to simulate it */ retval = -1; errno = EAGAIN; } @@ -1468,23 +943,29 @@ efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd, do { fdrec.sfv_off = *offset; len = 0; - // check if *nbytes is 0 or greater than chunk size + /* check if *nbytes is 0 or greater than chunk size */ if (*nbytes == 0 || *nbytes > SENDFILE_CHUNK_SIZE) fdrec.sfv_len = SENDFILE_CHUNK_SIZE; else fdrec.sfv_len = *nbytes; retval = sendfilev(out_fd, &fdrec, 1, &len); - if (retval != -1 || errno == EAGAIN || errno == EINTR) { + + /* Sometimes sendfilev can return -1 and still send data. + When that happens we just pretend that no error happend. */ + if (retval != -1 || errno == EAGAIN || errno == EINTR || + len != 0) { *offset += len; *nbytes -= len; written += len; + if (errno != EAGAIN && errno != EINTR && len != 0) + retval = len; } } while (len == SENDFILE_CHUNK_SIZE); #elif defined(DARWIN) int retval; off_t len; do { - // check if *nbytes is 0 or greater than chunk size + /* check if *nbytes is 0 or greater than chunk size */ if(*nbytes > SENDFILE_CHUNK_SIZE) len = SENDFILE_CHUNK_SIZE; else @@ -1516,3 +997,81 @@ efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd, return check_error(retval, errInfo); } #endif /* HAVE_SENDFILE */ + +#ifdef HAVE_POSIX_FALLOCATE +static int +call_posix_fallocate(int fd, Sint64 offset, Sint64 length) +{ + int ret; + + /* + * On Linux and Solaris for example, posix_fallocate() returns + * a positive error number on error and it does not set errno. + * On FreeBSD however (9.0 at least), it returns -1 on error + * and it sets errno. + */ + do { + ret = posix_fallocate(fd, (off_t) offset, (off_t) length); + if (ret > 0) { + errno = ret; + ret = -1; + } + } while (ret != 0 && errno == EINTR); + + return ret; +} +#endif /* HAVE_POSIX_FALLOCATE */ + +int +efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length) +{ +#if defined HAVE_FALLOCATE + /* Linux specific, more efficient than posix_fallocate. */ + int ret; + + do { + ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, (off_t) offset, (off_t) length); + } while (ret != 0 && errno == EINTR); + +#if defined HAVE_POSIX_FALLOCATE + /* Fallback to posix_fallocate if available. */ + if (ret != 0) { + ret = call_posix_fallocate(fd, offset, length); + } +#endif + + return check_error(ret, errInfo); +#elif defined F_PREALLOCATE + /* Mac OS X specific, equivalent to posix_fallocate. */ + int ret; + fstore_t fs; + + memset(&fs, 0, sizeof(fs)); + fs.fst_flags = F_ALLOCATECONTIG; + fs.fst_posmode = F_VOLPOSMODE; + fs.fst_offset = (off_t) offset; + fs.fst_length = (off_t) length; + + ret = fcntl(fd, F_PREALLOCATE, &fs); + + if (-1 == ret) { + fs.fst_flags = F_ALLOCATEALL; + ret = fcntl(fd, F_PREALLOCATE, &fs); + +#if defined HAVE_POSIX_FALLOCATE + /* Fallback to posix_fallocate if available. */ + if (-1 == ret) { + ret = call_posix_fallocate(fd, offset, length); + } +#endif + } + + return check_error(ret, errInfo); +#elif defined HAVE_POSIX_FALLOCATE + /* Other Unixes, use posix_fallocate if available. */ + return check_error(call_posix_fallocate(fd, offset, length), errInfo); +#else + errno = ENOTSUP; + return check_error(-1, errInfo); +#endif +} diff --git a/erts/emulator/drivers/win32/registry_drv.c b/erts/emulator/drivers/win32/registry_drv.c index 1fad34e380..e908c956ae 100644 --- a/erts/emulator/drivers/win32/registry_drv.c +++ b/erts/emulator/drivers/win32/registry_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2011. All Rights Reserved. + * Copyright Ericsson AB 1997-2013. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -344,7 +344,7 @@ fix_value_result(RegPort* rp, LONG result, DWORD type, #ifdef DEBUG if (ok != ERROR_SUCCESS) { char buff[256]; - sprintf(buff,"Failure in registry_drv line %d, error = %d", + erts_snprintf(buff, sizeof(buff), "Failure in registry_drv line %d, error = %d", __LINE__, GetLastError()); MessageBox(NULL, buff, "Internal error", MB_OK); ASSERT(ok == ERROR_SUCCESS); diff --git a/erts/emulator/drivers/win32/ttsl_drv.c b/erts/emulator/drivers/win32/ttsl_drv.c index 1a74d21e99..502cb58dfa 100644 --- a/erts/emulator/drivers/win32/ttsl_drv.c +++ b/erts/emulator/drivers/win32/ttsl_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2011. All Rights Reserved. + * Copyright Ericsson AB 1996-2013. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -414,12 +414,12 @@ static int check_buf_size(byte *s, int n) } if (utf8_mode) { /* That is, terminal is UTF8 compliant */ if (ch >= 128 || isprint(ch)) { - DEBUGLOG(("Printable(UTF-8:%d):%d",(pos - opos),ch)); + DEBUGLOG(("Printable(UTF-8:%d):%d",pos,ch)); size++; /* Buffer contains wide characters... */ } else if (ch == '\t') { size += 8; } else { - DEBUGLOG(("Magic(UTF-8:%d):%d",(pos - opos),ch)); + DEBUGLOG(("Magic(UTF-8:%d):%d",pos,ch)); size += 2; } } else { diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index dc7add01f7..a321bb9641 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2012. All Rights Reserved. + * Copyright Ericsson AB 1997-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -29,18 +29,35 @@ #include <wchar.h> #include "erl_efile.h" +#define DBG_TRACE_MASK 0 +/* 1 = file name ops + * 2 = file descr ops + * 4 = errors + * 8 = path name conversion + */ +#if !DBG_TRACE_MASK +# define DBG_TRACE(M,S) +# define DBG_TRACE1(M,FMT,A) +# define DBG_TRACE2(M,FMT,A,B) +#else +# define DBG_TRACE(M,S) do { if ((M)&DBG_TRACE_MASK) fwprintf(stderr, L"DBG_TRACE %d: %s\r\n", __LINE__, (WCHAR*)(S)); }while(0) +# define DBG_TRACE1(M,FMT,A) do { if ((M)&DBG_TRACE_MASK) fwprintf(stderr, L"DBG_TRACE %d: " L##FMT L"\r\n", __LINE__, (A)); }while(0) +# define DBG_TRACE2(M,FMT,A,B) do { if ((M)&DBG_TRACE_MASK) fwprintf(stderr, L"DBG_TRACE %d: " L##FMT L"\r\n", __LINE__, (A), (B)); }while(0) +#endif + /* * Microsoft-specific function to map a WIN32 error code to a Posix errno. */ #define ISSLASH(a) ((a) == L'\\' || (a) == L'/') - #define ISDIR(st) (((st).st_mode&S_IFMT) == S_IFDIR) #define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG) #define IS_DOT_OR_DOTDOT(s) \ ((s)[0] == L'.' && ((s)[1] == L'\0' || ((s)[1] == L'.' && (s)[2] == L'\0'))) +#define FILE_SHARE_FLAGS (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE) + #ifndef INVALID_FILE_ATTRIBUTES #define INVALID_FILE_ATTRIBUTES ((DWORD) 0xFFFFFFFF) #endif @@ -67,10 +84,92 @@ static int check_error(int result, Efile_error* errInfo); static int set_error(Efile_error* errInfo); +static int set_os_errno(Efile_error* errInfo, DWORD os_errno); static int is_root_unc_name(const WCHAR *path); static int extract_root(WCHAR *name); static unsigned short dos_to_posix_mode(int attr, const WCHAR *name); + +struct wpath_tmp_buffer { + struct wpath_tmp_buffer* next; + WCHAR buffer[1]; +}; + +typedef struct { + Efile_error* errInfo; + struct wpath_tmp_buffer* buf_list; +}Efile_call_state; + +static void call_state_init(Efile_call_state* state, Efile_error* errInfo) +{ + state->errInfo = errInfo; + state->buf_list = NULL; +} +static WCHAR* wpath_tmp_alloc(Efile_call_state* state, size_t len) +{ + size_t sz = offsetof(struct wpath_tmp_buffer, buffer) + + (len+1)*sizeof(WCHAR); + struct wpath_tmp_buffer* p = driver_alloc(sz); + p->next = state->buf_list; + state->buf_list = p; + return p->buffer; +} +static void call_state_free(Efile_call_state* state) +{ + while(state->buf_list) { + struct wpath_tmp_buffer* next = state->buf_list->next; + driver_free(state->buf_list); + state->buf_list = next; + } +} +static WCHAR* get_cwd_wpath_tmp(Efile_call_state* state) +{ + WCHAR dummy; + DWORD size = GetCurrentDirectoryW(0, &dummy); + WCHAR* ret = NULL; + + if (size) { + ret = wpath_tmp_alloc(state, size); + if (!GetCurrentDirectoryW(size, ret)) { + ret = NULL; + } + } + return ret; +} +static WCHAR* get_full_wpath_tmp(Efile_call_state* state, + const WCHAR* file, + WCHAR** file_part, + DWORD extra) +{ + WCHAR dummy; + DWORD size = GetFullPathNameW(file, 0, &dummy, NULL); + WCHAR* ret = NULL; + + if (size) { + int ok; + ret = wpath_tmp_alloc(state, size + extra); + if (file_part) { + ok = (GetFullPathNameW(file, size, ret, file_part) != 0); + } + else { + ok = (_wfullpath(ret, file, size) != NULL); + } + if (!ok) { + ret = NULL; + } + } + return ret; +} + +static void ensure_wpath_max(Efile_call_state* state, WCHAR** pathp, size_t max); +static int do_rmdir(Efile_call_state*, char* name); +static int do_rename(Efile_call_state*, char* src, char* dst); +static int do_readdir(Efile_call_state*, char* name, EFILE_DIR_HANDLE*, char* buffer, size_t *size); +static int do_fileinfo(Efile_call_state*, Efile_info*, char* orig_name, int info_for_link); +static char* do_readlink(Efile_call_state*, char* name, char* buffer, size_t size); +static int do_altname(Efile_call_state*, char* orig_name, char* buffer, size_t size); + + static int errno_map(DWORD last_error) { switch (last_error) { @@ -152,6 +251,8 @@ static int errno_map(DWORD last_error) { return EAGAIN; case ERROR_CANT_RESOLVE_FILENAME: return EMLINK; + case ERROR_PRIVILEGE_NOT_HELD: + return EPERM; case ERROR_ARENA_TRASHED: case ERROR_INVALID_BLOCK: case ERROR_BAD_ENVIRONMENT: @@ -174,11 +275,23 @@ check_error(int result, Efile_error* errInfo) if (result < 0) { errInfo->posix_errno = errno; errInfo->os_errno = GetLastError(); + DBG_TRACE2(4, "ERROR os_error=%d errno=%d @@@@@@@@@@@@@@@@@@@@@@@@@@@@", + errInfo->os_errno, errInfo->posix_errno); return 0; } return 1; } +static void +save_last_error(Efile_error* errInfo) +{ + errInfo->posix_errno = errno; + errInfo->os_errno = GetLastError(); + DBG_TRACE2(4, "ERROR os_error=%d errno=%d $$$$$$$$$$$$$$$$$$$$$$$$$$$$$", + errInfo->os_errno, errInfo->posix_errno); +} + + /* * Fills the provided error information structure with information * with the error code given by GetLastError() and its corresponding @@ -190,10 +303,26 @@ check_error(int result, Efile_error* errInfo) static int set_error(Efile_error* errInfo) { - errInfo->posix_errno = errno_map(errInfo->os_errno = GetLastError()); + set_os_errno(errInfo, GetLastError()); + return 0; +} + + +static int +set_os_errno(Efile_error* errInfo, DWORD os_errno) +{ + errInfo->os_errno = os_errno; + errInfo->posix_errno = errno_map(os_errno); + DBG_TRACE2(4, "ERROR os_error=%d errno=%d ############################", + errInfo->os_errno, errInfo->posix_errno); return 0; } +int +efile_init() { + return 1; +} + /* * A writev with Unix semantics, but with Windows arguments */ @@ -219,21 +348,151 @@ win_writev(Efile_error* errInfo, } +/* Check '*pathp' and convert it if needed to something that windows will accept. + * Typically use UNC path with \\?\ prefix if absolute path is longer than 260. + */ +static void ensure_wpath(Efile_call_state* state, WCHAR** pathp) +{ + ensure_wpath_max(state, pathp, MAX_PATH); +} + +static void ensure_wpath_max(Efile_call_state* state, WCHAR** pathp, size_t max) +{ + WCHAR* path = *pathp; + WCHAR* p; + size_t len = wcslen(path); + int unc_fixup = 0; + + if (path[0] == 0) { + DBG_TRACE(8, L"Let empty path pass through"); + return; + } + + DBG_TRACE1(8,"IN: %s", path); + + if (path[1] == L':' && ISSLASH(path[2])) { /* absolute path */ + if (len >= max) { + WCHAR *src, *dst; + + *pathp = wpath_tmp_alloc(state, 4+len+1); + dst = *pathp; + wcscpy(dst, L"\\\\?\\"); + for (src=path,dst+=4; *src; src++) { + if (*src == L'/') { + if (dst[-1] != L'\\') { + *dst++ = L'\\'; + } + /*else ignore redundant slashes */ + } + else + *dst++ = *src; + } + *dst = 0; + unc_fixup = 1; + } + } + else if (!(ISSLASH(path[0]) && ISSLASH(path[1]))) { /* relative path */ + DWORD cwdLen = GetCurrentDirectoryW(0, NULL); + DWORD absLen = cwdLen + 1 + len; + if (absLen >= max) { + WCHAR *fullPath = wpath_tmp_alloc(state, 4+4+absLen); + DWORD fullLen; + + fullLen = GetFullPathNameW(path, 4 + absLen, fullPath+4, NULL); + if (fullLen >= 4+absLen) { + *pathp = path; + DBG_TRACE2(8,"ensure_wpath FAILED absLen=%u %s", (int)absLen, path); + return; + } + /* GetFullPathNameW can return paths longer than MAX_PATH without the \\?\ prefix. + * At least seen on Windows 7. Go figure... + */ + if (fullLen >= max && wcsncmp(fullPath+4, L"\\\\?\\", 4) != 0) { + wcsncpy(fullPath, L"\\\\?\\", 4); + *pathp = fullPath; + } + else { + *pathp = fullPath + 4; + } + } + } + + if (unc_fixup) { + WCHAR* endp; + + p = *pathp; + len = wcslen(p); + endp = p + len; + if (len > 4) { + p += 4; + while (*p) { + if (p[0] == L'\\' && p[1] == L'.') { + if (p[2] == L'\\' || !p[2]) { /* single dot */ + wmemmove(p, p+2, (&endp[1] - &p[2])); + endp -= 2; + } + else if (p[2] == L'.' && (p[3] == L'\\' || !p[3])) { /* double dot */ + WCHAR* r; + for (r=p-1; *r == L'\\'; --r) + /*skip redundant slashes*/; + for (; *r != L'\\'; --r) + /*find start of prev directory*/; + if (r < *pathp + 6) + break; + wmemmove(r, p+3, (&endp[1] - &p[3])); + p = r; + } + else p += 3; + } + else ++p; + } + } + } + DBG_TRACE1(8,"OUT: %s", *pathp); +} int efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of directory to create. */ { - return check_error(_wmkdir((WCHAR *) name), errInfo); + Efile_call_state state; + WCHAR* wname = (WCHAR*)name; + int ret; + + DBG_TRACE(1, name); + call_state_init(&state, errInfo); + ensure_wpath_max(&state, &wname, 248); /* Yes, 248 limit for normal paths */ + + ret = (int) CreateDirectoryW(wname, NULL); + if (!ret) + set_error(errInfo); + + call_state_free(&state); + return ret; } int efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of directory to delete. */ { + Efile_call_state state; + int ret; + + DBG_TRACE(1, name); + call_state_init(&state, errInfo); + ret = do_rmdir(&state, name); + call_state_free(&state); + return ret; +} + +static int do_rmdir(Efile_call_state* state, char* name) +{ OSVERSIONINFO os; DWORD attr; WCHAR *wname = (WCHAR *) name; + WCHAR *buffer = NULL; + + ensure_wpath(state, &wname); if (RemoveDirectoryW(wname) != FALSE) { return 1; @@ -263,10 +522,9 @@ efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ if (os.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { HANDLE handle; WIN32_FIND_DATAW data; - WCHAR buffer[2*MAX_PATH]; - int len; + int len = wcslen(wname); - len = wcslen(wname); + buffer = wpath_tmp_alloc(state, len + 4); wcscpy(buffer, wname); if (buffer[0] && buffer[len-1] != L'\\' && buffer[len-1] != L'/') { wcscat(buffer, L"\\"); @@ -304,16 +562,30 @@ efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ } end: - return check_error(-1, errInfo); + save_last_error(state->errInfo); + return 0; } int efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of file to delete. */ { + Efile_call_state state; + int ret; + DBG_TRACE(1, name); + call_state_init(&state, errInfo); + ret = do_delete_file(&state, name); + call_state_free(&state); + return ret; +} + +static int do_delete_file(Efile_call_state* state, char* name) +{ DWORD attr; WCHAR *wname = (WCHAR *) name; + ensure_wpath(state, &wname); + if (DeleteFileW(wname) != FALSE) { return 1; } @@ -352,7 +624,7 @@ efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ errno = EACCES; } - return check_error(-1, errInfo); + return check_error(-1, state->errInfo); } /* @@ -386,14 +658,29 @@ efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ */ int -efile_rename(Efile_error* errInfo, /* Where to return error codes. */ - char* src, /* Original name. */ - char* dst) /* New name. */ +efile_rename(Efile_error* errInfo, char* src, char* dst) +{ + Efile_call_state state; + int ret; + DBG_TRACE(1, src); + call_state_init(&state, errInfo); + ret = do_rename(&state, src, dst); + call_state_free(&state); + return ret; +} + +static int +do_rename(Efile_call_state* state, + char* src, /* Original name. */ + char* dst) /* New name. */ { DWORD srcAttr, dstAttr; WCHAR *wsrc = (WCHAR *) src; WCHAR *wdst = (WCHAR *) dst; - + + ensure_wpath(state, &wsrc); + ensure_wpath(state, &wdst); + if (MoveFileW(wsrc, wdst) != FALSE) { return 1; } @@ -410,23 +697,27 @@ efile_rename(Efile_error* errInfo, /* Where to return error codes. */ if (errno == EBADF) { errno = EACCES; - return check_error(-1, errInfo); + return check_error(-1, state->errInfo); } if (errno == EACCES) { decode: if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) { - WCHAR srcPath[MAX_PATH], dstPath[MAX_PATH]; + WCHAR *srcPath, *dstPath; WCHAR *srcRest, *dstRest; int size; - size = GetFullPathNameW(wsrc, MAX_PATH, srcPath, &srcRest); - if ((size == 0) || (size > MAX_PATH)) { - return check_error(-1, errInfo); + srcPath = get_full_wpath_tmp(state, wsrc, &srcRest, 0); + if (!srcPath) { + save_last_error(state->errInfo); + return 0; } - size = GetFullPathNameW(wdst, MAX_PATH, dstPath, &dstRest); - if ((size == 0) || (size > MAX_PATH)) { - return check_error(-1, errInfo); + + dstPath = get_full_wpath_tmp(state, wdst, &dstRest, 0); + if (!dstPath) { + save_last_error(state->errInfo); + return 0; } + if (srcRest == NULL) { srcRest = srcPath + wcslen(srcPath); } @@ -531,14 +822,16 @@ efile_rename(Efile_error* errInfo, /* Where to return error codes. */ * put temp file back to old name. */ - WCHAR tempName[MAX_PATH]; - int result, size; + WCHAR *tempName; + int result; WCHAR *rest; - size = GetFullPathNameW(wdst, MAX_PATH, tempName, &rest); - if ((size == 0) || (size > MAX_PATH) || (rest == NULL)) { - return check_error(-1, errInfo); + tempName = get_full_wpath_tmp(state, wdst, &rest, 14); + if (!tempName || !rest) { + save_last_error(state->errInfo); + return 0; } + *rest = L'\0'; result = -1; if (GetTempFileNameW(tempName, L"erlr", 0, tempName) != 0) { @@ -571,7 +864,6 @@ efile_rename(Efile_error* errInfo, /* Where to return error codes. */ /* * Decode the EACCES to a more meaningful error. */ - goto decode; } } @@ -579,16 +871,20 @@ efile_rename(Efile_error* errInfo, /* Where to return error codes. */ } } } - return check_error(-1, errInfo); + return check_error(-1, state->errInfo); } int efile_chdir(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of directory to make current. */ -{ - int success = check_error(_wchdir((WCHAR *) name), errInfo); - if (!success && errInfo->posix_errno == EINVAL) - /* POSIXification of errno */ +{
+ /* We don't even try to handle long paths here
+ * as current working directory is always limited to MAX_PATH
+ * even if we use UNC paths and SetCurrentDirectoryW()
+ */
+ int success = check_error(_wchdir((WCHAR *) name), errInfo);
+ if (!success && errInfo->posix_errno == EINVAL)
+ /* POSIXification of errno */
errInfo->posix_errno = ENOENT; return success; } @@ -601,28 +897,45 @@ efile_getdcwd(Efile_error* errInfo, /* Where to return error codes. */ { WCHAR *wbuffer = (WCHAR *) buffer; size_t wbuffer_size = size / 2; - if (_wgetdcwd(drive, wbuffer, wbuffer_size) == NULL) + DBG_TRACE(1, L"#getdcwd#"); + if (_wgetdcwd(drive, wbuffer, wbuffer_size) == NULL) { return check_error(-1, errInfo); + } + DBG_TRACE1(8, "getdcwd OS=%s", wbuffer); + if (wcsncmp(wbuffer, L"\\\\?\\", 4) == 0) { + wmemmove(wbuffer, wbuffer+4, wcslen(wbuffer+4)+1); + } for ( ; *wbuffer; wbuffer++) if (*wbuffer == L'\\') *wbuffer = L'/'; + DBG_TRACE1(8, "getdcwd ERLANG=%s", (WCHAR*)buffer); return 1; } int -efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ - char* name, /* Name of directory to list */ - EFILE_DIR_HANDLE* dir_handle, /* Handle of opened directory or NULL */ - char* buffer, /* Buffer to put one filename in */ - size_t *size) /* in-out size of buffer/size of filename excluding zero - termination in bytes*/ +efile_readdir(Efile_error* errInfo, char* name, EFILE_DIR_HANDLE* dir_handle, + char* buffer, size_t *size) +{ + Efile_call_state state; + int ret; + DBG_TRACE(dir_handle?2:1, name); + call_state_init(&state, errInfo); + ret = do_readdir(&state, name, dir_handle, buffer, size); + call_state_free(&state); + return ret; +} + +static int do_readdir(Efile_call_state* state, + char* name, /* Name of directory to list */ + EFILE_DIR_HANDLE* dir_handle, /* Handle of opened directory or NULL */ + char* buffer, /* Buffer to put one filename in */ + size_t *size) /* in-out size of buffer/size of filename excluding zero + termination in bytes*/ { HANDLE dir; /* Handle to directory. */ - WCHAR wildcard[MAX_PATH]; /* Wildcard to search for. */ WIN32_FIND_DATAW findData; /* Data found by FindFirstFile() or FindNext(). */ /* Alignment is not honored, this works on x86 because of alignment fixup by processor. Not perfect, but faster than alinging by hand (really) */ - WCHAR *wname = (WCHAR *) name; WCHAR *wbuffer = (WCHAR *) buffer; /* @@ -630,13 +943,15 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ */ if (*dir_handle == NULL) { - int length = wcslen(wname); + WCHAR *wname = (WCHAR *) name; + WCHAR* wildcard; + int length; WCHAR* s; - if (length+3 >= MAX_PATH) { - errno = ENAMETOOLONG; - return check_error(-1, errInfo); - } + ensure_wpath_max(state, &wname, MAX_PATH-2); + length = wcslen(wname); + + wildcard = wpath_tmp_alloc(state, length+3); wcscpy(wildcard, wname); s = wildcard+length-1; @@ -646,8 +961,10 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ *++s = L'\0'; DEBUGF(("Reading %ws\n", wildcard)); dir = FindFirstFileW(wildcard, &findData); - if (dir == INVALID_HANDLE_VALUE) - return set_error(errInfo); + if (dir == INVALID_HANDLE_VALUE) { + set_error(state->errInfo); + return 0; + } *dir_handle = (EFILE_DIR_HANDLE) dir; if (!IS_DOT_OR_DOTDOT(findData.cFileName)) { @@ -657,7 +974,6 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ } } - /* * Retrieve the name of the next file using the directory handle. */ @@ -674,28 +990,41 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ } if (GetLastError() == ERROR_NO_MORE_FILES) { - FindClose(dir); - errInfo->posix_errno = errInfo->os_errno = 0; - return 0; + state->errInfo->posix_errno = state->errInfo->os_errno = 0; + } + else { + set_error(state->errInfo); } - - set_error(errInfo); FindClose(dir); return 0; } } int -efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ - char* name, /* Name of directory to open. */ - int flags, /* Flags to use for opening. */ - int* pfd, /* Where to store the file descriptor. */ - Sint64* pSize) /* Where to store the size of the file. */ +efile_openfile(Efile_error* errInfo, char* name, int flags, int* pfd, Sint64* pSize) { + Efile_call_state state; + int ret; + DBG_TRACE1(1, "openfile(%s)", name); + call_state_init(&state, errInfo); + ret = do_openfile(&state, name, flags, pfd, pSize); + call_state_free(&state); + return ret; +} + +static +int do_openfile(Efile_call_state* state, /* Where to return error codes. */ + char* name, /* Name of directory to open. */ + int flags, /* Flags to use for opening. */ + int* pfd, /* Where to store the file descriptor. */ + Sint64* pSize) /* Where to store the size of the file. */ +{ + Efile_error* errInfo = state->errInfo; BY_HANDLE_FILE_INFORMATION fileInfo; /* File information from a handle. */ HANDLE fd; /* Handle to open file. */ DWORD access; /* Access mode: GENERIC_READ, GENERIC_WRITE. */ DWORD crFlags; + DWORD flagsAndAttrs = FILE_ATTRIBUTE_NORMAL; WCHAR *wname = (WCHAR *) name; switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) { @@ -717,15 +1046,20 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ return 0; } + if (flags & EFILE_MODE_SYNC) { + flagsAndAttrs = FILE_FLAG_WRITE_THROUGH; + } + if (flags & EFILE_MODE_APPEND) { crFlags = OPEN_ALWAYS; } if (flags & EFILE_MODE_EXCL) { crFlags = CREATE_NEW; } + ensure_wpath(state, &wname); fd = CreateFileW(wname, access, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, crFlags, FILE_ATTRIBUTE_NORMAL, NULL); + FILE_SHARE_FLAGS, + NULL, crFlags, flagsAndAttrs, NULL); /* * Check for errors. @@ -765,33 +1099,56 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ } int -efile_may_openfile(Efile_error* errInfo, char *name) { +efile_may_openfile(Efile_error* errInfo, char *name) +{ + Efile_call_state state; WCHAR *wname = (WCHAR *) name; DWORD attr; + int ret; + DBG_TRACE(1, name); + call_state_init(&state, errInfo); + ensure_wpath(&state, &wname); if ((attr = GetFileAttributesW(wname)) == INVALID_FILE_ATTRIBUTES) { - return check_error(-1, errInfo); + errno = ENOENT; + ret = check_error(-1, errInfo); } - - if (attr & FILE_ATTRIBUTE_DIRECTORY) { + else if (attr & FILE_ATTRIBUTE_DIRECTORY) { errno = EISDIR; - return check_error(-1, errInfo); + ret = check_error(-1, errInfo); } - return 1; + else ret = 1; + + call_state_free(&state); + return ret; } void efile_closefile(fd) int fd; /* File descriptor for file to close. */ { + DBG_TRACE(2, L""); CloseHandle((HANDLE) fd); } +FILE* efile_wfopen(const WCHAR* name, const WCHAR* mode) +{ + Efile_call_state state; + Efile_error dummy; + FILE* f; + call_state_init(&state, &dummy); + ensure_wpath(&state, (WCHAR**)&name); + f = _wfopen(name, mode); + call_state_free(&state); + return f; +} + int efile_fdatasync(errInfo, fd) Efile_error* errInfo; /* Where to return error codes. */ int fd; /* File descriptor for file to sync. */ { + DBG_TRACE(2, L""); /* Not available in Windows, just call regular fsync */ return efile_fsync(errInfo, fd); } @@ -801,6 +1158,7 @@ efile_fsync(errInfo, fd) Efile_error* errInfo; /* Where to return error codes. */ int fd; /* File descriptor for file to sync. */ { + DBG_TRACE(2, L""); if (!FlushFileBuffers((HANDLE) fd)) { return check_error(-1, errInfo); } @@ -811,64 +1169,87 @@ int efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, char* orig_name, int info_for_link) { + Efile_call_state state; + int ret; + DBG_TRACE(1, L""); + call_state_init(&state, errInfo); + ret = do_fileinfo(&state, pInfo, orig_name, info_for_link); + call_state_free(&state); + return ret; +} + +static int +do_fileinfo(Efile_call_state* state, Efile_info* pInfo, + char* orig_name, int info_for_link) +{ + Efile_error* errInfo = state->errInfo; HANDLE findhandle; /* Handle returned by FindFirstFile(). */ WIN32_FIND_DATAW findbuf; /* Data return by FindFirstFile(). */ - WCHAR name[_MAX_PATH]; + WCHAR* name = NULL; + WCHAR* win_path; int name_len; - WCHAR *path; - WCHAR pathbuf[_MAX_PATH]; int drive; /* Drive for filename (1 = A:, 2 = B: etc). */ - WCHAR *worig_name = (WCHAR *) orig_name; + WCHAR *worig_name = (WCHAR *) orig_name; + ensure_wpath(state, &worig_name); /* Don't allow wildcards to be interpreted by system */ - if (wcspbrk(worig_name, L"?*")) { - enoent: - errInfo->posix_errno = ENOENT; - errInfo->os_errno = ERROR_FILE_NOT_FOUND; - return 0; - } /* * Move the name to a buffer and make sure to remove a trailing * slash, because it causes FindFirstFile() to fail on Win95. */ - if ((name_len = wcslen(worig_name)) >= _MAX_PATH) { - goto enoent; - } else { - wcscpy(name, worig_name); - if (name_len > 2 && ISSLASH(name[name_len-1]) && - name[name_len-2] != L':') { - name[name_len-1] = L'\0'; - } + name_len = wcslen(worig_name); + + name = wpath_tmp_alloc(state, name_len+1); + wcscpy(name, worig_name); + if (name_len > 2 && ISSLASH(name[name_len-1]) && + name[name_len-2] != L':') { + name[name_len-1] = L'\0'; } - + + win_path = name; + if (wcsncmp(name, L"\\\\?\\", 4) == 0) { + win_path += 4; + } + + if (wcspbrk(win_path, L"?*")) { + enoent: + errInfo->posix_errno = ENOENT; + errInfo->os_errno = ERROR_FILE_NOT_FOUND; + return 0; + } + /* Try to get disk from name. If none, get current disk. */ - if (name[1] != L':') { + if (win_path[1] != L':') { + WCHAR* cwd_path = get_cwd_wpath_tmp(state); drive = 0; - if (GetCurrentDirectoryW(_MAX_PATH, pathbuf) && - pathbuf[1] == L':') { - drive = towlower(pathbuf[0]) - L'a' + 1; + if (cwd_path[1] == L':') { + drive = towlower(cwd_path[0]) - L'a' + 1; } - } else if (*name && name[2] == L'\0') { + } else if (*win_path && win_path[2] == L'\0') { /* * X: and nothing more is an error. */ errInfo->posix_errno = ENOENT; errInfo->os_errno = ERROR_FILE_NOT_FOUND; return 0; - } else - drive = towlower(*name) - L'a' + 1; + } else { + drive = towlower(*win_path) - L'a' + 1; + } findhandle = FindFirstFileW(name, &findbuf); if (findhandle == INVALID_HANDLE_VALUE) { + WCHAR* path = NULL; + if (!(wcspbrk(name, L"./\\") && - (path = _wfullpath(pathbuf, name, _MAX_PATH)) && + (path = get_full_wpath_tmp(state, name, NULL, 0)) && /* root dir. ('C:\') or UNC root dir. ('\\server\share\') */ ((wcslen(path) == 3) || is_root_unc_name(path)) && (GetDriveTypeW(path) > 1) ) ) { + errInfo->posix_errno = ENOENT; errInfo->os_errno = ERROR_FILE_NOT_FOUND; return 0; @@ -895,13 +1276,11 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, /* * given that we know this is a symlink, we should be able to find its target */ - WCHAR target_name[_MAX_PATH]; - if (efile_readlink(errInfo, (char *) name, - (char *) target_name, - _MAX_PATH * sizeof(WCHAR)) == 1) { + WCHAR* target_name = (WCHAR*) do_readlink(state, (char *) name, NULL, 0); + if (target_name) { FindClose(findhandle); - return efile_fileinfo(errInfo, pInfo, - (char *) target_name, info_for_link); + return do_fileinfo(state, pInfo, + (char *) target_name, info_for_link); } } @@ -909,7 +1288,7 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, { HANDLE handle; /* Handle returned by CreateFile() */ BY_HANDLE_FILE_INFORMATION fileInfo; /* from CreateFile() */ - if (handle = CreateFileW(name, GENERIC_READ, 0,NULL, + if (handle = CreateFileW(name, GENERIC_READ, FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, 0, NULL)) { GetFileInformationByHandle(handle, &fileInfo); pInfo->links = fileInfo.nNumberOfLinks; @@ -968,6 +1347,20 @@ efile_write_info(Efile_error* errInfo, Efile_info* pInfo, char* name) { + Efile_call_state state; + int ret; + call_state_init(&state, errInfo); + ret = do_write_info(&state, pInfo, name); + call_state_free(&state); + return ret; +} + +static int +do_write_info(Efile_call_state* state, + Efile_info* pInfo, + char* name) +{ + Efile_error* errInfo = state->errInfo; SYSTEMTIME timebuf; FILETIME ModifyFileTime; FILETIME AccessFileTime; @@ -977,6 +1370,10 @@ efile_write_info(Efile_error* errInfo, DWORD tempAttr; WCHAR *wname = (WCHAR *) name; + DBG_TRACE(1, name); + + ensure_wpath(state, &wname); + /* * Get the attributes for the file. */ @@ -1021,7 +1418,7 @@ efile_write_info(Efile_error* errInfo, } fd = CreateFileW(wname, GENERIC_READ|GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (fd != INVALID_HANDLE_VALUE) { BOOL result = SetFileTime(fd, &CreationFileTime, &AccessFileTime, &ModifyFileTime); @@ -1053,7 +1450,9 @@ char* buf; /* Buffer to write. */ size_t count; /* Number of bytes to write. */ Sint64 offset; /* where to write it */ { - int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); + int res; + DBG_TRACE(2, L""); + res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); if (res) { return efile_write(errInfo, EFILE_MODE_WRITE, fd, buf, count); } else { @@ -1071,7 +1470,9 @@ char* buf; /* Buffer to read into. */ size_t count; /* Number of bytes to read. */ size_t* pBytesRead; /* Where to return number of bytes read. */ { - int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); + int res; + DBG_TRACE(2, L""); + res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); if (res) { return efile_read(errInfo, EFILE_MODE_READ, fd, buf, count, pBytesRead); } else { @@ -1093,6 +1494,7 @@ size_t count; /* Number of bytes to write. */ OVERLAPPED overlapped; OVERLAPPED* pOverlapped = NULL; + DBG_TRACE(2, L""); if (flags & EFILE_MODE_APPEND) { memset(&overlapped, 0, sizeof(overlapped)); overlapped.Offset = 0xffffffff; @@ -1122,6 +1524,7 @@ efile_writev(Efile_error* errInfo, /* Where to return error codes */ OVERLAPPED overlapped; OVERLAPPED* pOverlapped = NULL; + DBG_TRACE(2, L""); ASSERT(iovcnt >= 0); if (flags & EFILE_MODE_APPEND) { @@ -1157,8 +1560,13 @@ char* buf; /* Buffer to read into. */ size_t count; /* Number of bytes to read. */ size_t* pBytesRead; /* Where to return number of bytes read. */ { - if (!ReadFile((HANDLE) fd, buf, count, (DWORD *) pBytesRead, NULL)) + DWORD nbytes = 0; + + DBG_TRACE(2, L""); + if (!ReadFile((HANDLE) fd, buf, count, &nbytes, NULL)) return set_error(errInfo); + + *pBytesRead = nbytes; return 1; } @@ -1174,6 +1582,7 @@ Sint64* new_location; /* Resulting new location in file. */ { LARGE_INTEGER off, new_loc; + DBG_TRACE(2, L""); switch (origin) { case EFILE_SEEK_SET: origin = FILE_BEGIN; break; case EFILE_SEEK_CUR: origin = FILE_CURRENT; break; @@ -1205,12 +1614,13 @@ Efile_error* errInfo; /* Where to return error codes. */ int *fd; /* File descriptor for file to truncate. */ int flags; { + DBG_TRACE(2, L""); if (!SetEndOfFile((HANDLE) (*fd))) return set_error(errInfo); return 1; } - + /* * is_root_unc_name - returns TRUE if the argument is a UNC name specifying * a root share. That is, if it is of the form \\server\share\. @@ -1357,9 +1767,24 @@ dos_to_posix_mode(int attr, const WCHAR *name) return uxmode; } + int efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size) { + Efile_call_state state; + int ret; + DBG_TRACE(1, name); + call_state_init(&state, errInfo); + ret = !!do_readlink(&state, name, buffer, size); + call_state_free(&state); + return ret; +} + +/* If buffer==0, return buffer allocated by wpath_tmp_allocate +*/ +static char* +do_readlink(Efile_call_state* state, char* name, char* buffer, size_t size) +{ /* * load dll and see if we have CreateSymbolicLink at runtime: * (Vista only) @@ -1367,6 +1792,9 @@ efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size) HINSTANCE hModule = NULL; WCHAR *wname = (WCHAR *) name; WCHAR *wbuffer = (WCHAR *) buffer; + DWORD wsize = size / sizeof(WCHAR); + char* ret = NULL; + if ((hModule = LoadLibrary("kernel32.dll")) != NULL) { typedef DWORD (WINAPI * GETFINALPATHNAMEBYHANDLEPTR)( HANDLE hFile, @@ -1377,58 +1805,84 @@ efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size) GETFINALPATHNAMEBYHANDLEPTR pGetFinalPathNameByHandle = (GETFINALPATHNAMEBYHANDLEPTR)GetProcAddress(hModule, "GetFinalPathNameByHandleW"); - if (pGetFinalPathNameByHandle == NULL) { - FreeLibrary(hModule); - } else { + if (pGetFinalPathNameByHandle != NULL) { + DWORD fileAttributes; + ensure_wpath(state, &wname); /* first check if file is a symlink; {error, einval} otherwise */ - DWORD fileAttributes = GetFileAttributesW(wname); + fileAttributes = GetFileAttributesW(wname); if ((fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { - BOOLEAN success = 0; - HANDLE h = CreateFileW(wname, GENERIC_READ, 0,NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + DWORD success = 0; + HANDLE h = CreateFileW(wname, GENERIC_READ, FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); int len; if(h != INVALID_HANDLE_VALUE) { - success = pGetFinalPathNameByHandle(h, wbuffer, size / sizeof(WCHAR),0); - /* GetFinalPathNameByHandle prepends path with "\\?\": */ - len = wcslen(wbuffer); - wmemmove(wbuffer,wbuffer+4,len-3); - if (len - 4 >= 2 && wbuffer[1] == L':' && wbuffer[0] >= L'A' && - wbuffer[0] <= L'Z') { - wbuffer[0] = wbuffer[0] + L'a' - L'A'; + if (!wbuffer) { /* dynamic allocation */ + WCHAR dummy; + wsize = pGetFinalPathNameByHandle(h, &dummy, 0, 0); + if (wsize) { + wbuffer = wpath_tmp_alloc(state, wsize); + } } + if (wbuffer + && (success = pGetFinalPathNameByHandle(h, wbuffer, wsize, 0)) + && success < wsize) { + WCHAR* wp; + + /* GetFinalPathNameByHandle prepends path with "\\?\": */ + len = wcslen(wbuffer); + wmemmove(wbuffer,wbuffer+4,len-3); + if (len - 4 >= 2 && wbuffer[1] == L':' && wbuffer[0] >= L'A' && + wbuffer[0] <= L'Z') { + wbuffer[0] = wbuffer[0] + L'a' - L'A'; + } - for ( ; *wbuffer; wbuffer++) - if (*wbuffer == L'\\') - *wbuffer = L'/'; + for (wp=wbuffer ; *wp; wp++) + if (*wp == L'\\') + *wp = L'/'; + } CloseHandle(h); - } - FreeLibrary(hModule); + } if (success) { - return 1; + ret = (char*) wbuffer; } else { - return set_error(errInfo); + set_error(state->errInfo); } } else { - FreeLibrary(hModule); errno = EINVAL; - return check_error(-1, errInfo); + save_last_error(state->errInfo); } + goto done; } } errno = ENOTSUP; - return check_error(-1, errInfo); + save_last_error(state->errInfo); + +done: + if (hModule) + FreeLibrary(hModule); + return ret; } int efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size) { + Efile_call_state state; + int ret; + DBG_TRACE(1, orig_name); + call_state_init(&state, errInfo); + ret = do_altname(&state, orig_name, buffer, size); + call_state_free(&state); + return ret; +} + +static int +do_altname(Efile_call_state* state, char* orig_name, char* buffer, size_t size) +{ WIN32_FIND_DATAW wfd; HANDLE fh; - WCHAR name[_MAX_PATH+1]; + WCHAR* name; int name_len; - WCHAR* path; - WCHAR pathbuf[_MAX_PATH+1]; /* Unclear weather GetCurrentDirectory will access one char after - _MAX_PATH */ + WCHAR* full_path = NULL; WCHAR *worig_name = (WCHAR *) orig_name; WCHAR *wbuffer = (WCHAR *) buffer; int drive; /* Drive for filename (1 = A:, 2 = B: etc). */ @@ -1437,8 +1891,8 @@ efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size) if (wcspbrk(worig_name, L"?*")) { enoent: - errInfo->posix_errno = ENOENT; - errInfo->os_errno = ERROR_FILE_NOT_FOUND; + state->errInfo->posix_errno = ENOENT; + state->errInfo->os_errno = ERROR_FILE_NOT_FOUND; return 0; } @@ -1446,24 +1900,23 @@ efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size) * Move the name to a buffer and make sure to remove a trailing * slash, because it causes FindFirstFile() to fail on Win95. */ - - if ((name_len = wcslen(worig_name)) >= _MAX_PATH) { - goto enoent; - } else { - wcscpy(name, worig_name); - if (name_len > 2 && ISSLASH(name[name_len-1]) && - name[name_len-2] != L':') { - name[name_len-1] = L'\0'; - } + ensure_wpath(state, &worig_name); + name_len = wcslen(worig_name); + + name = wpath_tmp_alloc(state, name_len + 1); + wcscpy(name, worig_name); + if (name_len > 2 && ISSLASH(name[name_len-1]) && + name[name_len-2] != L':') { + name[name_len-1] = L'\0'; } /* Try to get disk from name. If none, get current disk. */ if (name[1] != L':') { + WCHAR* cwd_path = get_cwd_wpath_tmp(state); drive = 0; - if (GetCurrentDirectoryW(_MAX_PATH, pathbuf) && - pathbuf[1] == L':') { - drive = towlower(pathbuf[0]) - L'a' + 1; + if (cwd_path[1] == L':') { + drive = towlower(cwd_path[0]) - L'a' + 1; } } else if (*name && name[2] == L'\0') { /* @@ -1475,13 +1928,15 @@ efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size) } fh = FindFirstFileW(name,&wfd); if (fh == INVALID_HANDLE_VALUE) { + DWORD fff_error = GetLastError(); if (!(wcspbrk(name, L"./\\") && - (path = _wfullpath(pathbuf, name, _MAX_PATH)) && + (full_path = get_full_wpath_tmp(state, name, NULL, 0)) && /* root dir. ('C:\') or UNC root dir. ('\\server\share\') */ - ((wcslen(path) == 3) || is_root_unc_name(path)) && - (GetDriveTypeW(path) > 1) ) ) { - errno = errno_map(GetLastError()); - return check_error(-1, errInfo); + ((wcslen(full_path) == 3) || is_root_unc_name(full_path)) && + (GetDriveTypeW(full_path) > 1) ) ) { + + set_os_errno(state->errInfo, fff_error); + return 0; } /* * Root directories (such as C:\ or \\server\share\ are fabricated. @@ -1502,17 +1957,37 @@ efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size) int efile_link(Efile_error* errInfo, char* old, char* new) { + Efile_call_state state; WCHAR *wold = (WCHAR *) old; WCHAR *wnew = (WCHAR *) new; + int ret; + DBG_TRACE(1, old); + call_state_init(&state, errInfo); + ensure_wpath(&state, &wold); + ensure_wpath(&state, &wnew); if(!CreateHardLinkW(wnew, wold, NULL)) { - return set_error(errInfo); + ret = set_error(errInfo); } - return 1; + else ret =1; + call_state_free(&state); + return ret; } int efile_symlink(Efile_error* errInfo, char* old, char* new) { + Efile_call_state state; + int ret; + DBG_TRACE2(1, "symlink(%s <- %s)", old, new); + call_state_init(&state, errInfo); + ret = do_symlink(&state, old, new); + call_state_free(&state); + return ret; +} + +static int +do_symlink(Efile_call_state* state, char* old, char* new) +{ /* * Load dll and see if we have CreateSymbolicLink at runtime: * (Vista only) @@ -1520,6 +1995,8 @@ efile_symlink(Efile_error* errInfo, char* old, char* new) HINSTANCE hModule = NULL; WCHAR *wold = (WCHAR *) old; WCHAR *wnew = (WCHAR *) new; + + DBG_TRACE(1, old); if ((hModule = LoadLibrary("kernel32.dll")) != NULL) { typedef BOOLEAN (WINAPI * CREATESYMBOLICLINKFUNCPTR) ( LPCWSTR lpSymlinkFileName, @@ -1531,6 +2008,9 @@ efile_symlink(Efile_error* errInfo, char* old, char* new) "CreateSymbolicLinkW"); /* A for MBCS, W for UNICODE... char* above implies 'W'! */ if (pCreateSymbolicLink != NULL) { + ensure_wpath(state, &wold); + ensure_wpath(state, &wnew); + { DWORD attr = GetFileAttributesW(wold); int flag = (attr != INVALID_FILE_ATTRIBUTES && attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0; @@ -1541,20 +2021,33 @@ efile_symlink(Efile_error* errInfo, char* old, char* new) if (success) { return 1; } else { - return set_error(errInfo); + return set_error(state->errInfo); } + } } else FreeLibrary(hModule); } errno = ENOTSUP; - return check_error(-1, errInfo); + return check_error(-1, state->errInfo); } int efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length, int advise) { + DBG_TRACE(2, L""); /* posix_fadvise is not available on Windows, do nothing */ errno = ERROR_SUCCESS; return check_error(0, errInfo); } + +int +efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length) +{ + DBG_TRACE(2, L""); + /* No file preallocation method available in Windows. */ + errno = errno_map(ERROR_NOT_SUPPORTED); + SetLastError(ERROR_NOT_SUPPORTED); + + return check_error(-1, errInfo); +} diff --git a/erts/emulator/drivers/win32/winsock_func.h b/erts/emulator/drivers/win32/winsock_func.h deleted file mode 100644 index 9d2c099c4d..0000000000 --- a/erts/emulator/drivers/win32/winsock_func.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 1997-2009. All Rights Reserved. - * - * The contents of this file are subject to the Erlang Public License, - * Version 1.1, (the "License"); you may not use this file except in - * compliance with the License. You should have received a copy of the - * Erlang Public License along with this software. If not, it can be - * retrieved online at http://www.erlang.org/. - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and limitations - * under the License. - * - * %CopyrightEnd% - */ - -typedef struct _WinSockFuncs { - int (WSAAPI *WSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData); - int (WSAAPI *WSACleanup)(void); - int (WSAAPI *WSAGetLastError)(void); - DWORD (WSAAPI *WSAWaitForMultipleEvents) (DWORD cEvents, - const WSAEVENT FAR * lphEvents, - BOOL fWaitAll, - DWORD dwTimeout, - BOOL fAlertable); - WSAEVENT (WSAAPI *WSACreateEvent)(void); - BOOL (WSAAPI *WSACloseEvent)(WSAEVENT hEvent); - - BOOL (WSAAPI *WSASetEvent)(WSAEVENT hEvent); - BOOL (WSAAPI *WSAResetEvent)(WSAEVENT hEvent); - int (WSAAPI *WSAEventSelect)(SOCKET s, WSAEVENT hEventObject, - long lNetworkEvents); - int (WSAAPI *WSAEnumNetworkEvents)(SOCKET s, - WSAEVENT hEventObject, - LPWSANETWORKEVENTS lpNetworkEvents); - int (WSAAPI *WSAIoctl)(SOCKET s, - DWORD dwIoControlCode, - LPVOID lpvInBuffer, - DWORD cbInBuffer, - LPVOID lpvOUTBuffer, - DWORD cbOUTBuffer, - LPDWORD lpcbBytesReturned, - LPWSAOVERLAPPED lpOverlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE - ); - SOCKET (WSAAPI *accept)(SOCKET s, struct sockaddr FAR *addr, - int FAR *addrlen); - int (WSAAPI *bind)(SOCKET s, const struct sockaddr FAR *addr, - int namelen); - int (WSAAPI *closesocket)(SOCKET s); - int (WSAAPI *connect)(SOCKET s, const struct sockaddr FAR *name, - int namelen); - int (WSAAPI *ioctlsocket)(SOCKET s, long cmd, u_long FAR *argp); - int (WSAAPI *getsockopt)(SOCKET s, int level, int optname, - char FAR * optval, int FAR *optlen); - u_long (WSAAPI *htonl)(u_long hostlong); - u_short (WSAAPI *htons)(u_short hostshort); - unsigned long (WSAAPI *inet_addr)(const char FAR * cp); - char FAR * (WSAAPI *inet_ntoa)(struct in_addr in); - int (WSAAPI *listen)(SOCKET s, int backlog); - u_short (WSAAPI *ntohs)(u_short netshort); - int (WSAAPI *recv)(SOCKET s, char FAR * buf, int len, int flags); - int (WSAAPI *send)(SOCKET s, const char FAR * buf, int len, int flags); - int (WSAAPI *setsockopt)(SOCKET s, int level, int optname, - const char FAR * optval, int optlen); - int (WSAAPI *shutdown)(SOCKET s, int how); - SOCKET (WSAAPI *socket)(int af, int type, int protocol); - struct hostent FAR * (WSAAPI *gethostbyname)(const char FAR * name); - struct hostent FAR * (WSAAPI *gethostbyaddr)(const char FAR *addr, - int addrlen, int addrtype); - int (WSAAPI *gethostname)(char FAR * name, int namelen); - struct servent FAR * (WSAAPI *getservbyname)(const char FAR * name, - const char FAR * proto); - struct servent FAR * (WSAAPI *getservbyport)(int port, - const char FAR * proto); - int (WSAAPI *getsockname)(SOCKET sock, struct sockaddr FAR *name, - int FAR *namelen); - - /* - * New, added for inet_drv. - */ - - int (WSAAPI *getpeername)(SOCKET s, struct sockaddr FAR * name, - int FAR * namelen); - u_long (WSAAPI *ntohl)(u_long netlong); - int (WSAAPI *WSASend)(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, - LPDWORD lpNumberOfBytesSent, DWORD dwFlags, - LPWSAOVERLAPPED lpOverlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); - int (WSAAPI *sendto)(SOCKET s, const char FAR * buf, int len, - int flags, const struct sockaddr FAR * to, int tolen); - int (WSAAPI *recvfrom)(SOCKET s, char FAR * buf, int len, int flags, - struct sockaddr FAR * from, int FAR * fromlen); -} WinSockFuncs; - - -extern WinSockFuncs winSock; - -extern int tcp_lookup_functions(void); |