1 /*
2  * Copyright (c) 2008, 2013, 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 sun.nio.fs;
27 
28 import java.nio.file.*;
29 import java.nio.file.attribute.*;
30 import java.nio.file.spi.*;
31 import java.io.IOException;
32 import java.util.*;
33 import java.util.regex.Pattern;
34 import java.security.AccessController;
35 import sun.security.action.GetPropertyAction;
36 
37 /**
38  * Base implementation of FileSystem for Unix-like implementations.
39  */
40 
41 abstract class UnixFileSystem
42     extends FileSystem
43 {
44     private final UnixFileSystemProvider provider;
45     private final byte[] defaultDirectory;
46     private final boolean needToResolveAgainstDefaultDirectory;
47     private final UnixPath rootDirectory;
48 
49     // package-private
UnixFileSystem(UnixFileSystemProvider provider, String dir)50     UnixFileSystem(UnixFileSystemProvider provider, String dir) {
51         this.provider = provider;
52         this.defaultDirectory = Util.toBytes(UnixPath.normalizeAndCheck(dir));
53         if (this.defaultDirectory[0] != '/') {
54             throw new RuntimeException("default directory must be absolute");
55         }
56 
57         // if process-wide chdir is allowed or default directory is not the
58         // process working directory then paths must be resolved against the
59         // default directory.
60         String propValue = AccessController.doPrivileged(
61             new GetPropertyAction("sun.nio.fs.chdirAllowed", "false"));
62         boolean chdirAllowed = (propValue.length() == 0) ?
63             true : Boolean.valueOf(propValue);
64         if (chdirAllowed) {
65             this.needToResolveAgainstDefaultDirectory = true;
66         } else {
67             byte[] cwd = UnixNativeDispatcher.getcwd();
68             boolean defaultIsCwd = (cwd.length == defaultDirectory.length);
69             if (defaultIsCwd) {
70                 for (int i=0; i<cwd.length; i++) {
71                     if (cwd[i] != defaultDirectory[i]) {
72                         defaultIsCwd = false;
73                         break;
74                     }
75                 }
76             }
77             this.needToResolveAgainstDefaultDirectory = !defaultIsCwd;
78         }
79 
80         // the root directory
81         this.rootDirectory = new UnixPath(this, "/");
82     }
83 
84     // package-private
defaultDirectory()85     byte[] defaultDirectory() {
86         return defaultDirectory;
87     }
88 
needToResolveAgainstDefaultDirectory()89     boolean needToResolveAgainstDefaultDirectory() {
90         return needToResolveAgainstDefaultDirectory;
91     }
92 
rootDirectory()93     UnixPath rootDirectory() {
94         return rootDirectory;
95     }
96 
isSolaris()97     boolean isSolaris() {
98         return false;
99     }
100 
standardFileAttributeViews()101     static List<String> standardFileAttributeViews() {
102         return Arrays.asList("basic", "posix", "unix", "owner");
103     }
104 
105     @Override
provider()106     public final FileSystemProvider provider() {
107         return provider;
108     }
109 
110     @Override
getSeparator()111     public final String getSeparator() {
112         return "/";
113     }
114 
115     @Override
isOpen()116     public final boolean isOpen() {
117         return true;
118     }
119 
120     @Override
isReadOnly()121     public final boolean isReadOnly() {
122         return false;
123     }
124 
125     @Override
close()126     public final void close() throws IOException {
127         throw new UnsupportedOperationException();
128     }
129 
130     /**
131      * Copies non-POSIX attributes from the source to target file.
132      *
133      * Copying a file preserving attributes, or moving a file, will preserve
134      * the file owner/group/permissions/timestamps but it does not preserve
135      * other non-POSIX attributes. This method is invoked by the
136      * copy or move operation to preserve these attributes. It should copy
137      * extended attributes, ACLs, or other attributes.
138      *
139      * @param   sfd
140      *          Open file descriptor to source file
141      * @param   tfd
142      *          Open file descriptor to target file
143      */
copyNonPosixAttributes(int sfd, int tfd)144     void copyNonPosixAttributes(int sfd, int tfd) {
145         // no-op by default
146     }
147 
148     /**
149      * Unix systems only have a single root directory (/)
150      */
151     @Override
getRootDirectories()152     public final Iterable<Path> getRootDirectories() {
153         final List<Path> allowedList =
154            Collections.unmodifiableList(Arrays.asList((Path)rootDirectory));
155         return new Iterable<Path>() {
156             public Iterator<Path> iterator() {
157                 try {
158                     SecurityManager sm = System.getSecurityManager();
159                     if (sm != null)
160                         sm.checkRead(rootDirectory.toString());
161                     return allowedList.iterator();
162                 } catch (SecurityException x) {
163                     List<Path> disallowed = Collections.emptyList();
164                     return disallowed.iterator();
165                 }
166             }
167         };
168     }
169 
170     /**
171      * Returns object to iterate over entries in mounttab or equivalent
172      */
173     abstract Iterable<UnixMountEntry> getMountEntries();
174 
175     /**
176      * Returns a FileStore to represent the file system for the given mount
177      * mount.
178      */
179     abstract FileStore getFileStore(UnixMountEntry entry) throws IOException;
180 
181     /**
182      * Iterator returned by getFileStores method.
183      */
184     private class FileStoreIterator implements Iterator<FileStore> {
185         private final Iterator<UnixMountEntry> entries;
186         private FileStore next;
187 
188         FileStoreIterator() {
189             this.entries = getMountEntries().iterator();
190         }
191 
192         private FileStore readNext() {
193             assert Thread.holdsLock(this);
194             for (;;) {
195                 if (!entries.hasNext())
196                     return null;
197                 UnixMountEntry entry = entries.next();
198 
199                 // skip entries with the "ignore" option
200                 if (entry.isIgnored())
201                     continue;
202 
203                 // check permission to read mount point
204                 SecurityManager sm = System.getSecurityManager();
205                 if (sm != null) {
206                     try {
207                         sm.checkRead(Util.toString(entry.dir()));
208                     } catch (SecurityException x) {
209                         continue;
210                     }
211                 }
212                 try {
213                     return getFileStore(entry);
214                 } catch (IOException ignore) {
215                     // ignore as per spec
216                 }
217             }
218         }
219 
220         @Override
221         public synchronized boolean hasNext() {
222             if (next != null)
223                 return true;
224             next = readNext();
225             return next != null;
226         }
227 
228         @Override
229         public synchronized FileStore next() {
230             if (next == null)
231                 next = readNext();
232             if (next == null) {
233                 throw new NoSuchElementException();
234             } else {
235                 FileStore result = next;
236                 next = null;
237                 return result;
238             }
239         }
240 
241         @Override
242         public void remove() {
243             throw new UnsupportedOperationException();
244         }
245     }
246 
247     @Override
248     public final Iterable<FileStore> getFileStores() {
249         SecurityManager sm = System.getSecurityManager();
250         if (sm != null) {
251             try {
252                 sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
253             } catch (SecurityException se) {
254                 return Collections.emptyList();
255             }
256         }
257         return new Iterable<FileStore>() {
258             public Iterator<FileStore> iterator() {
259                 return new FileStoreIterator();
260             }
261         };
262     }
263 
264     @Override
265     public final Path getPath(String first, String... more) {
266         String path;
267         if (more.length == 0) {
268             path = first;
269         } else {
270             StringBuilder sb = new StringBuilder();
271             sb.append(first);
272             for (String segment: more) {
273                 if (segment.length() > 0) {
274                     if (sb.length() > 0)
275                         sb.append('/');
276                     sb.append(segment);
277                 }
278             }
279             path = sb.toString();
280         }
281         return new UnixPath(this, path);
282     }
283 
284     @Override
285     public PathMatcher getPathMatcher(String syntaxAndInput) {
286         int pos = syntaxAndInput.indexOf(':');
287         if (pos <= 0 || pos == syntaxAndInput.length())
288             throw new IllegalArgumentException();
289         String syntax = syntaxAndInput.substring(0, pos);
290         String input = syntaxAndInput.substring(pos+1);
291 
292         String expr;
293         if (syntax.equals(GLOB_SYNTAX)) {
294             expr = Globs.toUnixRegexPattern(input);
295         } else {
296             if (syntax.equals(REGEX_SYNTAX)) {
297                 expr = input;
298             } else {
299                 throw new UnsupportedOperationException("Syntax '" + syntax +
300                     "' not recognized");
301             }
302         }
303 
304         // return matcher
305         final Pattern pattern = compilePathMatchPattern(expr);
306 
307         return new PathMatcher() {
308             @Override
309             public boolean matches(Path path) {
310                 return pattern.matcher(path.toString()).matches();
311             }
312         };
313     }
314 
315     private static final String GLOB_SYNTAX = "glob";
316     private static final String REGEX_SYNTAX = "regex";
317 
318     @Override
319     public final UserPrincipalLookupService getUserPrincipalLookupService() {
320         return LookupService.instance;
321     }
322 
323     private static class LookupService {
324         static final UserPrincipalLookupService instance =
325             new UserPrincipalLookupService() {
326                 @Override
327                 public UserPrincipal lookupPrincipalByName(String name)
328                     throws IOException
329                 {
330                     return UnixUserPrincipals.lookupUser(name);
331                 }
332 
333                 @Override
334                 public GroupPrincipal lookupPrincipalByGroupName(String group)
335                     throws IOException
336                 {
337                     return UnixUserPrincipals.lookupGroup(group);
338                 }
339             };
340     }
341 
342     // Override if the platform has different path match requrement, such as
343     // case insensitive or Unicode canonical equal on MacOSX
344     Pattern compilePathMatchPattern(String expr) {
345         return Pattern.compile(expr);
346     }
347 
348     // Override if the platform uses different Unicode normalization form
349     // for native file path. For example on MacOSX, the native path is stored
350     // in Unicode NFD form.
351     char[] normalizeNativePath(char[] path) {
352         return path;
353     }
354 
355     // Override if the native file path use non-NFC form. For example on MacOSX,
356     // the native path is stored in Unicode NFD form, the path need to be
357     // normalized back to NFC before passed back to Java level.
358     String normalizeJavaPath(String path) {
359         return path;
360     }
361 }
362