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 }