/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Preferences implementation for Unix. Preferences are stored in the file
* system, with one directory per preferences node. All of the preferences
* at each node are stored in a single file. Atomic file system operations
* (e.g. File.renameTo) are used to ensure integrity. An in-memory cache of
* the "explored" portion of the tree is maintained for performance, and
* written back to the disk periodically. File-locking is used to ensure
* reasonable behavior when multiple VMs are running at the same time.
* (The file lock is obtained only for sync(), flush() and removeNode().)
*
* @author Josh Bloch
* @see Preferences
* @since 1.4
*/
/**
* Sync interval in seconds.
*/
"java.util.prefs.syncInterval", "30"))));
/**
* Returns logger for error messages. Backing store exceptions are logged at
* WARNING level.
*/
}
/**
* Directory for system preferences.
*/
/*
* Flag, indicating whether systemRoot directory is writable
*/
private static boolean isSystemRootWritable;
/**
* Directory for user preferences.
*/
/*
* Flag, indicating whether userRoot directory is writable
*/
private static boolean isUserRootWritable;
/**
* The user root.
*/
userRoot = new FileSystemPreferences(true);
}
return userRoot;
}
private static void setupUserRoot() {
// Attempt to create root dir if it does not yet exist.
if (!userRootDir.exists()) {
if (userRootDir.mkdirs()) {
try {
} catch (IOException e) {
" on userRoot directory. ");
}
}
else
" directory. User preferences are unusable.");
}
".userRootModFile." + USER_NAME);
if (!userRootModFile.exists())
try {
// create if does not exist.
if (result !=0)
"mod file. Chmod failed on " +
" Unix error code " + result);
} catch (IOException e) {
}
return null;
}
});
}
/**
* The system root.
*/
if (systemRoot == null) {
systemRoot = new FileSystemPreferences(false);
}
return systemRoot;
}
private static void setupSystemRoot() {
// Attempt to create root dir if it does not yet exist.
if (!systemRootDir.exists()) {
// system root does not exist in /etc/.java
// Switching to java.home
".systemPrefs");
if (!systemRootDir.exists()) {
if (systemRootDir.mkdirs()) {
"Created system preferences directory "
+ "in java.home.");
try {
} catch (IOException e) {
}
} else {
+ "system preferences directory. System "
+ "preferences are unusable.");
}
}
}
try {
// create if does not exist.
if (result !=0)
" Unix error code " + result);
}
return null;
}
});
}
/**
*/
/**
* The lock file for the user tree.
*/
/**
* The lock file for the system tree.
*/
/**
* Unix lock handle for userRoot.
* Zero, if unlocked.
*/
/**
* Unix lock handle for systemRoot.
* Zero, if unlocked.
*/
/**
* The directory representing this preference node. There is no guarantee
* that this directory exits, as another VM can delete it at any time
* that it (the other VM) holds the file-lock. While the root node cannot
* be deleted, it may not yet have been created, or the underlying
* directory could have been deleted accidentally.
*/
/**
* The file representing this preference node's preferences.
* The file format is undocumented, and subject to change
* from release to release, but I'm sure that you can figure
* it out if you try real hard.
*/
/**
* A temporary file used for saving changes to preferences. As part of
* the sync operation, changes are first saved into this file, and then
* atomically renamed to prefsFile. This results in an atomic state
* change from one valid set of preferences to another. The
* the file-lock is held for the duration of this transformation.
*/
/**
* File, which keeps track of global modifications of userRoot.
*/
/**
* Flag, which indicated whether userRoot was modified by another VM
*/
private static boolean isUserRootModified = false;
/**
* Keeps track of userRoot modification time. This time is reset to
* zero after UNIX reboot, and is increased by 1 second each time
* userRoot is modified.
*/
private static long userRootModTime;
/*
* File, which keeps track of global modifications of systemRoot
*/
/*
* Flag, which indicates whether systemRoot was modified by another VM
*/
private static boolean isSystemRootModified = false;
/**
* Keeps track of systemRoot modification time. This time is reset to
* zero after system reboot, and is increased by 1 second each time
* systemRoot is modified.
*/
private static long systemRootModTime;
/**
* Locally cached preferences for this node (includes uncommitted
* changes). This map is initialized with from disk when the first get or
* put operation occurs on this node. It is synchronized with the
* corresponding disk file (prefsFile) by the sync operation. The initial
* value is read *without* acquiring the file-lock.
*/
/**
* The last modification time of the file backing this node at the time
* that prefCache was last synchronized (or initially read). This
* value is set *before* reading the file, so it's conservative; the
* actual timestamp could be (slightly) higher. A value of zero indicates
* that we were unable to initialize prefsCache from the disk, or
* have not yet attempted to do so. (If prefsCache is non-null, it
* indicates the former; if it's null, the latter.)
*/
/**
* Unix error code for locked file.
*/
/**
* Unix error code for denied access.
*/
/* Used to interpret results of native functions */
/**
* A list of all uncommitted preference changes. The elements in this
* list are of type PrefChange. If this node is concurrently modified on
* disk by another VM, the two sets of changes are merged when this node
* is sync'ed by overwriting our prefsCache with the preference map last
* written out to disk (by the other VM), and then replaying this change
* log against that map. The resulting map is then written back
* to the disk.
*/
/**
* Represents a change to a preference.
*/
private abstract class Change {
/**
* Reapplies the change to prefsCache.
*/
abstract void replay();
};
/**
* Represents a preference put.
*/
}
void replay() {
}
}
/**
* Represents a preference remove.
*/
}
void replay() {
}
}
/**
* Represents the creation of this node.
*/
/**
* Performs no action, but the presence of this object in changeLog
* will force the node and its ancestors to be made permanent at the
* next sync.
*/
void replay() {
}
}
/**
* NodeCreate object for this node.
*/
/**
* Replay changeLog against prefsCache.
*/
private void replayChanges() {
}
static {
// Add periodic timer task to periodically sync cached prefs
public void run() {
syncWorld();
}
// Add shutdown hook to flush cached prefs on normal termination
public void run() {
syncWorld();
}
});
return null;
}
});
}
private static void syncWorld() {
/*
* Synchronization necessary because userRoot and systemRoot are
* lazily initialized.
*/
synchronized(FileSystemPreferences.class) {
}
try {
} catch(BackingStoreException e) {
}
try {
} catch(BackingStoreException e) {
}
}
private final boolean isUserNode;
/**
* Special constructor for roots (both user and system). This constructor
* will only be called twice, by the static initializer.
*/
super(null, "");
isUserNode = user;
}
/**
* Construct a new FileSystemPreferences instance with the specified
* parent node and name. This constructor, called from childSpi,
* is used to make every node except for the two //roots.
*/
return null;
}
});
if (newNode) {
prefsCache = new TreeMap<>();
nodeCreate = new NodeCreate();
}
}
public boolean isUserNode() {
return isUserNode;
}
}
}
}
/**
* Initialize prefsCache if it has yet to be initialized. When this method
* returns, prefsCache will be non-null. If the data was successfully
* read from the file, lastSyncTime will be updated. If prefsCache was
* null, but it was impossible to read the file (because it didn't
* exist or for any other reason) prefsCache will be initialized to an
* empty, modifiable Map, and lastSyncTime remain zero.
*/
private void initCacheIfNecessary() {
if (prefsCache != null)
return;
try {
loadCache();
} catch(Exception e) {
// assert lastSyncTime == 0;
prefsCache = new TreeMap<>();
}
}
/**
* Attempt to load prefsCache from the backing store. If the attempt
* succeeds, lastSyncTime will be updated (the new value will typically
* correspond to the data loaded into the map, but it may be less,
* if another VM is updating this node concurrently). If the attempt
* fails, a BackingStoreException is thrown and both prefsCache and
* lastSyncTime are unaffected by the call.
*/
try {
new PrivilegedExceptionAction<Void>() {
long newLastSyncTime = 0;
try {
}
} catch(Exception e) {
if (e instanceof InvalidPreferencesFormatException) {
m = new TreeMap<>();
} else if (e instanceof FileNotFoundException) {
} else {
throw new BackingStoreException(e);
}
}
// Attempt succeeded; update state
prefsCache = m;
return null;
}
});
} catch (PrivilegedActionException e) {
throw (BackingStoreException) e.getException();
}
}
/**
* Attempt to write back prefsCache to the backing store. If the attempt
* succeeds, lastSyncTime will be updated (the new value will correspond
* exactly to the data thust written back, as we hold the file lock, which
* prevents a concurrent write. If the attempt fails, a
* BackingStoreException is thrown and both the backing store (prefsFile)
* and lastSyncTime will be unaffected by this call. This call will
* NEVER leave prefsFile in a corrupt state.
*/
try {
new PrivilegedExceptionAction<Void>() {
try {
throw new BackingStoreException(dir +
" create failed.");
}
throw new BackingStoreException("Can't rename " +
} catch(Exception e) {
if (e instanceof BackingStoreException)
throw (BackingStoreException)e;
throw new BackingStoreException(e);
}
return null;
}
});
} catch (PrivilegedActionException e) {
throw (BackingStoreException) e.getException();
}
}
}
return AccessController.doPrivileged(
new PrivilegedAction<String[]>() {
if (dirContents != null) {
if (dirContents[i].isDirectory())
}
}
});
}
return new FileSystemPreferences(this, name);
}
// to remove a node we need an exclusive lock
if (!lockFile(false))
throw(new BackingStoreException("Couldn't get file lock."));
try {
super.removeNode();
} finally {
unlockFile();
}
}
}
/**
* Called with file lock held (in addition to node locks).
*/
try {
new PrivilegedExceptionAction<Void>() {
nodeCreate = null;
return null;
}
return null;
// dir should be empty now. If it's not, empty it
"Found extraneous files when removing node: "
}
throw new BackingStoreException("Couldn't delete dir: "
+ dir);
return null;
}
});
} catch (PrivilegedActionException e) {
throw (BackingStoreException) e.getException();
}
}
boolean userNode = isUserNode();
boolean shared;
if (userNode) {
shared = false; /* use exclusive lock for user prefs */
} else {
/* if can write to system root, use exclusive lock.
otherwise use shared lock. */
}
throw(new BackingStoreException("Couldn't get file lock."));
final Long newModTime =
new PrivilegedAction<Long>() {
long nmt;
if (isUserNode()) {
} else {
}
}
});
try {
super.sync();
if (isUserNode()) {
} else {
}
return null;
}
});
} finally {
unlockFile();
}
}
}
try {
new PrivilegedExceptionAction<Void>() {
return null;
}
});
} catch (PrivilegedActionException e) {
throw (BackingStoreException) e.getException();
}
}
if (isRemoved())
throw new IllegalStateException("Node has been removed");
if (prefsCache == null)
return; // We've never been used, don't bother syncing
long lastModifiedTime;
if (lastModifiedTime != lastSyncTime) {
// Prefs at this node were externally modified; read in node and
// playback any local mods since last sync
loadCache();
}
// This node was removed in the background. Playback any changes
// against a virgin (empty) Map.
prefsCache = new TreeMap<>();
}
writeBackCache(); // Creates directory & file if necessary
/*
* Attempt succeeded; it's barely possible that the call to
* lastModified might fail (i.e., return 0), but this would not
* be a disaster, as lastSyncTime is allowed to lag.
*/
/* If lastSyncTime did not change, or went back
* increment by 1 second. Since we hold the lock
* lastSyncTime always monotonically encreases in the
* atomic sense.
*/
if (lastSyncTime <= lastModifiedTime) {
}
}
}
if (isRemoved())
return;
sync();
}
// assert false;
}
/**
* Returns true if the specified character is appropriate for use in
* Unix directory names. A character is appropriate if it's a printable
* ASCII character (> 0x1f && < 0x7f) and unequal to slash ('/', 0x2f),
* dot ('.', 0x2e), or underscore ('_', 0x5f).
*/
}
/**
* Returns the directory name corresponding to the specified node name.
* Generally, this is just the node name. If the node name includes
* inappropriate characters (as per isDirChar) it is translated to Base64.
* with the underscore character ('_', 0x5f) prepended.
*/
return nodeName;
}
/**
* Translate a string into a byte array by translating each character
* into two bytes, high-byte first ("big-endian").
*/
char c = s.charAt(i);
result[j++] = (byte) (c>>8);
result[j++] = (byte) c;
}
return result;
}
/**
* Returns the node name corresponding to the specified directory name.
* (Inverts the transformation of dirName(String).
*/
return dirName;
for (int i = 0; i < a.length; ) {
int highByte = a[i++] & 0xff;
int lowByte = a[i++] & 0xff;
}
}
/**
* Try to acquire the appropriate file lock (user or system). If
* the initial attempt fails, several more attempts are made using
* an exponential backoff strategy. If all attempts fail, this method
* returns false.
* @throws SecurityException if file access denied.
*/
boolean usernode = isUserNode();
int[] result;
int errorCode = 0;
long sleepTime = INIT_SLEEP_TIME;
for (int i = 0; i < MAX_ATTEMPTS; i++) {
try {
if (usernode) {
} else {
}
return true;
}
} catch(IOException e) {
// // If at first, you don't succeed...
}
try {
} catch(InterruptedException e) {
return false;
}
sleepTime *= 2;
}
return false;
}
/**
* Checks if unlockFile0() returned an error. Throws a SecurityException,
* if access denied. Logs a warning otherwise.
*/
throws SecurityException {
throw new SecurityException("Could not lock " +
" Lock file access denied.");
}
/**
* Locks file using UNIX file locking.
* @param fileName Absolute file name of the lock file.
* @return Returns a lock handle, used to unlock the file.
*/
private static native int[]
/**
* Unlocks file previously locked by lockFile0().
* @param lockHandle Handle to the file lock.
* @return Returns zero if OK, UNIX error code if failure.
*/
/**
* Changes UNIX file permissions.
*/
/**
* Initial time between lock attempts, in ms. The time is doubled
* after each failing attempt (except the first).
*/
/**
* Maximum number of lock attempts.
*/
/**
* Release the the appropriate file lock (user or system).
* @throws SecurityException if file access denied.
*/
private void unlockFile() {
int result;
boolean usernode = isUserNode();
if (lockHandle == 0) {
return;
}
if (result != 0) {
throw new SecurityException("Could not unlock" +
" Lock file access denied.");
}
if (isUserNode()) {
userRootLockHandle = 0;
} else {
systemRootLockHandle = 0;
}
}
}