]> git.proxmox.com Git - rustc.git/blob - src/libuv/src/win/fs-event.c
Imported Upstream version 0.6
[rustc.git] / src / libuv / src / win / fs-event.c
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 <malloc.h>
24 #include <errno.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "uv.h"
29 #include "internal.h"
30 #include "handle-inl.h"
31 #include "req-inl.h"
32
33
34 const unsigned int uv_directory_watcher_buffer_size = 4096;
35
36
37 static void uv_fs_event_init_handle(uv_loop_t* loop, uv_fs_event_t* handle,
38 const char* filename, uv_fs_event_cb cb) {
39 uv__handle_init(loop, (uv_handle_t*) handle, UV_FS_EVENT);
40 handle->cb = cb;
41 handle->dir_handle = INVALID_HANDLE_VALUE;
42 handle->buffer = NULL;
43 handle->req_pending = 0;
44 handle->filew = NULL;
45 handle->short_filew = NULL;
46 handle->dirw = NULL;
47
48 uv_req_init(loop, (uv_req_t*)&handle->req);
49 handle->req.type = UV_FS_EVENT_REQ;
50 handle->req.data = (void*)handle;
51
52 handle->filename = strdup(filename);
53 if (!handle->filename) {
54 uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
55 }
56
57 uv__handle_start(handle);
58 }
59
60
61 static void uv_fs_event_queue_readdirchanges(uv_loop_t* loop,
62 uv_fs_event_t* handle) {
63 assert(handle->dir_handle != INVALID_HANDLE_VALUE);
64 assert(!handle->req_pending);
65
66 memset(&(handle->req.overlapped), 0, sizeof(handle->req.overlapped));
67 if (!ReadDirectoryChangesW(handle->dir_handle,
68 handle->buffer,
69 uv_directory_watcher_buffer_size,
70 FALSE,
71 FILE_NOTIFY_CHANGE_FILE_NAME |
72 FILE_NOTIFY_CHANGE_DIR_NAME |
73 FILE_NOTIFY_CHANGE_ATTRIBUTES |
74 FILE_NOTIFY_CHANGE_SIZE |
75 FILE_NOTIFY_CHANGE_LAST_WRITE |
76 FILE_NOTIFY_CHANGE_LAST_ACCESS |
77 FILE_NOTIFY_CHANGE_CREATION |
78 FILE_NOTIFY_CHANGE_SECURITY,
79 NULL,
80 &handle->req.overlapped,
81 NULL)) {
82 /* Make this req pending reporting an error. */
83 SET_REQ_ERROR(&handle->req, GetLastError());
84 uv_insert_pending_req(loop, (uv_req_t*)&handle->req);
85 }
86
87 handle->req_pending = 1;
88 }
89
90
91 static int uv_split_path(const WCHAR* filename, WCHAR** dir,
92 WCHAR** file) {
93 int len = wcslen(filename);
94 int i = len;
95 while (i > 0 && filename[--i] != '\\' && filename[i] != '/');
96
97 if (i == 0) {
98 if (dir) {
99 *dir = (WCHAR*)malloc((MAX_PATH + 1) * sizeof(WCHAR));
100 if (!*dir) {
101 uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
102 }
103
104 if (!GetCurrentDirectoryW(MAX_PATH, *dir)) {
105 free(*dir);
106 *dir = NULL;
107 return -1;
108 }
109 }
110
111 *file = wcsdup(filename);
112 } else {
113 if (dir) {
114 *dir = (WCHAR*)malloc((i + 1) * sizeof(WCHAR));
115 if (!*dir) {
116 uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
117 }
118 wcsncpy(*dir, filename, i);
119 (*dir)[i] = L'\0';
120 }
121
122 *file = (WCHAR*)malloc((len - i) * sizeof(WCHAR));
123 if (!*file) {
124 uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
125 }
126 wcsncpy(*file, filename + i + 1, len - i - 1);
127 (*file)[len - i - 1] = L'\0';
128 }
129
130 return 0;
131 }
132
133
134 int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle,
135 const char* filename, uv_fs_event_cb cb, int flags) {
136 int name_size, is_path_dir;
137 DWORD attr, last_error;
138 WCHAR* dir = NULL, *dir_to_watch, *filenamew = NULL;
139 WCHAR short_path[MAX_PATH];
140
141 uv_fs_event_init_handle(loop, handle, filename, cb);
142
143 /* Convert name to UTF16. */
144 name_size = uv_utf8_to_utf16(filename, NULL, 0) * sizeof(WCHAR);
145 filenamew = (WCHAR*)malloc(name_size);
146 if (!filenamew) {
147 uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
148 }
149
150 if (!uv_utf8_to_utf16(filename, filenamew,
151 name_size / sizeof(WCHAR))) {
152 uv__set_sys_error(loop, GetLastError());
153 return -1;
154 }
155
156 /* Determine whether filename is a file or a directory. */
157 attr = GetFileAttributesW(filenamew);
158 if (attr == INVALID_FILE_ATTRIBUTES) {
159 last_error = GetLastError();
160 goto error;
161 }
162
163 is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;
164
165 if (is_path_dir) {
166 /* filename is a directory, so that's the directory that we will watch. */
167 handle->dirw = filenamew;
168 dir_to_watch = filenamew;
169 } else {
170 /*
171 * filename is a file. So we split filename into dir & file parts, and
172 * watch the dir directory.
173 */
174
175 /* Convert to short path. */
176 if (!GetShortPathNameW(filenamew, short_path, ARRAY_SIZE(short_path))) {
177 last_error = GetLastError();
178 goto error;
179 }
180
181 if (uv_split_path(filenamew, &dir, &handle->filew) != 0) {
182 last_error = GetLastError();
183 goto error;
184 }
185
186 if (uv_split_path(short_path, NULL, &handle->short_filew) != 0) {
187 last_error = GetLastError();
188 goto error;
189 }
190
191 dir_to_watch = dir;
192 free(filenamew);
193 filenamew = NULL;
194 }
195
196 handle->dir_handle = CreateFileW(dir_to_watch,
197 FILE_LIST_DIRECTORY,
198 FILE_SHARE_READ | FILE_SHARE_DELETE |
199 FILE_SHARE_WRITE,
200 NULL,
201 OPEN_EXISTING,
202 FILE_FLAG_BACKUP_SEMANTICS |
203 FILE_FLAG_OVERLAPPED,
204 NULL);
205
206 if (dir) {
207 free(dir);
208 dir = NULL;
209 }
210
211 if (handle->dir_handle == INVALID_HANDLE_VALUE) {
212 last_error = GetLastError();
213 goto error;
214 }
215
216 if (CreateIoCompletionPort(handle->dir_handle,
217 loop->iocp,
218 (ULONG_PTR)handle,
219 0) == NULL) {
220 last_error = GetLastError();
221 goto error;
222 }
223
224 handle->buffer = (char*)_aligned_malloc(uv_directory_watcher_buffer_size,
225 sizeof(DWORD));
226 if (!handle->buffer) {
227 uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
228 }
229
230 memset(&(handle->req.overlapped), 0, sizeof(handle->req.overlapped));
231
232 if (!ReadDirectoryChangesW(handle->dir_handle,
233 handle->buffer,
234 uv_directory_watcher_buffer_size,
235 FALSE,
236 FILE_NOTIFY_CHANGE_FILE_NAME |
237 FILE_NOTIFY_CHANGE_DIR_NAME |
238 FILE_NOTIFY_CHANGE_ATTRIBUTES |
239 FILE_NOTIFY_CHANGE_SIZE |
240 FILE_NOTIFY_CHANGE_LAST_WRITE |
241 FILE_NOTIFY_CHANGE_LAST_ACCESS |
242 FILE_NOTIFY_CHANGE_CREATION |
243 FILE_NOTIFY_CHANGE_SECURITY,
244 NULL,
245 &handle->req.overlapped,
246 NULL)) {
247 last_error = GetLastError();
248 goto error;
249 }
250
251 handle->req_pending = 1;
252 return 0;
253
254 error:
255 if (handle->filename) {
256 free(handle->filename);
257 handle->filename = NULL;
258 }
259
260 if (handle->filew) {
261 free(handle->filew);
262 handle->filew = NULL;
263 }
264
265 if (handle->short_filew) {
266 free(handle->short_filew);
267 handle->short_filew = NULL;
268 }
269
270 free(filenamew);
271
272 if (handle->dir_handle != INVALID_HANDLE_VALUE) {
273 CloseHandle(handle->dir_handle);
274 handle->dir_handle = INVALID_HANDLE_VALUE;
275 }
276
277 if (handle->buffer) {
278 _aligned_free(handle->buffer);
279 handle->buffer = NULL;
280 }
281
282 uv__set_sys_error(loop, last_error);
283 return -1;
284 }
285
286
287 void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
288 uv_fs_event_t* handle) {
289 FILE_NOTIFY_INFORMATION* file_info;
290 int sizew, size, result;
291 char* filename = NULL;
292 WCHAR* filenamew, *long_filenamew = NULL;
293 DWORD offset = 0;
294
295 assert(req->type == UV_FS_EVENT_REQ);
296 assert(handle->req_pending);
297 handle->req_pending = 0;
298
299 /* If we're closing, don't report any callbacks, and just push the handle */
300 /* onto the endgame queue. */
301 if (handle->flags & UV__HANDLE_CLOSING) {
302 uv_want_endgame(loop, (uv_handle_t*) handle);
303 return;
304 };
305
306 file_info = (FILE_NOTIFY_INFORMATION*)(handle->buffer + offset);
307
308 if (REQ_SUCCESS(req)) {
309 if (req->overlapped.InternalHigh > 0) {
310 do {
311 file_info = (FILE_NOTIFY_INFORMATION*)((char*)file_info + offset);
312 assert(!filename);
313 assert(!long_filenamew);
314
315 /*
316 * Fire the event only if we were asked to watch a directory,
317 * or if the filename filter matches.
318 */
319 if (handle->dirw ||
320 _wcsnicmp(handle->filew, file_info->FileName,
321 file_info->FileNameLength / sizeof(WCHAR)) == 0 ||
322 _wcsnicmp(handle->short_filew, file_info->FileName,
323 file_info->FileNameLength / sizeof(WCHAR)) == 0) {
324
325 if (handle->dirw) {
326 /*
327 * We attempt to convert the file name to its long form for
328 * events that still point to valid files on disk.
329 * For removed and renamed events, we do not provide the file name.
330 */
331 if (file_info->Action != FILE_ACTION_REMOVED &&
332 file_info->Action != FILE_ACTION_RENAMED_OLD_NAME) {
333 /* Construct a full path to the file. */
334 size = wcslen(handle->dirw) +
335 file_info->FileNameLength / sizeof(WCHAR) + 2;
336
337 filenamew = (WCHAR*)malloc(size * sizeof(WCHAR));
338 if (!filenamew) {
339 uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
340 }
341
342 _snwprintf(filenamew, size, L"%s\\%s", handle->dirw,
343 file_info->FileName);
344
345 filenamew[size - 1] = L'\0';
346
347 /* Convert to long name. */
348 size = GetLongPathNameW(filenamew, NULL, 0);
349
350 if (size) {
351 long_filenamew = (WCHAR*)malloc(size * sizeof(WCHAR));
352 if (!long_filenamew) {
353 uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
354 }
355
356 size = GetLongPathNameW(filenamew, long_filenamew, size);
357 if (size) {
358 long_filenamew[size] = '\0';
359 } else {
360 free(long_filenamew);
361 long_filenamew = NULL;
362 }
363 }
364
365 free(filenamew);
366
367 if (long_filenamew) {
368 /* Get the file name out of the long path. */
369 result = uv_split_path(long_filenamew, NULL, &filenamew);
370 free(long_filenamew);
371
372 if (result == 0) {
373 long_filenamew = filenamew;
374 sizew = -1;
375 } else {
376 long_filenamew = NULL;
377 }
378 }
379
380 /*
381 * If we couldn't get the long name - just use the name
382 * provided by ReadDirectoryChangesW.
383 */
384 if (!long_filenamew) {
385 filenamew = file_info->FileName;
386 sizew = file_info->FileNameLength / sizeof(WCHAR);
387 }
388 } else {
389 /* Removed or renamed callbacks don't provide filename. */
390 filenamew = NULL;
391 }
392 } else {
393 /* We already have the long name of the file, so just use it. */
394 filenamew = handle->filew;
395 sizew = -1;
396 }
397
398 if (filenamew) {
399 /* Convert the filename to utf8. */
400 size = uv_utf16_to_utf8(filenamew,
401 sizew,
402 NULL,
403 0);
404 if (size) {
405 filename = (char*)malloc(size + 1);
406 if (!filename) {
407 uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
408 }
409
410 size = uv_utf16_to_utf8(filenamew,
411 sizew,
412 filename,
413 size);
414 if (size) {
415 filename[size] = '\0';
416 } else {
417 free(filename);
418 filename = NULL;
419 }
420 }
421 }
422
423 switch (file_info->Action) {
424 case FILE_ACTION_ADDED:
425 case FILE_ACTION_REMOVED:
426 case FILE_ACTION_RENAMED_OLD_NAME:
427 case FILE_ACTION_RENAMED_NEW_NAME:
428 handle->cb(handle, filename, UV_RENAME, 0);
429 break;
430
431 case FILE_ACTION_MODIFIED:
432 handle->cb(handle, filename, UV_CHANGE, 0);
433 break;
434 }
435
436 free(filename);
437 filename = NULL;
438 free(long_filenamew);
439 long_filenamew = NULL;
440 }
441
442 offset = file_info->NextEntryOffset;
443 } while (offset && !(handle->flags & UV__HANDLE_CLOSING));
444 } else {
445 handle->cb(handle, NULL, UV_CHANGE, 0);
446 }
447 } else {
448 uv__set_sys_error(loop, GET_REQ_ERROR(req));
449 handle->cb(handle, NULL, 0, -1);
450 }
451
452 if (!(handle->flags & UV__HANDLE_CLOSING)) {
453 uv_fs_event_queue_readdirchanges(loop, handle);
454 } else {
455 uv_want_endgame(loop, (uv_handle_t*)handle);
456 }
457 }
458
459
460 void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle) {
461 if (handle->dir_handle != INVALID_HANDLE_VALUE) {
462 CloseHandle(handle->dir_handle);
463 handle->dir_handle = INVALID_HANDLE_VALUE;
464 }
465
466 if (!handle->req_pending) {
467 uv_want_endgame(loop, (uv_handle_t*)handle);
468 }
469
470 uv__handle_closing(handle);
471 }
472
473
474 void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) {
475 if (handle->flags & UV__HANDLE_CLOSING &&
476 !handle->req_pending) {
477 assert(!(handle->flags & UV_HANDLE_CLOSED));
478
479 if (handle->buffer) {
480 _aligned_free(handle->buffer);
481 handle->buffer = NULL;
482 }
483
484 if (handle->filew) {
485 free(handle->filew);
486 handle->filew = NULL;
487 }
488
489 if (handle->short_filew) {
490 free(handle->short_filew);
491 handle->short_filew = NULL;
492 }
493
494 if (handle->filename) {
495 free(handle->filename);
496 handle->filename = NULL;
497 }
498
499 if (handle->dirw) {
500 free(handle->dirw);
501 handle->dirw = NULL;
502 }
503
504 uv__handle_close(handle);
505 }
506 }