Path: blob/master/src/java.base/unix/classes/java/io/UnixFileSystem.java
41134 views
/*1* Copyright (c) 1998, 2021, 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 String resolve(File f) {145if (isAbsolute(f)) return f.getPath();146@SuppressWarnings("removal")147SecurityManager sm = System.getSecurityManager();148if (sm != null) {149sm.checkPropertyAccess("user.dir");150}151return resolve(userDir, f.getPath());152}153154// Caches for canonicalization results to improve startup performance.155// The first cache handles repeated canonicalizations of the same path156// name. The prefix cache handles repeated canonicalizations within the157// same directory, and must not create results differing from the true158// canonicalization algorithm in canonicalize_md.c. For this reason the159// prefix cache is conservative and is not used for complex path names.160private final ExpiringCache cache;161// On Unix symlinks can jump anywhere in the file system, so we only162// treat prefixes in java.home as trusted and cacheable in the163// canonicalization algorithm164private final ExpiringCache javaHomePrefixCache;165166@Override167public String canonicalize(String path) throws IOException {168if (!useCanonCaches) {169return canonicalize0(path);170} else {171String res = cache.get(path);172if (res == null) {173String dir = null;174String resDir;175if (useCanonPrefixCache) {176// Note that this can cause symlinks that should177// be resolved to a destination directory to be178// resolved to the directory they're contained in179dir = parentOrNull(path);180if (dir != null) {181resDir = javaHomePrefixCache.get(dir);182if (resDir != null) {183// Hit only in prefix cache; full path is canonical184String filename = path.substring(1 + dir.length());185res = resDir + slash + filename;186cache.put(dir + slash + filename, res);187}188}189}190if (res == null) {191res = canonicalize0(path);192cache.put(path, res);193if (useCanonPrefixCache &&194dir != null && dir.startsWith(javaHome)) {195resDir = parentOrNull(res);196// Note that we don't allow a resolved symlink197// to elsewhere in java.home to pollute the198// prefix cache (java.home prefix cache could199// just as easily be a set at this point)200if (resDir != null && resDir.equals(dir)) {201File f = new File(res);202if (f.exists() && !f.isDirectory()) {203javaHomePrefixCache.put(dir, resDir);204}205}206}207}208}209return res;210}211}212private native String canonicalize0(String path) throws IOException;213// Best-effort attempt to get parent of this path; used for214// optimization of filename canonicalization. This must return null for215// any cases where the code in canonicalize_md.c would throw an216// exception or otherwise deal with non-simple pathnames like handling217// of "." and "..". It may conservatively return null in other218// situations as well. Returning null will cause the underlying219// (expensive) canonicalization routine to be called.220static String parentOrNull(String path) {221if (path == null) return null;222char sep = File.separatorChar;223int last = path.length() - 1;224int idx = last;225int adjacentDots = 0;226int nonDotCount = 0;227while (idx > 0) {228char c = path.charAt(idx);229if (c == '.') {230if (++adjacentDots >= 2) {231// Punt on pathnames containing . and ..232return null;233}234} else if (c == sep) {235if (adjacentDots == 1 && nonDotCount == 0) {236// Punt on pathnames containing . and ..237return null;238}239if (idx == 0 ||240idx >= last - 1 ||241path.charAt(idx - 1) == sep) {242// Punt on pathnames containing adjacent slashes243// toward the end244return null;245}246return path.substring(0, idx);247} else {248++nonDotCount;249adjacentDots = 0;250}251--idx;252}253return null;254}255256/* -- Attribute accessors -- */257258public native int getBooleanAttributes0(File f);259260@Override261public int getBooleanAttributes(File f) {262int rv = getBooleanAttributes0(f);263return rv | isHidden(f);264}265266@Override267public boolean hasBooleanAttributes(File f, int attributes) {268int rv = getBooleanAttributes0(f);269if ((attributes & BA_HIDDEN) != 0) {270rv |= isHidden(f);271}272return (rv & attributes) == attributes;273}274275private static int isHidden(File f) {276return f.getName().startsWith(".") ? BA_HIDDEN : 0;277}278279@Override280public native boolean checkAccess(File f, int access);281282@Override283public native long getLastModifiedTime(File f);284285@Override286public native long getLength(File f);287288@Override289public native boolean setPermission(File f, int access, boolean enable, boolean owneronly);290291/* -- File operations -- */292293@Override294public native boolean createFileExclusively(String path)295throws IOException;296297@Override298public boolean delete(File f) {299// Keep canonicalization caches in sync after file deletion300// and renaming operations. Could be more clever than this301// (i.e., only remove/update affected entries) but probably302// not worth it since these entries expire after 30 seconds303// anyway.304if (useCanonCaches) {305cache.clear();306}307if (useCanonPrefixCache) {308javaHomePrefixCache.clear();309}310return delete0(f);311}312private native boolean delete0(File f);313314@Override315public native String[] list(File f);316317@Override318public native boolean createDirectory(File f);319320@Override321public boolean rename(File f1, File f2) {322// Keep canonicalization caches in sync after file deletion323// and renaming operations. Could be more clever than this324// (i.e., only remove/update affected entries) but probably325// not worth it since these entries expire after 30 seconds326// anyway.327if (useCanonCaches) {328cache.clear();329}330if (useCanonPrefixCache) {331javaHomePrefixCache.clear();332}333return rename0(f1, f2);334}335private native boolean rename0(File f1, File f2);336337@Override338public native boolean setLastModifiedTime(File f, long time);339340@Override341public native boolean setReadOnly(File f);342343/* -- Filesystem interface -- */344345@Override346public File[] listRoots() {347try {348@SuppressWarnings("removal")349SecurityManager security = System.getSecurityManager();350if (security != null) {351security.checkRead("/");352}353return new File[] { new File("/") };354} catch (SecurityException x) {355return new File[0];356}357}358359/* -- Disk usage -- */360361@Override362public native long getSpace(File f, int t);363364/* -- Basic infrastructure -- */365366private native long getNameMax0(String path);367368@Override369public int getNameMax(String path) {370long nameMax = getNameMax0(path);371if (nameMax > Integer.MAX_VALUE) {372nameMax = Integer.MAX_VALUE;373}374return (int)nameMax;375}376377@Override378public int compare(File f1, File f2) {379return f1.getPath().compareTo(f2.getPath());380}381382@Override383public int hashCode(File f) {384return f.getPath().hashCode() ^ 1234321;385}386387388private static native void initIDs();389390static {391initIDs();392}393}394395396