|
160
|
1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|
|
2 *
|
|
|
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:
|
|
|
9 *
|
|
|
10 * The above copyright notice and this permission notice shall be included in
|
|
|
11 * all copies or substantial portions of the Software.
|
|
|
12 *
|
|
|
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
|
|
|
19 * IN THE SOFTWARE.
|
|
|
20 */
|
|
|
21
|
|
|
22 #include <assert.h>
|
|
|
23 #include <errno.h>
|
|
|
24 #include <stdio.h>
|
|
|
25 #include <string.h>
|
|
|
26
|
|
|
27 #include "uv.h"
|
|
|
28 #include "internal.h"
|
|
|
29 #include "handle-inl.h"
|
|
|
30 #include "req-inl.h"
|
|
|
31
|
|
|
32
|
|
|
33 const unsigned int uv_directory_watcher_buffer_size = 4096;
|
|
|
34
|
|
|
35
|
|
|
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);
|
|
|
40
|
|
|
41 memset(&(handle->req.u.io.overlapped), 0,
|
|
|
42 sizeof(handle->req.u.io.overlapped));
|
|
|
43 if (!ReadDirectoryChangesW(handle->dir_handle,
|
|
|
44 handle->buffer,
|
|
|
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,
|
|
|
55 NULL,
|
|
|
56 &handle->req.u.io.overlapped,
|
|
|
57 NULL)) {
|
|
|
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);
|
|
|
61 }
|
|
|
62
|
|
|
63 handle->req_pending = 1;
|
|
|
64 }
|
|
|
65
|
|
|
66 static void uv__relative_path(const WCHAR* filename,
|
|
|
67 const WCHAR* dir,
|
|
|
68 WCHAR** relpath) {
|
|
|
69 size_t relpathlen;
|
|
|
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] == '\\')
|
|
|
74 dirlen--;
|
|
|
75 relpathlen = filenamelen - dirlen - 1;
|
|
|
76 *relpath = uv__malloc((relpathlen + 1) * sizeof(WCHAR));
|
|
|
77 if (!*relpath)
|
|
|
78 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
|
|
79 wcsncpy(*relpath, filename + dirlen + 1, relpathlen);
|
|
|
80 (*relpath)[relpathlen] = L'\0';
|
|
|
81 }
|
|
|
82
|
|
|
83 static int uv__split_path(const WCHAR* filename, WCHAR** dir,
|
|
|
84 WCHAR** file) {
|
|
|
85 size_t len, i;
|
|
|
86 DWORD dir_len;
|
|
|
87
|
|
|
88 if (filename == NULL) {
|
|
|
89 if (dir != NULL)
|
|
|
90 *dir = NULL;
|
|
|
91 *file = NULL;
|
|
|
92 return 0;
|
|
|
93 }
|
|
|
94
|
|
|
95 len = wcslen(filename);
|
|
|
96 i = len;
|
|
|
97 while (i > 0 && filename[--i] != '\\' && filename[i] != '/');
|
|
|
98
|
|
|
99 if (i == 0) {
|
|
|
100 if (dir) {
|
|
|
101 dir_len = GetCurrentDirectoryW(0, NULL);
|
|
|
102 if (dir_len == 0) {
|
|
|
103 return -1;
|
|
|
104 }
|
|
|
105 *dir = (WCHAR*)uv__malloc(dir_len * sizeof(WCHAR));
|
|
|
106 if (!*dir) {
|
|
|
107 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
|
|
108 }
|
|
|
109
|
|
|
110 if (!GetCurrentDirectoryW(dir_len, *dir)) {
|
|
|
111 uv__free(*dir);
|
|
|
112 *dir = NULL;
|
|
|
113 return -1;
|
|
|
114 }
|
|
|
115 }
|
|
|
116
|
|
|
117 *file = _wcsdup(filename);
|
|
|
118 } else {
|
|
|
119 if (dir) {
|
|
|
120 *dir = (WCHAR*)uv__malloc((i + 2) * sizeof(WCHAR));
|
|
|
121 if (!*dir) {
|
|
|
122 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
|
|
123 }
|
|
|
124 wcsncpy(*dir, filename, i + 1);
|
|
|
125 (*dir)[i + 1] = L'\0';
|
|
|
126 }
|
|
|
127
|
|
|
128 *file = (WCHAR*)uv__malloc((len - i) * sizeof(WCHAR));
|
|
|
129 if (!*file) {
|
|
|
130 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
|
|
131 }
|
|
|
132 wcsncpy(*file, filename + i + 1, len - i - 1);
|
|
|
133 (*file)[len - i - 1] = L'\0';
|
|
|
134 }
|
|
|
135
|
|
|
136 return 0;
|
|
|
137 }
|
|
|
138
|
|
|
139
|
|
|
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;
|
|
|
147 handle->dirw = NULL;
|
|
|
148
|
|
|
149 UV_REQ_INIT(&handle->req, UV_FS_EVENT_REQ);
|
|
|
150 handle->req.data = handle;
|
|
|
151
|
|
|
152 return 0;
|
|
|
153 }
|
|
|
154
|
|
|
155
|
|
|
156 int uv_fs_event_start(uv_fs_event_t* handle,
|
|
|
157 uv_fs_event_cb cb,
|
|
|
158 const char* path,
|
|
|
159 unsigned int flags) {
|
|
|
160 int is_path_dir;
|
|
|
161 size_t size;
|
|
|
162 DWORD attr, last_error;
|
|
|
163 WCHAR* dir = NULL, *dir_to_watch, *pathw = NULL;
|
|
|
164 DWORD short_path_buffer_len;
|
|
|
165 WCHAR *short_path_buffer;
|
|
|
166 WCHAR* short_path, *long_path;
|
|
|
167
|
|
|
168 short_path = NULL;
|
|
|
169 if (uv__is_active(handle))
|
|
|
170 return UV_EINVAL;
|
|
|
171
|
|
|
172 handle->cb = cb;
|
|
|
173 handle->path = uv__strdup(path);
|
|
|
174 if (!handle->path) {
|
|
|
175 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
|
|
176 }
|
|
|
177
|
|
|
178 uv__handle_start(handle);
|
|
|
179
|
|
|
180 last_error = uv__convert_utf8_to_utf16(path, &pathw);
|
|
|
181 if (last_error)
|
|
|
182 goto error_uv;
|
|
|
183
|
|
|
184 /* Determine whether path is a file or a directory. */
|
|
|
185 attr = GetFileAttributesW(pathw);
|
|
|
186 if (attr == INVALID_FILE_ATTRIBUTES) {
|
|
|
187 last_error = GetLastError();
|
|
|
188 goto error;
|
|
|
189 }
|
|
|
190
|
|
|
191 is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;
|
|
|
192
|
|
|
193 if (is_path_dir) {
|
|
|
194 /* path is a directory, so that's the directory that we will watch. */
|
|
|
195
|
|
|
196 /* Convert to long path. */
|
|
|
197 size = GetLongPathNameW(pathw, NULL, 0);
|
|
|
198
|
|
|
199 if (size) {
|
|
|
200 long_path = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
|
|
|
201 if (!long_path) {
|
|
|
202 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
|
|
203 }
|
|
|
204
|
|
|
205 size = GetLongPathNameW(pathw, long_path, size);
|
|
|
206 if (size) {
|
|
|
207 long_path[size] = '\0';
|
|
|
208 } else {
|
|
|
209 uv__free(long_path);
|
|
|
210 long_path = NULL;
|
|
|
211 }
|
|
|
212
|
|
|
213 if (long_path) {
|
|
|
214 uv__free(pathw);
|
|
|
215 pathw = long_path;
|
|
|
216 }
|
|
|
217 }
|
|
|
218
|
|
|
219 dir_to_watch = pathw;
|
|
|
220 } else {
|
|
|
221 /*
|
|
|
222 * path is a file. So we split path into dir & file parts, and
|
|
|
223 * watch the dir directory.
|
|
|
224 */
|
|
|
225
|
|
|
226 /* Convert to short path. */
|
|
|
227 short_path_buffer = NULL;
|
|
|
228 short_path_buffer_len = GetShortPathNameW(pathw, NULL, 0);
|
|
|
229 if (short_path_buffer_len == 0) {
|
|
|
230 goto short_path_done;
|
|
|
231 }
|
|
|
232 short_path_buffer = uv__malloc(short_path_buffer_len * sizeof(WCHAR));
|
|
|
233 if (short_path_buffer == NULL) {
|
|
|
234 goto short_path_done;
|
|
|
235 }
|
|
|
236 if (GetShortPathNameW(pathw,
|
|
|
237 short_path_buffer,
|
|
|
238 short_path_buffer_len) == 0) {
|
|
|
239 uv__free(short_path_buffer);
|
|
|
240 short_path_buffer = NULL;
|
|
|
241 }
|
|
|
242 short_path_done:
|
|
|
243 short_path = short_path_buffer;
|
|
|
244
|
|
|
245 if (uv__split_path(pathw, &dir, &handle->filew) != 0) {
|
|
|
246 last_error = GetLastError();
|
|
|
247 goto error;
|
|
|
248 }
|
|
|
249
|
|
|
250 if (uv__split_path(short_path, NULL, &handle->short_filew) != 0) {
|
|
|
251 last_error = GetLastError();
|
|
|
252 goto error;
|
|
|
253 }
|
|
|
254
|
|
|
255 dir_to_watch = dir;
|
|
|
256 uv__free(short_path);
|
|
|
257 short_path = NULL;
|
|
|
258 uv__free(pathw);
|
|
|
259 pathw = NULL;
|
|
|
260 }
|
|
|
261
|
|
|
262 handle->dir_handle = CreateFileW(dir_to_watch,
|
|
|
263 FILE_LIST_DIRECTORY,
|
|
|
264 FILE_SHARE_READ | FILE_SHARE_DELETE |
|
|
|
265 FILE_SHARE_WRITE,
|
|
|
266 NULL,
|
|
|
267 OPEN_EXISTING,
|
|
|
268 FILE_FLAG_BACKUP_SEMANTICS |
|
|
|
269 FILE_FLAG_OVERLAPPED,
|
|
|
270 NULL);
|
|
|
271
|
|
|
272 if (dir) {
|
|
|
273 uv__free(dir);
|
|
|
274 dir = NULL;
|
|
|
275 }
|
|
|
276
|
|
|
277 if (handle->dir_handle == INVALID_HANDLE_VALUE) {
|
|
|
278 last_error = GetLastError();
|
|
|
279 goto error;
|
|
|
280 }
|
|
|
281
|
|
|
282 if (CreateIoCompletionPort(handle->dir_handle,
|
|
|
283 handle->loop->iocp,
|
|
|
284 (ULONG_PTR)handle,
|
|
|
285 0) == NULL) {
|
|
|
286 last_error = GetLastError();
|
|
|
287 goto error;
|
|
|
288 }
|
|
|
289
|
|
|
290 if (!handle->buffer) {
|
|
|
291 handle->buffer = (char*)uv__malloc(uv_directory_watcher_buffer_size);
|
|
|
292 }
|
|
|
293 if (!handle->buffer) {
|
|
|
294 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
|
|
295 }
|
|
|
296
|
|
|
297 memset(&(handle->req.u.io.overlapped), 0,
|
|
|
298 sizeof(handle->req.u.io.overlapped));
|
|
|
299
|
|
|
300 if (!ReadDirectoryChangesW(handle->dir_handle,
|
|
|
301 handle->buffer,
|
|
|
302 uv_directory_watcher_buffer_size,
|
|
|
303 (flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE,
|
|
|
304 FILE_NOTIFY_CHANGE_FILE_NAME |
|
|
|
305 FILE_NOTIFY_CHANGE_DIR_NAME |
|
|
|
306 FILE_NOTIFY_CHANGE_ATTRIBUTES |
|
|
|
307 FILE_NOTIFY_CHANGE_SIZE |
|
|
|
308 FILE_NOTIFY_CHANGE_LAST_WRITE |
|
|
|
309 FILE_NOTIFY_CHANGE_LAST_ACCESS |
|
|
|
310 FILE_NOTIFY_CHANGE_CREATION |
|
|
|
311 FILE_NOTIFY_CHANGE_SECURITY,
|
|
|
312 NULL,
|
|
|
313 &handle->req.u.io.overlapped,
|
|
|
314 NULL)) {
|
|
|
315 last_error = GetLastError();
|
|
|
316 goto error;
|
|
|
317 }
|
|
|
318
|
|
|
319 assert(is_path_dir ? pathw != NULL : pathw == NULL);
|
|
|
320 handle->dirw = pathw;
|
|
|
321 handle->req_pending = 1;
|
|
|
322 return 0;
|
|
|
323
|
|
|
324 error:
|
|
|
325 last_error = uv_translate_sys_error(last_error);
|
|
|
326
|
|
|
327 error_uv:
|
|
|
328 if (handle->path) {
|
|
|
329 uv__free(handle->path);
|
|
|
330 handle->path = NULL;
|
|
|
331 }
|
|
|
332
|
|
|
333 if (handle->filew) {
|
|
|
334 uv__free(handle->filew);
|
|
|
335 handle->filew = NULL;
|
|
|
336 }
|
|
|
337
|
|
|
338 if (handle->short_filew) {
|
|
|
339 uv__free(handle->short_filew);
|
|
|
340 handle->short_filew = NULL;
|
|
|
341 }
|
|
|
342
|
|
|
343 uv__free(pathw);
|
|
|
344
|
|
|
345 if (handle->dir_handle != INVALID_HANDLE_VALUE) {
|
|
|
346 CloseHandle(handle->dir_handle);
|
|
|
347 handle->dir_handle = INVALID_HANDLE_VALUE;
|
|
|
348 }
|
|
|
349
|
|
|
350 if (handle->buffer) {
|
|
|
351 uv__free(handle->buffer);
|
|
|
352 handle->buffer = NULL;
|
|
|
353 }
|
|
|
354
|
|
|
355 if (uv__is_active(handle))
|
|
|
356 uv__handle_stop(handle);
|
|
|
357
|
|
|
358 uv__free(short_path);
|
|
|
359
|
|
|
360 return last_error;
|
|
|
361 }
|
|
|
362
|
|
|
363
|
|
|
364 int uv_fs_event_stop(uv_fs_event_t* handle) {
|
|
|
365 if (!uv__is_active(handle))
|
|
|
366 return 0;
|
|
|
367
|
|
|
368 if (handle->dir_handle != INVALID_HANDLE_VALUE) {
|
|
|
369 CloseHandle(handle->dir_handle);
|
|
|
370 handle->dir_handle = INVALID_HANDLE_VALUE;
|
|
|
371 }
|
|
|
372
|
|
|
373 uv__handle_stop(handle);
|
|
|
374
|
|
|
375 if (handle->filew) {
|
|
|
376 uv__free(handle->filew);
|
|
|
377 handle->filew = NULL;
|
|
|
378 }
|
|
|
379
|
|
|
380 if (handle->short_filew) {
|
|
|
381 uv__free(handle->short_filew);
|
|
|
382 handle->short_filew = NULL;
|
|
|
383 }
|
|
|
384
|
|
|
385 if (handle->path) {
|
|
|
386 uv__free(handle->path);
|
|
|
387 handle->path = NULL;
|
|
|
388 }
|
|
|
389
|
|
|
390 if (handle->dirw) {
|
|
|
391 uv__free(handle->dirw);
|
|
|
392 handle->dirw = NULL;
|
|
|
393 }
|
|
|
394
|
|
|
395 return 0;
|
|
|
396 }
|
|
|
397
|
|
|
398
|
|
|
399 static int file_info_cmp(WCHAR* str, WCHAR* file_name, size_t file_name_len) {
|
|
|
400 size_t str_len;
|
|
|
401
|
|
|
402 if (str == NULL)
|
|
|
403 return -1;
|
|
|
404
|
|
|
405 str_len = wcslen(str);
|
|
|
406
|
|
|
407 /*
|
|
|
408 Since we only care about equality, return early if the strings
|
|
|
409 aren't the same length
|
|
|
410 */
|
|
|
411 if (str_len != (file_name_len / sizeof(WCHAR)))
|
|
|
412 return -1;
|
|
|
413
|
|
|
414 return _wcsnicmp(str, file_name, str_len);
|
|
|
415 }
|
|
|
416
|
|
|
417
|
|
|
418 void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
|
|
|
419 uv_fs_event_t* handle) {
|
|
|
420 FILE_NOTIFY_INFORMATION* file_info;
|
|
|
421 int err, sizew, size;
|
|
|
422 char* filename = NULL;
|
|
|
423 WCHAR* filenamew = NULL;
|
|
|
424 WCHAR* long_filenamew = NULL;
|
|
|
425 DWORD offset = 0;
|
|
|
426
|
|
|
427 assert(req->type == UV_FS_EVENT_REQ);
|
|
|
428 assert(handle->req_pending);
|
|
|
429 handle->req_pending = 0;
|
|
|
430
|
|
|
431 /* Don't report any callbacks if:
|
|
|
432 * - We're closing, just push the handle onto the endgame queue
|
|
|
433 * - We are not active, just ignore the callback
|
|
|
434 */
|
|
|
435 if (!uv__is_active(handle)) {
|
|
|
436 if (handle->flags & UV_HANDLE_CLOSING) {
|
|
|
437 uv__want_endgame(loop, (uv_handle_t*) handle);
|
|
|
438 }
|
|
|
439 return;
|
|
|
440 }
|
|
|
441
|
|
|
442 file_info = (FILE_NOTIFY_INFORMATION*)(handle->buffer + offset);
|
|
|
443
|
|
|
444 if (REQ_SUCCESS(req)) {
|
|
|
445 if (req->u.io.overlapped.InternalHigh > 0) {
|
|
|
446 do {
|
|
|
447 file_info = (FILE_NOTIFY_INFORMATION*)((char*)file_info + offset);
|
|
|
448 assert(!filename);
|
|
|
449 assert(!filenamew);
|
|
|
450 assert(!long_filenamew);
|
|
|
451
|
|
|
452 /*
|
|
|
453 * Fire the event only if we were asked to watch a directory,
|
|
|
454 * or if the filename filter matches.
|
|
|
455 */
|
|
|
456 if (handle->dirw ||
|
|
|
457 file_info_cmp(handle->filew,
|
|
|
458 file_info->FileName,
|
|
|
459 file_info->FileNameLength) == 0 ||
|
|
|
460 file_info_cmp(handle->short_filew,
|
|
|
461 file_info->FileName,
|
|
|
462 file_info->FileNameLength) == 0) {
|
|
|
463
|
|
|
464 if (handle->dirw) {
|
|
|
465 /*
|
|
|
466 * We attempt to resolve the long form of the file name explicitly.
|
|
|
467 * We only do this for file names that might still exist on disk.
|
|
|
468 * If this fails, we use the name given by ReadDirectoryChangesW.
|
|
|
469 * This may be the long form or the 8.3 short name in some cases.
|
|
|
470 */
|
|
|
471 if (file_info->Action != FILE_ACTION_REMOVED &&
|
|
|
472 file_info->Action != FILE_ACTION_RENAMED_OLD_NAME) {
|
|
|
473 /* Construct a full path to the file. */
|
|
|
474 size = wcslen(handle->dirw) +
|
|
|
475 file_info->FileNameLength / sizeof(WCHAR) + 2;
|
|
|
476
|
|
|
477 filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
|
|
|
478 if (!filenamew) {
|
|
|
479 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
|
|
480 }
|
|
|
481
|
|
|
482 _snwprintf(filenamew, size, L"%s\\%.*s", handle->dirw,
|
|
|
483 file_info->FileNameLength / (DWORD)sizeof(WCHAR),
|
|
|
484 file_info->FileName);
|
|
|
485
|
|
|
486 filenamew[size - 1] = L'\0';
|
|
|
487
|
|
|
488 /* Convert to long name. */
|
|
|
489 size = GetLongPathNameW(filenamew, NULL, 0);
|
|
|
490
|
|
|
491 if (size) {
|
|
|
492 long_filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
|
|
|
493 if (!long_filenamew) {
|
|
|
494 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
|
|
495 }
|
|
|
496
|
|
|
497 size = GetLongPathNameW(filenamew, long_filenamew, size);
|
|
|
498 if (size) {
|
|
|
499 long_filenamew[size] = '\0';
|
|
|
500 } else {
|
|
|
501 uv__free(long_filenamew);
|
|
|
502 long_filenamew = NULL;
|
|
|
503 }
|
|
|
504 }
|
|
|
505
|
|
|
506 uv__free(filenamew);
|
|
|
507
|
|
|
508 if (long_filenamew) {
|
|
|
509 /* Get the file name out of the long path. */
|
|
|
510 uv__relative_path(long_filenamew,
|
|
|
511 handle->dirw,
|
|
|
512 &filenamew);
|
|
|
513 uv__free(long_filenamew);
|
|
|
514 long_filenamew = filenamew;
|
|
|
515 sizew = -1;
|
|
|
516 } else {
|
|
|
517 /* We couldn't get the long filename, use the one reported. */
|
|
|
518 filenamew = file_info->FileName;
|
|
|
519 sizew = file_info->FileNameLength / sizeof(WCHAR);
|
|
|
520 }
|
|
|
521 } else {
|
|
|
522 /*
|
|
|
523 * Removed or renamed events cannot be resolved to the long form.
|
|
|
524 * We therefore use the name given by ReadDirectoryChangesW.
|
|
|
525 * This may be the long form or the 8.3 short name in some cases.
|
|
|
526 */
|
|
|
527 filenamew = file_info->FileName;
|
|
|
528 sizew = file_info->FileNameLength / sizeof(WCHAR);
|
|
|
529 }
|
|
|
530 } else {
|
|
|
531 /* We already have the long name of the file, so just use it. */
|
|
|
532 filenamew = handle->filew;
|
|
|
533 sizew = -1;
|
|
|
534 }
|
|
|
535
|
|
|
536 /* Convert the filename to utf8. */
|
|
|
537 uv__convert_utf16_to_utf8(filenamew, sizew, &filename);
|
|
|
538
|
|
|
539 switch (file_info->Action) {
|
|
|
540 case FILE_ACTION_ADDED:
|
|
|
541 case FILE_ACTION_REMOVED:
|
|
|
542 case FILE_ACTION_RENAMED_OLD_NAME:
|
|
|
543 case FILE_ACTION_RENAMED_NEW_NAME:
|
|
|
544 handle->cb(handle, filename, UV_RENAME, 0);
|
|
|
545 break;
|
|
|
546
|
|
|
547 case FILE_ACTION_MODIFIED:
|
|
|
548 handle->cb(handle, filename, UV_CHANGE, 0);
|
|
|
549 break;
|
|
|
550 }
|
|
|
551
|
|
|
552 uv__free(filename);
|
|
|
553 filename = NULL;
|
|
|
554 uv__free(long_filenamew);
|
|
|
555 long_filenamew = NULL;
|
|
|
556 filenamew = NULL;
|
|
|
557 }
|
|
|
558
|
|
|
559 offset = file_info->NextEntryOffset;
|
|
|
560 } while (offset && !(handle->flags & UV_HANDLE_CLOSING));
|
|
|
561 } else {
|
|
|
562 handle->cb(handle, NULL, UV_CHANGE, 0);
|
|
|
563 }
|
|
|
564 } else {
|
|
|
565 err = GET_REQ_ERROR(req);
|
|
|
566 /*
|
|
|
567 * Check whether the ERROR_ACCESS_DENIED is caused by the watched directory
|
|
|
568 * being actually deleted (not an actual error) or a legit error. Retrieve
|
|
|
569 * FileStandardInfo to check whether the directory is pending deletion.
|
|
|
570 */
|
|
|
571 FILE_STANDARD_INFO info;
|
|
|
572 if (err == ERROR_ACCESS_DENIED &&
|
|
|
573 handle->dirw != NULL &&
|
|
|
574 GetFileInformationByHandleEx(handle->dir_handle,
|
|
|
575 FileStandardInfo,
|
|
|
576 &info,
|
|
|
577 sizeof(info)) &&
|
|
|
578 info.Directory &&
|
|
|
579 info.DeletePending) {
|
|
|
580 uv__convert_utf16_to_utf8(handle->dirw, -1, &filename);
|
|
|
581 handle->cb(handle, filename, UV_RENAME, 0);
|
|
|
582 uv__free(filename);
|
|
|
583 filename = NULL;
|
|
|
584 } else {
|
|
|
585 handle->cb(handle, NULL, 0, uv_translate_sys_error(err));
|
|
|
586 }
|
|
|
587 }
|
|
|
588
|
|
|
589 if (handle->flags & UV_HANDLE_CLOSING) {
|
|
|
590 uv__want_endgame(loop, (uv_handle_t*)handle);
|
|
|
591 } else if (uv__is_active(handle)) {
|
|
|
592 uv__fs_event_queue_readdirchanges(loop, handle);
|
|
|
593 }
|
|
|
594 }
|
|
|
595
|
|
|
596
|
|
|
597 void uv__fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle) {
|
|
|
598 uv_fs_event_stop(handle);
|
|
|
599
|
|
|
600 uv__handle_closing(handle);
|
|
|
601
|
|
|
602 if (!handle->req_pending) {
|
|
|
603 uv__want_endgame(loop, (uv_handle_t*)handle);
|
|
|
604 }
|
|
|
605
|
|
|
606 }
|
|
|
607
|
|
|
608
|
|
|
609 void uv__fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) {
|
|
|
610 if ((handle->flags & UV_HANDLE_CLOSING) && !handle->req_pending) {
|
|
|
611 assert(!(handle->flags & UV_HANDLE_CLOSED));
|
|
|
612
|
|
|
613 if (handle->buffer) {
|
|
|
614 uv__free(handle->buffer);
|
|
|
615 handle->buffer = NULL;
|
|
|
616 }
|
|
|
617
|
|
|
618 uv__handle_close(handle);
|
|
|
619 }
|
|
|
620 }
|