diff --git a/src/unix/fsevents.c b/src/unix/fsevents.c index 5efe495e..7fb6bb2e 100644 --- a/src/unix/fsevents.c +++ b/src/unix/fsevents.c @@ -276,10 +276,6 @@ static void uv__fsevents_event_cb(const FSEventStreamRef streamRef, path += handle->realpath_len; len -= handle->realpath_len; - /* Ignore events with path equal to directory itself */ - if (len <= 1 && (flags & kFSEventStreamEventFlagItemIsDir)) - continue; - if (len == 0) { /* Since we're using fsevents to watch the file itself, * realpath == path, and we now need to get the basename of the file back diff --git a/src/win/fs-event.c b/src/win/fs-event.c index fce41181..7ab407e0 100644 --- a/src/win/fs-event.c +++ b/src/win/fs-event.c @@ -561,7 +561,25 @@ void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req, } } else { err = GET_REQ_ERROR(req); - handle->cb(handle, NULL, 0, uv_translate_sys_error(err)); + /* + * Check whether the ERROR_ACCESS_DENIED is caused by the watched directory + * being actually deleted (not an actual error) or a legit error. Retrieve + * FileStandardInfo to check whether the directory is pending deletion. + */ + FILE_STANDARD_INFO info; + if (err == ERROR_ACCESS_DENIED && + handle->dirw != NULL && + GetFileInformationByHandleEx(handle->dir_handle, + FileStandardInfo, + &info, + sizeof(info)) && + info.Directory && + info.DeletePending) { + uv__convert_utf16_to_utf8(handle->dirw, -1, &filename); + handle->cb(handle, filename, UV_RENAME, 0); + } else { + handle->cb(handle, NULL, 0, uv_translate_sys_error(err)); + } } if (handle->flags & UV_HANDLE_CLOSING) { diff --git a/test/test-fs-event.c b/test/test-fs-event.c index 67910880..ccfa2cbe 100644 --- a/test/test-fs-event.c +++ b/test/test-fs-event.c @@ -81,6 +81,22 @@ static void create_file(const char* name) { uv_fs_req_cleanup(&req); } +static int delete_dir(const char* name) { + int r; + uv_fs_t req; + r = uv_fs_rmdir(NULL, &req, name, NULL); + uv_fs_req_cleanup(&req); + return r; +} + +static int delete_file(const char* name) { + int r; + uv_fs_t req; + r = uv_fs_unlink(NULL, &req, name, NULL); + uv_fs_req_cleanup(&req); + return r; +} + static void touch_file(const char* name) { int r; uv_file file; @@ -129,6 +145,19 @@ static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename, uv_close((uv_handle_t*)handle, close_cb); } +static void fs_event_cb_del_dir(uv_fs_event_t* handle, + const char* filename, + int events, + int status) { + ++fs_event_cb_called; + ASSERT_PTR_EQ(handle, &fs_event); + ASSERT_OK(status); + ASSERT_EQ(events, UV_RENAME); + ASSERT_OK(strcmp(filename, "watch_del_dir")); + ASSERT_OK(uv_fs_event_stop(handle)); + uv_close((uv_handle_t*)handle, close_cb); +} + static const char* fs_event_get_filename(int i) { snprintf(fs_event_filename, sizeof(fs_event_filename), @@ -152,6 +181,15 @@ static void fs_event_create_files(uv_timer_t* handle) { } } +static void fs_event_del_dir(uv_timer_t* handle) { + int r; + + r = delete_dir("watch_del_dir"); + ASSERT_OK(r); + + uv_close((uv_handle_t*)handle, close_cb); +} + static void fs_event_unlink_files(uv_timer_t* handle) { int r; int i; @@ -160,7 +198,7 @@ static void fs_event_unlink_files(uv_timer_t* handle) { if (handle == NULL) { /* Unlink all files */ for (i = 0; i < 16; i++) { - r = remove(fs_event_get_filename(i)); + r = delete_file(fs_event_get_filename(i)); if (handle != NULL) ASSERT_OK(r); } @@ -169,7 +207,7 @@ static void fs_event_unlink_files(uv_timer_t* handle) { ASSERT_LT(fs_event_removed, fs_event_file_count); /* Remove the file */ - ASSERT_OK(remove(fs_event_get_filename(fs_event_removed))); + ASSERT_OK(delete_file(fs_event_get_filename(fs_event_removed))); if (++fs_event_removed < fs_event_file_count) { /* Remove another file on a different event loop tick. We do it this way @@ -237,7 +275,7 @@ static void fs_event_unlink_files_in_subdir(uv_timer_t* handle) { if (handle == NULL) { /* Unlink all files */ for (i = 0; i < 16; i++) { - r = remove(fs_event_get_filename_in_subdir(i)); + r = delete_file(fs_event_get_filename_in_subdir(i)); if (handle != NULL) ASSERT_OK(r); } @@ -246,7 +284,7 @@ static void fs_event_unlink_files_in_subdir(uv_timer_t* handle) { ASSERT_LT(fs_event_removed, fs_event_file_count); /* Remove the file */ - ASSERT_OK(remove(fs_event_get_filename_in_subdir(fs_event_removed))); + ASSERT_OK(delete_file(fs_event_get_filename_in_subdir(fs_event_removed))); if (++fs_event_removed < fs_event_file_count) { /* Remove another file on a different event loop tick. We do it this way @@ -407,9 +445,9 @@ TEST_IMPL(fs_event_watch_dir) { /* Setup */ fs_event_unlink_files(NULL); - remove("watch_dir/file2"); - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file2"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); create_dir("watch_dir"); r = uv_fs_event_init(loop, &fs_event); @@ -428,9 +466,47 @@ TEST_IMPL(fs_event_watch_dir) { /* Cleanup */ fs_event_unlink_files(NULL); - remove("watch_dir/file2"); - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file2"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); + + MAKE_VALGRIND_HAPPY(loop); + return 0; +} + +TEST_IMPL(fs_event_watch_delete_dir) { +#if defined(NO_FS_EVENTS) + RETURN_SKIP(NO_FS_EVENTS); +#elif defined(__MVS__) + RETURN_SKIP("Directory watching not supported on this platform."); +#elif defined(__APPLE__) && defined(__TSAN__) + RETURN_SKIP("Times out under TSAN."); +#endif + + uv_loop_t* loop = uv_default_loop(); + int r; + + /* Setup */ + fs_event_unlink_files(NULL); + delete_dir("watch_del_dir/"); + create_dir("watch_del_dir"); + + r = uv_fs_event_init(loop, &fs_event); + ASSERT_OK(r); + r = uv_fs_event_start(&fs_event, fs_event_cb_del_dir, "watch_del_dir", 0); + ASSERT_OK(r); + r = uv_timer_init(loop, &timer); + ASSERT_OK(r); + r = uv_timer_start(&timer, fs_event_del_dir, 100, 0); + ASSERT_OK(r); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT_EQ(1, fs_event_cb_called); + ASSERT_EQ(2, close_cb_called); + + /* Cleanup */ + fs_event_unlink_files(NULL); MAKE_VALGRIND_HAPPY(loop); return 0; @@ -448,10 +524,10 @@ TEST_IMPL(fs_event_watch_dir_recursive) { /* Setup */ loop = uv_default_loop(); fs_event_unlink_files(NULL); - remove("watch_dir/file2"); - remove("watch_dir/file1"); - remove("watch_dir/subdir"); - remove("watch_dir/"); + delete_file("watch_dir/file2"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/subdir"); + delete_dir("watch_dir/"); create_dir("watch_dir"); create_dir("watch_dir/subdir"); @@ -491,10 +567,10 @@ TEST_IMPL(fs_event_watch_dir_recursive) { /* Cleanup */ fs_event_unlink_files_in_subdir(NULL); - remove("watch_dir/file2"); - remove("watch_dir/file1"); - remove("watch_dir/subdir"); - remove("watch_dir/"); + delete_file("watch_dir/file2"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/subdir"); + delete_dir("watch_dir/"); MAKE_VALGRIND_HAPPY(loop); return 0; @@ -512,8 +588,8 @@ TEST_IMPL(fs_event_watch_dir_short_path) { /* Setup */ loop = uv_default_loop(); - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); create_dir("watch_dir"); create_file("watch_dir/file1"); @@ -540,8 +616,8 @@ TEST_IMPL(fs_event_watch_dir_short_path) { } /* Cleanup */ - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); MAKE_VALGRIND_HAPPY(loop); @@ -562,9 +638,9 @@ TEST_IMPL(fs_event_watch_file) { int r; /* Setup */ - remove("watch_dir/file2"); - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file2"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); create_dir("watch_dir"); create_file("watch_dir/file1"); create_file("watch_dir/file2"); @@ -585,9 +661,9 @@ TEST_IMPL(fs_event_watch_file) { ASSERT_EQ(2, close_cb_called); /* Cleanup */ - remove("watch_dir/file2"); - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file2"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); MAKE_VALGRIND_HAPPY(loop); return 0; @@ -609,9 +685,9 @@ TEST_IMPL(fs_event_watch_file_exact_path) { loop = uv_default_loop(); /* Setup */ - remove("watch_dir/file.js"); - remove("watch_dir/file.jsx"); - remove("watch_dir/"); + delete_file("watch_dir/file.js"); + delete_file("watch_dir/file.jsx"); + delete_dir("watch_dir/"); create_dir("watch_dir"); create_file("watch_dir/file.js"); create_file("watch_dir/file.jsx"); @@ -638,9 +714,9 @@ TEST_IMPL(fs_event_watch_file_exact_path) { ASSERT_EQ(2, timer_cb_exact_called); /* Cleanup */ - remove("watch_dir/file.js"); - remove("watch_dir/file.jsx"); - remove("watch_dir/"); + delete_file("watch_dir/file.js"); + delete_file("watch_dir/file.jsx"); + delete_dir("watch_dir/"); MAKE_VALGRIND_HAPPY(loop); return 0; @@ -681,7 +757,7 @@ TEST_IMPL(fs_event_watch_file_current_dir) { loop = uv_default_loop(); /* Setup */ - remove("watch_file"); + delete_file("watch_file"); create_file("watch_file"); #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_12) /* Empirically, kevent seems to (sometimes) report the preceding @@ -719,7 +795,7 @@ TEST_IMPL(fs_event_watch_file_current_dir) { ASSERT_EQ(1, close_cb_called); /* Cleanup */ - remove("watch_file"); + delete_file("watch_file"); MAKE_VALGRIND_HAPPY(loop); return 0; @@ -761,8 +837,8 @@ TEST_IMPL(fs_event_no_callback_after_close) { int r; /* Setup */ - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); create_dir("watch_dir"); create_file("watch_dir/file1"); @@ -783,8 +859,8 @@ TEST_IMPL(fs_event_no_callback_after_close) { ASSERT_EQ(1, close_cb_called); /* Cleanup */ - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); MAKE_VALGRIND_HAPPY(loop); return 0; @@ -799,8 +875,8 @@ TEST_IMPL(fs_event_no_callback_on_close) { int r; /* Setup */ - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); create_dir("watch_dir"); create_file("watch_dir/file1"); @@ -820,8 +896,8 @@ TEST_IMPL(fs_event_no_callback_on_close) { ASSERT_EQ(1, close_cb_called); /* Cleanup */ - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); MAKE_VALGRIND_HAPPY(loop); return 0; @@ -893,8 +969,8 @@ TEST_IMPL(fs_event_close_with_pending_event) { ASSERT_EQ(1, close_cb_called); /* Clean up */ - remove("watch_dir/file"); - remove("watch_dir/"); + delete_file("watch_dir/file"); + delete_dir("watch_dir/"); MAKE_VALGRIND_HAPPY(loop); return 0; @@ -918,7 +994,7 @@ TEST_IMPL(fs_event_close_with_pending_delete_event) { ASSERT_OK(r); /* Generate an fs event. */ - remove("watch_dir/file"); + delete_file("watch_dir/file"); /* Allow time for the remove event to propagate to the pending list. */ /* XXX - perhaps just for __sun? */ @@ -932,7 +1008,7 @@ TEST_IMPL(fs_event_close_with_pending_delete_event) { ASSERT_EQ(1, close_cb_called); /* Clean up */ - remove("watch_dir/"); + delete_dir("watch_dir/"); MAKE_VALGRIND_HAPPY(loop); return 0; @@ -975,7 +1051,7 @@ TEST_IMPL(fs_event_close_in_callback) { /* Clean up */ fs_event_unlink_files(NULL); - remove("watch_dir/"); + delete_dir("watch_dir/"); MAKE_VALGRIND_HAPPY(loop); return 0; @@ -1011,7 +1087,7 @@ TEST_IMPL(fs_event_start_and_close) { ASSERT_EQ(2, close_cb_called); - remove("watch_dir/"); + delete_dir("watch_dir/"); MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -1064,7 +1140,7 @@ TEST_IMPL(fs_event_getpath) { close_cb_called = 0; } - remove("watch_dir/"); + delete_dir("watch_dir/"); MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -1103,7 +1179,7 @@ TEST_IMPL(fs_event_error_reporting) { TEST_FILE_LIMIT(ARRAY_SIZE(loops) * 3); - remove("watch_dir/"); + delete_dir("watch_dir/"); create_dir("watch_dir"); /* Create a lot of loops, and start FSEventStream in each of them. @@ -1153,7 +1229,7 @@ TEST_IMPL(fs_event_error_reporting) { uv_loop_close(loop); } while (i-- != 0); - remove("watch_dir/"); + delete_dir("watch_dir/"); MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -1207,7 +1283,7 @@ TEST_IMPL(fs_event_stop_in_cb) { RETURN_SKIP(NO_FS_EVENTS); #endif - remove(path); + delete_file(path); create_file(path); ASSERT_OK(uv_fs_event_init(uv_default_loop(), &fs)); @@ -1230,7 +1306,7 @@ TEST_IMPL(fs_event_stop_in_cb) { ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT_EQ(1, fs_event_cb_stop_calls); - remove(path); + delete_file(path); MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; diff --git a/test/test-list.h b/test/test-list.h index 68878cf8..1bc8c9a9 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -396,6 +396,7 @@ TEST_DECLARE (fs_stat_missing_path) TEST_DECLARE (fs_read_bufs) TEST_DECLARE (fs_read_file_eof) TEST_DECLARE (fs_event_watch_dir) +TEST_DECLARE (fs_event_watch_delete_dir) TEST_DECLARE (fs_event_watch_dir_recursive) #ifdef _WIN32 TEST_DECLARE (fs_event_watch_dir_short_path) @@ -1105,6 +1106,7 @@ TASK_LIST_START TEST_ENTRY (fs_read_file_eof) TEST_ENTRY (fs_file_open_append) TEST_ENTRY (fs_event_watch_dir) + TEST_ENTRY (fs_event_watch_delete_dir) TEST_ENTRY (fs_event_watch_dir_recursive) #ifdef _WIN32 TEST_ENTRY (fs_event_watch_dir_short_path)