From e9235a39f85e3f3bf9b907dcf3a9240b88a77e05 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 18 Dec 2011 16:47:06 +0100 Subject: [PATCH 1/7] unix: the dl functions don't set errno --- src/unix/dl.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/unix/dl.c b/src/unix/dl.c index 6c4ddff8..01f51d3e 100644 --- a/src/unix/dl.c +++ b/src/unix/dl.c @@ -25,11 +25,17 @@ #include #include +/* The dl family of functions don't set errno. We need a good way to communicate + * errors to the caller but there is only dlerror() and that returns a string - + * a string that may or may not be safe to keep a reference to... + */ +static const uv_err_t uv_inval_ = { UV_EINVAL, EINVAL }; + uv_err_t uv_dlopen(const char* filename, uv_lib_t* library) { void* handle = dlopen(filename, RTLD_LAZY); if (handle == NULL) { - return uv__new_sys_error(errno); + return uv_inval_; } *library = handle; @@ -39,7 +45,7 @@ uv_err_t uv_dlopen(const char* filename, uv_lib_t* library) { uv_err_t uv_dlclose(uv_lib_t library) { if (dlclose(library) != 0) { - return uv__new_sys_error(errno); + return uv_inval_; } return uv_ok_; @@ -49,7 +55,7 @@ uv_err_t uv_dlclose(uv_lib_t library) { uv_err_t uv_dlsym(uv_lib_t library, const char* name, void** ptr) { void* address = dlsym(library, name); if (address == NULL) { - return uv__new_sys_error(errno); + return uv_inval_; } *ptr = (void*) address; From feb267e61184887bb0cd1da09e62762ef0bbe9a4 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 18 Dec 2011 16:49:37 +0100 Subject: [PATCH 2/7] unix: it's legal for dlsym() to return NULL A symbol name can map to NULL. Check dlerror() to see if a real error happened. --- include/uv.h | 3 ++- src/unix/dl.c | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/uv.h b/include/uv.h index e8b70b18..a2007087 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1250,7 +1250,8 @@ UV_EXTERN uv_err_t uv_dlopen(const char* filename, uv_lib_t* library); UV_EXTERN uv_err_t uv_dlclose(uv_lib_t library); /* - * Retrieves a data pointer from a dynamic library. + * Retrieves a data pointer from a dynamic library. It is legal for a symbol to + * map to NULL. */ UV_EXTERN uv_err_t uv_dlsym(uv_lib_t library, const char* name, void** ptr); diff --git a/src/unix/dl.c b/src/unix/dl.c index 01f51d3e..41c244d7 100644 --- a/src/unix/dl.c +++ b/src/unix/dl.c @@ -53,8 +53,14 @@ uv_err_t uv_dlclose(uv_lib_t library) { uv_err_t uv_dlsym(uv_lib_t library, const char* name, void** ptr) { - void* address = dlsym(library, name); - if (address == NULL) { + void* address; + + /* Reset error status. */ + dlerror(); + + address = dlsym(library, name); + + if (dlerror()) { return uv_inval_; } From 39481b78963cbb272d0a3199afc4bedf4117af07 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Thu, 22 Dec 2011 17:19:22 +0100 Subject: [PATCH 3/7] docs: add c-ares license note --- LICENSE | 2 ++ 1 file changed, 2 insertions(+) diff --git a/LICENSE b/LICENSE index f7df47f7..f62d7f19 100644 --- a/LICENSE +++ b/LICENSE @@ -33,6 +33,8 @@ The externally maintained libraries used by libuv are: - ngx_queue.h (from Nginx), copyright Igor Sysoev. Two clause BSD license. + - c-ares, copyright Daniel Stenberg and others. MIT licensed. + - libev, located at ev/ is copyright Marc Alexander Lehmann, and dual-licensed under the MIT license and GPL2. From 4d1d02fd18060017a63b1baf555e360245363b83 Mon Sep 17 00:00:00 2001 From: Igor Zinkovsky Date: Mon, 26 Dec 2011 14:01:48 -0800 Subject: [PATCH 4/7] windows: ensure that uv_fs_event callbacks have long names --- include/uv-private/uv-win.h | 2 +- src/win/fs-event.c | 145 +++++++++++++++++++++++++++++------- 2 files changed, 118 insertions(+), 29 deletions(-) diff --git a/include/uv-private/uv-win.h b/include/uv-private/uv-win.h index 7c1818cb..ea770132 100644 --- a/include/uv-private/uv-win.h +++ b/include/uv-private/uv-win.h @@ -414,7 +414,7 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); uv_fs_event_cb cb; \ wchar_t* filew; \ wchar_t* short_filew; \ - int is_path_dir; \ + wchar_t* dirw; \ char* buffer; int uv_utf16_to_utf8(const wchar_t* utf16Buffer, size_t utf16Size, diff --git a/src/win/fs-event.c b/src/win/fs-event.c index b2f6583a..5a25e9d6 100644 --- a/src/win/fs-event.c +++ b/src/win/fs-event.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "uv.h" #include "internal.h" @@ -36,12 +37,12 @@ static void uv_fs_event_init_handle(uv_loop_t* loop, uv_fs_event_t* handle, handle->loop = loop; handle->flags = 0; handle->cb = cb; - handle->is_path_dir = 0; handle->dir_handle = INVALID_HANDLE_VALUE; handle->buffer = NULL; handle->req_pending = 0; handle->filew = NULL; handle->short_filew = NULL; + handle->dirw = NULL; uv_req_init(loop, (uv_req_t*)&handle->req); handle->req.type = UV_FS_EVENT_REQ; @@ -134,9 +135,9 @@ static int uv_split_path(const wchar_t* filename, wchar_t** dir, int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle, const char* filename, uv_fs_event_cb cb, int flags) { - int name_size; + int name_size, is_path_dir; DWORD attr, last_error; - wchar_t* dir = NULL, *dir_to_watch, *filenamew; + wchar_t* dir = NULL, *dir_to_watch, *filenamew = NULL; wchar_t short_path[MAX_PATH]; /* We don't support any flags yet. */ @@ -164,10 +165,11 @@ int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle, goto error; } - handle->is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0; + is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0; - if (handle->is_path_dir) { + if (is_path_dir) { /* filename is a directory, so that's the directory that we will watch. */ + handle->dirw = filenamew; dir_to_watch = filenamew; } else { /* @@ -192,6 +194,8 @@ int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle, } dir_to_watch = dir; + free(filenamew); + filenamew = NULL; } handle->dir_handle = CreateFileW(dir_to_watch, @@ -268,6 +272,8 @@ error: handle->short_filew = NULL; } + free(filenamew); + if (handle->dir_handle != INVALID_HANDLE_VALUE) { CloseHandle(handle->dir_handle); handle->dir_handle = INVALID_HANDLE_VALUE; @@ -286,8 +292,9 @@ error: void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, uv_fs_event_t* handle) { FILE_NOTIFY_INFORMATION* file_info; + int sizew, size, result; char* filename = NULL; - int utf8size; + wchar_t* filenamew, *long_filenamew = NULL; DWORD offset = 0; assert(req->type == UV_FS_EVENT_REQ); @@ -300,39 +307,114 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, if (req->overlapped.InternalHigh > 0) { do { file_info = (FILE_NOTIFY_INFORMATION*)((char*)file_info + offset); + assert(!filename); + assert(!long_filenamew); /* * Fire the event only if we were asked to watch a directory, * or if the filename filter matches. */ - if (handle->is_path_dir || + if (handle->dirw || _wcsnicmp(handle->filew, file_info->FileName, file_info->FileNameLength / sizeof(wchar_t)) == 0 || _wcsnicmp(handle->short_filew, file_info->FileName, file_info->FileNameLength / sizeof(wchar_t)) == 0) { - - /* Convert the filename to utf8. */ - utf8size = uv_utf16_to_utf8(file_info->FileName, - file_info->FileNameLength / - sizeof(wchar_t), - NULL, - 0); - if (utf8size) { - filename = (char*)malloc(utf8size + 1); - if (!filename) { - uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); - } - utf8size = uv_utf16_to_utf8(file_info->FileName, - file_info->FileNameLength / - sizeof(wchar_t), - filename, - utf8size); - if (utf8size) { - filename[utf8size] = '\0'; + if (handle->dirw) { + /* + * We attempt to convert the file name to its long form for + * events that still point to valid files on disk. + * For removed and renamed events, we do not provide the file name. + */ + if (file_info->Action != FILE_ACTION_REMOVED && + file_info->Action != FILE_ACTION_RENAMED_OLD_NAME) { + /* Construct a full path to the file. */ + size = wcslen(handle->dirw) + + file_info->FileNameLength / sizeof(wchar_t) + 2; + + filenamew = (wchar_t*)malloc(size * sizeof(wchar_t)); + if (!filenamew) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + _snwprintf(filenamew, size, L"%s\\%s", handle->dirw, + file_info->FileName); + + filenamew[size - 1] = L'\0'; + + /* Convert to long name. */ + size = GetLongPathNameW(filenamew, NULL, 0); + + if (size) { + long_filenamew = (wchar_t*)malloc(size * sizeof(wchar_t)); + if (!long_filenamew) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + size = GetLongPathNameW(filenamew, long_filenamew, size); + if (size) { + long_filenamew[size] = '\0'; + } else { + free(long_filenamew); + long_filenamew = NULL; + } + } + + free(filenamew); + + if (long_filenamew) { + /* Get the file name out of the long path. */ + result = uv_split_path(long_filenamew, NULL, &filenamew); + free(long_filenamew); + + if (result == 0) { + long_filenamew = filenamew; + sizew = -1; + } else { + long_filenamew = NULL; + } + } + + /* + * If we couldn't get the long name - just use the name + * provided by ReadDirectoryChangesW. + */ + if (!long_filenamew) { + filenamew = file_info->FileName; + sizew = file_info->FileNameLength / sizeof(wchar_t); + } } else { - free(filename); - filename = NULL; + /* Removed or renamed callbacks don't provide filename. */ + filenamew = NULL; + } + } else { + /* We already have the long name of the file, so just use it. */ + filenamew = handle->filew; + sizew = -1; + } + + if (filenamew) { + /* Convert the filename to utf8. */ + size = uv_utf16_to_utf8(filenamew, + sizew, + NULL, + 0); + if (size) { + filename = (char*)malloc(size + 1); + if (!filename) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + size = uv_utf16_to_utf8(filenamew, + sizew, + filename, + size); + if (size) { + filename[size] = '\0'; + } else { + free(filename); + filename = NULL; + } } } @@ -351,6 +433,8 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, free(filename); filename = NULL; + free(long_filenamew); + long_filenamew = NULL; } offset = file_info->NextEntryOffset; @@ -411,6 +495,11 @@ void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) { handle->filename = NULL; } + if (handle->dirw) { + free(handle->dirw); + handle->dirw = NULL; + } + if (handle->close_cb) { handle->close_cb((uv_handle_t*)handle); } From 43e3ac579871819f7730cf60374e49b7ec81fd1f Mon Sep 17 00:00:00 2001 From: Igor Zinkovsky Date: Fri, 30 Dec 2011 15:46:17 -0800 Subject: [PATCH 5/7] windows: uv_fs_rename to replace the new file if it exists fixes #283 --- src/win/fs.c | 8 ++++-- test/test-fs.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++ test/test-list.h | 2 ++ 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/win/fs.c b/src/win/fs.c index 2037ddd6..78ccffab 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -542,8 +542,12 @@ void fs__fstat(uv_fs_t* req, uv_file file) { void fs__rename(uv_fs_t* req, const wchar_t* path, const wchar_t* new_path) { - int result = _wrename(path, new_path); - SET_REQ_RESULT(req, result); + if (!MoveFileExW(path, new_path, MOVEFILE_REPLACE_EXISTING)) { + SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + return; + } + + SET_REQ_RESULT(req, 0); } diff --git a/test/test-fs.c b/test/test-fs.c index 34f891c1..763af8a7 100644 --- a/test/test-fs.c +++ b/test/test-fs.c @@ -1476,3 +1476,71 @@ TEST_IMPL(fs_file_open_append) { return 0; } + + +TEST_IMPL(fs_rename_to_existing_file) { + int r; + + /* Setup. */ + unlink("test_file"); + unlink("test_file2"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &open_req1, "test_file", O_WRONLY | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + ASSERT(open_req1.result != -1); + uv_fs_req_cleanup(&open_req1); + + r = uv_fs_write(loop, &write_req, open_req1.result, test_buf, + sizeof(test_buf), -1, NULL); + ASSERT(r != -1); + ASSERT(write_req.result != -1); + uv_fs_req_cleanup(&write_req); + + r = uv_fs_close(loop, &close_req, open_req1.result, NULL); + ASSERT(r != -1); + ASSERT(close_req.result != -1); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_open(loop, &open_req1, "test_file2", O_WRONLY | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + ASSERT(open_req1.result != -1); + uv_fs_req_cleanup(&open_req1); + + r = uv_fs_close(loop, &close_req, open_req1.result, NULL); + ASSERT(r != -1); + ASSERT(close_req.result != -1); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_rename(loop, &rename_req, "test_file", "test_file2", NULL); + ASSERT(r != -1); + ASSERT(rename_req.result != -1); + uv_fs_req_cleanup(&rename_req); + + r = uv_fs_open(loop, &open_req1, "test_file2", O_RDONLY, 0, NULL); + ASSERT(r != -1); + ASSERT(open_req1.result != -1); + uv_fs_req_cleanup(&open_req1); + + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &read_req, open_req1.result, buf, sizeof(buf), -1, + NULL); + ASSERT(r != -1); + ASSERT(read_req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_close(loop, &close_req, open_req1.result, NULL); + ASSERT(r != -1); + ASSERT(close_req.result != -1); + uv_fs_req_cleanup(&close_req); + + /* Cleanup */ + unlink("test_file"); + unlink("test_file2"); + + return 0; +} \ No newline at end of file diff --git a/test/test-list.h b/test/test-list.h index dec51053..3efc721f 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -115,6 +115,7 @@ TEST_DECLARE (fs_event_immediate_close) TEST_DECLARE (fs_readdir_empty_dir) TEST_DECLARE (fs_readdir_file) TEST_DECLARE (fs_open_dir) +TEST_DECLARE (fs_rename_to_existing_file) TEST_DECLARE (threadpool_queue_work_simple) TEST_DECLARE (counters_init) #ifdef _WIN32 @@ -270,6 +271,7 @@ TASK_LIST_START TEST_ENTRY (fs_readdir_empty_dir) TEST_ENTRY (fs_readdir_file) TEST_ENTRY (fs_open_dir) + TEST_ENTRY (fs_rename_to_existing_file) TEST_ENTRY (threadpool_queue_work_simple) TEST_ENTRY (counters_init) From 38fc6ad839734a3f76d2535b8f62d92e9cfef8c7 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 2 Jan 2012 09:55:48 +0100 Subject: [PATCH 6/7] unix: unref fs event watcher Watchers were being ref-counted twice which wasn't harmful in itself but stopped uv_unref() from working like you'd expect it to. --- src/unix/kqueue.c | 2 ++ src/unix/linux.c | 2 ++ src/unix/sunos.c | 2 ++ test/test-fs-event.c | 20 +++++++++++++++++++- test/test-list.h | 2 ++ 5 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c index 551bce0a..58a6816d 100644 --- a/src/unix/kqueue.c +++ b/src/unix/kqueue.c @@ -43,10 +43,12 @@ static void uv__fs_event_start(uv_fs_event_t* handle) { handle->fd, EV_LIBUV_KQUEUE_HACK); ev_io_start(handle->loop->ev, &handle->event_watcher); + ev_unref(handle->loop->ev); } static void uv__fs_event_stop(uv_fs_event_t* handle) { + ev_ref(handle->loop->ev); ev_io_stop(handle->loop->ev, &handle->event_watcher); } diff --git a/src/unix/linux.c b/src/unix/linux.c index 965de017..3ed65653 100644 --- a/src/unix/linux.c +++ b/src/unix/linux.c @@ -283,12 +283,14 @@ int uv_fs_event_init(uv_loop_t* loop, ev_io_init(&handle->read_watcher, uv__inotify_read, fd, EV_READ); ev_io_start(loop->ev, &handle->read_watcher); + ev_unref(loop->ev); return 0; } void uv__fs_event_destroy(uv_fs_event_t* handle) { + ev_ref(handle->loop->ev); ev_io_stop(handle->loop->ev, &handle->read_watcher); uv__close(handle->fd); handle->fd = -1; diff --git a/src/unix/sunos.c b/src/unix/sunos.c index cf9f162a..dbdad3ce 100644 --- a/src/unix/sunos.c +++ b/src/unix/sunos.c @@ -166,12 +166,14 @@ int uv_fs_event_init(uv_loop_t* loop, ev_io_init(&handle->event_watcher, uv__fs_event_read, portfd, EV_READ); ev_io_start(loop->ev, &handle->event_watcher); + ev_unref(loop->ev); return 0; } void uv__fs_event_destroy(uv_fs_event_t* handle) { + ev_ref(handle->loop->ev); ev_io_stop(handle->loop->ev, &handle->event_watcher); uv__close(handle->fd); handle->fd = -1; diff --git a/test/test-fs-event.c b/test/test-fs-event.c index 59bdebc0..8b52f67f 100644 --- a/test/test-fs-event.c +++ b/test/test-fs-event.c @@ -282,7 +282,7 @@ static void timer_cb(uv_timer_t* handle, int status) { ASSERT(status == 0); r = uv_fs_event_init(handle->loop, &fs_event, ".", fs_event_fail, 0); - ASSERT(r != -1); + ASSERT(r == 0); uv_close((uv_handle_t*)&fs_event, close_cb); uv_close((uv_handle_t*)handle, close_cb); @@ -308,3 +308,21 @@ TEST_IMPL(fs_event_immediate_close) { return 0; } + + +TEST_IMPL(fs_event_unref) { + uv_loop_t* loop; + int r; + + loop = uv_default_loop(); + + r = uv_fs_event_init(loop, &fs_event, ".", fs_event_fail, 0); + ASSERT(r == 0); + + uv_unref(loop); + + r = uv_run(loop); + ASSERT(r == 0); + + return 0; +} diff --git a/test/test-list.h b/test/test-list.h index 3efc721f..f6b58a69 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -112,6 +112,7 @@ TEST_DECLARE (fs_event_watch_file) 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_unref) TEST_DECLARE (fs_readdir_empty_dir) TEST_DECLARE (fs_readdir_file) TEST_DECLARE (fs_open_dir) @@ -268,6 +269,7 @@ TASK_LIST_START TEST_ENTRY (fs_event_watch_file_current_dir) TEST_ENTRY (fs_event_no_callback_on_close) TEST_ENTRY (fs_event_immediate_close) + TEST_ENTRY (fs_event_unref) TEST_ENTRY (fs_readdir_empty_dir) TEST_ENTRY (fs_readdir_file) TEST_ENTRY (fs_open_dir) From 803f5a096e1ce3d1b22c59565185e5a728ec2abd Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 9 Jan 2012 15:42:56 +0100 Subject: [PATCH 7/7] linux: fix build on older distros --- src/unix/internal.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/unix/internal.h b/src/unix/internal.h index 1adb5f20..3ae5d054 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -59,6 +59,18 @@ # define HAVE_SYS_ACCEPT4 1 # endif +# ifndef O_CLOEXEC +# define O_CLOEXEC 02000000 +# endif + +# ifndef SOCK_CLOEXEC +# define SOCK_CLOEXEC O_CLOEXEC +# endif + +# ifndef SOCK_NONBLOCK +# define SOCK_NONBLOCK O_NONBLOCK +# endif + # if HAVE_SYS_UTIMESAT inline static int sys_utimesat(int dirfd, const char* path,