1 /*
2 * Copyright (c) 2000, 2015, 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
155
156 /**
157 * Initial time between registry access attempts, in ms. The time is doubled
158 * after each failing attempt (except the first).
159 */
160 private static int INIT_SLEEP_TIME = 50;
161
162 /**
163 * Maximum number of registry access attempts.
164 */
165 private static int MAX_ATTEMPTS = 5;
166
167 /**
168 * BackingStore availability flag.
169 */
170 private boolean isBackingStoreAvailable = true;
171
172 /**
173 * Java wrapper for Windows registry API RegOpenKey()
174 */
175 private static native int[] WindowsRegOpenKey(int hKey, byte[] subKey,
176 int securityMask);
177 /**
178 * Retries RegOpenKey() MAX_ATTEMPTS times before giving up.
179 */
180 private static int[] WindowsRegOpenKey1(int hKey, byte[] subKey,
181 int securityMask) {
182 int[] 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 Integer.toHexString(hKey) + ".");
189 // Try recreation
190 int handle = WindowsRegCreateKeyEx(hKey, subKey)[NATIVE_HANDLE];
191 WindowsRegCloseKey(handle);
192 return WindowsRegOpenKey(hKey, subKey, securityMask);
193 } else if (result[ERROR_CODE] != ERROR_ACCESS_DENIED) {
194 long sleepTime = INIT_SLEEP_TIME;
195 for (int i = 0; i < MAX_ATTEMPTS; i++) {
196 try {
197 Thread.sleep(sleepTime);
198 } catch(InterruptedException e) {
199 return result;
200 }
201 sleepTime *= 2;
202 result = WindowsRegOpenKey(hKey, subKey, securityMask);
203 if (result[ERROR_CODE] == ERROR_SUCCESS) {
204 return result;
205 }
206 }
207 }
208 return result;
209 }
210
211 /**
212 * Java wrapper for Windows registry API RegCloseKey()
213 */
214 private static native int WindowsRegCloseKey(int hKey);
215
216 /**
217 * Java wrapper for Windows registry API RegCreateKeyEx()
218 */
219 private static native int[] WindowsRegCreateKeyEx(int hKey, byte[] subKey);
220
221 /**
222 * Retries RegCreateKeyEx() MAX_ATTEMPTS times before giving up.
223 */
224 private static int[] WindowsRegCreateKeyEx1(int hKey, byte[] subKey) {
225 int[] result = WindowsRegCreateKeyEx(hKey, subKey);
226 if (result[ERROR_CODE] == ERROR_SUCCESS) {
227 return result;
228 } else {
229 long sleepTime = INIT_SLEEP_TIME;
230 for (int i = 0; i < MAX_ATTEMPTS; i++) {
231 try {
232 Thread.sleep(sleepTime);
233 } catch(InterruptedException e) {
234 return result;
235 }
236 sleepTime *= 2;
237 result = WindowsRegCreateKeyEx(hKey, subKey);
238 if (result[ERROR_CODE] == ERROR_SUCCESS) {
239 return result;
240 }
241 }
242 }
243 return result;
244 }
245 /**
246 * Java wrapper for Windows registry API RegDeleteKey()
247 */
248 private static native int WindowsRegDeleteKey(int hKey, byte[] subKey);
249
250 /**
251 * Java wrapper for Windows registry API RegFlushKey()
252 */
253 private static native int WindowsRegFlushKey(int hKey);
254
255 /**
256 * Retries RegFlushKey() MAX_ATTEMPTS times before giving up.
257 */
258 private static int WindowsRegFlushKey1(int hKey) {
259 int result = WindowsRegFlushKey(hKey);
260 if (result == ERROR_SUCCESS) {
261 return result;
262 } else {
263 long sleepTime = INIT_SLEEP_TIME;
264 for (int i = 0; i < MAX_ATTEMPTS; i++) {
265 try {
266 Thread.sleep(sleepTime);
267 } catch(InterruptedException e) {
268 return result;
269 }
270 sleepTime *= 2;
271 result = WindowsRegFlushKey(hKey);
272 if (result == ERROR_SUCCESS) {
273 return result;
274 }
275 }
276 }
277 return result;
278 }
279
280 /**
281 * Java wrapper for Windows registry API RegQueryValueEx()
282 */
283 private static native byte[] WindowsRegQueryValueEx(int hKey,
284 byte[] valueName);
285 /**
286 * Java wrapper for Windows registry API RegSetValueEx()
287 */
288 private static native int WindowsRegSetValueEx(int hKey, byte[] valueName,
289 byte[] value);
290 /**
291 * Retries RegSetValueEx() MAX_ATTEMPTS times before giving up.
292 */
293 private static int WindowsRegSetValueEx1(int hKey, byte[] valueName,
294 byte[] value) {
295 int result = WindowsRegSetValueEx(hKey, valueName, value);
296 if (result == ERROR_SUCCESS) {
297 return result;
298 } else {
299 long sleepTime = INIT_SLEEP_TIME;
300 for (int i = 0; i < MAX_ATTEMPTS; i++) {
301 try {
302 Thread.sleep(sleepTime);
303 } catch(InterruptedException e) {
304 return result;
305 }
306 sleepTime *= 2;
307 result = WindowsRegSetValueEx(hKey, valueName, value);
308 if (result == ERROR_SUCCESS) {
309 return result;
310 }
311 }
312 }
313 return result;
314 }
315
316 /**
317 * Java wrapper for Windows registry API RegDeleteValue()
318 */
319 private static native int WindowsRegDeleteValue(int hKey, byte[] valueName);
320
321 /**
322 * Java wrapper for Windows registry API RegQueryInfoKey()
323 */
324 private static native int[] WindowsRegQueryInfoKey(int hKey);
325
326 /**
327 * Retries RegQueryInfoKey() MAX_ATTEMPTS times before giving up.
328 */
329 private static int[] WindowsRegQueryInfoKey1(int hKey) {
330 int[] result = WindowsRegQueryInfoKey(hKey);
331 if (result[ERROR_CODE] == ERROR_SUCCESS) {
332 return result;
333 } else {
334 long sleepTime = INIT_SLEEP_TIME;
335 for (int i = 0; i < MAX_ATTEMPTS; i++) {
336 try {
337 Thread.sleep(sleepTime);
338 } catch(InterruptedException e) {
339 return result;
340 }
341 sleepTime *= 2;
342 result = WindowsRegQueryInfoKey(hKey);
343 if (result[ERROR_CODE] == ERROR_SUCCESS) {
344 return result;
345 }
346 }
347 }
348 return result;
349 }
350
351 /**
352 * Java wrapper for Windows registry API RegEnumKeyEx()
353 */
354 private static native byte[] WindowsRegEnumKeyEx(int hKey, int subKeyIndex,
355 int maxKeyLength);
356
357 /**
358 * Retries RegEnumKeyEx() MAX_ATTEMPTS times before giving up.
359 */
360 private static byte[] WindowsRegEnumKeyEx1(int hKey, int subKeyIndex,
361 int maxKeyLength) {
362 byte[] result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength);
363 if (result != null) {
364 return result;
365 } else {
366 long sleepTime = INIT_SLEEP_TIME;
367 for (int i = 0; i < MAX_ATTEMPTS; i++) {
368 try {
369 Thread.sleep(sleepTime);
370 } catch(InterruptedException e) {
371 return result;
372 }
373 sleepTime *= 2;
374 result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength);
375 if (result != null) {
376 return result;
377 }
378 }
379 }
380 return result;
381 }
382
383 /**
384 * Java wrapper for Windows registry API RegEnumValue()
385 */
386 private static native byte[] WindowsRegEnumValue(int hKey, int valueIndex,
387 int maxValueNameLength);
388 /**
389 * Retries RegEnumValueEx() MAX_ATTEMPTS times before giving up.
390 */
391 private static byte[] WindowsRegEnumValue1(int hKey, int valueIndex,
392 int maxValueNameLength) {
393 byte[] result = WindowsRegEnumValue(hKey, valueIndex,
394 maxValueNameLength);
395 if (result != null) {
396 return result;
397 } else {
398 long sleepTime = INIT_SLEEP_TIME;
399 for (int i = 0; i < MAX_ATTEMPTS; i++) {
400 try {
401 Thread.sleep(sleepTime);
402 } catch(InterruptedException e) {
403 return result;
404 }
405 sleepTime *= 2;
406 result = WindowsRegEnumValue(hKey, valueIndex,
407 maxValueNameLength);
408 if (result != null) {
409 return result;
410 }
411 }
412 }
413 return result;
414 }
415
416 /**
417 * Constructs a {@code WindowsPreferences} node, creating underlying
418 * Windows registry node and all its Windows parents, if they are not yet
419 * created.
420 * Logs a warning message, if Windows Registry is unavailable.
421 */
422 private WindowsPreferences(WindowsPreferences parent, String name) {
423 super(parent, name);
424 int 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 int[] 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" + Integer.toHexString(rootNativeHandle()) +
436 ". Windows RegCreateKeyEx(...) returned error code " +
437 result[ERROR_CODE] + ".");
438 isBackingStoreAvailable = false;
439 return;
440 }
441 newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
442 closeKey(parentNativeHandle);
443 closeKey(result[NATIVE_HANDLE]);
444 }
445
446 /**
447 * Constructs a root node creating the underlying
448 * Windows registry node and all of its parents, if they have not yet been
449 * created.
450 * Logs a warning message, if Windows Registry is unavailable.
451 * @param rootNativeHandle Native handle to one of Windows top level keys.
452 * @param rootDirectory Path to root directory, as a byte-encoded string.
453 */
454 private WindowsPreferences(int rootNativeHandle, byte[] rootDirectory) {
455 super(null, "");
456 int[] 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" + Integer.toHexString(rootNativeHandle()) +
462 ". Windows RegCreateKeyEx(...) returned error code " +
463 result[ERROR_CODE] + ".");
464 isBackingStoreAvailable = false;
465 return;
466 }
467 // Check if a new node
468 newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
469 closeKey(result[NATIVE_HANDLE]);
470 }
471
472 /**
473 * Returns Windows absolute path of the current node as a byte array.
474 * Java "/" separator is transformed into Windows "\".
475 * @see Preferences#absolutePath()
476 */
477 private byte[] windowsAbsolutePath() {
478 ByteArrayOutputStream bstream = new ByteArrayOutputStream();
479 bstream.write(WINDOWS_ROOT_PATH, 0, WINDOWS_ROOT_PATH.length-1);
480 StringTokenizer tokenizer = new StringTokenizer(absolutePath(), "/");
481 while (tokenizer.hasMoreTokens()) {
482 bstream.write((byte)'\\');
483 String nextName = tokenizer.nextToken();
484 byte[] windowsNextName = toWindowsName(nextName);
485 bstream.write(windowsNextName, 0, windowsNextName.length-1);
486 }
487 bstream.write(0);
488 return bstream.toByteArray();
489 }
490
491 /**
492 * Opens current node's underlying Windows registry key using a
493 * given security mask.
494 * @param securityMask Windows security mask.
495 * @return Windows registry key's handle.
496 * @see #openKey(byte[], int)
497 * @see #openKey(int, byte[], int)
498 * @see #closeKey(int)
499 */
500 private int openKey(int securityMask) {
501 return openKey(securityMask, securityMask);
502 }
503
504 /**
505 * Opens current node's underlying Windows registry key using a
506 * given security mask.
507 * @param mask1 Preferred Windows security mask.
508 * @param mask2 Alternate Windows security mask.
509 * @return Windows registry key's handle.
510 * @see #openKey(byte[], int)
511 * @see #openKey(int, byte[], int)
512 * @see #closeKey(int)
513 */
514 private int openKey(int mask1, int mask2) {
515 return openKey(windowsAbsolutePath(), mask1, mask2);
516 }
517
518 /**
519 * Opens Windows registry key at a given absolute path using a given
520 * security mask.
521 * @param windowsAbsolutePath Windows absolute path of the
522 * key as a byte-encoded string.
523 * @param mask1 Preferred Windows security mask.
524 * @param mask2 Alternate Windows security mask.
525 * @return Windows registry key's handle.
526 * @see #openKey(int)
527 * @see #openKey(int, byte[],int)
528 * @see #closeKey(int)
529 */
530 private int 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 int[] 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 Integer.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 Integer.toHexString(rootNativeHandle()) +
554 ": Access denied");
555 }
556 }
557 return result[NATIVE_HANDLE];
558 } else {
559 return openKey(rootNativeHandle(), windowsAbsolutePath, mask1, mask2);
560 }
561 }
562
563 /**
564 * Opens Windows registry key at a given relative path
565 * with respect to a given Windows registry key.
566 * @param windowsAbsolutePath Windows relative path of the
567 * key as a byte-encoded string.
568 * @param nativeHandle handle to the base Windows key.
569 * @param mask1 Preferred Windows security mask.
570 * @param mask2 Alternate Windows security mask.
571 * @return Windows registry key's handle.
572 * @see #openKey(int)
573 * @see #openKey(byte[],int)
574 * @see #closeKey(int)
575 */
576 private int openKey(int 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 int[] 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" + Integer.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 int nextNativeHandle = openKey(nativeHandle, nextRelativeRoot,
614 mask1, mask2);
615 if (nextNativeHandle == NULL_NATIVE_HANDLE) {
616 return NULL_NATIVE_HANDLE;
617 }
618 int result = openKey(nextNativeHandle, nextRelativePath,
619 mask1,mask2);
620 closeKey(nextNativeHandle);
621 return result;
622 }
623 }
624
625 /**
626 * Closes Windows registry key.
627 * Logs a warning if Windows registry is unavailable.
628 * @param key's Windows registry handle.
629 * @see #openKey(int)
630 * @see #openKey(byte[],int)
631 * @see #openKey(int, byte[],int)
632 */
633 private void closeKey(int 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 Integer.toHexString(rootNativeHandle()) +
640 ". Windows RegCloseKey(...) returned error code " +
641 result + ".");
642 }
643 }
644
645 /**
646 * Implements {@code AbstractPreferences} {@code putSpi()} method.
647 * Puts name-value pair into the underlying Windows registry node.
648 * Logs a warning, if Windows registry is unavailable.
649 * @see #getSpi(String)
650 */
651 protected void putSpi(String javaName, String value) {
652 int 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 Integer.toHexString(rootNativeHandle()) +
666 ". Windows RegSetValueEx(...) returned error code " +
667 result + ".");
668 isBackingStoreAvailable = false;
669 }
670 closeKey(nativeHandle);
671 }
672
673 /**
674 * Implements {@code AbstractPreferences} {@code getSpi()} method.
675 * Gets a string value from the underlying Windows registry node.
676 * Logs a warning, if Windows registry is unavailable.
677 * @see #putSpi(String, String)
678 */
679 protected String getSpi(String javaName) {
680 int nativeHandle = openKey(KEY_QUERY_VALUE);
681 if (nativeHandle == NULL_NATIVE_HANDLE) {
682 return null;
683 }
684 Object resultObject = WindowsRegQueryValueEx(nativeHandle,
685 toWindowsName(javaName));
686 if (resultObject == null) {
687 closeKey(nativeHandle);
688 return null;
689 }
690 closeKey(nativeHandle);
691 return toJavaValueString((byte[]) resultObject);
692 }
693
694 /**
695 * Implements {@code AbstractPreferences} {@code removeSpi()} method.
696 * Deletes a string name-value pair from the underlying Windows registry
697 * node, if this value still exists.
698 * Logs a warning, if Windows registry is unavailable or key has already
699 * been deleted.
700 */
701 protected void removeSpi(String key) {
702 int 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 Integer.toHexString(rootNativeHandle()) +
713 ". Windows RegDeleteValue(...) returned error code " +
714 result + ".");
715 isBackingStoreAvailable = false;
716 }
717 closeKey(nativeHandle);
718 }
719
720 /**
721 * Implements {@code AbstractPreferences} {@code keysSpi()} method.
722 * Gets value names from the underlying Windows registry node.
723 * Throws a BackingStoreException and logs a warning, if
724 * Windows registry is unavailable.
725 */
726 protected String[] keysSpi() throws BackingStoreException{
727 // Find out the number of values
728 int 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 Integer.toHexString(rootNativeHandle()) + ".");
735 }
736 int[] 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 Integer.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 = result[MAX_VALUE_NAME_LENGTH];
748 int valuesNumber = 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 Integer.toHexString(rootNativeHandle()) + ".";
763 logger().warning(info);
764 throw new BackingStoreException(info);
765 }
766 valueNames[i] = toJavaName(windowsName);
767 }
768 closeKey(nativeHandle);
769 return valueNames;
770 }
771
772 /**
773 * Implements {@code AbstractPreferences} {@code childrenNamesSpi()} method.
774 * Calls Windows registry to retrive children of this node.
775 * Throws a BackingStoreException and logs a warning message,
776 * if Windows registry is not available.
777 */
778 protected String[] childrenNamesSpi() throws BackingStoreException {
779 // Open key
780 int 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 Integer.toHexString(rootNativeHandle()) + ".");
787 }
788 // Get number of children
789 int[] 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" + Integer.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 = result[MAX_KEY_LENGTH];
800 int subKeysNumber = 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 Integer.toHexString(rootNativeHandle()) + ". ";
816 logger().warning(info);
817 throw new BackingStoreException(info);
818 }
819 String javaName = toJavaName(windowsName);
820 children[i] = javaName;
821 }
822 closeKey(nativeHandle);
823 return children;
824 }
825
826 /**
827 * Implements {@code Preferences} {@code flush()} method.
828 * Flushes Windows registry changes to disk.
829 * Throws a BackingStoreException and logs a warning message if Windows
830 * registry is not available.
831 */
832 public void flush() throws BackingStoreException{
833
834 if (isRemoved()) {
835 parent.flush();
836 return;
837 }
838 if (!isBackingStoreAvailable) {
839 throw new BackingStoreException(
840 "flush(): Backing store not available.");
841 }
842 int 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 Integer.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 Integer.toHexString(rootNativeHandle()) +
856 ". Windows RegFlushKey(...) returned error code " +
857 result + ".";
858 logger().warning(info);
859 throw new BackingStoreException(info);
860 }
861 closeKey(nativeHandle);
862 }
863
864
865 /**
866 * Implements {@code Preferences} {@code sync()} method.
867 * Flushes Windows registry changes to disk. Equivalent to flush().
868 * @see flush()
869 */
870 public void sync() throws BackingStoreException{
871 if (isRemoved())
872 throw new IllegalStateException("Node has been removed");
873 flush();
874 }
875
876 /**
877 * Implements {@code AbstractPreferences} {@code childSpi()} method.
878 * Constructs a child node with a
879 * given name and creates its underlying Windows registry node,
880 * if it does not exist.
881 * Logs a warning message, if Windows Registry is unavailable.
882 */
883 protected AbstractPreferences childSpi(String name) {
884 return new WindowsPreferences(this, name);
885 }
886
887 /**
888 * Implements {@code AbstractPreferences} {@code removeNodeSpi()} method.
889 * Deletes underlying Windows registry node.
890 * Throws a BackingStoreException and logs a warning, if Windows registry
891 * is not available.
892 */
893 public void removeNodeSpi() throws BackingStoreException {
894 int 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 Integer.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" + Integer.toHexString(rootNativeHandle()) +
909 ". Windows RegDeleteKeyEx(...) returned error code " +
910 result + ".";
911 logger().warning(info);
912 throw new BackingStoreException(info);
913 }
914 closeKey(parentNativeHandle);
915 }
916
917 /**
918 * Converts value's or node's name from its byte array representation to
919 * java string. Two encodings, simple and altBase64 are used. See
920 * {@link #toWindowsName(String) toWindowsName()} for a detailed
921 * description of encoding conventions.
922 * @param windowsNameArray Null-terminated byte array.
923 */
924 private static String toJavaName(byte[] windowsNameArray) {
925 String windowsName = byteArrayToString(windowsNameArray);
926 // check if Alt64
927 if ((windowsName.length() > 1) &&
928 (windowsName.substring(0, 2).equals("/!"))) {
1072 javaName.append(ch);
1073 }
1074 return javaName.toString();
1075 }
1076
1077 /**
1078 * Converts value string to it Windows representation.
1079 * as a byte-encoded string.
1080 * Encoding algorithm adds "/" character to capital letters, i.e.
1081 * "A" is encoded as "/A". Character '\' is encoded as '//',
1082 * '/' is encoded as '\'.
1083 * Then convert java string to a byte array of ASCII characters.
1084 */
1085 private static byte[] toWindowsValueString(String javaName) {
1086 StringBuilder windowsName = new StringBuilder();
1087 for (int i = 0; i < javaName.length(); i++) {
1088 char ch = javaName.charAt(i);
1089 if ((ch < 0x0020) || (ch > 0x007f)){
1090 // write \udddd
1091 windowsName.append("/u");
1092 String hex = Integer.toHexString(javaName.charAt(i));
1093 StringBuilder hex4 = new StringBuilder(hex);
1094 hex4.reverse();
1095 int len = 4 - hex4.length();
1096 for (int j = 0; j < len; j++){
1097 hex4.append('0');
1098 }
1099 for (int j = 0; j < 4; j++){
1100 windowsName.append(hex4.charAt(3 - j));
1101 }
1102 } else if (ch == '\\') {
1103 windowsName.append("//");
1104 } else if (ch == '/') {
1105 windowsName.append('\\');
1106 } else if ((ch >= 'A') && (ch <='Z')) {
1107 windowsName.append('/').append(ch);
1108 } else {
1109 windowsName.append(ch);
1110 }
1111 }
1112 return stringToByteArray(windowsName.toString());
1113 }
1114
1115 /**
1116 * Returns native handle for the top Windows node for this node.
1117 */
1118 private int rootNativeHandle() {
1119 return (isUserNode()
1120 ? USER_ROOT_NATIVE_HANDLE
1121 : SYSTEM_ROOT_NATIVE_HANDLE);
1122 }
1123
1124 /**
1125 * Returns this java string as a null-terminated byte array
1126 */
1127 private static byte[] stringToByteArray(String str) {
1128 byte[] result = new byte[str.length()+1];
1129 for (int i = 0; i < str.length(); i++) {
1130 result[i] = (byte) str.charAt(i);
1131 }
1132 result[str.length()] = 0;
1133 return result;
1134 }
1135
1136 /**
1137 * Converts a null-terminated byte array to java string
1138 */
|
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
155
156 /**
157 * Initial time between registry access attempts, in ms. The time is doubled
158 * after each failing attempt (except the first).
159 */
160 private static int INIT_SLEEP_TIME = 50;
161
162 /**
163 * Maximum number of registry access attempts.
164 */
165 private static int MAX_ATTEMPTS = 5;
166
167 /**
168 * BackingStore availability flag.
169 */
170 private boolean isBackingStoreAvailable = true;
171
172 /**
173 * Java wrapper for Windows registry API RegOpenKey()
174 */
175 private static native long[] WindowsRegOpenKey(long hKey, byte[] subKey,
176 int securityMask);
177 /**
178 * Retries RegOpenKey() MAX_ATTEMPTS times before giving up.
179 */
180 private static long[] WindowsRegOpenKey1(long hKey, byte[] subKey,
181 int securityMask) {
182 long[] result = WindowsRegOpenKey(hKey, subKey, securityMask);
183 if (result[ERROR_CODE] == ERROR_SUCCESS) {
184 return result;
185 } else if (result[ERROR_CODE] == ERROR_FILE_NOT_FOUND) {
186 logger().warning("Trying to recreate Windows registry node " +
187 byteArrayToString(subKey) + " at root 0x" +
188 Long.toHexString(hKey) + ".");
189 // Try recreation
190 long handle = WindowsRegCreateKeyEx(hKey, subKey)[NATIVE_HANDLE];
191 WindowsRegCloseKey(handle);
192 return WindowsRegOpenKey(hKey, subKey, securityMask);
193 } else if (result[ERROR_CODE] != ERROR_ACCESS_DENIED) {
194 long sleepTime = INIT_SLEEP_TIME;
195 for (int i = 0; i < MAX_ATTEMPTS; i++) {
196 try {
197 Thread.sleep(sleepTime);
198 } catch(InterruptedException e) {
199 return result;
200 }
201 sleepTime *= 2;
202 result = WindowsRegOpenKey(hKey, subKey, securityMask);
203 if (result[ERROR_CODE] == ERROR_SUCCESS) {
204 return result;
205 }
206 }
207 }
208 return result;
209 }
210
211 /**
212 * Java wrapper for Windows registry API RegCloseKey()
213 */
214 private static native int WindowsRegCloseKey(long hKey);
215
216 /**
217 * Java wrapper for Windows registry API RegCreateKeyEx()
218 */
219 private static native long[] WindowsRegCreateKeyEx(long hKey, byte[] subKey);
220
221 /**
222 * Retries RegCreateKeyEx() MAX_ATTEMPTS times before giving up.
223 */
224 private static long[] WindowsRegCreateKeyEx1(long hKey, byte[] subKey) {
225 long[] result = WindowsRegCreateKeyEx(hKey, subKey);
226 if (result[ERROR_CODE] == ERROR_SUCCESS) {
227 return result;
228 } else {
229 long sleepTime = INIT_SLEEP_TIME;
230 for (int i = 0; i < MAX_ATTEMPTS; i++) {
231 try {
232 Thread.sleep(sleepTime);
233 } catch(InterruptedException e) {
234 return result;
235 }
236 sleepTime *= 2;
237 result = WindowsRegCreateKeyEx(hKey, subKey);
238 if (result[ERROR_CODE] == ERROR_SUCCESS) {
239 return result;
240 }
241 }
242 }
243 return result;
244 }
245 /**
246 * Java wrapper for Windows registry API RegDeleteKey()
247 */
248 private static native int WindowsRegDeleteKey(long hKey, byte[] subKey);
249
250 /**
251 * Java wrapper for Windows registry API RegFlushKey()
252 */
253 private static native int WindowsRegFlushKey(long hKey);
254
255 /**
256 * Retries RegFlushKey() MAX_ATTEMPTS times before giving up.
257 */
258 private static int WindowsRegFlushKey1(long hKey) {
259 int result = WindowsRegFlushKey(hKey);
260 if (result == ERROR_SUCCESS) {
261 return result;
262 } else {
263 long sleepTime = INIT_SLEEP_TIME;
264 for (int i = 0; i < MAX_ATTEMPTS; i++) {
265 try {
266 Thread.sleep(sleepTime);
267 } catch(InterruptedException e) {
268 return result;
269 }
270 sleepTime *= 2;
271 result = WindowsRegFlushKey(hKey);
272 if (result == ERROR_SUCCESS) {
273 return result;
274 }
275 }
276 }
277 return result;
278 }
279
280 /**
281 * Java wrapper for Windows registry API RegQueryValueEx()
282 */
283 private static native byte[] WindowsRegQueryValueEx(long hKey,
284 byte[] valueName);
285 /**
286 * Java wrapper for Windows registry API RegSetValueEx()
287 */
288 private static native int WindowsRegSetValueEx(long hKey, byte[] valueName,
289 byte[] value);
290 /**
291 * Retries RegSetValueEx() MAX_ATTEMPTS times before giving up.
292 */
293 private static int WindowsRegSetValueEx1(long hKey, byte[] valueName,
294 byte[] value) {
295 int result = WindowsRegSetValueEx(hKey, valueName, value);
296 if (result == ERROR_SUCCESS) {
297 return result;
298 } else {
299 long sleepTime = INIT_SLEEP_TIME;
300 for (int i = 0; i < MAX_ATTEMPTS; i++) {
301 try {
302 Thread.sleep(sleepTime);
303 } catch(InterruptedException e) {
304 return result;
305 }
306 sleepTime *= 2;
307 result = WindowsRegSetValueEx(hKey, valueName, value);
308 if (result == ERROR_SUCCESS) {
309 return result;
310 }
311 }
312 }
313 return result;
314 }
315
316 /**
317 * Java wrapper for Windows registry API RegDeleteValue()
318 */
319 private static native int WindowsRegDeleteValue(long hKey, byte[] valueName);
320
321 /**
322 * Java wrapper for Windows registry API RegQueryInfoKey()
323 */
324 private static native long[] WindowsRegQueryInfoKey(long hKey);
325
326 /**
327 * Retries RegQueryInfoKey() MAX_ATTEMPTS times before giving up.
328 */
329 private static long[] WindowsRegQueryInfoKey1(long hKey) {
330 long[] result = WindowsRegQueryInfoKey(hKey);
331 if (result[ERROR_CODE] == ERROR_SUCCESS) {
332 return result;
333 } else {
334 long sleepTime = INIT_SLEEP_TIME;
335 for (int i = 0; i < MAX_ATTEMPTS; i++) {
336 try {
337 Thread.sleep(sleepTime);
338 } catch(InterruptedException e) {
339 return result;
340 }
341 sleepTime *= 2;
342 result = WindowsRegQueryInfoKey(hKey);
343 if (result[ERROR_CODE] == ERROR_SUCCESS) {
344 return result;
345 }
346 }
347 }
348 return result;
349 }
350
351 /**
352 * Java wrapper for Windows registry API RegEnumKeyEx()
353 */
354 private static native byte[] WindowsRegEnumKeyEx(long hKey, int subKeyIndex,
355 int maxKeyLength);
356
357 /**
358 * Retries RegEnumKeyEx() MAX_ATTEMPTS times before giving up.
359 */
360 private static byte[] WindowsRegEnumKeyEx1(long hKey, int subKeyIndex,
361 int maxKeyLength) {
362 byte[] result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength);
363 if (result != null) {
364 return result;
365 } else {
366 long sleepTime = INIT_SLEEP_TIME;
367 for (int i = 0; i < MAX_ATTEMPTS; i++) {
368 try {
369 Thread.sleep(sleepTime);
370 } catch(InterruptedException e) {
371 return result;
372 }
373 sleepTime *= 2;
374 result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength);
375 if (result != null) {
376 return result;
377 }
378 }
379 }
380 return result;
381 }
382
383 /**
384 * Java wrapper for Windows registry API RegEnumValue()
385 */
386 private static native byte[] WindowsRegEnumValue(long hKey, int valueIndex,
387 int maxValueNameLength);
388 /**
389 * Retries RegEnumValueEx() MAX_ATTEMPTS times before giving up.
390 */
391 private static byte[] WindowsRegEnumValue1(long hKey, int valueIndex,
392 int maxValueNameLength) {
393 byte[] result = WindowsRegEnumValue(hKey, valueIndex,
394 maxValueNameLength);
395 if (result != null) {
396 return result;
397 } else {
398 long sleepTime = INIT_SLEEP_TIME;
399 for (int i = 0; i < MAX_ATTEMPTS; i++) {
400 try {
401 Thread.sleep(sleepTime);
402 } catch(InterruptedException e) {
403 return result;
404 }
405 sleepTime *= 2;
406 result = WindowsRegEnumValue(hKey, valueIndex,
407 maxValueNameLength);
408 if (result != null) {
409 return result;
410 }
411 }
412 }
413 return result;
414 }
415
416 /**
417 * Constructs a {@code WindowsPreferences} node, creating underlying
418 * Windows registry node and all its Windows parents, if they are not yet
419 * created.
420 * Logs a warning message, if Windows Registry is unavailable.
421 */
422 private WindowsPreferences(WindowsPreferences parent, String name) {
423 super(parent, name);
424 long parentNativeHandle = parent.openKey(KEY_CREATE_SUB_KEY, KEY_READ);
425 if (parentNativeHandle == NULL_NATIVE_HANDLE) {
426 // if here, openKey failed and logged
427 isBackingStoreAvailable = false;
428 return;
429 }
430 long[] result =
431 WindowsRegCreateKeyEx1(parentNativeHandle, toWindowsName(name));
432 if (result[ERROR_CODE] != ERROR_SUCCESS) {
433 logger().warning("Could not create windows registry node " +
434 byteArrayToString(windowsAbsolutePath()) +
435 " at root 0x" + Long.toHexString(rootNativeHandle()) +
436 ". Windows RegCreateKeyEx(...) returned error code " +
437 result[ERROR_CODE] + ".");
438 isBackingStoreAvailable = false;
439 return;
440 }
441 newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
442 closeKey(parentNativeHandle);
443 closeKey(result[NATIVE_HANDLE]);
444 }
445
446 /**
447 * Constructs a root node creating the underlying
448 * Windows registry node and all of its parents, if they have not yet been
449 * created.
450 * Logs a warning message, if Windows Registry is unavailable.
451 * @param rootNativeHandle Native handle to one of Windows top level keys.
452 * @param rootDirectory Path to root directory, as a byte-encoded string.
453 */
454 private WindowsPreferences(long rootNativeHandle, byte[] rootDirectory) {
455 super(null, "");
456 long[] result =
457 WindowsRegCreateKeyEx1(rootNativeHandle, rootDirectory);
458 if (result[ERROR_CODE] != ERROR_SUCCESS) {
459 logger().warning("Could not open/create prefs root node " +
460 byteArrayToString(windowsAbsolutePath()) +
461 " at root 0x" + Long.toHexString(rootNativeHandle()) +
462 ". Windows RegCreateKeyEx(...) returned error code " +
463 result[ERROR_CODE] + ".");
464 isBackingStoreAvailable = false;
465 return;
466 }
467 // Check if a new node
468 newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
469 closeKey(result[NATIVE_HANDLE]);
470 }
471
472 /**
473 * Returns Windows absolute path of the current node as a byte array.
474 * Java "/" separator is transformed into Windows "\".
475 * @see Preferences#absolutePath()
476 */
477 private byte[] windowsAbsolutePath() {
478 ByteArrayOutputStream bstream = new ByteArrayOutputStream();
479 bstream.write(WINDOWS_ROOT_PATH, 0, WINDOWS_ROOT_PATH.length-1);
480 StringTokenizer tokenizer = new StringTokenizer(absolutePath(), "/");
481 while (tokenizer.hasMoreTokens()) {
482 bstream.write((byte)'\\');
483 String nextName = tokenizer.nextToken();
484 byte[] windowsNextName = toWindowsName(nextName);
485 bstream.write(windowsNextName, 0, windowsNextName.length-1);
486 }
487 bstream.write(0);
488 return bstream.toByteArray();
489 }
490
491 /**
492 * Opens current node's underlying Windows registry key using a
493 * given security mask.
494 * @param securityMask Windows security mask.
495 * @return Windows registry key's handle.
496 * @see #openKey(byte[], int)
497 * @see #openKey(int, byte[], int)
498 * @see #closeKey(int)
499 */
500 private long openKey(int securityMask) {
501 return openKey(securityMask, securityMask);
502 }
503
504 /**
505 * Opens current node's underlying Windows registry key using a
506 * given security mask.
507 * @param mask1 Preferred Windows security mask.
508 * @param mask2 Alternate Windows security mask.
509 * @return Windows registry key's handle.
510 * @see #openKey(byte[], int)
511 * @see #openKey(int, byte[], int)
512 * @see #closeKey(int)
513 */
514 private long openKey(int mask1, int mask2) {
515 return openKey(windowsAbsolutePath(), mask1, mask2);
516 }
517
518 /**
519 * Opens Windows registry key at a given absolute path using a given
520 * security mask.
521 * @param windowsAbsolutePath Windows absolute path of the
522 * key as a byte-encoded string.
523 * @param mask1 Preferred Windows security mask.
524 * @param mask2 Alternate Windows security mask.
525 * @return Windows registry key's handle.
526 * @see #openKey(int)
527 * @see #openKey(int, byte[],int)
528 * @see #closeKey(int)
529 */
530 private long openKey(byte[] windowsAbsolutePath, int mask1, int mask2) {
531 /* Check if key's path is short enough be opened at once
532 otherwise use a path-splitting procedure */
533 if (windowsAbsolutePath.length <= MAX_WINDOWS_PATH_LENGTH + 1) {
534 long[] result = WindowsRegOpenKey1(rootNativeHandle(),
535 windowsAbsolutePath, mask1);
536 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)
537 result = WindowsRegOpenKey1(rootNativeHandle(),
538 windowsAbsolutePath, mask2);
539
540 if (result[ERROR_CODE] != ERROR_SUCCESS) {
541 logger().warning("Could not open windows registry node " +
542 byteArrayToString(windowsAbsolutePath()) +
543 " at root 0x" +
544 Long.toHexString(rootNativeHandle()) +
545 ". Windows RegOpenKey(...) returned error code " +
546 result[ERROR_CODE] + ".");
547 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;
548 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED) {
549 throw new SecurityException(
550 "Could not open windows registry node " +
551 byteArrayToString(windowsAbsolutePath()) +
552 " at root 0x" +
553 Long.toHexString(rootNativeHandle()) +
554 ": Access denied");
555 }
556 }
557 return result[NATIVE_HANDLE];
558 } else {
559 return openKey(rootNativeHandle(), windowsAbsolutePath, mask1, mask2);
560 }
561 }
562
563 /**
564 * Opens Windows registry key at a given relative path
565 * with respect to a given Windows registry key.
566 * @param windowsAbsolutePath Windows relative path of the
567 * key as a byte-encoded string.
568 * @param nativeHandle handle to the base Windows key.
569 * @param mask1 Preferred Windows security mask.
570 * @param mask2 Alternate Windows security mask.
571 * @return Windows registry key's handle.
572 * @see #openKey(int)
573 * @see #openKey(byte[],int)
574 * @see #closeKey(int)
575 */
576 private long openKey(long nativeHandle, byte[] windowsRelativePath,
577 int mask1, int mask2) {
578 /* If the path is short enough open at once. Otherwise split the path */
579 if (windowsRelativePath.length <= MAX_WINDOWS_PATH_LENGTH + 1 ) {
580 long[] result = WindowsRegOpenKey1(nativeHandle,
581 windowsRelativePath, mask1);
582 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)
583 result = WindowsRegOpenKey1(nativeHandle,
584 windowsRelativePath, mask2);
585
586 if (result[ERROR_CODE] != ERROR_SUCCESS) {
587 logger().warning("Could not open windows registry node " +
588 byteArrayToString(windowsAbsolutePath()) +
589 " at root 0x" + Long.toHexString(nativeHandle) +
590 ". Windows RegOpenKey(...) returned error code " +
591 result[ERROR_CODE] + ".");
592 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;
593 }
594 return result[NATIVE_HANDLE];
595 } else {
596 int separatorPosition = -1;
597 // Be greedy - open the longest possible path
598 for (int i = MAX_WINDOWS_PATH_LENGTH; i > 0; i--) {
599 if (windowsRelativePath[i] == ((byte)'\\')) {
600 separatorPosition = i;
601 break;
602 }
603 }
604 // Split the path and do the recursion
605 byte[] nextRelativeRoot = new byte[separatorPosition+1];
606 System.arraycopy(windowsRelativePath, 0, nextRelativeRoot,0,
607 separatorPosition);
608 nextRelativeRoot[separatorPosition] = 0;
609 byte[] nextRelativePath = new byte[windowsRelativePath.length -
610 separatorPosition - 1];
611 System.arraycopy(windowsRelativePath, separatorPosition+1,
612 nextRelativePath, 0, nextRelativePath.length);
613 long nextNativeHandle = openKey(nativeHandle, nextRelativeRoot,
614 mask1, mask2);
615 if (nextNativeHandle == NULL_NATIVE_HANDLE) {
616 return NULL_NATIVE_HANDLE;
617 }
618 long result = openKey(nextNativeHandle, nextRelativePath,
619 mask1,mask2);
620 closeKey(nextNativeHandle);
621 return result;
622 }
623 }
624
625 /**
626 * Closes Windows registry key.
627 * Logs a warning if Windows registry is unavailable.
628 * @param key's Windows registry handle.
629 * @see #openKey(int)
630 * @see #openKey(byte[],int)
631 * @see #openKey(int, byte[],int)
632 */
633 private void closeKey(long nativeHandle) {
634 int result = WindowsRegCloseKey(nativeHandle);
635 if (result != ERROR_SUCCESS) {
636 logger().warning("Could not close windows registry node " +
637 byteArrayToString(windowsAbsolutePath()) +
638 " at root 0x" +
639 Long.toHexString(rootNativeHandle()) +
640 ". Windows RegCloseKey(...) returned error code " +
641 result + ".");
642 }
643 }
644
645 /**
646 * Implements {@code AbstractPreferences} {@code putSpi()} method.
647 * Puts name-value pair into the underlying Windows registry node.
648 * Logs a warning, if Windows registry is unavailable.
649 * @see #getSpi(String)
650 */
651 protected void putSpi(String javaName, String value) {
652 long nativeHandle = openKey(KEY_SET_VALUE);
653 if (nativeHandle == NULL_NATIVE_HANDLE) {
654 isBackingStoreAvailable = false;
655 return;
656 }
657 int result = WindowsRegSetValueEx1(nativeHandle,
658 toWindowsName(javaName), toWindowsValueString(value));
659 if (result != ERROR_SUCCESS) {
660 logger().warning("Could not assign value to key " +
661 byteArrayToString(toWindowsName(javaName)) +
662 " at Windows registry node " +
663 byteArrayToString(windowsAbsolutePath()) +
664 " at root 0x" +
665 Long.toHexString(rootNativeHandle()) +
666 ". Windows RegSetValueEx(...) returned error code " +
667 result + ".");
668 isBackingStoreAvailable = false;
669 }
670 closeKey(nativeHandle);
671 }
672
673 /**
674 * Implements {@code AbstractPreferences} {@code getSpi()} method.
675 * Gets a string value from the underlying Windows registry node.
676 * Logs a warning, if Windows registry is unavailable.
677 * @see #putSpi(String, String)
678 */
679 protected String getSpi(String javaName) {
680 long nativeHandle = openKey(KEY_QUERY_VALUE);
681 if (nativeHandle == NULL_NATIVE_HANDLE) {
682 return null;
683 }
684 Object resultObject = WindowsRegQueryValueEx(nativeHandle,
685 toWindowsName(javaName));
686 if (resultObject == null) {
687 closeKey(nativeHandle);
688 return null;
689 }
690 closeKey(nativeHandle);
691 return toJavaValueString((byte[]) resultObject);
692 }
693
694 /**
695 * Implements {@code AbstractPreferences} {@code removeSpi()} method.
696 * Deletes a string name-value pair from the underlying Windows registry
697 * node, if this value still exists.
698 * Logs a warning, if Windows registry is unavailable or key has already
699 * been deleted.
700 */
701 protected void removeSpi(String key) {
702 long nativeHandle = openKey(KEY_SET_VALUE);
703 if (nativeHandle == NULL_NATIVE_HANDLE) {
704 return;
705 }
706 int result =
707 WindowsRegDeleteValue(nativeHandle, toWindowsName(key));
708 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
709 logger().warning("Could not delete windows registry value " +
710 byteArrayToString(windowsAbsolutePath()) + "\\" +
711 toWindowsName(key) + " at root 0x" +
712 Long.toHexString(rootNativeHandle()) +
713 ". Windows RegDeleteValue(...) returned error code " +
714 result + ".");
715 isBackingStoreAvailable = false;
716 }
717 closeKey(nativeHandle);
718 }
719
720 /**
721 * Implements {@code AbstractPreferences} {@code keysSpi()} method.
722 * Gets value names from the underlying Windows registry node.
723 * Throws a BackingStoreException and logs a warning, if
724 * Windows registry is unavailable.
725 */
726 protected String[] keysSpi() throws BackingStoreException{
727 // Find out the number of values
728 long nativeHandle = openKey(KEY_QUERY_VALUE);
729 if (nativeHandle == NULL_NATIVE_HANDLE) {
730 throw new BackingStoreException(
731 "Could not open windows registry node " +
732 byteArrayToString(windowsAbsolutePath()) +
733 " at root 0x" +
734 Long.toHexString(rootNativeHandle()) + ".");
735 }
736 long[] result = WindowsRegQueryInfoKey1(nativeHandle);
737 if (result[ERROR_CODE] != ERROR_SUCCESS) {
738 String info = "Could not query windows registry node " +
739 byteArrayToString(windowsAbsolutePath()) +
740 " at root 0x" +
741 Long.toHexString(rootNativeHandle()) +
742 ". Windows RegQueryInfoKeyEx(...) returned error code " +
743 result[ERROR_CODE] + ".";
744 logger().warning(info);
745 throw new BackingStoreException(info);
746 }
747 int maxValueNameLength = (int)result[MAX_VALUE_NAME_LENGTH];
748 int valuesNumber = (int)result[VALUES_NUMBER];
749 if (valuesNumber == 0) {
750 closeKey(nativeHandle);
751 return new String[0];
752 }
753 // Get the values
754 String[] valueNames = new String[valuesNumber];
755 for (int i = 0; i < valuesNumber; i++) {
756 byte[] windowsName = WindowsRegEnumValue1(nativeHandle, i,
757 maxValueNameLength+1);
758 if (windowsName == null) {
759 String info =
760 "Could not enumerate value #" + i + " of windows node " +
761 byteArrayToString(windowsAbsolutePath()) + " at root 0x" +
762 Long.toHexString(rootNativeHandle()) + ".";
763 logger().warning(info);
764 throw new BackingStoreException(info);
765 }
766 valueNames[i] = toJavaName(windowsName);
767 }
768 closeKey(nativeHandle);
769 return valueNames;
770 }
771
772 /**
773 * Implements {@code AbstractPreferences} {@code childrenNamesSpi()} method.
774 * Calls Windows registry to retrive children of this node.
775 * Throws a BackingStoreException and logs a warning message,
776 * if Windows registry is not available.
777 */
778 protected String[] childrenNamesSpi() throws BackingStoreException {
779 // Open key
780 long nativeHandle = openKey(KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE);
781 if (nativeHandle == NULL_NATIVE_HANDLE) {
782 throw new BackingStoreException(
783 "Could not open windows registry node " +
784 byteArrayToString(windowsAbsolutePath()) +
785 " at root 0x" +
786 Long.toHexString(rootNativeHandle()) + ".");
787 }
788 // Get number of children
789 long[] result = WindowsRegQueryInfoKey1(nativeHandle);
790 if (result[ERROR_CODE] != ERROR_SUCCESS) {
791 String info = "Could not query windows registry node " +
792 byteArrayToString(windowsAbsolutePath()) +
793 " at root 0x" + Long.toHexString(rootNativeHandle()) +
794 ". Windows RegQueryInfoKeyEx(...) returned error code " +
795 result[ERROR_CODE] + ".";
796 logger().warning(info);
797 throw new BackingStoreException(info);
798 }
799 int maxKeyLength = (int)result[MAX_KEY_LENGTH];
800 int subKeysNumber = (int)result[SUBKEYS_NUMBER];
801 if (subKeysNumber == 0) {
802 closeKey(nativeHandle);
803 return new String[0];
804 }
805 String[] subkeys = new String[subKeysNumber];
806 String[] children = new String[subKeysNumber];
807 // Get children
808 for (int i = 0; i < subKeysNumber; i++) {
809 byte[] windowsName = WindowsRegEnumKeyEx1(nativeHandle, i,
810 maxKeyLength+1);
811 if (windowsName == null) {
812 String info =
813 "Could not enumerate key #" + i + " of windows node " +
814 byteArrayToString(windowsAbsolutePath()) + " at root 0x" +
815 Long.toHexString(rootNativeHandle()) + ". ";
816 logger().warning(info);
817 throw new BackingStoreException(info);
818 }
819 String javaName = toJavaName(windowsName);
820 children[i] = javaName;
821 }
822 closeKey(nativeHandle);
823 return children;
824 }
825
826 /**
827 * Implements {@code Preferences} {@code flush()} method.
828 * Flushes Windows registry changes to disk.
829 * Throws a BackingStoreException and logs a warning message if Windows
830 * registry is not available.
831 */
832 public void flush() throws BackingStoreException{
833
834 if (isRemoved()) {
835 parent.flush();
836 return;
837 }
838 if (!isBackingStoreAvailable) {
839 throw new BackingStoreException(
840 "flush(): Backing store not available.");
841 }
842 long nativeHandle = openKey(KEY_READ);
843 if (nativeHandle == NULL_NATIVE_HANDLE) {
844 throw new BackingStoreException(
845 "Could not open windows registry node " +
846 byteArrayToString(windowsAbsolutePath()) +
847 " at root 0x" +
848 Long.toHexString(rootNativeHandle()) + ".");
849 }
850 int result = WindowsRegFlushKey1(nativeHandle);
851 if (result != ERROR_SUCCESS) {
852 String info = "Could not flush windows registry node " +
853 byteArrayToString(windowsAbsolutePath()) +
854 " at root 0x" +
855 Long.toHexString(rootNativeHandle()) +
856 ". Windows RegFlushKey(...) returned error code " +
857 result + ".";
858 logger().warning(info);
859 throw new BackingStoreException(info);
860 }
861 closeKey(nativeHandle);
862 }
863
864
865 /**
866 * Implements {@code Preferences} {@code sync()} method.
867 * Flushes Windows registry changes to disk. Equivalent to flush().
868 * @see flush()
869 */
870 public void sync() throws BackingStoreException{
871 if (isRemoved())
872 throw new IllegalStateException("Node has been removed");
873 flush();
874 }
875
876 /**
877 * Implements {@code AbstractPreferences} {@code childSpi()} method.
878 * Constructs a child node with a
879 * given name and creates its underlying Windows registry node,
880 * if it does not exist.
881 * Logs a warning message, if Windows Registry is unavailable.
882 */
883 protected AbstractPreferences childSpi(String name) {
884 return new WindowsPreferences(this, name);
885 }
886
887 /**
888 * Implements {@code AbstractPreferences} {@code removeNodeSpi()} method.
889 * Deletes underlying Windows registry node.
890 * Throws a BackingStoreException and logs a warning, if Windows registry
891 * is not available.
892 */
893 public void removeNodeSpi() throws BackingStoreException {
894 long parentNativeHandle =
895 ((WindowsPreferences)parent()).openKey(DELETE);
896 if (parentNativeHandle == NULL_NATIVE_HANDLE) {
897 throw new BackingStoreException(
898 "Could not open parent windows registry node of " +
899 byteArrayToString(windowsAbsolutePath()) +
900 " at root 0x" +
901 Long.toHexString(rootNativeHandle()) + ".");
902 }
903 int result =
904 WindowsRegDeleteKey(parentNativeHandle, toWindowsName(name()));
905 if (result != ERROR_SUCCESS) {
906 String info = "Could not delete windows registry node " +
907 byteArrayToString(windowsAbsolutePath()) +
908 " at root 0x" + Long.toHexString(rootNativeHandle()) +
909 ". Windows RegDeleteKeyEx(...) returned error code " +
910 result + ".";
911 logger().warning(info);
912 throw new BackingStoreException(info);
913 }
914 closeKey(parentNativeHandle);
915 }
916
917 /**
918 * Converts value's or node's name from its byte array representation to
919 * java string. Two encodings, simple and altBase64 are used. See
920 * {@link #toWindowsName(String) toWindowsName()} for a detailed
921 * description of encoding conventions.
922 * @param windowsNameArray Null-terminated byte array.
923 */
924 private static String toJavaName(byte[] windowsNameArray) {
925 String windowsName = byteArrayToString(windowsNameArray);
926 // check if Alt64
927 if ((windowsName.length() > 1) &&
928 (windowsName.substring(0, 2).equals("/!"))) {
1072 javaName.append(ch);
1073 }
1074 return javaName.toString();
1075 }
1076
1077 /**
1078 * Converts value string to it Windows representation.
1079 * as a byte-encoded string.
1080 * Encoding algorithm adds "/" character to capital letters, i.e.
1081 * "A" is encoded as "/A". Character '\' is encoded as '//',
1082 * '/' is encoded as '\'.
1083 * Then convert java string to a byte array of ASCII characters.
1084 */
1085 private static byte[] toWindowsValueString(String javaName) {
1086 StringBuilder windowsName = new StringBuilder();
1087 for (int i = 0; i < javaName.length(); i++) {
1088 char ch = javaName.charAt(i);
1089 if ((ch < 0x0020) || (ch > 0x007f)){
1090 // write \udddd
1091 windowsName.append("/u");
1092 String hex = Long.toHexString(javaName.charAt(i));
1093 StringBuilder hex4 = new StringBuilder(hex);
1094 hex4.reverse();
1095 int len = 4 - hex4.length();
1096 for (int j = 0; j < len; j++){
1097 hex4.append('0');
1098 }
1099 for (int j = 0; j < 4; j++){
1100 windowsName.append(hex4.charAt(3 - j));
1101 }
1102 } else if (ch == '\\') {
1103 windowsName.append("//");
1104 } else if (ch == '/') {
1105 windowsName.append('\\');
1106 } else if ((ch >= 'A') && (ch <='Z')) {
1107 windowsName.append('/').append(ch);
1108 } else {
1109 windowsName.append(ch);
1110 }
1111 }
1112 return stringToByteArray(windowsName.toString());
1113 }
1114
1115 /**
1116 * Returns native handle for the top Windows node for this node.
1117 */
1118 private long rootNativeHandle() {
1119 return (isUserNode()
1120 ? USER_ROOT_NATIVE_HANDLE
1121 : SYSTEM_ROOT_NATIVE_HANDLE);
1122 }
1123
1124 /**
1125 * Returns this java string as a null-terminated byte array
1126 */
1127 private static byte[] stringToByteArray(String str) {
1128 byte[] result = new byte[str.length()+1];
1129 for (int i = 0; i < str.length(); i++) {
1130 result[i] = (byte) str.charAt(i);
1131 }
1132 result[str.length()] = 0;
1133 return result;
1134 }
1135
1136 /**
1137 * Converts a null-terminated byte array to java string
1138 */
|