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