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 } 621