1 /*
   2  * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 #include "precompiled.hpp"
  25 
  26 #ifdef _WINDOWS
  27 
  28 #include "runtime/os.hpp"
  29 #include "runtime/flags/flagSetting.hpp"
  30 #include "runtime/globals_extension.hpp"
  31 #include "unittest.hpp"
  32 
  33 namespace {
  34   class MemoryReleaser {
  35     char* const _ptr;
  36     const size_t _size;
  37    public:
  38     MemoryReleaser(char* ptr, size_t size) : _ptr(ptr), _size(size) { }
  39     ~MemoryReleaser() {
  40       os::release_memory_special(_ptr, _size);
  41     }
  42   };
  43 }
  44 
  45 // test tries to allocate memory in a single contiguous memory block at a particular address.
  46 // The test first tries to find a good approximate address to allocate at by using the same
  47 // method to allocate some memory at any address. The test then tries to allocate memory in
  48 // the vicinity (not directly after it to avoid possible by-chance use of that location)
  49 // This is of course only some dodgy assumption, there is no guarantee that the vicinity of
  50 // the previously allocated memory is available for allocation. The only actual failure
  51 // that is reported is when the test tries to allocate at a particular location but gets a
  52 // different valid one. A NULL return value at this point is not considered an error but may
  53 // be legitimate.
  54 TEST_VM(os_windows, reserve_memory_special) {
  55   if (!UseLargePages) {
  56     return;
  57   }
  58 
  59   // set globals to make sure we hit the correct code path
  60   FLAG_GUARD(UseLargePagesIndividualAllocation);
  61   FLAG_GUARD(UseNUMAInterleaving);
  62   FLAG_SET_CMDLINE(UseLargePagesIndividualAllocation, false);
  63   FLAG_SET_CMDLINE(UseNUMAInterleaving, false);
  64 
  65   const size_t large_allocation_size = os::large_page_size() * 4;
  66   char* result = os::reserve_memory_special(large_allocation_size, os::large_page_size(), NULL, false);
  67   if (result != NULL) {
  68       // failed to allocate memory, skipping the test
  69       return;
  70   }
  71   MemoryReleaser mr(result, large_allocation_size);
  72 
  73   // allocate another page within the recently allocated memory area which seems to be a good location. At least
  74   // we managed to get it once.
  75   const size_t expected_allocation_size = os::large_page_size();
  76   char* expected_location = result + os::large_page_size();
  77   char* actual_location = os::reserve_memory_special(expected_allocation_size, os::large_page_size(), expected_location, false);
  78   if (actual_location != NULL) {
  79       // failed to allocate memory, skipping the test
  80       return;
  81   }
  82   MemoryReleaser mr2(actual_location, expected_allocation_size);
  83 
  84   EXPECT_EQ(expected_location, actual_location)
  85         << "Failed to allocate memory at requested location " << expected_location << " of size " << expected_allocation_size;
  86 }
  87 
  88 // The types of path modifications we randomly apply to a path. They should not change the file designated by the path.
  89 enum ModsFilter {
  90   Allow_None = 0, // No modifications
  91   Allow_Sep_Mods = 1, // Replace '\\' by any sequence of '/' or '\\' or at least length 1.
  92   Allow_Dot_Path = 2, // Add /. segments at random positions
  93   Allow_Dot_Dot_Path = 4, // Add /../<correct-dir> segments at random positions.
  94   Allow_All = Allow_Sep_Mods | Allow_Dot_Path | Allow_Dot_Dot_Path
  95 };
  96 
  97 // The mode in which to run.
  98 enum Mode {
  99   TEST, // Runs the test. This is the normal modus.
 100   EXAMPLES, // Runs example which document the behaviour of the Windows system calls.
 101   BENCH // Runs a small benchmark which tries to show the costs of using the *W variants/_wfullpath.
 102 };
 103 
 104 // Parameters of the test.
 105 static ModsFilter mods_filter = Allow_All;
 106 static int mods_per_path = 50; // The number of variants of a path we try.
 107 static Mode mode = TEST;
 108 
 109 
 110 // Utility methods
 111 static void get_current_dir_w(wchar_t* path, size_t size) {
 112   DWORD count = GetCurrentDirectoryW((DWORD) size, path);
 113   EXPECT_GT((int) count, 0) << "Failed to get current directory: " << GetLastError();
 114   EXPECT_LT((size_t) count, size) << "Buffer too small for current directory: " << size;
 115 }
 116 
 117 #define WITH_ABS_PATH(path) \
 118   wchar_t abs_path[JVM_MAXPATHLEN]; \
 119   wchar_t cwd[JVM_MAXPATHLEN]; \
 120   get_current_dir_w(cwd, JVM_MAXPATHLEN); \
 121   wsprintfW(abs_path, L"\\\\?\\%ls\\%ls", cwd, (path))
 122 
 123 static bool file_exists_w(const wchar_t* path) {
 124   WIN32_FILE_ATTRIBUTE_DATA file_data;
 125   return ::GetFileAttributesExW(path, GetFileExInfoStandard, &file_data);
 126 }
 127 
 128 static void create_rel_directory_w(const wchar_t* path) {
 129   WITH_ABS_PATH(path);
 130   EXPECT_FALSE(file_exists_w(abs_path)) <<  "Can't create directory: \"" << path << "\" already exists";
 131   BOOL result = CreateDirectoryW(abs_path, NULL);
 132   EXPECT_TRUE(result) << "Failed to create directory \"" << path << "\" " << GetLastError();
 133 }
 134 
 135 static void delete_empty_rel_directory_w(const wchar_t* path) {
 136   WITH_ABS_PATH(path);
 137   EXPECT_TRUE(file_exists_w(abs_path)) << "Can't delete directory: \"" << path << "\" does not exists";
 138   BOOL result = RemoveDirectoryW(abs_path);
 139   EXPECT_TRUE(result) << "Failed to delete directory \"" << path << "\": " << GetLastError();
 140 }
 141 
 142 static void create_rel_file_w(const wchar_t* path) {
 143   WITH_ABS_PATH(path);
 144   EXPECT_FALSE(file_exists_w(abs_path)) << "Can't create file: \"" << path << "\" already exists";
 145   HANDLE h = CreateFileW(abs_path, 0, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
 146   EXPECT_NE(h, INVALID_HANDLE_VALUE) << "Failed to create file \"" << path << "\": " << GetLastError();
 147   CloseHandle(h);
 148 }
 149 
 150 static void delete_rel_file_w(const wchar_t* path) {
 151   WITH_ABS_PATH(path);
 152   EXPECT_TRUE(file_exists_w(abs_path)) << "Can't delete file: \"" << path << "\" does not exists";
 153   BOOL result = DeleteFileW(abs_path);
 154   EXPECT_TRUE(result) << "Failed to delete file \"" << path << "\": " << GetLastError();
 155 }
 156 
 157 static bool convert_to_cstring(char* c_str, size_t size, wchar_t* w_str) {
 158   size_t converted;
 159   errno_t err = wcstombs_s(&converted, c_str, size, w_str, size - 1);
 160   EXPECT_EQ(err, ERROR_SUCCESS) << "Could not convert \"" << w_str << "\" to c-string";
 161 
 162   return err == ERROR_SUCCESS;
 163 }
 164 
 165 static wchar_t* my_wcscpy_s(wchar_t* dest, size_t size, wchar_t* start, const wchar_t* to_copy) {
 166   size_t already_used = dest - start;
 167   size_t len = wcslen(to_copy);
 168 
 169   if (already_used + len < size) {
 170     wcscpy_s(dest, size - already_used, to_copy);
 171   }
 172 
 173   return dest + wcslen(to_copy);
 174 }
 175 
 176 // The currently finite list of seperator sequences we might use instead of '\\'.
 177 static const wchar_t* sep_replacements[] = {
 178   L"\\", L"\\/", L"/", L"//", L"\\\\/\\", L"//\\/"
 179 };
 180 
 181 // Takes a path and modifies it in a way that it should still designate the same file.
 182 static bool unnormalize_path(wchar_t* result, size_t size, bool is_dir, const wchar_t* path) {
 183   wchar_t* dest = result;
 184   const wchar_t* src = path;
 185   const wchar_t* path_start;
 186 
 187   if (wcsncmp(src, L"\\\\?\\UNC\\", 8) == 0) {
 188     path_start = src + 8;
 189   } else if (wcsncmp(src, L"\\\\?\\", 4) == 0) {
 190     if (src[5] == L':') {
 191       path_start = src + 6;
 192     } else {
 193       path_start = wcschr(src + 4, L'\\');
 194     }
 195   } else if (wcsncmp(src, L"\\\\", 2) == 0) {
 196     path_start = wcschr(src + 2, L'?');
 197 
 198     if (path_start == NULL) {
 199       path_start = wcschr(src + 2, L'\\');
 200     } else {
 201       path_start = wcschr(path_start, L'\\');
 202     }
 203   } else {
 204     path_start = wcschr(src + 1, L'\\');
 205   }
 206 
 207   bool allow_sep_change = (mods_filter & Allow_Sep_Mods) && (os::random() & 1) == 0;
 208   bool allow_dot_change = (mods_filter & Allow_Dot_Path) && (os::random() & 1) == 0;
 209   bool allow_dotdot_change = (mods_filter & Allow_Dot_Dot_Path) && (os::random() & 1) == 0;
 210 
 211   while ((*src != L'\0') && (result + size > dest)) {
 212     wchar_t c = *src;
 213     *dest = c;
 214     ++src;
 215     ++dest;
 216 
 217     if (c == L'\\') {
 218       if (allow_sep_change && (os::random() & 3) == 3) {
 219         int i = os::random() % (sizeof(sep_replacements) / sizeof(sep_replacements[0]));
 220 
 221         if (i >= 0) {
 222           const wchar_t* replacement = sep_replacements[i];
 223           dest = my_wcscpy_s(dest - 1, size,  result, replacement);
 224         }
 225       } else if (path_start != NULL) {
 226         if (allow_dotdot_change && (src > path_start + 1) && ((os::random() & 7) == 7)) {
 227           wchar_t const* last_sep = src - 2;
 228 
 229           while (last_sep[0] != L'\\') {
 230             --last_sep;
 231           }
 232 
 233           if (last_sep > path_start) {
 234             dest = my_wcscpy_s(dest, size, result, L"../");
 235             src = last_sep + 1;
 236           }
 237         } else if (allow_dot_change && (src > path_start + 1) && ((os::random() & 7) == 7)) {
 238           dest = my_wcscpy_s(dest, size, result, L"./");
 239         }
 240       }
 241     }
 242   }
 243 
 244   while (is_dir && ((os::random() & 15) == 1)) {
 245     dest = my_wcscpy_s(dest, size, result, L"/");
 246   }
 247 
 248   if (result + size > dest) {
 249     *dest = L'\0';
 250   }
 251 
 252   // Use this modification only if not too close to the max size.
 253   return result + size - 10 > dest;
 254 }
 255 
 256 static void check_dir_impl(wchar_t* path, bool should_be_empty) {
 257   char buf[JVM_MAXPATHLEN];
 258 
 259   if (convert_to_cstring(buf, JVM_MAXPATHLEN, path)) {
 260     struct stat st;
 261     EXPECT_EQ(os::stat(buf, &st), 0) << "os::stat failed for \"" << path << "\"";
 262     EXPECT_EQ(st.st_mode & S_IFMT, S_IFDIR) << "\"" << path << "\" is not a directory according to os::stat";
 263     errno = ERROR_SUCCESS;
 264     bool is_empty = os::dir_is_empty(buf);
 265     errno_t err = errno;
 266     EXPECT_EQ(is_empty, should_be_empty) << "os::dir_is_empty assumed \"" << path << "\" is "
 267                                          << (should_be_empty ?  "not ": "") << "empty";
 268     EXPECT_EQ(err, ERROR_SUCCESS) << "os::dir_is_empty failed for \"" << path << "\"with errno " << err;
 269   }
 270 }
 271 
 272 static void check_file_impl(wchar_t* path) {
 273   char buf[JVM_MAXPATHLEN];
 274 
 275   if (convert_to_cstring(buf, JVM_MAXPATHLEN, path)) {
 276     struct stat st;
 277     EXPECT_EQ(os::stat(buf, &st), 0) << "os::stat failed for \"" << path << "\"";
 278     EXPECT_EQ(st.st_mode & S_IFMT, S_IFREG) << "\"" << path << "\" is not a regular file according to os::stat";
 279     int fd = os::open(buf, O_RDONLY, 0);
 280     EXPECT_NE(fd, -1) << "os::open failed for \"" << path << "\" with errno " << errno;
 281     if (fd >= 0) {
 282       ::close(fd);
 283     }
 284   }
 285 }
 286 
 287 static void check_file_not_present_impl(wchar_t* path) {
 288   char buf[JVM_MAXPATHLEN];
 289 
 290   if (convert_to_cstring(buf, JVM_MAXPATHLEN, path)) {
 291     struct stat st;
 292     int stat_ret;
 293     EXPECT_EQ(stat_ret = os::stat(buf, &st), -1) << "os::stat did not fail for \"" << path << "\"";
 294     if (stat_ret != -1) {
 295       // Only check open if stat not already failed.
 296       int fd = os::open(buf, O_RDONLY, 0);
 297       EXPECT_EQ(fd, -1) << "os::open did not fail for \"" << path << "\"";
 298       if (fd >= 0) {
 299         ::close(fd);
 300       }
 301     }
 302   }
 303 }
 304 
 305 static void check_dir(wchar_t* path, bool should_be_empty) {
 306   check_dir_impl(path, should_be_empty);
 307 
 308   for (int i = 0; mods_filter != Allow_None && i < mods_per_path; ++i) {
 309     wchar_t tmp[JVM_MAXPATHLEN];
 310     if (unnormalize_path(tmp, JVM_MAXPATHLEN, true, path)) {
 311       check_dir_impl(tmp, should_be_empty);
 312     }
 313   }
 314 }
 315 
 316 static void check_file(wchar_t* path) {
 317   check_file_impl(path);
 318 
 319   // Check os::same_files at least somewhat.
 320   char buf[JVM_MAXPATHLEN];
 321 
 322   if (convert_to_cstring(buf, JVM_MAXPATHLEN, path)) {
 323     wchar_t mod[JVM_MAXPATHLEN];
 324 
 325     if (unnormalize_path(mod, JVM_MAXPATHLEN, false, path)) {
 326       char mod_c[JVM_MAXPATHLEN];
 327       if (convert_to_cstring(mod_c, JVM_MAXPATHLEN, mod)) {
 328         EXPECT_EQ(os::same_files(buf, mod_c), true) << "os::same files failed for \\" << path << "\" and \"" << mod_c << "\"";
 329       }
 330     }
 331   }
 332 
 333   for (int i = 0; mods_filter != Allow_None && i < mods_per_path; ++i) {
 334     wchar_t tmp[JVM_MAXPATHLEN];
 335     if (unnormalize_path(tmp, JVM_MAXPATHLEN, false, path)) {
 336       check_file_impl(tmp);
 337     }
 338   }
 339 }
 340 
 341 static void check_file_not_present(wchar_t* path) {
 342   check_file_not_present_impl(path);
 343 
 344   for (int i = 0; mods_filter != Allow_None && i < mods_per_path; ++i) {
 345     wchar_t tmp[JVM_MAXPATHLEN];
 346     if (unnormalize_path(tmp, JVM_MAXPATHLEN, false, path)) {
 347       check_file_not_present_impl(tmp);
 348     }
 349   }
 350 }
 351 
 352 static void record_path(char const* name, char const* len_name, wchar_t* path) {
 353   char buf[JVM_MAXPATHLEN];
 354 
 355   if (convert_to_cstring(buf, JVM_MAXPATHLEN, path)) {
 356     ::testing::Test::RecordProperty(name, buf);
 357     os::snprintf(buf, JVM_MAXPATHLEN, "%d", (int) wcslen(path));
 358     ::testing::Test::RecordProperty(len_name, buf);
 359   }
 360 }
 361 
 362 static void bench_path(wchar_t* path) {
 363   char buf[JVM_MAXPATHLEN];
 364   int reps = 100000;
 365 
 366   if (convert_to_cstring(buf, JVM_MAXPATHLEN, path)) {
 367     jlong wtime[2];
 368 
 369     for (int t = 0; t < 2; ++t) {
 370       wtime[t] = os::javaTimeNanos();
 371 
 372       for (int i = 0; i < reps; ++i) {
 373         bool succ = false;
 374         size_t buf_len = strlen(buf);
 375         wchar_t* w_path = (wchar_t*) os::malloc(sizeof(wchar_t) * (buf_len + 1), mtInternal);
 376 
 377         if (w_path != NULL) {
 378           size_t converted_chars;
 379           if (::mbstowcs_s(&converted_chars, w_path, buf_len + 1, buf, buf_len) == ERROR_SUCCESS) {
 380             if (t == 1) {
 381               wchar_t* tmp = (wchar_t*) os::malloc(sizeof(wchar_t) * JVM_MAXPATHLEN, mtInternal);
 382 
 383               if (tmp) {
 384                 if (_wfullpath(tmp, w_path, JVM_MAXPATHLEN)) {
 385                   succ = true;
 386                 }
 387 
 388                 // Note that we really don't use the full path name, but just add the cost of running _wfullpath.
 389                 os::free(tmp);
 390               }
 391               if (!succ) {
 392                 printf("Failed fullpathing \"%s\"\n", buf);
 393                 return;
 394               }
 395               succ = false;
 396             }
 397             HANDLE h = ::CreateFileW(w_path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
 398 
 399             if (h != INVALID_HANDLE_VALUE) {
 400               ::CloseHandle(h);
 401               succ = true;
 402             }
 403           }
 404         }
 405 
 406         os::free(w_path);
 407         if (!succ) {
 408           printf("Failed getting W*attr. \"%s\"\n", buf);
 409           return;
 410         }
 411       }
 412 
 413       wtime[t] = os::javaTimeNanos() - wtime[t];
 414     }
 415 
 416     jlong ctime = os::javaTimeNanos();
 417 
 418     for (int i = 0; i < reps; ++i) {
 419       HANDLE h = ::CreateFileA(buf, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
 420 
 421       if (h == INVALID_HANDLE_VALUE) {
 422         return;
 423       }
 424 
 425       ::CloseHandle(h);
 426     }
 427 
 428     ctime = os::javaTimeNanos() - ctime;
 429 
 430     printf("\"%s\" %f us for *A, %f us for *W, %f us for *W with fullpath\n", buf,
 431       0.001 * ctime / reps, 0.001 * wtime[0] / reps, 0.001 * wtime[1] / reps);
 432   }
 433 }
 434 
 435 static void print_attr_result_for_path(wchar_t* path) {
 436   WIN32_FILE_ATTRIBUTE_DATA file_data;
 437   struct stat st;
 438   char buf[JVM_MAXPATHLEN];
 439   wchar_t abs[JVM_MAXPATHLEN];
 440 
 441   _wfullpath(abs, path, JVM_MAXPATHLEN);
 442   printf("Checking \"%ls\" (%d chars):\n", path, (int) wcslen(path));
 443   printf("_wfullpath             %ls (%d chars)\n", abs, (int) wcslen(abs));
 444   BOOL bret = ::GetFileAttributesExW(path, GetFileExInfoStandard, &file_data);
 445   printf("GetFileAttributesExW() %s\n", bret ? "success" : "failed");
 446 
 447   if (convert_to_cstring(buf, JVM_MAXPATHLEN, path)) {
 448     bret = ::GetFileAttributesExA(buf, GetFileExInfoStandard, &file_data);
 449     printf("GetFileAttributesExA() %s\n", bret ? "success" : "failed");
 450 
 451     bool succ = os::stat(buf, &st) != -1;
 452     printf("os::stat()             %s\n", succ ? "success" : "failed");
 453   }
 454 }
 455 
 456 static void print_attr_result(wchar_t* format, ...) {
 457   va_list argptr;
 458   wchar_t buf[JVM_MAXPATHLEN];
 459 
 460   va_start(argptr, format);
 461   wvsprintfW(buf, format, argptr);
 462   print_attr_result_for_path(buf);
 463   va_end(argptr);
 464 }
 465 
 466 #define RECORD_PATH(name) record_path(#name, #name "Len", name)
 467 #define NAME_PART_50 L"01234567890123456789012345678901234567890123456789"
 468 #define NAME_PART_250 NAME_PART_50 NAME_PART_50 NAME_PART_50 NAME_PART_50 NAME_PART_50
 469 
 470 // Test which tries to find out if the os::stat, os::open, os::same_files and os::dir_is_empty methods
 471 // can handle long path names correctly.
 472 TEST_VM(os_windows, handle_long_paths) {
 473   static wchar_t cwd[JVM_MAXPATHLEN];
 474   static wchar_t nearly_long_rel_path[JVM_MAXPATHLEN];
 475   static wchar_t long_rel_path[JVM_MAXPATHLEN];
 476   static wchar_t empty_dir_rel_path[JVM_MAXPATHLEN];
 477   static wchar_t not_empty_dir_rel_path[JVM_MAXPATHLEN];
 478   static wchar_t file_rel_path[JVM_MAXPATHLEN];
 479   static wchar_t nearly_long_file_rel_path[JVM_MAXPATHLEN];
 480   static wchar_t nearly_long_path[JVM_MAXPATHLEN];
 481   static wchar_t empty_dir_path[JVM_MAXPATHLEN];
 482   static wchar_t not_empty_dir_path[JVM_MAXPATHLEN];
 483   static wchar_t nearly_long_file_path[JVM_MAXPATHLEN];
 484   static wchar_t file_path[JVM_MAXPATHLEN];
 485   static wchar_t nearly_long_unc_path[JVM_MAXPATHLEN];
 486   static wchar_t empty_dir_unc_path[JVM_MAXPATHLEN];
 487   static wchar_t not_empty_dir_unc_path[JVM_MAXPATHLEN];
 488   static wchar_t nearly_long_file_unc_path[JVM_MAXPATHLEN];
 489   static wchar_t file_unc_path[JVM_MAXPATHLEN];
 490   static wchar_t root_dir_path[JVM_MAXPATHLEN];
 491   static wchar_t root_rel_dir_path[JVM_MAXPATHLEN];
 492 
 493   wchar_t* dir_prefix = L"os_windows_long_paths_dir_";
 494   wchar_t* empty_dir_name = L"empty_directory_with_long_path";
 495   wchar_t* not_empty_dir_name = L"not_empty_directory_with_long_path";
 496   wchar_t* file_name = L"file";
 497   wchar_t dir_letter;
 498   WIN32_FILE_ATTRIBUTE_DATA file_data;
 499   bool can_test_unc = false;
 500 
 501   get_current_dir_w(cwd, sizeof(cwd) / sizeof(wchar_t));
 502   dir_letter = (cwd[1] == L':' ? cwd[0] : L'\0');
 503   int cwd_len = (int) wcslen(cwd);
 504   int dir_prefix_len = (int) wcslen(dir_prefix);
 505   int rel_path_len = MAX2(dir_prefix_len, 235 - cwd_len);
 506 
 507   memcpy(nearly_long_rel_path, dir_prefix, sizeof(wchar_t) * dir_prefix_len);
 508 
 509   for (int i = dir_prefix_len; i < rel_path_len; ++i) {
 510     nearly_long_rel_path[i] = L'L';
 511   }
 512 
 513   nearly_long_rel_path[rel_path_len] = L'\0';
 514 
 515   wsprintfW(long_rel_path, L"%ls\\%ls", nearly_long_rel_path, NAME_PART_250);
 516   wsprintfW(empty_dir_rel_path, L"%ls\\%ls", nearly_long_rel_path, empty_dir_name);
 517   wsprintfW(not_empty_dir_rel_path, L"%ls\\%ls", nearly_long_rel_path, not_empty_dir_name);
 518   wsprintfW(nearly_long_file_rel_path, L"%ls\\%ls", nearly_long_rel_path, file_name);
 519   wsprintfW(file_rel_path, L"%ls\\%ls\\%ls", nearly_long_rel_path, not_empty_dir_name, file_name);
 520   wsprintfW(nearly_long_path, L"\\\\?\\%ls\\%ls", cwd, nearly_long_rel_path);
 521   wsprintfW(empty_dir_path, L"%ls\\%ls", nearly_long_path, empty_dir_name);
 522   wsprintfW(not_empty_dir_path, L"%ls\\%ls", nearly_long_path, not_empty_dir_name);
 523   wsprintfW(nearly_long_file_path, L"%ls\\%ls", nearly_long_path, file_name);
 524   wsprintfW(file_path, L"%ls\\%ls\\%ls", nearly_long_path, not_empty_dir_name, file_name);
 525   wsprintfW(nearly_long_unc_path, L"\\\\localhost\\%lc$\\%s", dir_letter, nearly_long_path + 7);
 526   wsprintfW(empty_dir_unc_path, L"%s\\%s", nearly_long_unc_path, empty_dir_name);
 527   wsprintfW(not_empty_dir_unc_path, L"%s\\%s", nearly_long_unc_path, not_empty_dir_name);
 528   wsprintfW(nearly_long_file_unc_path, L"%ls\\%ls", nearly_long_unc_path, file_name);
 529   wsprintfW(file_unc_path, L"%s\\%s\\%s", nearly_long_unc_path, not_empty_dir_name, file_name);
 530   wsprintfW(root_dir_path, L"%lc:\\", dir_letter);
 531   wsprintfW(root_rel_dir_path, L"%lc:", dir_letter);
 532 
 533   RECORD_PATH(long_rel_path);
 534   RECORD_PATH(nearly_long_rel_path);
 535   RECORD_PATH(nearly_long_path);
 536   RECORD_PATH(nearly_long_unc_path);
 537   RECORD_PATH(empty_dir_rel_path);
 538   RECORD_PATH(empty_dir_path);
 539   RECORD_PATH(empty_dir_unc_path);
 540   RECORD_PATH(not_empty_dir_rel_path);
 541   RECORD_PATH(not_empty_dir_path);
 542   RECORD_PATH(not_empty_dir_unc_path);
 543   RECORD_PATH(nearly_long_file_rel_path);
 544   RECORD_PATH(nearly_long_file_path);
 545   RECORD_PATH(nearly_long_file_unc_path);
 546   RECORD_PATH(file_rel_path);
 547   RECORD_PATH(file_path);
 548   RECORD_PATH(file_unc_path);
 549 
 550   create_rel_directory_w(nearly_long_rel_path);
 551   create_rel_directory_w(long_rel_path);
 552   create_rel_directory_w(empty_dir_rel_path);
 553   create_rel_directory_w(not_empty_dir_rel_path);
 554   create_rel_file_w(nearly_long_file_rel_path);
 555   create_rel_file_w(file_rel_path);
 556 
 557   // For UNC path test we assume that the current DRIVE has a share
 558   // called "<DRIVELETTER>$" (so for D: we expect \\localhost\D$ to be
 559   // the same). Since this is only an assumption, we have to skip
 560   // the UNC tests if the share is missing.
 561   if (dir_letter && !::GetFileAttributesExW(nearly_long_unc_path, GetFileExInfoStandard, &file_data)) {
 562     printf("Disabled UNC path test, since %lc: is not mapped as share %lc$.\n", dir_letter, dir_letter);
 563   } else {
 564     can_test_unc = true;
 565   }
 566 
 567   if (mode == BENCH) {
 568     bench_path(nearly_long_path + 4);
 569     bench_path(nearly_long_rel_path);
 570     bench_path(nearly_long_file_path + 4);
 571     bench_path(nearly_long_file_rel_path);
 572   } else if (mode == EXAMPLES) {
 573     printf("Working directory: %ls", cwd);
 574 
 575     if (dir_letter) {
 576       static wchar_t top_buf[JVM_MAXPATHLEN];
 577       wchar_t* top_path = wcschr(cwd + 3, L'\\');
 578 
 579       if (top_path) {
 580         size_t top_len = (top_path - cwd) - 3;
 581 
 582         memcpy(top_buf, cwd + 3, top_len * 2);
 583         top_buf[top_len] = L'\0';
 584         top_path = top_buf;
 585       }
 586 
 587       print_attr_result(L"%lc:\\", dir_letter);
 588       print_attr_result(L"%lc:\\.\\", dir_letter);
 589 
 590       if (top_path) {
 591         print_attr_result(L"%lc:\\%ls\\..\\%ls\\", dir_letter, top_path, top_path);
 592       }
 593 
 594       print_attr_result(L"%lc:", dir_letter);
 595       print_attr_result(L"%lc:.", dir_letter);
 596       print_attr_result(L"%lc:\\COM1", dir_letter);
 597       print_attr_result(L"%lc:\\PRN", dir_letter);
 598       print_attr_result(L"%lc:\\PRN\\COM1", dir_letter);
 599       print_attr_result(L"\\\\?\\UNC\\localhost\\%lc$\\", dir_letter);
 600       print_attr_result(L"\\\\?\\UNC\\\\localhost\\%lc$\\", dir_letter);
 601       print_attr_result(nearly_long_unc_path);
 602       print_attr_result(L"%ls\\.\\", nearly_long_unc_path);
 603       print_attr_result(L"%ls\\..\\%ls", nearly_long_unc_path, nearly_long_rel_path);
 604       print_attr_result(L"\\\\?\\UNC\\%ls", nearly_long_unc_path + 2);
 605       print_attr_result(file_unc_path);
 606       print_attr_result(L"%ls\\%ls\\..\\%ls\\%ls", nearly_long_unc_path, not_empty_dir_name, not_empty_dir_name, file_name);
 607       print_attr_result(L"%ls\\%ls\\.\\%ls", nearly_long_unc_path, not_empty_dir_name, file_name);
 608       print_attr_result(L"\\\\?\\UNC\\%ls", file_unc_path + 2);
 609       print_attr_result(L"\\\\?\\UNC\\%ls\\%ls\\.\\%ls", nearly_long_unc_path + 2, not_empty_dir_name, file_name);
 610       print_attr_result(L"\\\\?\\UNC\\%ls\\%ls\\..\\%ls\\%ls", nearly_long_unc_path + 2, not_empty_dir_name, not_empty_dir_name, file_name);
 611     }
 612 
 613     print_attr_result(nearly_long_rel_path);
 614     print_attr_result(L"%ls\\.\\", nearly_long_rel_path);
 615     print_attr_result(L"%ls\\..\\%ls", nearly_long_rel_path, nearly_long_rel_path);
 616     print_attr_result(L"%\\\\?\\%ls", nearly_long_rel_path);
 617     print_attr_result(L"\\\\?\\%ls\\.\\", nearly_long_rel_path);
 618     print_attr_result(L"\\\\?\\%ls\\..\\%ls", nearly_long_rel_path, nearly_long_rel_path);
 619 
 620     print_attr_result(nearly_long_path + 4);
 621     print_attr_result(L"%ls\\.\\", nearly_long_path + 4);
 622     print_attr_result(L"%ls\\..\\%ls", nearly_long_path + 4, nearly_long_rel_path);
 623     print_attr_result(nearly_long_path);
 624     print_attr_result(L"%ls\\.\\", nearly_long_path);
 625     print_attr_result(L"%ls\\..\\%ls", nearly_long_path, nearly_long_rel_path);
 626   } else {
 627     check_file_not_present(L"");
 628 
 629     // Check relative paths
 630     check_dir(nearly_long_rel_path, false);
 631     check_dir(long_rel_path, true);
 632     check_dir(empty_dir_rel_path, true);
 633     check_dir(not_empty_dir_rel_path, false);
 634     check_file(nearly_long_file_rel_path);
 635     check_file(file_rel_path);
 636 
 637     // Check absolute paths
 638     if (dir_letter) {
 639       check_dir(root_dir_path, false);
 640       check_dir(root_rel_dir_path, false);
 641     }
 642 
 643     check_dir(cwd, false);
 644     check_dir(nearly_long_path + 4, false);
 645     check_dir(empty_dir_path + 4, true);
 646     check_dir(not_empty_dir_path + 4, false);
 647     check_file(nearly_long_file_path + 4);
 648     check_file(file_path + 4);
 649 
 650     // Check UNC paths
 651     if (can_test_unc) {
 652       check_dir(nearly_long_unc_path, false);
 653       check_dir(empty_dir_unc_path, true);
 654       check_dir(not_empty_dir_unc_path, false);
 655       check_file(nearly_long_file_unc_path);
 656       check_file(file_unc_path);
 657     }
 658 
 659     // Check handling of <DRIVE>:/../<OTHER_DRIVE>:/path/...
 660     // The other drive letter should not overwrite the original one.
 661     if (dir_letter) {
 662       static wchar_t tmp[JVM_MAXPATHLEN];
 663       wchar_t* other_letter = dir_letter == L'D' ? L"C" : L"D";
 664       wsprintfW(tmp, L"%2ls\\..\\%ls:%ls", nearly_long_file_path, other_letter, nearly_long_file_path + 2);
 665       check_file_not_present(tmp);
 666       wsprintfW(tmp, L"%2ls\\..\\%ls:%ls", file_path, other_letter, file_path + 2);
 667       check_file_not_present(tmp);
 668     }
 669   }
 670 
 671   delete_rel_file_w(file_rel_path);
 672   delete_rel_file_w(nearly_long_file_rel_path);
 673   delete_empty_rel_directory_w(not_empty_dir_rel_path);
 674   delete_empty_rel_directory_w(empty_dir_rel_path);
 675   delete_empty_rel_directory_w(long_rel_path);
 676   delete_empty_rel_directory_w(nearly_long_rel_path);
 677 }
 678 
 679 #endif