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 
getUserRoot()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 
getSystemRoot()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      */
WindowsRegOpenKey(long hKey, byte[] subKey, int securityMask)175     private static native long[] WindowsRegOpenKey(long hKey, byte[] subKey,
176                                                    int securityMask);
177     /**
178      * Retries RegOpenKey() MAX_ATTEMPTS times before giving up.
179      */
WindowsRegOpenKey1(long hKey, byte[] subKey, int securityMask)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      */
WindowsRegCloseKey(long hKey)214     private static native int WindowsRegCloseKey(long hKey);
215 
216     /**
217      * Java wrapper for Windows registry API RegCreateKeyEx()
218      */
WindowsRegCreateKeyEx(long hKey, byte[] subKey)219     private static native long[] WindowsRegCreateKeyEx(long hKey, byte[] subKey);
220 
221     /**
222      * Retries RegCreateKeyEx() MAX_ATTEMPTS times before giving up.
223      */
WindowsRegCreateKeyEx1(long hKey, byte[] subKey)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      */
WindowsRegDeleteKey(long hKey, byte[] subKey)248     private static native int WindowsRegDeleteKey(long hKey, byte[] subKey);
249 
250     /**
251      * Java wrapper for Windows registry API RegFlushKey()
252      */
WindowsRegFlushKey(long hKey)253     private static native int WindowsRegFlushKey(long hKey);
254 
255     /**
256      * Retries RegFlushKey() MAX_ATTEMPTS times before giving up.
257      */
WindowsRegFlushKey1(long hKey)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      */
WindowsRegQueryValueEx(long hKey, byte[] valueName)283     private static native byte[] WindowsRegQueryValueEx(long hKey,
284                                                         byte[] valueName);
285     /**
286      * Java wrapper for Windows registry API RegSetValueEx()
287      */
WindowsRegSetValueEx(long hKey, byte[] valueName, byte[] value)288     private static native int WindowsRegSetValueEx(long hKey, byte[] valueName,
289                                                    byte[] value);
290     /**
291      * Retries RegSetValueEx() MAX_ATTEMPTS times before giving up.
292      */
WindowsRegSetValueEx1(long hKey, byte[] valueName, byte[] value)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      */
WindowsRegDeleteValue(long hKey, byte[] valueName)319     private static native int WindowsRegDeleteValue(long hKey, byte[] valueName);
320 
321     /**
322      * Java wrapper for Windows registry API RegQueryInfoKey()
323      */
WindowsRegQueryInfoKey(long hKey)324     private static native long[] WindowsRegQueryInfoKey(long hKey);
325 
326     /**
327      * Retries RegQueryInfoKey() MAX_ATTEMPTS times before giving up.
328      */
WindowsRegQueryInfoKey1(long hKey)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      */
WindowsRegEnumKeyEx(long hKey, int subKeyIndex, int maxKeyLength)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      */
WindowsRegEnumKeyEx1(long hKey, int subKeyIndex, int maxKeyLength)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      */
WindowsRegEnumValue(long hKey, int valueIndex, int maxValueNameLength)386     private static native byte[] WindowsRegEnumValue(long hKey, int valueIndex,
387                                                      int maxValueNameLength);
388     /**
389      * Retries RegEnumValueEx() MAX_ATTEMPTS times before giving up.
390      */
WindowsRegEnumValue1(long hKey, int valueIndex, int maxValueNameLength)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      */
WindowsPreferences(WindowsPreferences parent, String name)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      */
WindowsPreferences(long rootNativeHandle, byte[] rootDirectory)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      */
windowsAbsolutePath()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      */
openKey(int securityMask)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      */
openKey(int mask1, int mask2)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      */
openKey(byte[] windowsAbsolutePath, int mask1, int mask2)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      */
openKey(long nativeHandle, byte[] windowsRelativePath, int mask1, int mask2)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     */
closeKey(long nativeHandle)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      */
putSpi(String javaName, String value)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      */
getSpi(String javaName)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      */
removeSpi(String key)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      */
keysSpi()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      */
childrenNamesSpi()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      */
flush()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      */
sync()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      */
childSpi(String name)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      */
removeNodeSpi()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      */
toJavaName(byte[] windowsNameArray)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 
toJavaAlt64Name(String windowsName)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      */
toWindowsName(String javaName)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      */
toWindowsAlt64Name(String javaName)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      */
toJavaValueString(byte[] windowsNameArray)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      */
toWindowsValueString(String javaName)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      */
rootNativeHandle()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      */
stringToByteArray(String str)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      */
byteArrayToString(byte[] array)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     */
flushSpi()1150     protected void flushSpi() throws BackingStoreException {
1151         // assert false;
1152     }
1153 
1154    /**
1155     * Empty, never used implementation  of AbstractPreferences.flushSpi().
1156     */
syncSpi()1157     protected void syncSpi() throws BackingStoreException {
1158         // assert false;
1159     }
1160 
logger()1161     private static synchronized PlatformLogger logger() {
1162         if (logger == null) {
1163             logger = PlatformLogger.getLogger("java.util.prefs");
1164         }
1165         return logger;
1166     }
1167 }
1168