aboutsummaryrefslogtreecommitdiffstats
path: root/erts
diff options
context:
space:
mode:
Diffstat (limited to 'erts')
-rw-r--r--erts/doc/src/notes.xml125
-rw-r--r--erts/emulator/drivers/common/inet_drv.c113
-rw-r--r--erts/emulator/nifs/common/prim_file_nif.c3
-rw-r--r--erts/emulator/nifs/win32/win_prim_file.c157
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin79028 -> 80864 bytes
-rw-r--r--erts/preloaded/src/prim_inet.erl154
-rw-r--r--erts/vsn.mk2
7 files changed, 447 insertions, 107 deletions
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index fb3bd18a53..102de90c56 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -31,6 +31,34 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 10.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A bug where the socket option 'pktoptions' caused a read
+ of uninitialized memory has been fixed. Would cause
+ malfunction on FreeBSD.</p>
+ <p>
+ Own Id: OTP-14297 Aux Id: OTP-15141 </p>
+ </item>
+ <item>
+ <p>Fixed a memory leak on errors when reading files.</p>
+ <p>
+ Own Id: OTP-15318</p>
+ </item>
+ <item>
+ <p>File access through UNC paths works again on Windows.
+ This regression was introduced in OTP 21.</p>
+ <p>
+ Own Id: OTP-15333 Aux Id: ERL-737 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 10.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -6221,6 +6249,103 @@
</section>
+<section><title>Erts 6.4.1.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A process communicating with a port via one of the
+ <c>erlang:port_*</c> BIFs could potentially end up in an
+ inconsistent state if the port terminated during the
+ communication. When this occurred the process could later
+ block in a <c>receive</c> even though it had messages
+ matching in its message queue.</p>
+ <p>
+ This bug was introduced in erts version 5.10 (OTP R16A).</p>
+ <p>
+ Own Id: OTP-13424 Aux Id: OTP-10336 </p>
+ </item>
+ <item>
+ <p>
+ Calls to <c>erl_drv_send_term()</c> or
+ <c>erl_drv_output_term()</c> from a non-scheduler thread
+ while the corresponding port was invalid caused the
+ emulator to enter an inconsistent state which eventually
+ caused an emulator crash.</p>
+ <p>
+ Own Id: OTP-13866</p>
+ </item>
+ <item>
+ <p>Driver and NIF operations accessing processes or ports
+ could cause an emulator crash when used from
+ non-scheduler threads. Those operations are:</p> <list>
+ <item><c>erl_drv_send_term()</c></item>
+ <item><c>driver_send_term()</c></item>
+ <item><c>erl_drv_output_term()</c></item>
+ <item><c>driver_output_term()</c></item>
+ <item><c>enif_send()</c></item>
+ <item><c>enif_port_command()</c></item> </list>
+ <p>
+ Own Id: OTP-13869</p>
+ </item>
+ <item>
+ <p>
+ Fix bug in <c>binary_to_term</c> for binaries created by
+ <c>term_to_binary </c> with option <c>compressed</c>. The
+ bug can cause <c>badarg</c> exception for a valid binary
+ when Erlang VM is linked against a <c>zlib</c> library of
+ version 1.2.9 or newer. Bug exists since OTP 17.0.</p>
+ <p>
+ Own Id: OTP-14159 Aux Id: ERL-340 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in operator <c>bxor</c> causing erroneuos
+ result when one operand is a big <em>negative</em>
+ integer with the lowest <c>N*W</c> bits as zero and the
+ other operand not larger than <c>N*W</c> bits. <c>N</c>
+ is an integer of 1 or larger and <c>W</c> is 32 or 64
+ depending on word size.</p>
+ <p>
+ Own Id: OTP-14514</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in <c>binary_to_term</c> and
+ <c>binary_to_atom</c> that could cause VM crash.
+ Typically happens when the last character of an UTF8
+ string is in the range 128 to 255, but truncated to only
+ one byte. Bug exists in <c>binary_to_term</c> since ERTS
+ version 5.10.2 (OTP_R16B01) and <c>binary_to_atom</c>
+ since ERTS version 9.0 (OTP-20.0).</p>
+ <p>
+ Own Id: OTP-14590 Aux Id: ERL-474 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 6.4.1.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ When calling <c>garbage_collect/[1,2]</c> or
+ <c>check_process_code/[2,3]</c> from a process with a
+ higher priority than the priority of the process operated
+ on, the run queues could end up in an inconsistent state.
+ This bug has now been fixed.</p>
+ <p>
+ Own Id: OTP-13298 Aux Id: OTP-11388 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 6.4.1.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 7f20477363..3195ca3874 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -812,6 +812,7 @@ static size_t my_strnlen(const char *s, size_t maxlen)
#define INET_OPT_PKTOPTIONS 45 /* IP(V6)_PKTOPTIONS get ancillary data */
#define INET_OPT_TTL 46 /* IP_TTL */
#define INET_OPT_RECVTTL 47 /* IP_RECVTTL ancillary data */
+#define TCP_OPT_NOPUSH 48 /* super-Nagle, aka TCP_CORK */
/* SCTP options: a separate range, from 100: */
#define SCTP_OPT_RTOINFO 100
#define SCTP_OPT_ASSOCINFO 101
@@ -955,6 +956,12 @@ static size_t my_strnlen(const char *s, size_t maxlen)
#endif
+#if defined(TCP_CORK)
+#define INET_TCP_NOPUSH TCP_CORK
+#elif defined(TCP_NOPUSH) && !defined(__DARWIN__)
+#define INET_TCP_NOPUSH TCP_NOPUSH
+#endif
+
#define BIN_REALLOC_MARGIN(x) ((x)/4) /* 25% */
/* The general purpose sockaddr */
@@ -5178,6 +5185,71 @@ static int hwaddr_libdlpi_lookup(const char *ifnm,
}
#endif
+#ifdef HAVE_GETIFADDRS
+/* Returns 0 for success and errno() for failure */
+static int call_getifaddrs(inet_descriptor* desc_p, struct ifaddrs **ifa_pp)
+{
+ int result, save_errno;
+#ifdef HAVE_SETNS
+ int current_ns;
+
+ current_ns = 0;
+ if (desc_p->netns != NULL) {
+ int new_ns;
+ /* Temporarily change network namespace for this thread
+ * over the getifaddrs() call
+ */
+ current_ns = open("/proc/self/ns/net", O_RDONLY);
+ if (current_ns == INVALID_SOCKET)
+ return sock_errno();
+ new_ns = open(desc_p->netns, O_RDONLY);
+ if (new_ns == INVALID_SOCKET) {
+ save_errno = sock_errno();
+ while (close(current_ns) == INVALID_SOCKET &&
+ sock_errno() == EINTR);
+ return save_errno;
+ }
+ if (setns(new_ns, CLONE_NEWNET) != 0) {
+ save_errno = sock_errno();
+ while (close(new_ns) == INVALID_SOCKET &&
+ sock_errno() == EINTR);
+ while (close(current_ns) == INVALID_SOCKET &&
+ sock_errno() == EINTR);
+ return save_errno;
+ }
+ else {
+ while (close(new_ns) == INVALID_SOCKET &&
+ sock_errno() == EINTR);
+ }
+ }
+#endif
+ save_errno = 0;
+ result = getifaddrs(ifa_pp);
+ if (result < 0)
+ save_errno = sock_errno();
+#ifdef HAVE_SETNS
+ if (desc_p->netns != NULL) {
+ /* Restore network namespace */
+ if (setns(current_ns, CLONE_NEWNET) != 0) {
+ /* XXX Failed to restore network namespace.
+ * What to do? Tidy up and return an error...
+ * Note that the thread now might still be in the set namespace.
+ * Can this even happen? Should the emulator be aborted?
+ */
+ if (result >= 0) {
+ /* We got a result but have to waste it */
+ save_errno = sock_errno();
+ freeifaddrs(*ifa_pp);
+ }
+ }
+ while (close(current_ns) == INVALID_SOCKET &&
+ sock_errno() == EINTR);
+ }
+#endif
+ return save_errno;
+}
+#endif /* #ifdef HAVE_GETIFADDRS */
+
/* FIXME: temporary hack */
#ifndef IFHWADDRLEN
#define IFHWADDRLEN 6
@@ -5255,8 +5327,8 @@ static ErlDrvSSizeT inet_ctl_ifget(inet_descriptor* desc,
struct sockaddr_dl *sdlp;
int found = 0;
- if (getifaddrs(&ifa) == -1)
- goto error;
+ if (call_getifaddrs(desc, &ifa) != 0)
+ goto error;
for (ifp = ifa; ifp; ifp = ifp->ifa_next) {
if ((ifp->ifa_addr->sa_family == AF_LINK) &&
@@ -5974,6 +6046,7 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p,
ErlDrvSizeT buf_size;
char *buf_p;
char *buf_alloc_p;
+ int save_errno;
buf_size = GETIFADDRS_BUFSZ;
buf_alloc_p = ALLOC(GETIFADDRS_BUFSZ);
@@ -6008,9 +6081,9 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p,
} \
} while (0)
- if (getifaddrs(&ifa_p) < 0) {
- return ctl_error(sock_errno(), rbuf_pp, rsize);
- }
+ if ((save_errno = call_getifaddrs(desc_p, &ifa_p)) != 0)
+ return ctl_error(save_errno, rbuf_pp, rsize);
+
ifa_free_p = ifa_p;
*buf_p++ = INET_REP_OK;
for (; ifa_p; ifa_p = ifa_p->ifa_next) {
@@ -6532,6 +6605,19 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
(long)desc->port, desc->s, ival));
break;
+ case TCP_OPT_NOPUSH:
+#if defined(INET_TCP_NOPUSH)
+ proto = IPPROTO_TCP;
+ type = INET_TCP_NOPUSH;
+ DEBUGF(("inet_set_opts(%ld): s=%d, t=%d TCP_NOPUSH=%d\r\n",
+ (long)desc->port, desc->s, type, ival));
+ break;
+#else
+ /* inet_fill_opts always returns a value for this option,
+ * so we need to ignore it if not implemented, just in case */
+ continue;
+#endif
+
#if defined(HAVE_MULTICAST_SUPPORT) && defined(IPPROTO_IP)
case UDP_OPT_MULTICAST_TTL:
@@ -7693,6 +7779,16 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc,
proto = IPPROTO_TCP;
type = TCP_NODELAY;
break;
+ case TCP_OPT_NOPUSH:
+#if defined(INET_TCP_NOPUSH)
+ proto = IPPROTO_TCP;
+ type = INET_TCP_NOPUSH;
+ break;
+#else
+ *ptr++ = opt;
+ put_int32(0, ptr);
+ continue;
+#endif
#if defined(HAVE_MULTICAST_SUPPORT) && defined(IPPROTO_IP)
case UDP_OPT_MULTICAST_TTL:
@@ -7839,8 +7935,8 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc,
* cmsg options and values
*/
PLACE_FOR(1+4, ptr);
- *ptr = opt;
- arg_ptr = ptr+1; /* Where to put total length */
+ *ptr++ = opt;
+ arg_ptr = ptr; /* Where to put total length */
arg_sz = 0; /* Total length */
for (cmsg_top = (struct cmsghdr*)(cmsgbuf.buf + cmsg_sz),
cmsg = (struct cmsghdr*)cmsgbuf.buf;
@@ -7852,7 +7948,6 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc,
PLACE_FOR(1+4, ptr); \
*ptr++ = OPT; \
put_cmsg_int32(cmsg, ptr); \
- ptr += 4; \
arg_sz += 1+4; \
continue; \
}
@@ -7866,7 +7961,6 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc,
PUT_CMSG_INT32(IPPROTO_IP, IP_TTL, INET_OPT_TTL);
#endif
/* BSD uses the RECV* names in CMSG fields */
- }
#if defined(IPPROTO_IP) && defined(IP_RECVTOS)
PUT_CMSG_INT32(IPPROTO_IP, IP_RECVTOS, INET_OPT_TOS);
#endif
@@ -7877,6 +7971,7 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc,
PUT_CMSG_INT32(IPPROTO_IP, IP_RECVTTL, INET_OPT_TTL);
#endif
#undef PUT_CMSG_INT32
+ }
put_int32(arg_sz, arg_ptr); /* Put total length */
continue;
}
diff --git a/erts/emulator/nifs/common/prim_file_nif.c b/erts/emulator/nifs/common/prim_file_nif.c
index a05d50b333..fd6aaa61f6 100644
--- a/erts/emulator/nifs/common/prim_file_nif.c
+++ b/erts/emulator/nifs/common/prim_file_nif.c
@@ -514,6 +514,7 @@ static ERL_NIF_TERM read_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, con
ASSERT(bytes_read <= block_size);
if(bytes_read < 0) {
+ enif_release_binary(&result);
return posix_error_to_tuple(env, d->posix_errno);
} else if(bytes_read == 0) {
enif_release_binary(&result);
@@ -576,6 +577,7 @@ static ERL_NIF_TERM pread_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, co
bytes_read = efile_preadv(d, offset, read_vec, 1);
if(bytes_read < 0) {
+ enif_release_binary(&result);
return posix_error_to_tuple(env, d->posix_errno);
} else if(bytes_read == 0) {
enif_release_binary(&result);
@@ -802,6 +804,7 @@ static ERL_NIF_TERM ipread_s32bu_p32bu_nif_impl(efile_data_t *d, ErlNifEnv *env,
bytes_read = efile_preadv(d, payload_offset, read_vec, 1);
if(bytes_read < 0) {
+ enif_release_binary(&payload);
return posix_error_to_tuple(env, d->posix_errno);
} else if(bytes_read == 0) {
enif_release_binary(&payload);
diff --git a/erts/emulator/nifs/win32/win_prim_file.c b/erts/emulator/nifs/win32/win_prim_file.c
index f7fae3c637..602a282dd1 100644
--- a/erts/emulator/nifs/win32/win_prim_file.c
+++ b/erts/emulator/nifs/win32/win_prim_file.c
@@ -33,16 +33,32 @@
#define FILE_SHARE_FLAGS (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
-#define LP_PREFIX L"\\\\?\\"
-#define LP_PREFIX_SIZE (sizeof(LP_PREFIX) - sizeof(WCHAR))
+/* Long paths can either be in the file (?) or the device (.) namespace. UNC
+ * paths are always in the file namespace. */
+#define LP_FILE_PREFIX L"\\\\?\\"
+#define LP_DEV_PREFIX L"\\\\.\\"
+#define LP_UNC_PREFIX (LP_FILE_PREFIX L"UNC\\")
+
+#define LP_PREFIX_SIZE (sizeof(LP_FILE_PREFIX) - sizeof(WCHAR))
#define LP_PREFIX_LENGTH (LP_PREFIX_SIZE / sizeof(WCHAR))
+#define LP_UNC_PREFIX_SIZE (sizeof(LP_UNC_PREFIX) - sizeof(WCHAR))
+#define LP_UNC_PREFIX_LENGTH (LP_UNC_PREFIX_SIZE / sizeof(WCHAR))
+
+#define IS_LONG_PATH(length, data) \
+ ((length) >= LP_PREFIX_LENGTH && \
+ (!sys_memcmp((data), LP_FILE_PREFIX, LP_PREFIX_SIZE) || \
+ !sys_memcmp((data), LP_DEV_PREFIX, LP_PREFIX_SIZE)))
+
+#define IS_LONG_UNC_PATH(length, data) \
+ ((length) >= LP_UNC_PREFIX_LENGTH && \
+ !sys_memcmp((data), LP_UNC_PREFIX, LP_UNC_PREFIX_SIZE))
+
#define PATH_LENGTH(path) (path->size / sizeof(WCHAR) - 1)
#define ASSERT_PATH_FORMAT(path) \
do { \
- ASSERT(PATH_LENGTH(path) >= 4 && \
- !memcmp(path->data, LP_PREFIX, LP_PREFIX_SIZE)); \
+ ASSERT(IS_LONG_PATH(PATH_LENGTH(path), (path)->data)); \
ASSERT(PATH_LENGTH(path) == wcslen((WCHAR*)path->data)); \
} while(0)
@@ -106,7 +122,7 @@ static posix_errno_t get_full_path(ErlNifEnv *env, WCHAR *input, efile_path_t *r
return ENOENT;
}
- maximum_length += LP_PREFIX_LENGTH;
+ maximum_length += MAX(LP_PREFIX_LENGTH, LP_UNC_PREFIX_LENGTH);
if(!enif_alloc_binary(maximum_length * sizeof(WCHAR), result)) {
return ENOMEM;
@@ -115,18 +131,28 @@ static posix_errno_t get_full_path(ErlNifEnv *env, WCHAR *input, efile_path_t *r
actual_length = GetFullPathNameW(input, maximum_length, (WCHAR*)result->data, NULL);
if(actual_length < maximum_length) {
- int has_long_path_prefix;
+ int is_long_path, maybe_unc_path;
WCHAR *path_start;
- /* Make sure we have a long-path prefix; GetFullPathNameW only adds one
- * if the path is relative. */
- has_long_path_prefix = actual_length >= LP_PREFIX_LENGTH &&
- !sys_memcmp(result->data, LP_PREFIX, LP_PREFIX_SIZE);
-
- if(!has_long_path_prefix) {
+ /* The APIs we use have varying path length limits and sometimes
+ * behave differently when given a long-path prefix, so it's simplest
+ * to always use long paths. */
+
+ is_long_path = IS_LONG_PATH(actual_length, result->data);
+ maybe_unc_path = !sys_memcmp(result->data, L"\\\\", sizeof(WCHAR) * 2);
+
+ if(maybe_unc_path && !is_long_path) {
+ /* \\localhost\c$\gurka -> \\?\UNC\localhost\c$\gurka */
+ sys_memmove(result->data + LP_UNC_PREFIX_SIZE,
+ &((WCHAR*)result->data)[2],
+ (actual_length - 1) * sizeof(WCHAR));
+ sys_memcpy(result->data, LP_UNC_PREFIX, LP_UNC_PREFIX_SIZE);
+ actual_length += LP_UNC_PREFIX_LENGTH;
+ } else if(!is_long_path) {
+ /* C:\gurka -> \\?\C:\gurka */
sys_memmove(result->data + LP_PREFIX_SIZE, result->data,
(actual_length + 1) * sizeof(WCHAR));
- sys_memcpy(result->data, LP_PREFIX, LP_PREFIX_SIZE);
+ sys_memcpy(result->data, LP_FILE_PREFIX, LP_PREFIX_SIZE);
actual_length += LP_PREFIX_LENGTH;
}
@@ -200,13 +226,19 @@ static int normalize_path_result(ErlNifBinary *path) {
ASSERT(length < path->size / sizeof(WCHAR));
/* Get rid of the long-path prefix, if present. */
- if(length >= LP_PREFIX_LENGTH) {
- if(!sys_memcmp(path_start, LP_PREFIX, LP_PREFIX_SIZE)) {
- length -= LP_PREFIX_LENGTH;
- sys_memmove(path_start, &path_start[LP_PREFIX_LENGTH],
- length * sizeof(WCHAR));
- }
+ if(IS_LONG_UNC_PATH(length, path_start)) {
+ /* The first two characters (\\) are the same for both long and short
+ * UNC paths. */
+ sys_memmove(&path_start[2], &path_start[LP_UNC_PREFIX_LENGTH],
+ (length - LP_UNC_PREFIX_LENGTH) * sizeof(WCHAR));
+
+ length -= LP_UNC_PREFIX_LENGTH - 2;
+ } else if(IS_LONG_PATH(length, path_start)) {
+ length -= LP_PREFIX_LENGTH;
+
+ sys_memmove(path_start, &path_start[LP_PREFIX_LENGTH],
+ length * sizeof(WCHAR));
}
path_end = &path_start[length];
@@ -318,49 +350,55 @@ static int has_same_mount_point(const efile_path_t *path_a, const efile_path_t *
/* Mirrors the PathIsRootW function of the shell API, but doesn't choke on
* paths longer than MAX_PATH. */
static int is_path_root(const efile_path_t *path) {
- const WCHAR *path_start, *path_end;
+ const WCHAR *path_start, *path_end, *path_iterator;
int length;
ASSERT_PATH_FORMAT(path);
- path_start = (WCHAR*)path->data + LP_PREFIX_LENGTH;
- length = PATH_LENGTH(path) - LP_PREFIX_LENGTH;
+ if(!IS_LONG_UNC_PATH(PATH_LENGTH(path), path->data)) {
+ path_start = (WCHAR*)path->data + LP_PREFIX_LENGTH;
+ length = PATH_LENGTH(path) - LP_PREFIX_LENGTH;
- path_end = &path_start[length];
-
- if(length == 1) {
/* A single \ refers to the root of the current working directory. */
- return IS_SLASH(path_start[0]);
- } else if(length == 3 && iswalpha(path_start[0]) && path_start[1] == L':') {
- /* Drive letter. */
- return IS_SLASH(path_start[2]);
- } else if(length >= 4) {
- /* Check whether we're a UNC root, eg. \\server, \\server\share */
- const WCHAR *path_iterator;
+ if(length == 1) {
+ return IS_SLASH(path_start[0]);
+ }
- if(!IS_SLASH(path_start[0]) || !IS_SLASH(path_start[1])) {
- return 0;
+ /* Drive letter. */
+ if(length == 3 && iswalpha(path_start[0]) && path_start[1] == L':') {
+ return IS_SLASH(path_start[2]);
}
- path_iterator = path_start + 2;
+ return 0;
+ }
- /* Slide to the slash between the server and share names, if present. */
- while(path_iterator < path_end && !IS_SLASH(*path_iterator)) {
- path_iterator++;
- }
+ /* Check whether we're a UNC root, eg. \\server, \\server\share */
- /* Slide past the end of the string, stopping at the first slash we
- * encounter. */
- do {
- path_iterator++;
- } while(path_iterator < path_end && !IS_SLASH(*path_iterator));
+ path_start = (WCHAR*)path->data + LP_UNC_PREFIX_LENGTH;
+ length = PATH_LENGTH(path) - LP_UNC_PREFIX_LENGTH;
- /* If we're past the end of the string and it didnt't end with a slash,
- * then we're a root path. */
- return path_iterator >= path_end && !IS_SLASH(path_start[length - 1]);
+ path_end = &path_start[length];
+ path_iterator = path_start;
+
+ /* Server name must be at least one character. */
+ if(length <= 1) {
+ return 0;
}
- return 0;
+ /* Slide to the slash between the server and share names, if present. */
+ while(path_iterator < path_end && !IS_SLASH(*path_iterator)) {
+ path_iterator++;
+ }
+
+ /* Slide past the end of the string, stopping at the first slash we
+ * encounter. */
+ do {
+ path_iterator++;
+ } while(path_iterator < path_end && !IS_SLASH(*path_iterator));
+
+ /* If we're past the end of the string and it didnt't end with a slash,
+ * then we're a root path. */
+ return path_iterator >= path_end && !IS_SLASH(path_start[length - 1]);
}
posix_errno_t efile_open(const efile_path_t *path, enum efile_modes_t modes,
@@ -687,7 +725,7 @@ static int is_name_surrogate(const efile_path_t *path) {
if(handle != INVALID_HANDLE_VALUE) {
REPARSE_GUID_DATA_BUFFER reparse_buffer;
- LPDWORD unused_length;
+ DWORD unused_length;
BOOL success;
success = DeviceIoControl(handle,
@@ -1248,11 +1286,22 @@ posix_errno_t efile_set_cwd(const efile_path_t *path) {
/* We have to use _wchdir since that's the only function that updates the
* per-drive working directory, but it naively assumes that all paths
- * starting with \\ are UNC paths, so we have to skip the \\?\-prefix. */
- path_start = (WCHAR*)path->data + LP_PREFIX_LENGTH;
+ * starting with \\ are UNC paths, so we have to skip the long-path prefix.
+ *
+ * _wchdir doesn't handle long-prefixed UNC paths either so we hand those
+ * to SetCurrentDirectoryW instead. The per-drive working directory is
+ * irrelevant for such paths anyway. */
- if(_wchdir(path_start)) {
- return windows_to_posix_errno(GetLastError());
+ if(!IS_LONG_UNC_PATH(PATH_LENGTH(path), path->data)) {
+ path_start = (WCHAR*)path->data + LP_PREFIX_LENGTH;
+
+ if(_wchdir(path_start)) {
+ return windows_to_posix_errno(GetLastError());
+ }
+ } else {
+ if(!SetCurrentDirectoryW((WCHAR*)path->data)) {
+ return windows_to_posix_errno(GetLastError());
+ }
}
return 0;
@@ -1333,7 +1382,7 @@ posix_errno_t efile_altname(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_TE
int name_length;
/* Reject path wildcards. */
- if(wcspbrk(&((const WCHAR*)path->data)[4], L"?*")) {
+ if(wcspbrk(&((const WCHAR*)path->data)[LP_PREFIX_LENGTH], L"?*")) {
return ENOENT;
}
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index 4a345f8152..52bab031ff 100644
--- a/erts/preloaded/ebin/prim_inet.beam
+++ b/erts/preloaded/ebin/prim_inet.beam
Binary files differ
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index 8169943dde..963e8933bc 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -520,13 +520,35 @@ sendfile(S, FileHandle, Offset, Length)
sendfile(S, FileHandle, Offset, Length) ->
case erlang:port_info(S, connected) of
{connected, Pid} when Pid =:= self() ->
- sendfile_1(S, FileHandle, Offset, Length);
+ Uncork = sendfile_maybe_cork(S),
+ Result = sendfile_1(S, FileHandle, Offset, Length),
+ sendfile_maybe_uncork(S, Uncork),
+ Result;
{connected, Pid} when Pid =/= self() ->
{error, not_owner};
_Other ->
{error, einval}
end.
+sendfile_maybe_cork(S) ->
+ case getprotocol(S) of
+ tcp ->
+ case getopts(S, [nopush]) of
+ {ok, [{nopush,false}]} ->
+ _ = setopts(S, [{nopush,true}]),
+ true;
+ _ ->
+ false
+ end;
+ _ -> false
+ end.
+
+sendfile_maybe_uncork(S, true) ->
+ _ = setopts(S, [{nopush,false}]),
+ ok;
+sendfile_maybe_uncork(_, false) ->
+ ok.
+
sendfile_1(S, FileHandle, Offset, 0) ->
sendfile_1(S, FileHandle, Offset, (1 bsl 63) - 1);
sendfile_1(_S, _FileHandle, Offset, Length) when
@@ -870,9 +892,9 @@ chgopts(S, Opts) when is_port(S), is_list(Opts) ->
getifaddrs(S) when is_port(S) ->
case ctl_cmd(S, ?INET_REQ_GETIFADDRS, []) of
- {ok, Data} ->
- {ok, comp_ifaddrs(build_ifaddrs(Data), ktree_empty())};
- {error,enotsup} ->
+ {ok, Data} ->
+ {ok, comp_ifaddrs(build_ifaddrs(Data))};
+ {error,enotsup} ->
case getiflist(S) of
{ok, IFs} ->
{ok, getifaddrs_ifget(S, IFs)};
@@ -881,30 +903,75 @@ getifaddrs(S) when is_port(S) ->
Err2 -> Err2
end.
-%% Restructure interface properties per interface and remove duplicates
-
-comp_ifaddrs([{If,Opts}|IfOpts], T) ->
- case ktree_is_defined(If, T) of
- true ->
- OptSet = comp_ifaddrs_add(ktree_get(If, T), Opts),
- comp_ifaddrs(IfOpts, ktree_update(If, OptSet, T));
- false ->
- OptSet = comp_ifaddrs_add(ktree_empty(), Opts),
- comp_ifaddrs(IfOpts, ktree_insert(If, OptSet, T))
- end;
-comp_ifaddrs([], T) ->
- [{If,ktree_keys(ktree_get(If, T))} || If <- ktree_keys(T)].
-
-comp_ifaddrs_add(OptSet, [Opt|Opts]) ->
- case ktree_is_defined(Opt, OptSet) of
- true
- when element(1, Opt) =:= flags;
- element(1, Opt) =:= hwaddr ->
- comp_ifaddrs_add(OptSet, Opts);
- _ ->
- comp_ifaddrs_add(ktree_insert(Opt, undefined, OptSet), Opts)
+%% Restructure interface properties per interface
+
+comp_ifaddrs(IfOpts) ->
+ comp_ifaddrs(IfOpts, ktree_empty()).
+%%
+comp_ifaddrs([{If,[{flags,Flags}|Opts]}|IfOpts], IfT) ->
+ case ktree_is_defined(If, IfT) of
+ true ->
+ comp_ifaddrs(
+ IfOpts,
+ ktree_update(
+ If,
+ comp_ifaddrs_flags(Flags, Opts, ktree_get(If, IfT)),
+ IfT));
+ false ->
+ comp_ifaddrs(
+ IfOpts,
+ ktree_insert(
+ If,
+ comp_ifaddrs_flags(Flags, Opts, ktree_empty()),
+ IfT))
end;
-comp_ifaddrs_add(OptSet, []) -> OptSet.
+comp_ifaddrs([], IfT) ->
+ comp_ifaddrs_2(ktree_keys(IfT), IfT).
+
+comp_ifaddrs_flags(Flags, Opts, FlagsT) ->
+ case ktree_is_defined(Flags, FlagsT) of
+ true ->
+ ktree_update(
+ Flags,
+ rev(Opts, ktree_get(Flags, FlagsT)),
+ FlagsT);
+ false ->
+ ktree_insert(Flags, rev(Opts), FlagsT)
+ end.
+
+comp_ifaddrs_2([If|Ifs], IfT) ->
+ FlagsT = ktree_get(If, IfT),
+ [{If,comp_ifaddrs_3(ktree_keys(FlagsT), FlagsT)}
+ | comp_ifaddrs_2(Ifs, IfT)];
+comp_ifaddrs_2([], _IfT) ->
+ [].
+%%
+comp_ifaddrs_3([Flags|FlagsL], FlagsT) ->
+ [{flags,Flags}|hwaddr_last(rev(ktree_get(Flags, FlagsT)))]
+ ++ hwaddr_last(comp_ifaddrs_3(FlagsL, FlagsT));
+comp_ifaddrs_3([], _FlagsT) ->
+ [].
+
+%% Place hwaddr last to look more like legacy emulation
+hwaddr_last(Opts) ->
+ hwaddr_last(Opts, Opts, []).
+%%
+hwaddr_last([{hwaddr,_} = Opt|Opts], L, R) ->
+ hwaddr_last(Opts, L, [Opt|R]);
+hwaddr_last([_|Opts], L, R) ->
+ hwaddr_last(Opts, L, R);
+hwaddr_last([], L, []) ->
+ L;
+hwaddr_last([], L, R) ->
+ rev(hwaddr_last(L, []), rev(R)).
+%%
+hwaddr_last([{hwaddr,_}|Opts], R) ->
+ hwaddr_last(Opts, R);
+hwaddr_last([Opt|Opts], R) ->
+ hwaddr_last(Opts, [Opt|R]);
+hwaddr_last([], R) ->
+ R.
+
%% Legacy emulation of getifaddrs
@@ -912,21 +979,19 @@ getifaddrs_ifget(_, []) -> [];
getifaddrs_ifget(S, [IF|IFs]) ->
case ifget(S, IF, [flags]) of
{ok,[{flags,Flags}]=FlagsVals} ->
- BroadOpts =
- case member(broadcast, Flags) of
- true ->
- [broadaddr,hwaddr];
- false ->
- [hwaddr]
- end,
- P2POpts =
- case member(pointtopoint, Flags) of
- true ->
- [dstaddr|BroadOpts];
- false ->
- BroadOpts
- end,
- getifaddrs_ifget(S, IFs, IF, FlagsVals, [addr,netmask|P2POpts]);
+ GetOpts =
+ case member(pointtopoint, Flags) of
+ true ->
+ [dstaddr,hwaddr];
+ false ->
+ case member(broadcast, Flags) of
+ true ->
+ [broadaddr,hwaddr];
+ false ->
+ [hwaddr]
+ end
+ end,
+ getifaddrs_ifget(S, IFs, IF, FlagsVals, [addr,netmask|GetOpts]);
_ ->
getifaddrs_ifget(S, IFs, IF, [], [addr,netmask,hwaddr])
end.
@@ -1275,6 +1340,7 @@ enc_opt(pktoptions) -> ?INET_OPT_PKTOPTIONS;
enc_opt(ttl) -> ?INET_OPT_TTL;
enc_opt(recvttl) -> ?INET_OPT_RECVTTL;
enc_opt(nodelay) -> ?TCP_OPT_NODELAY;
+enc_opt(nopush) -> ?TCP_OPT_NOPUSH;
enc_opt(multicast_if) -> ?UDP_OPT_MULTICAST_IF;
enc_opt(multicast_ttl) -> ?UDP_OPT_MULTICAST_TTL;
enc_opt(multicast_loop) -> ?UDP_OPT_MULTICAST_LOOP;
@@ -1336,6 +1402,7 @@ dec_opt(?INET_OPT_PRIORITY) -> priority;
dec_opt(?INET_OPT_TOS) -> tos;
dec_opt(?INET_OPT_TCLASS) -> tclass;
dec_opt(?TCP_OPT_NODELAY) -> nodelay;
+dec_opt(?TCP_OPT_NOPUSH) -> nopush;
dec_opt(?INET_OPT_RECVTOS) -> recvtos;
dec_opt(?INET_OPT_RECVTCLASS) -> recvtclass;
dec_opt(?INET_OPT_PKTOPTIONS) -> pktoptions;
@@ -1422,6 +1489,7 @@ type_opt_1(pktoptions) -> opts;
type_opt_1(ttl) -> int;
type_opt_1(recvttl) -> bool;
type_opt_1(nodelay) -> bool;
+type_opt_1(nopush) -> bool;
type_opt_1(ipv6_v6only) -> bool;
%% multicast
type_opt_1(multicast_ttl) -> int;
@@ -2500,7 +2568,7 @@ get_addrs([F|Addrs]) ->
[Addr|get_addrs(Rest)].
get_addr(?INET_AF_LOCAL, [N|Addr]) ->
- {A,Rest} = lists:split(N, Addr),
+ {A,Rest} = split(N, Addr),
{{local,iolist_to_binary(A)},Rest};
get_addr(?INET_AF_UNSPEC, Rest) ->
{{unspec,<<>>},Rest};
diff --git a/erts/vsn.mk b/erts/vsn.mk
index 5b187b1f4d..01c19aff2f 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -18,7 +18,7 @@
# %CopyrightEnd%
#
-VSN = 10.1
+VSN = 10.1.1
# Port number 4365 in 4.2
# Port number 4366 in 4.3