1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
29 #include "handle-inl.h"
33 const unsigned int uv_directory_watcher_buffer_size = 4096;
36 static void uv__fs_event_queue_readdirchanges(uv_loop_t* loop,
37 uv_fs_event_t* handle) {
38 assert(handle->dir_handle != INVALID_HANDLE_VALUE);
39 assert(!handle->req_pending);
41 memset(&(handle->req.u.io.overlapped), 0,
42 sizeof(handle->req.u.io.overlapped));
43 if (!ReadDirectoryChangesW(handle->dir_handle,
45 uv_directory_watcher_buffer_size,
46 (handle->flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE,
47 FILE_NOTIFY_CHANGE_FILE_NAME |
48 FILE_NOTIFY_CHANGE_DIR_NAME |
49 FILE_NOTIFY_CHANGE_ATTRIBUTES |
50 FILE_NOTIFY_CHANGE_SIZE |
51 FILE_NOTIFY_CHANGE_LAST_WRITE |
52 FILE_NOTIFY_CHANGE_LAST_ACCESS |
53 FILE_NOTIFY_CHANGE_CREATION |
54 FILE_NOTIFY_CHANGE_SECURITY,
56 &handle->req.u.io.overlapped,
58 /* Make this req pending reporting an error. */
59 SET_REQ_ERROR(&handle->req, GetLastError());
60 uv__insert_pending_req(loop, (uv_req_t*)&handle->req);
63 handle->req_pending = 1;
66 static void uv__relative_path(const WCHAR* filename,
70 size_t filenamelen = wcslen(filename);
71 size_t dirlen = wcslen(dir);
72 assert(!_wcsnicmp(filename, dir, dirlen));
73 if (dirlen > 0 && dir[dirlen - 1] == '\\')
75 relpathlen = filenamelen - dirlen - 1;
76 *relpath = uv__malloc((relpathlen + 1) * sizeof(WCHAR));
78 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
79 wcsncpy(*relpath, filename + dirlen + 1, relpathlen);
80 (*relpath)[relpathlen] = L'\0';
83 static int uv__split_path(const WCHAR* filename, WCHAR** dir,
88 if (filename == NULL) {
95 len = wcslen(filename);
97 while (i > 0 && filename[--i] != '\\' && filename[i] != '/');
101 dir_len = GetCurrentDirectoryW(0, NULL);
105 *dir = (WCHAR*)uv__malloc(dir_len * sizeof(WCHAR));
107 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
110 if (!GetCurrentDirectoryW(dir_len, *dir)) {
117 *file = wcsdup(filename);
120 *dir = (WCHAR*)uv__malloc((i + 2) * sizeof(WCHAR));
122 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
124 wcsncpy(*dir, filename, i + 1);
125 (*dir)[i + 1] = L'\0';
128 *file = (WCHAR*)uv__malloc((len - i) * sizeof(WCHAR));
130 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
132 wcsncpy(*file, filename + i + 1, len - i - 1);
133 (*file)[len - i - 1] = L'\0';
140 int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
141 uv__handle_init(loop, (uv_handle_t*) handle, UV_FS_EVENT);
142 handle->dir_handle = INVALID_HANDLE_VALUE;
143 handle->buffer = NULL;
144 handle->req_pending = 0;
145 handle->filew = NULL;
146 handle->short_filew = NULL;
149 UV_REQ_INIT(&handle->req, UV_FS_EVENT_REQ);
150 handle->req.data = handle;
156 int uv_fs_event_start(uv_fs_event_t* handle,
159 unsigned int flags) {
160 int name_size, is_path_dir, size;
161 DWORD attr, last_error;
162 WCHAR* dir = NULL, *dir_to_watch, *pathw = NULL;
163 DWORD short_path_buffer_len;
164 WCHAR *short_path_buffer;
165 WCHAR* short_path, *long_path;
168 if (uv__is_active(handle))
172 handle->path = uv__strdup(path);
174 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
177 uv__handle_start(handle);
179 /* Convert name to UTF16. */
181 name_size = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0) *
183 pathw = (WCHAR*)uv__malloc(name_size);
185 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
188 if (!MultiByteToWideChar(CP_UTF8,
193 name_size / sizeof(WCHAR))) {
194 return uv_translate_sys_error(GetLastError());
197 /* Determine whether path is a file or a directory. */
198 attr = GetFileAttributesW(pathw);
199 if (attr == INVALID_FILE_ATTRIBUTES) {
200 last_error = GetLastError();
204 is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;
207 /* path is a directory, so that's the directory that we will watch. */
209 /* Convert to long path. */
210 size = GetLongPathNameW(pathw, NULL, 0);
213 long_path = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
215 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
218 size = GetLongPathNameW(pathw, long_path, size);
220 long_path[size] = '\0';
232 dir_to_watch = pathw;
235 * path is a file. So we split path into dir & file parts, and
236 * watch the dir directory.
239 /* Convert to short path. */
240 short_path_buffer = NULL;
241 short_path_buffer_len = GetShortPathNameW(pathw, NULL, 0);
242 if (short_path_buffer_len == 0) {
243 goto short_path_done;
245 short_path_buffer = uv__malloc(short_path_buffer_len * sizeof(WCHAR));
246 if (short_path_buffer == NULL) {
247 goto short_path_done;
249 if (GetShortPathNameW(pathw,
251 short_path_buffer_len) == 0) {
252 uv__free(short_path_buffer);
253 short_path_buffer = NULL;
256 short_path = short_path_buffer;
258 if (uv__split_path(pathw, &dir, &handle->filew) != 0) {
259 last_error = GetLastError();
263 if (uv__split_path(short_path, NULL, &handle->short_filew) != 0) {
264 last_error = GetLastError();
273 handle->dir_handle = CreateFileW(dir_to_watch,
275 FILE_SHARE_READ | FILE_SHARE_DELETE |
279 FILE_FLAG_BACKUP_SEMANTICS |
280 FILE_FLAG_OVERLAPPED,
288 if (handle->dir_handle == INVALID_HANDLE_VALUE) {
289 last_error = GetLastError();
293 if (CreateIoCompletionPort(handle->dir_handle,
297 last_error = GetLastError();
301 if (!handle->buffer) {
302 handle->buffer = (char*)uv__malloc(uv_directory_watcher_buffer_size);
304 if (!handle->buffer) {
305 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
308 memset(&(handle->req.u.io.overlapped), 0,
309 sizeof(handle->req.u.io.overlapped));
311 if (!ReadDirectoryChangesW(handle->dir_handle,
313 uv_directory_watcher_buffer_size,
314 (flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE,
315 FILE_NOTIFY_CHANGE_FILE_NAME |
316 FILE_NOTIFY_CHANGE_DIR_NAME |
317 FILE_NOTIFY_CHANGE_ATTRIBUTES |
318 FILE_NOTIFY_CHANGE_SIZE |
319 FILE_NOTIFY_CHANGE_LAST_WRITE |
320 FILE_NOTIFY_CHANGE_LAST_ACCESS |
321 FILE_NOTIFY_CHANGE_CREATION |
322 FILE_NOTIFY_CHANGE_SECURITY,
324 &handle->req.u.io.overlapped,
326 last_error = GetLastError();
330 assert(is_path_dir ? pathw != NULL : pathw == NULL);
331 handle->dirw = pathw;
332 handle->req_pending = 1;
337 uv__free(handle->path);
342 uv__free(handle->filew);
343 handle->filew = NULL;
346 if (handle->short_filew) {
347 uv__free(handle->short_filew);
348 handle->short_filew = NULL;
353 if (handle->dir_handle != INVALID_HANDLE_VALUE) {
354 CloseHandle(handle->dir_handle);
355 handle->dir_handle = INVALID_HANDLE_VALUE;
358 if (handle->buffer) {
359 uv__free(handle->buffer);
360 handle->buffer = NULL;
363 if (uv__is_active(handle))
364 uv__handle_stop(handle);
366 uv__free(short_path);
368 return uv_translate_sys_error(last_error);
372 int uv_fs_event_stop(uv_fs_event_t* handle) {
373 if (!uv__is_active(handle))
376 if (handle->dir_handle != INVALID_HANDLE_VALUE) {
377 CloseHandle(handle->dir_handle);
378 handle->dir_handle = INVALID_HANDLE_VALUE;
381 uv__handle_stop(handle);
384 uv__free(handle->filew);
385 handle->filew = NULL;
388 if (handle->short_filew) {
389 uv__free(handle->short_filew);
390 handle->short_filew = NULL;
394 uv__free(handle->path);
399 uv__free(handle->dirw);
407 static int file_info_cmp(WCHAR* str, WCHAR* file_name, size_t file_name_len) {
413 str_len = wcslen(str);
416 Since we only care about equality, return early if the strings
417 aren't the same length
419 if (str_len != (file_name_len / sizeof(WCHAR)))
422 return _wcsnicmp(str, file_name, str_len);
426 void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
427 uv_fs_event_t* handle) {
428 FILE_NOTIFY_INFORMATION* file_info;
429 int err, sizew, size;
430 char* filename = NULL;
431 WCHAR* filenamew = NULL;
432 WCHAR* long_filenamew = NULL;
435 assert(req->type == UV_FS_EVENT_REQ);
436 assert(handle->req_pending);
437 handle->req_pending = 0;
439 /* Don't report any callbacks if:
440 * - We're closing, just push the handle onto the endgame queue
441 * - We are not active, just ignore the callback
443 if (!uv__is_active(handle)) {
444 if (handle->flags & UV_HANDLE_CLOSING) {
445 uv__want_endgame(loop, (uv_handle_t*) handle);
450 file_info = (FILE_NOTIFY_INFORMATION*)(handle->buffer + offset);
452 if (REQ_SUCCESS(req)) {
453 if (req->u.io.overlapped.InternalHigh > 0) {
455 file_info = (FILE_NOTIFY_INFORMATION*)((char*)file_info + offset);
458 assert(!long_filenamew);
461 * Fire the event only if we were asked to watch a directory,
462 * or if the filename filter matches.
465 file_info_cmp(handle->filew,
467 file_info->FileNameLength) == 0 ||
468 file_info_cmp(handle->short_filew,
470 file_info->FileNameLength) == 0) {
474 * We attempt to resolve the long form of the file name explicitly.
475 * We only do this for file names that might still exist on disk.
476 * If this fails, we use the name given by ReadDirectoryChangesW.
477 * This may be the long form or the 8.3 short name in some cases.
479 if (file_info->Action != FILE_ACTION_REMOVED &&
480 file_info->Action != FILE_ACTION_RENAMED_OLD_NAME) {
481 /* Construct a full path to the file. */
482 size = wcslen(handle->dirw) +
483 file_info->FileNameLength / sizeof(WCHAR) + 2;
485 filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
487 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
490 _snwprintf(filenamew, size, L"%s\\%.*s", handle->dirw,
491 file_info->FileNameLength / (DWORD)sizeof(WCHAR),
492 file_info->FileName);
494 filenamew[size - 1] = L'\0';
496 /* Convert to long name. */
497 size = GetLongPathNameW(filenamew, NULL, 0);
500 long_filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
501 if (!long_filenamew) {
502 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
505 size = GetLongPathNameW(filenamew, long_filenamew, size);
507 long_filenamew[size] = '\0';
509 uv__free(long_filenamew);
510 long_filenamew = NULL;
516 if (long_filenamew) {
517 /* Get the file name out of the long path. */
518 uv__relative_path(long_filenamew,
521 uv__free(long_filenamew);
522 long_filenamew = filenamew;
525 /* We couldn't get the long filename, use the one reported. */
526 filenamew = file_info->FileName;
527 sizew = file_info->FileNameLength / sizeof(WCHAR);
531 * Removed or renamed events cannot be resolved to the long form.
532 * We therefore use the name given by ReadDirectoryChangesW.
533 * This may be the long form or the 8.3 short name in some cases.
535 filenamew = file_info->FileName;
536 sizew = file_info->FileNameLength / sizeof(WCHAR);
539 /* We already have the long name of the file, so just use it. */
540 filenamew = handle->filew;
544 /* Convert the filename to utf8. */
545 uv__convert_utf16_to_utf8(filenamew, sizew, &filename);
547 switch (file_info->Action) {
548 case FILE_ACTION_ADDED:
549 case FILE_ACTION_REMOVED:
550 case FILE_ACTION_RENAMED_OLD_NAME:
551 case FILE_ACTION_RENAMED_NEW_NAME:
552 handle->cb(handle, filename, UV_RENAME, 0);
555 case FILE_ACTION_MODIFIED:
556 handle->cb(handle, filename, UV_CHANGE, 0);
562 uv__free(long_filenamew);
563 long_filenamew = NULL;
567 offset = file_info->NextEntryOffset;
568 } while (offset && !(handle->flags & UV_HANDLE_CLOSING));
570 handle->cb(handle, NULL, UV_CHANGE, 0);
573 err = GET_REQ_ERROR(req);
574 handle->cb(handle, NULL, 0, uv_translate_sys_error(err));
577 if (handle->flags & UV_HANDLE_CLOSING) {
578 uv__want_endgame(loop, (uv_handle_t*)handle);
579 } else if (uv__is_active(handle)) {
580 uv__fs_event_queue_readdirchanges(loop, handle);
585 void uv__fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle) {
586 uv_fs_event_stop(handle);
588 uv__handle_closing(handle);
590 if (!handle->req_pending) {
591 uv__want_endgame(loop, (uv_handle_t*)handle);
597 void uv__fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) {
598 if ((handle->flags & UV_HANDLE_CLOSING) && !handle->req_pending) {
599 assert(!(handle->flags & UV_HANDLE_CLOSED));
601 if (handle->buffer) {
602 uv__free(handle->buffer);
603 handle->buffer = NULL;
606 uv__handle_close(handle);