1 /* 2 * Copyright (c) 1998, 2023, 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 26 package java.io; 27 28 import java.util.Properties; 29 import jdk.internal.util.StaticProperty; 30 import sun.security.action.GetPropertyAction; 31 32 final class UnixFileSystem extends FileSystem { 33 34 private final char slash; 35 private final char colon; 36 private final String userDir; 37 38 UnixFileSystem() { 39 Properties props = GetPropertyAction.privilegedGetProperties(); 40 slash = props.getProperty("file.separator").charAt(0); 41 colon = props.getProperty("path.separator").charAt(0); 42 userDir = StaticProperty.userDir(); 43 } 44 45 /* -- Normalization and construction -- */ 46 47 @Override 48 public char getSeparator() { 49 return slash; 50 } 51 52 @Override 53 public char getPathSeparator() { 54 return colon; 55 } 56 57 /* A normal Unix pathname contains no duplicate slashes and does not end 58 with a slash. It may be the empty string. */ 59 60 /** 61 * Normalize the given pathname, starting at the given 62 * offset; everything before off is already normal, and there's at least 63 * one duplicate or trailing slash to be removed 64 */ 65 private String normalize(String pathname, int off) { 66 int n = pathname.length(); 67 while ((n > off) && (pathname.charAt(n - 1) == '/')) n--; 68 if (n == 0) return "/"; 69 if (n == off) return pathname.substring(0, off); 70 71 StringBuilder sb = new StringBuilder(n); 72 if (off > 0) sb.append(pathname, 0, off); 73 char prevChar = 0; 74 for (int i = off; i < n; i++) { 75 char c = pathname.charAt(i); 76 if ((prevChar == '/') && (c == '/')) continue; 77 sb.append(c); 78 prevChar = c; 79 } 80 return sb.toString(); 81 } 82 83 /* Check that the given pathname is normal. If not, invoke the real 84 normalizer on the part of the pathname that requires normalization. 85 This way we iterate through the whole pathname string only once. */ 86 @Override 87 public String normalize(String pathname) { 88 int doubleSlash = pathname.indexOf("//"); 89 if (doubleSlash >= 0) { 90 return normalize(pathname, doubleSlash); 91 } 92 if (pathname.endsWith("/")) { 93 return normalize(pathname, pathname.length() - 1); 94 } 95 return pathname; 96 } 97 98 @Override 99 public int prefixLength(String pathname) { 100 return pathname.startsWith("/") ? 1 : 0; 101 } 102 103 // Remove trailing '/' if present and there are at least two characters 104 private static String trimSeparator(String s) { 105 int len = s.length(); 106 if (len > 1 && s.charAt(len - 1) == '/') 107 return s.substring(0, len - 1); 108 return s; 109 } 110 111 @Override 112 public String resolve(String parent, String child) { 113 if (child.isEmpty()) return parent; 114 if (child.charAt(0) == '/') { 115 if (parent.equals("/")) return child; 116 return trimSeparator(parent + child); 117 } 118 if (parent.equals("/")) return trimSeparator(parent + child); 119 return trimSeparator(parent + '/' + child); 120 } 121 122 @Override 123 public String getDefaultParent() { 124 return "/"; 125 } 126 127 @Override 128 public String fromURIPath(String path) { 129 String p = path; 130 if (p.endsWith("/") && (p.length() > 1)) { 131 // "/foo/" --> "/foo", but "/" --> "/" 132 p = p.substring(0, p.length() - 1); 133 } 134 return p; 135 } 136 137 138 /* -- Path operations -- */ 139 140 @Override 141 public boolean isAbsolute(File f) { 142 return (f.getPrefixLength() != 0); 143 } 144 145 @Override 146 public boolean isInvalid(File f) { 147 return f.getPath().indexOf('\u0000') >= 0; 148 } 149 150 @Override 151 public String resolve(File f) { 152 if (isAbsolute(f)) return f.getPath(); 153 @SuppressWarnings("removal") 154 SecurityManager sm = System.getSecurityManager(); 155 if (sm != null) { 156 sm.checkPropertyAccess("user.dir"); 157 } 158 return resolve(userDir, f.getPath()); 159 } 160 161 @Override 162 public String canonicalize(String path) throws IOException { 163 return canonicalize0(path); 164 } 165 private native String canonicalize0(String path) throws IOException; 166 167 /* -- Attribute accessors -- */ 168 169 private native int getBooleanAttributes0(File f); 170 171 @Override 172 public int getBooleanAttributes(File f) { 173 int rv = getBooleanAttributes0(f); 174 return rv | isHidden(f); 175 } 176 177 @Override 178 public boolean hasBooleanAttributes(File f, int attributes) { 179 int rv = getBooleanAttributes0(f); 180 if ((attributes & BA_HIDDEN) != 0) { 181 rv |= isHidden(f); 182 } 183 return (rv & attributes) == attributes; 184 } 185 186 private static int isHidden(File f) { 187 return f.getName().startsWith(".") ? BA_HIDDEN : 0; 188 } 189 190 @Override 191 public boolean checkAccess(File f, int access) { 192 return checkAccess0(f, access); 193 } 194 private native boolean checkAccess0(File f, int access); 195 196 @Override 197 public long getLastModifiedTime(File f) { 198 return getLastModifiedTime0(f); 199 } 200 private native long getLastModifiedTime0(File f); 201 202 @Override 203 public long getLength(File f) { 204 return getLength0(f); 205 } 206 private native long getLength0(File f); 207 208 @Override 209 public boolean setPermission(File f, int access, boolean enable, boolean owneronly) { 210 return setPermission0(f, access, enable, owneronly); 211 } 212 private native boolean setPermission0(File f, int access, boolean enable, boolean owneronly); 213 214 /* -- File operations -- */ 215 216 @Override 217 public boolean createFileExclusively(String path) throws IOException { 218 return createFileExclusively0(path); 219 } 220 private native boolean createFileExclusively0(String path) throws IOException; 221 222 @Override 223 public boolean delete(File f) { 224 return delete0(f); 225 } 226 private native boolean delete0(File f); 227 228 @Override 229 public String[] list(File f) { 230 return list0(f); 231 } 232 private native String[] list0(File f); 233 234 @Override 235 public boolean createDirectory(File f) { 236 return createDirectory0(f); 237 } 238 private native boolean createDirectory0(File f); 239 240 @Override 241 public boolean rename(File f1, File f2) { 242 return rename0(f1, f2); 243 } 244 private native boolean rename0(File f1, File f2); 245 246 @Override 247 public boolean setLastModifiedTime(File f, long time) { 248 return setLastModifiedTime0(f, time); 249 } 250 private native boolean setLastModifiedTime0(File f, long time); 251 252 @Override 253 public boolean setReadOnly(File f) { 254 return setReadOnly0(f); 255 } 256 private native boolean setReadOnly0(File f); 257 258 /* -- Filesystem interface -- */ 259 260 @Override 261 public File[] listRoots() { 262 try { 263 @SuppressWarnings("removal") 264 SecurityManager security = System.getSecurityManager(); 265 if (security != null) { 266 security.checkRead("/"); 267 } 268 return new File[] { new File("/") }; 269 } catch (SecurityException x) { 270 return new File[0]; 271 } 272 } 273 274 /* -- Disk usage -- */ 275 276 @Override 277 public long getSpace(File f, int t) { 278 return getSpace0(f, t); 279 } 280 private native long getSpace0(File f, int t); 281 282 /* -- Basic infrastructure -- */ 283 284 private native long getNameMax0(String path); 285 286 @Override 287 public int getNameMax(String path) { 288 long nameMax = getNameMax0(path); 289 if (nameMax > Integer.MAX_VALUE) { 290 nameMax = Integer.MAX_VALUE; 291 } 292 return (int)nameMax; 293 } 294 295 @Override 296 public int compare(File f1, File f2) { 297 return f1.getPath().compareTo(f2.getPath()); 298 } 299 300 @Override 301 public int hashCode(File f) { 302 return f.getPath().hashCode() ^ 1234321; 303 } 304 305 private static native void initIDs(); 306 307 static { 308 initIDs(); 309 } 310 }