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 }