1 /* 2 * Copyright (c) 2000, 2018, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.util.prefs; 27 28 import java.util.StringTokenizer; 29 import java.io.ByteArrayOutputStream; 30 import java.security.AccessController; 31 import java.security.PrivilegedAction; 32 33 import sun.util.logging.PlatformLogger; 34 35 /** 36 * Windows registry based implementation of {@code Preferences}. 37 * {@code Preferences}' {@code systemRoot} and {@code userRoot} are stored in 38 * {@code HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs} and 39 * {@code HKEY_CURRENT_USER\Software\JavaSoft\Prefs} correspondingly. 40 * 41 * @author Konstantin Kladko 42 * @see Preferences 43 * @see PreferencesFactory 44 * @since 1.4 45 */ 46 47 class WindowsPreferences extends AbstractPreferences { 48 49 static { 50 PrivilegedAction<Void> load = () -> { 51 System.loadLibrary("prefs"); 52 return null; 53 }; 54 AccessController.doPrivileged(load); 55 } 56 57 /** 58 * Logger for error messages 59 */ 60 private static PlatformLogger logger; 61 62 /** 63 * Windows registry path to {@code Preferences}'s root nodes. 64 */ 65 private static final byte[] WINDOWS_ROOT_PATH = 66 stringToByteArray("Software\\JavaSoft\\Prefs"); 67 68 /** 69 * Windows handles to {@code HKEY_CURRENT_USER} and 70 * {@code HKEY_LOCAL_MACHINE} hives. 71 */ 72 private static final int HKEY_CURRENT_USER = 0x80000001; 73 private static final int HKEY_LOCAL_MACHINE = 0x80000002; 74 75 /** 76 * Mount point for {@code Preferences}' user root. 77 */ 78 private static final int USER_ROOT_NATIVE_HANDLE = HKEY_CURRENT_USER; 79 80 /** 81 * Mount point for {@code Preferences}' system root. 82 */ 83 private static final int SYSTEM_ROOT_NATIVE_HANDLE = HKEY_LOCAL_MACHINE; 84 85 /** 86 * Maximum byte-encoded path length for Windows native functions, 87 * ending {@code null} character not included. 88 */ 89 private static final int MAX_WINDOWS_PATH_LENGTH = 256; 90 91 /** 92 * User root node. 93 */ 94 private static volatile Preferences userRoot; 95 96 static Preferences getUserRoot() { 97 Preferences root = userRoot; 98 if (root == null) { 99 synchronized (WindowsPreferences.class) { 100 root = userRoot; 101 if (root == null) { 102 root = new WindowsPreferences(USER_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH); 103 userRoot = root; 104 } 105 } 106 } 107 return root; 108 } 109 110 /** 111 * System root node. 112 */ 113 private static volatile Preferences systemRoot; 114 115 static Preferences getSystemRoot() { 116 Preferences root = systemRoot; 117 if (root == null) { 118 synchronized (WindowsPreferences.class) { 119 root = systemRoot; 120 if (root == null) { 121 root = new WindowsPreferences(SYSTEM_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH); 122 systemRoot = root; 123 } 124 } 125 } 126 return root; 127 } 128 129 /* Windows error codes. */ 130 private static final int ERROR_SUCCESS = 0; 131 private static final int ERROR_FILE_NOT_FOUND = 2; 132 private static final int ERROR_ACCESS_DENIED = 5; 133 134 /* Constants used to interpret returns of native functions */ 135 private static final int NATIVE_HANDLE = 0; 136 private static final int ERROR_CODE = 1; 137 private static final int SUBKEYS_NUMBER = 0; 138 private static final int VALUES_NUMBER = 2; 139 private static final int MAX_KEY_LENGTH = 3; 140 private static final int MAX_VALUE_NAME_LENGTH = 4; 141 private static final int DISPOSITION = 2; 142 private static final int REG_CREATED_NEW_KEY = 1; 143 private static final int REG_OPENED_EXISTING_KEY = 2; 144 private static final int NULL_NATIVE_HANDLE = 0; 145 146 /* Windows security masks */ 147 private static final int DELETE = 0x10000; 148 private static final int KEY_QUERY_VALUE = 1; 149 private static final int KEY_SET_VALUE = 2; 150 private static final int KEY_CREATE_SUB_KEY = 4; 151 private static final int KEY_ENUMERATE_SUB_KEYS = 8; 152 private static final int KEY_READ = 0x20019; 153 private static final int KEY_WRITE = 0x20006; 154 private static final int KEY_ALL_ACCESS = 0xf003f; 155 156 /** 157 * Initial time between registry access attempts, in ms. The time is doubled 158 * after each failing attempt (except the first). 159 */ 160 private static int INIT_SLEEP_TIME = 50; 161 162 /** 163 * Maximum number of registry access attempts. 164 */ 165 private static int MAX_ATTEMPTS = 5; 166 167 /** 168 * BackingStore availability flag. 169 */ 170 private boolean isBackingStoreAvailable = true; 171 172 /** 173 * Java wrapper for Windows registry API RegOpenKey() 174 */ 175 private static native long[] WindowsRegOpenKey(long hKey, byte[] subKey, 176 int securityMask); 177 /** 178 * Retries RegOpenKey() MAX_ATTEMPTS times before giving up. 179 */ 180 private static long[] WindowsRegOpenKey1(long hKey, byte[] subKey, 181 int securityMask) { 182 long[] result = WindowsRegOpenKey(hKey, subKey, securityMask); 183 if (result[ERROR_CODE] == ERROR_SUCCESS) { 184 return result; 185 } else if (result[ERROR_CODE] == ERROR_FILE_NOT_FOUND) { 186 logger().warning("Trying to recreate Windows registry node " + 187 byteArrayToString(subKey) + " at root 0x" + 188 Long.toHexString(hKey) + "."); 189 // Try recreation 190 long handle = WindowsRegCreateKeyEx(hKey, subKey)[NATIVE_HANDLE]; 191 WindowsRegCloseKey(handle); 192 return WindowsRegOpenKey(hKey, subKey, securityMask); 193 } else if (result[ERROR_CODE] != ERROR_ACCESS_DENIED) { 194 long sleepTime = INIT_SLEEP_TIME; 195 for (int i = 0; i < MAX_ATTEMPTS; i++) { 196 try { 197 Thread.sleep(sleepTime); 198 } catch(InterruptedException e) { 199 return result; 200 } 201 sleepTime *= 2; 202 result = WindowsRegOpenKey(hKey, subKey, securityMask); 203 if (result[ERROR_CODE] == ERROR_SUCCESS) { 204 return result; 205 } 206 } 207 } 208 return result; 209 } 210 211 /** 212 * Java wrapper for Windows registry API RegCloseKey() 213 */ 214 private static native int WindowsRegCloseKey(long hKey); 215 216 /** 217 * Java wrapper for Windows registry API RegCreateKeyEx() 218 */ 219 private static native long[] WindowsRegCreateKeyEx(long hKey, byte[] subKey); 220 221 /** 222 * Retries RegCreateKeyEx() MAX_ATTEMPTS times before giving up. 223 */ 224 private static long[] WindowsRegCreateKeyEx1(long hKey, byte[] subKey) { 225 long[] result = WindowsRegCreateKeyEx(hKey, subKey); 226 if (result[ERROR_CODE] == ERROR_SUCCESS) { 227 return result; 228 } else { 229 long sleepTime = INIT_SLEEP_TIME; 230 for (int i = 0; i < MAX_ATTEMPTS; i++) { 231 try { 232 Thread.sleep(sleepTime); 233 } catch(InterruptedException e) { 234 return result; 235 } 236 sleepTime *= 2; 237 result = WindowsRegCreateKeyEx(hKey, subKey); 238 if (result[ERROR_CODE] == ERROR_SUCCESS) { 239 return result; 240 } 241 } 242 } 243 return result; 244 } 245 /** 246 * Java wrapper for Windows registry API RegDeleteKey() 247 */ 248 private static native int WindowsRegDeleteKey(long hKey, byte[] subKey); 249 250 /** 251 * Java wrapper for Windows registry API RegFlushKey() 252 */ 253 private static native int WindowsRegFlushKey(long hKey); 254 255 /** 256 * Retries RegFlushKey() MAX_ATTEMPTS times before giving up. 257 */ 258 private static int WindowsRegFlushKey1(long hKey) { 259 int result = WindowsRegFlushKey(hKey); 260 if (result == ERROR_SUCCESS) { 261 return result; 262 } else { 263 long sleepTime = INIT_SLEEP_TIME; 264 for (int i = 0; i < MAX_ATTEMPTS; i++) { 265 try { 266 Thread.sleep(sleepTime); 267 } catch(InterruptedException e) { 268 return result; 269 } 270 sleepTime *= 2; 271 result = WindowsRegFlushKey(hKey); 272 if (result == ERROR_SUCCESS) { 273 return result; 274 } 275 } 276 } 277 return result; 278 } 279 280 /** 281 * Java wrapper for Windows registry API RegQueryValueEx() 282 */ 283 private static native byte[] WindowsRegQueryValueEx(long hKey, 284 byte[] valueName); 285 /** 286 * Java wrapper for Windows registry API RegSetValueEx() 287 */ 288 private static native int WindowsRegSetValueEx(long hKey, byte[] valueName, 289 byte[] value); 290 /** 291 * Retries RegSetValueEx() MAX_ATTEMPTS times before giving up. 292 */ 293 private static int WindowsRegSetValueEx1(long hKey, byte[] valueName, 294 byte[] value) { 295 int result = WindowsRegSetValueEx(hKey, valueName, value); 296 if (result == ERROR_SUCCESS) { 297 return result; 298 } else { 299 long sleepTime = INIT_SLEEP_TIME; 300 for (int i = 0; i < MAX_ATTEMPTS; i++) { 301 try { 302 Thread.sleep(sleepTime); 303 } catch(InterruptedException e) { 304 return result; 305 } 306 sleepTime *= 2; 307 result = WindowsRegSetValueEx(hKey, valueName, value); 308 if (result == ERROR_SUCCESS) { 309 return result; 310 } 311 } 312 } 313 return result; 314 } 315 316 /** 317 * Java wrapper for Windows registry API RegDeleteValue() 318 */ 319 private static native int WindowsRegDeleteValue(long hKey, byte[] valueName); 320 321 /** 322 * Java wrapper for Windows registry API RegQueryInfoKey() 323 */ 324 private static native long[] WindowsRegQueryInfoKey(long hKey); 325 326 /** 327 * Retries RegQueryInfoKey() MAX_ATTEMPTS times before giving up. 328 */ 329 private static long[] WindowsRegQueryInfoKey1(long hKey) { 330 long[] result = WindowsRegQueryInfoKey(hKey); 331 if (result[ERROR_CODE] == ERROR_SUCCESS) { 332 return result; 333 } else { 334 long sleepTime = INIT_SLEEP_TIME; 335 for (int i = 0; i < MAX_ATTEMPTS; i++) { 336 try { 337 Thread.sleep(sleepTime); 338 } catch(InterruptedException e) { 339 return result; 340 } 341 sleepTime *= 2; 342 result = WindowsRegQueryInfoKey(hKey); 343 if (result[ERROR_CODE] == ERROR_SUCCESS) { 344 return result; 345 } 346 } 347 } 348 return result; 349 } 350 351 /** 352 * Java wrapper for Windows registry API RegEnumKeyEx() 353 */ 354 private static native byte[] WindowsRegEnumKeyEx(long hKey, int subKeyIndex, 355 int maxKeyLength); 356 357 /** 358 * Retries RegEnumKeyEx() MAX_ATTEMPTS times before giving up. 359 */ 360 private static byte[] WindowsRegEnumKeyEx1(long hKey, int subKeyIndex, 361 int maxKeyLength) { 362 byte[] result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength); 363 if (result != null) { 364 return result; 365 } else { 366 long sleepTime = INIT_SLEEP_TIME; 367 for (int i = 0; i < MAX_ATTEMPTS; i++) { 368 try { 369 Thread.sleep(sleepTime); 370 } catch(InterruptedException e) { 371 return result; 372 } 373 sleepTime *= 2; 374 result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength); 375 if (result != null) { 376 return result; 377 } 378 } 379 } 380 return result; 381 } 382 383 /** 384 * Java wrapper for Windows registry API RegEnumValue() 385 */ 386 private static native byte[] WindowsRegEnumValue(long hKey, int valueIndex, 387 int maxValueNameLength); 388 /** 389 * Retries RegEnumValueEx() MAX_ATTEMPTS times before giving up. 390 */ 391 private static byte[] WindowsRegEnumValue1(long hKey, int valueIndex, 392 int maxValueNameLength) { 393 byte[] result = WindowsRegEnumValue(hKey, valueIndex, 394 maxValueNameLength); 395 if (result != null) { 396 return result; 397 } else { 398 long sleepTime = INIT_SLEEP_TIME; 399 for (int i = 0; i < MAX_ATTEMPTS; i++) { 400 try { 401 Thread.sleep(sleepTime); 402 } catch(InterruptedException e) { 403 return result; 404 } 405 sleepTime *= 2; 406 result = WindowsRegEnumValue(hKey, valueIndex, 407 maxValueNameLength); 408 if (result != null) { 409 return result; 410 } 411 } 412 } 413 return result; 414 } 415 416 /** 417 * Constructs a {@code WindowsPreferences} node, creating underlying 418 * Windows registry node and all its Windows parents, if they are not yet 419 * created. 420 * Logs a warning message, if Windows Registry is unavailable. 421 */ 422 private WindowsPreferences(WindowsPreferences parent, String name) { 423 super(parent, name); 424 long parentNativeHandle = parent.openKey(KEY_CREATE_SUB_KEY, KEY_READ); 425 if (parentNativeHandle == NULL_NATIVE_HANDLE) { 426 // if here, openKey failed and logged 427 isBackingStoreAvailable = false; 428 return; 429 } 430 long[] result = 431 WindowsRegCreateKeyEx1(parentNativeHandle, toWindowsName(name)); 432 if (result[ERROR_CODE] != ERROR_SUCCESS) { 433 logger().warning("Could not create windows registry node " + 434 byteArrayToString(windowsAbsolutePath()) + 435 " at root 0x" + Long.toHexString(rootNativeHandle()) + 436 ". Windows RegCreateKeyEx(...) returned error code " + 437 result[ERROR_CODE] + "."); 438 isBackingStoreAvailable = false; 439 return; 440 } 441 newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY); 442 closeKey(parentNativeHandle); 443 closeKey(result[NATIVE_HANDLE]); 444 } 445 446 /** 447 * Constructs a root node creating the underlying 448 * Windows registry node and all of its parents, if they have not yet been 449 * created. 450 * Logs a warning message, if Windows Registry is unavailable. 451 * @param rootNativeHandle Native handle to one of Windows top level keys. 452 * @param rootDirectory Path to root directory, as a byte-encoded string. 453 */ 454 private WindowsPreferences(long rootNativeHandle, byte[] rootDirectory) { 455 super(null, ""); 456 long[] result = 457 WindowsRegCreateKeyEx1(rootNativeHandle, rootDirectory); 458 if (result[ERROR_CODE] != ERROR_SUCCESS) { 459 logger().warning("Could not open/create prefs root node " + 460 byteArrayToString(windowsAbsolutePath()) + 461 " at root 0x" + Long.toHexString(rootNativeHandle()) + 462 ". Windows RegCreateKeyEx(...) returned error code " + 463 result[ERROR_CODE] + "."); 464 isBackingStoreAvailable = false; 465 return; 466 } 467 // Check if a new node 468 newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY); 469 closeKey(result[NATIVE_HANDLE]); 470 } 471 472 /** 473 * Returns Windows absolute path of the current node as a byte array. 474 * Java "/" separator is transformed into Windows "\". 475 * @see Preferences#absolutePath() 476 */ 477 private byte[] windowsAbsolutePath() { 478 ByteArrayOutputStream bstream = new ByteArrayOutputStream(); 479 bstream.write(WINDOWS_ROOT_PATH, 0, WINDOWS_ROOT_PATH.length-1); 480 StringTokenizer tokenizer = new StringTokenizer(absolutePath(), "/"); 481 while (tokenizer.hasMoreTokens()) { 482 bstream.write((byte)'\\'); 483 String nextName = tokenizer.nextToken(); 484 byte[] windowsNextName = toWindowsName(nextName); 485 bstream.write(windowsNextName, 0, windowsNextName.length-1); 486 } 487 bstream.write(0); 488 return bstream.toByteArray(); 489 } 490 491 /** 492 * Opens current node's underlying Windows registry key using a 493 * given security mask. 494 * @param securityMask Windows security mask. 495 * @return Windows registry key's handle. 496 * @see #openKey(byte[], int) 497 * @see #openKey(int, byte[], int) 498 * @see #closeKey(int) 499 */ 500 private long openKey(int securityMask) { 501 return openKey(securityMask, securityMask); 502 } 503 504 /** 505 * Opens current node's underlying Windows registry key using a 506 * given security mask. 507 * @param mask1 Preferred Windows security mask. 508 * @param mask2 Alternate Windows security mask. 509 * @return Windows registry key's handle. 510 * @see #openKey(byte[], int) 511 * @see #openKey(int, byte[], int) 512 * @see #closeKey(int) 513 */ 514 private long openKey(int mask1, int mask2) { 515 return openKey(windowsAbsolutePath(), mask1, mask2); 516 } 517 518 /** 519 * Opens Windows registry key at a given absolute path using a given 520 * security mask. 521 * @param windowsAbsolutePath Windows absolute path of the 522 * key as a byte-encoded string. 523 * @param mask1 Preferred Windows security mask. 524 * @param mask2 Alternate Windows security mask. 525 * @return Windows registry key's handle. 526 * @see #openKey(int) 527 * @see #openKey(int, byte[],int) 528 * @see #closeKey(int) 529 */ 530 private long openKey(byte[] windowsAbsolutePath, int mask1, int mask2) { 531 /* Check if key's path is short enough be opened at once 532 otherwise use a path-splitting procedure */ 533 if (windowsAbsolutePath.length <= MAX_WINDOWS_PATH_LENGTH + 1) { 534 long[] result = WindowsRegOpenKey1(rootNativeHandle(), 535 windowsAbsolutePath, mask1); 536 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1) 537 result = WindowsRegOpenKey1(rootNativeHandle(), 538 windowsAbsolutePath, mask2); 539 540 if (result[ERROR_CODE] != ERROR_SUCCESS) { 541 logger().warning("Could not open windows registry node " + 542 byteArrayToString(windowsAbsolutePath()) + 543 " at root 0x" + 544 Long.toHexString(rootNativeHandle()) + 545 ". Windows RegOpenKey(...) returned error code " + 546 result[ERROR_CODE] + "."); 547 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE; 548 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED) { 549 throw new SecurityException( 550 "Could not open windows registry node " + 551 byteArrayToString(windowsAbsolutePath()) + 552 " at root 0x" + 553 Long.toHexString(rootNativeHandle()) + 554 ": Access denied"); 555 } 556 } 557 return result[NATIVE_HANDLE]; 558 } else { 559 return openKey(rootNativeHandle(), windowsAbsolutePath, mask1, mask2); 560 } 561 } 562 563 /** 564 * Opens Windows registry key at a given relative path 565 * with respect to a given Windows registry key. 566 * @param windowsAbsolutePath Windows relative path of the 567 * key as a byte-encoded string. 568 * @param nativeHandle handle to the base Windows key. 569 * @param mask1 Preferred Windows security mask. 570 * @param mask2 Alternate Windows security mask. 571 * @return Windows registry key's handle. 572 * @see #openKey(int) 573 * @see #openKey(byte[],int) 574 * @see #closeKey(int) 575 */ 576 private long openKey(long nativeHandle, byte[] windowsRelativePath, 577 int mask1, int mask2) { 578 /* If the path is short enough open at once. Otherwise split the path */ 579 if (windowsRelativePath.length <= MAX_WINDOWS_PATH_LENGTH + 1 ) { 580 long[] result = WindowsRegOpenKey1(nativeHandle, 581 windowsRelativePath, mask1); 582 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1) 583 result = WindowsRegOpenKey1(nativeHandle, 584 windowsRelativePath, mask2); 585 586 if (result[ERROR_CODE] != ERROR_SUCCESS) { 587 logger().warning("Could not open windows registry node " + 588 byteArrayToString(windowsAbsolutePath()) + 589 " at root 0x" + Long.toHexString(nativeHandle) + 590 ". Windows RegOpenKey(...) returned error code " + 591 result[ERROR_CODE] + "."); 592 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE; 593 } 594 return result[NATIVE_HANDLE]; 595 } else { 596 int separatorPosition = -1; 597 // Be greedy - open the longest possible path 598 for (int i = MAX_WINDOWS_PATH_LENGTH; i > 0; i--) { 599 if (windowsRelativePath[i] == ((byte)'\\')) { 600 separatorPosition = i; 601 break; 602 } 603 } 604 // Split the path and do the recursion 605 byte[] nextRelativeRoot = new byte[separatorPosition+1]; 606 System.arraycopy(windowsRelativePath, 0, nextRelativeRoot,0, 607 separatorPosition); 608 nextRelativeRoot[separatorPosition] = 0; 609 byte[] nextRelativePath = new byte[windowsRelativePath.length - 610 separatorPosition - 1]; 611 System.arraycopy(windowsRelativePath, separatorPosition+1, 612 nextRelativePath, 0, nextRelativePath.length); 613 long nextNativeHandle = openKey(nativeHandle, nextRelativeRoot, 614 mask1, mask2); 615 if (nextNativeHandle == NULL_NATIVE_HANDLE) { 616 return NULL_NATIVE_HANDLE; 617 } 618 long result = openKey(nextNativeHandle, nextRelativePath, 619 mask1,mask2); 620 closeKey(nextNativeHandle); 621 return result; 622 } 623 } 624 625 /** 626 * Closes Windows registry key. 627 * Logs a warning if Windows registry is unavailable. 628 * @param key's Windows registry handle. 629 * @see #openKey(int) 630 * @see #openKey(byte[],int) 631 * @see #openKey(int, byte[],int) 632 */ 633 private void closeKey(long nativeHandle) { 634 int result = WindowsRegCloseKey(nativeHandle); 635 if (result != ERROR_SUCCESS) { 636 logger().warning("Could not close windows registry node " + 637 byteArrayToString(windowsAbsolutePath()) + 638 " at root 0x" + 639 Long.toHexString(rootNativeHandle()) + 640 ". Windows RegCloseKey(...) returned error code " + 641 result + "."); 642 } 643 } 644 645 /** 646 * Implements {@code AbstractPreferences} {@code putSpi()} method. 647 * Puts name-value pair into the underlying Windows registry node. 648 * Logs a warning, if Windows registry is unavailable. 649 * @see #getSpi(String) 650 */ 651 protected void putSpi(String javaName, String value) { 652 long nativeHandle = openKey(KEY_SET_VALUE); 653 if (nativeHandle == NULL_NATIVE_HANDLE) { 654 isBackingStoreAvailable = false; 655 return; 656 } 657 int result = WindowsRegSetValueEx1(nativeHandle, 658 toWindowsName(javaName), toWindowsValueString(value)); 659 if (result != ERROR_SUCCESS) { 660 logger().warning("Could not assign value to key " + 661 byteArrayToString(toWindowsName(javaName)) + 662 " at Windows registry node " + 663 byteArrayToString(windowsAbsolutePath()) + 664 " at root 0x" + 665 Long.toHexString(rootNativeHandle()) + 666 ". Windows RegSetValueEx(...) returned error code " + 667 result + "."); 668 isBackingStoreAvailable = false; 669 } 670 closeKey(nativeHandle); 671 } 672 673 /** 674 * Implements {@code AbstractPreferences} {@code getSpi()} method. 675 * Gets a string value from the underlying Windows registry node. 676 * Logs a warning, if Windows registry is unavailable. 677 * @see #putSpi(String, String) 678 */ 679 protected String getSpi(String javaName) { 680 long nativeHandle = openKey(KEY_QUERY_VALUE); 681 if (nativeHandle == NULL_NATIVE_HANDLE) { 682 return null; 683 } 684 Object resultObject = WindowsRegQueryValueEx(nativeHandle, 685 toWindowsName(javaName)); 686 if (resultObject == null) { 687 closeKey(nativeHandle); 688 return null; 689 } 690 closeKey(nativeHandle); 691 return toJavaValueString((byte[]) resultObject); 692 } 693 694 /** 695 * Implements {@code AbstractPreferences} {@code removeSpi()} method. 696 * Deletes a string name-value pair from the underlying Windows registry 697 * node, if this value still exists. 698 * Logs a warning, if Windows registry is unavailable or key has already 699 * been deleted. 700 */ 701 protected void removeSpi(String key) { 702 long nativeHandle = openKey(KEY_SET_VALUE); 703 if (nativeHandle == NULL_NATIVE_HANDLE) { 704 return; 705 } 706 int result = 707 WindowsRegDeleteValue(nativeHandle, toWindowsName(key)); 708 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) { 709 logger().warning("Could not delete windows registry value " + 710 byteArrayToString(windowsAbsolutePath()) + "\\" + 711 toWindowsName(key) + " at root 0x" + 712 Long.toHexString(rootNativeHandle()) + 713 ". Windows RegDeleteValue(...) returned error code " + 714 result + "."); 715 isBackingStoreAvailable = false; 716 } 717 closeKey(nativeHandle); 718 } 719 720 /** 721 * Implements {@code AbstractPreferences} {@code keysSpi()} method. 722 * Gets value names from the underlying Windows registry node. 723 * Throws a BackingStoreException and logs a warning, if 724 * Windows registry is unavailable. 725 */ 726 protected String[] keysSpi() throws BackingStoreException{ 727 // Find out the number of values 728 long nativeHandle = openKey(KEY_QUERY_VALUE); 729 if (nativeHandle == NULL_NATIVE_HANDLE) { 730 throw new BackingStoreException( 731 "Could not open windows registry node " + 732 byteArrayToString(windowsAbsolutePath()) + 733 " at root 0x" + 734 Long.toHexString(rootNativeHandle()) + "."); 735 } 736 long[] result = WindowsRegQueryInfoKey1(nativeHandle); 737 if (result[ERROR_CODE] != ERROR_SUCCESS) { 738 String info = "Could not query windows registry node " + 739 byteArrayToString(windowsAbsolutePath()) + 740 " at root 0x" + 741 Long.toHexString(rootNativeHandle()) + 742 ". Windows RegQueryInfoKeyEx(...) returned error code " + 743 result[ERROR_CODE] + "."; 744 logger().warning(info); 745 throw new BackingStoreException(info); 746 } 747 int maxValueNameLength = (int)result[MAX_VALUE_NAME_LENGTH]; 748 int valuesNumber = (int)result[VALUES_NUMBER]; 749 if (valuesNumber == 0) { 750 closeKey(nativeHandle); 751 return new String[0]; 752 } 753 // Get the values 754 String[] valueNames = new String[valuesNumber]; 755 for (int i = 0; i < valuesNumber; i++) { 756 byte[] windowsName = WindowsRegEnumValue1(nativeHandle, i, 757 maxValueNameLength+1); 758 if (windowsName == null) { 759 String info = 760 "Could not enumerate value #" + i + " of windows node " + 761 byteArrayToString(windowsAbsolutePath()) + " at root 0x" + 762 Long.toHexString(rootNativeHandle()) + "."; 763 logger().warning(info); 764 throw new BackingStoreException(info); 765 } 766 valueNames[i] = toJavaName(windowsName); 767 } 768 closeKey(nativeHandle); 769 return valueNames; 770 } 771 772 /** 773 * Implements {@code AbstractPreferences} {@code childrenNamesSpi()} method. 774 * Calls Windows registry to retrive children of this node. 775 * Throws a BackingStoreException and logs a warning message, 776 * if Windows registry is not available. 777 */ 778 protected String[] childrenNamesSpi() throws BackingStoreException { 779 // Open key 780 long nativeHandle = openKey(KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE); 781 if (nativeHandle == NULL_NATIVE_HANDLE) { 782 throw new BackingStoreException( 783 "Could not open windows registry node " + 784 byteArrayToString(windowsAbsolutePath()) + 785 " at root 0x" + 786 Long.toHexString(rootNativeHandle()) + "."); 787 } 788 // Get number of children 789 long[] result = WindowsRegQueryInfoKey1(nativeHandle); 790 if (result[ERROR_CODE] != ERROR_SUCCESS) { 791 String info = "Could not query windows registry node " + 792 byteArrayToString(windowsAbsolutePath()) + 793 " at root 0x" + Long.toHexString(rootNativeHandle()) + 794 ". Windows RegQueryInfoKeyEx(...) returned error code " + 795 result[ERROR_CODE] + "."; 796 logger().warning(info); 797 throw new BackingStoreException(info); 798 } 799 int maxKeyLength = (int)result[MAX_KEY_LENGTH]; 800 int subKeysNumber = (int)result[SUBKEYS_NUMBER]; 801 if (subKeysNumber == 0) { 802 closeKey(nativeHandle); 803 return new String[0]; 804 } 805 String[] subkeys = new String[subKeysNumber]; 806 String[] children = new String[subKeysNumber]; 807 // Get children 808 for (int i = 0; i < subKeysNumber; i++) { 809 byte[] windowsName = WindowsRegEnumKeyEx1(nativeHandle, i, 810 maxKeyLength+1); 811 if (windowsName == null) { 812 String info = 813 "Could not enumerate key #" + i + " of windows node " + 814 byteArrayToString(windowsAbsolutePath()) + " at root 0x" + 815 Long.toHexString(rootNativeHandle()) + ". "; 816 logger().warning(info); 817 throw new BackingStoreException(info); 818 } 819 String javaName = toJavaName(windowsName); 820 children[i] = javaName; 821 } 822 closeKey(nativeHandle); 823 return children; 824 } 825 826 /** 827 * Implements {@code Preferences} {@code flush()} method. 828 * Flushes Windows registry changes to disk. 829 * Throws a BackingStoreException and logs a warning message if Windows 830 * registry is not available. 831 */ 832 public void flush() throws BackingStoreException{ 833 834 if (isRemoved()) { 835 parent.flush(); 836 return; 837 } 838 if (!isBackingStoreAvailable) { 839 throw new BackingStoreException( 840 "flush(): Backing store not available."); 841 } 842 long nativeHandle = openKey(KEY_READ); 843 if (nativeHandle == NULL_NATIVE_HANDLE) { 844 throw new BackingStoreException( 845 "Could not open windows registry node " + 846 byteArrayToString(windowsAbsolutePath()) + 847 " at root 0x" + 848 Long.toHexString(rootNativeHandle()) + "."); 849 } 850 int result = WindowsRegFlushKey1(nativeHandle); 851 if (result != ERROR_SUCCESS) { 852 String info = "Could not flush windows registry node " + 853 byteArrayToString(windowsAbsolutePath()) + 854 " at root 0x" + 855 Long.toHexString(rootNativeHandle()) + 856 ". Windows RegFlushKey(...) returned error code " + 857 result + "."; 858 logger().warning(info); 859 throw new BackingStoreException(info); 860 } 861 closeKey(nativeHandle); 862 } 863 864 865 /** 866 * Implements {@code Preferences} {@code sync()} method. 867 * Flushes Windows registry changes to disk. Equivalent to flush(). 868 * @see flush() 869 */ 870 public void sync() throws BackingStoreException{ 871 if (isRemoved()) 872 throw new IllegalStateException("Node has been removed"); 873 flush(); 874 } 875 876 /** 877 * Implements {@code AbstractPreferences} {@code childSpi()} method. 878 * Constructs a child node with a 879 * given name and creates its underlying Windows registry node, 880 * if it does not exist. 881 * Logs a warning message, if Windows Registry is unavailable. 882 */ 883 protected AbstractPreferences childSpi(String name) { 884 return new WindowsPreferences(this, name); 885 } 886 887 /** 888 * Implements {@code AbstractPreferences} {@code removeNodeSpi()} method. 889 * Deletes underlying Windows registry node. 890 * Throws a BackingStoreException and logs a warning, if Windows registry 891 * is not available. 892 */ 893 public void removeNodeSpi() throws BackingStoreException { 894 long parentNativeHandle = 895 ((WindowsPreferences)parent()).openKey(DELETE); 896 if (parentNativeHandle == NULL_NATIVE_HANDLE) { 897 throw new BackingStoreException( 898 "Could not open parent windows registry node of " + 899 byteArrayToString(windowsAbsolutePath()) + 900 " at root 0x" + 901 Long.toHexString(rootNativeHandle()) + "."); 902 } 903 int result = 904 WindowsRegDeleteKey(parentNativeHandle, toWindowsName(name())); 905 if (result != ERROR_SUCCESS) { 906 String info = "Could not delete windows registry node " + 907 byteArrayToString(windowsAbsolutePath()) + 908 " at root 0x" + Long.toHexString(rootNativeHandle()) + 909 ". Windows RegDeleteKeyEx(...) returned error code " + 910 result + "."; 911 logger().warning(info); 912 throw new BackingStoreException(info); 913 } 914 closeKey(parentNativeHandle); 915 } 916 917 /** 918 * Converts value's or node's name from its byte array representation to 919 * java string. Two encodings, simple and altBase64 are used. See 920 * {@link #toWindowsName(String) toWindowsName()} for a detailed 921 * description of encoding conventions. 922 * @param windowsNameArray Null-terminated byte array. 923 */ 924 private static String toJavaName(byte[] windowsNameArray) { 925 String windowsName = byteArrayToString(windowsNameArray); 926 // check if Alt64 927 if ((windowsName.length() > 1) && 928 (windowsName.substring(0, 2).equals("/!"))) { 929 return toJavaAlt64Name(windowsName); 930 } 931 StringBuilder javaName = new StringBuilder(); 932 char ch; 933 // Decode from simple encoding 934 for (int i = 0; i < windowsName.length(); i++) { 935 if ((ch = windowsName.charAt(i)) == '/') { 936 char next = ' '; 937 if ((windowsName.length() > i + 1) && 938 ((next = windowsName.charAt(i+1)) >= 'A') && 939 (next <= 'Z')) { 940 ch = next; 941 i++; 942 } else if ((windowsName.length() > i + 1) && 943 (next == '/')) { 944 ch = '\\'; 945 i++; 946 } 947 } else if (ch == '\\') { 948 ch = '/'; 949 } 950 javaName.append(ch); 951 } 952 return javaName.toString(); 953 } 954 955 /** 956 * Converts value's or node's name from its Windows representation to java 957 * string, using altBase64 encoding. See 958 * {@link #toWindowsName(String) toWindowsName()} for a detailed 959 * description of encoding conventions. 960 */ 961 962 private static String toJavaAlt64Name(String windowsName) { 963 byte[] byteBuffer = 964 Base64.altBase64ToByteArray(windowsName.substring(2)); 965 StringBuilder result = new StringBuilder(); 966 for (int i = 0; i < byteBuffer.length; i++) { 967 int firstbyte = (byteBuffer[i++] & 0xff); 968 int secondbyte = (byteBuffer[i] & 0xff); 969 result.append((char)((firstbyte << 8) + secondbyte)); 970 } 971 return result.toString(); 972 } 973 974 /** 975 * Converts value's or node's name to its Windows representation 976 * as a byte-encoded string. 977 * Two encodings, simple and altBase64 are used. 978 * <p> 979 * <i>Simple</i> encoding is used, if java string does not contain 980 * any characters less, than 0x0020, or greater, than 0x007f. 981 * Simple encoding adds "/" character to capital letters, i.e. 982 * "A" is encoded as "/A". Character '\' is encoded as '//', 983 * '/' is encoded as '\'. 984 * The constructed string is converted to byte array by truncating the 985 * highest byte and adding the terminating {@code null} character. 986 * <p> 987 * <i>altBase64</i> encoding is used, if java string does contain at least 988 * one character less, than 0x0020, or greater, than 0x007f. 989 * This encoding is marked by setting first two bytes of the 990 * Windows string to '/!'. The java name is then encoded using 991 * byteArrayToAltBase64() method from 992 * Base64 class. 993 */ 994 private static byte[] toWindowsName(String javaName) { 995 StringBuilder windowsName = new StringBuilder(); 996 for (int i = 0; i < javaName.length(); i++) { 997 char ch = javaName.charAt(i); 998 if ((ch < 0x0020) || (ch > 0x007f)) { 999 // If a non-trivial character encountered, use altBase64 1000 return toWindowsAlt64Name(javaName); 1001 } 1002 if (ch == '\\') { 1003 windowsName.append("//"); 1004 } else if (ch == '/') { 1005 windowsName.append('\\'); 1006 } else if ((ch >= 'A') && (ch <='Z')) { 1007 windowsName.append('/').append(ch); 1008 } else { 1009 windowsName.append(ch); 1010 } 1011 } 1012 return stringToByteArray(windowsName.toString()); 1013 } 1014 1015 /** 1016 * Converts value's or node's name to its Windows representation 1017 * as a byte-encoded string, using altBase64 encoding. See 1018 * {@link #toWindowsName(String) toWindowsName()} for a detailed 1019 * description of encoding conventions. 1020 */ 1021 private static byte[] toWindowsAlt64Name(String javaName) { 1022 byte[] javaNameArray = new byte[2*javaName.length()]; 1023 // Convert to byte pairs 1024 int counter = 0; 1025 for (int i = 0; i < javaName.length();i++) { 1026 int ch = javaName.charAt(i); 1027 javaNameArray[counter++] = (byte)(ch >>> 8); 1028 javaNameArray[counter++] = (byte)ch; 1029 } 1030 1031 return stringToByteArray("/!" + 1032 Base64.byteArrayToAltBase64(javaNameArray)); 1033 } 1034 1035 /** 1036 * Converts value string from its Windows representation 1037 * to java string. See 1038 * {@link #toWindowsValueString(String) toWindowsValueString()} for the 1039 * description of the encoding algorithm. 1040 */ 1041 private static String toJavaValueString(byte[] windowsNameArray) { 1042 String windowsName = byteArrayToString(windowsNameArray); 1043 StringBuilder javaName = new StringBuilder(); 1044 char ch; 1045 for (int i = 0; i < windowsName.length(); i++){ 1046 if ((ch = windowsName.charAt(i)) == '/') { 1047 char next = ' '; 1048 1049 if (windowsName.length() > i + 1 && 1050 (next = windowsName.charAt(i + 1)) == 'u') { 1051 if (windowsName.length() < i + 6) { 1052 break; 1053 } else { 1054 ch = (char)Integer.parseInt( 1055 windowsName.substring(i + 2, i + 6), 16); 1056 i += 5; 1057 } 1058 } else 1059 if ((windowsName.length() > i + 1) && 1060 ((windowsName.charAt(i+1)) >= 'A') && 1061 (next <= 'Z')) { 1062 ch = next; 1063 i++; 1064 } else if ((windowsName.length() > i + 1) && 1065 (next == '/')) { 1066 ch = '\\'; 1067 i++; 1068 } 1069 } else if (ch == '\\') { 1070 ch = '/'; 1071 } 1072 javaName.append(ch); 1073 } 1074 return javaName.toString(); 1075 } 1076 1077 /** 1078 * Converts value string to it Windows representation. 1079 * as a byte-encoded string. 1080 * Encoding algorithm adds "/" character to capital letters, i.e. 1081 * "A" is encoded as "/A". Character '\' is encoded as '//', 1082 * '/' is encoded as '\'. 1083 * Then convert java string to a byte array of ASCII characters. 1084 */ 1085 private static byte[] toWindowsValueString(String javaName) { 1086 StringBuilder windowsName = new StringBuilder(); 1087 for (int i = 0; i < javaName.length(); i++) { 1088 char ch = javaName.charAt(i); 1089 if ((ch < 0x0020) || (ch > 0x007f)){ 1090 // write \udddd 1091 windowsName.append("/u"); 1092 String hex = Long.toHexString(javaName.charAt(i)); 1093 StringBuilder hex4 = new StringBuilder(hex); 1094 hex4.reverse(); 1095 int len = 4 - hex4.length(); 1096 for (int j = 0; j < len; j++){ 1097 hex4.append('0'); 1098 } 1099 for (int j = 0; j < 4; j++){ 1100 windowsName.append(hex4.charAt(3 - j)); 1101 } 1102 } else if (ch == '\\') { 1103 windowsName.append("//"); 1104 } else if (ch == '/') { 1105 windowsName.append('\\'); 1106 } else if ((ch >= 'A') && (ch <='Z')) { 1107 windowsName.append('/').append(ch); 1108 } else { 1109 windowsName.append(ch); 1110 } 1111 } 1112 return stringToByteArray(windowsName.toString()); 1113 } 1114 1115 /** 1116 * Returns native handle for the top Windows node for this node. 1117 */ 1118 private long rootNativeHandle() { 1119 return (isUserNode() 1120 ? USER_ROOT_NATIVE_HANDLE 1121 : SYSTEM_ROOT_NATIVE_HANDLE); 1122 } 1123 1124 /** 1125 * Returns this java string as a null-terminated byte array 1126 */ 1127 private static byte[] stringToByteArray(String str) { 1128 byte[] result = new byte[str.length()+1]; 1129 for (int i = 0; i < str.length(); i++) { 1130 result[i] = (byte) str.charAt(i); 1131 } 1132 result[str.length()] = 0; 1133 return result; 1134 } 1135 1136 /** 1137 * Converts a null-terminated byte array to java string 1138 */ 1139 private static String byteArrayToString(byte[] array) { 1140 StringBuilder result = new StringBuilder(); 1141 for (int i = 0; i < array.length - 1; i++) { 1142 result.append((char)array[i]); 1143 } 1144 return result.toString(); 1145 } 1146 1147 /** 1148 * Empty, never used implementation of AbstractPreferences.flushSpi(). 1149 */ 1150 protected void flushSpi() throws BackingStoreException { 1151 // assert false; 1152 } 1153 1154 /** 1155 * Empty, never used implementation of AbstractPreferences.flushSpi(). 1156 */ 1157 protected void syncSpi() throws BackingStoreException { 1158 // assert false; 1159 } 1160 1161 private static synchronized PlatformLogger logger() { 1162 if (logger == null) { 1163 logger = PlatformLogger.getLogger("java.util.prefs"); 1164 } 1165 return logger; 1166 } 1167 }