diff options
Diffstat (limited to 'erts')
33 files changed, 1132 insertions, 348 deletions
diff --git a/erts/configure.in b/erts/configure.in index e6c412e666..50f8908f7a 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1698,7 +1698,9 @@ case $host_os in AC_CHECK_FUNCS([sendfile]) ;; solaris*) - AC_SEARCH_LIBS(sendfile, sendfile, AC_DEFINE(HAVE_SENDFILE, 1)) + AC_SEARCH_LIBS(sendfilev, sendfile, + AC_DEFINE([HAVE_SENDFILEV],[1], + [Define to 1 if you have the `sendfilev' function.])) ;; win32) LIBS="$LIBS -lmswsock" diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index eac7db5eaa..a603d5c2b8 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -724,9 +724,12 @@ false</pre> size limit.</p> </item> <tag><c>{line_length, integer()}</c></tag> - <item><p>Applies only to line oriented protocols - (<c>line</c>, <c>http</c>). Lines longer than this - will be truncated.</p> + <item><p>For packet type <c>line</c>, truncate lines longer + than the indicated length.</p> + <p>Option <c>line_length</c> also applies to <c>http*</c> + packet types as an alias for option <c>packet_size</c> in the + case when <c>packet_size</c> itself is not set. This usage is + only intended for backward compatibility.</p> </item> </taglist> <pre> diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 26f1b4facb..55f4798892 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -3385,6 +3385,61 @@ BIF_RETTYPE universaltime_to_localtime_1(BIF_ALIST_1) BIF_RET(TUPLE2(hp, res1, res2)); } +/* convert calendar:universaltime_to_seconds/1 */ + +BIF_RETTYPE universaltime_to_posixtime_1(BIF_ALIST_1) +{ + Sint year, month, day; + Sint hour, minute, second; + + Sint64 seconds = 0; + Eterm *hp; + Uint hsz = 0; + + if (!time_to_parts(BIF_ARG_1, &year, &month, &day, + &hour, &minute, &second)) + BIF_ERROR(BIF_P, BADARG); + + if (!univ_to_seconds(year, month, day, hour, minute, second, &seconds)) { + BIF_ERROR(BIF_P, BADARG); + } + + erts_bld_sint64(NULL, &hsz, seconds); + hp = HAlloc(BIF_P, hsz); + BIF_RET(erts_bld_sint64(&hp, NULL, seconds)); +} + +/* convert calendar:seconds_to_universaltime/1 */ + +BIF_RETTYPE posixtime_to_universaltime_1(BIF_ALIST_1) +{ + Sint year, month, day; + Sint hour, minute, second; + Eterm res1, res2; + Eterm* hp; + + Sint64 time = 0; + + if (!term_to_Sint64(BIF_ARG_1, &time)) { + BIF_ERROR(BIF_P, BADARG); + } + + if (!seconds_to_univ(time, &year, &month, &day, + &hour, &minute, &second)) { + BIF_ERROR(BIF_P, BADARG); + } + + hp = HAlloc(BIF_P, 4+4+3); + res1 = TUPLE3(hp,make_small(year),make_small(month), + make_small(day)); + hp += 4; + res2 = TUPLE3(hp,make_small(hour),make_small(minute), + make_small(second)); + hp += 4; + BIF_RET(TUPLE2(hp, res1, res2)); +} + + /**********************************************************************/ diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 987008c937..8cc568b16c 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -806,6 +806,12 @@ bif file:native_name_encoding/0 # bif erlang:check_old_code/1 + +# +# New in R15B +# +bif erlang:universaltime_to_posixtime/1 +bif erlang:posixtime_to_universaltime/1 # # Obsolete # diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 1379f8645a..1e75afe6f6 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -2877,8 +2877,9 @@ reply_alloc_info(void *vair) ainfo); ainfo = erts_bld_tuple(hpp, szp, 2, erts_bld_atom(hpp, szp, - "otps"), + "options"), ainfo); + ainfo = erts_bld_cons(hpp, szp,ainfo,NIL); } ainfo = erts_bld_tuple(hpp, szp, 3, alloc_atom, diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index af386c9197..c32938bdff 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -3014,9 +3014,7 @@ info_options(Allctr_t *allctr, add_2tup(hpp, szp, &res, am.low, allctr->mseg_opt.low_mem ? am_true : am_false); #endif add_2tup(hpp, szp, &res, am.ramv, allctr->ramv ? am_true : am_false); - add_2tup(hpp, szp, &res, am.t, (allctr->t - ? bld_uint(hpp, szp, (Uint) allctr->t) - : am_false)); + add_2tup(hpp, szp, &res, am.t, (allctr->t ? am_true : am_false)); add_2tup(hpp, szp, &res, am.e, am_true); } diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index 2dc7237f7c..8bca9ae582 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -304,8 +304,9 @@ static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q, switch (erts_thr_q_inspect(q, 1)) { case ERTS_THR_Q_DIRTY: break; + case ERTS_THR_Q_NEED_THR_PRGR: #ifdef ERTS_SMP - case ERTS_THR_Q_NEED_THR_PRGR: { + { ErtsThrPrgrVal prgr = erts_thr_q_need_thr_progress(q); erts_thr_progress_wakeup(NULL, prgr); /* @@ -522,8 +523,8 @@ int erts_async_ready_clean(void *varq, void *val) switch (cstate) { case ERTS_THR_Q_DIRTY: return ERTS_ASYNC_READY_DIRTY; -#ifdef ERTS_SMP case ERTS_THR_Q_NEED_THR_PRGR: +#ifdef ERTS_SMP *((ErtsThrPrgrVal *) val) = erts_thr_q_need_thr_progress(&arq->thr_q); return ERTS_ASYNC_READY_NEED_THR_PRGR; diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 055211ad9b..b8c6b64fc0 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -769,8 +769,8 @@ misc_aux_work_clean(ErtsThrQ_t *q, case ERTS_THR_Q_DIRTY: set_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_MISC); return aux_work | ERTS_SSI_AUX_WORK_MISC; -#ifdef ERTS_SMP case ERTS_THR_Q_NEED_THR_PRGR: +#ifdef ERTS_SMP set_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_MISC_THR_PRGR); erts_thr_progress_wakeup(awdp->esdp, erts_thr_q_need_thr_progress(q)); diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c index 9324bcde51..02f36fc75e 100644 --- a/erts/emulator/beam/erl_thr_progress.c +++ b/erts/emulator/beam/erl_thr_progress.c @@ -80,12 +80,12 @@ #ifdef ERTS_SMP -/* - * We use a 64-bit value for thread progress. By this wrapping of - * the thread progress will more or less never occur. - * - * On 32-bit systems we therefore need a double word atomic. - */ +#define ERTS_THR_PRGR_DBG_CHK_WAKEUP_REQUEST_VALUE 0 + +#ifdef DEBUG +#undef ERTS_THR_PRGR_DBG_CHK_WAKEUP_REQUEST_VALUE +#define ERTS_THR_PRGR_DBG_CHK_WAKEUP_REQUEST_VALUE 1 +#endif #define ERTS_THR_PRGR_PRINT_LEADER 0 #define ERTS_THR_PRGR_PRINT_VAL 0 @@ -106,6 +106,13 @@ |ERTS_THR_PRGR_LFLG_ACTIVE_MASK)) \ == ERTS_THR_PRGR_LFLG_NO_LEADER) +/* + * We use a 64-bit value for thread progress. By this wrapping of + * the thread progress will more or less never occur. + * + * On 32-bit systems we therefore need a double word atomic. + */ + #define read_acqb erts_thr_prgr_read_acqb__ #ifdef ARCH_64 @@ -937,8 +944,10 @@ request_wakeup_managed(ErtsThrPrgrData *tpd, ErtsThrPrgrVal value) ASSERT(tpd->is_managed); ASSERT(tpd->previous.local != ERTS_THR_PRGR_VAL_WAITING); - if (has_reached_wakeup(value)) + if (has_reached_wakeup(value)) { wakeup_managed(tpd->id); + return; + } wix = ERTS_THR_PRGR_WAKEUP_IX(value); if (tpd->wakeup_request[wix] == value) @@ -976,6 +985,10 @@ request_wakeup_managed(ErtsThrPrgrData *tpd, ErtsThrPrgrVal value) mwd = intrnl->managed.data[wix]; ix = erts_atomic32_inc_read_nob(&mwd->len) - 1; +#if ERTS_THR_PRGR_DBG_CHK_WAKEUP_REQUEST_VALUE + if (ix >= intrnl->managed.no) + erl_exit(ERTS_ABORT_EXIT, "Internal error: Too many wakeup requests\n"); +#endif mwd->id[ix] = tpd->id; ASSERT(!erts_thr_progress_has_reached(value)); @@ -1001,8 +1014,10 @@ request_wakeup_unmanaged(ErtsThrPrgrData *tpd, ErtsThrPrgrVal value) * we are writing the request. */ - if (has_reached_wakeup(value)) + if (has_reached_wakeup(value)) { wakeup_unmanaged(tpd->id); + return; + } wix = ERTS_THR_PRGR_WAKEUP_IX(value); diff --git a/erts/emulator/beam/erl_thr_queue.c b/erts/emulator/beam/erl_thr_queue.c index 9ac4cd4b8e..efb8c635d7 100644 --- a/erts/emulator/beam/erl_thr_queue.c +++ b/erts/emulator/beam/erl_thr_queue.c @@ -449,32 +449,44 @@ clean(ErtsThrQ_t *q, int max_ops, int do_notify) if (inext == (erts_aint_t) &q->tail.data.marker) { q->head.head.ptr->next.ptr = &q->tail.data.marker; q->head.head.ptr = &q->tail.data.marker; -#ifdef ERTS_SMP - if (!q->head.next.thr_progress_reached) - return ERTS_THR_Q_NEED_THR_PRGR; -#else - if (do_notify) - q->head.notify(q->head.arg); -#endif - return ERTS_THR_Q_DIRTY; + goto check_thr_progress; } } } + + if (q->q.finalizing) { + ilast = erts_atomic_read_nob(&q->tail.data.last); + if (q->head.first == ((ErtsThrQElement_t *) ilast) + && ((ErtsThrQElement_t *) ilast) == &q->tail.data.marker + && q->head.first == &q->tail.data.marker) { + destroy(q); + } + else { + goto dirty; + } + } return ERTS_THR_Q_CLEAN; } - if (q->head.first != q->head.unref_end) { - if (do_notify) - q->head.notify(q->head.arg); - return ERTS_THR_Q_DIRTY; - } + if (q->head.first != q->head.unref_end) + goto dirty; + +check_thr_progress: #ifdef ERTS_SMP - if (!q->head.next.thr_progress_reached) - return ERTS_THR_Q_NEED_THR_PRGR; + if (q->head.next.thr_progress_reached) #endif + { + int um_refc_ix = q->head.next.um_refc_ix; + if (erts_atomic_read_acqb(&q->tail.data.um_refc[um_refc_ix]) == 0) { + dirty: + if (do_notify) + q->head.notify(q->head.arg); + return ERTS_THR_Q_DIRTY; + } + } - return ERTS_THR_Q_CLEAN; /* Waiting for unmanaged threads to complete... */ + return ERTS_THR_Q_NEED_THR_PRGR; } #endif @@ -492,7 +504,9 @@ erts_thr_q_clean(ErtsThrQ_t *q) ErtsThrQCleanState_t erts_thr_q_inspect(ErtsThrQ_t *q, int ensure_empty) { -#ifdef USE_THREADS +#ifndef USE_THREADS + return ERTS_THR_Q_CLEAN; +#else if (ensure_empty) { erts_aint_t inext; inext = erts_atomic_read_acqb(&q->head.head.ptr->next.atmc); @@ -523,11 +537,15 @@ erts_thr_q_inspect(ErtsThrQ_t *q, int ensure_empty) return ERTS_THR_Q_DIRTY; #ifdef ERTS_SMP - if (!q->head.next.thr_progress_reached) - return ERTS_THR_Q_NEED_THR_PRGR; + if (q->head.next.thr_progress_reached) #endif + { + int um_refc_ix = q->head.next.um_refc_ix; + if (erts_atomic_read_acqb(&q->tail.data.um_refc[um_refc_ix]) == 0) + return ERTS_THR_Q_DIRTY; + } + return ERTS_THR_Q_NEED_THR_PRGR; #endif - return ERTS_THR_Q_CLEAN; } static void diff --git a/erts/emulator/beam/erl_thr_queue.h b/erts/emulator/beam/erl_thr_queue.h index 407c23f5eb..edcf2c3823 100644 --- a/erts/emulator/beam/erl_thr_queue.h +++ b/erts/emulator/beam/erl_thr_queue.h @@ -96,9 +96,7 @@ typedef struct { typedef enum { ERTS_THR_Q_CLEAN, -#ifdef ERTS_SMP ERTS_THR_Q_NEED_THR_PRGR, -#endif ERTS_THR_Q_DIRTY, } ErtsThrQCleanState_t; diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index b319288f7d..f782e2f0b1 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -494,7 +494,7 @@ get_time(int *hour, int *minute, int *second) the_clock = time((time_t *)0); #ifdef HAVE_LOCALTIME_R - localtime_r(&the_clock, (tm = &tmbuf)); + tm = localtime_r(&the_clock, &tmbuf); #else tm = localtime(&the_clock); #endif @@ -516,7 +516,7 @@ get_date(int *year, int *month, int *day) the_clock = time((time_t *)0); #ifdef HAVE_LOCALTIME_R - localtime_r(&the_clock, (tm = &tmbuf)); + tm = localtime_r(&the_clock, &tmbuf); #else tm = localtime(&the_clock); #endif @@ -586,7 +586,44 @@ static const int mdays[14] = {0, 31, 28, 31, 30, 31, 30, (((y) % 100) != 0)) || \ (((y) % 400) == 0)) -#define BASEYEAR 1970 +/* This is the earliest year we are sure to be able to handle + on all platforms w/o problems */ +#define BASEYEAR 1902 + +/* A more "clever" mktime + * return 1, if successful + * return -1, if not successful + */ + +static int erl_mktime(time_t *c, struct tm *tm) { + time_t clock; + + clock = mktime(tm); + + if (clock != -1) { + *c = clock; + return 1; + } + + /* in rare occasions mktime returns -1 + * when a correct value has been entered + * + * decrease seconds with one second + * if the result is -2, epochs should be -1 + */ + + tm->tm_sec = tm->tm_sec - 1; + clock = mktime(tm); + tm->tm_sec = tm->tm_sec + 1; + + *c = -1; + + if (clock == -2) { + return 1; + } + + return -1; +} /* * gregday @@ -597,8 +634,8 @@ static const int mdays[14] = {0, 31, 28, 31, 30, 31, 30, */ static time_t gregday(int year, int month, int day) { - time_t ndays = 0; - time_t gyear, pyear, m; + Sint ndays = 0; + Sint gyear, pyear, m; /* number of days in previous years */ gyear = year - 1600; @@ -613,10 +650,72 @@ static time_t gregday(int year, int month, int day) if (is_leap_year(year) && (month > 2)) ndays++; ndays += day - 1; - return ndays - 135140; /* 135140 = Jan 1, 1970 */ + return (time_t) (ndays - 135140); /* 135140 = Jan 1, 1970 */ +} + +#define SECONDS_PER_MINUTE (60) +#define SECONDS_PER_HOUR (60 * SECONDS_PER_MINUTE) +#define SECONDS_PER_DAY (24 * SECONDS_PER_HOUR) + +int seconds_to_univ(Sint64 time, Sint *year, Sint *month, Sint *day, + Sint *hour, Sint *minute, Sint *second) { + + Sint y,mi; + Sint days = time / SECONDS_PER_DAY; + Sint secs = time % SECONDS_PER_DAY; + Sint tmp; + + if (secs < 0) { + days--; + secs += SECONDS_PER_DAY; + } + + tmp = secs % SECONDS_PER_HOUR; + + *hour = secs / SECONDS_PER_HOUR; + *minute = tmp / SECONDS_PER_MINUTE; + *second = tmp % SECONDS_PER_MINUTE; + + days += 719468; + y = (10000*((Sint64)days) + 14780) / 3652425; + tmp = days - (365 * y + y/4 - y/100 + y/400); + + if (tmp < 0) { + y--; + tmp = days - (365*y + y/4 - y/100 + y/400); + } + mi = (100 * tmp + 52)/3060; + *month = (mi + 2) % 12 + 1; + *year = y + (mi + 2) / 12; + *day = tmp - (mi * 306 + 5)/10 + 1; + + return 1; } +int univ_to_seconds(Sint year, Sint month, Sint day, Sint hour, Sint minute, Sint second, Sint64 *time) { + Sint days; + + if (!(IN_RANGE(1600, year, INT_MAX - 1) && + IN_RANGE(1, month, 12) && + IN_RANGE(1, day, (mdays[month] + + (month == 2 + && (year % 4 == 0) + && (year % 100 != 0 || year % 400 == 0)))) && + IN_RANGE(0, hour, 23) && + IN_RANGE(0, minute, 59) && + IN_RANGE(0, second, 59))) { + return 0; + } + + days = gregday(year, month, day); + *time = SECONDS_PER_DAY; + *time *= days; /* don't try overflow it, it hurts */ + *time += SECONDS_PER_HOUR * hour; + *time += SECONDS_PER_MINUTE * minute; + *time += second; + return 1; +} int local_to_univ(Sint *year, Sint *month, Sint *day, @@ -647,15 +746,18 @@ local_to_univ(Sint *year, Sint *month, Sint *day, t.tm_min = *minute; t.tm_sec = *second; t.tm_isdst = isdst; - the_clock = mktime(&t); - if (the_clock == -1) { + + /* the nature of mktime makes this a bit interesting, + * up to four mktime calls could happen here + */ + + if (erl_mktime(&the_clock, &t) < 0) { if (isdst) { /* If this is a timezone without DST and the OS (correctly) refuses to give us a DST time, we simulate the Linux/Solaris behaviour of giving the same data as if is_dst was not set. */ t.tm_isdst = 0; - the_clock = mktime(&t); - if (the_clock == -1) { + if (erl_mktime(&the_clock, &t)) { /* Failed anyway, something else is bad - will be a badarg */ return 0; } @@ -665,10 +767,13 @@ local_to_univ(Sint *year, Sint *month, Sint *day, } } #ifdef HAVE_GMTIME_R - gmtime_r(&the_clock, (tm = &tmbuf)); + tm = gmtime_r(&the_clock, &tmbuf); #else tm = gmtime(&the_clock); #endif + if (!tm) { + return 0; + } *year = tm->tm_year + 1900; *month = tm->tm_mon +1; *day = tm->tm_mday; @@ -722,17 +827,20 @@ univ_to_local(Sint *year, Sint *month, Sint *day, #endif #ifdef HAVE_LOCALTIME_R - localtime_r(&the_clock, (tm = &tmbuf)); + tm = localtime_r(&the_clock, &tmbuf); #else tm = localtime(&the_clock); #endif - *year = tm->tm_year + 1900; - *month = tm->tm_mon +1; - *day = tm->tm_mday; - *hour = tm->tm_hour; - *minute = tm->tm_min; - *second = tm->tm_sec; - return 1; + if (tm) { + *year = tm->tm_year + 1900; + *month = tm->tm_mon +1; + *day = tm->tm_mday; + *hour = tm->tm_hour; + *minute = tm->tm_min; + *second = tm->tm_sec; + return 1; + } + return 0; } diff --git a/erts/emulator/beam/packet_parser.c b/erts/emulator/beam/packet_parser.c index a66d60aa22..4d4b6ea196 100644 --- a/erts/emulator/beam/packet_parser.c +++ b/erts/emulator/beam/packet_parser.c @@ -301,7 +301,11 @@ int packet_get_length(enum PacketParseType htype, /* TCP_PB_LINE_LF: [Data ... \n] */ const char* ptr2; if ((ptr2 = memchr(ptr, '\n', n)) == NULL) { - if (n >= trunc_len && trunc_len!=0) { /* buffer full */ + if (n > max_plen && max_plen != 0) { /* packet full */ + DEBUGF((" => packet full (no NL)=%d\r\n", n)); + goto error; + } + else if (n >= trunc_len && trunc_len!=0) { /* buffer full */ DEBUGF((" => line buffer full (no NL)=%d\r\n", n)); return trunc_len; } @@ -309,6 +313,10 @@ int packet_get_length(enum PacketParseType htype, } else { int len = (ptr2 - ptr) + 1; /* including newline */ + if (len > max_plen && max_plen!=0) { + DEBUGF((" => packet_size %d exceeded\r\n", max_plen)); + goto error; + } if (len > trunc_len && trunc_len!=0) { DEBUGF((" => truncated line=%d\r\n", trunc_len)); return trunc_len; @@ -397,33 +405,50 @@ int packet_get_length(enum PacketParseType htype, const char* ptr1 = ptr; int len = plen; + if (!max_plen) { + /* This is for backward compatibility with old user of decode_packet + * that might use option 'line_length' to limit accepted length of + * http lines. + */ + max_plen = trunc_len; + } + while (1) { const char* ptr2 = memchr(ptr1, '\n', len); if (ptr2 == NULL) { - if (n >= trunc_len && trunc_len!=0) { /* buffer full */ - plen = trunc_len; - goto done; + if (max_plen != 0) { + if (n >= max_plen) /* packet full */ + goto error; } goto more; } else { plen = (ptr2 - ptr) + 1; - - if (*statep == 0) + + if (*statep == 0) { + if (max_plen != 0 && plen > max_plen) + goto error; goto done; - + } + if (plen < n) { if (SP(ptr2+1) && plen>2) { /* header field value continue on next line */ ptr1 = ptr2+1; len = n - plen; } - else + else { + if (max_plen != 0 && plen > max_plen) + goto error; goto done; + } } - else + else { + if (max_plen != 0 && plen > max_plen) + goto error; goto more; + } } } } diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index efc6dd2c6b..6b4b382caa 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -667,7 +667,14 @@ void get_localtime(int *year, int *month, int *day, int *hour, int *minute, int *second); void get_universaltime(int *year, int *month, int *day, int *hour, int *minute, int *second); -int univ_to_local(Sint *year, Sint *month, Sint *day, +int seconds_to_univ(Sint64 seconds, + Sint *year, Sint *month, Sint *day, + Sint *hour, Sint *minute, Sint *second); +int univ_to_seconds(Sint year, Sint month, Sint day, + Sint hour, Sint minute, Sint second, + Sint64* seconds); +int univ_to_local( + Sint *year, Sint *month, Sint *day, Sint *hour, Sint *minute, Sint *second); int local_to_univ(Sint *year, Sint *month, Sint *day, Sint *hour, Sint *minute, Sint *second, int isdst); diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 15b6c5a17f..b132991a3b 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -104,6 +104,7 @@ #ifndef WANT_NONBLOCKING #define WANT_NONBLOCKING #endif + #include "sys.h" #include "erl_driver.h" @@ -147,6 +148,22 @@ static ErlDrvSysInfo sys_info; #define MUTEX_UNLOCK(m) #endif + +/** + * On DARWIN sendfile can deadlock with close if called in + * different threads. So until Apple fixes so that sendfile + * is not buggy we disable usage of the async pool for + * DARWIN. The testcase t_sendfile_crashduring reproduces + * this error when using +A 10. + */ +#if !defined(DARWIN) +#define USE_THRDS_FOR_SENDFILE (sys_info.async_threads > 0) +#else +#define USE_THRDS_FOR_SENDFILE 0 +#endif /* !DARWIN */ + + + #if 0 /* Experimental, for forcing all file operations to use the same thread. */ static unsigned file_fixed_key = 1; @@ -735,6 +752,15 @@ file_stop(ErlDrvData e) TRACE_C('p'); +#ifdef HAVE_SENDFILE + if (desc->sendfile_state == sending && !USE_THRDS_FOR_SENDFILE) { + driver_select(desc->port,(ErlDrvEvent)(long)desc->d->c.sendfile.out_fd, + ERL_DRV_WRITE|ERL_DRV_USE,0); + } else if (desc->sendfile_state == sending) { + SET_NONBLOCKING(desc->d->c.sendfile.out_fd); + } +#endif /* HAVE_SENDFILE */ + if (desc->fd != FILE_FD_INVALID) { do_close(desc->flags, desc->fd); desc->fd = FILE_FD_INVALID; @@ -800,7 +826,16 @@ static void reply_Uint_posix_error(file_descriptor *desc, Uint num, driver_output2(desc->port, response, t-response, NULL, 0); } +static void reply_string_error(file_descriptor *desc, char* str) { + char response[256]; /* Response buffer. */ + char* s; + char* t; + response[0] = FILE_RESP_ERROR; + for (s = str, t = response+1; *s; s++, t++) + *t = tolower(*s); + driver_output2(desc->port, response, t-response, NULL, 0); +} static int reply_error(file_descriptor *desc, Efile_error *errInfo) /* The error codes. */ @@ -1745,7 +1780,7 @@ static void invoke_sendfile(void *data) d->c.sendfile.written += nbytes; if (result == 1) { - if (sys_info.async_threads != 0) { + if (USE_THRDS_FOR_SENDFILE) { d->result_ok = 0; } else if (d->c.sendfile.nbytes == 0 && nbytes != 0) { d->result_ok = 1; @@ -2121,24 +2156,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 @@ -2209,8 +2245,13 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) case FILE_SENDFILE: if (d->result_ok == -1) { desc->sendfile_state = not_sending; - reply_error(desc, &d->errInfo); - if (sys_info.async_threads != 0) { + if (d->errInfo.posix_errno == ECONNRESET || + d->errInfo.posix_errno == ENOTCONN || + d->errInfo.posix_errno == EPIPE) + reply_string_error(desc,"closed"); + else + reply_error(desc, &d->errInfo); + if (USE_THRDS_FOR_SENDFILE) { SET_NONBLOCKING(d->c.sendfile.out_fd); free_sendfile(data); } else { @@ -2221,7 +2262,7 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData 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) { + if (USE_THRDS_FOR_SENDFILE) { SET_NONBLOCKING(d->c.sendfile.out_fd); free_sendfile(data); } else { @@ -2486,15 +2527,16 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT 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; @@ -3426,7 +3468,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->c.sendfile.nbytes = nbytes; - if (sys_info.async_threads != 0) { + if (USE_THRDS_FOR_SENDFILE) { SET_BLOCKING(d->c.sendfile.out_fd); } diff --git a/erts/emulator/drivers/common/erl_efile.h b/erts/emulator/drivers/common/erl_efile.h index 349ab0e17b..be1faa13f5 100644 --- a/erts/emulator/drivers/common/erl_efile.h +++ b/erts/emulator/drivers/common/erl_efile.h @@ -67,6 +67,11 @@ #define FILENAMES_16BIT 1 #endif +// We use sendfilev if it exist on solaris +#if !defined(HAVE_SENDFILE) && defined(HAVE_SENDFILEV) +#define HAVE_SENDFILE +#endif + /* * An handle to an open directory. To be cast to the correct type * in the system-dependent directory functions. @@ -85,14 +90,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,9 +117,9 @@ 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; @@ -121,7 +127,7 @@ typedef struct _Efile_info { #ifdef HAVE_SENDFILE /* - * Described the structure of header/trailers for sendfile + * Describes the structure of headers/trailers for sendfile */ struct t_sendfile_hdtl { SysIOVec *headers; diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index cd580fcbe0..eeaa4d24ea 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -523,7 +523,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); \ + ASSERT(!(d)->is_ignored); \ (d)->event_mask = (onoff) ? \ ((d)->event_mask | (flags)) : \ ((d)->event_mask & ~(flags)); \ @@ -546,6 +546,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. ** @@ -950,9 +957,9 @@ 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. */ + int is_ignored; /* if a fd is ignored by the inet_drv. + This flag should be set to true when + the fd is used outside of inet_drv. */ } inet_descriptor; @@ -3836,7 +3843,13 @@ static void desc_close(inet_descriptor* desc) desc->forced_events = 0; desc->send_would_block = 0; #endif - driver_select(desc->port, (ErlDrvEvent)(long)desc->event, ERL_DRV_USE, 0); + // We should close the fd here, but the other driver might still + // be selecting on it. + if (!desc->is_ignored) + driver_select(desc->port,(ErlDrvEvent)(long)desc->event, + ERL_DRV_USE, 0); + else + inet_stop_select((ErlDrvEvent)(long)desc->event,NULL); desc->event = INVALID_EVENT; /* closed by stop_select callback */ desc->s = INVALID_SOCKET; desc->event_mask = 0; @@ -6224,7 +6237,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 @@ -7764,8 +7777,8 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, 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); + desc->is_ignored = INET_IGNORE_READ; } 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; @@ -8769,8 +8782,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; } @@ -10279,6 +10299,7 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT 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. diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index 72911641d3..9160e2aed2 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -816,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 @@ -883,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 - - -#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->accessTime = statbuf.st_atime; + pInfo->modifyTime = statbuf.st_mtime; + pInfo->cTime = statbuf.st_ctime; - pInfo->mode = statbuf.st_mode; - pInfo->links = statbuf.st_nlink; + pInfo->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; } @@ -924,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 @@ -976,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; } @@ -1469,6 +1427,9 @@ efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, } #ifdef HAVE_SENDFILE + +// For some reason the maximum size_t cannot be used as the max size +// 3GB seems to work on all platforms #define SENDFILE_CHUNK_SIZE ((1 << 30) -1) /* @@ -1477,7 +1438,13 @@ efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, * 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 + * All of the calls will split a command which tries to send more than + * SENDFILE_CHUNK_SIZE of data at once. + * + * On platforms where *nbytes of 0 does not mean the entire file, this is + * simulated. + * + * 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. @@ -1488,10 +1455,10 @@ 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)) +#if defined(__linux__) ssize_t retval; do { - // check if *nbytes is 0 or greater than the largest size_t + // check if *nbytes is 0 or greater than chunk size if (*nbytes == 0 || *nbytes > SENDFILE_CHUNK_SIZE) retval = sendfile(out_fd, in_fd, offset, SENDFILE_CHUNK_SIZE); else @@ -1503,11 +1470,34 @@ efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd, } while (retval != -1 && retval == SENDFILE_CHUNK_SIZE); *nbytes = written; return check_error(retval == -1 ? -1 : 0, errInfo); +#elif defined(__sun) && defined(__SVR4) && defined(HAVE_SENDFILEV) + ssize_t retval; + size_t len; + sendfilevec_t fdrec; + fdrec.sfv_fd = in_fd; + fdrec.sfv_flag = 0; + do { + fdrec.sfv_off = *offset; + len = 0; + // check if *nbytes is 0 or greater than chunk size + if (*nbytes == 0 || *nbytes > SENDFILE_CHUNK_SIZE) + fdrec.sfv_len = SENDFILE_CHUNK_SIZE; + else + fdrec.sfv_len = *nbytes; + retval = sendfilev(out_fd, &fdrec, 1, &len); + if (retval != -1 || errno == EAGAIN || errno == EINTR) { + *offset += len; + *nbytes -= len; + written += len; + } + } while (len == 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 + // check if *nbytes is 0 or greater than chunk size if(*nbytes > SENDFILE_CHUNK_SIZE) len = SENDFILE_CHUNK_SIZE; else diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index 0bc701c4cb..0d3d334154 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -45,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); @@ -864,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; @@ -905,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); } @@ -968,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; /* @@ -1003,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); } /* diff --git a/erts/emulator/hipe/hipe_mode_switch.h b/erts/emulator/hipe/hipe_mode_switch.h index dbc2386e14..a3e908a3b3 100644 --- a/erts/emulator/hipe/hipe_mode_switch.h +++ b/erts/emulator/hipe/hipe_mode_switch.h @@ -49,7 +49,7 @@ #include "error.h" -int hipe_modeswitch_debug; +extern int hipe_modeswitch_debug; void hipe_mode_switch_init(void); void hipe_set_call_trap(Uint *bfun, void *nfun, int is_closure); diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index cf3fb4446f..e8453205ea 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -128,6 +128,16 @@ int erts_check_io_debug(void); */ typedef __int64 erts_time_t; +struct tm *sys_localtime_r(time_t *epochs, struct tm *ptm); +struct tm *sys_gmtime_r(time_t *epochs, struct tm *ptm); +time_t sys_mktime( struct tm *ptm); + +#define localtime_r sys_localtime_r +#define HAVE_LOCALTIME_R 1 +#define gmtime_r sys_gmtime_r +#define HAVE_GMTIME_R +#define mktime sys_mktime + typedef struct { erts_time_t tv_sec; erts_time_t tv_usec; diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c index e5b9513edc..6362c1a06d 100644 --- a/erts/emulator/sys/win32/sys_time.c +++ b/erts/emulator/sys/win32/sys_time.c @@ -35,28 +35,336 @@ /******************* Routines for time measurement *********************/ #define EPOCH_JULIAN_DIFF LL_LITERAL(11644473600) +#define TICKS_PER_SECOND LL_LITERAL(10000000) +#define SECONDS_PER_DAY LL_LITERAL(86400) +#define ULI_TO_FILETIME(ft,ull) \ + do { \ + (ft).dwLowDateTime = (ull).LowPart; \ + (ft).dwHighDateTime = (ull).HighPart; \ + } while (0) + +#define FILETIME_TO_ULI(ull,ft) \ + do { \ + (ull).LowPart = (ft).dwLowDateTime; \ + (ull).HighPart = (ft).dwHighDateTime; \ + } while (0) + + +#define EPOCH_TO_FILETIME(ft, epoch) \ + do { \ + ULARGE_INTEGER ull; \ + ull.QuadPart = (((epoch) + EPOCH_JULIAN_DIFF) * TICKS_PER_SECOND); \ + ULI_TO_FILETIME(ft,ull); \ + } while(0) + +#define FILETIME_TO_EPOCH(epoch, ft) \ + do { \ + ULARGE_INTEGER ull; \ + FILETIME_TO_ULI(ull,ft); \ + (epoch) = ((ull.QuadPart / TICKS_PER_SECOND) - EPOCH_JULIAN_DIFF); \ + } while(0) + static SysHrTime wrap = 0; static DWORD last_tick_count = 0; +/* Getting timezone information is a heavy operation, so we want to do this + only once */ + +static TIME_ZONE_INFORMATION static_tzi; +static int have_static_tzi = 0; + +static int days_in_month[2][13] = { + {0,31,28,31,30,31,30,31,31,30,31,30,31}, + {0,31,29,31,30,31,30,31,31,30,31,30,31}}; + int sys_init_time(void) { + if(GetTimeZoneInformation(&static_tzi) && + static_tzi.StandardDate.wMonth != 0 && + static_tzi.DaylightDate.wMonth != 0) { + have_static_tzi = 1; + } return 1; } +/* Returns a switchtimes for DST as UTC filetimes given data from a + TIME_ZONE_INFORMATION, see sys_localtime_r for usage. */ +static void +get_dst_switchtime(DWORD year, + SYSTEMTIME dstinfo, LONG bias, + FILETIME *utc_switchtime) +{ + DWORD occu; + DWORD weekday,wday_1st; + DWORD day, days_in; + FILETIME tmp,tmp2; + ULARGE_INTEGER ull; + int leap_year = 0; + if (dstinfo.wYear != 0) { + /* A year specific transition, in which case the data in the structure + is already properly set for a specific year. Compare year + with parameter and see if they correspond, in that case generate a + filetime directly, otherwise set the filetime to 0 */ + if (year != dstinfo.wYear) { + utc_switchtime->dwLowDateTime = utc_switchtime->dwHighDateTime = 0; + return; + } + } else { + occu = dstinfo.wDay; + weekday = dstinfo.wDayOfWeek; + + dstinfo.wDayOfWeek = 0; + dstinfo.wDay = 1; + dstinfo.wYear = year; + + SystemTimeToFileTime(&dstinfo,&tmp); + ull.LowPart = tmp.dwLowDateTime; + ull.HighPart = tmp.dwHighDateTime; + + ull.QuadPart /= (TICKS_PER_SECOND*SECONDS_PER_DAY); /* Julian Day */ + wday_1st = (DWORD) ((ull.QuadPart + LL_LITERAL(1)) % LL_LITERAL(7)); + day = (weekday >= wday_1st) ? + weekday - wday_1st + 1 : + weekday - wday_1st + 8; + --occu; + if (((dstinfo.wYear % 4) == 0 && (dstinfo.wYear % 100) > 0) || + ((dstinfo.wYear % 400) == 0)) { + leap_year = 1; + } + days_in = days_in_month[leap_year][dstinfo.wMonth]; + while (occu > 0 && (day + 7 <= days_in)) { + --occu; + day += 7; + } + dstinfo.wDay = day; + } + SystemTimeToFileTime(&dstinfo,&tmp); + /* correct for bias */ + ull.LowPart = tmp.dwLowDateTime; + ull.HighPart = tmp.dwHighDateTime; + ull.QuadPart += (((LONGLONG) bias) * LL_LITERAL(60) * TICKS_PER_SECOND); + utc_switchtime->dwLowDateTime = ull.LowPart; + utc_switchtime->dwHighDateTime = ull.HighPart; + return; +} + +/* This function gives approximately the correct year from a FILETIME + Around the actual new year, it may return the wrong value, but that's OK + as DST never switches around new year. */ +static DWORD +approx_year(FILETIME ft) +{ + ULARGE_INTEGER ull; + FILETIME_TO_ULI(ull,ft); + ull.QuadPart /= LL_LITERAL(1000); + ull.QuadPart /= SECONDS_PER_DAY; + ull.QuadPart /= LL_LITERAL(3652425); + ull.QuadPart += 1601; + return (DWORD) ull.QuadPart; +} + +struct tm * +sys_localtime_r(time_t *epochs, struct tm *ptm) +{ + FILETIME ft,lft; + SYSTEMTIME st; + + if ((((*epochs) + EPOCH_JULIAN_DIFF) * TICKS_PER_SECOND) < 0LL) { + fprintf(stderr,"1\r\n"); fflush(stderr); + return NULL; + } + + EPOCH_TO_FILETIME(ft,*epochs); + ptm->tm_isdst = 0; + if (have_static_tzi) { + FILETIME dst_start, dst_stop; + ULARGE_INTEGER ull; + DWORD year = approx_year(ft); + get_dst_switchtime(year,static_tzi.DaylightDate, + static_tzi.Bias+static_tzi.StandardBias,&dst_start); + get_dst_switchtime(year,static_tzi.StandardDate, + static_tzi.Bias+static_tzi.StandardBias+ + static_tzi.DaylightBias, + &dst_stop); + FILETIME_TO_ULI(ull,ft); + + if (CompareFileTime(&ft,&dst_start) >= 0 && + CompareFileTime(&ft,&dst_stop) < 0) { + ull.QuadPart -= + ((LONGLONG) static_tzi.Bias+static_tzi.StandardBias+ + static_tzi.DaylightBias) * + LL_LITERAL(60) * TICKS_PER_SECOND; + ptm->tm_isdst = 1; + } else { + ull.QuadPart -= + ((LONGLONG) static_tzi.Bias+static_tzi.StandardBias) + * LL_LITERAL(60) * TICKS_PER_SECOND; + } + ULI_TO_FILETIME(ft,ull); + } else { + if (!FileTimeToLocalFileTime(&ft,&lft)) { + return NULL; + } + ft = lft; + } + + if (!FileTimeToSystemTime(&ft,&st)) { + return NULL; + } + + ptm->tm_year = (int) st.wYear - 1900; + ptm->tm_mon = (int) st.wMonth - 1; + ptm->tm_mday = (int) st.wDay; + ptm->tm_hour = (int) st.wHour; + ptm->tm_min = (int) st.wMinute; + ptm->tm_sec = (int) st.wSecond; + ptm->tm_wday = (int) st.wDayOfWeek; + { + int yday = ptm->tm_mday - 1; + int m = ptm->tm_mon; + int leap_year = 0; + if (((st.wYear % 4) == 0 && (st.wYear % 100) > 0) || + ((st.wYear % 400) == 0)) { + leap_year = 1; + } + while (m > 0) { + yday +=days_in_month[leap_year][m]; + --m; + } + ptm->tm_yday = yday; + } + return ptm; +} + +struct tm * +sys_gmtime_r(time_t *epochs, struct tm *ptm) +{ + FILETIME ft; + SYSTEMTIME st; + + if ((((*epochs) + EPOCH_JULIAN_DIFF) * TICKS_PER_SECOND) < 0LL) { + return NULL; + } + + EPOCH_TO_FILETIME(ft,*epochs); + + if (!FileTimeToSystemTime(&ft,&st)) { + return NULL; + } + + ptm->tm_year = (int) st.wYear - 1900; + ptm->tm_mon = (int) st.wMonth - 1; + ptm->tm_mday = (int) st.wDay; + ptm->tm_hour = (int) st.wHour; + ptm->tm_min = (int) st.wMinute; + ptm->tm_sec = (int) st.wSecond; + ptm->tm_wday = (int) st.wDayOfWeek; + ptm->tm_isdst = 0; + { + int yday = ptm->tm_mday - 1; + int m = ptm->tm_mon; + int leap_year = 0; + if (((st.wYear % 4) == 0 && (st.wYear % 100) > 0) || + ((st.wYear % 400) == 0)) { + leap_year = 1; + } + while (m > 0) { + yday +=days_in_month[leap_year][m]; + --m; + } + ptm->tm_yday = yday; + } + + return ptm; +} + +time_t +sys_mktime(struct tm *ptm) +{ + FILETIME ft; + SYSTEMTIME st; + int dst = 0; + time_t epochs; + + memset(&st,0,sizeof(st)); + /* Convert relevant parts of truct tm to SYSTEMTIME */ + st.wYear = (USHORT) (ptm->tm_year + 1900); + st.wMonth = (USHORT) (ptm->tm_mon + 1); + st.wDay = (USHORT) ptm->tm_mday; + st.wHour = (USHORT) ptm->tm_hour; + st.wMinute = (USHORT) ptm->tm_min; + st.wSecond = (USHORT) ptm->tm_sec; + + SystemTimeToFileTime(&st,&ft); + + /* ft is now some kind of local file time, but it may be wrong depending + on what is in the tm_dst field. We need to manually convert it to + UTC before turning it into epochs */ + + if (have_static_tzi) { + FILETIME dst_start, dst_stop; + ULARGE_INTEGER ull_start,ull_stop,ull_ft; + + FILETIME_TO_ULI(ull_ft,ft); + + /* Correct everything except DST */ + ull_ft.QuadPart += (static_tzi.Bias+static_tzi.StandardBias) + * LL_LITERAL(60) * TICKS_PER_SECOND; + + /* Determine if DST is active */ + if (ptm->tm_isdst >= 0) { + dst = ptm->tm_isdst; + } else if (static_tzi.DaylightDate.wMonth != 0){ + /* This is how windows mktime does it, meaning it does not + take nonexisting local times into account */ + get_dst_switchtime(st.wYear,static_tzi.DaylightDate, + static_tzi.Bias+static_tzi.StandardBias, + &dst_start); + get_dst_switchtime(st.wYear,static_tzi.StandardDate, + static_tzi.Bias+static_tzi.StandardBias+ + static_tzi.DaylightBias, + &dst_stop); + FILETIME_TO_ULI(ull_start,dst_start); + FILETIME_TO_ULI(ull_stop,dst_stop); + if ((ull_ft.QuadPart >= ull_start.QuadPart) && + (ull_ft.QuadPart < ull_stop.QuadPart)) { + /* We are in DST */ + dst = 1; + } + } + /* Correct for DST */ + if (dst) { + ull_ft.QuadPart += static_tzi.DaylightBias * + LL_LITERAL(60) * TICKS_PER_SECOND; + } + epochs = ((ull_ft.QuadPart / TICKS_PER_SECOND) - EPOCH_JULIAN_DIFF); + } else { + /* No DST, life is easy... */ + FILETIME lft; + LocalFileTimeToFileTime(&ft,&lft); + FILETIME_TO_EPOCH(epochs,lft); + } + /* Normalize the struct tm */ + sys_localtime_r(&epochs,ptm); + return epochs; +} + void sys_gettimeofday(SysTimeval *tv) { SYSTEMTIME t; FILETIME ft; - LONGLONG lft; + ULARGE_INTEGER ull; GetSystemTime(&t); SystemTimeToFileTime(&t, &ft); - memcpy(&lft, &ft, sizeof(lft)); - tv->tv_usec = (erts_time_t) ((lft / LL_LITERAL(10)) % LL_LITERAL(1000000)); - tv->tv_sec = (erts_time_t) ((lft / LL_LITERAL(10000000)) - EPOCH_JULIAN_DIFF); + FILETIME_TO_ULI(ull,ft); + tv->tv_usec = (long) ((ull.QuadPart / LL_LITERAL(10)) % + LL_LITERAL(1000000)); + tv->tv_sec = (long) ((ull.QuadPart / LL_LITERAL(10000000)) - + EPOCH_JULIAN_DIFF); } SysHrTime @@ -91,9 +399,3 @@ sys_times(SysTimes *buffer) { buffer->tms_stime = (clock_t) (system & LL_LITERAL(0x7FFFFFFF)); return kernel_ticks; } - - - - - - diff --git a/erts/emulator/test/decode_packet_SUITE.erl b/erts/emulator/test/decode_packet_SUITE.erl index c0499554eb..4acbe8c6e0 100644 --- a/erts/emulator/test/decode_packet_SUITE.erl +++ b/erts/emulator/test/decode_packet_SUITE.erl @@ -26,12 +26,14 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, - basic/1, packet_size/1, neg/1, http/1, line/1, ssl/1, otp_8536/1]). + basic/1, packet_size/1, neg/1, http/1, line/1, ssl/1, otp_8536/1, + otp_9389/1, otp_9389_line/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [basic, packet_size, neg, http, line, ssl, otp_8536]. + [basic, packet_size, neg, http, line, ssl, otp_8536, + otp_9389, otp_9389_line]. groups() -> []. @@ -251,6 +253,28 @@ packet_size(Config) when is_list(Config) -> ?line {error,_} = decode_pkt(4,<<Size:32,Packet/binary>>) end, lists:seq(-10,-1)), + + %% Test OTP-9389, long HTTP header lines. + Opts = [{packet_size, 128}], + Pkt = list_to_binary(["GET / HTTP/1.1\r\nHost: localhost\r\nLink: /", + string:chars($Y, 64), "\r\n\r\n"]), + <<Pkt1:50/binary, Pkt2/binary>> = Pkt, + ?line {ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest1} = + erlang:decode_packet(http, Pkt1, Opts), + ?line {ok, {http_header,_,'Host',_,"localhost"}, Rest2} = + erlang:decode_packet(httph, Rest1, Opts), + ?line {more, undefined} = erlang:decode_packet(httph, Rest2, Opts), + ?line {ok, {http_header,_,"Link",_,_}, _} = + erlang:decode_packet(httph, list_to_binary([Rest2, Pkt2]), Opts), + + Pkt3 = list_to_binary(["GET / HTTP/1.1\r\nHost: localhost\r\nLink: /", + string:chars($Y, 129), "\r\n\r\n"]), + ?line {ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest3} = + erlang:decode_packet(http, Pkt3, Opts), + ?line {ok, {http_header,_,'Host',_,"localhost"}, Rest4} = + erlang:decode_packet(httph, Rest3, Opts), + ?line {error, invalid} = erlang:decode_packet(httph, Rest4, Opts), + ok. @@ -557,3 +581,35 @@ decode_pkt(Type,Bin,Opts) -> %%io:format(" -> ~p\n",[Res]), Res. +otp_9389(doc) -> ["Verify line_length works correctly for HTTP headers"]; +otp_9389(suite) -> []; +otp_9389(Config) when is_list(Config) -> + Opts = [{packet_size, 16384}, {line_length, 3000}], + Pkt = list_to_binary(["GET / HTTP/1.1\r\nHost: localhost\r\nLink: /", + string:chars($X, 8192), + "\r\nContent-Length: 0\r\n\r\n"]), + <<Pkt1:5000/binary, Pkt2/binary>> = Pkt, + {ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest1} = + erlang:decode_packet(http, Pkt1, Opts), + {ok, {http_header,_,'Host',_,"localhost"}, Rest2} = + erlang:decode_packet(httph, Rest1, Opts), + {more, undefined} = erlang:decode_packet(httph, Rest2, Opts), + {ok, {http_header,_,"Link",_,Link}, Rest3} = + erlang:decode_packet(httph, list_to_binary([Rest2, Pkt2]), Opts), + true = (length(Link) > 8000), + {ok, {http_header,_,'Content-Length',_,"0"}, <<"\r\n">>} = + erlang:decode_packet(httph, Rest3, Opts), + ok. + +otp_9389_line(doc) -> ["Verify packet_size works correctly for line mode"]; +otp_9389_line(suite) -> []; +otp_9389_line(Config) when is_list(Config) -> + Opts = [{packet_size, 20}], + Line1 = <<"0123456789012345678\n">>, + Line2 = <<"0123456789\n">>, + Line3 = <<"01234567890123456789\n">>, + Pkt = list_to_binary([Line1, Line2, Line3]), + ?line {ok, Line1, Rest1} = erlang:decode_packet(line, Pkt, Opts), + ?line {ok, Line2, Rest2} = erlang:decode_packet(line, Rest1, Opts), + ?line {error, invalid} = erlang:decode_packet(line, Rest2, Opts), + ok. diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index e7ee04f40e..643357263c 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -2185,6 +2185,14 @@ wait_deallocations() -> end. driver_alloc_size() -> + case erlang:system_info(smp_support) of + true -> + ok; + false -> + %% driver_alloc also used by elements in lock-free queues, + %% give these some time to be deallocated... + receive after 100 -> ok end + end, wait_deallocations(), case erlang:system_info({allocator_sizes, driver_alloc}) of false -> diff --git a/erts/emulator/test/driver_SUITE_data/monitor_drv.c b/erts/emulator/test/driver_SUITE_data/monitor_drv.c index e714587354..3da067fd09 100644 --- a/erts/emulator/test/driver_SUITE_data/monitor_drv.c +++ b/erts/emulator/test/driver_SUITE_data/monitor_drv.c @@ -21,6 +21,7 @@ #include "erl_driver.h" static ErlDrvData monitor_drv_start(ErlDrvPort, char *); +static void monitor_drv_stop(ErlDrvData data); static ErlDrvSSizeT monitor_drv_control(ErlDrvData, unsigned int, char *, ErlDrvSizeT, char **, ErlDrvSizeT); static void handle_monitor(ErlDrvData drv_data, ErlDrvMonitor *monitor); @@ -50,7 +51,7 @@ typedef struct { static ErlDrvEntry monitor_drv_entry = { NULL /* init */, monitor_drv_start, - NULL /* stop */, + monitor_drv_stop, NULL /* output */, NULL /* ready_input */, NULL /* ready_output */, diff --git a/erts/emulator/test/driver_SUITE_data/thr_free_drv.c b/erts/emulator/test/driver_SUITE_data/thr_free_drv.c index c15059bcf4..439fe6a184 100644 --- a/erts/emulator/test/driver_SUITE_data/thr_free_drv.c +++ b/erts/emulator/test/driver_SUITE_data/thr_free_drv.c @@ -175,7 +175,7 @@ fail: driver_free(ttd[t].blocks[b]); } } - + driver_free(td); return ERL_DRV_ERROR_GENERAL; } diff --git a/erts/emulator/test/driver_SUITE_data/timer_drv.c b/erts/emulator/test/driver_SUITE_data/timer_drv.c index 70af662e5f..8c3f203a64 100644 --- a/erts/emulator/test/driver_SUITE_data/timer_drv.c +++ b/erts/emulator/test/driver_SUITE_data/timer_drv.c @@ -22,9 +22,9 @@ static ErlDrvPort erlang_port; static ErlDrvData timer_start(ErlDrvPort, char*); -static void timer_stop(ErlDrvData), - timer_read(ErlDrvData, char*, ErlDrvSizeT), - timer(ErlDrvData); +static void timer_stop(ErlDrvData); +static void timer_read(ErlDrvData, char*, ErlDrvSizeT); +static void timer(ErlDrvData); static ErlDrvEntry timer_driver_entry = { diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index bd48a0a7db..4d12e3449c 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -32,6 +32,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, univ_to_local/1, local_to_univ/1, bad_univ_to_local/1, bad_local_to_univ/1, + univ_to_seconds/1, seconds_to_univ/1, consistency/1, now_unique/1, now_update/1, timestamp/1]). @@ -59,7 +60,9 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [univ_to_local, local_to_univ, local_to_univ_utc, - bad_univ_to_local, bad_local_to_univ, consistency, + bad_univ_to_local, bad_local_to_univ, + univ_to_seconds, seconds_to_univ, + consistency, {group, now}, timestamp]. groups() -> @@ -162,6 +165,30 @@ bad_test_local_to_univ([Local|Rest]) -> bad_test_local_to_univ([]) -> ok. + +%% Test universaltime to seconds conversions +univ_to_seconds(Config) when is_list(Config) -> + test_univ_to_seconds(ok_utc_seconds()). + +test_univ_to_seconds([{Datetime, Seconds}|DSs]) -> + io:format("universaltime = ~p -> seconds = ~p", [Datetime, Seconds]), + Seconds = erlang:universaltime_to_posixtime(Datetime), + test_univ_to_seconds(DSs); +test_univ_to_seconds([]) -> + ok. + +%% Test seconds to universaltime conversions +seconds_to_univ(Config) when is_list(Config) -> + test_seconds_to_univ(ok_utc_seconds()). + +test_seconds_to_univ([{Datetime, Seconds}|DSs]) -> + io:format("universaltime = ~p <- seconds = ~p", [Datetime, Seconds]), + Datetime = erlang:posixtime_to_universaltime(Seconds), + test_seconds_to_univ(DSs); +test_seconds_to_univ([]) -> + ok. + + %% Test that the the different time functions return %% consistent results. (See the test case for assumptions %% and limitations.) @@ -453,6 +480,32 @@ dst_dates() -> {1998, 06, 3}, {1999, 06, 4}]. +%% exakt utc {date(), time()} which corresponds to the same seconds since 1 jan 1970 +%% negative seconds are ok +%% generated with date --date='1979-05-28 12:30:35 UTC' +%s +ok_utc_seconds() -> [ + { {{1970, 1, 1},{ 0, 0, 0}}, 0 }, + { {{1970, 1, 1},{ 0, 0, 1}}, 1 }, + { {{1969,12,31},{23,59,59}}, -1 }, + { {{1920,12,31},{23,59,59}}, -1546300801 }, + { {{1600,02,19},{15,14,08}}, -11671807552 }, + { {{1979,05,28},{12,30,35}}, 296742635 }, + { {{1999,12,31},{23,59,59}}, 946684799 }, + { {{2000, 1, 1},{ 0, 0, 0}}, 946684800 }, + { {{2000, 1, 1},{ 0, 0, 1}}, 946684801 }, + + { {{2038, 1,19},{03,14,07}}, 2147483647 }, % Sint32 full - 1 + { {{2038, 1,19},{03,14,08}}, 2147483648 }, % Sint32 full + { {{2038, 1,19},{03,14,09}}, 2147483649 }, % Sint32 full + 1 + + { {{2106, 2, 7},{ 6,28,14}}, 4294967294 }, % Uint32 full 0xFFFFFFFF - 1 + { {{2106, 2, 7},{ 6,28,15}}, 4294967295 }, % Uint32 full 0xFFFFFFFF + { {{2106, 2, 7},{ 6,28,16}}, 4294967296 }, % Uint32 full 0xFFFFFFFF + 1 + { {{2012,12, 6},{16,28,08}}, 1354811288 }, + { {{2412,12, 6},{16,28,08}}, 13977592088 } + ]. + + %% The following dates should not be near the end or beginning of %% a month, because they will be used to test when the dates are %% different in UTC and local time. diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam Binary files differindex ad1d7031a3..bc5631f3dd 100644 --- a/erts/preloaded/ebin/prim_file.beam +++ b/erts/preloaded/ebin/prim_file.beam diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam Binary files differindex 7e1a5d1fdb..b6c49d5c0c 100644 --- a/erts/preloaded/ebin/prim_zip.beam +++ b/erts/preloaded/ebin/prim_zip.beam diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 7316e0be99..36cbe329e8 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -45,13 +45,13 @@ rename/2, rename/3, make_dir/1, make_dir/2, del_dir/1, del_dir/2, - read_file_info/1, read_file_info/2, + read_file_info/1, read_file_info/2, read_file_info/3, altname/1, altname/2, - write_file_info/2, write_file_info/3, + write_file_info/2, write_file_info/3, write_file_info/4, make_link/2, make_link/3, make_symlink/2, make_symlink/3, read_link/1, read_link/2, - read_link_info/1, read_link_info/2, + read_link_info/1, read_link_info/2, read_link_info/3, list_dir/1, list_dir/2]). %% How to start and stop the ?DRV port. -export([start/0, stop/1]). @@ -725,16 +725,33 @@ del_dir_int(Port, Dir) -> -%% read_file_info/{1,2} +%% read_file_info/{1,2,3} read_file_info(File) -> - read_file_info_int({?DRV, [binary]}, File). + read_file_info_int({?DRV, [binary]}, File, local). read_file_info(Port, File) when is_port(Port) -> - read_file_info_int(Port, File). + read_file_info_int(Port, File, local); +read_file_info(File, Opts) -> + read_file_info_int({?DRV, [binary]}, File, plgv(time, Opts, local)). + +read_file_info(Port, File, Opts) when is_port(Port) -> + read_file_info_int(Port, File, plgv(time, Opts, local)). + +read_file_info_int(Port, File, TimeType) -> + try + case drv_command(Port, [?FILE_FSTAT, pathname(File)]) of + {ok, FI} -> {ok, FI#file_info{ + ctime = from_seconds(FI#file_info.ctime, TimeType), + mtime = from_seconds(FI#file_info.mtime, TimeType), + atime = from_seconds(FI#file_info.atime, TimeType) + }}; + Error -> Error + end + catch + error:_ -> {error, badarg} + end. -read_file_info_int(Port, File) -> - drv_command(Port, [?FILE_FSTAT, pathname(File)]). %% altname/{1,2} @@ -747,38 +764,61 @@ altname(Port, File) when is_port(Port) -> altname_int(Port, File) -> drv_command(Port, [?FILE_ALTNAME, pathname(File)]). -%% write_file_info/{2,3} +%% write_file_info/{2,3,4} write_file_info(File, Info) -> - write_file_info_int({?DRV, [binary]}, File, Info). + write_file_info_int({?DRV, [binary]}, File, Info, local). write_file_info(Port, File, Info) when is_port(Port) -> - write_file_info_int(Port, File, Info). + write_file_info_int(Port, File, Info, local); +write_file_info(File, Info, Opts) -> + write_file_info_int({?DRV, [binary]}, File, Info, plgv(time, Opts, local)). + +write_file_info(Port, File, Info, Opts) when is_port(Port) -> + write_file_info_int(Port, File, Info, plgv(time, Opts, local)). -write_file_info_int(Port, - File, +write_file_info_int(Port, File, #file_info{mode=Mode, uid=Uid, gid=Gid, atime=Atime0, mtime=Mtime0, - ctime=Ctime}) -> - {Atime, Mtime} = - case {Atime0, Mtime0} of - {undefined, Mtime0} -> {erlang:localtime(), Mtime0}; - {Atime0, undefined} -> {Atime0, Atime0}; - Complete -> Complete - end, - drv_command(Port, [?FILE_WRITE_INFO, - int_to_bytes(Mode), - int_to_bytes(Uid), - int_to_bytes(Gid), - date_to_bytes(Atime), - date_to_bytes(Mtime), - date_to_bytes(Ctime), - pathname(File)]). + ctime=Ctime0}, + TimeType) -> + + % Atime and/or Mtime might be undefined + % - use localtime() for atime, if atime is undefined + % - use atime as mtime if mtime is undefined + % - use mtime as ctime if ctime is undefined + + try + Atime = file_info_validate_atime(Atime0, TimeType), + Mtime = file_info_validate_mtime(Mtime0, Atime), + Ctime = file_info_validate_ctime(Ctime0, Mtime), + + drv_command(Port, [?FILE_WRITE_INFO, + int_to_int32bytes(Mode), + int_to_int32bytes(Uid), + int_to_int32bytes(Gid), + int_to_int64bytes(to_seconds(Atime, TimeType)), + int_to_int64bytes(to_seconds(Mtime, TimeType)), + int_to_int64bytes(to_seconds(Ctime, TimeType)), + pathname(File)]) + catch + error:_ -> {error, badarg} + end. +file_info_validate_atime(Atime, _) when Atime =/= undefined -> Atime; +file_info_validate_atime(undefined, local) -> erlang:localtime(); +file_info_validate_atime(undefined, universal) -> erlang:universaltime(); +file_info_validate_atime(undefined, posix) -> erlang:universaltime_to_posixtime(erlang:universaltime()). + +file_info_validate_mtime(undefined, Atime) -> Atime; +file_info_validate_mtime(Mtime, _) -> Mtime. + +file_info_validate_ctime(undefined, Mtime) -> Mtime; +file_info_validate_ctime(Ctime, _) -> Ctime. %% make_link/{2,3} @@ -822,16 +862,32 @@ read_link_int(Port, Link) -> %% read_link_info/{2,3} read_link_info(Link) -> - read_link_info_int({?DRV, [binary]}, Link). + read_link_info_int({?DRV, [binary]}, Link, local). read_link_info(Port, Link) when is_port(Port) -> - read_link_info_int(Port, Link). + read_link_info_int(Port, Link, local); -read_link_info_int(Port, Link) -> - drv_command(Port, [?FILE_LSTAT, pathname(Link)]). +read_link_info(Link, Opts) -> + read_link_info_int({?DRV, [binary]}, Link, plgv(time, Opts, local)). +read_link_info(Port, Link, Opts) when is_port(Port) -> + read_link_info_int(Port, Link, plgv(time, Opts, local)). +read_link_info_int(Port, Link, TimeType) -> + try + case drv_command(Port, [?FILE_LSTAT, pathname(Link)]) of + {ok, FI} -> {ok, FI#file_info{ + ctime = from_seconds(FI#file_info.ctime, TimeType), + mtime = from_seconds(FI#file_info.mtime, TimeType), + atime = from_seconds(FI#file_info.atime, TimeType) + }}; + Error -> Error + end + catch + error:_ -> {error, badarg} + end. + %% list_dir/{1,2} list_dir(Dir) -> @@ -1075,7 +1131,7 @@ translate_response(?FILE_RESP_DATA, List) -> {_N, _Data} = ND = get_uint64(List), {ok, ND}; translate_response(?FILE_RESP_INFO, List) when is_list(List) -> - {ok, transform_info_ints(get_uint32s(List))}; + {ok, transform_info(List)}; translate_response(?FILE_RESP_NUMERR, L0) -> {N, L1} = get_uint64(L0), {error, {N, list_to_atom(L1)}}; @@ -1129,27 +1185,37 @@ translate_response(?FILE_RESP_ALL_DATA, Data) -> translate_response(X, Data) -> {error, {bad_response_from_port, [X | Data]}}. -transform_info_ints(Ints) -> - [HighSize, LowSize, Type|Tail0] = Ints, - Size = HighSize * 16#100000000 + LowSize, - [Ay, Am, Ad, Ah, Ami, As|Tail1] = Tail0, - [My, Mm, Md, Mh, Mmi, Ms|Tail2] = Tail1, - [Cy, Cm, Cd, Ch, Cmi, Cs|Tail3] = Tail2, - [Mode, Links, Major, Minor, Inode, Uid, Gid, Access] = Tail3, +transform_info([ + Hsize1, Hsize2, Hsize3, Hsize4, + Lsize1, Lsize2, Lsize3, Lsize4, + Type1, Type2, Type3, Type4, + Atime1, Atime2, Atime3, Atime4, Atime5, Atime6, Atime7, Atime8, + Mtime1, Mtime2, Mtime3, Mtime4, Mtime5, Mtime6, Mtime7, Mtime8, + Ctime1, Ctime2, Ctime3, Ctime4, Ctime5, Ctime6, Ctime7, Ctime8, + Mode1, Mode2, Mode3, Mode4, + Links1, Links2, Links3, Links4, + Major1, Major2, Major3, Major4, + Minor1, Minor2, Minor3, Minor4, + Inode1, Inode2, Inode3, Inode4, + Uid1, Uid2, Uid3, Uid4, + Gid1, Gid2, Gid3, Gid4, + Access1,Access2,Access3,Access4]) -> #file_info { - size = Size, - type = file_type(Type), - access = file_access(Access), - atime = {{Ay, Am, Ad}, {Ah, Ami, As}}, - mtime = {{My, Mm, Md}, {Mh, Mmi, Ms}}, - ctime = {{Cy, Cm, Cd}, {Ch, Cmi, Cs}}, - mode = Mode, - links = Links, - major_device = Major, - minor_device = Minor, - inode = Inode, - uid = Uid, - gid = Gid}. + size = uint32(Hsize1,Hsize2,Hsize3,Hsize4)*16#100000000 + uint32(Lsize1,Lsize2,Lsize3,Lsize4), + type = file_type(uint32(Type1,Type2,Type3,Type4)), + access = file_access(uint32(Access1,Access2,Access3,Access4)), + atime = sint64(Atime1, Atime2, Atime3, Atime4, Atime5, Atime6, Atime7, Atime8), + mtime = sint64(Mtime1, Mtime2, Mtime3, Mtime4, Mtime5, Mtime6, Mtime7, Mtime8), + ctime = sint64(Ctime1, Ctime2, Ctime3, Ctime4, Ctime5, Ctime6, Ctime7, Ctime8), + mode = uint32(Mode1,Mode2,Mode3,Mode4), + links = uint32(Links1,Links2,Links3,Links4), + major_device = uint32(Major1,Major2,Major3,Major4), + minor_device = uint32(Minor1,Minor2,Minor3,Minor4), + inode = uint32(Inode1,Inode2,Inode3,Inode4), + uid = uint32(Uid1,Uid2,Uid3,Uid4), + gid = uint32(Gid1,Gid2,Gid3,Gid4) + }. + file_type(1) -> device; file_type(2) -> directory; @@ -1162,24 +1228,22 @@ file_access(1) -> write; file_access(2) -> read; file_access(3) -> read_write. -int_to_bytes(Int) when is_integer(Int) -> +int_to_int32bytes(Int) when is_integer(Int) -> <<Int:32>>; -int_to_bytes(undefined) -> +int_to_int32bytes(undefined) -> <<-1:32>>. -date_to_bytes(undefined) -> - <<-1:32, -1:32, -1:32, -1:32, -1:32, -1:32>>; -date_to_bytes({{Y, Mon, D}, {H, Min, S}}) -> - <<Y:32, Mon:32, D:32, H:32, Min:32, S:32>>. +int_to_int64bytes(Int) when is_integer(Int) -> + <<Int:64/signed>>. -%% uint64([[X1, X2, X3, X4] = Y1 | [X5, X6, X7, X8] = Y2]) -> -%% (uint32(Y1) bsl 32) bor uint32(Y2). -%% uint64(X1, X2, X3, X4, X5, X6, X7, X8) -> -%% (uint32(X1, X2, X3, X4) bsl 32) bor uint32(X5, X6, X7, X8). +sint64(I1,I2,I3,I4,I5,I6,I7,I8) when I1 > 127 -> + ((I1 bsl 56) bor (I2 bsl 48) bor (I3 bsl 40) bor (I4 bsl 32) bor + (I5 bsl 24) bor (I6 bsl 16) bor (I7 bsl 8) bor I8) - (1 bsl 64); +sint64(I1,I2,I3,I4,I5,I6,I7,I8) -> + ((I1 bsl 56) bor (I2 bsl 48) bor (I3 bsl 40) bor (I4 bsl 32) bor + (I5 bsl 24) bor (I6 bsl 16) bor (I7 bsl 8) bor I8). -%% uint32([X1,X2,X3,X4]) -> -%% (X1 bsl 24) bor (X2 bsl 16) bor (X3 bsl 8) bor X4. uint32(X1,X2,X3,X4) -> (X1 bsl 24) bor (X2 bsl 16) bor (X3 bsl 8) bor X4. @@ -1192,11 +1256,6 @@ get_uint64(L0) -> get_uint32([X1,X2,X3,X4|List]) -> {(((((X1 bsl 8) bor X2) bsl 8) bor X3) bsl 8) bor X4, List}. -get_uint32s([X1,X2,X3,X4|Tail]) -> - [uint32(X1,X2,X3,X4) | get_uint32s(Tail)]; -get_uint32s([]) -> []. - - %% Binary mode transform_ldata(<<0:32, 0:32>>) -> @@ -1275,3 +1334,28 @@ reverse(L, T) -> lists:reverse(L, T). % in list_to_binary, which is caught and generates the {error,badarg} return pathname(File) -> (catch prim_file:internal_name2native(File)). + + +%% proplist:get_value/3 +plgv(K, [{K, V}|_], _) -> V; +plgv(K, [_|KVs], D) -> plgv(K, KVs, D); +plgv(_, [], D) -> D. + +%% +%% We don't actually want this here +%% We want to use posix time in all prim but erl_prim_loader makes that tricky +%% It is probably needed to redo the whole erl_prim_loader + +from_seconds(Seconds, posix) when is_integer(Seconds) -> + Seconds; +from_seconds(Seconds, universal) when is_integer(Seconds) -> + erlang:posixtime_to_universaltime(Seconds); +from_seconds(Seconds, local) when is_integer(Seconds) -> + erlang:universaltime_to_localtime(erlang:posixtime_to_universaltime(Seconds)). + +to_seconds(Seconds, posix) when is_integer(Seconds) -> + Seconds; +to_seconds({_,_} = Datetime, universal) -> + erlang:universaltime_to_posixtime(Datetime); +to_seconds({_,_} = Datetime, local) -> + erlang:universaltime_to_posixtime(erlang:localtime_to_universaltime(Datetime)). diff --git a/erts/preloaded/src/prim_zip.erl b/erts/preloaded/src/prim_zip.erl index 392a9feb45..d29f17ae56 100644 --- a/erts/preloaded/src/prim_zip.erl +++ b/erts/preloaded/src/prim_zip.erl @@ -432,7 +432,7 @@ binary_io({file_info, B}, _) -> is_binary(B) -> {regular, byte_size(B)}; B =:= directory -> {directory, 0} end, - Now = calendar:local_time(), + Now = erlang:localtime(), #file_info{size = Size, type = Type, access = read_write, atime = Now, mtime = Now, ctime = Now, mode = 0, links = 1, major_device = 0, diff --git a/erts/test/erl_print_SUITE_data/Makefile.src b/erts/test/erl_print_SUITE_data/Makefile.src index 109d55e572..9164c672dc 100644 --- a/erts/test/erl_print_SUITE_data/Makefile.src +++ b/erts/test/erl_print_SUITE_data/Makefile.src @@ -27,7 +27,7 @@ EPTF_CFLAGS = -Wall $(CFLAGS) @DEFS@ -I@erts_lib_include_internal@ -I@erts_lib_i EPTF_LIBS = $(LIBS) -L@erts_lib_internal_path@ -lerts_internal@type_marker@ EPTT_CFLAGS = -DTHREAD_SAFE $(ETHR_DEFS) $(EPTF_CFLAGS) -EPTT_LIBS = $(LIBS) -L@erts_lib_internal_path@ -lerts_internal_r@type_marker@ $(ETHR_LIBS) +EPTT_LIBS = -L@erts_lib_internal_path@ -lerts_internal_r@type_marker@ $(ETHR_LIBS) $(LIBS) GCC = .@DS@gccifier -CC"$(CC)" |