KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > io > UnixFileSystem


1 /*
2  * @(#)UnixFileSystem.java 1.17 04/01/20
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.io;
9
10 import java.security.AccessController;
11 import sun.security.action.GetPropertyAction;
12
13
14 class UnixFileSystem extends FileSystem {
15
16     private final char slash;
17     private final char colon;
18     private final String javaHome;
19
20     public UnixFileSystem() {
21         slash =
22         ((String) AccessController.doPrivileged(
23               new GetPropertyAction("file.separator"))).charAt(0);
24     colon =
25         ((String) AccessController.doPrivileged(
26               new GetPropertyAction("path.separator"))).charAt(0);
27     javaHome =
28         (String) AccessController.doPrivileged(
29               new GetPropertyAction("java.home"));
30     }
31
32
33     /* -- Normalization and construction -- */
34
35     public char getSeparator() {
36     return slash;
37     }
38
39     public char getPathSeparator() {
40     return colon;
41     }
42
43     /* A normal Unix pathname contains no duplicate slashes and does not end
44        with a slash. It may be the empty string. */

45
46     /* Normalize the given pathname, whose length is len, starting at the given
47        offset; everything before this offset is already normal. */

48     private String normalize(String pathname, int len, int off) {
49     if (len == 0) return pathname;
50     int n = len;
51     while ((n > 0) && (pathname.charAt(n - 1) == '/')) n--;
52     if (n == 0) return "/";
53     StringBuffer sb = new StringBuffer(pathname.length());
54     if (off > 0) sb.append(pathname.substring(0, off));
55     char prevChar = 0;
56     for (int i = off; i < n; i++) {
57         char c = pathname.charAt(i);
58         if ((prevChar == '/') && (c == '/')) continue;
59         sb.append(c);
60         prevChar = c;
61     }
62     return sb.toString();
63     }
64
65     /* Check that the given pathname is normal. If not, invoke the real
66        normalizer on the part of the pathname that requires normalization.
67        This way we iterate through the whole pathname string only once. */

