1 7 8 package java.util.prefs; 9 import java.util.*; 10 import java.io.*; 11 import java.util.logging.Logger; 12 import java.security.AccessController; 13 import java.security.PrivilegedAction; 14 import java.security.PrivilegedExceptionAction; 15 import java.security.PrivilegedActionException; 16 17 18 33 class FileSystemPreferences extends AbstractPreferences { 34 37 private static final int SYNC_INTERVAL = Math.max(1, 38 Integer.parseInt((String) 39 AccessController.doPrivileged(new PrivilegedAction() { 40 public Object run() { 41 return System.getProperty("java.util.prefs.syncInterval", 42 "30"); 43 } 44 }))); 45 46 47 51 private static Logger getLogger() { 52 return Logger.getLogger("java.util.prefs"); 53 } 54 55 58 private static File systemRootDir; 59 60 63 private static boolean isSystemRootWritable; 64 65 68 private static File userRootDir; 69 70 73 private static boolean isUserRootWritable; 74 75 78 static Preferences userRoot = null; 79 80 static synchronized Preferences getUserRoot() { 81 if (userRoot == null) { 82 setupUserRoot(); 83 userRoot = new FileSystemPreferences(true); 84 } 85 return userRoot; 86 } 87 88 private static void setupUserRoot() { 89 AccessController.doPrivileged(new PrivilegedAction() { 90 public Object run() { 91 userRootDir = 92 new File(System.getProperty("java.util.prefs.userRoot", 93 System.getProperty("user.home")), ".java/.userPrefs"); 94 if (!userRootDir.exists()) { 96 if (userRootDir.mkdirs()) { 97 try { 98 chmod(userRootDir.getCanonicalPath(), USER_RWX); 99 } catch (IOException e) { 100 getLogger().warning("Could not change permissions" + 101 " on userRoot directory. "); 102 } 103 getLogger().info("Created user preferences directory."); 104 } 105 else 106 getLogger().warning("Couldn't create user preferences" + 107 " directory. User preferences are unusable."); 108 } 109 isUserRootWritable = userRootDir.canWrite(); 110 String USER_NAME = System.getProperty("user.name"); 111 userLockFile = new File (userRootDir,".user.lock." + USER_NAME); 112 userRootModFile = new File (userRootDir, 113 ".userRootModFile." + USER_NAME); 114 if (!userRootModFile.exists()) 115 try { 116 userRootModFile.createNewFile(); 118 int result = chmod(userRootModFile.getCanonicalPath(), 120 USER_READ_WRITE); 121 if (result !=0) 122 getLogger().warning("Problem creating userRoot " + 123 "mod file. Chmod failed on " + 124 userRootModFile.getCanonicalPath() + 125 " Unix error code " + result); 126 } catch (IOException e) { 127 getLogger().warning(e.toString()); 128 } 129 userRootModTime = userRootModFile.lastModified(); 130 return null; 131 } 132 }); 133 } 134 135 136 139 static Preferences systemRoot; 140 141 static synchronized Preferences getSystemRoot() { 142 if (systemRoot == null) { 143 setupSystemRoot(); 144 systemRoot = new FileSystemPreferences(false); 145 } 146 return systemRoot; 147 } 148 149 private static void setupSystemRoot() { 150 AccessController.doPrivileged( new PrivilegedAction() { 151 public Object run() { 152 String systemPrefsDirName = (String) 153 System.getProperty("java.util.prefs.systemRoot","/etc/.java"); 154 systemRootDir = 155 new File(systemPrefsDirName, ".systemPrefs"); 156 if (!systemRootDir.exists()) { 158 systemRootDir = 161 new File(System.getProperty("java.home"), 162 ".systemPrefs"); 163 if (!systemRootDir.exists()) { 164 if (systemRootDir.mkdirs()) { 165 getLogger().info( 166 "Created system preferences directory " 167 + "in java.home."); 168 try { 169 chmod(systemRootDir.getCanonicalPath(), 170 USER_RWX_ALL_RX); 171 } catch (IOException e) { 172 } 173 } else { 174 getLogger().warning("Could not create " 175 + "system preferences directory. System " 176 + "preferences are unusable."); 177 } 178 } 179 } 180 isSystemRootWritable = systemRootDir.canWrite(); 181 systemLockFile = new File(systemRootDir, ".system.lock"); 182 systemRootModFile = 183 new File (systemRootDir,".systemRootModFile"); 184 if (!systemRootModFile.exists() && isSystemRootWritable) 185 try { 186 systemRootModFile.createNewFile(); 188 int result = chmod(systemRootModFile.getCanonicalPath(), 189 USER_RW_ALL_READ); 190 if (result !=0) 191 getLogger().warning("Chmod failed on " + 192 systemRootModFile.getCanonicalPath() + 193 " Unix error code " + result); 194 } catch (IOException e) { getLogger().warning(e.toString()); 195 } 196 systemRootModTime = systemRootModFile.lastModified(); 197 return null; 198 } 199 }); 200 } 201 202 203 206 private static final int USER_READ_WRITE = 0600; 207 208 private static final int USER_RW_ALL_READ = 0644; 209 210 211 private static final int USER_RWX_ALL_RX = 0755; 212 213 private static final int USER_RWX = 0700; 214 215 218 static File userLockFile; 219 220 221 222 225 static File systemLockFile; 226 227 231 232 private static int userRootLockHandle = 0; 233 234 238 239 private static int systemRootLockHandle = 0; 240 241 248 private final File dir; 249 250 256 private final File prefsFile; 257 258 265 private final File tmpFile; 266 267 270 private static File userRootModFile; 271 272 275 private static boolean isUserRootModified = false; 276 277 282 private static long userRootModTime; 283 284 285 288 private static File systemRootModFile; 289 292 private static boolean isSystemRootModified = false; 293 294 299 private static long systemRootModTime; 300 301 308 private Map prefsCache = null; 309 310 319 private long lastSyncTime = 0; 320 321 324 private static final int EAGAIN = 11; 325 326 329 private static final int EACCES = 13; 330 331 332 private static final int LOCK_HANDLE = 0; 333 private static final int ERROR_CODE = 1; 334 335 344 final List changeLog = new ArrayList(); 345 346 349 private abstract class Change { 350 353 abstract void replay(); 354 }; 355 356 359 private class Put extends Change { 360 String key, value; 361 362 Put(String key, String value) { 363 this.key = key; 364 this.value = value; 365 } 366 367 void replay() { 368 prefsCache.put(key, value); 369 } 370 } 371 372 375 private class Remove extends Change { 376 String key; 377 378 Remove(String key) { 379 this.key = key; 380 } 381 382 void replay() { 383 prefsCache.remove(key); 384 } 385 } 386 387 390 private class NodeCreate extends Change { 391 396 void replay() { 397 } 398 } 399 400 403 NodeCreate nodeCreate = null; 404 405 408 private void replayChanges() { 409 for (int i = 0, n = changeLog.size(); i<n; i++) 410 ((Change)changeLog.get(i)).replay(); 411 } 412 413 private static Timer syncTimer = new Timer(true); 415 static { 416 syncTimer.schedule(new TimerTask() { 418 public void run() { 419 syncWorld(); 420 } 421 }, SYNC_INTERVAL*1000, SYNC_INTERVAL*1000); 422 423 AccessController.doPrivileged(new PrivilegedAction() { 425 public Object run() { 426 Runtime.getRuntime().addShutdownHook(new Thread() { 427 public void run() { 428 syncTimer.cancel(); 429 syncWorld(); 430 } 431 }); 432 return null; 433 } 434 }); 435 } 436 437 private static void syncWorld() { 438 442 Preferences userRt; 443 Preferences systemRt; 444 synchronized(FileSystemPreferences.class) { 445 userRt = userRoot; 446 systemRt = systemRoot; 447 } 448 449 try { 450 if (userRt != null) 451 userRt.flush(); 452 } catch(BackingStoreException e) { 453 getLogger().warning("Couldn't flush user prefs: " + e); 454 } 455 456 try { 457 if (systemRt != null) 458 systemRt.flush(); 459 } catch(BackingStoreException e) { 460 getLogger().warning("Couldn't flush system prefs: " + e); 461 } 462 } 463 464 private final boolean isUserNode; 465 466 470 private FileSystemPreferences(boolean user) { 471 super(null, ""); 472 isUserNode = user; 473 dir = (user ? userRootDir: systemRootDir); 474 prefsFile = new File(dir, "prefs.xml"); 475 tmpFile = new File(dir, "prefs.tmp"); 476 } 477 478 483 private FileSystemPreferences(FileSystemPreferences parent, String name) { 484 super(parent, name); 485 isUserNode = parent.isUserNode; 486 dir = new File(parent.dir, dirName(name)); 487 prefsFile = new File(dir, "prefs.xml"); 488 tmpFile = new File(dir, "prefs.tmp"); 489 AccessController.doPrivileged( new PrivilegedAction() { 490 public Object run() { 491 newNode = !dir.exists(); 492 return null; 493 } 494 }); 495 if (newNode) { 496 prefsCache = new TreeMap(); 498 nodeCreate = new NodeCreate(); 499 changeLog.add(nodeCreate); 500 } 501 } 502 503 public boolean isUserNode() { 504 return isUserNode; 505 } 506 507 protected void putSpi(String key, String value) { 508 initCacheIfNecessary(); 509 changeLog.add(new Put(key, value)); 510 prefsCache.put(key, value); 511 } 512 513 protected String getSpi(String key) { 514 initCacheIfNecessary(); 515 return (String) prefsCache.get(key); 516 } 517 518 protected void removeSpi(String key) { 519 initCacheIfNecessary(); 520 changeLog.add(new Remove(key)); 521 prefsCache.remove(key); 522 } 523 524 532 private void initCacheIfNecessary() { 533 if (prefsCache != null) 534 return; 535 536 try { 537 loadCache(); 538 } catch(Exception e) { 539 prefsCache = new TreeMap(); 541 } 542 } 543 544 552 private void loadCache() throws BackingStoreException { 553 try { 554 AccessController.doPrivileged( new PrivilegedExceptionAction() { 555 public Object run() throws BackingStoreException { 556 Map m = new TreeMap(); 557 long newLastSyncTime = 0; 558 try { 559 newLastSyncTime = prefsFile.lastModified(); 560 FileInputStream fis = new FileInputStream(prefsFile); 561 XmlSupport.importMap(fis, m); 562 fis.close(); 563 } catch(Exception e) { 564 if (e instanceof InvalidPreferencesFormatException) { 565 getLogger().warning("Invalid preferences format in " 566 + prefsFile.getPath()); 567 prefsFile.renameTo( new File( 568 prefsFile.getParentFile(), 569 "IncorrectFormatPrefs.xml")); 570 m = new TreeMap(); 571 } else if (e instanceof FileNotFoundException) { 572 getLogger().warning("Prefs file removed in background " 573 + prefsFile.getPath()); 574 } else { 575 throw new BackingStoreException(e); 576 } 577 } 578 prefsCache = m; 580 lastSyncTime = newLastSyncTime; 581 return null; 582 } 583 }); 584 } catch (PrivilegedActionException e) { 585 throw (BackingStoreException) e.getException(); 586 } 587 } 588 589 598 private void writeBackCache() throws BackingStoreException { 599 try { 600 AccessController.doPrivileged( new PrivilegedExceptionAction() { 601 public Object run() throws BackingStoreException { 602 try { 603 if (!dir.exists() && !dir.mkdirs()) 604 throw new BackingStoreException(dir + 605 " create failed."); 606 FileOutputStream fos = new FileOutputStream(tmpFile); 607 XmlSupport.exportMap(fos, prefsCache); 608 fos.close(); 609 if (!tmpFile.renameTo(prefsFile)) 610 throw new BackingStoreException("Can't rename " + 611 tmpFile + " to " + prefsFile); 612 } catch(Exception e) { 613 if (e instanceof BackingStoreException) 614 throw (BackingStoreException)e; 615 throw new BackingStoreException(e); 616 } 617 return null; 618 } 619 }); 620 } catch (PrivilegedActionException e) { 621 throw (BackingStoreException) e.getException(); 622 } 623 } 624 625 protected String[] keysSpi() { 626 initCacheIfNecessary(); 627 return (String[]) 628 prefsCache.keySet().toArray(new String[prefsCache.size()]); 629 } 630 631 protected String[] childrenNamesSpi() { 632 return (String[]) 633 AccessController.doPrivileged( new PrivilegedAction() { 634 public Object run() { 635 List result = new ArrayList(); 636 File[] dirContents = dir.listFiles(); 637 if (dirContents != null) { 638 for (int i = 0; i < dirContents.length; i++) 639 if (dirContents[i].isDirectory()) 640 result.add(nodeName(dirContents[i].getName())); 641 } 642 return result.toArray(EMPTY_STRING_ARRAY); 643 } 644 }); 645 } 646 647 private static final String[] EMPTY_STRING_ARRAY = new String[0]; 648 649 protected AbstractPreferences childSpi(String name) { 650 return new FileSystemPreferences(this, name); 651 } 652 653 public void removeNode() throws BackingStoreException { 654 synchronized (isUserNode()? userLockFile: systemLockFile) { 655 if (!lockFile(false)) 657 throw(new BackingStoreException("Couldn't get file lock.")); 658 try { 659 super.removeNode(); 660 } finally { 661 unlockFile(); 662 } 663 } 664 } 665 666 669 protected void removeNodeSpi() throws BackingStoreException { 670 try { 671 AccessController.doPrivileged( new PrivilegedExceptionAction() { 672 public Object run() throws BackingStoreException { 673 if (changeLog.contains(nodeCreate)) { 674 changeLog.remove(nodeCreate); 675 nodeCreate = null; 676 return null; 677 } 678 if (!dir.exists()) 679 return null; 680 prefsFile.delete(); 681 tmpFile.delete(); 682 File[] junk = dir.listFiles(); 684 if (junk.length != 0) { 685 getLogger().warning( 686 "Found extraneous files when removing node: " 687 + Arrays.asList(junk)); 688 for (int i=0; i<junk.length; i++) 689 junk[i].delete(); 690 } 691 if (!dir.delete()) 692 throw new BackingStoreException("Couldn't delete dir: " 693 + dir); 694 return null; 695 } 696 }); 697 } catch (PrivilegedActionException e) { 698 throw (BackingStoreException) e.getException(); 699 } 700 } 701 702 public synchronized void sync() throws BackingStoreException { 703 boolean userNode = isUserNode(); 704 boolean shared; 705 706 if (userNode) { 707 shared = false; 708 } else { 709 711 shared = !isSystemRootWritable; 712 } 713 synchronized (isUserNode()? userLockFile:systemLockFile) { 714 if (!lockFile(shared)) 715 throw(new BackingStoreException("Couldn't get file lock.")); 716 final Long newModTime = 717 (Long) AccessController.doPrivileged( new PrivilegedAction() { 718 public Object run() { 719 long nmt; 720 if (isUserNode()) { 721 nmt = userRootModFile.lastModified(); 722 isUserRootModified = userRootModTime == nmt; 723 } else { 724 nmt = systemRootModFile.lastModified(); 725 isSystemRootModified = systemRootModTime == nmt; 726 } 727 return new Long(nmt); 728 } 729 }); 730 try { 731 super.sync(); 732 AccessController.doPrivileged( new PrivilegedAction() { 733 public Object run() { 734 if (isUserNode()) { 735 userRootModTime = newModTime.longValue() + 1000; 736 userRootModFile.setLastModified(userRootModTime); 737 } else { 738 systemRootModTime = newModTime.longValue() + 1000; 739 systemRootModFile.setLastModified(systemRootModTime); 740 } 741 return null; 742 } 743 }); 744 } finally { 745 unlockFile(); 746 } 747 } 748 } 749 750 protected void syncSpi() throws BackingStoreException { 751 try { 752 AccessController.doPrivileged( new PrivilegedExceptionAction() { 753 public Object run() throws BackingStoreException { 754 syncSpiPrivileged(); 755 return null; 756 } 757 }); 758 } catch (PrivilegedActionException e) { 759 throw (BackingStoreException) e.getException(); 760 } 761 } 762 private void syncSpiPrivileged() throws BackingStoreException { 763 if (isRemoved()) 764 throw new IllegalStateException("Node has been removed"); 765 if (prefsCache == null) 766 return; long lastModifiedTime; 768 if ((isUserNode() ? isUserRootModified : isSystemRootModified)) { 769 lastModifiedTime = prefsFile.lastModified(); 770 if (lastModifiedTime != lastSyncTime) { 771 loadCache(); 774 replayChanges(); 775 lastSyncTime = lastModifiedTime; 776 } 777 } else if (lastSyncTime != 0 && !dir.exists()) { 778 prefsCache = new TreeMap(); 781 replayChanges(); 782 } 783 if (!changeLog.isEmpty()) { 784 writeBackCache(); 790 lastModifiedTime = prefsFile.lastModified(); 791 796 if (lastSyncTime <= lastModifiedTime) { 797 lastSyncTime = lastModifiedTime + 1000; 798 prefsFile.setLastModified(lastSyncTime); 799 } 800 changeLog.clear(); 801 } 802 } 803 804 public void flush() throws BackingStoreException { 805 if (isRemoved()) 806 return; 807 sync(); 808 } 809 810 protected void flushSpi() throws BackingStoreException { 811 } 813 814 820 private static boolean isDirChar(char ch) { 821 return ch > 0x1f && ch < 0x7f && ch != '/' && ch != '.' && ch != '_'; 822 } 823 824 830 private static String dirName(String nodeName) { 831 for (int i=0, n=nodeName.length(); i < n; i++) 832 if (!isDirChar(nodeName.charAt(i))) 833 return "_" + Base64.byteArrayToAltBase64(byteArray(nodeName)); 834 return nodeName; 835 } 836 837 841 private static byte[] byteArray(String s) { 842 int len = s.length(); 843 byte[] result = new byte[2*len]; 844 for (int i=0, j=0; i<len; i++) { 845 char c = s.charAt(i); 846 result[j++] = (byte) (c>>8); 847 result[j++] = (byte) c; 848 } 849 return result; 850 } 851 852 856 private static String nodeName(String dirName) { 857 if (dirName.charAt(0) != '_') 858 return dirName; 859 byte a[] = Base64.altBase64ToByteArray(dirName.substring(1)); 860 StringBuffer result = new StringBuffer(a.length/2); 861 for (int i = 0; i < a.length; ) { 862 int highByte = a[i++] & 0xff; 863 int lowByte = a[i++] & 0xff; 864 result.append((char) ((highByte << 8) | lowByte)); 865 } 866 return result.toString(); 867 } 868 869 876 private boolean lockFile(boolean shared) throws SecurityException{ 877 boolean usernode = isUserNode(); 878 int[] result; 879 int errorCode = 0; 880 File lockFile = (usernode ? userLockFile : systemLockFile); 881 long sleepTime = INIT_SLEEP_TIME; 882 for (int i = 0; i < MAX_ATTEMPTS; i++) { 883 try { 884 int perm = (usernode? USER_READ_WRITE: USER_RW_ALL_READ); 885 result = lockFile0(lockFile.getCanonicalPath(), perm, shared); 886 887 errorCode = result[ERROR_CODE]; 888 if (result[LOCK_HANDLE] != 0) { 889 if (usernode) { 890 userRootLockHandle = result[LOCK_HANDLE]; 891 } else { 892 systemRootLockHandle = result[LOCK_HANDLE]; 893 } 894 return true; 895 } 896 } catch(IOException e) { 897 } 899 900 try { 901 Thread.sleep(sleepTime); 902 } catch(InterruptedException e) { 903 checkLockFile0ErrorCode(errorCode); 904 return false; 905 } 906 sleepTime *= 2; 907 } 908 checkLockFile0ErrorCode(errorCode); 909 return false; 910 } 911 912 916 private void checkLockFile0ErrorCode (int errorCode) 917 throws SecurityException { 918 if (errorCode == EACCES) 919 throw new SecurityException("Could not lock " + 920 (isUserNode()? "User prefs." : "System prefs.") + 921 "Lock file access denied."); 922 if (errorCode != EAGAIN) 923 getLogger().warning("Could not lock " + 924 (isUserNode()? "User prefs. " : "System prefs.") + 925 "Unix error code " + errorCode + "."); 926 } 927 928 933 private static native int[] 934 lockFile0(String fileName, int permission, boolean shared); 935 936 941 private static native int unlockFile0(int lockHandle); 942 943 946 private static native int chmod(String fileName, int permission); 947 948 952 private static int INIT_SLEEP_TIME = 50; 953 954 957 private static int MAX_ATTEMPTS = 5; 958 959 963 private void unlockFile() { 964 int result; 965 boolean usernode = isUserNode(); 966 File lockFile = (usernode ? userLockFile : systemLockFile); 967 int lockHandle = ( usernode ? userRootLockHandle:systemRootLockHandle); 968 if (lockHandle == 0) { 969 getLogger().warning("Unlock: zero lockHandle for " + 970 (usernode ? "user":"system") + " preferences.)"); 971 return; 972 } 973 result = unlockFile0(lockHandle); 974 if (result != 0) { 975 getLogger().warning("Could not drop file-lock on " + 976 (isUserNode() ? "user" : "system") + " preferences." + 977 "Unix error code " + result + "."); 978 if (result == EACCES) 979 throw new SecurityException("Could not unlock" + 980 (isUserNode()? "User prefs." : "System prefs.") + 981 "Lock file access denied."); 982 } 983 if (isUserNode()) { 984 userRootLockHandle = 0; 985 } else { 986 systemRootLockHandle = 0; 987 } 988 } 989 } 990 | Popular Tags |