Rizin
unix-like reverse engineering framework and cli tools
fs-event.c
Go to the documentation of this file.
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 
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,
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());
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 
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 
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 
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) {
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,
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))
365 
366  uv__free(short_path);
367 
368  return uv_translate_sys_error(last_error);
369 }
370 
371 
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 
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 
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) {
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 
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);
575  }
576 
577  if (!(handle->flags & UV_HANDLE_CLOSING)) {
579  } else {
581  }
582 }
583 
584 
587 
589 
590  if (!handle->req_pending) {
592  }
593 
594 }
595 
596 
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 
607  }
608 }
size_t len
Definition: 6502dis.c:15
lzma_index ** i
Definition: index.h:629
static bool err
Definition: armass.c:435
static mcore_handle handle
Definition: asm_mcore.c:8
#define NULL
Definition: cris-opc.c:27
static static fork const void static count static fd const char const char static newpath const char static path const char path
Definition: sflib.h:35
static static sync static getppid static getegid const char static filename char static len const char char static bufsiz static mask static vfork const void static prot static getpgrp const char static swapflags static arg static fd static protocol static who struct sockaddr static addrlen static backlog struct timeval struct timezone static tz const struct iovec static count static mode const void const struct sockaddr static tolen const char static pathname void static offset struct stat static buf void long static basep static whence static length const void static len static semflg const void static shmflg const struct timespec req
Definition: sflib.h:128
void uv_fatal_error(const int errorno, const char *syscall)
Definition: error.c:35
void uv_fs_event_endgame(uv_loop_t *loop, uv_fs_event_t *handle)
Definition: fs-event.c:597
static int uv_split_path(const WCHAR *filename, WCHAR **dir, WCHAR **file)
Definition: fs-event.c:83
int uv_fs_event_stop(uv_fs_event_t *handle)
Definition: fs-event.c:372
int uv_fs_event_start(uv_fs_event_t *handle, uv_fs_event_cb cb, const char *path, unsigned int flags)
Definition: fs-event.c:156
int uv_fs_event_init(uv_loop_t *loop, uv_fs_event_t *handle)
Definition: fs-event.c:140
const unsigned int uv_directory_watcher_buffer_size
Definition: fs-event.c:33
static void uv_relative_path(const WCHAR *filename, const WCHAR *dir, WCHAR **relpath)
Definition: fs-event.c:66
static void uv_fs_event_queue_readdirchanges(uv_loop_t *loop, uv_fs_event_t *handle)
Definition: fs-event.c:36
static int file_info_cmp(WCHAR *str, WCHAR *file_name, size_t file_name_len)
Definition: fs-event.c:407
void uv_process_fs_event_req(uv_loop_t *loop, uv_req_t *req, uv_fs_event_t *handle)
Definition: fs-event.c:426
void uv_fs_event_close(uv_loop_t *loop, uv_fs_event_t *handle)
Definition: fs-event.c:585
static INLINE void uv_want_endgame(uv_loop_t *loop, uv_handle_t *handle)
Definition: handle-inl.h:88
#define uv__handle_close(handle)
Definition: handle-inl.h:76
#define uv__handle_closing(handle)
Definition: handle-inl.h:63
voidpf void uLong size
Definition: ioapi.h:138
const char * filename
Definition: ioapi.h:137
voidpf uLong offset
Definition: ioapi.h:144
#define INVALID_HANDLE_VALUE
Definition: iowin32.c:21
return memset(p, 0, total)
static const char file_name
Definition: sflib.h:131
assert(limit<=UINT32_MAX/2)
#define TRUE
Definition: mybfd.h:103
#define FALSE
Definition: mybfd.h:102
#define GET_REQ_ERROR(req)
Definition: req-inl.h:49
static INLINE void uv_insert_pending_req(uv_loop_t *loop, uv_req_t *req)
Definition: req-inl.h:90
#define REQ_SUCCESS(req)
Definition: req-inl.h:46
#define SET_REQ_ERROR(req, error)
Definition: req-inl.h:34
static struct sockaddr static addrlen static backlog const void static flags void flags
Definition: sfsocketcall.h:123
Definition: gzappend.c:170
Definition: uv.h:1780
Definition: uv.h:407
uv_loop_t * loop
Definition: main.c:7
int uv__convert_utf16_to_utf8(const WCHAR *utf16, int utf16len, char **utf8)
Definition: util.c:1280
void error(const char *msg)
Definition: untgz.c:593
char * uv__strdup(const char *s)
Definition: uv-common.c:55
void * uv__malloc(size_t size)
Definition: uv-common.c:75
void uv__free(void *ptr)
Definition: uv-common.c:81
@ UV_HANDLE_CLOSING
Definition: uv-common.h:74
@ UV_HANDLE_CLOSED
Definition: uv-common.h:75
#define UV_REQ_INIT(req, typ)
Definition: uv-common.h:322
#define uv__handle_init(loop_, h, type_)
Definition: uv-common.h:301
#define uv__handle_stop(h)
Definition: uv-common.h:266
#define uv__is_active(h)
Definition: uv-common.h:252
#define uv__handle_start(h)
Definition: uv-common.h:258
UV_EXTERN int uv_translate_sys_error(int sys_errno)
Definition: core.c:1249
void(* uv_fs_event_cb)(uv_fs_event_t *handle, const char *filename, int events, int status)
Definition: uv.h:369
@ UV_FS_EVENT_RECURSIVE
Definition: uv.h:1620
@ UV_CHANGE
Definition: uv.h:1542
@ UV_RENAME
Definition: uv.h:1541
DWORD
static const char * cb[]
Definition: z80_tab.h:176
#define L
Definition: zip_err_str.c:7