diff --git a/src/win/fs-event.c b/src/win/fs-event.c index fd77f2a49..2506af29e 100644 --- a/src/win/fs-event.c +++ b/src/win/fs-event.c @@ -433,6 +433,7 @@ void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req, WCHAR* filenamew = NULL; WCHAR* long_filenamew = NULL; DWORD offset = 0; + int dir_event_detected = 0; assert(req->type == UV_FS_EVENT_REQ); assert(handle->req_pending); @@ -459,6 +460,10 @@ void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req, assert(!filenamew); assert(!long_filenamew); + if (file_info->FileNameLength == 0) { + dir_event_detected = 1; + } + /* * Fire the event only if we were asked to watch a directory, * or if the filename filter matches. @@ -587,6 +592,7 @@ void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req, sizeof(info)) && info.Directory && info.DeletePending) { + dir_event_detected = 1; uv__convert_utf16_to_utf8(handle->dirw, -1, &filename); handle->cb(handle, filename, UV_RENAME, 0); uv__free(filename); @@ -599,6 +605,26 @@ void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req, if (handle->flags & UV_HANDLE_CLOSING) { uv__want_endgame(loop, (uv_handle_t*)handle); } else if (uv__is_active(handle)) { + /* + * Check if the handle has become a zombie pointing to \$Extend\$Deleted\. + * Only perform the check if we detected an event on the directory, which + * may indicate deletion. + */ + if (dir_event_detected) { + WCHAR path_buf[MAX_PATH]; + DWORD path_len = GetFinalPathNameByHandleW(handle->dir_handle, + path_buf, + ARRAY_SIZE(path_buf), + FILE_NAME_NORMALIZED | VOLUME_NAME_NONE); + + if (path_len > 0 && path_len < ARRAY_SIZE(path_buf)) { + if (wcsstr(path_buf, L"\\$Extend\\$Deleted\\") != NULL) { + handle->cb(handle, NULL, 0, UV_ENOENT); + return; + } + } + } + uv__fs_event_queue_readdirchanges(loop, handle); } } diff --git a/test/test-fs-event.c b/test/test-fs-event.c index f224181fc..a34e2d52e 100644 --- a/test/test-fs-event.c +++ b/test/test-fs-event.c @@ -519,6 +519,51 @@ TEST_IMPL(fs_event_watch_delete_dir) { return 0; } +#ifdef _WIN32 +static int fs_event_cb_del_dir_perm_got_enoent; + +static void fs_event_cb_del_dir_perm(uv_fs_event_t* handle, + const char* filename, + int events, + int status) { + if (status == UV_ENOENT) { + fs_event_cb_del_dir_perm_got_enoent = 1; + uv_close((uv_handle_t*)handle, close_cb); + } +} + +TEST_IMPL(fs_event_watch_delete_dir_win) { + uv_loop_t* loop = uv_default_loop(); + int r; + + /* Setup */ + fs_event_cb_del_dir_perm_got_enoent = 0; + 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_perm, "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_del_dir_perm_got_enoent); + ASSERT_EQ(2, close_cb_called); + + /* Cleanup */ + fs_event_unlink_files(NULL); + + MAKE_VALGRIND_HAPPY(loop); + return 0; +} +#endif + TEST_IMPL(fs_event_watch_dir_recursive) { #if defined(__APPLE__) && defined(__TSAN__) diff --git a/test/test-list.h b/test/test-list.h index 020923b72..5ce84e9f6 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -410,6 +410,9 @@ TEST_FS_DECLARE (fs_read_bufs) TEST_FS_DECLARE (fs_read_file_eof) TEST_DECLARE (fs_event_watch_dir) TEST_DECLARE (fs_event_watch_delete_dir) +#ifdef _WIN32 +TEST_DECLARE (fs_event_watch_delete_dir_win) +#endif TEST_DECLARE (fs_event_watch_dir_recursive) #ifdef _WIN32 TEST_DECLARE (fs_event_watch_dir_short_path) @@ -1143,6 +1146,9 @@ TASK_LIST_START TEST_FS_ENTRY (fs_file_open_append) TEST_ENTRY (fs_event_watch_dir) TEST_ENTRY (fs_event_watch_delete_dir) +#ifdef _WIN32 + TEST_ENTRY (fs_event_watch_delete_dir_win) +#endif TEST_ENTRY (fs_event_watch_dir_recursive) #ifdef _WIN32 TEST_ENTRY (fs_event_watch_dir_short_path)