68     public String normalize(String pathname) {
69     int n = pathname.length();
70     char prevChar = 0;
71     for (int i = 0; i < n; i++) {
72         char c = pathname.charAt(i);
73         if ((prevChar == '/') && (c == '/'))
74         return normalize(pathname, n, i - 1);
75         prevChar = c;
76     }
77     if (prevChar == '/') return normalize(pathname, n, n - 1);
78     return pathname;
79     }
80
81     public int prefixLength(String pathname) {
82     if (pathname.length() == 0) return 0;
83     return (pathname.charAt(0) == '/') ? 1 : 0;
84     }
85
86     public String resolve(String parent, String child) {
87     if (child.equals("")) return parent;
88     if (child.charAt(0) == '/') {
89         if (parent.equals("/")) return child;
90         return parent + child;
91     }
92     if (parent.equals("/")) return parent + child;
93     return parent + '/' + child;
94     }
95
96     public String getDefaultParent() {
97     return "/";
98     }
99
100     public String fromURIPath(String path) {
101     String p = path;
102     if (p.endsWith("/") && (p.length() > 1)) {
103         // "/foo/" --> "/foo", but "/" --> "/"
104
p = p.substring(0, p.length() - 1);
105     }
106     return p;
107     }
108
109
110     /* -- Path operations -- */
111
112     public boolean isAbsolute(File f) {
113     return (f.getPrefixLength() != 0);
114     }
115
116     public String resolve(File f) {
117     if (isAbsolute(f)) return f.getPath();
118     return resolve(System.getProperty("user.dir"), f.getPath());
119     }
120
121     // Caches for canonicalization results to improve startup performance.
122
// The first cache handles repeated canonicalizations of the same path
123
// name. The prefix cache handles repeated canonicalizations within the
124
// same directory, and must not create results differing from the true
125
// canonicalization algorithm in canonicalize_md.c. For this reason the
126
// prefix cache is conservative and is not used for complex path names.
127
private ExpiringCache cache = new ExpiringCache();
128     // On Unix symlinks can jump anywhere in the file system, so we only
129
// treat prefixes in java.home as trusted and cacheable in the
130
// canonicalization algorithm
131
private ExpiringCache javaHomePrefixCache = new ExpiringCache();
132
133     public String canonicalize(String path) throws IOException {
134         if (!useCanonCaches) {
135             return canonicalize0(path);
136         } else {
137             String res = cache.get(path);
138             if (res == null) {
139         String dir = null;
140         String resDir = null;
141                 if (useCanonPrefixCache) {
142                     // Note that this can cause symlinks that should
143
// be resolved to a destination directory to be
144
// resolved to the directory they're contained in
145
dir = parentOrNull(path);
146                     if (dir != null) {
147                         resDir = javaHomePrefixCache.get(dir);
148                         if (resDir != null) {
149                             // Hit only in prefix cache; full path is canonical
150
String filename = path.substring(1 + dir.length());
151                             res = resDir + slash + filename;
152                             cache.put(dir + slash + filename, res);
153                         }
154                     }
155                 }
156                 if (res == null) {
157                     res = canonicalize0(path);
158                     cache.put(path, res);
159                     if (useCanonPrefixCache &&
160             dir != null && dir.startsWith(javaHome)) {
161                         resDir = parentOrNull(res);
162                         // Note that we don't allow a resolved symlink
163
// to elsewhere in java.home to pollute the
164
// prefix cache (java.home prefix cache could
165
// just as easily be a set at this point)
166
if (resDir != null && resDir.equals(dir)) {
167                             File f = new File(res);
168                             if (f.exists() && !f.isDirectory()) {
169                                 javaHomePrefixCache.put(dir, resDir);
170                             }
171                         }
172                     }
173                 }
174             }
175             assert canonicalize0(path).equals(res) || path.startsWith(javaHome);
176             return res;
177         }
178     }
179     private native String canonicalize0(String path) throws IOException;
180     // Best-effort attempt to get parent of this path; used for
181
// optimization of filename canonicalization. This must return null for
182
// any cases where the code in canonicalize_md.c would throw an
183
// exception or otherwise deal with non-simple pathnames like handling
184
// of "." and "..". It may conservatively return null in other
185
// situations as well. Returning null will cause the underlying
186
// (expensive) canonicalization routine to be called.
187
static String parentOrNull(String path) {
188         if (path == null) return null;
189         char sep = File.separatorChar;
190         int last = path.length() - 1;
191         int idx = last;
192         int adjacentDots = 0;
193         int nonDotCount = 0;
194         while (idx > 0) {
195             char c = path.charAt(idx);
196             if (c == '.') {
197                 if (++adjacentDots >= 2) {
198                     // Punt on pathnames containing . and ..
199
return null;
200                 }
201             } else if (c == sep) {
202                 if (adjacentDots == 1 && nonDotCount == 0) {
203                     // Punt on pathnames containing . and ..
204
return null;
205                 }
206                 if (idx == 0 ||
207                     idx >= last - 1 ||
208                     path.charAt(idx - 1) == sep) {
209                     // Punt on pathnames containing adjacent slashes
210
// toward the end
211
return null;
212                 }
213                 return path.substring(0, idx);
214             } else {
215                 ++nonDotCount;
216                 adjacentDots = 0;
217             }
218             --idx;
219         }
220         return null;
221     }
222
223     /* -- Attribute accessors -- */
224
225     public native int getBooleanAttributes0(File f);
226
227     public int getBooleanAttributes(File f) {
228     int rv = getBooleanAttributes0(f);
229     String name = f.getName();
230     boolean hidden = (name.length() > 0) && (name.charAt(0) == '.');
231     return rv | (hidden ? BA_HIDDEN : 0);
232     }
233
234     public native boolean checkAccess(File f, boolean write);
235     public native long getLastModifiedTime(File f);
236     public native long getLength(File f);
237
238
239     /* -- File operations -- */
240
241     public native boolean createFileExclusively(String path)
242     throws IOException;
243     public boolean delete(File f) {
244         // Keep canonicalization caches in sync after file deletion
245
// and renaming operations. Could be more clever than this
246
// (i.e., only remove/update affected entries) but probably
247
// not worth it since these entries expire after 30 seconds
248
// anyway.
249
cache.clear();
250         javaHomePrefixCache.clear();
251         return delete0(f);
252     }
253     private native boolean delete0(File f);
254     public synchronized native boolean deleteOnExit(File f);
255     public native String[] list(File f);
256     public native boolean createDirectory(File f);
257     public boolean rename(File f1, File f2) {
258         // Keep canonicalization caches in sync after file deletion
259
// and renaming operations. Could be more clever than this
260
// (i.e., only remove/update affected entries) but probably
261
// not worth it since these entries expire after 30 seconds
262
// anyway.
263
cache.clear();
264         javaHomePrefixCache.clear();
265         return rename0(f1, f2);
266     }
267     private native boolean rename0(File f1, File f2);
268     public native boolean setLastModifiedTime(File f, long time);
269     public native boolean setReadOnly(File f);
270
271
272     /* -- Filesystem interface -- */
273
274     public File[] listRoots() {
275     try {
276         SecurityManager security = System.getSecurityManager();
277         if (security != null) {
278         security.checkRead("/");
279         }
280         return new File[] { new File("/") };
281     } catch (SecurityException x) {
282         return new File[0];
283     }
284     }
285
286
287     /* -- Basic infrastructure -- */
288
289     public int compare(File f1, File f2) {
290     return f1.getPath().compareTo(f2.getPath());
291     }
292
293     public int hashCode(File f) {
294     return f.getPath().hashCode() ^ 1234321;
295     }
296
297     
298     private static native void initIDs();
299
300     static {
301     initIDs();
302     }
303
304 }
305
Popular Tags