diff options
Diffstat (limited to 'erts/emulator/drivers')
-rw-r--r-- | erts/emulator/drivers/common/efile_drv.c | 442 | ||||
-rw-r--r-- | erts/emulator/drivers/common/erl_efile.h | 40 | ||||
-rw-r--r-- | erts/emulator/drivers/common/gzio.c | 2 | ||||
-rw-r--r-- | erts/emulator/drivers/common/inet_drv.c | 933 | ||||
-rw-r--r-- | erts/emulator/drivers/unix/ttsl_drv.c | 3 | ||||
-rw-r--r-- | erts/emulator/drivers/unix/unix_efile.c | 155 | ||||
-rw-r--r-- | erts/emulator/drivers/win32/ttsl_drv.c | 3 | ||||
-rw-r--r-- | erts/emulator/drivers/win32/win_con.c | 101 | ||||
-rw-r--r--[-rwxr-xr-x] | erts/emulator/drivers/win32/win_efile.c | 119 |
9 files changed, 1288 insertions, 510 deletions
diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index f0ff3f54c5..e7235c30f7 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -55,6 +55,7 @@ #define FILE_READ_LINE 29 #define FILE_FDATASYNC 30 #define FILE_FADVISE 31 +#define FILE_SENDFILE 32 /* Return codes */ @@ -69,6 +70,7 @@ #define FILE_RESP_EOF 8 #define FILE_RESP_FNAME 9 #define FILE_RESP_ALL_DATA 10 +#define FILE_RESP_LFNAME 11 /* Options */ @@ -97,7 +99,13 @@ # include "config.h" #endif #include <stdlib.h> + +// Need (NON)BLOCKING macros for sendfile +#ifndef WANT_NONBLOCKING +#define WANT_NONBLOCKING +#endif #include "sys.h" + #include "erl_driver.h" #include "erl_efile.h" #include "erl_threads.h" @@ -184,6 +192,7 @@ static ErlDrvSysInfo sys_info; # define RESBUFSIZE BUFSIZ #endif +#define READDIR_CHUNKS (5) @@ -223,9 +232,16 @@ static void file_outputv(ErlDrvData, ErlIOVec*); static void file_async_ready(ErlDrvData, ErlDrvThreadData); static void file_flush(ErlDrvData); +#ifdef HAVE_SENDFILE +static void file_ready_output(ErlDrvData data, ErlDrvEvent event); +static void file_stop_select(ErlDrvEvent event, void* _); +#endif /* HAVE_SENDFILE */ enum e_timer {timer_idle, timer_again, timer_write}; +#ifdef HAVE_SENDFILE +enum e_sendfile {sending, not_sending}; +#endif /* HAVE_SENDFILE */ struct t_data; @@ -240,6 +256,9 @@ typedef struct { struct t_data *cq_head; /* Queue of incoming commands */ struct t_data *cq_tail; /* -""- */ enum e_timer timer_state; +#ifdef HAVE_SENDFILE + enum e_sendfile sendfile_state; +#endif /* HAVE_SENDFILE */ size_t read_bufsize; ErlDrvBinary *read_binp; size_t read_offset; @@ -262,7 +281,11 @@ struct erl_drv_entry efile_driver_entry = { file_stop, file_output, NULL, +#ifdef HAVE_SENDFILE + file_ready_output, +#else NULL, +#endif /* HAVE_SENDFILE */ "efile", NULL, NULL, @@ -277,7 +300,13 @@ struct erl_drv_entry efile_driver_entry = { ERL_DRV_EXTENDED_MAJOR_VERSION, ERL_DRV_EXTENDED_MINOR_VERSION, ERL_DRV_FLAG_USE_PORT_LOCKING, + NULL, + NULL, +#ifdef HAVE_SENDFILE + file_stop_select +#else NULL +#endif /* HAVE_SENDFILE */ }; @@ -317,15 +346,16 @@ struct t_preadv { Sint64 offsets[1]; }; -#define READDIR_BUFSIZE (8*1024) -#if READDIR_BUFSIZE < (FILENAME_CHARSIZE*2*(MAXPATHLEN+1)) +#define READDIR_BUFSIZE (8*1024)*READDIR_CHUNKS +#if READDIR_BUFSIZE < (1 + (2 + MAXPATHLEN)*FILENAME_CHARSIZE*READDIR_CHUNKS) # undef READDIR_BUFSIZE -# define READDIR_BUFSIZE (FILENAME_CHARSIZE*2*(MAXPATHLEN+1)) +# define READDIR_BUFSIZE (1 + (2 + MAXPATHLEN)*FILENAME_CHARSIZE*READDIR_CHUNKS) #endif struct t_readdir_buf { - struct t_readdir_buf *next; - char buf[READDIR_BUFSIZE]; + struct t_readdir_buf *next; + size_t n; + char buf[READDIR_BUFSIZE]; }; struct t_data @@ -395,6 +425,14 @@ struct t_data Sint64 length; int advise; } fadvise; +#ifdef HAVE_SENDFILE + struct { + int out_fd; + off_t offset; + Uint64 nbytes; + Uint64 written; + } sendfile; +#endif /* HAVE_SENDFILE */ } c; char b[1]; }; @@ -482,7 +520,6 @@ static void *ef_safe_realloc(void *op, Uint s) : 0) - #if 0 static void ev_clear(ErlIOVec *ev) { @@ -610,7 +647,6 @@ static struct t_data *cq_deq(file_descriptor *desc) { } - /********************************************************************* * Driver entry point -> init */ @@ -625,6 +661,7 @@ file_init(void) ? atoi(buf) : 0); driver_system_info(&sys_info, sizeof(ErlDrvSysInfo)); + return 0; } @@ -652,6 +689,9 @@ file_start(ErlDrvPort port, char* command) desc->cq_head = NULL; desc->cq_tail = NULL; desc->timer_state = timer_idle; +#ifdef HAVE_SENDFILE + desc->sendfile_state = not_sending; +#endif desc->read_bufsize = 0; desc->read_binp = NULL; desc->read_offset = 0; @@ -890,8 +930,6 @@ static int reply_eof(file_descriptor *desc) { driver_output2(desc->port, &c, 1, NULL, 0); return 0; } - - static void invoke_name(void *data, int (*f)(Efile_error *, char *)) { @@ -1598,54 +1636,46 @@ static void invoke_lseek(void *data) static void invoke_readdir(void *data) { struct t_data *d = (struct t_data *) data; - int s; char *p = NULL; - int buf_sz = 0; - size_t tmp_bs; + size_t file_bs; + size_t n = 0, total = 0; + struct t_readdir_buf *b = NULL; + int res = 0; d->again = 0; d->errInfo.posix_errno = 0; - while (1) { - char *str; - if (buf_sz < (4 /* sz */ + 1 /* cmd */ + - FILENAME_CHARSIZE*(MAXPATHLEN + 1))) { - struct t_readdir_buf *b; - if (p) { - put_int32(0, p); /* EOB */ - } - b = EF_SAFE_ALLOC(sizeof(struct t_readdir_buf)); - b->next = NULL; - if (d->c.read_dir.last_buf) - d->c.read_dir.last_buf->next = b; - else - d->c.read_dir.first_buf = b; - d->c.read_dir.last_buf = b; - p = &b->buf[0]; - buf_sz = READDIR_BUFSIZE - 4/* EOB */; - } - - p[4] = FILE_RESP_FNAME; - buf_sz -= 4 + 1; - str = p + 4 + 1; - ASSERT(buf_sz >= MAXPATHLEN + 1); - tmp_bs = buf_sz; - s = efile_readdir(&d->errInfo, d->b, &d->dir_handle, str, &tmp_bs); - - if (s) { - put_int32(tmp_bs + 1 /* 1 byte for opcode */, p); - p += 4 + tmp_bs + 1; - ASSERT(p == (str + tmp_bs)); - buf_sz -= tmp_bs; - } - else { - put_int32(1, p); - p += 4 + 1; - put_int32(0, p); /* EOB */ - d->result_ok = (d->errInfo.posix_errno == 0); - break; + do { + total = READDIR_BUFSIZE; + n = 1; + b = EF_SAFE_ALLOC(sizeof(struct t_readdir_buf)); + b->next = NULL; + + if (d->c.read_dir.last_buf) { + d->c.read_dir.last_buf->next = b; + } else { + d->c.read_dir.first_buf = b; } - } + d->c.read_dir.last_buf = b; + + p = &b->buf[0]; + p[0] = FILE_RESP_LFNAME; + file_bs = READDIR_BUFSIZE - n; + + do { + res = efile_readdir(&d->errInfo, d->b, &d->dir_handle, p + n + 2, &file_bs); + + if (res) { + put_int16((Uint16)file_bs, p + n); + n += 2 + file_bs; + file_bs = READDIR_BUFSIZE - n; + } + } while( res && ((total - n - 2) >= MAXPATHLEN*FILENAME_CHARSIZE)); + + b->n = n; + } while(res); + + d->result_ok = (d->errInfo.posix_errno == 0); } static void invoke_open(void *data) @@ -1699,6 +1729,66 @@ static void invoke_fadvise(void *data) d->result_ok = efile_fadvise(&d->errInfo, fd, offset, length, advise); } +#ifdef HAVE_SENDFILE +static void invoke_sendfile(void *data) +{ + struct t_data *d = (struct t_data *)data; + int fd = d->fd; + int out_fd = (int)d->c.sendfile.out_fd; + Uint64 nbytes = d->c.sendfile.nbytes; + int result = 0; + d->again = 0; + + result = efile_sendfile(&d->errInfo, fd, out_fd, &d->c.sendfile.offset, &nbytes, NULL); + + d->c.sendfile.written += nbytes; + + if (result == 1) { + if (sys_info.async_threads != 0) { + d->result_ok = 0; + } else if (d->c.sendfile.nbytes == 0 && nbytes != 0) { + d->result_ok = 1; + } else if ((d->c.sendfile.nbytes - nbytes) != 0) { + d->result_ok = 1; + d->c.sendfile.nbytes -= nbytes; + } else { + d->result_ok = 0; + } + } else if (result == 0 && (d->errInfo.posix_errno == EAGAIN + || d->errInfo.posix_errno == EINTR)) { + d->result_ok = 1; + } else { + d->result_ok = -1; + } +} + +static void free_sendfile(void *data) { + EF_FREE(data); +} + +static void file_ready_output(ErlDrvData data, ErlDrvEvent event) +{ + file_descriptor* fd = (file_descriptor*) data; + + switch (fd->d->command) { + case FILE_SENDFILE: + driver_select(fd->port, event, + (int)ERL_DRV_WRITE,(int) 0); + invoke_sendfile((void *)fd->d); + file_async_ready(data, (ErlDrvThreadData)fd->d); + break; + default: + break; + } +} + +static void file_stop_select(ErlDrvEvent event, void* _) +{ + +} +#endif /* HAVE_SENDFILE */ + + static void free_readdir(void *data) { struct t_data *d = (struct t_data *) data; @@ -1760,6 +1850,10 @@ static void cq_execute(file_descriptor *desc) { register void *void_ptr; /* Soft cast variable */ if (desc->timer_state == timer_again) return; +#ifdef HAVE_SENDFILE + if (desc->sendfile_state == sending) + return; +#endif if (! (d = cq_deq(desc))) return; TRACE_F(("x%i", (int) d->command)); @@ -2026,24 +2120,25 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) if (d->result_ok) { resbuf[0] = FILE_RESP_INFO; - put_int32(d->info.size_high, &resbuf[1 + (0 * 4)]); - put_int32(d->info.size_low, &resbuf[1 + (1 * 4)]); - put_int32(d->info.type, &resbuf[1 + (2 * 4)]); - - PUT_TIME(d->info.accessTime, resbuf + 1 + 3*4); - PUT_TIME(d->info.modifyTime, resbuf + 1 + 9*4); - PUT_TIME(d->info.cTime, resbuf + 1 + 15*4); - - put_int32(d->info.mode, &resbuf[1 + (21 * 4)]); - put_int32(d->info.links, &resbuf[1 + (22 * 4)]); - put_int32(d->info.major_device, &resbuf[1 + (23 * 4)]); - put_int32(d->info.minor_device, &resbuf[1 + (24 * 4)]); - put_int32(d->info.inode, &resbuf[1 + (25 * 4)]); - put_int32(d->info.uid, &resbuf[1 + (26 * 4)]); - put_int32(d->info.gid, &resbuf[1 + (27 * 4)]); - put_int32(d->info.access, &resbuf[1 + (28 * 4)]); - -#define RESULT_SIZE (1 + (29 * 4)) + put_int32(d->info.size_high, &resbuf[1 + ( 0 * 4)]); + put_int32(d->info.size_low, &resbuf[1 + ( 1 * 4)]); + put_int32(d->info.type, &resbuf[1 + ( 2 * 4)]); + + /* Note 64 bit indexing in resbuf here */ + put_int64(d->info.accessTime, &resbuf[1 + ( 3 * 4)]); + put_int64(d->info.modifyTime, &resbuf[1 + ( 5 * 4)]); + put_int64(d->info.cTime, &resbuf[1 + ( 7 * 4)]); + + put_int32(d->info.mode, &resbuf[1 + ( 9 * 4)]); + put_int32(d->info.links, &resbuf[1 + (10 * 4)]); + put_int32(d->info.major_device, &resbuf[1 + (11 * 4)]); + put_int32(d->info.minor_device, &resbuf[1 + (12 * 4)]); + put_int32(d->info.inode, &resbuf[1 + (13 * 4)]); + put_int32(d->info.uid, &resbuf[1 + (14 * 4)]); + put_int32(d->info.gid, &resbuf[1 + (15 * 4)]); + put_int32(d->info.access, &resbuf[1 + (16 * 4)]); + +#define RESULT_SIZE (1 + (17 * 4)) TRACE_C('R'); driver_output2(desc->port, resbuf, RESULT_SIZE, NULL, 0); #undef RESULT_SIZE @@ -2053,30 +2148,24 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) free_data(data); break; case FILE_READDIR: - if (!d->result_ok) + if (!d->result_ok) { reply_error(desc, &d->errInfo); - else { + } else { struct t_readdir_buf *b1 = d->c.read_dir.first_buf; + char op = FILE_RESP_LFNAME; + TRACE_C('R'); ASSERT(b1); + while (b1) { struct t_readdir_buf *b2 = b1; char *p = &b1->buf[0]; - int sz = get_int32(p); - while (sz) { /* 0 == EOB */ - p += 4; - if (sz - 1 > 0) { - driver_output2(desc->port, p, 1, p+1, sz-1); - } else { - driver_output2(desc->port, p, 1, NULL, 0); - } - p += sz; - sz = get_int32(p); - } + driver_output2(desc->port, p, 1, p + 1, b1->n - 1); b1 = b1->next; EF_FREE(b2); } - + driver_output2(desc->port, &op, 1, NULL, 0); + d->c.read_dir.first_buf = NULL; d->c.read_dir.last_buf = NULL; } @@ -2116,6 +2205,37 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) } free_preadv(data); break; +#ifdef HAVE_SENDFILE + case FILE_SENDFILE: + if (d->result_ok == -1) { + desc->sendfile_state = not_sending; + reply_error(desc, &d->errInfo); + if (sys_info.async_threads != 0) { + SET_NONBLOCKING(d->c.sendfile.out_fd); + free_sendfile(data); + } else { + driver_select(desc->port, (ErlDrvEvent)(long)d->c.sendfile.out_fd, + ERL_DRV_USE, 0); + free_sendfile(data); + } + } else if (d->result_ok == 0) { + desc->sendfile_state = not_sending; + reply_Sint64(desc, d->c.sendfile.written); + if (sys_info.async_threads != 0) { + SET_NONBLOCKING(d->c.sendfile.out_fd); + free_sendfile(data); + } else { + driver_select(desc->port, (ErlDrvEvent)(long)d->c.sendfile.out_fd, ERL_DRV_USE, 0); + free_sendfile(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, + ERL_DRV_USE|ERL_DRV_WRITE, 1); + } + break; +#endif default: abort(); } @@ -2126,6 +2246,7 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) cq_execute(desc); } + /********************************************************************* * Driver entry point -> output */ @@ -2246,19 +2367,46 @@ file_output(ErlDrvData e, char* buf, int count) #endif { size_t resbufsize; - char resbuf[RESBUFSIZE+1]; + size_t n = 0, total = 0; + int res = 0; + char resbuf[READDIR_BUFSIZE]; + EFILE_DIR_HANDLE dir_handle; /* Handle to open directory. */ + total = READDIR_BUFSIZE; errInfo.posix_errno = 0; - dir_handle = NULL; - resbuf[0] = FILE_RESP_FNAME; - resbufsize = RESBUFSIZE; - - while (efile_readdir(&errInfo, name, &dir_handle, - resbuf+1, &resbufsize)) { - driver_output2(desc->port, resbuf, 1, resbuf+1, resbufsize); - resbufsize = RESBUFSIZE; - } + dir_handle = NULL; + resbuf[0] = FILE_RESP_LFNAME; + + /* Fill the buffer with multiple directory listings before sending it to the + * receiving process. READDIR_CHUNKS is minimum number of files sent to the + * receiver. + * Format for each driver_output2: + * ------------------------------------ + * | Type | Len | Filename | ... + * | 1 byte | 2 bytes | Len bytes | ... + * ------------------------------------ + */ + + do { + n = 1; + resbufsize = READDIR_BUFSIZE - n; + + do { + res = efile_readdir(&errInfo, name, &dir_handle, resbuf + n + 2, &resbufsize); + + if (res) { + put_int16((Uint16)resbufsize, resbuf + n); + n += 2 + resbufsize; + resbufsize = READDIR_BUFSIZE - n; + } + } while( res && ((total - n - 2) >= MAXPATHLEN*FILENAME_CHARSIZE)); + + if (n > 1) { + driver_output2(desc->port, resbuf, 1, resbuf + 1, n - 1); + } + } while(res); + if (errInfo.posix_errno != 0) { reply_error(desc, &errInfo); return; @@ -2338,15 +2486,16 @@ file_output(ErlDrvData e, char* buf, int count) case FILE_WRITE_INFO: { d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 - + FILENAME_BYTELEN(buf+21*4) + FILENAME_CHARSIZE); + + FILENAME_BYTELEN(buf + 9*4) + FILENAME_CHARSIZE); - d->info.mode = get_int32(buf + 0 * 4); - d->info.uid = get_int32(buf + 1 * 4); - d->info.gid = get_int32(buf + 2 * 4); - GET_TIME(d->info.accessTime, buf + 3 * 4); - GET_TIME(d->info.modifyTime, buf + 9 * 4); - GET_TIME(d->info.cTime, buf + 15 * 4); - FILENAME_COPY(d->b, buf+21*4); + d->info.mode = get_int32(buf + 0 * 4); + d->info.uid = get_int32(buf + 1 * 4); + d->info.gid = get_int32(buf + 2 * 4); + d->info.accessTime = (time_t)((Sint64)get_int64(buf + 3 * 4)); + d->info.modifyTime = (time_t)((Sint64)get_int64(buf + 5 * 4)); + d->info.cTime = (time_t)((Sint64)get_int64(buf + 7 * 4)); + + FILENAME_COPY(d->b, buf + 9*4); d->command = command; d->invoke = invoke_write_info; d->free = free_data; @@ -2455,16 +2604,22 @@ file_output(ErlDrvData e, char* buf, int count) static void file_flush(ErlDrvData e) { file_descriptor *desc = (file_descriptor *)e; +#ifdef DEBUG int r; +#endif TRACE_C('f'); - r = flush_write(desc, NULL); +#ifdef DEBUG + r = +#endif + flush_write(desc, NULL); /* Only possible reason for bad return value is ENOMEM, and * there is nobody to tell... */ +#ifdef DEBUG ASSERT(r == 0); - r = 0; /* Avoiding warning */ +#endif cq_execute(desc); } @@ -2476,13 +2631,20 @@ file_flush(ErlDrvData e) { static int file_control(ErlDrvData e, unsigned int command, char* buf, int len, char **rbuf, int rlen) { + /* + * warning: variable ‘desc’ set but not used + * [-Wunused-but-set-variable] + * ... no kidding ... + * + * file_descriptor *desc = (file_descriptor *)e; switch (command) { default: return 0; - } /* switch (command) */ + } ASSERT(0); - desc = NULL; /* XXX Avoid warning while empty switch */ + desc = NULL; + */ return 0; } @@ -2507,12 +2669,14 @@ file_timeout(ErlDrvData e) { driver_async(desc->port, KEY(desc), desc->invoke, desc->d, desc->free); break; case timer_write: { - int r = flush_write(desc, NULL); +#ifdef DEBUG + int r = +#endif + flush_write(desc, NULL); /* Only possible reason for bad return value is ENOMEM, and * there is nobody to tell... */ ASSERT(r == 0); - r = 0; /* Avoiding warning */ cq_execute(desc); } break; } /* case */ @@ -3213,9 +3377,69 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { goto done; } /* case FILE_OPT_DELAYED_WRITE: */ } ASSERT(0); goto done; /* case FILE_SETOPT: */ - + + case FILE_SENDFILE: { + +#ifdef HAVE_SENDFILE + struct t_data *d; + Uint32 out_fd, offsetH, offsetL, hd_len, tl_len; + Uint64 nbytes; + char flags; + + if (ev->size < 1 + 7 * sizeof(Uint32) + sizeof(char) + || !EV_GET_UINT32(ev, &out_fd, &p, &q) + || !EV_GET_CHAR(ev, &flags, &p, &q) + || !EV_GET_UINT32(ev, &offsetH, &p, &q) + || !EV_GET_UINT32(ev, &offsetL, &p, &q) + || !EV_GET_UINT64(ev, &nbytes, &p, &q) + || !EV_GET_UINT32(ev, &hd_len, &p, &q) + || !EV_GET_UINT32(ev, &tl_len, &p, &q)) { + /* Buffer has wrong length to contain all the needed values */ + reply_posix_error(desc, EINVAL); + goto done; + } + + if (hd_len != 0 || tl_len != 0 || flags != 0) { + // We do not allow header, trailers and/or flags right now + reply_posix_error(desc, EINVAL); + goto done; + } + + d = EF_SAFE_ALLOC(sizeof(struct t_data)); + d->fd = desc->fd; + d->command = command; + d->invoke = invoke_sendfile; + d->free = NULL; + d->level = 2; + + d->c.sendfile.out_fd = (int) out_fd; + d->c.sendfile.written = 0; + + #if SIZEOF_OFF_T == 4 + if (offsetH != 0) { + reply_posix_error(desc, EINVAL); + goto done; + } + d->c.sendfile.offset = (off_t) offsetL; + #else + d->c.sendfile.offset = ((off_t) offsetH << 32) | offsetL; + #endif + + d->c.sendfile.nbytes = nbytes; + + if (sys_info.async_threads != 0) { + SET_BLOCKING(d->c.sendfile.out_fd); + } + + cq_enq(desc, d); +#else + reply_posix_error(desc, ENOTSUP); +#endif + goto done; + } /* case FILE_SENDFILE: */ + } /* switch(command) */ - + if (lseek_flush_read(desc, &err) < 0) { reply_posix_error(desc, err); goto done; diff --git a/erts/emulator/drivers/common/erl_efile.h b/erts/emulator/drivers/common/erl_efile.h index 3097ded3f1..5c0b89e850 100644 --- a/erts/emulator/drivers/common/erl_efile.h +++ b/erts/emulator/drivers/common/erl_efile.h @@ -85,14 +85,15 @@ typedef struct _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; + +//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; /* @@ -111,13 +112,26 @@ typedef struct _Efile_info { Uint32 inode; /* Inode number. */ Uint32 uid; /* User id of owner. */ Uint32 gid; /* Group id of owner. */ - Efile_time accessTime; /* Last time the file was accessed. */ - Efile_time modifyTime; /* Last time the file was modified. */ - Efile_time cTime; /* Creation time (Windows) or last + time_t accessTime; /* Last time the file was accessed. */ + time_t modifyTime; /* Last time the file was modified. */ + time_t cTime; /* Creation time (Windows) or last * inode change (Unix). */ } Efile_info; + +#ifdef HAVE_SENDFILE +/* + * Described the structure of header/trailers for sendfile + */ +struct t_sendfile_hdtl { + SysIOVec *headers; + int hdr_cnt; + SysIOVec *trailers; + int trl_cnt; +}; +#endif /* HAVE_SENDFILE */ + /* * Functions. */ @@ -162,3 +176,7 @@ int efile_symlink(Efile_error* errInfo, char* old, char* new); int efile_may_openfile(Efile_error* errInfo, char *name); int efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length, int advise); +#ifdef HAVE_SENDFILE +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 */ diff --git a/erts/emulator/drivers/common/gzio.c b/erts/emulator/drivers/common/gzio.c index 741cb6ae20..a9303d55bc 100644 --- a/erts/emulator/drivers/common/gzio.c +++ b/erts/emulator/drivers/common/gzio.c @@ -27,7 +27,9 @@ #endif #ifdef __WIN32__ +#ifndef HAVE_CONFLICTING_FREAD_DECLARATION #define HAVE_CONFLICTING_FREAD_DECLARATION +#endif #define FILENAMES_16BIT 1 #endif diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 43114c6039..b452acba32 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -110,6 +110,77 @@ #undef EWOULDBLOCK #undef ETIMEDOUT +#ifdef EINPROGRESS +#undef EINPROGRESS +#endif +#ifdef EALREADY +#undef EALREADY +#endif +#ifdef ENOTSOCK +#undef ENOTSOCK +#endif +#ifdef EDESTADDRREQ +#undef EDESTADDRREQ +#endif +#ifdef EMSGSIZE +#undef EMSGSIZE +#endif +#ifdef EPROTOTYPE +#undef EPROTOTYPE +#endif +#ifdef ENOPROTOOPT +#undef ENOPROTOOPT +#endif +#ifdef EPROTONOSUPPORT +#undef EPROTONOSUPPORT +#endif +#ifdef EOPNOTSUPP +#undef EOPNOTSUPP +#endif +#ifdef EAFNOSUPPORT +#undef EAFNOSUPPORT +#endif +#ifdef EADDRINUSE +#undef EADDRINUSE +#endif +#ifdef EADDRNOTAVAIL +#undef EADDRNOTAVAIL +#endif +#ifdef ENETDOWN +#undef ENETDOWN +#endif +#ifdef ENETUNREACH +#undef ENETUNREACH +#endif +#ifdef ENETRESET +#undef ENETRESET +#endif +#ifdef ECONNABORTED +#undef ECONNABORTED +#endif +#ifdef ECONNRESET +#undef ECONNRESET +#endif +#ifdef ENOBUFS +#undef ENOBUFS +#endif +#ifdef EISCONN +#undef EISCONN +#endif +#ifdef ENOTCONN +#undef ENOTCONN +#endif +#ifdef ECONNREFUSED +#undef ECONNREFUSED +#endif +#ifdef ELOOP +#undef ELOOP +#endif +#ifdef EHOSTUNREACH +#undef EHOSTUNREACH +#endif + + #define HAVE_MULTICAST_SUPPORT #define ERRNO_BLOCK WSAEWOULDBLOCK @@ -280,6 +351,57 @@ static unsigned long one_value = 1; # define SCTP_EOF MSG_EOF #endif +/* More Solaris 10 fixes: */ +#if ! HAVE_DECL_SCTP_CLOSED && HAVE_DECL_SCTPS_IDLE +# define SCTP_CLOSED SCTPS_IDLE +# undef HAVE_DECL_SCTP_CLOSED +# define HAVE_DECL_SCTP_CLOSED 1 +#endif +#if ! HAVE_DECL_SCTP_BOUND && HAVE_DECL_SCTPS_BOUND +# define SCTP_BOUND SCTPS_BOUND +# undef HAVE_DECL_SCTP_BOUND +# define HAVE_DECL_SCTP_BOUND 1 +#endif +#if ! HAVE_DECL_SCTP_LISTEN && HAVE_DECL_SCTPS_LISTEN +# define SCTP_LISTEN SCTPS_LISTEN +# undef HAVE_DECL_SCTP_LISTEN +# define HAVE_DECL_SCTP_LISTEN 1 +#endif +#if ! HAVE_DECL_SCTP_COOKIE_WAIT && HAVE_DECL_SCTPS_COOKIE_WAIT +# define SCTP_COOKIE_WAIT SCTPS_COOKIE_WAIT +# undef HAVE_DECL_SCTP_COOKIE_WAIT +# define HAVE_DECL_SCTP_COOKIE_WAIT 1 +#endif +#if ! HAVE_DECL_SCTP_COOKIE_ECHOED && HAVE_DECL_SCTPS_COOKIE_ECHOED +# define SCTP_COOKIE_ECHOED SCTPS_COOKIE_ECHOED +# undef HAVE_DECL_SCTP_COOKIE_ECHOED +# define HAVE_DECL_SCTP_COOKIE_ECHOED 1 +#endif +#if ! HAVE_DECL_SCTP_ESTABLISHED && HAVE_DECL_SCTPS_ESTABLISHED +# define SCTP_ESTABLISHED SCTPS_ESTABLISHED +# undef HAVE_DECL_SCTP_ESTABLISHED +# define HAVE_DECL_SCTP_ESTABLISHED 1 +#endif +#if ! HAVE_DECL_SCTP_SHUTDOWN_PENDING && HAVE_DECL_SCTPS_SHUTDOWN_PENDING +# define SCTP_SHUTDOWN_PENDING SCTPS_SHUTDOWN_PENDING +# undef HAVE_DECL_SCTP_SHUTDOWN_PENDING +# define HAVE_DECL_SCTP_SHUTDOWN_PENDING 1 +#endif +#if ! HAVE_DECL_SCTP_SHUTDOWN_SENT && HAVE_DECL_SCTPS_SHUTDOWN_SENT +# define SCTP_SHUTDOWN_SENT SCTPS_SHUTDOWN_SENT +# undef HAVE_DECL_SCTP_SHUTDOWN_SENT +# define HAVE_DECL_SCTP_SHUTDOWN_SENT 1 +#endif +#if ! HAVE_DECL_SCTP_SHUTDOWN_RECEIVED && HAVE_DECL_SCTPS_SHUTDOWN_RECEIVED +# define SCTP_SHUTDOWN_RECEIVED SCTPS_SHUTDOWN_RECEIVED +# undef HAVE_DECL_SCTP_SHUTDOWN_RECEIVED +# define HAVE_DECL_SCTP_SHUTDOWN_RECEIVED 1 +#endif +#if ! HAVE_DECL_SCTP_SHUTDOWN_ACK_SENT && HAVE_DECL_SCTPS_SHUTDOWN_ACK_SENT +# define SCTP_SHUTDOWN_ACK_SENT SCTPS_SHUTDOWN_ACK_SENT +# undef HAVE_DECL_SCTP_SHUTDOWN_ACK_SENT +# define HAVE_DECL_SCTP_SHUTDOWN_ACK_SENT 1 +#endif /* New spelling in lksctp 2.6.22 or maybe even earlier: * adaption -> adaptation */ @@ -294,12 +416,13 @@ static unsigned long one_value = 1; # define sctp_adaptation_layer_event sctp_adaption_layer_event #endif -static void *h_libsctp = NULL; #ifdef __GNUC__ static typeof(sctp_bindx) *p_sctp_bindx = NULL; +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; #endif #endif /* SCTP supported */ @@ -393,6 +516,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) driver_select(port, e, mode | (on?ERL_DRV_USE:0), on) #define sock_select(d, flags, onoff) do { \ + ASSERT(!onoff || !(d)->is_ignored); \ (d)->event_mask = (onoff) ? \ ((d)->event_mask | (flags)) : \ ((d)->event_mask & ~(flags)); \ @@ -415,6 +539,13 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) (((unsigned char*) (s))[1] << 8) | \ (((unsigned char*) (s))[0])) + +#ifdef VALGRIND +# include <valgrind/memcheck.h> +#else +# define VALGRIND_MAKE_MEM_DEFINED(ptr,size) +#endif + /*---------------------------------------------------------------------------- ** Interface constants. ** @@ -427,7 +558,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_AF_ANY 3 /* INADDR_ANY or IN6ADDR_ANY_INIT */ #define INET_AF_LOOPBACK 4 /* INADDR_LOOPBACK or IN6ADDR_LOOPBACK_INIT */ -/* INET_REQ_GETTYPE enumeration */ +/* open and INET_REQ_GETTYPE enumeration */ #define INET_TYPE_STREAM 1 #define INET_TYPE_DGRAM 2 #define INET_TYPE_SEQPACKET 3 @@ -484,16 +615,21 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_REQ_IFSET 23 #define INET_REQ_SUBSCRIBE 24 #define INET_REQ_GETIFADDRS 25 +#define INET_REQ_ACCEPT 26 +#define INET_REQ_LISTEN 27 +#define INET_REQ_IGNOREFD 28 + /* TCP requests */ -#define TCP_REQ_ACCEPT 40 -#define TCP_REQ_LISTEN 41 +/* #define TCP_REQ_ACCEPT 40 MOVED */ +/* #define TCP_REQ_LISTEN 41 MERGED */ #define TCP_REQ_RECV 42 #define TCP_REQ_UNRECV 43 #define TCP_REQ_SHUTDOWN 44 /* UDP and SCTP requests */ #define PACKET_REQ_RECV 60 /* Common for UDP and SCTP */ -#define SCTP_REQ_LISTEN 61 /* Different from TCP; not for UDP */ +/* #define SCTP_REQ_LISTEN 61 MERGED Different from TCP; not for UDP */ #define SCTP_REQ_BINDX 62 /* Multi-home SCTP bind */ +#define SCTP_REQ_PEELOFF 63 /* INET_REQ_SUBSCRIBE sub-requests */ #define INET_SUBS_EMPTY_OUT_Q 1 @@ -507,7 +643,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) /* *_REQ_* replies */ #define INET_REP_ERROR 0 #define INET_REP_OK 1 -#define INET_REP_SCTP 2 +#define INET_REP 2 /* INET_REQ_SETOPTS and INET_REQ_GETOPTS options */ #define INET_OPT_REUSEADDR 0 /* enable/disable local address reuse */ @@ -628,10 +764,14 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) ** End of interface constants. **--------------------------------------------------------------------------*/ -#define INET_STATE_CLOSED 0 -#define INET_STATE_OPEN (INET_F_OPEN) -#define INET_STATE_BOUND (INET_STATE_OPEN | INET_F_BOUND) -#define INET_STATE_CONNECTED (INET_STATE_BOUND | INET_F_ACTIVE) +#define INET_STATE_CLOSED (0) +#define INET_STATE_OPEN (INET_F_OPEN) +#define INET_STATE_BOUND (INET_STATE_OPEN | INET_F_BOUND) +#define INET_STATE_CONNECTED (INET_STATE_BOUND | INET_F_ACTIVE) +#define INET_STATE_LISTENING (INET_STATE_BOUND | INET_F_LISTEN) +#define INET_STATE_CONNECTING (INET_STATE_BOUND | INET_F_CON) +#define INET_STATE_ACCEPTING (INET_STATE_LISTENING | INET_F_ACC) +#define INET_STATE_MULTI_ACCEPTING (INET_STATE_ACCEPTING | INET_F_MULTI_CLIENT) #define IS_OPEN(d) \ (((d)->state & INET_F_OPEN) == INET_F_OPEN) @@ -666,6 +806,11 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) /* Max interface name */ #define INET_IFNAMSIZ 16 +/* INET Ignore states */ +#define INET_IGNORE_NONE 0 +#define INET_IGNORE_READ 1 +#define INET_IGNORE_WRITE 1 << 1 + /* Max length of Erlang Term Buffer (for outputting structured terms): */ #ifdef HAVE_SCTP #define PACKET_ERL_DRV_TERM_DATA_LEN 512 @@ -674,7 +819,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #endif -#define BIN_REALLOC_LIMIT(x) (((x)*3)/4) /* 75% */ +#define BIN_REALLOC_MARGIN(x) ((x)/4) /* 25% */ /* The general purpose sockaddr */ typedef union { @@ -805,20 +950,13 @@ typedef struct { double send_avg; /* average packet size sent */ subs_list empty_out_q_subs; /* Empty out queue subscribers */ + int is_ignored; /* if a fd is ignored by from the inet_drv, + this should be set to true when the fd is used + outside of inet_drv. */ } inet_descriptor; -#define TCP_STATE_CLOSED INET_STATE_CLOSED -#define TCP_STATE_OPEN (INET_F_OPEN) -#define TCP_STATE_BOUND (TCP_STATE_OPEN | INET_F_BOUND) -#define TCP_STATE_CONNECTED (TCP_STATE_BOUND | INET_F_ACTIVE) -#define TCP_STATE_LISTEN (TCP_STATE_BOUND | INET_F_LISTEN) -#define TCP_STATE_CONNECTING (TCP_STATE_BOUND | INET_F_CON) -#define TCP_STATE_ACCEPTING (TCP_STATE_LISTEN | INET_F_ACC) -#define TCP_STATE_MULTI_ACCEPTING (TCP_STATE_ACCEPTING | INET_F_MULTI_CLIENT) - - #define TCP_MAX_PACKET_SIZE 0x4000000 /* 64 M */ #define MAX_VSIZE 16 /* Max number of entries allowed in an I/O @@ -874,12 +1012,6 @@ static struct erl_drv_entry tcp_inet_driver_entry = inet_stop_select }; -#define PACKET_STATE_CLOSED INET_STATE_CLOSED -#define PACKET_STATE_OPEN (INET_F_OPEN) -#define PACKET_STATE_BOUND (PACKET_STATE_OPEN | INET_F_BOUND) -#define SCTP_STATE_LISTEN (PACKET_STATE_BOUND | INET_F_LISTEN) -#define SCTP_STATE_CONNECTING (PACKET_STATE_BOUND | INET_F_CON) -#define PACKET_STATE_CONNECTED (PACKET_STATE_BOUND | INET_F_ACTIVE) static int packet_inet_init(void); @@ -997,6 +1129,9 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event); typedef struct { inet_descriptor inet; /* common data structure (DON'T MOVE) */ int read_packets; /* Number of packets to read per invocation */ + int i_bufsz; /* current input buffer size */ + ErlDrvBinary* i_buf; /* current binary buffer */ + char* i_ptr; /* current pos in buf */ } udp_descriptor; @@ -1851,6 +1986,26 @@ static int inet_reply_ok(inet_descriptor* desc) return driver_send_term(desc->port, caller, spec, i); } +#ifdef HAVE_SCTP +static int inet_reply_ok_port(inet_descriptor* desc, ErlDrvTermData dport) +{ + ErlDrvTermData spec[2*LOAD_ATOM_CNT + 2*LOAD_PORT_CNT + 2*LOAD_TUPLE_CNT]; + ErlDrvTermData caller = desc->caller; + int i = 0; + + i = LOAD_ATOM(spec, i, am_inet_reply); + i = LOAD_PORT(spec, i, desc->dport); + i = LOAD_ATOM(spec, i, am_ok); + i = LOAD_PORT(spec, i, dport); + i = LOAD_TUPLE(spec, i, 2); + i = LOAD_TUPLE(spec, i, 3); + ASSERT(i == sizeof(spec)/sizeof(*spec)); + + desc->caller = 0; + return driver_send_term(desc->port, caller, spec, i); +} +#endif + /* send: ** {inet_reply, S, {error, Reason}} */ @@ -2389,14 +2544,19 @@ static ErlDrvTermData am_sctp_rtoinfo, /* Option names */ am_active, am_inactive, /* For #sctp_status{}: */ - am_empty, am_closed, +# if HAVE_DECL_SCTP_EMPTY + am_empty, +# endif +# if HAVE_DECL_SCTP_BOUND + am_bound, +# endif +# if HAVE_DECL_SCTP_LISTEN + am_listen, +# endif am_cookie_wait, am_cookie_echoed, am_established, am_shutdown_pending, am_shutdown_sent, am_shutdown_received, am_shutdown_ack_sent; - /* Not yet implemented in the Linux kernel: - ** am_bound, am_listen; - */ /* ** Parsing of "sctp_sndrcvinfo": ancillary data coming with received msgs. @@ -2665,7 +2825,8 @@ static int sctp_parse_async_event # ifdef HAVE_STRUCT_SCTP_REMOTE_ERROR_SRE_DATA chunk = (char*) (&(sptr->sre_data)); # else - chunk = ((char*)sptr) + sizeof(*sptr); + chunk = ((char*) &(sptr->sre_assoc_id)) + + sizeof(sptr->sre_assoc_id); # endif chlen = sptr->sre_length - (chunk - (char *)sptr); i = sctp_parse_error_chunk(spec, i, chunk, chlen); @@ -2716,7 +2877,8 @@ static int sctp_parse_async_event # ifdef HAVE_STRUCT_SCTP_SEND_FAILED_SSF_DATA chunk = (char*) (&(sptr->ssf_data)); # else - chunk = ((char*)sptr) + sizeof(*sptr); + chunk = ((char*) &(sptr->ssf_assoc_id)) + + sizeof(sptr->ssf_assoc_id); # endif chlen = sptr->ssf_length - (chunk - (char*) sptr); choff = chunk - bin->orig_bytes; @@ -3390,8 +3552,15 @@ static void inet_init_sctp(void) { INIT_ATOM(inactive); /* For #sctp_status{}: */ +# if HAVE_DECL_SCTP_EMPTY INIT_ATOM(empty); - INIT_ATOM(closed); +# endif +# if HAVE_DECL_SCTP_BOUND + INIT_ATOM(bound); +# endif +# if HAVE_DECL_SCTP_LISTEN + INIT_ATOM(listen); +# endif INIT_ATOM(cookie_wait); INIT_ATOM(cookie_echoed); INIT_ATOM(established); @@ -3399,10 +3568,6 @@ static void inet_init_sctp(void) { INIT_ATOM(shutdown_sent); INIT_ATOM(shutdown_received); INIT_ATOM(shutdown_ack_sent); - /* Not yet implemented in the Linux kernel: - ** INIT_ATOM(bound); - ** INIT_ATOM(listen); - */ } #endif /* HAVE_SCTP */ @@ -3453,17 +3618,32 @@ static int inet_init() /* 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); -# ifndef LIBSCTP -# error LIBSCTP not defined -# endif - if (erts_sys_ddll_open_noext(STRINGIFY(LIBSCTP), &h_libsctp, NULL) == 0) { - 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 defined(HAVE_SCTP_BINDX) && defined (HAVE_SCTP_PEELOFF) + p_sctp_bindx = sctp_bindx; + p_sctp_peeloff = sctp_peeloff; + inet_init_sctp(); + add_driver_entry(&sctp_inet_driver_entry); +# else +# ifndef LIBSCTP +# error LIBSCTP not defined +# endif + { + static void *h_libsctp = NULL; + + if (erts_sys_ddll_open_noext(STRINGIFY(LIBSCTP), &h_libsctp, NULL) + == 0) { + 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; + } + } } } +# endif #endif /* remove the dummy inet driver */ @@ -4131,6 +4311,31 @@ static int inet_ctl_getiflist(inet_descriptor* desc, char** rbuf, int rsize) return sp - sbuf; } +#ifdef HAVE_LIBDLPI_H +#include <libdlpi.h> +static int hwaddr_libdlpi_lookup(const char *ifnm, + uchar_t *addr, size_t *alen) +{ + dlpi_handle_t handle; + dlpi_info_t linkinfo; + int ret = -1; + + if (dlpi_open(ifnm, &handle, 0) != DLPI_SUCCESS) { + return -1; + } + + if (dlpi_get_physaddr(handle, DL_CURR_PHYS_ADDR, + addr, alen) == DLPI_SUCCESS && + dlpi_info(handle, &linkinfo, 0) == DLPI_SUCCESS) + { + ret = 0; + } + + dlpi_close(handle); + return ret; +} +#endif + /* FIXME: temporary hack */ #ifndef IFHWADDRLEN #define IFHWADDRLEN 6 @@ -4166,7 +4371,24 @@ static int inet_ctl_ifget(inet_descriptor* desc, char* buf, int len, break; case INET_IFOPT_HWADDR: { -#ifdef SIOCGIFHWADDR +#ifdef HAVE_LIBDLPI_H + /* + ** OpenSolaris have SIGCGIFHWADDR, but no ifr_hwaddr member.. + ** The proper way to get the mac address would be to + ** use libdlpi... + */ + uchar_t addr[DLPI_PHYSADDR_MAX]; + size_t alen = sizeof(addr); + + if (hwaddr_libdlpi_lookup(ifreq.ifr_name, addr, &alen) == 0) { + buf_check(sptr, s_end, 1+2+alen); + *sptr++ = INET_IFOPT_HWADDR; + put_int16(alen, sptr); + sptr += 2; + sys_memcpy(sptr, addr, alen); + sptr += alen; + } +#elif defined(SIOCGIFHWADDR) && defined(HAVE_STRUCT_IFREQ_IFR_HWADDR) if (ioctl(desc->s, SIOCGIFHWADDR, (char *)&ifreq) < 0) break; buf_check(sptr, s_end, 1+2+IFHWADDRLEN); @@ -4175,7 +4397,7 @@ static int inet_ctl_ifget(inet_descriptor* desc, char* buf, int len, /* raw memcpy (fix include autoconf later) */ sys_memcpy(sptr, (char*)(&ifreq.ifr_hwaddr.sa_data), IFHWADDRLEN); sptr += IFHWADDRLEN; -#elif defined(SIOCGENADDR) +#elif defined(SIOCGENADDR) && defined(HAVE_STRUCT_IFREQ_IFR_ENADDR) if (ioctl(desc->s, SIOCGENADDR, (char *)&ifreq) < 0) break; buf_check(sptr, s_end, 1+2+sizeof(ifreq.ifr_enaddr)); @@ -4459,6 +4681,7 @@ static int inet_ctl_ifset(inet_descriptor* desc, char* buf, int len, +#if defined(__WIN32__) || defined(HAVE_GETIFADDRS) /* Latin-1 to utf8 */ static int utf8_len(const char *c, int m) { @@ -4481,6 +4704,7 @@ static void utf8_encode(const char *c, int m, char *p) { } } } +#endif #if defined(__WIN32__) @@ -5972,7 +6196,7 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len) proto = IPPROTO_SCTP; type = SCTP_DELAYED_ACK_TIME; arg_ptr = (char*) (&arg.av); - arg_sz = sizeof ( arg.es); + arg_sz = sizeof ( arg.av); break; } # endif @@ -6736,7 +6960,7 @@ static int sctp_fill_opts(inet_descriptor* desc, char* buf, int buflen, 2*LOAD_ATOM_CNT + LOAD_INT_CNT + 2*LOAD_TUPLE_CNT); i = LOAD_ATOM (spec, i, am_sctp_adaptation_layer); i = LOAD_ATOM (spec, i, am_sctp_setadaptation); - i = LOAD_INT (spec, i, ad.ssb_adaptation_ind); + i = LOAD_INT (spec, i, sock_ntohl(ad.ssb_adaptation_ind)); i = LOAD_TUPLE (spec, i, 2); i = LOAD_TUPLE (spec, i, 2); break; @@ -6879,7 +7103,7 @@ static int sctp_fill_opts(inet_descriptor* desc, char* buf, int buflen, break; } /* The following option is not available in Solaris 10: */ -# ifdef SCTP_DELAYED_ACK_TIME +# if HAVE_DECL_SCTP_DELAYED_ACK_TIME case SCTP_OPT_DELAYED_ACK_TIME: { struct sctp_assoc_value av; @@ -6926,7 +7150,7 @@ static int sctp_fill_opts(inet_descriptor* desc, char* buf, int buflen, switch(st.sstat_state) { /* SCTP_EMPTY is not supported on SOLARIS10: */ -# ifdef SCTP_EMPTY +# if HAVE_DECL_SCTP_EMPTY case SCTP_EMPTY: i = LOAD_ATOM (spec, i, am_empty); break; @@ -6934,14 +7158,16 @@ static int sctp_fill_opts(inet_descriptor* desc, char* buf, int buflen, case SCTP_CLOSED: i = LOAD_ATOM (spec, i, am_closed); break; - /* The following states are not supported by Linux Kernel SCTP yet: +# if HAVE_DECL_SCTP_BOUND case SCTP_BOUND: i = LOAD_ATOM (spec, i, am_bound); break; +# endif +# if HAVE_DECL_SCTP_LISTEN case SCTP_LISTEN: i = LOAD_ATOM (spec, i, am_listen); break; - */ +# endif case SCTP_COOKIE_WAIT: i = LOAD_ATOM (spec, i, am_cookie_wait); break; @@ -7032,7 +7258,7 @@ static int sctp_fill_opts(inet_descriptor* desc, char* buf, int buflen, driver_send_term(desc->port, driver_caller(desc->port), spec, i); FREE(spec); - (*dest)[0] = INET_REP_SCTP; + (*dest)[0] = INET_REP; return 1; /* Response length */ # undef PLACE_FOR # undef RETURN_ERROR @@ -7207,6 +7433,8 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol) sys_memzero((char *)&desc->remote,sizeof(desc->remote)); + desc->is_ignored = 0; + return (ErlDrvData)desc; } @@ -7489,6 +7717,33 @@ static int inet_ctl(inet_descriptor* desc, int cmd, char* buf, int len, return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize); } + case INET_REQ_IGNOREFD: { + DEBUGF(("inet_ctl(%ld): IGNOREFD, IGNORED = %d\r\n", + (long)desc->port,(int)*buf)); + + /* + * FD can only be ignored for connected TCP connections for now, + * possible to add UDP and SCTP support if needed. + */ + if (!IS_CONNECTED(desc)) + return ctl_error(ENOTCONN, rbuf, rsize); + + if (!desc->stype == SOCK_STREAM) + return ctl_error(EINVAL, rbuf, rsize); + + if (*buf == 1 && !desc->is_ignored) { + desc->is_ignored = INET_IGNORE_READ; + sock_select(desc, (FD_READ|FD_WRITE|FD_CLOSE|ERL_DRV_USE_NO_CALLBACK), 0); + } else if (*buf == 0 && desc->is_ignored) { + int flags = (FD_READ|FD_CLOSE|((desc->is_ignored & INET_IGNORE_WRITE)?FD_WRITE:0)); + desc->is_ignored = INET_IGNORE_NONE; + 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 */ @@ -7807,22 +8062,22 @@ static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s, static void tcp_close_check(tcp_descriptor* desc) { /* XXX:PaN - multiple clients to handle! */ - if (desc->inet.state == TCP_STATE_ACCEPTING) { + if (desc->inet.state == INET_STATE_ACCEPTING) { inet_async_op *this_op = desc->inet.opt; sock_select(INETP(desc), FD_ACCEPT, 0); - desc->inet.state = TCP_STATE_LISTEN; + desc->inet.state = INET_STATE_LISTENING; if (this_op != NULL) { driver_demonitor_process(desc->inet.port, &(this_op->monitor)); } async_error_am(INETP(desc), am_closed); } - else if (desc->inet.state == TCP_STATE_MULTI_ACCEPTING) { + else if (desc->inet.state == INET_STATE_MULTI_ACCEPTING) { int id,req; ErlDrvTermData caller; ErlDrvMonitor monitor; sock_select(INETP(desc), FD_ACCEPT, 0); - desc->inet.state = TCP_STATE_LISTEN; + 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); @@ -7830,10 +8085,10 @@ static void tcp_close_check(tcp_descriptor* desc) clean_multi_timers(&(desc->mtd), desc->inet.port); } - else if (desc->inet.state == TCP_STATE_CONNECTING) { + else if (desc->inet.state == INET_STATE_CONNECTING) { async_error_am(INETP(desc), am_closed); } - else if (desc->inet.state == TCP_STATE_CONNECTED) { + else if (desc->inet.state == INET_STATE_CONNECTED) { async_error_am_all(INETP(desc), am_closed); } } @@ -7864,41 +8119,64 @@ static int tcp_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, char** rbuf, int rsize) { tcp_descriptor* desc = (tcp_descriptor*)e; + switch(cmd) { - case INET_REQ_OPEN: /* open socket and return internal index */ + case INET_REQ_OPEN: { /* open socket and return internal index */ + int domain; DEBUGF(("tcp_inet_ctl(%ld): OPEN\r\n", (long)desc->inet.port)); - if ((len == 1) && (buf[0] == INET_AF_INET)) - return - inet_ctl_open(INETP(desc), AF_INET, SOCK_STREAM, rbuf, rsize); + if (len != 2) return ctl_error(EINVAL, rbuf, rsize); + switch(buf[0]) { + case INET_AF_INET: + domain = AF_INET; + break; #if defined(HAVE_IN6) && defined(AF_INET6) - else if ((len == 1) && (buf[0] == INET_AF_INET6)) - return - inet_ctl_open(INETP(desc), AF_INET6, SOCK_STREAM, rbuf, rsize); + case INET_AF_INET6: + domain = AF_INET6; + break; #else - else if ((len == 1) && (buf[0] == INET_AF_INET6)) - return ctl_xerror("eafnosupport",rbuf,rsize); + case INET_AF_INET6: + return ctl_xerror("eafnosupport", rbuf, rsize); + break; #endif - else + default: return ctl_error(EINVAL, rbuf, rsize); + } + if (buf[1] != INET_TYPE_STREAM) return ctl_error(EINVAL, rbuf, rsize); + return inet_ctl_open(INETP(desc), domain, SOCK_STREAM, rbuf, rsize); + break; + } - case INET_REQ_FDOPEN: /* pass in an open socket */ - DEBUGF(("tcp_inet_ctl(%ld): FDOPEN\r\n", (long)desc->inet.port)); - if ((len == 5) && (buf[0] == INET_AF_INET)) - return inet_ctl_fdopen(INETP(desc), AF_INET, SOCK_STREAM, - (SOCKET) get_int32(buf+1), rbuf, rsize); + case INET_REQ_FDOPEN: { /* pass in an open socket */ + int domain; + DEBUGF(("tcp_inet_ctl(%ld): FDOPEN\r\n", (long)desc->inet.port)); + if (len != 6) return ctl_error(EINVAL, rbuf, rsize); + switch(buf[0]) { + case INET_AF_INET: + domain = AF_INET; + break; #if defined(HAVE_IN6) && defined(AF_INET6) - else if ((len == 5) && (buf[0] == INET_AF_INET6)) - return inet_ctl_fdopen(INETP(desc), AF_INET6, SOCK_STREAM, - (SOCKET) get_int32(buf+1), rbuf, rsize); + case INET_AF_INET6: + domain = AF_INET6; + break; +#else + case INET_AF_INET6: + return ctl_xerror("eafnosupport", rbuf, rsize); + break; #endif - else + default: return ctl_error(EINVAL, rbuf, rsize); + } + if (buf[1] != INET_TYPE_STREAM) return ctl_error(EINVAL, rbuf, rsize); + return inet_ctl_fdopen(INETP(desc), domain, SOCK_STREAM, + (SOCKET) get_int32(buf+2), rbuf, rsize); + break; + } - case TCP_REQ_LISTEN: { /* argument backlog */ + case INET_REQ_LISTEN: { /* argument backlog */ int backlog; DEBUGF(("tcp_inet_ctl(%ld): LISTEN\r\n", (long)desc->inet.port)); - if (desc->inet.state == TCP_STATE_CLOSED) + if (desc->inet.state == INET_STATE_CLOSED) return ctl_xerror(EXBADPORT, rbuf, rsize); if (!IS_OPEN(INETP(desc))) return ctl_xerror(EXBADPORT, rbuf, rsize); @@ -7909,7 +8187,7 @@ static int tcp_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, backlog = get_int16(buf); if (IS_SOCKET_ERROR(sock_listen(desc->inet.s, backlog))) return ctl_error(sock_errno(), rbuf, rsize); - desc->inet.state = TCP_STATE_LISTEN; + desc->inet.state = INET_STATE_LISTENING; return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } @@ -7945,13 +8223,13 @@ static int tcp_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, ((sock_errno() == ERRNO_BLOCK) || /* Winsock2 */ (sock_errno() == EINPROGRESS))) { /* Unix & OSE!! */ sock_select(INETP(desc), FD_CONNECT, 1); - desc->inet.state = TCP_STATE_CONNECTING; + desc->inet.state = INET_STATE_CONNECTING; if (timeout != INET_INFINITY) driver_set_timer(desc->inet.port, timeout); enq_async(INETP(desc), tbuf, INET_REQ_CONNECT); } else if (code == 0) { /* ok we are connected */ - desc->inet.state = TCP_STATE_CONNECTED; + desc->inet.state = INET_STATE_CONNECTED; if (desc->inet.active) sock_select(INETP(desc), (FD_READ|FD_CLOSE), 1); enq_async(INETP(desc), tbuf, INET_REQ_CONNECT); @@ -7963,7 +8241,7 @@ static int tcp_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize); } - case TCP_REQ_ACCEPT: { /* do async accept */ + case INET_REQ_ACCEPT: { /* do async accept */ char tbuf[2]; unsigned timeout; inet_address remote; @@ -7973,14 +8251,14 @@ static int tcp_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, DEBUGF(("tcp_inet_ctl(%ld): ACCEPT\r\n", (long)desc->inet.port)); /* INPUT: Timeout(4) */ - if ((desc->inet.state != TCP_STATE_LISTEN && desc->inet.state != TCP_STATE_ACCEPTING && - desc->inet.state != TCP_STATE_MULTI_ACCEPTING) || len != 4) { + if ((desc->inet.state != INET_STATE_LISTENING && desc->inet.state != INET_STATE_ACCEPTING && + desc->inet.state != INET_STATE_MULTI_ACCEPTING) || len != 4) { return ctl_error(EINVAL, rbuf, rsize); } timeout = get_int32(buf); - if (desc->inet.state == TCP_STATE_ACCEPTING) { + if (desc->inet.state == INET_STATE_ACCEPTING) { unsigned long time_left = 0; int oid = 0; ErlDrvTermData ocaller = ERL_DRV_NIL; @@ -8009,10 +8287,10 @@ static int tcp_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, mtd = add_multi_timer(&(desc->mtd), desc->inet.port, caller, timeout, &tcp_inet_multi_timeout); } - enq_multi_op(desc, tbuf, TCP_REQ_ACCEPT, caller, mtd, &monitor); - desc->inet.state = TCP_STATE_MULTI_ACCEPTING; + enq_multi_op(desc, tbuf, INET_REQ_ACCEPT, caller, mtd, &monitor); + desc->inet.state = INET_STATE_MULTI_ACCEPTING; return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize); - } else if (desc->inet.state == TCP_STATE_MULTI_ACCEPTING) { + } else if (desc->inet.state == INET_STATE_MULTI_ACCEPTING) { ErlDrvTermData caller = driver_caller(desc->inet.port); MultiTimerData *mtd = NULL; ErlDrvMonitor monitor; @@ -8024,7 +8302,7 @@ static int tcp_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, mtd = add_multi_timer(&(desc->mtd), desc->inet.port, caller, timeout, &tcp_inet_multi_timeout); } - enq_multi_op(desc, tbuf, TCP_REQ_ACCEPT, caller, mtd, &monitor); + enq_multi_op(desc, tbuf, INET_REQ_ACCEPT, caller, mtd, &monitor); return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize); } else { n = sizeof(desc->inet.remote); @@ -8036,8 +8314,8 @@ static int tcp_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, &monitor) != 0) { return ctl_xerror("noproc", rbuf, rsize); } - enq_async_w_tmo(INETP(desc), tbuf, TCP_REQ_ACCEPT, timeout, &monitor); - desc->inet.state = TCP_STATE_ACCEPTING; + enq_async_w_tmo(INETP(desc), tbuf, INET_REQ_ACCEPT, timeout, &monitor); + desc->inet.state = INET_STATE_ACCEPTING; sock_select(INETP(desc),FD_ACCEPT,1); if (timeout != INET_INFINITY) { driver_set_timer(desc->inet.port, timeout); @@ -8064,8 +8342,8 @@ static int tcp_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, driver_select(accept_desc->inet.port, accept_desc->inet.event, ERL_DRV_READ, 1); #endif - accept_desc->inet.state = TCP_STATE_CONNECTED; - enq_async(INETP(desc), tbuf, TCP_REQ_ACCEPT); + accept_desc->inet.state = INET_STATE_CONNECTED; + enq_async(INETP(desc), tbuf, INET_REQ_ACCEPT); async_ok_port(INETP(desc), accept_desc->inet.dport); } return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize); @@ -8107,13 +8385,14 @@ static int tcp_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, if (enq_async(INETP(desc), tbuf, TCP_REQ_RECV) < 0) return ctl_error(EALREADY, rbuf, rsize); - if (tcp_recv(desc, n) == 0) { + if (INETP(desc)->is_ignored || tcp_recv(desc, n) == 0) { if (timeout == 0) async_error_am(INETP(desc), am_timeout); else { if (timeout != INET_INFINITY) - driver_set_timer(desc->inet.port, timeout); - sock_select(INETP(desc),(FD_READ|FD_CLOSE),1); + driver_set_timer(desc->inet.port, timeout); + if (!INETP(desc)->is_ignored) + sock_select(INETP(desc),(FD_READ|FD_CLOSE),1); } } return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize); @@ -8171,7 +8450,7 @@ static void tcp_inet_timeout(ErlDrvData e) (long)desc->inet.port, desc->inet.s)); if ((state & INET_F_MULTI_CLIENT)) { /* Multi-client always means multi-timers */ fire_multi_timers(&(desc->mtd), desc->inet.port, e); - } else if ((state & TCP_STATE_CONNECTED) == TCP_STATE_CONNECTED) { + } else if ((state & INET_STATE_CONNECTED) == INET_STATE_CONNECTED) { if (desc->busy_on_send) { ASSERT(IS_BUSY(INETP(desc))); desc->inet.caller = desc->inet.busy_caller; @@ -8191,20 +8470,20 @@ static void tcp_inet_timeout(ErlDrvData e) async_error_am(INETP(desc), am_timeout); } } - else if ((state & TCP_STATE_CONNECTING) == TCP_STATE_CONNECTING) { + else if ((state & INET_STATE_CONNECTING) == INET_STATE_CONNECTING) { /* assume connect timeout */ /* close the socket since it's not usable (see man pages) */ erl_inet_close(INETP(desc)); async_error_am(INETP(desc), am_timeout); } - else if ((state & TCP_STATE_ACCEPTING) == TCP_STATE_ACCEPTING) { + else if ((state & INET_STATE_ACCEPTING) == INET_STATE_ACCEPTING) { inet_async_op *this_op = desc->inet.opt; /* timer is set on accept */ sock_select(INETP(desc), FD_ACCEPT, 0); if (this_op != NULL) { driver_demonitor_process(desc->inet.port, &(this_op->monitor)); } - desc->inet.state = TCP_STATE_LISTEN; + desc->inet.state = INET_STATE_LISTENING; async_error_am(INETP(desc), am_timeout); } DEBUGF(("tcp_inet_timeout(%ld) }\r\n", (long)desc->inet.port)); @@ -8222,7 +8501,7 @@ static void tcp_inet_multi_timeout(ErlDrvData e, ErlDrvTermData caller) driver_demonitor_process(desc->inet.port, &monitor); if (desc->multi_first == NULL) { sock_select(INETP(desc),FD_ACCEPT,0); - desc->inet.state = TCP_STATE_LISTEN; /* restore state */ + desc->inet.state = INET_STATE_LISTENING; /* restore state */ } send_async_error(desc->inet.port, desc->inet.dport, id, caller, am_timeout); } @@ -8288,7 +8567,7 @@ static void tcp_inet_process_exit(ErlDrvData e, ErlDrvMonitor *monitorp) ErlDrvTermData who = driver_get_monitored_process(desc->inet.port,monitorp); int state = desc->inet.state; - if ((state & TCP_STATE_MULTI_ACCEPTING) == TCP_STATE_MULTI_ACCEPTING) { + if ((state & INET_STATE_MULTI_ACCEPTING) == INET_STATE_MULTI_ACCEPTING) { int id,req; MultiTimerData *timeout; if (remove_multi_op(desc, &id, &req, who, &timeout, NULL) != 0) { @@ -8299,15 +8578,15 @@ static void tcp_inet_process_exit(ErlDrvData e, ErlDrvMonitor *monitorp) } if (desc->multi_first == NULL) { sock_select(INETP(desc),FD_ACCEPT,0); - desc->inet.state = TCP_STATE_LISTEN; /* restore state */ + desc->inet.state = INET_STATE_LISTENING; /* restore state */ } - } else if ((state & TCP_STATE_ACCEPTING) == TCP_STATE_ACCEPTING) { + } else if ((state & INET_STATE_ACCEPTING) == INET_STATE_ACCEPTING) { int did,drid; ErlDrvTermData dcaller; deq_async(INETP(desc), &did, &dcaller, &drid); driver_cancel_timer(desc->inet.port); sock_select(INETP(desc),FD_ACCEPT,0); - desc->inet.state = TCP_STATE_LISTEN; /* restore state */ + desc->inet.state = INET_STATE_LISTENING; /* restore state */ } } @@ -8457,8 +8736,15 @@ static int tcp_remain(tcp_descriptor* desc, int* len) else if (tlen == 0) { /* need unknown more */ *len = 0; if (nsz == 0) { - if (nfill == n) - goto error; + if (nfill == n) { + if (desc->inet.psize != 0 && desc->inet.psize > nfill) { + if (tcp_expand_buffer(desc, desc->inet.psize) < 0) + return -1; + return desc->inet.psize; + } + else + goto error; + } DEBUGF((" => restart more=%d\r\n", nfill - n)); return nfill - n; } @@ -8497,32 +8783,29 @@ static int tcp_deliver(tcp_descriptor* desc, int len) } while (len > 0) { - int code = 0; + int code; inet_input_count(INETP(desc), len); /* deliver binary? */ if (len*4 >= desc->i_buf->orig_size*3) { /* >=75% */ + code = tcp_reply_binary_data(desc, desc->i_buf, + (desc->i_ptr_start - + desc->i_buf->orig_bytes), + len); + if (code < 0) + return code; + /* something after? */ if (desc->i_ptr_start + len == desc->i_ptr) { /* no */ - code = tcp_reply_binary_data(desc, desc->i_buf, - (desc->i_ptr_start - - desc->i_buf->orig_bytes), - len); tcp_clear_input(desc); } else { /* move trail to beginning of a new buffer */ - ErlDrvBinary* bin; + ErlDrvBinary* bin = alloc_buffer(desc->i_bufsz); char* ptr_end = desc->i_ptr_start + len; int sz = desc->i_ptr - ptr_end; - bin = alloc_buffer(desc->i_bufsz); memcpy(bin->orig_bytes, ptr_end, sz); - - code = tcp_reply_binary_data(desc, desc->i_buf, - (desc->i_ptr_start- - desc->i_buf->orig_bytes), - len); free_buffer(desc->i_buf); desc->i_buf = bin; desc->i_ptr_start = desc->i_buf->orig_bytes; @@ -8534,17 +8817,15 @@ static int tcp_deliver(tcp_descriptor* desc, int len) code = tcp_reply_data(desc, desc->i_ptr_start, len); /* XXX The buffer gets thrown away on error (code < 0) */ /* Windows needs workaround for this in tcp_inet_event... */ + if (code < 0) + return code; desc->i_ptr_start += len; if (desc->i_ptr_start == desc->i_ptr) tcp_clear_input(desc); else desc->i_remain = 0; - } - if (code < 0) - return code; - count++; len = 0; @@ -8849,8 +9130,8 @@ static void tcp_inet_event(ErlDrvData e, ErlDrvEvent event) /* socket has input: -** 1. TCP_STATE_ACCEPTING => non block accept ? -** 2. TCP_STATE_CONNECTED => read input +** 1. INET_STATE_ACCEPTING => non block accept ? +** 2. INET_STATE_CONNECTED => read input */ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) { @@ -8858,8 +9139,9 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) #ifdef DEBUG long port = (long) desc->inet.port; /* Used after driver_exit() */ #endif + ASSERT(!INETP(desc)->is_ignored); DEBUGF(("tcp_inet_input(%ld) {s=%d\r\n", port, desc->inet.s)); - if (desc->inet.state == TCP_STATE_ACCEPTING) { + if (desc->inet.state == INET_STATE_ACCEPTING) { SOCKET s; unsigned int len; inet_address remote; @@ -8874,7 +9156,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) } sock_select(INETP(desc),FD_ACCEPT,0); - desc->inet.state = TCP_STATE_LISTEN; /* restore state */ + desc->inet.state = INET_STATE_LISTENING; /* restore state */ if (this_op != NULL) { driver_demonitor_process(desc->inet.port, &(this_op->monitor)); @@ -8914,11 +9196,11 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) driver_select(accept_desc->inet.port, accept_desc->inet.event, ERL_DRV_READ, 1); #endif - accept_desc->inet.state = TCP_STATE_CONNECTED; + accept_desc->inet.state = INET_STATE_CONNECTED; ret = async_ok_port(INETP(desc), accept_desc->inet.dport); goto done; } - } else if (desc->inet.state == TCP_STATE_MULTI_ACCEPTING) { + } else if (desc->inet.state == INET_STATE_MULTI_ACCEPTING) { SOCKET s; unsigned int len; inet_address remote; @@ -8930,7 +9212,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) int times = 0; #endif - while (desc->inet.state == TCP_STATE_MULTI_ACCEPTING) { + while (desc->inet.state == INET_STATE_MULTI_ACCEPTING) { len = sizeof(desc->inet.remote); s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &len); @@ -8950,7 +9232,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) if (desc->multi_first == NULL) { sock_select(INETP(desc),FD_ACCEPT,0); - desc->inet.state = TCP_STATE_LISTEN; /* restore state */ + desc->inet.state = INET_STATE_LISTENING; /* restore state */ } if (timeout != NULL) { @@ -8981,7 +9263,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) driver_select(accept_desc->inet.port, accept_desc->inet.event, ERL_DRV_READ, 1); #endif - accept_desc->inet.state = TCP_STATE_CONNECTED; + accept_desc->inet.state = INET_STATE_CONNECTED; ret = send_async_ok_port(desc->inet.port, desc->inet.dport, id, caller, accept_desc->inet.dport); } @@ -9119,7 +9401,11 @@ static int tcp_sendv(tcp_descriptor* desc, ErlIOVec* ev) DEBUGF(("tcp_sendv(%ld): s=%d, about to send %d,%d bytes\r\n", (long)desc->inet.port, desc->inet.s, h_len, len)); - if (desc->tcp_add_flags & TCP_ADDF_DELAY_SEND) { + + if (INETP(desc)->is_ignored) { + INETP(desc)->is_ignored |= INET_IGNORE_WRITE; + n = 0; + } else if (desc->tcp_add_flags & TCP_ADDF_DELAY_SEND) { n = 0; } else if (IS_SOCKET_ERROR(sock_sendv(desc->inet.s, ev->iov, vsize, &n, 0))) { @@ -9147,7 +9433,8 @@ static int tcp_sendv(tcp_descriptor* desc, ErlIOVec* ev) DEBUGF(("tcp_sendv(%ld): s=%d, Send failed, queuing\r\n", (long)desc->inet.port, desc->inet.s)); driver_enqv(ix, ev, n); - sock_select(INETP(desc),(FD_WRITE|FD_CLOSE), 1); + if (!INETP(desc)->is_ignored) + sock_select(INETP(desc),(FD_WRITE|FD_CLOSE), 1); } return 0; } @@ -9212,7 +9499,10 @@ static int tcp_send(tcp_descriptor* desc, char* ptr, int len) DEBUGF(("tcp_send(%ld): s=%d, about to send %d,%d bytes\r\n", (long)desc->inet.port, desc->inet.s, h_len, len)); - if (desc->tcp_add_flags & TCP_ADDF_DELAY_SEND) { + if (INETP(desc)->is_ignored) { + INETP(desc)->is_ignored |= INET_IGNORE_WRITE; + n = 0; + } else if (desc->tcp_add_flags & TCP_ADDF_DELAY_SEND) { sock_send(desc->inet.s, buf, 0, 0); n = 0; } else if (IS_SOCKET_ERROR(sock_sendv(desc->inet.s,iov,2,&n,0))) { @@ -9243,7 +9533,8 @@ static int tcp_send(tcp_descriptor* desc, char* ptr, int len) n -= h_len; driver_enq(ix, ptr+n, len-n); } - sock_select(INETP(desc),(FD_WRITE|FD_CLOSE), 1); + if (!INETP(desc)->is_ignored) + sock_select(INETP(desc),(FD_WRITE|FD_CLOSE), 1); } return 0; } @@ -9259,17 +9550,18 @@ static void tcp_inet_drv_input(ErlDrvData data, ErlDrvEvent event) } /* socket ready for ouput: -** 1. TCP_STATE_CONNECTING => non block connect ? -** 2. TCP_STATE_CONNECTED => write output +** 1. INET_STATE_CONNECTING => non block connect ? +** 2. INET_STATE_CONNECTED => write output */ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) { int ret = 0; ErlDrvPort ix = desc->inet.port; + ASSERT(!INETP(desc)->is_ignored); DEBUGF(("tcp_inet_output(%ld) {s=%d\r\n", (long)desc->inet.port, desc->inet.s)); - if (desc->inet.state == TCP_STATE_CONNECTING) { + if (desc->inet.state == INET_STATE_CONNECTING) { sock_select(INETP(desc),FD_CONNECT,0); driver_cancel_timer(ix); /* posssibly cancel a timer */ @@ -9289,7 +9581,7 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) (struct sockaddr*) &desc->inet.remote, &sz); if (IS_SOCKET_ERROR(code)) { - desc->inet.state = TCP_STATE_BOUND; /* restore state */ + desc->inet.state = INET_STATE_BOUND; /* restore state */ ret = async_error(INETP(desc), sock_errno()); goto done; } @@ -9302,15 +9594,15 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) (void *)&error, &sz); if ((code < 0) || error) { - desc->inet.state = TCP_STATE_BOUND; /* restore state */ + desc->inet.state = INET_STATE_BOUND; /* restore state */ ret = async_error(INETP(desc), error); goto done; } } -#endif /* SOCKOPT_CONNECT_STAT */ +#endif /* SO_ERROR */ #endif /* !__WIN32__ */ - desc->inet.state = TCP_STATE_CONNECTED; + desc->inet.state = INET_STATE_CONNECTED; if (desc->inet.active) sock_select(INETP(desc),(FD_READ|FD_CLOSE),1); async_ok(INETP(desc)); @@ -9410,6 +9702,59 @@ static int should_use_so_bsdcompat(void) #endif /* __linux__ */ #endif /* HAVE_SO_BSDCOMPAT */ + + +#ifdef HAVE_SCTP +/* Copy a descriptor, by creating a new port with same settings + * as the descriptor desc. + * return NULL on error (ENFILE no ports avail) + */ +static udp_descriptor* sctp_inet_copy(udp_descriptor* desc, SOCKET s, int* err) +{ + ErlDrvPort port = desc->inet.port; + udp_descriptor* copy_desc; + + copy_desc = (udp_descriptor*) sctp_inet_start(port, NULL); + + /* Setup event if needed */ + if ((copy_desc->inet.s = s) != INVALID_SOCKET) { + if ((copy_desc->inet.event = sock_create_event(INETP(copy_desc))) == + INVALID_EVENT) { + *err = sock_errno(); + FREE(copy_desc); + return NULL; + } + } + + /* 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; + copy_desc->inet.stype = desc->inet.stype; + copy_desc->inet.sfamily = desc->inet.sfamily; + copy_desc->inet.hsz = desc->inet.hsz; + copy_desc->inet.bufsz = desc->inet.bufsz; + + /* The new port will be linked and connected to the caller */ + port = driver_create_port(port, desc->inet.caller, "sctp_inet", + (ErlDrvData) copy_desc); + if ((long)port == -1) { + *err = ENFILE; + FREE(copy_desc); + return NULL; + } + copy_desc->inet.port = port; + copy_desc->inet.dport = driver_mk_port(port); + *err = 0; + return copy_desc; +} +#endif + + + static int packet_inet_init() { return 0; @@ -9428,6 +9773,9 @@ static ErlDrvData packet_inet_start(ErlDrvPort port, char* args, int protocol) return ERL_DRV_ERROR_ERRNO; desc->read_packets = INET_PACKET_POLL; + desc->i_bufsz = 0; + desc->i_buf = NULL; + desc->i_ptr = NULL; return drvd; } @@ -9452,6 +9800,10 @@ static void packet_inet_stop(ErlDrvData e) */ udp_descriptor * udesc = (udp_descriptor*) e; inet_descriptor* descr = INETP(udesc); + if (udesc->i_buf != NULL) { + release_buffer(udesc->i_buf); + udesc->i_buf = NULL; + } ASSERT(NO_SUBSCRIBERS(&(descr->empty_out_q_subs))); inet_stop(descr); @@ -9476,21 +9828,31 @@ static int packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, udp_descriptor * udesc = (udp_descriptor *) e; inet_descriptor* desc = INETP(udesc); int type = SOCK_DGRAM; - int af; -#ifdef HAVE_SCTP - if (IS_SCTP(desc)) type = SOCK_SEQPACKET; -#endif + int af = AF_INET; switch(cmd) { case INET_REQ_OPEN: /* open socket and return internal index */ DEBUGF(("packet_inet_ctl(%ld): OPEN\r\n", (long)desc->port)); - if (len != 1) { + if (len != 2) { return ctl_error(EINVAL, rbuf, rsize); } switch (buf[0]) { case INET_AF_INET: af = AF_INET; break; #if defined(HAVE_IN6) && defined(AF_INET6) - case INET_AF_INET6: af = AF_INET6; break; + case INET_AF_INET6: af = AF_INET6; break; +#else + case INET_AF_INET6: + return ctl_xerror("eafnosupport", rbuf, rsize); + break; +#endif + default: + return ctl_error(EINVAL, rbuf, rsize); + } + switch (buf[1]) { + case INET_TYPE_STREAM: type = SOCK_STREAM; break; + case INET_TYPE_DGRAM: type = SOCK_DGRAM; break; +#ifdef HAVE_SCTP + case INET_TYPE_SEQPACKET: type = SOCK_SEQPACKET; break; #endif default: return ctl_error(EINVAL, rbuf, rsize); @@ -9517,18 +9879,35 @@ static int packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, return replen; - case INET_REQ_FDOPEN: /* pass in an open (and bound) socket */ + case INET_REQ_FDOPEN: { /* pass in an open (and bound) socket */ + SOCKET s; DEBUGF(("packet inet_ctl(%ld): FDOPEN\r\n", (long)desc->port)); - if ((len == 5) && (buf[0] == INET_AF_INET)) - replen = inet_ctl_fdopen(desc, AF_INET, SOCK_DGRAM, - (SOCKET)get_int32(buf+1),rbuf,rsize); + if (len != 6) { + return ctl_error(EINVAL, rbuf, rsize); + } + switch (buf[0]) { + case INET_AF_INET: af = AF_INET; break; #if defined(HAVE_IN6) && defined(AF_INET6) - else if ((len == 5) && (buf[0] == INET_AF_INET6)) - replen = inet_ctl_fdopen(desc, AF_INET6, SOCK_DGRAM, - (SOCKET)get_int32(buf+1),rbuf,rsize); + case INET_AF_INET6: af = AF_INET6; break; +#else + case INET_AF_INET6: + return ctl_xerror("eafnosupport", rbuf, rsize); + break; #endif - else + default: + return ctl_error(EINVAL, rbuf, rsize); + } + switch (buf[1]) { + case INET_TYPE_STREAM: type = SOCK_STREAM; break; + case INET_TYPE_DGRAM: type = SOCK_DGRAM; break; +#ifdef HAVE_SCTP + case INET_TYPE_SEQPACKET: type = SOCK_SEQPACKET; break; +#endif + default: return ctl_error(EINVAL, rbuf, rsize); + } + s = (SOCKET)get_int32(buf+2); + replen = inet_ctl_fdopen(desc, af, type, s, rbuf, rsize); if ((*rbuf)[0] != INET_REP_ERROR) { if (desc->active) @@ -9548,6 +9927,7 @@ static int packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, #endif } return replen; + } case INET_REQ_CLOSE: @@ -9567,8 +9947,9 @@ static int packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, */ int code; char tbuf[2]; +#ifdef HAVE_SCTP unsigned timeout; - +#endif DEBUGF(("packet_inet_ctl(%ld): CONNECT\r\n", (long)desc->port)); /* INPUT: [ Timeout(4), Port(2), Address(N) ] */ @@ -9600,14 +9981,14 @@ static int packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, if (IS_SOCKET_ERROR(code) && (sock_errno() == EINPROGRESS)) { /* XXX: Unix only -- WinSock would have a different cond! */ - desc->state = SCTP_STATE_CONNECTING; + desc->state = INET_STATE_CONNECTING; if (timeout != INET_INFINITY) driver_set_timer(desc->port, timeout); enq_async(desc, tbuf, INET_REQ_CONNECT); } else if (code == 0) { /* OK we are connected */ sock_select(desc, FD_CONNECT, 0); - desc->state = PACKET_STATE_CONNECTED; + desc->state = INET_STATE_CONNECTED; enq_async(desc, tbuf, INET_REQ_CONNECT); async_ok(desc); } @@ -9629,7 +10010,7 @@ static int packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, else if (len < 6) return ctl_error(EINVAL, rbuf, rsize); else { - timeout = get_int32(buf); /* IGNORED */ + /* Ignore timeout */ buf += 4; len -= 4; if (inet_set_address(desc->sfamily, @@ -9653,11 +10034,11 @@ static int packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, } #ifdef HAVE_SCTP - case SCTP_REQ_LISTEN: + case INET_REQ_LISTEN: { /* LISTEN is only for SCTP sockets, not UDP. This code is borrowed from the TCP section. Returns: {ok,[]} on success. */ - int flag; + int backlog; DEBUGF(("packet_inet_ctl(%ld): LISTEN\r\n", (long)desc->port)); if (!IS_SCTP(desc)) @@ -9667,15 +10048,14 @@ static int packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, if (!IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize); - /* The arg is a binary value: 1:enable, 0:disable */ - if (len != 1) + if (len != 2) return ctl_error(EINVAL, rbuf, rsize); - flag = get_int8(buf); + backlog = get_int16(buf); - if (IS_SOCKET_ERROR(sock_listen(desc->s, flag))) + if (IS_SOCKET_ERROR(sock_listen(desc->s, backlog))) return ctl_error(sock_errno(), rbuf, rsize); - desc->state = SCTP_STATE_LISTEN; /* XXX: not used? */ + desc->state = INET_STATE_LISTENING; /* XXX: not used? */ return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } @@ -9721,6 +10101,46 @@ static int packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } + + case SCTP_REQ_PEELOFF: + { + Uint32 assoc_id; + udp_descriptor* new_udesc; + int err; + SOCKET new_socket; + + DEBUGF(("packet_inet_ctl(%ld): PEELOFF\r\n", (long)desc->port)); + if (!IS_SCTP(desc)) + return ctl_xerror(EXBADPORT, rbuf, rsize); + if (!IS_OPEN(desc)) + return ctl_xerror(EXBADPORT, rbuf, rsize); + if (!IS_BOUND(desc)) + return ctl_xerror(EXBADSEQ, rbuf, rsize); + if (! p_sctp_peeloff) + return ctl_error(ENOTSUP, rbuf, rsize); + + if (len != 4) + return ctl_error(EINVAL, rbuf, rsize); + assoc_id = get_int32(buf); + + new_socket = p_sctp_peeloff(desc->s, assoc_id); + if (IS_SOCKET_ERROR(new_socket)) { + return ctl_error(sock_errno(), rbuf, rsize); + } + + desc->caller = driver_caller(desc->port); + if ((new_udesc = sctp_inet_copy(udesc, new_socket, &err)) == NULL) { + sock_close(new_socket); + desc->caller = 0; + return ctl_error(err, rbuf, rsize); + } + new_udesc->inet.state = INET_STATE_CONNECTED; + new_udesc->inet.stype = SOCK_STREAM; + + inet_reply_ok_port(desc, new_udesc->inet.dport); + (*rbuf)[0] = INET_REP; + return 1; + } #endif /* HAVE_SCTP */ case PACKET_REQ_RECV: @@ -9832,6 +10252,7 @@ static void packet_inet_command(ErlDrvData e, char* buf, int len) cmsg.hdr.cmsg_level = IPPROTO_SCTP; cmsg.hdr.cmsg_type = SCTP_SNDRCV; cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(*sri)); + VALGRIND_MAKE_MEM_DEFINED(&cmsg, (char*)sri - (char*)&cmsg); /*suppress padding as "uninitialised bytes"*/ data_len = (buf + len) - ptr; /* The whole msg. @@ -9919,12 +10340,8 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event) { inet_descriptor* desc = INETP(udesc); int n; - unsigned int len; inet_address other; char abuf[sizeof(inet_address)]; /* buffer address; enough??? */ - int sz; - char* ptr; - ErlDrvBinary* buf; /* binary */ int packet_count = udesc->read_packets; int count = 0; /* number of packets delivered to owner */ #ifdef HAVE_SCTP @@ -9935,23 +10352,39 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event) #endif while(packet_count--) { - len = sizeof(other); - sz = desc->bufsz; - /* Allocate space for message and address. NB: "bufsz" is in "desc", - but the "buf" itself is allocated separately: - */ - if ((buf = alloc_buffer(sz+len)) == NULL) - return packet_error(udesc, ENOMEM); - ptr = buf->orig_bytes + len; /* pointer to message part */ + unsigned int len = sizeof(other); + + /* udesc->i_buf is only kept between SCTP fragments */ + if (udesc->i_buf == NULL) { + udesc->i_bufsz = desc->bufsz + len; + if ((udesc->i_buf = alloc_buffer(udesc->i_bufsz)) == NULL) + return packet_error(udesc, ENOMEM); + /* pointer to message start */ + udesc->i_ptr = udesc->i_buf->orig_bytes + len; + } else { + ErlDrvBinary* tmp; + int bufsz; + bufsz = desc->bufsz + (udesc->i_ptr - udesc->i_buf->orig_bytes); + if ((tmp = realloc_buffer(udesc->i_buf, bufsz)) == NULL) { + release_buffer(udesc->i_buf); + udesc->i_buf = NULL; + return packet_error(udesc, ENOMEM); + } else { + udesc->i_ptr = + tmp->orig_bytes + (udesc->i_ptr - udesc->i_buf->orig_bytes); + udesc->i_buf = tmp; + udesc->i_bufsz = bufsz; + } + } /* Note: On Windows NT, recvfrom() fails if the socket is connected. */ #ifdef HAVE_SCTP /* For SCTP we must use recvmsg() */ if (IS_SCTP(desc)) { - iov->iov_base = ptr; /* Data will come here */ - iov->iov_len = sz; /* Remaining buffer space */ + iov->iov_base = udesc->i_ptr; /* Data will come here */ + iov->iov_len = desc->bufsz; /* Remaining buffer space */ - mhdr.msg_name = &other; /* Peer addr comes into "other" */ + mhdr.msg_name = &other; /* Peer addr comes into "other" */ mhdr.msg_namelen = len; mhdr.msg_iov = iov; mhdr.msg_iovlen = 1; @@ -9961,42 +10394,28 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event) /* Do the actual SCTP receive: */ n = sock_recvmsg(desc->s, &mhdr, 0); + len = mhdr.msg_namelen; goto check_result; } #endif /* Use recv() instead on connected sockets. */ if ((desc->state & INET_F_ACTIVE)) { - n = sock_recv(desc->s, ptr, sz, 0); + n = sock_recv(desc->s, udesc->i_ptr, desc->bufsz, 0); other = desc->remote; + goto check_result; } - else - n = sock_recvfrom(desc->s, ptr, sz, 0, &other.sa, &len); - -#ifdef HAVE_SCTP + n = sock_recvfrom(desc->s, udesc->i_ptr, desc->bufsz, + 0, &other.sa, &len); check_result: -#endif /* Analyse the result: */ - if (IS_SOCKET_ERROR(n) -#ifdef HAVE_SCTP - || (short_recv = (IS_SCTP(desc) && !(mhdr.msg_flags & MSG_EOR))) - /* NB: here we check for EOR not being set -- this is an error as - well, we don't support partial msgs: - */ -#endif - ) { + if (IS_SOCKET_ERROR(n)) { int err = sock_errno(); - release_buffer(buf); if (err != ERRNO_BLOCK) { + /* real error */ + release_buffer(udesc->i_buf); + udesc->i_buf = NULL; if (!desc->active) { -#ifdef HAVE_SCTP - if (short_recv) { - async_error_am(desc, am_short_recv); - } else { - async_error(desc, err); - } -#else async_error(desc, err); -#endif driver_cancel_timer(desc->port); sock_select(desc,FD_READ,0); } @@ -10004,46 +10423,72 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event) /* This is for an active desc only: */ packet_error_message(udesc, err); } + return count; } - else if (!desc->active) + /* would block error - try again */ + if (!desc->active +#ifdef HAVE_SCTP + || short_recv +#endif + ) { sock_select(desc,FD_READ,1); + } return count; /* strange, not ready */ } - else { - int offs; - int nsz; + +#ifdef HAVE_SCTP + if (IS_SCTP(desc) && (short_recv = !(mhdr.msg_flags & MSG_EOR))) { + /* SCTP non-final message fragment */ + inet_input_count(desc, n); + udesc->i_ptr += n; + continue; /* wait for more fragments */ + } +#endif + + { + /* message received */ int code; - unsigned int alen = len; void * extra = NULL; + char * ptr; + int nsz; inet_input_count(desc, n); - inet_get_address(desc->sfamily, abuf, &other, &alen); - /* Copy formatted address to the buffer allocated; "alen" is the - actual length which must be <= than the original reserved "len". + udesc->i_ptr += n; + inet_get_address(desc->sfamily, 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, - but they may start not at the "orig_bytes", but with some "offs" - from them: + but they may start not at the "orig_bytes", instead at "ptr": */ - ASSERT (alen <= len); - sys_memcpy(ptr - alen, abuf, alen); - ptr -= alen; - nsz = n + alen; /* nsz = data + address */ - offs = ptr - buf->orig_bytes; /* initial pointer offset */ + ASSERT (len <= sizeof(other)); + ptr = udesc->i_buf->orig_bytes + sizeof(other) - len; + sys_memcpy(ptr, abuf, len); + + nsz = udesc->i_ptr - ptr; /* Check if we need to reallocate binary */ - if ((desc->mode == INET_MODE_BINARY) && - (desc->hsz < n) && (nsz < BIN_REALLOC_LIMIT(sz))) { + if ((desc->mode == INET_MODE_BINARY) + && (desc->hsz < (nsz - len)) + && (nsz + BIN_REALLOC_MARGIN(desc->bufsz) < udesc->i_bufsz)) { ErlDrvBinary* tmp; - if ((tmp = realloc_buffer(buf,nsz+offs)) != NULL) - buf = tmp; + int bufsz; + bufsz = udesc->i_ptr - udesc->i_buf->orig_bytes; + if ((tmp = realloc_buffer(udesc->i_buf, bufsz)) != NULL) { + udesc->i_buf = tmp; + udesc->i_bufsz = bufsz; + udesc->i_ptr = NULL; /* not used from here */ + } } #ifdef HAVE_SCTP if (IS_SCTP(desc)) extra = &mhdr; #endif /* Actual parsing and return of the data received, occur here: */ - code = packet_reply_binary_data(desc, (unsigned int)alen, - buf, offs, nsz, extra); - free_buffer(buf); + code = packet_reply_binary_data(desc, len, udesc->i_buf, + (sizeof(other) - len), + nsz, + extra); + free_buffer(udesc->i_buf); + udesc->i_buf = NULL; if (code < 0) return count; count++; @@ -10053,7 +10498,17 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event) return count; /* passive mode (read one packet only) */ } } + } /* while(packet_count--) { */ + + /* we ran out of tries (packet_count) either on an active socket + * that got that many messages or an SCTP socket that got that + * many message fragments but still not the final + */ +#ifdef HAVE_SCTP + if (short_recv) { + sock_select(desc, FD_READ, 1); } +#endif return count; } @@ -10063,7 +10518,7 @@ static void packet_inet_drv_output(ErlDrvData e, ErlDrvEvent event) } /* UDP/SCTP socket ready for output: -** This is a Back-End for Non-Block SCTP Connect (SCTP_STATE_CONNECTING) +** This is a Back-End for Non-Block SCTP Connect (INET_STATE_CONNECTING) */ static int packet_inet_output(udp_descriptor* udesc, HANDLE event) { @@ -10074,7 +10529,7 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event) DEBUGF(("packet_inet_output(%ld) {s=%d\r\n", (long)desc->port, desc->s)); - if (desc->state == SCTP_STATE_CONNECTING) { + if (desc->state == INET_STATE_CONNECTING) { sock_select(desc, FD_CONNECT, 0); driver_cancel_timer(ix); /* posssibly cancel a timer */ @@ -10094,7 +10549,7 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event) (struct sockaddr*) &desc->remote, &sz); if (IS_SOCKET_ERROR(code)) { - desc->state = PACKET_STATE_BOUND; /* restore state */ + desc->state = INET_STATE_BOUND; /* restore state */ ret = async_error(desc, sock_errno()); goto done; } @@ -10107,15 +10562,15 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event) (void *)&error, &sz); if ((code < 0) || error) { - desc->state = PACKET_STATE_BOUND; /* restore state */ + desc->state = INET_STATE_BOUND; /* restore state */ ret = async_error(desc, error); goto done; } } -#endif /* SOCKOPT_CONNECT_STAT */ +#endif /* SO_ERROR */ #endif /* !__WIN32__ */ - desc->state = PACKET_STATE_CONNECTED; + desc->state = INET_STATE_CONNECTED; async_ok(desc); } else { diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c index d782b044a9..45d39a559f 100644 --- a/erts/emulator/drivers/unix/ttsl_drv.c +++ b/erts/emulator/drivers/unix/ttsl_drv.c @@ -242,7 +242,7 @@ static ErlDrvData ttysl_start(ErlDrvPort port, char* buf) #ifndef HAVE_TERMCAP return ERL_DRV_ERROR_GENERAL; #else - char *s, *t, c, *l; + char *s, *t, *l; int canon, echo, sig; /* Terminal characteristics */ int flag; extern int using_oldshell; /* set this to let the rest of erts know */ @@ -262,7 +262,6 @@ static ErlDrvData ttysl_start(ErlDrvPort port, char* buf) s++; /* Find end of this argument (start of next) and insert NUL. */ if ((t = strchr(s, ' '))) { - c = *t; *t = '\0'; } if ((flag = ((*s == '+') ? 1 : ((*s == '-') ? -1 : 0)))) { diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index 4b3934657c..d2d8713c1e 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -33,6 +33,9 @@ #include <sys/types.h> #include <sys/uio.h> #endif +#if defined(HAVE_SENDFILE) && (defined(__linux__) || (defined(__sun) && defined(__SVR4))) +#include <sys/sendfile.h> +#endif #if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) #define DARWIN 1 @@ -813,7 +816,6 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, char* name, int info_for_link) { struct stat statbuf; /* Information about the file */ - struct tm *timep; /* Broken-apart filetime. */ int result; #ifdef VXWORKS @@ -880,40 +882,17 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, else pInfo->type = FT_OTHER; -#if defined(HAVE_LOCALTIME_R) || defined(VXWORKS) - { - /* Use the reentrant version of localtime() */ - static struct tm local_tm; -#define localtime(a) (localtime_r((a), &local_tm), &local_tm) -#endif - + pInfo->accessTime = statbuf.st_atime; + pInfo->modifyTime = statbuf.st_mtime; + pInfo->cTime = statbuf.st_ctime; -#define GET_TIME(dst, src) \ - timep = localtime(&statbuf.src); \ - (dst).year = timep->tm_year+1900; \ - (dst).month = timep->tm_mon+1; \ - (dst).day = timep->tm_mday; \ - (dst).hour = timep->tm_hour; \ - (dst).minute = timep->tm_min; \ - (dst).second = timep->tm_sec - - GET_TIME(pInfo->accessTime, st_atime); - GET_TIME(pInfo->modifyTime, st_mtime); - GET_TIME(pInfo->cTime, st_ctime); - -#undef GET_TIME - -#if defined(HAVE_LOCALTIME_R) || defined(VXWORKS) - } -#endif - - pInfo->mode = statbuf.st_mode; - pInfo->links = statbuf.st_nlink; + pInfo->mode = statbuf.st_mode; + pInfo->links = statbuf.st_nlink; pInfo->major_device = statbuf.st_dev; pInfo->minor_device = statbuf.st_rdev; - pInfo->inode = statbuf.st_ino; - pInfo->uid = statbuf.st_uid; - pInfo->gid = statbuf.st_gid; + pInfo->inode = statbuf.st_ino; + pInfo->uid = statbuf.st_uid; + pInfo->gid = statbuf.st_gid; return 1; } @@ -921,6 +900,8 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, int efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name) { + struct utimbuf tval; + CHECK_PATHLEN(name, errInfo); #ifdef VXWORKS @@ -973,38 +954,18 @@ efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name) #endif /* !VXWORKS */ - if (pInfo->accessTime.year != -1 && pInfo->modifyTime.year != -1) { - struct utimbuf tval; - struct tm timebuf; - -#define MKTIME(tb, ts) \ - timebuf.tm_year = ts.year-1900; \ - timebuf.tm_mon = ts.month-1; \ - timebuf.tm_mday = ts.day; \ - timebuf.tm_hour = ts.hour; \ - timebuf.tm_min = ts.minute; \ - timebuf.tm_sec = ts.second; \ - timebuf.tm_isdst = -1; \ - if ((tb = mktime(&timebuf)) == (time_t) -1) { \ - errno = EINVAL; \ - return check_error(-1, errInfo); \ - } + tval.actime = pInfo->accessTime; + tval.modtime = pInfo->modifyTime; - MKTIME(tval.actime, pInfo->accessTime); - MKTIME(tval.modtime, pInfo->modifyTime); -#undef MKTIME - #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; + /* 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); + return check_error(utime(name, &tval), errInfo); #endif - } - return 1; } @@ -1464,3 +1425,77 @@ efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, return check_error(0, errInfo); #endif } + +#ifdef HAVE_SENDFILE +#define SENDFILE_CHUNK_SIZE ((1 << 30) -1) + +/* + * sendfile: The implementation of the sendfile system call varies + * a lot on different *nix platforms so to make the api similar in all + * we have to emulate some things in linux and play with variables on + * bsd/darwin. + * + * It could be possible to implement header/trailer in sendfile, though + * you would have to emulate it in linux and on BSD/Darwin some complex + * calculations have to be made when using a non blocking socket to figure + * out how much of the header/file/trailer was sent in each command. + */ + +int +efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd, + off_t *offset, Uint64 *nbytes, struct t_sendfile_hdtl* hdtl) +{ + Uint64 written = 0; +#if defined(__linux__) || (defined(__sun) && defined(__SVR4)) + ssize_t retval; + do { + // check if *nbytes is 0 or greater than the largest size_t + if (*nbytes == 0 || *nbytes > SENDFILE_CHUNK_SIZE) + retval = sendfile(out_fd, in_fd, offset, SENDFILE_CHUNK_SIZE); + else + retval = sendfile(out_fd, in_fd, offset, *nbytes); + if (retval > 0) { + written += retval; + *nbytes -= retval; + } + } while (retval != -1 && retval == SENDFILE_CHUNK_SIZE); + *nbytes = written; + return check_error(retval == -1 ? -1 : 0, errInfo); +#elif defined(DARWIN) + int retval; + off_t len; + do { + // check if *nbytes is 0 or greater than the largest off_t + if(*nbytes > SENDFILE_CHUNK_SIZE) + len = SENDFILE_CHUNK_SIZE; + else + len = *nbytes; + retval = sendfile(in_fd, out_fd, *offset, &len, NULL, 0); + if (retval != -1 || errno == EAGAIN || errno == EINTR) { + *offset += len; + *nbytes -= len; + written += len; + } + } while (len == SENDFILE_CHUNK_SIZE); + *nbytes = written; + return check_error(retval, errInfo); +#elif defined(__FreeBSD__) || defined(__DragonFly__) + off_t len; + int retval; + do { + if (*nbytes > SENDFILE_CHUNK_SIZE) + retval = sendfile(in_fd, out_fd, *offset, SENDFILE_CHUNK_SIZE, + NULL, &len, 0); + else + retval = sendfile(in_fd, out_fd, *offset, *nbytes, NULL, &len, 0); + if (retval != -1 || errno == EAGAIN || errno == EINTR) { + *offset += len; + *nbytes -= len; + written += len; + } + } while(len == SENDFILE_CHUNK_SIZE); + *nbytes = written; + return check_error(retval, errInfo); +#endif +} +#endif /* HAVE_SENDFILE */ diff --git a/erts/emulator/drivers/win32/ttsl_drv.c b/erts/emulator/drivers/win32/ttsl_drv.c index fd88dafd34..e636761c67 100644 --- a/erts/emulator/drivers/win32/ttsl_drv.c +++ b/erts/emulator/drivers/win32/ttsl_drv.c @@ -21,6 +21,9 @@ * smart line for output. */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif #include "sys.h" #include <ctype.h> #include <stdlib.h> diff --git a/erts/emulator/drivers/win32/win_con.c b/erts/emulator/drivers/win32/win_con.c index c788ad409d..6b45b92cbe 100644 --- a/erts/emulator/drivers/win32/win_con.c +++ b/erts/emulator/drivers/win32/win_con.c @@ -21,6 +21,9 @@ #define _UNICODE 1 #include <tchar.h> #include <stdio.h> +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif #include "sys.h" #include <windowsx.h> #include "resource.h" @@ -34,6 +37,23 @@ #define REALLOC(X,Y) realloc(X,Y) #define FREE(X) free(X) +#if SIZEOF_VOID_P == 8 +#define WIN64 1 +#ifndef GCL_HBRBACKGROUND +#define GCL_HBRBACKGROUND GCLP_HBRBACKGROUND +#endif +#define DIALOG_PROC_RET INT_PTR +#define CF_HOOK_RET INT_PTR +#define CC_HOOK_RET INT_PTR +#define OFN_HOOK_RET INT_PTR +#else +#define DIALOG_PROC_RET BOOL +#define CF_HOOK_RET UINT +#define CC_HOOK_RET UINT +#define OFN_HOOK_RET UINT +#endif + + #ifndef STATE_SYSTEM_INVISIBLE /* Mingw problem with oleacc.h and WIN32_LEAN_AND_MEAN */ #define STATE_SYSTEM_INVISIBLE 0x00008000 @@ -150,7 +170,7 @@ static TCHAR *erlang_window_title = TEXT("Erlang"); static unsigned __stdcall ConThreadInit(LPVOID param); static LRESULT CALLBACK ClientWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam); -static BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam); +static DIALOG_PROC_RET CALLBACK AboutDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam); static ScreenLine_t *ConNewLine(void); static void DeleteTopLine(void); static void ensure_line_below(void); @@ -1608,7 +1628,7 @@ OnEditSelAll(HWND hwnd) InvalidateRect(hwnd, NULL, TRUE); } -UINT APIENTRY CFHookProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam) +CF_HOOK_RET APIENTRY CFHookProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam) { /* Hook procedure for font dialog box */ HWND hOwner; @@ -1626,11 +1646,11 @@ UINT APIENTRY CFHookProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam) OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom); SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2), rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE); - return 1; + return (CF_HOOK_RET) 1; default: break; } - return 0; /* Let the default procedure process the message */ + return (CF_HOOK_RET) 0; /* Let the default procedure process the message */ } static BOOL @@ -1705,7 +1725,7 @@ ConSetFont(HWND hwnd) InvalidateRect(hwnd, NULL, TRUE); } -UINT APIENTRY +CC_HOOK_RET APIENTRY CCHookProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam) { /* Hook procedure for choose color dialog box */ @@ -1724,11 +1744,11 @@ CCHookProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam) OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom); SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2), rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE); - return 1; + return (CC_HOOK_RET) 1; default: break; } - return 0; /* Let the default procedure process the message */ + return (CC_HOOK_RET) 0; /* Let the default procedure process the message */ } void ConChooseColor(HWND hwnd) @@ -1758,7 +1778,8 @@ void ConChooseColor(HWND hwnd) } } -UINT APIENTRY OFNHookProc(HWND hwndDlg,UINT iMsg,WPARAM wParam,LPARAM lParam) +OFN_HOOK_RET APIENTRY OFNHookProc(HWND hwndDlg,UINT iMsg, + WPARAM wParam,LPARAM lParam) { /* Hook procedure for open file dialog box */ HWND hOwner,hDlg; @@ -1777,11 +1798,11 @@ UINT APIENTRY OFNHookProc(HWND hwndDlg,UINT iMsg,WPARAM wParam,LPARAM lParam) OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom); SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2), rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE); - return 1; + return (OFN_HOOK_RET) 1; default: break; } - return 0; /* the let default procedure process the message */ + return (OFN_HOOK_RET) 0; /* the let default procedure process the message */ } static void @@ -1933,7 +1954,7 @@ write_outbuf(TCHAR *data, int num_chars) return num_chars; } -BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam) +DIALOG_PROC_RET CALLBACK AboutDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam) { HWND hOwner; RECT rc,rcOwner,rcDlg; @@ -1953,17 +1974,17 @@ BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam) rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE); SetDlgItemText(hDlg, ID_VERSIONSTRING, TEXT("Erlang emulator version ") TEXT(ERLANG_VERSION)); - return TRUE; + return (DIALOG_PROC_RET) TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: EndDialog(hDlg,0); - return TRUE; + return (DIALOG_PROC_RET) TRUE; } break; } - return FALSE; + return (DIALOG_PROC_RET) FALSE; } static void @@ -2117,7 +2138,7 @@ AddToCmdHistory(void) } } -static TBBUTTON tbb[] = +/*static TBBUTTON tbb[] = { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, @@ -2149,6 +2170,39 @@ static TBBUTTON tbb[] = 2, IDMENU_FONT, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0, 3, IDMENU_ABOUT, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0, 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + };*/ +static TBBUTTON tbb[] = +{ + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP}, + {0, IDMENU_COPY, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE}, + {1, IDMENU_PASTE, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE}, + {2, IDMENU_FONT, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE}, + {3, IDMENU_ABOUT, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE}, + {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP} }; static TBADDBITMAP tbbitmap = @@ -2156,6 +2210,17 @@ static TBADDBITMAP tbbitmap = HINST_COMMCTRL, IDB_STD_SMALL_COLOR, }; +#ifdef HARDDEBUG +/* For really hard GUI startup debugging, place DEBUGBOX() macros in code + and get modal message boxes with the line number. */ +static void debug_box(int line) { + TCHAR buff[1024]; + swprintf(buff,1024,TEXT("DBG:%d"),line); + MessageBox(NULL,buff,TEXT("DBG"),MB_OK|MB_APPLMODAL); +} + +#define DEBUGBOX() debug_box(__LINE__) +#endif static HWND InitToolBar(HWND hwndParent) @@ -2169,7 +2234,6 @@ InitToolBar(HWND hwndParent) COLORMAP colorMap; colorMap.from = RGB(192, 192, 192); colorMap.to = backgroundColor; - /* Create toolbar window with tooltips */ hwndTB = CreateWindowEx(0,TOOLBARCLASSNAME,(TCHAR *)NULL, WS_CHILD|CCS_TOP|WS_CLIPSIBLINGS|TBSTYLE_TOOLTIPS, @@ -2180,9 +2244,10 @@ InitToolBar(HWND hwndParent) tbbitmap.hInst = NULL; tbbitmap.nID = (UINT) CreateMappedBitmap(beam_module, 1,0, &colorMap, 1); SendMessage(hwndTB, TB_ADDBITMAP, (WPARAM) 4, - (WPARAM) &tbbitmap); + (LPARAM) &tbbitmap); + SendMessage(hwndTB,TB_ADDBUTTONS, (WPARAM) 30, - (LPARAM) (LPTBBUTTON) tbb); + (LPARAM) tbb); if (toolbarVisible) ShowWindow(hwndTB, SW_SHOW); diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index 931bb196f1..0d3d334154 100755..100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -21,6 +21,9 @@ */ #include <windows.h> +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif #include "sys.h" #include <ctype.h> #include <wchar.h> @@ -42,6 +45,26 @@ #define INVALID_FILE_ATTRIBUTES ((DWORD) 0xFFFFFFFF) #endif +#define TICKS_PER_SECOND (10000000ULL) +#define EPOCH_DIFFERENCE (11644473600LL) + +#define FILETIME_TO_EPOCH(epoch, ft) \ + do { \ + ULARGE_INTEGER ull; \ + ull.LowPart = (ft).dwLowDateTime; \ + ull.HighPart = (ft).dwHighDateTime; \ + (epoch) = ((ull.QuadPart / TICKS_PER_SECOND) - EPOCH_DIFFERENCE); \ + } while(0) + +#define EPOCH_TO_FILETIME(ft, epoch) \ + do { \ + ULARGE_INTEGER ull; \ + ull.QuadPart = (((epoch) + EPOCH_DIFFERENCE) * TICKS_PER_SECOND); \ + (ft).dwLowDateTime = ull.LowPart; \ + (ft).dwHighDateTime = ull.HighPart; \ + } while(0) + + static int check_error(int result, Efile_error* errInfo); static int set_error(Efile_error* errInfo); static int is_root_unc_name(const WCHAR *path); @@ -861,14 +884,7 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, findbuf.cFileName[0] = L'\0'; pInfo->links = 1; - pInfo->modifyTime.year = 1980; - pInfo->modifyTime.month = 1; - pInfo->modifyTime.day = 1; - pInfo->modifyTime.hour = 0; - pInfo->modifyTime.minute = 0; - pInfo->modifyTime.second = 0; - - pInfo->accessTime = pInfo->modifyTime; + pInfo->cTime = pInfo->accessTime = pInfo->modifyTime = 0; } else { SYSTEMTIME SystemTime; FILETIME LocalFTime; @@ -902,34 +918,21 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, } } -#define GET_TIME(dst, src) \ -if (!FileTimeToLocalFileTime(&findbuf.src, &LocalFTime) || \ - !FileTimeToSystemTime(&LocalFTime, &SystemTime)) { \ - return set_error(errInfo); \ -} \ -(dst).year = SystemTime.wYear; \ -(dst).month = SystemTime.wMonth; \ -(dst).day = SystemTime.wDay; \ -(dst).hour = SystemTime.wHour; \ -(dst).minute = SystemTime.wMinute; \ -(dst).second = SystemTime.wSecond; - - GET_TIME(pInfo->modifyTime, ftLastWriteTime); + FILETIME_TO_EPOCH(pInfo->modifyTime, findbuf.ftLastWriteTime); if (findbuf.ftLastAccessTime.dwLowDateTime == 0 && findbuf.ftLastAccessTime.dwHighDateTime == 0) { pInfo->accessTime = pInfo->modifyTime; } else { - GET_TIME(pInfo->accessTime, ftLastAccessTime); + FILETIME_TO_EPOCH(pInfo->accessTime, findbuf.ftLastAccessTime); } if (findbuf.ftCreationTime.dwLowDateTime == 0 && findbuf.ftCreationTime.dwHighDateTime == 0) { pInfo->cTime = pInfo->modifyTime; } else { - GET_TIME(pInfo->cTime, ftCreationTime); + FILETIME_TO_EPOCH(pInfo->cTime ,findbuf.ftCreationTime); } -#undef GET_TIME FindClose(findhandle); } @@ -965,17 +968,12 @@ efile_write_info(Efile_error* errInfo, char* name) { SYSTEMTIME timebuf; - FILETIME LocalFileTime; FILETIME ModifyFileTime; FILETIME AccessFileTime; FILETIME CreationFileTime; HANDLE fd; - FILETIME* mtime = NULL; - FILETIME* atime = NULL; - FILETIME* ctime = NULL; DWORD attr; DWORD tempAttr; - BOOL modifyTime = FALSE; WCHAR *wname = (WCHAR *) name; /* @@ -1000,57 +998,36 @@ efile_write_info(Efile_error* errInfo, * Construct all file times. */ -#define MKTIME(tb, ts, ptr) \ - timebuf.wYear = ts.year; \ - timebuf.wMonth = ts.month; \ - timebuf.wDay = ts.day; \ - timebuf.wHour = ts.hour; \ - timebuf.wMinute = ts.minute; \ - timebuf.wSecond = ts.second; \ - timebuf.wMilliseconds = 0; \ - if (ts.year != -1) { \ - modifyTime = TRUE; \ - ptr = &tb; \ - if (!SystemTimeToFileTime(&timebuf, &LocalFileTime ) || \ - !LocalFileTimeToFileTime(&LocalFileTime, &tb)) { \ - errno = EINVAL; \ - return check_error(-1, errInfo); \ - } \ - } - - MKTIME(ModifyFileTime, pInfo->modifyTime, mtime); - MKTIME(AccessFileTime, pInfo->accessTime, atime); - MKTIME(CreationFileTime, pInfo->cTime, ctime); -#undef MKTIME + EPOCH_TO_FILETIME(ModifyFileTime, pInfo->modifyTime); + EPOCH_TO_FILETIME(AccessFileTime, pInfo->accessTime); + EPOCH_TO_FILETIME(CreationFileTime, pInfo->cTime); /* * If necessary, set the file times. */ - if (modifyTime) { - /* - * If the has read only access, we must temporarily turn on - * write access (this is necessary for native filesystems, - * but not for NFS filesystems). - */ + /* + * If the has read only access, we must temporarily turn on + * write access (this is necessary for native filesystems, + * but not for NFS filesystems). + */ - if (tempAttr & FILE_ATTRIBUTE_READONLY) { - tempAttr &= ~FILE_ATTRIBUTE_READONLY; - if (!SetFileAttributesW(wname, tempAttr)) { - return set_error(errInfo); - } + if (tempAttr & FILE_ATTRIBUTE_READONLY) { + tempAttr &= ~FILE_ATTRIBUTE_READONLY; + if (!SetFileAttributesW(wname, tempAttr)) { + return set_error(errInfo); } + } - fd = CreateFileW(wname, GENERIC_READ|GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (fd != INVALID_HANDLE_VALUE) { - BOOL result = SetFileTime(fd, ctime, atime, mtime); - if (!result) { - return set_error(errInfo); - } - CloseHandle(fd); + fd = CreateFileW(wname, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (fd != INVALID_HANDLE_VALUE) { + BOOL result = SetFileTime(fd, &CreationFileTime, &AccessFileTime, &ModifyFileTime); + if (!result) { + return set_error(errInfo); } + CloseHandle(fd); } /* |