From ea501263588c0efbaa77b24feda20075a943271a Mon Sep 17 00:00:00 2001 From: Igor Zinkovsky Date: Thu, 5 Apr 2012 16:00:38 -0700 Subject: [PATCH 01/15] add 64bit offset fs functions --- include/uv.h | 9 ++++++ src/win/fs.c | 88 +++++++++++++++++++++++++++++++++++++++++--------- test/test-fs.c | 35 +++++++++++++++----- 3 files changed, 108 insertions(+), 24 deletions(-) diff --git a/include/uv.h b/include/uv.h index 49ce47a9..ed6100ae 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1155,12 +1155,18 @@ UV_EXTERN int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, UV_EXTERN int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, size_t length, off_t offset, uv_fs_cb cb); +int uv_fs_read64(uv_loop_t* loop, uv_fs_t* req, uv_file file, + void* buf, size_t length, int64_t offset, uv_fs_cb cb); + UV_EXTERN int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); UV_EXTERN int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, size_t length, off_t offset, uv_fs_cb cb); +int uv_fs_write64(uv_loop_t* loop, uv_fs_t* req, uv_file file, + void* buf, size_t length, int64_t offset, uv_fs_cb cb); + UV_EXTERN int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb); @@ -1188,6 +1194,9 @@ UV_EXTERN int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, UV_EXTERN int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file, off_t offset, uv_fs_cb cb); +int uv_fs_ftruncate64(uv_loop_t* loop, uv_fs_t* req, uv_file file, + int64_t offset, uv_fs_cb cb); + UV_EXTERN int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, off_t in_offset, size_t length, uv_fs_cb cb); diff --git a/src/win/fs.c b/src/win/fs.c index 507336ee..daa23ed1 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -33,12 +33,11 @@ #include "uv.h" #include "internal.h" -#define UV_FS_ASYNC_QUEUED 0x0001 -#define UV_FS_FREE_ARG0 0x0002 -#define UV_FS_FREE_ARG1 0x0004 -#define UV_FS_FREE_PTR 0x0008 -#define UV_FS_CLEANEDUP 0x0010 - +#define UV_FS_ASYNC_QUEUED 0x0001 +#define UV_FS_FREE_ARG0 0x0002 +#define UV_FS_FREE_ARG1 0x0004 +#define UV_FS_FREE_PTR 0x0008 +#define UV_FS_CLEANEDUP 0x0010 #define UTF8_TO_UTF16(s, t) \ size = uv_utf8_to_utf16(s, NULL, 0) * sizeof(wchar_t); \ @@ -289,7 +288,7 @@ void fs__close(uv_fs_t* req, uv_file file) { void fs__read(uv_fs_t* req, uv_file file, void *buf, size_t length, - off_t offset) { + int64_t offset) { HANDLE handle; OVERLAPPED overlapped, *overlapped_ptr; LARGE_INTEGER offset_; @@ -335,7 +334,7 @@ void fs__read(uv_fs_t* req, uv_file file, void *buf, size_t length, void fs__write(uv_fs_t* req, uv_file file, void *buf, size_t length, - off_t offset) { + int64_t offset) { HANDLE handle; OVERLAPPED overlapped, *overlapped_ptr; LARGE_INTEGER offset_; @@ -597,12 +596,12 @@ void fs__fsync(uv_fs_t* req, uv_file file) { } -void fs__ftruncate(uv_fs_t* req, uv_file file, off_t offset) { +void fs__ftruncate(uv_fs_t* req, uv_file file, int64_t offset) { int result; VERIFY_UV_FILE(file, req); - result = _chsize(file, offset); + result = _chsize_s(file, offset); SET_REQ_RESULT(req, result); } @@ -878,14 +877,14 @@ static DWORD WINAPI uv_fs_thread_proc(void* parameter) { (uv_file) req->arg0, req->arg1, (size_t) req->arg2, - (off_t) req->arg3); + req->stat.st_atime); break; case UV_FS_WRITE: fs__write(req, (uv_file)req->arg0, req->arg1, (size_t) req->arg2, - (off_t) req->arg3); + req->stat.st_atime); break; case UV_FS_UNLINK: fs__unlink(req, req->pathw); @@ -914,7 +913,7 @@ static DWORD WINAPI uv_fs_thread_proc(void* parameter) { fs__fsync(req, (uv_file)req->arg0); break; case UV_FS_FTRUNCATE: - fs__ftruncate(req, (uv_file)req->arg0, (off_t)req->arg1); + fs__ftruncate(req, (uv_file)req->arg0, (off_t)req->stat.st_atime); break; case UV_FS_SENDFILE: fs__sendfile(req, @@ -1002,7 +1001,26 @@ int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, size_t length, off_t offset, uv_fs_cb cb) { if (cb) { uv_fs_req_init_async(loop, req, UV_FS_READ, NULL, NULL, cb); - WRAP_REQ_ARGS4(req, file, buf, length, offset); + WRAP_REQ_ARGS3(req, file, buf, length); + req->stat.st_atime = offset; + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_READ); + fs__read(req, file, buf, length, offset); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_read64(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, + size_t length, int64_t offset, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_READ, NULL, NULL, cb); + WRAP_REQ_ARGS3(req, file, buf, length); + req->stat.st_atime = offset; QUEUE_FS_TP_JOB(loop, req); } else { uv_fs_req_init_sync(loop, req, UV_FS_READ); @@ -1019,7 +1037,26 @@ int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, size_t length, off_t offset, uv_fs_cb cb) { if (cb) { uv_fs_req_init_async(loop, req, UV_FS_WRITE, NULL, NULL, cb); - WRAP_REQ_ARGS4(req, file, buf, length, offset); + WRAP_REQ_ARGS3(req, file, buf, length); + req->stat.st_atime = offset; + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_WRITE); + fs__write(req, file, buf, length, offset); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_write64(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, + size_t length, int64_t offset, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_WRITE, NULL, NULL, cb); + WRAP_REQ_ARGS3(req, file, buf, length); + req->stat.st_atime = offset; QUEUE_FS_TP_JOB(loop, req); } else { uv_fs_req_init_sync(loop, req, UV_FS_WRITE); @@ -1412,7 +1449,26 @@ int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file, off_t offset, uv_fs_cb cb) { if (cb) { uv_fs_req_init_async(loop, req, UV_FS_FTRUNCATE, NULL, NULL, cb); - WRAP_REQ_ARGS2(req, file, offset); + WRAP_REQ_ARGS1(req, file); + req->stat.st_atime = offset; + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_FTRUNCATE); + fs__ftruncate(req, file, offset); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_ftruncate64(uv_loop_t* loop, uv_fs_t* req, uv_file file, + int64_t offset, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_FTRUNCATE, NULL, NULL, cb); + WRAP_REQ_ARGS1(req, file); + req->stat.st_atime = offset; QUEUE_FS_TP_JOB(loop, req); } else { uv_fs_req_init_sync(loop, req, UV_FS_FTRUNCATE); diff --git a/test/test-fs.c b/test/test-fs.c index 8dc71473..73d01382 100644 --- a/test/test-fs.c +++ b/test/test-fs.c @@ -248,6 +248,11 @@ static void read_cb(uv_fs_t* req) { read_cb_count++; uv_fs_req_cleanup(req); if (read_cb_count == 1) { + ASSERT(strcmp(buf, test_buf) == 0); + memset(buf, 0, sizeof(buf)); + r = uv_fs_read64(loop, &read_req, open_req1.result, buf, sizeof(buf), 0, + read_cb); + } else if (read_cb_count == 2) { ASSERT(strcmp(buf, test_buf) == 0); r = uv_fs_ftruncate(loop, &ftruncate_req, open_req1.result, 7, ftruncate_cb); @@ -319,7 +324,13 @@ static void write_cb(uv_fs_t* req) { ASSERT(req->result != -1); write_cb_count++; uv_fs_req_cleanup(req); - r = uv_fs_fdatasync(loop, &fdatasync_req, open_req1.result, fdatasync_cb); + + if (write_cb_count == 1) { + r = uv_fs_write64(loop, &write_req, open_req1.result, test_buf, sizeof(test_buf), + -1, write_cb); + } else { + r = uv_fs_fdatasync(loop, &fdatasync_req, open_req1.result, fdatasync_cb); + } } @@ -596,7 +607,7 @@ TEST_IMPL(fs_file_async) { uv_run(loop); ASSERT(create_cb_count == 1); - ASSERT(write_cb_count == 1); + ASSERT(write_cb_count == 2); ASSERT(fsync_cb_count == 1); ASSERT(fdatasync_cb_count == 1); ASSERT(close_cb_count == 1); @@ -606,7 +617,7 @@ TEST_IMPL(fs_file_async) { uv_run(loop); ASSERT(create_cb_count == 1); - ASSERT(write_cb_count == 1); + ASSERT(write_cb_count == 2); ASSERT(close_cb_count == 1); ASSERT(rename_cb_count == 1); @@ -615,11 +626,11 @@ TEST_IMPL(fs_file_async) { uv_run(loop); ASSERT(open_cb_count == 1); - ASSERT(read_cb_count == 1); + ASSERT(read_cb_count == 2); ASSERT(close_cb_count == 2); ASSERT(rename_cb_count == 1); ASSERT(create_cb_count == 1); - ASSERT(write_cb_count == 1); + ASSERT(write_cb_count == 2); ASSERT(ftruncate_cb_count == 1); r = uv_fs_open(loop, &open_req1, "test_file2", O_RDONLY, 0, open_cb); @@ -627,12 +638,12 @@ TEST_IMPL(fs_file_async) { uv_run(loop); ASSERT(open_cb_count == 2); - ASSERT(read_cb_count == 2); + ASSERT(read_cb_count == 3); ASSERT(close_cb_count == 3); ASSERT(rename_cb_count == 1); ASSERT(unlink_cb_count == 1); ASSERT(create_cb_count == 1); - ASSERT(write_cb_count == 1); + ASSERT(write_cb_count == 2); ASSERT(ftruncate_cb_count == 1); /* Cleanup. */ @@ -681,6 +692,14 @@ TEST_IMPL(fs_file_sync) { ASSERT(strcmp(buf, test_buf) == 0); uv_fs_req_cleanup(&read_req); + memset(buf, 0, sizeof(buf)); + r = uv_fs_read64(loop, &read_req, open_req1.result, buf, sizeof(buf), 0, + NULL); + ASSERT(r != -1); + ASSERT(read_req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + uv_fs_req_cleanup(&read_req); + r = uv_fs_ftruncate(loop, &ftruncate_req, open_req1.result, 7, NULL); ASSERT(r != -1); ASSERT(ftruncate_req.result != -1); @@ -899,7 +918,7 @@ TEST_IMPL(fs_fstat) { file = req.result; uv_fs_req_cleanup(&req); - r = uv_fs_write(loop, &req, file, test_buf, sizeof(test_buf), -1, NULL); + r = uv_fs_write64(loop, &req, file, test_buf, sizeof(test_buf), -1, NULL); ASSERT(r == sizeof(test_buf)); ASSERT(req.result == sizeof(test_buf)); uv_fs_req_cleanup(&req); From d68b3d960b6d95bfc16027cecca2f3fa48bcc36f Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 6 Apr 2012 01:47:16 +0200 Subject: [PATCH 02/15] unix: add uv_fs_read64, uv_fs_write64 and uv_fs_ftruncate64 --- src/unix/fs.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/unix/fs.c b/src/unix/fs.c index 436e54c6..ddcb8b5d 100644 --- a/src/unix/fs.c +++ b/src/unix/fs.c @@ -701,3 +701,34 @@ int uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, return 0; } + + +int uv_fs_read64(uv_loop_t* loop, + uv_fs_t* req, + uv_file file, + void* buf, + size_t length, + int64_t offset, + uv_fs_cb cb) { + return uv_fs_read(loop, req, file, buf, length, offset, cb); +} + + +int uv_fs_write64(uv_loop_t* loop, + uv_fs_t* req, + uv_file file, + void* buf, + size_t length, + int64_t offset, + uv_fs_cb cb) { + return uv_fs_write(loop, req, file, buf, length, offset, cb); +} + + +int uv_fs_ftruncate64(uv_loop_t* loop, + uv_fs_t* req, + uv_file file, + int64_t offset, + uv_fs_cb cb) { + return uv_fs_ftruncate(loop, req, file, offset, cb); +} From f9fcaf5ccf60486e82745ccf8321c98c966caacd Mon Sep 17 00:00:00 2001 From: isaacs Date: Fri, 20 Apr 2012 08:16:55 -0700 Subject: [PATCH 03/15] Map ENOMEM to UV_ENOMEM --- src/unix/error.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/unix/error.c b/src/unix/error.c index d99d4112..703b5fde 100644 --- a/src/unix/error.c +++ b/src/unix/error.c @@ -91,6 +91,7 @@ uv_err_code uv_translate_sys_error(int sys_errno) { case EBUSY: return UV_EBUSY; case ENOTEMPTY: return UV_ENOTEMPTY; case ENOSPC: return UV_ENOSPC; + case ENOMEM: return UV_ENOMEM; default: return UV_UNKNOWN; } From 73cc3677fcdcef507154cc5860e3d0db1faa0c17 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 21 Apr 2012 05:53:14 +0200 Subject: [PATCH 04/15] unix: map EWOULDBLOCK to UV_EAGAIN --- src/unix/error.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/unix/error.c b/src/unix/error.c index 703b5fde..e01d06ac 100644 --- a/src/unix/error.c +++ b/src/unix/error.c @@ -68,6 +68,9 @@ uv_err_code uv_translate_sys_error(int sys_errno) { case EBADF: return UV_EBADF; case EPIPE: return UV_EPIPE; case EAGAIN: return UV_EAGAIN; +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: return UV_EAGAIN; +#endif case ECONNRESET: return UV_ECONNRESET; case EFAULT: return UV_EFAULT; case EMFILE: return UV_EMFILE; From df22d5935eb0fd44e2c6e3773308fcebb0ab646c Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 21 Apr 2012 05:52:38 +0200 Subject: [PATCH 05/15] unix: handle EWOULDBLOCK in uv__server_io() --- src/unix/stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unix/stream.c b/src/unix/stream.c index eee4199f..49881b94 100644 --- a/src/unix/stream.c +++ b/src/unix/stream.c @@ -176,7 +176,7 @@ void uv__server_io(EV_P_ ev_io* watcher, int revents) { fd = uv__accept(stream->fd, (struct sockaddr*)&addr, sizeof addr); if (fd < 0) { - if (errno == EAGAIN) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { /* No problem. */ return; } else if (errno == EMFILE) { From 01cc491853ab0084b4f4dacf5ab0ca0580428ad4 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 21 Apr 2012 05:56:43 +0200 Subject: [PATCH 06/15] unix: handle EWOULDBLOCK in uv__write() --- src/unix/stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unix/stream.c b/src/unix/stream.c index 49881b94..bb63dded 100644 --- a/src/unix/stream.c +++ b/src/unix/stream.c @@ -416,7 +416,7 @@ start: } if (n < 0) { - if (errno != EAGAIN) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { /* Error */ req->error = errno; stream->write_queue_size -= uv__write_req_size(req); From cf6dc082caf61c46689341e9c515f7ae81ffbce2 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 21 Apr 2012 05:57:49 +0200 Subject: [PATCH 07/15] unix: handle EWOULDBLOCK in uv__read() --- src/unix/stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unix/stream.c b/src/unix/stream.c index bb63dded..6f0d1378 100644 --- a/src/unix/stream.c +++ b/src/unix/stream.c @@ -562,7 +562,7 @@ static void uv__read(uv_stream_t* stream) { if (nread < 0) { /* Error */ - if (errno == EAGAIN) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { /* Wait for the next one. */ if (stream->flags & UV_READING) { ev_io_start(ev, &stream->read_watcher); From 21bee8c36f0f9086efd457ffcc22b17c5abab2c8 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 21 Apr 2012 06:06:06 +0200 Subject: [PATCH 08/15] unix: call pipe handle connection cb on accept() error --- src/unix/pipe.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/unix/pipe.c b/src/unix/pipe.c index f1be9e97..1efb664b 100644 --- a/src/unix/pipe.c +++ b/src/unix/pipe.c @@ -254,16 +254,15 @@ void uv__pipe_accept(EV_P_ ev_io* watcher, int revents) { sockfd = uv__accept(pipe->fd, (struct sockaddr *)&saddr, sizeof saddr); if (sockfd == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - assert(0 && "EAGAIN on uv__accept(pipefd)"); - } else { + if (errno != EAGAIN && errno != EWOULDBLOCK) { uv__set_sys_error(pipe->loop, errno); + pipe->connection_cb((uv_stream_t*)pipe, -1); } } else { pipe->accepted_fd = sockfd; pipe->connection_cb((uv_stream_t*)pipe, 0); if (pipe->accepted_fd == sockfd) { - /* The user hasn't yet accepted called uv_accept() */ + /* The user hasn't called uv_accept() yet */ ev_io_stop(pipe->loop->ev, &pipe->read_watcher); } } From e6d4bca2ecb417b9654f637b59f35669ebf85fe2 Mon Sep 17 00:00:00 2001 From: Igor Zinkovsky Date: Mon, 23 Apr 2012 10:52:53 -0700 Subject: [PATCH 09/15] remove left-over cast fixes #3160 --- src/win/fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/win/fs.c b/src/win/fs.c index daa23ed1..9e8a9951 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -913,7 +913,7 @@ static DWORD WINAPI uv_fs_thread_proc(void* parameter) { fs__fsync(req, (uv_file)req->arg0); break; case UV_FS_FTRUNCATE: - fs__ftruncate(req, (uv_file)req->arg0, (off_t)req->stat.st_atime); + fs__ftruncate(req, (uv_file)req->arg0, req->stat.st_atime); break; case UV_FS_SENDFILE: fs__sendfile(req, From 06ae804e03c69c246edfa5edef5b65d915e55586 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 25 Apr 2012 01:26:41 +0200 Subject: [PATCH 10/15] linux: add IN_MOVE_SELF to inotify event mask Partially fixes joyent/node#3172, behavior is now consistent with inotifywait. --- src/unix/linux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/unix/linux.c b/src/unix/linux.c index 809e644d..a01d14b7 100644 --- a/src/unix/linux.c +++ b/src/unix/linux.c @@ -271,6 +271,7 @@ int uv_fs_event_init(uv_loop_t* loop, | IN_MODIFY | IN_DELETE | IN_DELETE_SELF + | IN_MOVE_SELF | IN_MOVED_FROM | IN_MOVED_TO; From c42ba103b7745fdce3ec7dfc6a772f935b3f8f7e Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Fri, 27 Apr 2012 17:41:56 +0200 Subject: [PATCH 11/15] Temporary API to support spawn with uid and gid options --- include/uv-private/uv-unix.h | 5 +++++ include/uv-private/uv-win.h | 4 ++++ include/uv.h | 28 ++++++++++++++++++++++++++++ src/unix/process.c | 20 ++++++++++++++++++-- src/uv-common.c | 26 ++++++++++++++++++++++++++ src/win/process.c | 15 ++++++++++----- 6 files changed, 91 insertions(+), 7 deletions(-) diff --git a/include/uv-private/uv-unix.h b/include/uv-private/uv-unix.h index 21078fe3..1c33684a 100644 --- a/include/uv-private/uv-unix.h +++ b/include/uv-private/uv-unix.h @@ -33,6 +33,7 @@ #include #include #include +#include #include /* Note: May be cast to struct iovec. See writev(2). */ @@ -43,6 +44,10 @@ typedef struct { typedef int uv_file; +/* Platform-specific definitions for uv_spawn support. */ +typedef gid_t uv_gid_t; +typedef uid_t uv_uid_t; + /* Platform-specific definitions for uv_dlopen support. */ typedef void* uv_lib_t; #define UV_DYNAMIC /* empty */ diff --git a/include/uv-private/uv-win.h b/include/uv-private/uv-win.h index e620f8b0..4d8ac9c6 100644 --- a/include/uv-private/uv-win.h +++ b/include/uv-private/uv-win.h @@ -137,6 +137,10 @@ typedef struct uv_buf_t { typedef int uv_file; +/* Platform-specific definitions for uv_spawn support. */ +typedef unsigned char uv_uid_t; +typedef unsigned char uv_gid_t; + /* Platform-specific definitions for uv_dlopen support. */ typedef HMODULE uv_lib_t; #define UV_DYNAMIC FAR WINAPI diff --git a/include/uv.h b/include/uv.h index ed6100ae..c6f15e84 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1058,6 +1058,34 @@ struct uv_process_s { UV_EXTERN int uv_spawn(uv_loop_t*, uv_process_t*, uv_process_options_t options); + +/* Temporary fix for node. Do no use. */ +enum uv_process_flags { + UV_PROCESS_SETUID = (1 << 0), + UV_PROCESS_SETGID = (1 << 1), + UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = (1 << 2) +}; + +/* Temporary fix for node. Do not use. */ +typedef struct uv_process_options2_s { + uv_exit_cb exit_cb; /* Called after the process exits. */ + const char* file; /* Path to program to execute. */ + char** args; + char** env; + char* cwd; + unsigned int flags; + uv_pipe_t* stdin_stream; + uv_pipe_t* stdout_stream; + uv_pipe_t* stderr_stream; + uv_uid_t uid; + uv_gid_t gid; +} uv_process_options2_t; + +/* Temporary fix for node. Do not use. */ +UV_EXTERN int uv_spawn2(uv_loop_t*, uv_process_t*, + uv_process_options2_t options); + + /* * Kills the process with the specified signal. The user must still * call uv_close on the process. diff --git a/src/unix/process.c b/src/unix/process.c index 5581d8b8..89c2c77c 100644 --- a/src/unix/process.c +++ b/src/unix/process.c @@ -161,8 +161,8 @@ static int uv__process_init_pipe(uv_pipe_t* handle, int fds[2], int flags) { # define SPAWN_WAIT_EXEC 1 #endif -int uv_spawn(uv_loop_t* loop, uv_process_t* process, - uv_process_options_t options) { +int uv_spawn2(uv_loop_t* loop, uv_process_t* process, + uv_process_options2_t options) { /* * Save environ in the case that we get it clobbered * by the child process. @@ -179,6 +179,12 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, pid_t pid; int flags; + assert(options.file != NULL); + assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS | + UV_PROCESS_SETGID | + UV_PROCESS_SETUID))); + + uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS); loop->counters.process_init++; @@ -268,6 +274,16 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, _exit(127); } + if ((options.flags & UV_PROCESS_SETGID) && setgid(options.gid)) { + perror("setgid()"); + _exit(127); + } + + if ((options.flags & UV_PROCESS_SETUID) && setuid(options.uid)) { + perror("setuid()"); + _exit(127); + } + environ = options.env; execvp(options.file, options.args); diff --git a/src/uv-common.c b/src/uv-common.c index 3143bd2d..c5172f62 100644 --- a/src/uv-common.c +++ b/src/uv-common.c @@ -261,3 +261,29 @@ int uv_tcp_connect6(uv_connect_t* req, return uv__tcp_connect6(req, handle, address, cb); } + + +/* Thunk that converts uv_process_options_t into uv_process_options2_t, */ +/* and then calls uv_spawn2. */ +int uv_spawn(uv_loop_t* loop, uv_process_t* process, + uv_process_options_t options) { + uv_process_options2_t options2; + + options2.exit_cb = options.exit_cb; + options2.file = options.file; + options2.args = options.args; + options2.cwd = options.cwd; + options2.env = options.env; + options2.stdin_stream = options.stdin_stream; + options2.stdout_stream = options.stdout_stream; + options2.stderr_stream = options.stderr_stream; + + options2.flags = 0; + if (options.windows_verbatim_arguments) { + options2.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; + } + + /* No need to set gid and uid. */ + + return uv_spawn2(loop, process, options2); +} diff --git a/src/win/process.c b/src/win/process.c index a23ba0b1..71620a00 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -860,8 +860,8 @@ static int duplicate_std_handle(uv_loop_t* loop, DWORD id, HANDLE* dup) { } -int uv_spawn(uv_loop_t* loop, uv_process_t* process, - uv_process_options_t options) { +int uv_spawn2(uv_loop_t* loop, uv_process_t* process, + uv_process_options2_t options) { int err = 0, keep_child_stdio_open = 0; wchar_t* path = NULL; int size; @@ -872,17 +872,22 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, STARTUPINFOW startup; PROCESS_INFORMATION info; - if (!options.file) { - uv__set_artificial_error(loop, UV_EINVAL); + if (options.flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) { + uv__set_sys_error(loop, UV_ENOTSUP); return -1; } + assert(options.file != NULL); + assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS | + UV_PROCESS_SETGID | + UV_PROCESS_SETUID))); + uv_process_init(loop, process); process->exit_cb = options.exit_cb; UTF8_TO_UTF16(options.file, application); arguments = options.args ? make_program_args(options.args, - options.windows_verbatim_arguments) : NULL; + options.flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS) : NULL; env = options.env ? make_program_env(options.env) : NULL; if (options.cwd) { From 66647bf934760b2bb5e498169e462d9cc9a62fd6 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Fri, 27 Apr 2012 21:05:15 +0200 Subject: [PATCH 12/15] Test for setuid/setgid --- test/test-list.h | 10 ++- test/test-spawn.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+), 1 deletion(-) diff --git a/test/test-list.h b/test/test-list.h index 5aad6331..8c6e9f11 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -110,6 +110,8 @@ TEST_DECLARE (spawn_stdout) TEST_DECLARE (spawn_stdin) TEST_DECLARE (spawn_and_kill) TEST_DECLARE (spawn_and_ping) +TEST_DECLARE (spawn_setuid_fails) +TEST_DECLARE (spawn_setgid_fails) TEST_DECLARE (kill) TEST_DECLARE (fs_file_noent) TEST_DECLARE (fs_file_nametoolong) @@ -133,7 +135,7 @@ TEST_DECLARE (fs_event_watch_file_current_dir) TEST_DECLARE (fs_event_no_callback_on_close) TEST_DECLARE (fs_event_immediate_close) TEST_DECLARE (fs_event_close_with_pending_event) -TEST_DECLARE (fs_event_close_in_callback); +TEST_DECLARE (fs_event_close_in_callback) TEST_DECLARE (fs_readdir_empty_dir) TEST_DECLARE (fs_readdir_file) TEST_DECLARE (fs_open_dir) @@ -147,6 +149,8 @@ TEST_DECLARE (environment_creation) TEST_DECLARE (listen_with_simultaneous_accepts) TEST_DECLARE (listen_no_simultaneous_accepts) TEST_DECLARE (fs_stat_root) +#else +TEST_DECLARE (spawn_setuid_setgid) #endif HELPER_DECLARE (tcp4_echo_server) HELPER_DECLARE (tcp6_echo_server) @@ -289,6 +293,8 @@ TASK_LIST_START TEST_ENTRY (spawn_stdin) TEST_ENTRY (spawn_and_kill) TEST_ENTRY (spawn_and_ping) + TEST_ENTRY (spawn_setuid_fails) + TEST_ENTRY (spawn_setgid_fails) TEST_ENTRY (kill) #ifdef _WIN32 TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows) @@ -297,6 +303,8 @@ TASK_LIST_START TEST_ENTRY (listen_with_simultaneous_accepts) TEST_ENTRY (listen_no_simultaneous_accepts) TEST_ENTRY (fs_stat_root) +#else + TEST_ENTRY (spawn_setuid_setgid) #endif TEST_ENTRY (fs_file_noent) diff --git a/test/test-spawn.c b/test/test-spawn.c index 68720114..69060517 100644 --- a/test/test-spawn.c +++ b/test/test-spawn.c @@ -30,6 +30,7 @@ static int exit_cb_called; static uv_process_t process; static uv_timer_t timer; static uv_process_options_t options; +static uv_process_options2_t options2; static char exepath[1024]; static size_t exepath_size = 1024; static char* args[3]; @@ -55,6 +56,22 @@ static void exit_cb(uv_process_t* process, int exit_status, int term_signal) { } +static void exit_cb_failure_expected(uv_process_t* process, int exit_status, + int term_signal) { + printf("exit_cb\n"); + exit_cb_called++; + ASSERT(exit_status == 127); + ASSERT(term_signal == 0); + uv_close((uv_handle_t*)process, close_cb); +} + + +static void exit_cb_unexpected(uv_process_t* process, int exit_status, + int term_signal) { + ASSERT(0 && "should not have been called"); +} + + static void kill_cb(uv_process_t* process, int exit_status, int term_signal) { uv_err_t err; @@ -116,6 +133,22 @@ static void init_process_options(char* test, uv_exit_cb exit_cb) { options.file = exepath; options.args = args; options.exit_cb = exit_cb; + options.windows_verbatim_arguments = 0; +} + + +static void init_process_options2(char* test, uv_exit_cb exit_cb) { + /* Note spawn_helper1 defined in test/run-tests.c */ + int r = uv_exepath(exepath, &exepath_size); + ASSERT(r == 0); + exepath[exepath_size] = '\0'; + args[0] = exepath; + args[1] = test; + args[2] = NULL; + options2.file = exepath; + options2.args = args; + options2.exit_cb = exit_cb; + options2.flags = 0; } @@ -466,3 +499,143 @@ TEST_IMPL(environment_creation) { return 0; } #endif + +#ifndef _WIN32 +TEST_IMPL(spawn_setuid_setgid) { + int r; + + /* if not root, then this will fail. */ + uv_uid_t uid = getuid(); + if (uid != 0) { + fprintf(stderr, "spawn_setuid_setgid skipped: not root\n"); + return 0; + } + + init_process_options2("spawn_helper1", exit_cb); + + // become the "nobody" user. + struct passwd* pw; + pw = getpwnam("nobody"); + ASSERT(pw != NULL); + options2.uid = pw->pw_uid; + options2.gid = pw->pw_gid; + options2.flags = UV_PROCESS_SETUID | UV_PROCESS_SETGID; + + r = uv_spawn2(uv_default_loop(), &process, options2); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + return 0; +} +#endif + + +#ifndef _WIN32 +TEST_IMPL(spawn_setuid_fails) { + int r; + + /* if root, become nobody. */ + uv_uid_t uid = getuid(); + if (uid == 0) { + struct passwd* pw; + pw = getpwnam("nobody"); + ASSERT(pw != NULL); + r = setuid(pw->pw_uid); + ASSERT(r == 0); + } + + init_process_options2("spawn_helper1", exit_cb_failure_expected); + + options2.flags |= UV_PROCESS_SETUID; + options2.uid = (uv_uid_t) -42424242; + + r = uv_spawn2(uv_default_loop(), &process, options2); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + return 0; +} + + +TEST_IMPL(spawn_setgid_fails) { + int r; + + /* if root, become nobody. */ + uv_uid_t uid = getuid(); + if (uid == 0) { + struct passwd* pw; + pw = getpwnam("nobody"); + ASSERT(pw != NULL); + r = setuid(pw->pw_uid); + ASSERT(r == 0); + } + + init_process_options2("spawn_helper1", exit_cb_failure_expected); + + options2.flags |= UV_PROCESS_SETGID; + options2.gid = (uv_gid_t) -42424242; + + r = uv_spawn2(uv_default_loop(), &process, options2); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + return 0; +} +#endif + + +#ifdef _WIN32 +TEST_IMPL(spawn_setuid_fails) { + int r; + + init_process_options2("spawn_helper1", exit_cb_unexpected); + + options2.flags |= UV_PROCESS_SETUID; + options2.uid = (uv_uid_t) -42424242; + + r = uv_spawn2(uv_default_loop(), &process, options2); + ASSERT(r == -1); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + + return 0; +} + + +TEST_IMPL(spawn_setgid_fails) { + int r; + + init_process_options2("spawn_helper1", exit_cb_unexpected); + + options2.flags |= UV_PROCESS_SETGID; + options2.gid = (uv_gid_t) -42424242; + + r = uv_spawn2(uv_default_loop(), &process, options2); + ASSERT(r == -1); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + + return 0; +} +#endif From aea5db5da1483d1ab0a1d3e4f622df312202d027 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Fri, 27 Apr 2012 21:24:27 +0200 Subject: [PATCH 13/15] Windows: add mappings for UV_ENOENT --- src/win/error.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/win/error.c b/src/win/error.c index 7bdc3cda..e818fc5c 100644 --- a/src/win/error.c +++ b/src/win/error.c @@ -68,6 +68,8 @@ uv_err_code uv_translate_sys_error(int sys_errno) { switch (sys_errno) { case ERROR_SUCCESS: return UV_OK; case ERROR_FILE_NOT_FOUND: return UV_ENOENT; + case ERROR_INVALID_NAME: return UV_ENOENT; + case ERROR_MOD_NOT_FOUND: return UV_ENOENT; case ERROR_PATH_NOT_FOUND: return UV_ENOENT; case ERROR_ACCESS_DENIED: return UV_EPERM; case ERROR_NOACCESS: return UV_EACCES; From d41cc9118df383ad8fb6007f4b175677c09e42f0 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Fri, 27 Apr 2012 21:58:44 +0200 Subject: [PATCH 14/15] Windows: uv_spawn2 reports the wrong error when setuid/setgid is specified --- src/win/process.c | 2 +- test/test-spawn.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/win/process.c b/src/win/process.c index 71620a00..b89ab812 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -873,7 +873,7 @@ int uv_spawn2(uv_loop_t* loop, uv_process_t* process, PROCESS_INFORMATION info; if (options.flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) { - uv__set_sys_error(loop, UV_ENOTSUP); + uv__set_artificial_error(loop, UV_ENOTSUP); return -1; } diff --git a/test/test-spawn.c b/test/test-spawn.c index 69060517..c79602ad 100644 --- a/test/test-spawn.c +++ b/test/test-spawn.c @@ -610,6 +610,7 @@ TEST_IMPL(spawn_setuid_fails) { r = uv_spawn2(uv_default_loop(), &process, options2); ASSERT(r == -1); + ASSERT(uv_last_error(uv_default_loop()).code == UV_ENOTSUP); r = uv_run(uv_default_loop()); ASSERT(r == 0); @@ -630,6 +631,7 @@ TEST_IMPL(spawn_setgid_fails) { r = uv_spawn2(uv_default_loop(), &process, options2); ASSERT(r == -1); + ASSERT(uv_last_error(uv_default_loop()).code == UV_ENOTSUP); r = uv_run(uv_default_loop()); ASSERT(r == 0); From e2b6f423cbb18607bb38a59ccf6e7dee61c10764 Mon Sep 17 00:00:00 2001 From: Erik Dubbelboer Date: Sat, 28 Apr 2012 18:16:11 +0200 Subject: [PATCH 15/15] test-spawn failed to build on linux - added missing header file - converted c++ style comment to c style comment --- test/test-spawn.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/test-spawn.c b/test/test-spawn.c index c79602ad..6c29fa76 100644 --- a/test/test-spawn.c +++ b/test/test-spawn.c @@ -25,6 +25,11 @@ #include #include +#ifndef _WIN32 +#include +#endif + + static int close_cb_called; static int exit_cb_called; static uv_process_t process; @@ -513,7 +518,7 @@ TEST_IMPL(spawn_setuid_setgid) { init_process_options2("spawn_helper1", exit_cb); - // become the "nobody" user. + /* become the "nobody" user. */ struct passwd* pw; pw = getpwnam("nobody"); ASSERT(pw != NULL);