UnixFileSystem.java revision 3261
0N/A/*
0N/A * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
0N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
873N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
0N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
0N/A * or visit www.oracle.com if you need additional information or have any
0N/A * questions.
873N/A */
0N/A
0N/Apackage java.io;
0N/A
0N/Aimport java.security.AccessController;
0N/Aimport sun.security.action.GetPropertyAction;
0N/A
0N/A
0N/Aclass UnixFileSystem extends FileSystem {
0N/A
0N/A private final char slash;
0N/A private final char colon;
0N/A private final String javaHome;
0N/A
0N/A public UnixFileSystem() {
0N/A slash = AccessController.doPrivileged(
0N/A new GetPropertyAction("file.separator")).charAt(0);
0N/A colon = AccessController.doPrivileged(
0N/A new GetPropertyAction("path.separator")).charAt(0);
0N/A javaHome = AccessController.doPrivileged(
2814N/A new GetPropertyAction("java.home"));
0N/A }
0N/A
0N/A
0N/A /* -- Normalization and construction -- */
0N/A
0N/A public char getSeparator() {
0N/A return slash;
0N/A }
0N/A
0N/A public char getPathSeparator() {
0N/A return colon;
0N/A }
0N/A
0N/A /* A normal Unix pathname contains no duplicate slashes and does not end
0N/A with a slash. It may be the empty string. */
0N/A
0N/A /* Normalize the given pathname, whose length is len, starting at the given
0N/A offset; everything before this offset is already normal. */
0N/A private String normalize(String pathname, int len, int off) {
0N/A if (len == 0) return pathname;
0N/A int n = len;
0N/A while ((n > 0) && (pathname.charAt(n - 1) == '/')) n--;
0N/A if (n == 0) return "/";
0N/A StringBuffer sb = new StringBuffer(pathname.length());
0N/A if (off > 0) sb.append(pathname.substring(0, off));
0N/A char prevChar = 0;
0N/A for (int i = off; i < n; i++) {
0N/A char c = pathname.charAt(i);
0N/A if ((prevChar == '/') && (c == '/')) continue;
0N/A sb.append(c);
0N/A prevChar = c;
0N/A }
0N/A return sb.toString();
2814N/A }
0N/A
0N/A /* Check that the given pathname is normal. If not, invoke the real
0N/A normalizer on the part of the pathname that requires normalization.
0N/A This way we iterate through the whole pathname string only once. */
0N/A public String normalize(String pathname) {
0N/A int n = pathname.length();
0N/A char prevChar = 0;
0N/A for (int i = 0; i < n; i++) {
0N/A char c = pathname.charAt(i);
0N/A if ((prevChar == '/') && (c == '/'))
0N/A return normalize(pathname, n, i - 1);
0N/A prevChar = c;
0N/A }
0N/A if (prevChar == '/') return normalize(pathname, n, n - 1);
0N/A return pathname;
0N/A }
0N/A
0N/A public int prefixLength(String pathname) {
0N/A if (pathname.length() == 0) return 0;
0N/A return (pathname.charAt(0) == '/') ? 1 : 0;
0N/A }
0N/A
0N/A public String resolve(String parent, String child) {
0N/A if (child.equals("")) return parent;
0N/A if (child.charAt(0) == '/') {
0N/A if (parent.equals("/")) return child;
0N/A return parent + child;
0N/A }
0N/A if (parent.equals("/")) return parent + child;
0N/A return parent + '/' + child;
0N/A }
0N/A
0N/A public String getDefaultParent() {
0N/A return "/";
0N/A }
0N/A
0N/A public String fromURIPath(String path) {
0N/A String p = path;
0N/A if (p.endsWith("/") && (p.length() > 1)) {
0N/A // "/foo/" --> "/foo", but "/" --> "/"
0N/A p = p.substring(0, p.length() - 1);
0N/A }
0N/A return p;
2814N/A }
0N/A
0N/A
0N/A /* -- Path operations -- */
0N/A
0N/A public boolean isAbsolute(File f) {
0N/A return (f.getPrefixLength() != 0);
0N/A }
0N/A
0N/A public String resolve(File f) {
0N/A if (isAbsolute(f)) return f.getPath();
0N/A return resolve(System.getProperty("user.dir"), f.getPath());
0N/A }
0N/A
0N/A // Caches for canonicalization results to improve startup performance.
0N/A // The first cache handles repeated canonicalizations of the same path
0N/A // name. The prefix cache handles repeated canonicalizations within the
0N/A // same directory, and must not create results differing from the true
0N/A // canonicalization algorithm in canonicalize_md.c. For this reason the
0N/A // prefix cache is conservative and is not used for complex path names.
0N/A private ExpiringCache cache = new ExpiringCache();
0N/A // On Unix symlinks can jump anywhere in the file system, so we only
0N/A // treat prefixes in java.home as trusted and cacheable in the
0N/A // canonicalization algorithm
0N/A private ExpiringCache javaHomePrefixCache = new ExpiringCache();
0N/A
0N/A public String canonicalize(String path) throws IOException {
0N/A if (!useCanonCaches) {
0N/A return canonicalize0(path);
0N/A } else {
0N/A String res = cache.get(path);
0N/A if (res == null) {
0N/A String dir = null;
0N/A String resDir = null;
0N/A if (useCanonPrefixCache) {
0N/A // Note that this can cause symlinks that should
0N/A // be resolved to a destination directory to be
0N/A // resolved to the directory they're contained in
0N/A dir = parentOrNull(path);
0N/A if (dir != null) {
0N/A resDir = javaHomePrefixCache.get(dir);
0N/A if (resDir != null) {
0N/A // Hit only in prefix cache; full path is canonical
0N/A String filename = path.substring(1 + dir.length());
0N/A res = resDir + slash + filename;
0N/A cache.put(dir + slash + filename, res);
0N/A }
0N/A }
0N/A }
0N/A if (res == null) {
0N/A res = canonicalize0(path);
0N/A cache.put(path, res);
0N/A if (useCanonPrefixCache &&
0N/A dir != null && dir.startsWith(javaHome)) {
0N/A resDir = parentOrNull(res);
0N/A // Note that we don't allow a resolved symlink
0N/A // to elsewhere in java.home to pollute the
0N/A // prefix cache (java.home prefix cache could
0N/A // just as easily be a set at this point)
0N/A if (resDir != null && resDir.equals(dir)) {
0N/A File f = new File(res);
0N/A if (f.exists() && !f.isDirectory()) {
0N/A javaHomePrefixCache.put(dir, resDir);
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
0N/A return res;
0N/A }
0N/A }
0N/A private native String canonicalize0(String path) throws IOException;
0N/A // Best-effort attempt to get parent of this path; used for
0N/A // optimization of filename canonicalization. This must return null for
0N/A // any cases where the code in canonicalize_md.c would throw an
0N/A // exception or otherwise deal with non-simple pathnames like handling
0N/A // of "." and "..". It may conservatively return null in other
0N/A // situations as well. Returning null will cause the underlying
0N/A // (expensive) canonicalization routine to be called.
0N/A static String parentOrNull(String path) {
0N/A if (path == null) return null;
0N/A char sep = File.separatorChar;
0N/A int last = path.length() - 1;
0N/A int idx = last;
0N/A int adjacentDots = 0;
0N/A int nonDotCount = 0;
0N/A while (idx > 0) {
0N/A char c = path.charAt(idx);
0N/A if (c == '.') {
0N/A if (++adjacentDots >= 2) {
0N/A // Punt on pathnames containing . and ..
0N/A return null;
0N/A }
0N/A } else if (c == sep) {
0N/A if (adjacentDots == 1 && nonDotCount == 0) {
// Punt on pathnames containing . and ..
return null;
}
if (idx == 0 ||
idx >= last - 1 ||
path.charAt(idx - 1) == sep) {
// Punt on pathnames containing adjacent slashes
// toward the end
return null;
}
return path.substring(0, idx);
} else {
++nonDotCount;
adjacentDots = 0;
}
--idx;
}
return null;
}
/* -- Attribute accessors -- */
public native int getBooleanAttributes0(File f);
public int getBooleanAttributes(File f) {
int rv = getBooleanAttributes0(f);
String name = f.getName();
boolean hidden = (name.length() > 0) && (name.charAt(0) == '.');
return rv | (hidden ? BA_HIDDEN : 0);
}
public native boolean checkAccess(File f, int access);
public native long getLastModifiedTime(File f);
public native long getLength(File f);
public native boolean setPermission(File f, int access, boolean enable, boolean owneronly);
/* -- File operations -- */
public native boolean createFileExclusively(String path)
throws IOException;
public boolean delete(File f) {
// Keep canonicalization caches in sync after file deletion
// and renaming operations. Could be more clever than this
// (i.e., only remove/update affected entries) but probably
// not worth it since these entries expire after 30 seconds
// anyway.
cache.clear();
javaHomePrefixCache.clear();
return delete0(f);
}
private native boolean delete0(File f);
public native String[] list(File f);
public native boolean createDirectory(File f);
public boolean rename(File f1, File f2) {
// Keep canonicalization caches in sync after file deletion
// and renaming operations. Could be more clever than this
// (i.e., only remove/update affected entries) but probably
// not worth it since these entries expire after 30 seconds
// anyway.
cache.clear();
javaHomePrefixCache.clear();
return rename0(f1, f2);
}
private native boolean rename0(File f1, File f2);
public native boolean setLastModifiedTime(File f, long time);
public native boolean setReadOnly(File f);
/* -- Filesystem interface -- */
public File[] listRoots() {
try {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead("/");
}
return new File[] { new File("/") };
} catch (SecurityException x) {
return new File[0];
}
}
/* -- Disk usage -- */
public native long getSpace(File f, int t);
/* -- Basic infrastructure -- */
public int compare(File f1, File f2) {
return f1.getPath().compareTo(f2.getPath());
}
public int hashCode(File f) {
return f.getPath().hashCode() ^ 1234321;
}
private static native void initIDs();
static {
initIDs();
}
}