443 lines
12 KiB
Diff
443 lines
12 KiB
Diff
|
From a37559fdde9d08aad081a28f3118273f61f5a360 Mon Sep 17 00:00:00 2001
|
||
|
From: Ben Noordhuis <info@bnoordhuis.nl>
|
||
|
Date: Fri, 19 Jan 2018 01:24:43 +0100
|
||
|
Subject: [PATCH 2/3] Revert "unix,fs: fix for potential partial reads/writes"
|
||
|
|
||
|
This commit has been reported as introducing a backwards-incompatible
|
||
|
change in reading from stdin and is independently suspected of breaking
|
||
|
the Node.js test suite on MacOS and maybe other platforms, possibly in
|
||
|
combination with commit fd049399 ("unix,tcp: avoid marking server
|
||
|
sockets connected".)
|
||
|
|
||
|
This reverts commit 14bfc27e641aff178c431083c0c0eada4d6f02dd.
|
||
|
|
||
|
Fixes: https://github.com/libuv/libuv/issues/1716
|
||
|
Fixes: https://github.com/libuv/libuv/issues/1720
|
||
|
Fixes: https://github.com/nodejs/node/issues/18225
|
||
|
---
|
||
|
Makefile.am | 1 +
|
||
|
src/unix/fs.c | 62 +++++++++++++++-------
|
||
|
test/test-eintr-handling.c | 94 +++++++++++++++++++++++++++++++++
|
||
|
test/test-fs.c | 127 +--------------------------------------------
|
||
|
test/test-list.h | 6 +--
|
||
|
uv.gyp | 1 +
|
||
|
6 files changed, 141 insertions(+), 150 deletions(-)
|
||
|
create mode 100644 test/test-eintr-handling.c
|
||
|
|
||
|
diff --git a/Makefile.am b/Makefile.am
|
||
|
index c49802f7..ae9d96bc 100644
|
||
|
--- a/Makefile.am
|
||
|
+++ b/Makefile.am
|
||
|
@@ -165,6 +165,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
||
|
test/test-default-loop-close.c \
|
||
|
test/test-delayed-accept.c \
|
||
|
test/test-dlerror.c \
|
||
|
+ test/test-eintr-handling.c \
|
||
|
test/test-embed.c \
|
||
|
test/test-emfile.c \
|
||
|
test/test-env-vars.c \
|
||
|
diff --git a/src/unix/fs.c b/src/unix/fs.c
|
||
|
index 79864638..e0969a4c 100644
|
||
|
--- a/src/unix/fs.c
|
||
|
+++ b/src/unix/fs.c
|
||
|
@@ -334,7 +334,25 @@ static ssize_t uv__fs_read(uv_fs_t* req) {
|
||
|
if (no_preadv) retry:
|
||
|
# endif
|
||
|
{
|
||
|
- result = pread(req->file, req->bufs[0].base, req->bufs[0].len, req->off);
|
||
|
+ off_t nread;
|
||
|
+ size_t index;
|
||
|
+
|
||
|
+ nread = 0;
|
||
|
+ index = 0;
|
||
|
+ result = 1;
|
||
|
+ do {
|
||
|
+ if (req->bufs[index].len > 0) {
|
||
|
+ result = pread(req->file,
|
||
|
+ req->bufs[index].base,
|
||
|
+ req->bufs[index].len,
|
||
|
+ req->off + nread);
|
||
|
+ if (result > 0)
|
||
|
+ nread += result;
|
||
|
+ }
|
||
|
+ index++;
|
||
|
+ } while (index < req->nbufs && result > 0);
|
||
|
+ if (nread > 0)
|
||
|
+ result = nread;
|
||
|
}
|
||
|
# if defined(__linux__)
|
||
|
else {
|
||
|
@@ -722,7 +740,25 @@ static ssize_t uv__fs_write(uv_fs_t* req) {
|
||
|
if (no_pwritev) retry:
|
||
|
# endif
|
||
|
{
|
||
|
- r = pwrite(req->file, req->bufs[0].base, req->bufs[0].len, req->off);
|
||
|
+ off_t written;
|
||
|
+ size_t index;
|
||
|
+
|
||
|
+ written = 0;
|
||
|
+ index = 0;
|
||
|
+ r = 0;
|
||
|
+ do {
|
||
|
+ if (req->bufs[index].len > 0) {
|
||
|
+ r = pwrite(req->file,
|
||
|
+ req->bufs[index].base,
|
||
|
+ req->bufs[index].len,
|
||
|
+ req->off + written);
|
||
|
+ if (r > 0)
|
||
|
+ written += r;
|
||
|
+ }
|
||
|
+ index++;
|
||
|
+ } while (index < req->nbufs && r >= 0);
|
||
|
+ if (written > 0)
|
||
|
+ r = written;
|
||
|
}
|
||
|
# if defined(__linux__)
|
||
|
else {
|
||
|
@@ -972,19 +1008,6 @@ static int uv__fs_fstat(int fd, uv_stat_t *buf) {
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
-static size_t uv__fs_buf_offset(uv_buf_t* bufs, size_t size) {
|
||
|
- size_t offset;
|
||
|
- /* Figure out which bufs are done */
|
||
|
- for (offset = 0; size > 0 && bufs[offset].len <= size; ++offset)
|
||
|
- size -= bufs[offset].len;
|
||
|
-
|
||
|
- /* Fix a partial read/write */
|
||
|
- if (size > 0) {
|
||
|
- bufs[offset].base += size;
|
||
|
- bufs[offset].len -= size;
|
||
|
- }
|
||
|
- return offset;
|
||
|
-}
|
||
|
|
||
|
typedef ssize_t (*uv__fs_buf_iter_processor)(uv_fs_t* req);
|
||
|
static ssize_t uv__fs_buf_iter(uv_fs_t* req, uv__fs_buf_iter_processor process) {
|
||
|
@@ -1004,10 +1027,7 @@ static ssize_t uv__fs_buf_iter(uv_fs_t* req, uv__fs_buf_iter_processor process)
|
||
|
if (req->nbufs > iovmax)
|
||
|
req->nbufs = iovmax;
|
||
|
|
||
|
- do
|
||
|
- result = process(req);
|
||
|
- while (result < 0 && errno == EINTR);
|
||
|
-
|
||
|
+ result = process(req);
|
||
|
if (result <= 0) {
|
||
|
if (total == 0)
|
||
|
total = result;
|
||
|
@@ -1017,12 +1037,14 @@ static ssize_t uv__fs_buf_iter(uv_fs_t* req, uv__fs_buf_iter_processor process)
|
||
|
if (req->off >= 0)
|
||
|
req->off += result;
|
||
|
|
||
|
- req->nbufs = uv__fs_buf_offset(req->bufs, result);
|
||
|
req->bufs += req->nbufs;
|
||
|
nbufs -= req->nbufs;
|
||
|
total += result;
|
||
|
}
|
||
|
|
||
|
+ if (errno == EINTR && total == -1)
|
||
|
+ return total;
|
||
|
+
|
||
|
if (bufs != req->bufsml)
|
||
|
uv__free(bufs);
|
||
|
|
||
|
diff --git a/test/test-eintr-handling.c b/test/test-eintr-handling.c
|
||
|
new file mode 100644
|
||
|
index 00000000..1aaf623b
|
||
|
--- /dev/null
|
||
|
+++ b/test/test-eintr-handling.c
|
||
|
@@ -0,0 +1,94 @@
|
||
|
+/* Copyright libuv project contributors. All rights reserved.
|
||
|
+ *
|
||
|
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
+ * of this software and associated documentation files (the "Software"), to
|
||
|
+ * deal in the Software without restriction, including without limitation the
|
||
|
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||
|
+ * sell copies of the Software, and to permit persons to whom the Software is
|
||
|
+ * furnished to do so, subject to the following conditions:
|
||
|
+ *
|
||
|
+ * The above copyright notice and this permission notice shall be included in
|
||
|
+ * all copies or substantial portions of the Software.
|
||
|
+ *
|
||
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
|
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||
|
+ * IN THE SOFTWARE.
|
||
|
+ */
|
||
|
+
|
||
|
+#include "uv.h"
|
||
|
+#include "task.h"
|
||
|
+
|
||
|
+#ifdef _WIN32
|
||
|
+
|
||
|
+TEST_IMPL(eintr_handling) {
|
||
|
+ RETURN_SKIP("Test not implemented on Windows.");
|
||
|
+}
|
||
|
+
|
||
|
+#else /* !_WIN32 */
|
||
|
+
|
||
|
+#include <string.h>
|
||
|
+#include <unistd.h>
|
||
|
+
|
||
|
+static uv_loop_t* loop;
|
||
|
+static uv_fs_t read_req;
|
||
|
+static uv_buf_t iov;
|
||
|
+
|
||
|
+static char buf[32];
|
||
|
+static char test_buf[] = "test-buffer\n";
|
||
|
+int pipe_fds[2];
|
||
|
+
|
||
|
+struct thread_ctx {
|
||
|
+ uv_barrier_t barrier;
|
||
|
+ int fd;
|
||
|
+};
|
||
|
+
|
||
|
+static void thread_main(void* arg) {
|
||
|
+ int nwritten;
|
||
|
+ ASSERT(0 == kill(getpid(), SIGUSR1));
|
||
|
+
|
||
|
+ do
|
||
|
+ nwritten = write(pipe_fds[1], test_buf, sizeof(test_buf));
|
||
|
+ while (nwritten == -1 && errno == EINTR);
|
||
|
+
|
||
|
+ ASSERT(nwritten == sizeof(test_buf));
|
||
|
+}
|
||
|
+
|
||
|
+static void sig_func(uv_signal_t* handle, int signum) {
|
||
|
+ uv_signal_stop(handle);
|
||
|
+}
|
||
|
+
|
||
|
+TEST_IMPL(eintr_handling) {
|
||
|
+ struct thread_ctx ctx;
|
||
|
+ uv_thread_t thread;
|
||
|
+ uv_signal_t signal;
|
||
|
+ int nread;
|
||
|
+
|
||
|
+ iov = uv_buf_init(buf, sizeof(buf));
|
||
|
+ loop = uv_default_loop();
|
||
|
+
|
||
|
+ ASSERT(0 == uv_signal_init(loop, &signal));
|
||
|
+ ASSERT(0 == uv_signal_start(&signal, sig_func, SIGUSR1));
|
||
|
+
|
||
|
+ ASSERT(0 == pipe(pipe_fds));
|
||
|
+ ASSERT(0 == uv_thread_create(&thread, thread_main, &ctx));
|
||
|
+
|
||
|
+ nread = uv_fs_read(loop, &read_req, pipe_fds[0], &iov, 1, -1, NULL);
|
||
|
+
|
||
|
+ ASSERT(nread == sizeof(test_buf));
|
||
|
+ ASSERT(0 == strcmp(buf, test_buf));
|
||
|
+
|
||
|
+ ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT));
|
||
|
+
|
||
|
+ ASSERT(0 == close(pipe_fds[0]));
|
||
|
+ ASSERT(0 == close(pipe_fds[1]));
|
||
|
+ uv_close((uv_handle_t*) &signal, NULL);
|
||
|
+
|
||
|
+ MAKE_VALGRIND_HAPPY();
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+#endif /* !_WIN32 */
|
||
|
diff --git a/test/test-fs.c b/test/test-fs.c
|
||
|
index d43d6e61..241416bc 100644
|
||
|
--- a/test/test-fs.c
|
||
|
+++ b/test/test-fs.c
|
||
|
@@ -2890,131 +2890,6 @@ TEST_IMPL(fs_write_alotof_bufs_with_offset) {
|
||
|
}
|
||
|
|
||
|
|
||
|
-#ifdef _WIN32
|
||
|
-
|
||
|
-TEST_IMPL(fs_partial_read) {
|
||
|
- RETURN_SKIP("Test not implemented on Windows.");
|
||
|
-}
|
||
|
-
|
||
|
-TEST_IMPL(fs_partial_write) {
|
||
|
- RETURN_SKIP("Test not implemented on Windows.");
|
||
|
-}
|
||
|
-
|
||
|
-#else /* !_WIN32 */
|
||
|
-
|
||
|
-static void thread_exec(int fd, char* data, int size, int interval, int doread) {
|
||
|
- pid_t pid;
|
||
|
- ssize_t result;
|
||
|
-
|
||
|
- pid = getpid();
|
||
|
- result = 1;
|
||
|
-
|
||
|
- while (size > 0 && result > 0) {
|
||
|
- do {
|
||
|
- if (doread)
|
||
|
- result = write(fd, data, size < interval ? size : interval);
|
||
|
- else
|
||
|
- result = read(fd, data, size < interval ? size : interval);
|
||
|
- } while (result == -1 && errno == EINTR);
|
||
|
-
|
||
|
- kill(pid, SIGUSR1);
|
||
|
- size -= result;
|
||
|
- data += result;
|
||
|
- }
|
||
|
-
|
||
|
- ASSERT(size == 0);
|
||
|
- ASSERT(result > 0);
|
||
|
-}
|
||
|
-
|
||
|
-struct thread_ctx {
|
||
|
- int fd;
|
||
|
- char *data;
|
||
|
- int size;
|
||
|
- int interval;
|
||
|
- int doread;
|
||
|
-};
|
||
|
-
|
||
|
-static void thread_main(void* arg) {
|
||
|
- struct thread_ctx *ctx;
|
||
|
- ctx = (struct thread_ctx*)arg;
|
||
|
- thread_exec(ctx->fd, ctx->data, ctx->size, ctx->interval, ctx->doread);
|
||
|
-}
|
||
|
-
|
||
|
-static void sig_func(uv_signal_t* handle, int signum) {
|
||
|
- uv_signal_stop(handle);
|
||
|
-}
|
||
|
-
|
||
|
-static void test_fs_partial(int doread) {
|
||
|
- struct thread_ctx ctx;
|
||
|
- uv_thread_t thread;
|
||
|
- uv_signal_t signal;
|
||
|
- int pipe_fds[2];
|
||
|
- size_t iovcount;
|
||
|
- uv_buf_t* iovs;
|
||
|
- char* buffer;
|
||
|
- size_t index;
|
||
|
- int result;
|
||
|
-
|
||
|
- iovcount = 54321;
|
||
|
-
|
||
|
- iovs = malloc(sizeof(*iovs) * iovcount);
|
||
|
- ASSERT(iovs != NULL);
|
||
|
-
|
||
|
- ctx.doread = doread;
|
||
|
- ctx.interval = 1000;
|
||
|
- ctx.size = sizeof(test_buf) * iovcount;
|
||
|
- ctx.data = malloc(ctx.size);
|
||
|
- ASSERT(ctx.data != NULL);
|
||
|
- buffer = malloc(ctx.size);
|
||
|
- ASSERT(buffer != NULL);
|
||
|
-
|
||
|
- for (index = 0; index < iovcount; ++index)
|
||
|
- iovs[index] = uv_buf_init(buffer + index * sizeof(test_buf), sizeof(test_buf));
|
||
|
-
|
||
|
- loop = uv_default_loop();
|
||
|
-
|
||
|
- ASSERT(0 == uv_signal_init(loop, &signal));
|
||
|
- ASSERT(0 == uv_signal_start(&signal, sig_func, SIGUSR1));
|
||
|
-
|
||
|
- ASSERT(0 == pipe(pipe_fds));
|
||
|
-
|
||
|
- ctx.fd = pipe_fds[doread];
|
||
|
- ASSERT(0 == uv_thread_create(&thread, thread_main, &ctx));
|
||
|
-
|
||
|
- if (doread)
|
||
|
- result = uv_fs_read(loop, &read_req, pipe_fds[0], iovs, iovcount, -1, NULL);
|
||
|
- else
|
||
|
- result = uv_fs_write(loop, &write_req, pipe_fds[1], iovs, iovcount, -1, NULL);
|
||
|
-
|
||
|
- ASSERT(result == ctx.size);
|
||
|
- ASSERT(0 == memcmp(buffer, ctx.data, result));
|
||
|
-
|
||
|
- ASSERT(0 == uv_thread_join(&thread));
|
||
|
- ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT));
|
||
|
-
|
||
|
- ASSERT(0 == close(pipe_fds[0]));
|
||
|
- ASSERT(0 == close(pipe_fds[1]));
|
||
|
- uv_close((uv_handle_t*) &signal, NULL);
|
||
|
-
|
||
|
- free(iovs);
|
||
|
- free(buffer);
|
||
|
- free(ctx.data);
|
||
|
-
|
||
|
- MAKE_VALGRIND_HAPPY();
|
||
|
-}
|
||
|
-
|
||
|
-TEST_IMPL(fs_partial_read) {
|
||
|
- test_fs_partial(1);
|
||
|
- return 0;
|
||
|
-}
|
||
|
-
|
||
|
-TEST_IMPL(fs_partial_write) {
|
||
|
- test_fs_partial(0);
|
||
|
- return 0;
|
||
|
-}
|
||
|
-
|
||
|
-#endif/* _WIN32 */
|
||
|
-
|
||
|
TEST_IMPL(fs_read_write_null_arguments) {
|
||
|
int r;
|
||
|
|
||
|
@@ -3223,7 +3098,7 @@ TEST_IMPL(fs_exclusive_sharing_mode) {
|
||
|
unlink("test_file");
|
||
|
|
||
|
ASSERT(UV_FS_O_EXLOCK > 0);
|
||
|
-
|
||
|
+
|
||
|
r = uv_fs_open(NULL,
|
||
|
&open_req1,
|
||
|
"test_file",
|
||
|
diff --git a/test/test-list.h b/test/test-list.h
|
||
|
index 8e4f2025..5a50ec67 100644
|
||
|
--- a/test/test-list.h
|
||
|
+++ b/test/test-list.h
|
||
|
@@ -213,6 +213,7 @@ TEST_DECLARE (active)
|
||
|
TEST_DECLARE (embed)
|
||
|
TEST_DECLARE (async)
|
||
|
TEST_DECLARE (async_null_cb)
|
||
|
+TEST_DECLARE (eintr_handling)
|
||
|
TEST_DECLARE (get_currentexe)
|
||
|
TEST_DECLARE (process_title)
|
||
|
TEST_DECLARE (process_title_threadsafe)
|
||
|
@@ -324,8 +325,6 @@ TEST_DECLARE (fs_read_write_null_arguments)
|
||
|
TEST_DECLARE (get_osfhandle_valid_handle)
|
||
|
TEST_DECLARE (fs_write_alotof_bufs)
|
||
|
TEST_DECLARE (fs_write_alotof_bufs_with_offset)
|
||
|
-TEST_DECLARE (fs_partial_read)
|
||
|
-TEST_DECLARE (fs_partial_write)
|
||
|
TEST_DECLARE (fs_file_pos_after_op_with_offset)
|
||
|
TEST_DECLARE (fs_null_req)
|
||
|
#ifdef _WIN32
|
||
|
@@ -675,6 +674,7 @@ TASK_LIST_START
|
||
|
|
||
|
TEST_ENTRY (async)
|
||
|
TEST_ENTRY (async_null_cb)
|
||
|
+ TEST_ENTRY (eintr_handling)
|
||
|
|
||
|
TEST_ENTRY (get_currentexe)
|
||
|
|
||
|
@@ -848,8 +848,6 @@ TASK_LIST_START
|
||
|
TEST_ENTRY (fs_write_multiple_bufs)
|
||
|
TEST_ENTRY (fs_write_alotof_bufs)
|
||
|
TEST_ENTRY (fs_write_alotof_bufs_with_offset)
|
||
|
- TEST_ENTRY (fs_partial_read)
|
||
|
- TEST_ENTRY (fs_partial_write)
|
||
|
TEST_ENTRY (fs_read_write_null_arguments)
|
||
|
TEST_ENTRY (fs_file_pos_after_op_with_offset)
|
||
|
TEST_ENTRY (fs_null_req)
|
||
|
diff --git a/uv.gyp b/uv.gyp
|
||
|
index 19008dfa..46606c5b 100644
|
||
|
--- a/uv.gyp
|
||
|
+++ b/uv.gyp
|
||
|
@@ -371,6 +371,7 @@
|
||
|
'test/test-cwd-and-chdir.c',
|
||
|
'test/test-default-loop-close.c',
|
||
|
'test/test-delayed-accept.c',
|
||
|
+ 'test/test-eintr-handling.c',
|
||
|
'test/test-error.c',
|
||
|
'test/test-embed.c',
|
||
|
'test/test-emfile.c',
|
||
|
--
|
||
|
2.15.1
|
||
|
|