@@ -587,45 +587,119 @@ TEST_IMPL(fs_event_watch_dir_recursive) { |
587 | 587 | } |
588 | 588 |
|
589 | 589 | #ifdef _WIN32 |
| 590 | +static char short_path_file[MAX_PATH]; |
| 591 | + |
| 592 | +static void short_path_timer_cb(uv_timer_t* handle) { |
| 593 | + ++timer_cb_called; |
| 594 | + touch_file(short_path_file); |
| 595 | +} |
| 596 | + |
| 597 | +/* Try to create a unique watch directory that has a 8.3 short component |
| 598 | + under `parent` ("" means the cwd). If it's successful, fills `watch_dir` |
| 599 | + and `short_dir` with the long and short forms of the created directory |
| 600 | + respectively, and returns 1. Otherwise removes the directory created |
| 601 | + and returns 0. */ |
| 602 | +static int short_path_make(const char* parent, |
| 603 | + char* watch_dir, size_t watch_n, |
| 604 | + char* short_dir, size_t short_n) { |
| 605 | + uv_fs_t req; |
| 606 | + char tmpl[MAX_PATH]; |
| 607 | + WCHAR watch_dirw[MAX_PATH]; |
| 608 | + WCHAR short_dirw[MAX_PATH]; |
| 609 | + WCHAR long_dirw[MAX_PATH]; |
| 610 | + size_t pathlen; |
| 611 | + int r; |
| 612 | + |
| 613 | + if (parent[0] != '\0') |
| 614 | + r = snprintf(tmpl, sizeof(tmpl), "%s\\watch_dirXXXXXX", parent); |
| 615 | + else |
| 616 | + r = snprintf(tmpl, sizeof(tmpl), "watch_dirXXXXXX"); |
| 617 | + if (r < 0 || (size_t) r >= sizeof(tmpl)) |
| 618 | + return 0; |
| 619 | + |
| 620 | + r = uv_fs_mkdtemp(NULL, &req, tmpl, NULL); |
| 621 | + if (r != 0) { |
| 622 | + uv_fs_req_cleanup(&req); |
| 623 | + return 0; |
| 624 | + } |
| 625 | + |
| 626 | + /* Copy the created path out before cleaning up the request that owns it. */ |
| 627 | + pathlen = strlen(req.path); |
| 628 | + memcpy(watch_dir, req.path, pathlen + 1); |
| 629 | + uv_fs_req_cleanup(&req); |
| 630 | + |
| 631 | + /* The caller appends "\\file1" to both watch_dir and short_dir. If there is |
| 632 | + no room for that suffix, skip this location. */ |
| 633 | + if (pathlen + sizeof("\\file1") <= watch_n && |
| 634 | + MultiByteToWideChar(CP_UTF8, 0, watch_dir, -1, |
| 635 | + watch_dirw, ARRAY_SIZE(watch_dirw)) != 0 && |
| 636 | + GetShortPathNameW(watch_dirw, short_dirw, ARRAY_SIZE(short_dirw)) != 0 && |
| 637 | + GetLongPathNameW(watch_dirw, long_dirw, ARRAY_SIZE(long_dirw)) != 0 && |
| 638 | + _wcsicmp(short_dirw, long_dirw) != 0 && |
| 639 | + WideCharToMultiByte(CP_UTF8, 0, short_dirw, -1, |
| 640 | + short_dir, (int) short_n, NULL, NULL) != 0) |
| 641 | + return 1; |
| 642 | + |
| 643 | + uv_fs_rmdir(NULL, &req, watch_dir, NULL); |
| 644 | + uv_fs_req_cleanup(&req); |
| 645 | + return 0; |
| 646 | +} |
| 647 | + |
590 | 648 | TEST_IMPL(fs_event_watch_dir_short_path) { |
591 | 649 | uv_loop_t* loop; |
592 | | - uv_fs_t req; |
| 650 | + char temp_path[MAX_PATH]; |
| 651 | + char watch_dir[MAX_PATH]; |
| 652 | + char watch_file[MAX_PATH]; |
| 653 | + char short_dir[MAX_PATH]; |
| 654 | + size_t temp_len; |
593 | 655 | int has_shortnames; |
594 | 656 | int r; |
595 | 657 |
|
596 | | - /* Setup */ |
597 | 658 | loop = uv_default_loop(); |
598 | | - delete_file("watch_dir/file1"); |
599 | | - delete_dir("watch_dir/"); |
600 | | - create_dir("watch_dir"); |
601 | | - create_file("watch_dir/file1"); |
602 | 659 |
|
603 | | - /* Newer version of Windows ship with |
604 | | - HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\NtfsDisable8dot3NameCreation |
605 | | - not equal to 0. So we verify the files we created are addressable by a 8.3 |
606 | | - short name */ |
607 | | - has_shortnames = uv_fs_stat(NULL, &req, "watch_~1", NULL) != UV_ENOENT; |
| 660 | + /* This test needs the watched directory to have an 8.3 short component. The |
| 661 | + generated "watch_dirXXXXXX" name is > 8 chars, so it gets a short alias on |
| 662 | + volumes where 8.3 name creation is enabled. For non-system volumes on |
| 663 | + newer Windows it's disabled by default (NtfsDisable8dot3NameCreation=3). |
| 664 | + The temp dir and the cwd may be on different volumes, so try each and use |
| 665 | + whichever has a short alias, and skip if neither does. */ |
| 666 | + has_shortnames = 0; |
| 667 | + temp_len = sizeof(temp_path); |
| 668 | + if (uv_os_tmpdir(temp_path, &temp_len) == 0) |
| 669 | + has_shortnames = short_path_make(temp_path, |
| 670 | + watch_dir, sizeof(watch_dir), |
| 671 | + short_dir, sizeof(short_dir)); |
| 672 | + if (!has_shortnames) |
| 673 | + has_shortnames = short_path_make("", |
| 674 | + watch_dir, sizeof(watch_dir), |
| 675 | + short_dir, sizeof(short_dir)); |
| 676 | + |
608 | 677 | if (has_shortnames) { |
| 678 | + snprintf(watch_file, sizeof(watch_file), "%s\\file1", watch_dir); |
| 679 | + /* short_path_file is used in the timer callback to touch the file. */ |
| 680 | + snprintf(short_path_file, sizeof(short_path_file), "%s\\file1", short_dir); |
| 681 | + /* The directory was just created, so file1 cannot exist yet. */ |
| 682 | + create_file(watch_file); |
| 683 | + |
609 | 684 | r = uv_fs_event_init(loop, &fs_event); |
610 | 685 | ASSERT_OK(r); |
611 | | - r = uv_fs_event_start(&fs_event, fs_event_cb_dir, "watch_~1", 0); |
| 686 | + r = uv_fs_event_start(&fs_event, fs_event_cb_dir, short_dir, 0); |
612 | 687 | ASSERT_OK(r); |
613 | 688 | r = uv_timer_init(loop, &timer); |
614 | 689 | ASSERT_OK(r); |
615 | | - r = uv_timer_start(&timer, timer_cb_file, 100, 0); |
| 690 | + r = uv_timer_start(&timer, short_path_timer_cb, 100, 0); |
616 | 691 | ASSERT_OK(r); |
617 | 692 |
|
618 | 693 | uv_run(loop, UV_RUN_DEFAULT); |
619 | 694 |
|
620 | 695 | ASSERT_EQ(1, fs_event_cb_called); |
621 | 696 | ASSERT_EQ(1, timer_cb_called); |
622 | 697 | ASSERT_EQ(1, close_cb_called); |
| 698 | + /* Cleanup */ |
| 699 | + delete_file(watch_file); |
| 700 | + delete_dir(watch_dir); |
623 | 701 | } |
624 | 702 |
|
625 | | - /* Cleanup */ |
626 | | - delete_file("watch_dir/file1"); |
627 | | - delete_dir("watch_dir/"); |
628 | | - |
629 | 703 | MAKE_VALGRIND_HAPPY(loop); |
630 | 704 |
|
631 | 705 | if (!has_shortnames) |
|