Path: blob/master/src/java.prefs/windows/classes/java/util/prefs/WindowsPreferences.java
41159 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.util.prefs;2627import java.util.StringTokenizer;28import java.io.ByteArrayOutputStream;29import java.security.AccessController;30import java.security.PrivilegedAction;3132import sun.util.logging.PlatformLogger;3334/**35* Windows registry based implementation of {@code Preferences}.36* {@code Preferences}' {@code systemRoot} and {@code userRoot} are stored in37* {@code HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs} and38* {@code HKEY_CURRENT_USER\Software\JavaSoft\Prefs} correspondingly.39*40* @author Konstantin Kladko41* @see Preferences42* @see PreferencesFactory43* @since 1.444*/4546@SuppressWarnings("removal")47class WindowsPreferences extends AbstractPreferences {4849static {50PrivilegedAction<Void> load = () -> {51System.loadLibrary("prefs");52return null;53};54AccessController.doPrivileged(load);55}5657/**58* Logger for error messages59*/60private static PlatformLogger logger;6162/**63* Windows registry path to {@code Preferences}'s root nodes.64*/65private static final byte[] WINDOWS_ROOT_PATH =66stringToByteArray("Software\\JavaSoft\\Prefs");6768/**69* Windows handles to {@code HKEY_CURRENT_USER} and70* {@code HKEY_LOCAL_MACHINE} hives.71*/72private static final int HKEY_CURRENT_USER = 0x80000001;73private static final int HKEY_LOCAL_MACHINE = 0x80000002;7475/**76* Mount point for {@code Preferences}' user root.77*/78private static final int USER_ROOT_NATIVE_HANDLE = HKEY_CURRENT_USER;7980/**81* Mount point for {@code Preferences}' system root.82*/83private static final int SYSTEM_ROOT_NATIVE_HANDLE = HKEY_LOCAL_MACHINE;8485/**86* Maximum byte-encoded path length for Windows native functions,87* ending {@code null} character not included.88*/89private static final int MAX_WINDOWS_PATH_LENGTH = 256;9091/**92* User root node.93*/94private static volatile Preferences userRoot;9596static Preferences getUserRoot() {97Preferences root = userRoot;98if (root == null) {99synchronized (WindowsPreferences.class) {100root = userRoot;101if (root == null) {102root = new WindowsPreferences(USER_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH);103userRoot = root;104}105}106}107return root;108}109110/**111* System root node.112*/113private static volatile Preferences systemRoot;114115static Preferences getSystemRoot() {116Preferences root = systemRoot;117if (root == null) {118synchronized (WindowsPreferences.class) {119root = systemRoot;120if (root == null) {121root = new WindowsPreferences(SYSTEM_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH);122systemRoot = root;123}124}125}126return root;127}128129/* Windows error codes. */130private static final int ERROR_SUCCESS = 0;131private static final int ERROR_FILE_NOT_FOUND = 2;132private static final int ERROR_ACCESS_DENIED = 5;133134/* Constants used to interpret returns of native functions */135private static final int NATIVE_HANDLE = 0;136private static final int ERROR_CODE = 1;137private static final int SUBKEYS_NUMBER = 0;138private static final int VALUES_NUMBER = 2;139private static final int MAX_KEY_LENGTH = 3;140private static final int MAX_VALUE_NAME_LENGTH = 4;141private static final int DISPOSITION = 2;142private static final int REG_CREATED_NEW_KEY = 1;143private static final int REG_OPENED_EXISTING_KEY = 2;144private static final int NULL_NATIVE_HANDLE = 0;145146/* Windows security masks */147private static final int DELETE = 0x10000;148private static final int KEY_QUERY_VALUE = 1;149private static final int KEY_SET_VALUE = 2;150private static final int KEY_CREATE_SUB_KEY = 4;151private static final int KEY_ENUMERATE_SUB_KEYS = 8;152private static final int KEY_READ = 0x20019;153private static final int KEY_WRITE = 0x20006;154private static final int KEY_ALL_ACCESS = 0xf003f;155156/**157* Initial time between registry access attempts, in ms. The time is doubled158* after each failing attempt (except the first).159*/160private static int INIT_SLEEP_TIME = 50;161162/**163* Maximum number of registry access attempts.164*/165private static int MAX_ATTEMPTS = 5;166167/**168* BackingStore availability flag.169*/170private boolean isBackingStoreAvailable = true;171172/**173* Java wrapper for Windows registry API RegOpenKey()174*/175private static native long[] WindowsRegOpenKey(long hKey, byte[] subKey,176int securityMask);177/**178* Retries RegOpenKey() MAX_ATTEMPTS times before giving up.179*/180private static long[] WindowsRegOpenKey1(long hKey, byte[] subKey,181int securityMask) {182long[] result = WindowsRegOpenKey(hKey, subKey, securityMask);183if (result[ERROR_CODE] == ERROR_SUCCESS) {184return result;185} else if (result[ERROR_CODE] == ERROR_FILE_NOT_FOUND) {186logger().warning("Trying to recreate Windows registry node " +187byteArrayToString(subKey) + " at root 0x" +188Long.toHexString(hKey) + ".");189// Try recreation190long handle = WindowsRegCreateKeyEx(hKey, subKey)[NATIVE_HANDLE];191WindowsRegCloseKey(handle);192return WindowsRegOpenKey(hKey, subKey, securityMask);193} else if (result[ERROR_CODE] != ERROR_ACCESS_DENIED) {194long sleepTime = INIT_SLEEP_TIME;195for (int i = 0; i < MAX_ATTEMPTS; i++) {196try {197Thread.sleep(sleepTime);198} catch(InterruptedException e) {199return result;200}201sleepTime *= 2;202result = WindowsRegOpenKey(hKey, subKey, securityMask);203if (result[ERROR_CODE] == ERROR_SUCCESS) {204return result;205}206}207}208return result;209}210211/**212* Java wrapper for Windows registry API RegCloseKey()213*/214private static native int WindowsRegCloseKey(long hKey);215216/**217* Java wrapper for Windows registry API RegCreateKeyEx()218*/219private static native long[] WindowsRegCreateKeyEx(long hKey, byte[] subKey);220221/**222* Retries RegCreateKeyEx() MAX_ATTEMPTS times before giving up.223*/224private static long[] WindowsRegCreateKeyEx1(long hKey, byte[] subKey) {225long[] result = WindowsRegCreateKeyEx(hKey, subKey);226if (result[ERROR_CODE] == ERROR_SUCCESS) {227return result;228} else {229long sleepTime = INIT_SLEEP_TIME;230for (int i = 0; i < MAX_ATTEMPTS; i++) {231try {232Thread.sleep(sleepTime);233} catch(InterruptedException e) {234return result;235}236sleepTime *= 2;237result = WindowsRegCreateKeyEx(hKey, subKey);238if (result[ERROR_CODE] == ERROR_SUCCESS) {239return result;240}241}242}243return result;244}245/**246* Java wrapper for Windows registry API RegDeleteKey()247*/248private static native int WindowsRegDeleteKey(long hKey, byte[] subKey);249250/**251* Java wrapper for Windows registry API RegFlushKey()252*/253private static native int WindowsRegFlushKey(long hKey);254255/**256* Retries RegFlushKey() MAX_ATTEMPTS times before giving up.257*/258private static int WindowsRegFlushKey1(long hKey) {259int result = WindowsRegFlushKey(hKey);260if (result == ERROR_SUCCESS) {261return result;262} else {263long sleepTime = INIT_SLEEP_TIME;264for (int i = 0; i < MAX_ATTEMPTS; i++) {265try {266Thread.sleep(sleepTime);267} catch(InterruptedException e) {268return result;269}270sleepTime *= 2;271result = WindowsRegFlushKey(hKey);272if (result == ERROR_SUCCESS) {273return result;274}275}276}277return result;278}279280/**281* Java wrapper for Windows registry API RegQueryValueEx()282*/283private static native byte[] WindowsRegQueryValueEx(long hKey,284byte[] valueName);285/**286* Java wrapper for Windows registry API RegSetValueEx()287*/288private static native int WindowsRegSetValueEx(long hKey, byte[] valueName,289byte[] value);290/**291* Retries RegSetValueEx() MAX_ATTEMPTS times before giving up.292*/293private static int WindowsRegSetValueEx1(long hKey, byte[] valueName,294byte[] value) {295int result = WindowsRegSetValueEx(hKey, valueName, value);296if (result == ERROR_SUCCESS) {297return result;298} else {299long sleepTime = INIT_SLEEP_TIME;300for (int i = 0; i < MAX_ATTEMPTS; i++) {301try {302Thread.sleep(sleepTime);303} catch(InterruptedException e) {304return result;305}306sleepTime *= 2;307result = WindowsRegSetValueEx(hKey, valueName, value);308if (result == ERROR_SUCCESS) {309return result;310}311}312}313return result;314}315316/**317* Java wrapper for Windows registry API RegDeleteValue()318*/319private static native int WindowsRegDeleteValue(long hKey, byte[] valueName);320321/**322* Java wrapper for Windows registry API RegQueryInfoKey()323*/324private static native long[] WindowsRegQueryInfoKey(long hKey);325326/**327* Retries RegQueryInfoKey() MAX_ATTEMPTS times before giving up.328*/329private static long[] WindowsRegQueryInfoKey1(long hKey) {330long[] result = WindowsRegQueryInfoKey(hKey);331if (result[ERROR_CODE] == ERROR_SUCCESS) {332return result;333} else {334long sleepTime = INIT_SLEEP_TIME;335for (int i = 0; i < MAX_ATTEMPTS; i++) {336try {337Thread.sleep(sleepTime);338} catch(InterruptedException e) {339return result;340}341sleepTime *= 2;342result = WindowsRegQueryInfoKey(hKey);343if (result[ERROR_CODE] == ERROR_SUCCESS) {344return result;345}346}347}348return result;349}350351/**352* Java wrapper for Windows registry API RegEnumKeyEx()353*/354private static native byte[] WindowsRegEnumKeyEx(long hKey, int subKeyIndex,355int maxKeyLength);356357/**358* Retries RegEnumKeyEx() MAX_ATTEMPTS times before giving up.359*/360private static byte[] WindowsRegEnumKeyEx1(long hKey, int subKeyIndex,361int maxKeyLength) {362byte[] result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength);363if (result != null) {364return result;365} else {366long sleepTime = INIT_SLEEP_TIME;367for (int i = 0; i < MAX_ATTEMPTS; i++) {368try {369Thread.sleep(sleepTime);370} catch(InterruptedException e) {371return result;372}373sleepTime *= 2;374result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength);375if (result != null) {376return result;377}378}379}380return result;381}382383/**384* Java wrapper for Windows registry API RegEnumValue()385*/386private static native byte[] WindowsRegEnumValue(long hKey, int valueIndex,387int maxValueNameLength);388/**389* Retries RegEnumValueEx() MAX_ATTEMPTS times before giving up.390*/391private static byte[] WindowsRegEnumValue1(long hKey, int valueIndex,392int maxValueNameLength) {393byte[] result = WindowsRegEnumValue(hKey, valueIndex,394maxValueNameLength);395if (result != null) {396return result;397} else {398long sleepTime = INIT_SLEEP_TIME;399for (int i = 0; i < MAX_ATTEMPTS; i++) {400try {401Thread.sleep(sleepTime);402} catch(InterruptedException e) {403return result;404}405sleepTime *= 2;406result = WindowsRegEnumValue(hKey, valueIndex,407maxValueNameLength);408if (result != null) {409return result;410}411}412}413return result;414}415416/**417* Constructs a {@code WindowsPreferences} node, creating underlying418* Windows registry node and all its Windows parents, if they are not yet419* created.420* Logs a warning message, if Windows Registry is unavailable.421*/422private WindowsPreferences(WindowsPreferences parent, String name) {423super(parent, name);424long parentNativeHandle = parent.openKey(KEY_CREATE_SUB_KEY, KEY_READ);425if (parentNativeHandle == NULL_NATIVE_HANDLE) {426// if here, openKey failed and logged427isBackingStoreAvailable = false;428return;429}430long[] result =431WindowsRegCreateKeyEx1(parentNativeHandle, toWindowsName(name));432if (result[ERROR_CODE] != ERROR_SUCCESS) {433logger().warning("Could not create windows registry node " +434byteArrayToString(windowsAbsolutePath()) +435" at root 0x" + Long.toHexString(parentNativeHandle) +436". Windows RegCreateKeyEx(...) returned error code " +437result[ERROR_CODE] + ".");438isBackingStoreAvailable = false;439return;440}441newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);442closeKey(parentNativeHandle);443closeKey(result[NATIVE_HANDLE]);444}445446/**447* Constructs a root node creating the underlying448* Windows registry node and all of its parents, if they have not yet been449* 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*/454private WindowsPreferences(long rootNativeHandle, byte[] rootDirectory) {455super(null, "");456long[] result =457WindowsRegCreateKeyEx1(rootNativeHandle, rootDirectory);458if (result[ERROR_CODE] != ERROR_SUCCESS) {459logger().warning("Could not open/create prefs root node " +460byteArrayToString(windowsAbsolutePath()) +461" at root 0x" + Long.toHexString(rootNativeHandle) +462". Windows RegCreateKeyEx(...) returned error code " +463result[ERROR_CODE] + ".");464isBackingStoreAvailable = false;465return;466}467// Check if a new node468newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);469closeKey(result[NATIVE_HANDLE]);470}471472/**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*/477private byte[] windowsAbsolutePath() {478ByteArrayOutputStream bstream = new ByteArrayOutputStream();479bstream.write(WINDOWS_ROOT_PATH, 0, WINDOWS_ROOT_PATH.length-1);480StringTokenizer tokenizer = new StringTokenizer(absolutePath(), "/");481while (tokenizer.hasMoreTokens()) {482bstream.write((byte)'\\');483String nextName = tokenizer.nextToken();484byte[] windowsNextName = toWindowsName(nextName);485bstream.write(windowsNextName, 0, windowsNextName.length-1);486}487bstream.write(0);488return bstream.toByteArray();489}490491/**492* Opens current node's underlying Windows registry key using a493* 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*/500private long openKey(int securityMask) {501return openKey(securityMask, securityMask);502}503504/**505* Opens current node's underlying Windows registry key using a506* 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*/514private long openKey(int mask1, int mask2) {515return openKey(windowsAbsolutePath(), mask1, mask2);516}517518/**519* Opens Windows registry key at a given absolute path using a given520* security mask.521* @param windowsAbsolutePath Windows absolute path of the522* 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*/530private long openKey(byte[] windowsAbsolutePath, int mask1, int mask2) {531/* Check if key's path is short enough be opened at once532otherwise use a path-splitting procedure */533if (windowsAbsolutePath.length <= MAX_WINDOWS_PATH_LENGTH + 1) {534long[] result = WindowsRegOpenKey1(rootNativeHandle(),535windowsAbsolutePath, mask1);536if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)537result = WindowsRegOpenKey1(rootNativeHandle(),538windowsAbsolutePath, mask2);539540if (result[ERROR_CODE] != ERROR_SUCCESS) {541logger().warning("Could not open windows registry node " +542byteArrayToString(windowsAbsolutePath()) +543" at root 0x" +544Long.toHexString(rootNativeHandle()) +545". Windows RegOpenKey(...) returned error code " +546result[ERROR_CODE] + ".");547result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;548if (result[ERROR_CODE] == ERROR_ACCESS_DENIED) {549throw new SecurityException(550"Could not open windows registry node " +551byteArrayToString(windowsAbsolutePath()) +552" at root 0x" +553Long.toHexString(rootNativeHandle()) +554": Access denied");555}556}557return result[NATIVE_HANDLE];558} else {559return openKey(rootNativeHandle(), windowsAbsolutePath, mask1, mask2);560}561}562563/**564* Opens Windows registry key at a given relative path565* with respect to a given Windows registry key.566* @param windowsAbsolutePath Windows relative path of the567* 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*/576private long openKey(long nativeHandle, byte[] windowsRelativePath,577int mask1, int mask2) {578/* If the path is short enough open at once. Otherwise split the path */579if (windowsRelativePath.length <= MAX_WINDOWS_PATH_LENGTH + 1 ) {580long[] result = WindowsRegOpenKey1(nativeHandle,581windowsRelativePath, mask1);582if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)583result = WindowsRegOpenKey1(nativeHandle,584windowsRelativePath, mask2);585586if (result[ERROR_CODE] != ERROR_SUCCESS) {587logger().warning("Could not open windows registry node " +588byteArrayToString(windowsAbsolutePath()) +589" at root 0x" + Long.toHexString(nativeHandle) +590". Windows RegOpenKey(...) returned error code " +591result[ERROR_CODE] + ".");592result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;593}594return result[NATIVE_HANDLE];595} else {596int separatorPosition = -1;597// Be greedy - open the longest possible path598for (int i = MAX_WINDOWS_PATH_LENGTH; i > 0; i--) {599if (windowsRelativePath[i] == ((byte)'\\')) {600separatorPosition = i;601break;602}603}604// Split the path and do the recursion605byte[] nextRelativeRoot = new byte[separatorPosition+1];606System.arraycopy(windowsRelativePath, 0, nextRelativeRoot,0,607separatorPosition);608nextRelativeRoot[separatorPosition] = 0;609byte[] nextRelativePath = new byte[windowsRelativePath.length -610separatorPosition - 1];611System.arraycopy(windowsRelativePath, separatorPosition+1,612nextRelativePath, 0, nextRelativePath.length);613long nextNativeHandle = openKey(nativeHandle, nextRelativeRoot,614mask1, mask2);615if (nextNativeHandle == NULL_NATIVE_HANDLE) {616return NULL_NATIVE_HANDLE;617}618long result = openKey(nextNativeHandle, nextRelativePath,619mask1,mask2);620closeKey(nextNativeHandle);621return result;622}623}624625/**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*/633private void closeKey(long nativeHandle) {634int result = WindowsRegCloseKey(nativeHandle);635if (result != ERROR_SUCCESS) {636logger().warning("Could not close windows registry node " +637byteArrayToString(windowsAbsolutePath()) +638" at root 0x" +639Long.toHexString(rootNativeHandle()) +640". Windows RegCloseKey(...) returned error code " +641result + ".");642}643}644645/**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*/651protected void putSpi(String javaName, String value) {652long nativeHandle = openKey(KEY_SET_VALUE);653if (nativeHandle == NULL_NATIVE_HANDLE) {654isBackingStoreAvailable = false;655return;656}657int result = WindowsRegSetValueEx1(nativeHandle,658toWindowsName(javaName), toWindowsValueString(value));659if (result != ERROR_SUCCESS) {660logger().warning("Could not assign value to key " +661byteArrayToString(toWindowsName(javaName)) +662" at Windows registry node " +663byteArrayToString(windowsAbsolutePath()) +664" at root 0x" +665Long.toHexString(rootNativeHandle()) +666". Windows RegSetValueEx(...) returned error code " +667result + ".");668isBackingStoreAvailable = false;669}670closeKey(nativeHandle);671}672673/**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*/679protected String getSpi(String javaName) {680long nativeHandle = openKey(KEY_QUERY_VALUE);681if (nativeHandle == NULL_NATIVE_HANDLE) {682return null;683}684Object resultObject = WindowsRegQueryValueEx(nativeHandle,685toWindowsName(javaName));686if (resultObject == null) {687closeKey(nativeHandle);688return null;689}690closeKey(nativeHandle);691return toJavaValueString((byte[]) resultObject);692}693694/**695* Implements {@code AbstractPreferences} {@code removeSpi()} method.696* Deletes a string name-value pair from the underlying Windows registry697* node, if this value still exists.698* Logs a warning, if Windows registry is unavailable or key has already699* been deleted.700*/701protected void removeSpi(String key) {702long nativeHandle = openKey(KEY_SET_VALUE);703if (nativeHandle == NULL_NATIVE_HANDLE) {704return;705}706int result =707WindowsRegDeleteValue(nativeHandle, toWindowsName(key));708if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {709logger().warning("Could not delete windows registry value " +710byteArrayToString(windowsAbsolutePath()) + "\\" +711toWindowsName(key) + " at root 0x" +712Long.toHexString(rootNativeHandle()) +713". Windows RegDeleteValue(...) returned error code " +714result + ".");715isBackingStoreAvailable = false;716}717closeKey(nativeHandle);718}719720/**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, if724* Windows registry is unavailable.725*/726protected String[] keysSpi() throws BackingStoreException{727// Find out the number of values728long nativeHandle = openKey(KEY_QUERY_VALUE);729if (nativeHandle == NULL_NATIVE_HANDLE) {730throw new BackingStoreException(731"Could not open windows registry node " +732byteArrayToString(windowsAbsolutePath()) +733" at root 0x" +734Long.toHexString(rootNativeHandle()) + ".");735}736long[] result = WindowsRegQueryInfoKey1(nativeHandle);737if (result[ERROR_CODE] != ERROR_SUCCESS) {738String info = "Could not query windows registry node " +739byteArrayToString(windowsAbsolutePath()) +740" at root 0x" +741Long.toHexString(rootNativeHandle()) +742". Windows RegQueryInfoKeyEx(...) returned error code " +743result[ERROR_CODE] + ".";744logger().warning(info);745throw new BackingStoreException(info);746}747int maxValueNameLength = (int)result[MAX_VALUE_NAME_LENGTH];748int valuesNumber = (int)result[VALUES_NUMBER];749if (valuesNumber == 0) {750closeKey(nativeHandle);751return new String[0];752}753// Get the values754String[] valueNames = new String[valuesNumber];755for (int i = 0; i < valuesNumber; i++) {756byte[] windowsName = WindowsRegEnumValue1(nativeHandle, i,757maxValueNameLength+1);758if (windowsName == null) {759String info =760"Could not enumerate value #" + i + " of windows node " +761byteArrayToString(windowsAbsolutePath()) + " at root 0x" +762Long.toHexString(rootNativeHandle()) + ".";763logger().warning(info);764throw new BackingStoreException(info);765}766valueNames[i] = toJavaName(windowsName);767}768closeKey(nativeHandle);769return valueNames;770}771772/**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*/778protected String[] childrenNamesSpi() throws BackingStoreException {779// Open key780long nativeHandle = openKey(KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE);781if (nativeHandle == NULL_NATIVE_HANDLE) {782throw new BackingStoreException(783"Could not open windows registry node " +784byteArrayToString(windowsAbsolutePath()) +785" at root 0x" +786Long.toHexString(rootNativeHandle()) + ".");787}788// Get number of children789long[] result = WindowsRegQueryInfoKey1(nativeHandle);790if (result[ERROR_CODE] != ERROR_SUCCESS) {791String info = "Could not query windows registry node " +792byteArrayToString(windowsAbsolutePath()) +793" at root 0x" + Long.toHexString(rootNativeHandle()) +794". Windows RegQueryInfoKeyEx(...) returned error code " +795result[ERROR_CODE] + ".";796logger().warning(info);797throw new BackingStoreException(info);798}799int maxKeyLength = (int)result[MAX_KEY_LENGTH];800int subKeysNumber = (int)result[SUBKEYS_NUMBER];801if (subKeysNumber == 0) {802closeKey(nativeHandle);803return new String[0];804}805String[] subkeys = new String[subKeysNumber];806String[] children = new String[subKeysNumber];807// Get children808for (int i = 0; i < subKeysNumber; i++) {809byte[] windowsName = WindowsRegEnumKeyEx1(nativeHandle, i,810maxKeyLength+1);811if (windowsName == null) {812String info =813"Could not enumerate key #" + i + " of windows node " +814byteArrayToString(windowsAbsolutePath()) + " at root 0x" +815Long.toHexString(rootNativeHandle()) + ". ";816logger().warning(info);817throw new BackingStoreException(info);818}819String javaName = toJavaName(windowsName);820children[i] = javaName;821}822closeKey(nativeHandle);823return children;824}825826/**827* Implements {@code Preferences} {@code flush()} method.828* Flushes Windows registry changes to disk.829* Throws a BackingStoreException and logs a warning message if Windows830* registry is not available.831*/832public void flush() throws BackingStoreException{833834if (isRemoved()) {835parent.flush();836return;837}838if (!isBackingStoreAvailable) {839throw new BackingStoreException(840"flush(): Backing store not available.");841}842long nativeHandle = openKey(KEY_READ);843if (nativeHandle == NULL_NATIVE_HANDLE) {844throw new BackingStoreException(845"Could not open windows registry node " +846byteArrayToString(windowsAbsolutePath()) +847" at root 0x" +848Long.toHexString(rootNativeHandle()) + ".");849}850int result = WindowsRegFlushKey1(nativeHandle);851if (result != ERROR_SUCCESS) {852String info = "Could not flush windows registry node " +853byteArrayToString(windowsAbsolutePath()) +854" at root 0x" +855Long.toHexString(rootNativeHandle()) +856". Windows RegFlushKey(...) returned error code " +857result + ".";858logger().warning(info);859throw new BackingStoreException(info);860}861closeKey(nativeHandle);862}863864865/**866* Implements {@code Preferences} {@code sync()} method.867* Flushes Windows registry changes to disk. Equivalent to flush().868* @see flush()869*/870public void sync() throws BackingStoreException{871if (isRemoved())872throw new IllegalStateException("Node has been removed");873flush();874}875876/**877* Implements {@code AbstractPreferences} {@code childSpi()} method.878* Constructs a child node with a879* 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*/883protected AbstractPreferences childSpi(String name) {884return new WindowsPreferences(this, name);885}886887/**888* Implements {@code AbstractPreferences} {@code removeNodeSpi()} method.889* Deletes underlying Windows registry node.890* Throws a BackingStoreException and logs a warning, if Windows registry891* is not available.892*/893public void removeNodeSpi() throws BackingStoreException {894long parentNativeHandle =895((WindowsPreferences)parent()).openKey(DELETE);896if (parentNativeHandle == NULL_NATIVE_HANDLE) {897throw new BackingStoreException(898"Could not open parent windows registry node of " +899byteArrayToString(windowsAbsolutePath()) +900" at root 0x" +901Long.toHexString(rootNativeHandle()) + ".");902}903int result =904WindowsRegDeleteKey(parentNativeHandle, toWindowsName(name()));905if (result != ERROR_SUCCESS) {906String info = "Could not delete windows registry node " +907byteArrayToString(windowsAbsolutePath()) +908" at root 0x" + Long.toHexString(rootNativeHandle()) +909". Windows RegDeleteKeyEx(...) returned error code " +910result + ".";911logger().warning(info);912throw new BackingStoreException(info);913}914closeKey(parentNativeHandle);915}916917/**918* Converts value's or node's name from its byte array representation to919* java string. Two encodings, simple and altBase64 are used. See920* {@link #toWindowsName(String) toWindowsName()} for a detailed921* description of encoding conventions.922* @param windowsNameArray Null-terminated byte array.923*/924private static String toJavaName(byte[] windowsNameArray) {925String windowsName = byteArrayToString(windowsNameArray);926// check if Alt64927if ((windowsName.length() > 1) &&928(windowsName.substring(0, 2).equals("/!"))) {929return toJavaAlt64Name(windowsName);930}931StringBuilder javaName = new StringBuilder();932char ch;933// Decode from simple encoding934for (int i = 0; i < windowsName.length(); i++) {935if ((ch = windowsName.charAt(i)) == '/') {936char next = ' ';937if ((windowsName.length() > i + 1) &&938((next = windowsName.charAt(i+1)) >= 'A') &&939(next <= 'Z')) {940ch = next;941i++;942} else if ((windowsName.length() > i + 1) &&943(next == '/')) {944ch = '\\';945i++;946}947} else if (ch == '\\') {948ch = '/';949}950javaName.append(ch);951}952return javaName.toString();953}954955/**956* Converts value's or node's name from its Windows representation to java957* string, using altBase64 encoding. See958* {@link #toWindowsName(String) toWindowsName()} for a detailed959* description of encoding conventions.960*/961962private static String toJavaAlt64Name(String windowsName) {963byte[] byteBuffer =964Base64.altBase64ToByteArray(windowsName.substring(2));965StringBuilder result = new StringBuilder();966for (int i = 0; i < byteBuffer.length; i++) {967int firstbyte = (byteBuffer[i++] & 0xff);968int secondbyte = (byteBuffer[i] & 0xff);969result.append((char)((firstbyte << 8) + secondbyte));970}971return result.toString();972}973974/**975* Converts value's or node's name to its Windows representation976* 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 contain980* 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 the985* highest byte and adding the terminating {@code null} character.986* <p>987* <i>altBase64</i> encoding is used, if java string does contain at least988* one character less, than 0x0020, or greater, than 0x007f.989* This encoding is marked by setting first two bytes of the990* Windows string to '/!'. The java name is then encoded using991* byteArrayToAltBase64() method from992* Base64 class.993*/994private static byte[] toWindowsName(String javaName) {995StringBuilder windowsName = new StringBuilder();996for (int i = 0; i < javaName.length(); i++) {997char ch = javaName.charAt(i);998if ((ch < 0x0020) || (ch > 0x007f)) {999// If a non-trivial character encountered, use altBase641000return toWindowsAlt64Name(javaName);1001}1002if (ch == '\\') {1003windowsName.append("//");1004} else if (ch == '/') {1005windowsName.append('\\');1006} else if ((ch >= 'A') && (ch <='Z')) {1007windowsName.append('/').append(ch);1008} else {1009windowsName.append(ch);1010}1011}1012return stringToByteArray(windowsName.toString());1013}10141015/**1016* Converts value's or node's name to its Windows representation1017* as a byte-encoded string, using altBase64 encoding. See1018* {@link #toWindowsName(String) toWindowsName()} for a detailed1019* description of encoding conventions.1020*/1021private static byte[] toWindowsAlt64Name(String javaName) {1022byte[] javaNameArray = new byte[2*javaName.length()];1023// Convert to byte pairs1024int counter = 0;1025for (int i = 0; i < javaName.length();i++) {1026int ch = javaName.charAt(i);1027javaNameArray[counter++] = (byte)(ch >>> 8);1028javaNameArray[counter++] = (byte)ch;1029}10301031return stringToByteArray("/!" +1032Base64.byteArrayToAltBase64(javaNameArray));1033}10341035/**1036* Converts value string from its Windows representation1037* to java string. See1038* {@link #toWindowsValueString(String) toWindowsValueString()} for the1039* description of the encoding algorithm.1040*/1041private static String toJavaValueString(byte[] windowsNameArray) {1042String windowsName = byteArrayToString(windowsNameArray);1043StringBuilder javaName = new StringBuilder();1044char ch;1045for (int i = 0; i < windowsName.length(); i++){1046if ((ch = windowsName.charAt(i)) == '/') {1047char next = ' ';10481049if (windowsName.length() > i + 1 &&1050(next = windowsName.charAt(i + 1)) == 'u') {1051if (windowsName.length() < i + 6) {1052break;1053} else {1054ch = (char)Integer.parseInt(1055windowsName.substring(i + 2, i + 6), 16);1056i += 5;1057}1058} else1059if ((windowsName.length() > i + 1) &&1060((windowsName.charAt(i+1)) >= 'A') &&1061(next <= 'Z')) {1062ch = next;1063i++;1064} else if ((windowsName.length() > i + 1) &&1065(next == '/')) {1066ch = '\\';1067i++;1068}1069} else if (ch == '\\') {1070ch = '/';1071}1072javaName.append(ch);1073}1074return javaName.toString();1075}10761077/**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*/1085private static byte[] toWindowsValueString(String javaName) {1086StringBuilder windowsName = new StringBuilder();1087for (int i = 0; i < javaName.length(); i++) {1088char ch = javaName.charAt(i);1089if ((ch < 0x0020) || (ch > 0x007f)){1090// write \udddd1091windowsName.append("/u");1092String hex = Long.toHexString(javaName.charAt(i));1093StringBuilder hex4 = new StringBuilder(hex);1094hex4.reverse();1095int len = 4 - hex4.length();1096for (int j = 0; j < len; j++){1097hex4.append('0');1098}1099for (int j = 0; j < 4; j++){1100windowsName.append(hex4.charAt(3 - j));1101}1102} else if (ch == '\\') {1103windowsName.append("//");1104} else if (ch == '/') {1105windowsName.append('\\');1106} else if ((ch >= 'A') && (ch <='Z')) {1107windowsName.append('/').append(ch);1108} else {1109windowsName.append(ch);1110}1111}1112return stringToByteArray(windowsName.toString());1113}11141115/**1116* Returns native handle for the top Windows node for this node.1117*/1118private long rootNativeHandle() {1119return (isUserNode()1120? USER_ROOT_NATIVE_HANDLE1121: SYSTEM_ROOT_NATIVE_HANDLE);1122}11231124/**1125* Returns this java string as a null-terminated byte array1126*/1127private static byte[] stringToByteArray(String str) {1128byte[] result = new byte[str.length()+1];1129for (int i = 0; i < str.length(); i++) {1130result[i] = (byte) str.charAt(i);1131}1132result[str.length()] = 0;1133return result;1134}11351136/**1137* Converts a null-terminated byte array to java string1138*/1139private static String byteArrayToString(byte[] array) {1140StringBuilder result = new StringBuilder();1141for (int i = 0; i < array.length - 1; i++) {1142result.append((char)array[i]);1143}1144return result.toString();1145}11461147/**1148* Empty, never used implementation of AbstractPreferences.flushSpi().1149*/1150protected void flushSpi() throws BackingStoreException {1151// assert false;1152}11531154/**1155* Empty, never used implementation of AbstractPreferences.flushSpi().1156*/1157protected void syncSpi() throws BackingStoreException {1158// assert false;1159}11601161private static synchronized PlatformLogger logger() {1162if (logger == null) {1163logger = PlatformLogger.getLogger("java.util.prefs");1164}1165return logger;1166}1167}116811691170