UnixFileSystem.java revision 51a43d9402a355b24c0445df615d8f4975f04fc3
1/* 2 * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package java.io; 27 28import java.security.AccessController; 29import sun.security.action.GetPropertyAction; 30 31 32class UnixFileSystem extends FileSystem { 33 34 private final char slash; 35 private final char colon; 36 private final String javaHome; 37 38 public UnixFileSystem() { 39 slash = AccessController.doPrivileged( 40 new GetPropertyAction("file.separator")).charAt(0); 41 colon = AccessController.doPrivileged( 42 new GetPropertyAction("path.separator")).charAt(0); 43 javaHome = AccessController.doPrivileged( 44 new GetPropertyAction("java.home")); 45 } 46 47 48 /* -- Normalization and construction -- */ 49 50 public char getSeparator() { 51 return slash; 52 } 53 54 public char getPathSeparator() { 55 return colon; 56 } 57 58 /* A normal Unix pathname contains no duplicate slashes and does not end 59 with a slash. It may be the empty string. */ 60 61 /* Normalize the given pathname, whose length is len, starting at the given 62 offset; everything before this offset is already normal. */ 63 private String normalize(String pathname, int len, int off) { 64 if (len == 0) return pathname; 65 int n = len; 66 while ((n > 0) && (pathname.charAt(n - 1) == '/')) n--; 67 if (n == 0) return "/"; 68 StringBuffer sb = new StringBuffer(pathname.length()); 69 if (off > 0) sb.append(pathname.substring(0, off)); 70 char prevChar = 0; 71 for (int i = off; i < n; i++) { 72 char c = pathname.charAt(i); 73 if ((prevChar == '/') && (c == '/')) continue; 74 sb.append(c); 75 prevChar = c; 76 } 77 return sb.toString(); 78 } 79 80 /* Check that the given pathname is normal. If not, invoke the real 81 normalizer on the part of the pathname that requires normalization. 82 This way we iterate through the whole pathname string only once. */ 83 public String normalize(String pathname) { 84 int n = pathname.length(); 85 char prevChar = 0; 86 for (int i = 0; i < n; i++) { 87 char c = pathname.charAt(i); 88 if ((prevChar == '/') && (c == '/')) 89 return normalize(pathname, n, i - 1); 90 prevChar = c; 91 } 92 if (prevChar == '/') return normalize(pathname, n, n - 1); 93 return pathname; 94 } 95 96 public int prefixLength(String pathname) { 97 if (pathname.length() == 0) return 0; 98 return (pathname.charAt(0) == '/') ? 1 : 0; 99 } 100 101 public String resolve(String parent, String child) { 102 if (child.equals("")) return parent; 103 if (child.charAt(0) == '/') { 104 if (parent.equals("/")) return child; 105 return parent + child; 106 } 107 if (parent.equals("/")) return parent + child; 108 return parent + '/' + child; 109 } 110 111 public String getDefaultParent() { 112 return "/"; 113 } 114 115 public String fromURIPath(String path) { 116 String p = path; 117 if (p.endsWith("/") && (p.length() > 1)) { 118 // "/foo/" --> "/foo", but "/" --> "/" 119 p = p.substring(0, p.length() - 1); 120 } 121 return p; 122 } 123 124 125 /* -- Path operations -- */ 126 127 public boolean isAbsolute(File f) { 128 return (f.getPrefixLength() != 0); 129 } 130 131 public String resolve(File f) { 132 if (isAbsolute(f)) return f.getPath(); 133 return resolve(System.getProperty("user.dir"), f.getPath()); 134 } 135 136 // Caches for canonicalization results to improve startup performance. 137 // The first cache handles repeated canonicalizations of the same path 138 // name. The prefix cache handles repeated canonicalizations within the 139 // same directory, and must not create results differing from the true 140 // canonicalization algorithm in canonicalize_md.c. For this reason the 141 // prefix cache is conservative and is not used for complex path names. 142 private ExpiringCache cache = new ExpiringCache(); 143 // On Unix symlinks can jump anywhere in the file system, so we only 144 // treat prefixes in java.home as trusted and cacheable in the 145 // canonicalization algorithm 146 private ExpiringCache javaHomePrefixCache = new ExpiringCache(); 147 148 public String canonicalize(String path) throws IOException { 149 if (!useCanonCaches) { 150 return canonicalize0(path); 151 } else { 152 String res = cache.get(path); 153 if (res == null) { 154 String dir = null; 155 String resDir = null; 156 if (useCanonPrefixCache) { 157 // Note that this can cause symlinks that should 158 // be resolved to a destination directory to be 159 // resolved to the directory they're contained in 160 dir = parentOrNull(path); 161 if (dir != null) { 162 resDir = javaHomePrefixCache.get(dir); 163 if (resDir != null) { 164 // Hit only in prefix cache; full path is canonical 165 String filename = path.substring(1 + dir.length()); 166 res = resDir + slash + filename; 167 cache.put(dir + slash + filename, res); 168 } 169 } 170 } 171 if (res == null) { 172 res = canonicalize0(path); 173 cache.put(path, res); 174 if (useCanonPrefixCache && 175 dir != null && dir.startsWith(javaHome)) { 176 resDir = parentOrNull(res); 177 // Note that we don't allow a resolved symlink 178 // to elsewhere in java.home to pollute the 179 // prefix cache (java.home prefix cache could 180 // just as easily be a set at this point) 181 if (resDir != null && resDir.equals(dir)) { 182 File f = new File(res); 183 if (f.exists() && !f.isDirectory()) { 184 javaHomePrefixCache.put(dir, resDir); 185 } 186 } 187 } 188 } 189 } 190 return res; 191 } 192 } 193 private native String canonicalize0(String path) throws IOException; 194 // Best-effort attempt to get parent of this path; used for 195 // optimization of filename canonicalization. This must return null for 196 // any cases where the code in canonicalize_md.c would throw an 197 // exception or otherwise deal with non-simple pathnames like handling 198 // of "." and "..". It may conservatively return null in other 199 // situations as well. Returning null will cause the underlying 200 // (expensive) canonicalization routine to be called. 201 static String parentOrNull(String path) { 202 if (path == null) return null; 203 char sep = File.separatorChar; 204 int last = path.length() - 1; 205 int idx = last; 206 int adjacentDots = 0; 207 int nonDotCount = 0; 208 while (idx > 0) { 209 char c = path.charAt(idx); 210 if (c == '.') { 211 if (++adjacentDots >= 2) { 212 // Punt on pathnames containing . and .. 213 return null; 214 } 215 } else if (c == sep) { 216 if (adjacentDots == 1 && nonDotCount == 0) { 217 // Punt on pathnames containing . and .. 218 return null; 219 } 220 if (idx == 0 || 221 idx >= last - 1 || 222 path.charAt(idx - 1) == sep) { 223 // Punt on pathnames containing adjacent slashes 224 // toward the end 225 return null; 226 } 227 return path.substring(0, idx); 228 } else { 229 ++nonDotCount; 230 adjacentDots = 0; 231 } 232 --idx; 233 } 234 return null; 235 } 236 237 /* -- Attribute accessors -- */ 238 239 /* ----- BEGIN android ----- 240 public native int getBooleanAttributes0(File f);*/ 241 public native int getBooleanAttributes0(String abspath); 242 243 public int getBooleanAttributes(File f) { 244 /* ----- BEGIN android ----- 245 int rv = getBooleanAttributes0(f);*/ 246 int rv = getBooleanAttributes0(f.getPath()); 247 // ----- END android ----- 248 String name = f.getName(); 249 boolean hidden = (name.length() > 0) && (name.charAt(0) == '.'); 250 return rv | (hidden ? BA_HIDDEN : 0); 251 } 252 253 public native boolean checkAccess(File f, int access); 254 public native long getLastModifiedTime(File f); 255 public native long getLength(File f); 256 public native boolean setPermission(File f, int access, boolean enable, boolean owneronly); 257 258 /* -- File operations -- */ 259 260 public native boolean createFileExclusively(String path) 261 throws IOException; 262 public boolean delete(File f) { 263 // Keep canonicalization caches in sync after file deletion 264 // and renaming operations. Could be more clever than this 265 // (i.e., only remove/update affected entries) but probably 266 // not worth it since these entries expire after 30 seconds 267 // anyway. 268 cache.clear(); 269 javaHomePrefixCache.clear(); 270 return delete0(f); 271 } 272 private native boolean delete0(File f); 273 public native String[] list(File f); 274 public native boolean createDirectory(File f); 275 public boolean rename(File f1, File f2) { 276 // Keep canonicalization caches in sync after file deletion 277 // and renaming operations. Could be more clever than this 278 // (i.e., only remove/update affected entries) but probably 279 // not worth it since these entries expire after 30 seconds 280 // anyway. 281 cache.clear(); 282 javaHomePrefixCache.clear(); 283 return rename0(f1, f2); 284 } 285 private native boolean rename0(File f1, File f2); 286 public native boolean setLastModifiedTime(File f, long time); 287 public native boolean setReadOnly(File f); 288 289 290 /* -- Filesystem interface -- */ 291 292 public File[] listRoots() { 293 try { 294 SecurityManager security = System.getSecurityManager(); 295 if (security != null) { 296 security.checkRead("/"); 297 } 298 return new File[] { new File("/") }; 299 } catch (SecurityException x) { 300 return new File[0]; 301 } 302 } 303 304 /* -- Disk usage -- */ 305 public native long getSpace(File f, int t); 306 307 /* -- Basic infrastructure -- */ 308 309 public int compare(File f1, File f2) { 310 return f1.getPath().compareTo(f2.getPath()); 311 } 312 313 public int hashCode(File f) { 314 return f.getPath().hashCode() ^ 1234321; 315 } 316 317 318 private static native void initIDs(); 319 320 static { 321 initIDs(); 322 } 323 324} 325