Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Kitware
GitHub Repository: Kitware/CMake
Path: blob/master/Utilities/cmlibuv/src/win/fs-event.c
3153 views
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 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;
166
167
short_path = NULL;
168
if (uv__is_active(handle))
169
return UV_EINVAL;
170
171
handle->cb = cb;
172
handle->path = uv__strdup(path);
173
if (!handle->path) {
174
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
175
}
176
177
uv__handle_start(handle);
178
179
/* Convert name to UTF16. */
180
181
name_size = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0) *
182
sizeof(WCHAR);
183
pathw = (WCHAR*)uv__malloc(name_size);
184
if (!pathw) {
185
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
186
}
187
188
if (!MultiByteToWideChar(CP_UTF8,
189
0,
190
path,
191
-1,
192
pathw,
193
name_size / sizeof(WCHAR))) {
194
return uv_translate_sys_error(GetLastError());
195
}
196
197
/* Determine whether path is a file or a directory. */
198
attr = GetFileAttributesW(pathw);
199
if (attr == INVALID_FILE_ATTRIBUTES) {
200
last_error = GetLastError();
201
goto error;
202
}
203
204
is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;
205
206
if (is_path_dir) {
207
/* path is a directory, so that's the directory that we will watch. */
208
209
/* Convert to long path. */
210
size = GetLongPathNameW(pathw, NULL, 0);
211
212
if (size) {
213
long_path = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
214
if (!long_path) {
215
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
216
}
217
218
size = GetLongPathNameW(pathw, long_path, size);
219
if (size) {
220
long_path[size] = '\0';
221
} else {
222
uv__free(long_path);
223
long_path = NULL;
224
}
225
226
if (long_path) {
227
uv__free(pathw);
228
pathw = long_path;
229
}
230
}
231
232
dir_to_watch = pathw;
233
} else {
234
/*
235
* path is a file. So we split path into dir & file parts, and
236
* watch the dir directory.
237
*/
238
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;
244
}
245
short_path_buffer = uv__malloc(short_path_buffer_len * sizeof(WCHAR));
246
if (short_path_buffer == NULL) {
247
goto short_path_done;
248
}
249
if (GetShortPathNameW(pathw,
250
short_path_buffer,
251
short_path_buffer_len) == 0) {
252
uv__free(short_path_buffer);
253
short_path_buffer = NULL;
254
}
255
short_path_done:
256
short_path = short_path_buffer;
257
258
if (uv__split_path(pathw, &dir, &handle->filew) != 0) {
259
last_error = GetLastError();
260
goto error;
261
}
262
263
if (uv__split_path(short_path, NULL, &handle->short_filew) != 0) {
264
last_error = GetLastError();
265
goto error;
266
}
267
268
dir_to_watch = dir;
269
uv__free(pathw);
270
pathw = NULL;
271
}
272
273
handle->dir_handle = CreateFileW(dir_to_watch,
274
FILE_LIST_DIRECTORY,
275
FILE_SHARE_READ | FILE_SHARE_DELETE |
276
FILE_SHARE_WRITE,
277
NULL,
278
OPEN_EXISTING,
279
FILE_FLAG_BACKUP_SEMANTICS |
280
FILE_FLAG_OVERLAPPED,
281
NULL);
282
283
if (dir) {
284
uv__free(dir);
285
dir = NULL;
286
}
287
288
if (handle->dir_handle == INVALID_HANDLE_VALUE) {
289
last_error = GetLastError();
290
goto error;
291
}
292
293
if (CreateIoCompletionPort(handle->dir_handle,
294
handle->loop->iocp,
295
(ULONG_PTR)handle,
296
0) == NULL) {
297
last_error = GetLastError();
298
goto error;
299
}
300
301
if (!handle->buffer) {
302
handle->buffer = (char*)uv__malloc(uv_directory_watcher_buffer_size);
303
}
304
if (!handle->buffer) {
305
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
306
}
307
308
memset(&(handle->req.u.io.overlapped), 0,
309
sizeof(handle->req.u.io.overlapped));
310
311
if (!ReadDirectoryChangesW(handle->dir_handle,
312
handle->buffer,
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,
323
NULL,
324
&handle->req.u.io.overlapped,
325
NULL)) {
326
last_error = GetLastError();
327
goto error;
328
}
329
330
assert(is_path_dir ? pathw != NULL : pathw == NULL);
331
handle->dirw = pathw;
332
handle->req_pending = 1;
333
return 0;
334
335
error:
336
if (handle->path) {
337
uv__free(handle->path);
338
handle->path = NULL;
339
}
340
341
if (handle->filew) {
342
uv__free(handle->filew);
343
handle->filew = NULL;
344
}
345
346
if (handle->short_filew) {
347
uv__free(handle->short_filew);
348
handle->short_filew = NULL;
349
}
350
351
uv__free(pathw);
352
353
if (handle->dir_handle != INVALID_HANDLE_VALUE) {
354
CloseHandle(handle->dir_handle);
355
handle->dir_handle = INVALID_HANDLE_VALUE;
356
}
357
358
if (handle->buffer) {
359
uv__free(handle->buffer);
360
handle->buffer = NULL;
361
}
362
363
if (uv__is_active(handle))
364
uv__handle_stop(handle);
365
366
uv__free(short_path);
367
368
return uv_translate_sys_error(last_error);
369
}
370
371
372
int uv_fs_event_stop(uv_fs_event_t* handle) {
373
if (!uv__is_active(handle))
374
return 0;
375
376
if (handle->dir_handle != INVALID_HANDLE_VALUE) {
377
CloseHandle(handle->dir_handle);
378
handle->dir_handle = INVALID_HANDLE_VALUE;
379
}
380
381
uv__handle_stop(handle);
382
383
if (handle->filew) {
384
uv__free(handle->filew);
385
handle->filew = NULL;
386
}
387
388
if (handle->short_filew) {
389
uv__free(handle->short_filew);
390
handle->short_filew = NULL;
391
}
392
393
if (handle->path) {
394
uv__free(handle->path);
395
handle->path = NULL;
396
}
397
398
if (handle->dirw) {
399
uv__free(handle->dirw);
400
handle->dirw = NULL;
401
}
402
403
return 0;
404
}
405
406
407
static int file_info_cmp(WCHAR* str, WCHAR* file_name, size_t file_name_len) {
408
size_t str_len;
409
410
if (str == NULL)
411
return -1;
412
413
str_len = wcslen(str);
414
415
/*
416
Since we only care about equality, return early if the strings
417
aren't the same length
418
*/
419
if (str_len != (file_name_len / sizeof(WCHAR)))
420
return -1;
421
422
return _wcsnicmp(str, file_name, str_len);
423
}
424
425
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;
433
DWORD offset = 0;
434
435
assert(req->type == UV_FS_EVENT_REQ);
436
assert(handle->req_pending);
437
handle->req_pending = 0;
438
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
442
*/
443
if (!uv__is_active(handle)) {
444
if (handle->flags & UV_HANDLE_CLOSING) {
445
uv__want_endgame(loop, (uv_handle_t*) handle);
446
}
447
return;
448
}
449
450
file_info = (FILE_NOTIFY_INFORMATION*)(handle->buffer + offset);
451
452
if (REQ_SUCCESS(req)) {
453
if (req->u.io.overlapped.InternalHigh > 0) {
454
do {
455
file_info = (FILE_NOTIFY_INFORMATION*)((char*)file_info + offset);
456
assert(!filename);
457
assert(!filenamew);
458
assert(!long_filenamew);
459
460
/*
461
* Fire the event only if we were asked to watch a directory,
462
* or if the filename filter matches.
463
*/
464
if (handle->dirw ||
465
file_info_cmp(handle->filew,
466
file_info->FileName,
467
file_info->FileNameLength) == 0 ||
468
file_info_cmp(handle->short_filew,
469
file_info->FileName,
470
file_info->FileNameLength) == 0) {
471
472
if (handle->dirw) {
473
/*
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.
478
*/
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;
484
485
filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
486
if (!filenamew) {
487
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
488
}
489
490
_snwprintf(filenamew, size, L"%s\\%.*s", handle->dirw,
491
file_info->FileNameLength / (DWORD)sizeof(WCHAR),
492
file_info->FileName);
493
494
filenamew[size - 1] = L'\0';
495
496
/* Convert to long name. */
497
size = GetLongPathNameW(filenamew, NULL, 0);
498
499
if (size) {
500
long_filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
501
if (!long_filenamew) {
502
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
503
}
504
505
size = GetLongPathNameW(filenamew, long_filenamew, size);
506
if (size) {
507
long_filenamew[size] = '\0';
508
} else {
509
uv__free(long_filenamew);
510
long_filenamew = NULL;
511
}
512
}
513
514
uv__free(filenamew);
515
516
if (long_filenamew) {
517
/* Get the file name out of the long path. */
518
uv__relative_path(long_filenamew,
519
handle->dirw,
520
&filenamew);
521
uv__free(long_filenamew);
522
long_filenamew = filenamew;
523
sizew = -1;
524
} else {
525
/* We couldn't get the long filename, use the one reported. */
526
filenamew = file_info->FileName;
527
sizew = file_info->FileNameLength / sizeof(WCHAR);
528
}
529
} else {
530
/*
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.
534
*/
535
filenamew = file_info->FileName;
536
sizew = file_info->FileNameLength / sizeof(WCHAR);
537
}
538
} else {
539
/* We already have the long name of the file, so just use it. */
540
filenamew = handle->filew;
541
sizew = -1;
542
}
543
544
/* Convert the filename to utf8. */
545
uv__convert_utf16_to_utf8(filenamew, sizew, &filename);
546
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);
553
break;
554
555
case FILE_ACTION_MODIFIED:
556
handle->cb(handle, filename, UV_CHANGE, 0);
557
break;
558
}
559
560
uv__free(filename);
561
filename = NULL;
562
uv__free(long_filenamew);
563
long_filenamew = NULL;
564
filenamew = NULL;
565
}
566
567
offset = file_info->NextEntryOffset;
568
} while (offset && !(handle->flags & UV_HANDLE_CLOSING));
569
} else {
570
handle->cb(handle, NULL, UV_CHANGE, 0);
571
}
572
} else {
573
err = GET_REQ_ERROR(req);
574
handle->cb(handle, NULL, 0, uv_translate_sys_error(err));
575
}
576
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);
581
}
582
}
583
584
585
void uv__fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle) {
586
uv_fs_event_stop(handle);
587
588
uv__handle_closing(handle);
589
590
if (!handle->req_pending) {
591
uv__want_endgame(loop, (uv_handle_t*)handle);
592
}
593
594
}
595
596
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));
600
601
if (handle->buffer) {
602
uv__free(handle->buffer);
603
handle->buffer = NULL;
604
}
605
606
uv__handle_close(handle);
607
}
608
}
609
610