Path: blob/master/src/java.base/unix/classes/java/io/UnixFileSystem.java
67849 views
/*1* Copyright (c) 1998, 2022, 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.io;2627import java.util.Properties;2829import jdk.internal.util.StaticProperty;30import sun.security.action.GetPropertyAction;313233class UnixFileSystem extends FileSystem {3435private final char slash;36private final char colon;37private final String javaHome;38private final String userDir;3940public UnixFileSystem() {41Properties props = GetPropertyAction.privilegedGetProperties();42slash = props.getProperty("file.separator").charAt(0);43colon = props.getProperty("path.separator").charAt(0);44javaHome = StaticProperty.javaHome();45userDir = StaticProperty.userDir();46cache = useCanonCaches ? new ExpiringCache() : null;47javaHomePrefixCache = useCanonPrefixCache ? new ExpiringCache() : null;48}495051/* -- Normalization and construction -- */5253@Override54public char getSeparator() {55return slash;56}5758@Override59public char getPathSeparator() {60return colon;61}6263/* A normal Unix pathname contains no duplicate slashes and does not end64with a slash. It may be the empty string. */6566/**67* Normalize the given pathname, starting at the given68* offset; everything before off is already normal, and there's at least69* one duplicate or trailing slash to be removed70*/71private String normalize(String pathname, int off) {72int n = pathname.length();73while ((n > off) && (pathname.charAt(n - 1) == '/')) n--;74if (n == 0) return "/";75if (n == off) return pathname.substring(0, off);7677StringBuilder sb = new StringBuilder(n);78if (off > 0) sb.append(pathname, 0, off);79char prevChar = 0;80for (int i = off; i < n; i++) {81char c = pathname.charAt(i);82if ((prevChar == '/') && (c == '/')) continue;83sb.append(c);84prevChar = c;85}86return sb.toString();87}8889/* Check that the given pathname is normal. If not, invoke the real90normalizer on the part of the pathname that requires normalization.91This way we iterate through the whole pathname string only once. */92@Override93public String normalize(String pathname) {94int doubleSlash = pathname.indexOf("//");95if (doubleSlash >= 0) {96return normalize(pathname, doubleSlash);97}98if (pathname.endsWith("/")) {99return normalize(pathname, pathname.length() - 1);100}101return pathname;102}103104@Override105public int prefixLength(String pathname) {106return pathname.startsWith("/") ? 1 : 0;107}108109@Override110public String resolve(String parent, String child) {111if (child.isEmpty()) return parent;112if (child.charAt(0) == '/') {113if (parent.equals("/")) return child;114return parent + child;115}116if (parent.equals("/")) return parent + child;117return parent + '/' + child;118}119120@Override121public String getDefaultParent() {122return "/";123}124125@Override126public String fromURIPath(String path) {127String p = path;128if (p.endsWith("/") && (p.length() > 1)) {129// "/foo/" --> "/foo", but "/" --> "/"130p = p.substring(0, p.length() - 1);131}132return p;133}134135136/* -- Path operations -- */137138@Override139public boolean isAbsolute(File f) {140return (f.getPrefixLength() != 0);141}142143@Override144public boolean isInvalid(File f) {145return f.getPath().indexOf('\u0000') < 0 ? false : true;146}147148@Override149public String resolve(File f) {150if (isAbsolute(f)) return f.getPath();151@SuppressWarnings("removal")152SecurityManager sm = System.getSecurityManager();153if (sm != null) {154sm.checkPropertyAccess("user.dir");155}156return resolve(userDir, f.getPath());157}158159// Caches for canonicalization results to improve startup performance.160// The first cache handles repeated canonicalizations of the same path161// name. The prefix cache handles repeated canonicalizations within the162// same directory, and must not create results differing from the true163// canonicalization algorithm in canonicalize_md.c. For this reason the164// prefix cache is conservative and is not used for complex path names.165private final ExpiringCache cache;166// On Unix symlinks can jump anywhere in the file system, so we only167// treat prefixes in java.home as trusted and cacheable in the168// canonicalization algorithm169private final ExpiringCache javaHomePrefixCache;170171@Override172public String canonicalize(String path) throws IOException {173if (!useCanonCaches) {174return canonicalize0(path);175} else {176String res = cache.get(path);177if (res == null) {178String dir = null;179String resDir;180if (useCanonPrefixCache) {181// Note that this can cause symlinks that should182// be resolved to a destination directory to be183// resolved to the directory they're contained in184dir = parentOrNull(path);185if (dir != null) {186resDir = javaHomePrefixCache.get(dir);187if (resDir != null) {188// Hit only in prefix cache; full path is canonical189String filename = path.substring(1 + dir.length());190res = resDir + slash + filename;191cache.put(dir + slash + filename, res);192}193}194}195if (res == null) {196res = canonicalize0(path);197cache.put(path, res);198if (useCanonPrefixCache &&199dir != null && dir.startsWith(javaHome)) {200resDir = parentOrNull(res);201// Note that we don't allow a resolved symlink202// to elsewhere in java.home to pollute the203// prefix cache (java.home prefix cache could204// just as easily be a set at this point)205if (resDir != null && resDir.equals(dir)) {206File f = new File(res);207if (f.exists() && !f.isDirectory()) {208javaHomePrefixCache.put(dir, resDir);209}210}211}212}213}214return res;215}216}217private native String canonicalize0(String path) throws IOException;218// Best-effort attempt to get parent of this path; used for219// optimization of filename canonicalization. This must return null for220// any cases where the code in canonicalize_md.c would throw an221// exception or otherwise deal with non-simple pathnames like handling222// of "." and "..". It may conservatively return null in other223// situations as well. Returning null will cause the underlying224// (expensive) canonicalization routine to be called.225static String parentOrNull(String path) {226if (path == null) return null;227char sep = File.separatorChar;228int last = path.length() - 1;229int idx = last;230int adjacentDots = 0;231int nonDotCount = 0;232while (idx > 0) {233char c = path.charAt(idx);234if (c == '.') {235if (++adjacentDots >= 2) {236// Punt on pathnames containing . and ..237return null;238}239} else if (c == sep) {240if (adjacentDots == 1 && nonDotCount == 0) {241// Punt on pathnames containing . and ..242return null;243}244if (idx == 0 ||245idx >= last - 1 ||246path.charAt(idx - 1) == sep) {247// Punt on pathnames containing adjacent slashes248// toward the end249return null;250}251return path.substring(0, idx);252} else {253++nonDotCount;254adjacentDots = 0;255}256--idx;257}258return null;259}260261/* -- Attribute accessors -- */262263public native int getBooleanAttributes0(File f);264265@Override266public int getBooleanAttributes(File f) {267int rv = getBooleanAttributes0(f);268return rv | isHidden(f);269}270271@Override272public boolean hasBooleanAttributes(File f, int attributes) {273int rv = getBooleanAttributes0(f);274if ((attributes & BA_HIDDEN) != 0) {275rv |= isHidden(f);276}277return (rv & attributes) == attributes;278}279280private static int isHidden(File f) {281return f.getName().startsWith(".") ? BA_HIDDEN : 0;282}283284@Override285public native boolean checkAccess(File f, int access);286287@Override288public native long getLastModifiedTime(File f);289290@Override291public native long getLength(File f);292293@Override294public native boolean setPermission(File f, int access, boolean enable, boolean owneronly);295296/* -- File operations -- */297298@Override299public native boolean createFileExclusively(String path)300throws IOException;301302@Override303public boolean delete(File f) {304// Keep canonicalization caches in sync after file deletion305// and renaming operations. Could be more clever than this306// (i.e., only remove/update affected entries) but probably307// not worth it since these entries expire after 30 seconds308// anyway.309if (useCanonCaches) {310cache.clear();311}312if (useCanonPrefixCache) {313javaHomePrefixCache.clear();314}315return delete0(f);316}317private native boolean delete0(File f);318319@Override320public native String[] list(File f);321322@Override323public native boolean createDirectory(File f);324325@Override326public boolean rename(File f1, File f2) {327// Keep canonicalization caches in sync after file deletion328// and renaming operations. Could be more clever than this329// (i.e., only remove/update affected entries) but probably330// not worth it since these entries expire after 30 seconds331// anyway.332if (useCanonCaches) {333cache.clear();334}335if (useCanonPrefixCache) {336javaHomePrefixCache.clear();337}338return rename0(f1, f2);339}340private native boolean rename0(File f1, File f2);341342@Override343public native boolean setLastModifiedTime(File f, long time);344345@Override346public native boolean setReadOnly(File f);347348/* -- Filesystem interface -- */349350@Override351public File[] listRoots() {352try {353@SuppressWarnings("removal")354SecurityManager security = System.getSecurityManager();355if (security != null) {356security.checkRead("/");357}358return new File[] { new File("/") };359} catch (SecurityException x) {360return new File[0];361}362}363364/* -- Disk usage -- */365366@Override367public native long getSpace(File f, int t);368369/* -- Basic infrastructure -- */370371private native long getNameMax0(String path);372373@Override374public int getNameMax(String path) {375long nameMax = getNameMax0(path);376if (nameMax > Integer.MAX_VALUE) {377nameMax = Integer.MAX_VALUE;378}379return (int)nameMax;380}381382@Override383public int compare(File f1, File f2) {384return f1.getPath().compareTo(f2.getPath());385}386387@Override388public int hashCode(File f) {389return f.getPath().hashCode() ^ 1234321;390}391392393private static native void initIDs();394395static {396initIDs();397}398}399400401