blob: 4d851a97f0988995c8787863637f79918bb1643f [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-2005 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package java.io;
27
28import java.security.AccessController;
29import sun.security.action.GetPropertyAction;
30
31
32class UnixFileSystem extends FileSystem {
33
34 private final char slash;
35 private final char colon;
36 private final String javaHome;
37
38 public UnixFileSystem() {
39 slash = AccessController.doPrivileged(
40 new GetPropertyAction("file.separator")).charAt(0);
41 colon = AccessController.doPrivileged(
42 new GetPropertyAction("path.separator")).charAt(0);
43 javaHome = AccessController.doPrivileged(
44 new GetPropertyAction("java.home"));
45 }
46
47
48 /* -- Normalization and construction -- */
49
50 public char getSeparator() {
51 return slash;
52 }
53
54 public char getPathSeparator() {
55 return colon;
56 }
57
58 /* A normal Unix pathname contains no duplicate slashes and does not end
59 with a slash. It may be the empty string. */
60
61 /* Normalize the given pathname, whose length is len, starting at the given
62 offset; everything before this offset is already normal. */
63 private String normalize(String pathname, int len, int off) {
64 if (len == 0) return pathname;
65 int n = len;
66 while ((n > 0) && (pathname.charAt(n - 1) == '/')) n--;
67 if (n == 0) return "/";
68 StringBuffer sb = new StringBuffer(pathname.length());
69 if (off > 0) sb.append(pathname.substring(0, off));
70 char prevChar = 0;
71 for (int i = off; i < n; i++) {
72 char c = pathname.charAt(i);
73 if ((prevChar == '/') && (c == '/')) continue;
74 sb.append(c);
75 prevChar = c;
76 }
77 return sb.toString();
78 }
79
80 /* Check that the given pathname is normal. If not, invoke the real
81 normalizer on the part of the pathname that requires normalization.
82 This way we iterate through the whole pathname string only once. */
83 public String normalize(String pathname) {
84 int n = pathname.length();
85 char prevChar = 0;
86 for (int i = 0; i < n; i++) {
87 char c = pathname.charAt(i);
88 if ((prevChar == '/') && (c == '/'))
89 return normalize(pathname, n, i - 1);
90 prevChar = c;
91 }
92 if (prevChar == '/') return normalize(pathname, n, n - 1);
93 return pathname;
94 }
95
96 public int prefixLength(String pathname) {
97 if (pathname.length() == 0) return 0;
98 return (pathname.charAt(0) == '/') ? 1 : 0;
99 }
100
101 public String resolve(String parent, String child) {
102 if (child.equals("")) return parent;
103 if (child.charAt(0) == '/') {
104 if (parent.equals("/")) return child;
105 return parent + child;
106 }
107 if (parent.equals("/")) return parent + child;
108 return parent + '/' + child;
109 }
110
111 public String getDefaultParent() {
112 return "/";
113 }
114
115 public String fromURIPath(String path) {
116 String p = path;
117 if (p.endsWith("/") && (p.length() > 1)) {
118 // "/foo/" --> "/foo", but "/" --> "/"
119 p = p.substring(0, p.length() - 1);
120 }
121 return p;
122 }
123
124
125 /* -- Path operations -- */
126
127 public boolean isAbsolute(File f) {
128 return (f.getPrefixLength() != 0);
129 }
130
131 public String resolve(File f) {
132 if (isAbsolute(f)) return f.getPath();
133 return resolve(System.getProperty("user.dir"), f.getPath());
134 }
135
136 // Caches for canonicalization results to improve startup performance.
137 // The first cache handles repeated canonicalizations of the same path
138 // name. The prefix cache handles repeated canonicalizations within the
139 // same directory, and must not create results differing from the true
140 // canonicalization algorithm in canonicalize_md.c. For this reason the
141 // prefix cache is conservative and is not used for complex path names.
142 private ExpiringCache cache = new ExpiringCache();
143 // On Unix symlinks can jump anywhere in the file system, so we only
144 // treat prefixes in java.home as trusted and cacheable in the
145 // canonicalization algorithm
146 private ExpiringCache javaHomePrefixCache = new ExpiringCache();
147
148 public String canonicalize(String path) throws IOException {
149 if (!useCanonCaches) {
150 return canonicalize0(path);
151 } else {
152 String res = cache.get(path);
153 if (res == null) {
154 String dir = null;
155 String resDir = null;
156 if (useCanonPrefixCache) {
157 // Note that this can cause symlinks that should
158 // be resolved to a destination directory to be
159 // resolved to the directory they're contained in
160 dir = parentOrNull(path);
161 if (dir != null) {
162 resDir = javaHomePrefixCache.get(dir);
163 if (resDir != null) {
164 // Hit only in prefix cache; full path is canonical
165 String filename = path.substring(1 + dir.length());
166 res = resDir + slash + filename;
167 cache.put(dir + slash + filename, res);
168 }
169 }
170 }
171 if (res == null) {
172 res = canonicalize0(path);
173 cache.put(path, res);
174 if (useCanonPrefixCache &&
175 dir != null && dir.startsWith(javaHome)) {
176 resDir = parentOrNull(res);
177 // Note that we don't allow a resolved symlink
178 // to elsewhere in java.home to pollute the
179 // prefix cache (java.home prefix cache could
180 // just as easily be a set at this point)
181 if (resDir != null && resDir.equals(dir)) {
182 File f = new File(res);
183 if (f.exists() && !f.isDirectory()) {
184 javaHomePrefixCache.put(dir, resDir);
185 }
186 }
187 }
188 }
189 }
190 assert canonicalize0(path).equals(res) || path.startsWith(javaHome);
191 return res;
192 }
193 }
194 private native String canonicalize0(String path) throws IOException;
195 // Best-effort attempt to get parent of this path; used for
196 // optimization of filename canonicalization. This must return null for
197 // any cases where the code in canonicalize_md.c would throw an
198 // exception or otherwise deal with non-simple pathnames like handling
199 // of "." and "..". It may conservatively return null in other
200 // situations as well. Returning null will cause the underlying
201 // (expensive) canonicalization routine to be called.
202 static String parentOrNull(String path) {
203 if (path == null) return null;
204 char sep = File.separatorChar;
205 int last = path.length() - 1;
206 int idx = last;
207 int adjacentDots = 0;
208 int nonDotCount = 0;
209 while (idx > 0) {
210 char c = path.charAt(idx);
211 if (c == '.') {
212 if (++adjacentDots >= 2) {
213 // Punt on pathnames containing . and ..
214 return null;
215 }
216 } else if (c == sep) {
217 if (adjacentDots == 1 && nonDotCount == 0) {
218 // Punt on pathnames containing . and ..
219 return null;
220 }
221 if (idx == 0 ||
222 idx >= last - 1 ||
223 path.charAt(idx - 1) == sep) {
224 // Punt on pathnames containing adjacent slashes
225 // toward the end
226 return null;
227 }
228 return path.substring(0, idx);
229 } else {
230 ++nonDotCount;
231 adjacentDots = 0;
232 }
233 --idx;
234 }
235 return null;
236 }
237
238 /* -- Attribute accessors -- */
239
240 public native int getBooleanAttributes0(File f);
241
242 public int getBooleanAttributes(File f) {
243 int rv = getBooleanAttributes0(f);
244 String name = f.getName();
245 boolean hidden = (name.length() > 0) && (name.charAt(0) == '.');
246 return rv | (hidden ? BA_HIDDEN : 0);
247 }
248
249 public native boolean checkAccess(File f, int access);
250 public native long getLastModifiedTime(File f);
251 public native long getLength(File f);
252 public native boolean setPermission(File f, int access, boolean enable, boolean owneronly);
253
254 /* -- File operations -- */
255
256 public native boolean createFileExclusively(String path)
257 throws IOException;
258 public boolean delete(File f) {
259 // Keep canonicalization caches in sync after file deletion
260 // and renaming operations. Could be more clever than this
261 // (i.e., only remove/update affected entries) but probably
262 // not worth it since these entries expire after 30 seconds
263 // anyway.
264 cache.clear();
265 javaHomePrefixCache.clear();
266 return delete0(f);
267 }
268 private native boolean delete0(File f);
269 public native String[] list(File f);
270 public native boolean createDirectory(File f);
271 public boolean rename(File f1, File f2) {
272 // Keep canonicalization caches in sync after file deletion
273 // and renaming operations. Could be more clever than this
274 // (i.e., only remove/update affected entries) but probably
275 // not worth it since these entries expire after 30 seconds
276 // anyway.
277 cache.clear();
278 javaHomePrefixCache.clear();
279 return rename0(f1, f2);
280 }
281 private native boolean rename0(File f1, File f2);
282 public native boolean setLastModifiedTime(File f, long time);
283 public native boolean setReadOnly(File f);
284
285
286 /* -- Filesystem interface -- */
287
288 public File[] listRoots() {
289 try {
290 SecurityManager security = System.getSecurityManager();
291 if (security != null) {
292 security.checkRead("/");
293 }
294 return new File[] { new File("/") };
295 } catch (SecurityException x) {
296 return new File[0];
297 }
298 }
299
300 /* -- Disk usage -- */
301 public native long getSpace(File f, int t);
302
303 /* -- Basic infrastructure -- */
304
305 public int compare(File f1, File f2) {
306 return f1.getPath().compareTo(f2.getPath());
307 }
308
309 public int hashCode(File f) {
310 return f.getPath().hashCode() ^ 1234321;
311 }
312
313
314 private static native void initIDs();
315
316 static {
317 initIDs();
318 }
319
320}