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.FileTypeDetector;
31 import java.nio.channels.*;
32 import java.net.URI;
33 import java.util.concurrent.ExecutorService;
34 import java.io.IOException;
35 import java.io.FilePermission;
36 import java.util.*;
37 import java.security.AccessController;
38 
39 import sun.nio.ch.ThreadPool;
40 import sun.security.util.SecurityConstants;
41 import static sun.nio.fs.UnixNativeDispatcher.*;
42 import static sun.nio.fs.UnixConstants.*;
43 
44 /**
45  * Base implementation of FileSystemProvider
46  */
47 
48 public abstract class UnixFileSystemProvider
49     extends AbstractFileSystemProvider
50 {
51     private static final String USER_DIR = "user.dir";
52     private final UnixFileSystem theFileSystem;
53 
UnixFileSystemProvider()54     public UnixFileSystemProvider() {
55         String userDir = System.getProperty(USER_DIR);
56         theFileSystem = newFileSystem(userDir);
57     }
58 
59     /**
60      * Constructs a new file system using the given default directory.
61      */
newFileSystem(String dir)62     abstract UnixFileSystem newFileSystem(String dir);
63 
64     @Override
getScheme()65     public final String getScheme() {
66         return "file";
67     }
68 
checkUri(URI uri)69     private void checkUri(URI uri) {
70         if (!uri.getScheme().equalsIgnoreCase(getScheme()))
71             throw new IllegalArgumentException("URI does not match this provider");
72         if (uri.getAuthority() != null)
73             throw new IllegalArgumentException("Authority component present");
74         if (uri.getPath() == null)
75             throw new IllegalArgumentException("Path component is undefined");
76         if (!uri.getPath().equals("/"))
77             throw new IllegalArgumentException("Path component should be '/'");
78         if (uri.getQuery() != null)
79             throw new IllegalArgumentException("Query component present");
80         if (uri.getFragment() != null)
81             throw new IllegalArgumentException("Fragment component present");
82     }
83 
84     @Override
newFileSystem(URI uri, Map<String,?> env)85     public final FileSystem newFileSystem(URI uri, Map<String,?> env) {
86         checkUri(uri);
87         throw new FileSystemAlreadyExistsException();
88     }
89 
90     @Override
getFileSystem(URI uri)91     public final FileSystem getFileSystem(URI uri) {
92         checkUri(uri);
93         return theFileSystem;
94     }
95 
96     @Override
getPath(URI uri)97     public Path getPath(URI uri) {
98         return UnixUriUtils.fromUri(theFileSystem, uri);
99     }
100 
checkPath(Path obj)101     UnixPath checkPath(Path obj) {
102         if (obj == null)
103             throw new NullPointerException();
104         if (!(obj instanceof UnixPath))
105             throw new ProviderMismatchException();
106         return (UnixPath)obj;
107     }
108 
109     @Override
110     @SuppressWarnings("unchecked")
getFileAttributeView(Path obj, Class<V> type, LinkOption... options)111     public <V extends FileAttributeView> V getFileAttributeView(Path obj,
112                                                                 Class<V> type,
113                                                                 LinkOption... options)
114     {
115         UnixPath file = UnixPath.toUnixPath(obj);
116         boolean followLinks = Util.followLinks(options);
117         if (type == BasicFileAttributeView.class)
118             return (V) UnixFileAttributeViews.createBasicView(file, followLinks);
119         if (type == PosixFileAttributeView.class)
120             return (V) UnixFileAttributeViews.createPosixView(file, followLinks);
121         if (type == FileOwnerAttributeView.class)
122             return (V) UnixFileAttributeViews.createOwnerView(file, followLinks);
123         if (type == null)
124             throw new NullPointerException();
125         return (V) null;
126     }
127 
128     @Override
129     @SuppressWarnings("unchecked")
readAttributes(Path file, Class<A> type, LinkOption... options)130     public <A extends BasicFileAttributes> A readAttributes(Path file,
131                                                                Class<A> type,
132                                                                LinkOption... options)
133         throws IOException
134     {
135         Class<? extends BasicFileAttributeView> view;
136         if (type == BasicFileAttributes.class)
137             view = BasicFileAttributeView.class;
138         else if (type == PosixFileAttributes.class)
139             view = PosixFileAttributeView.class;
140         else if (type == null)
141             throw new NullPointerException();
142         else
143             throw new UnsupportedOperationException();
144         return (A) getFileAttributeView(file, view, options).readAttributes();
145     }
146 
147     @Override
getFileAttributeView(Path obj, String name, LinkOption... options)148     protected DynamicFileAttributeView getFileAttributeView(Path obj,
149                                                             String name,
150                                                             LinkOption... options)
151     {
152         UnixPath file = UnixPath.toUnixPath(obj);
153         boolean followLinks = Util.followLinks(options);
154         if (name.equals("basic"))
155             return UnixFileAttributeViews.createBasicView(file, followLinks);
156         if (name.equals("posix"))
157             return UnixFileAttributeViews.createPosixView(file, followLinks);
158         if (name.equals("unix"))
159             return UnixFileAttributeViews.createUnixView(file, followLinks);
160         if (name.equals("owner"))
161             return UnixFileAttributeViews.createOwnerView(file, followLinks);
162         return null;
163     }
164 
165     @Override
newFileChannel(Path obj, Set<? extends OpenOption> options, FileAttribute<?>... attrs)166     public FileChannel newFileChannel(Path obj,
167                                       Set<? extends OpenOption> options,
168                                       FileAttribute<?>... attrs)
169         throws IOException
170     {
171         UnixPath file = checkPath(obj);
172         int mode = UnixFileModeAttribute
173             .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs);
174         try {
175             return UnixChannelFactory.newFileChannel(file, options, mode);
176         } catch (UnixException x) {
177             x.rethrowAsIOException(file);
178             return null;
179         }
180     }
181 
182     @Override
newAsynchronousFileChannel(Path obj, Set<? extends OpenOption> options, ExecutorService executor, FileAttribute<?>... attrs)183     public AsynchronousFileChannel newAsynchronousFileChannel(Path obj,
184                                                               Set<? extends OpenOption> options,
185                                                               ExecutorService executor,
186                                                               FileAttribute<?>... attrs) throws IOException
187     {
188         UnixPath file = checkPath(obj);
189         int mode = UnixFileModeAttribute
190             .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs);
191         ThreadPool pool = (executor == null) ? null : ThreadPool.wrap(executor, 0);
192         try {
193             return UnixChannelFactory
194                 .newAsynchronousFileChannel(file, options, mode, pool);
195         } catch (UnixException x) {
196             x.rethrowAsIOException(file);
197             return null;
198         }
199     }
200 
201 
202     @Override
newByteChannel(Path obj, Set<? extends OpenOption> options, FileAttribute<?>... attrs)203     public SeekableByteChannel newByteChannel(Path obj,
204                                               Set<? extends OpenOption> options,
205                                               FileAttribute<?>... attrs)
206          throws IOException
207     {
208         UnixPath file = UnixPath.toUnixPath(obj);
209         int mode = UnixFileModeAttribute
210             .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs);
211         try {
212             return UnixChannelFactory.newFileChannel(file, options, mode);
213         } catch (UnixException x) {
214             x.rethrowAsIOException(file);
215             return null;  // keep compiler happy
216         }
217     }
218 
219     @Override
implDelete(Path obj, boolean failIfNotExists)220     boolean implDelete(Path obj, boolean failIfNotExists) throws IOException {
221         UnixPath file = UnixPath.toUnixPath(obj);
222         file.checkDelete();
223 
224         // need file attributes to know if file is directory
225         UnixFileAttributes attrs = null;
226         try {
227             attrs = UnixFileAttributes.get(file, false);
228             if (attrs.isDirectory()) {
229                 rmdir(file);
230             } else {
231                 unlink(file);
232             }
233             return true;
234         } catch (UnixException x) {
235             // no-op if file does not exist
236             if (!failIfNotExists && x.errno() == ENOENT)
237                 return false;
238 
239             // DirectoryNotEmptyException if not empty
240             if (attrs != null && attrs.isDirectory() &&
241                 (x.errno() == EEXIST || x.errno() == ENOTEMPTY))
242                 throw new DirectoryNotEmptyException(file.getPathForExceptionMessage());
243 
244             x.rethrowAsIOException(file);
245             return false;
246         }
247     }
248 
249     @Override
copy(Path source, Path target, CopyOption... options)250     public void copy(Path source, Path target, CopyOption... options)
251         throws IOException
252     {
253         UnixCopyFile.copy(UnixPath.toUnixPath(source),
254                           UnixPath.toUnixPath(target),
255                           options);
256     }
257 
258     @Override
move(Path source, Path target, CopyOption... options)259     public void move(Path source, Path target, CopyOption... options)
260         throws IOException
261     {
262         UnixCopyFile.move(UnixPath.toUnixPath(source),
263                           UnixPath.toUnixPath(target),
264                           options);
265     }
266 
267     @Override
checkAccess(Path obj, AccessMode... modes)268     public void checkAccess(Path obj, AccessMode... modes) throws IOException {
269         UnixPath file = UnixPath.toUnixPath(obj);
270         boolean e = false;
271         boolean r = false;
272         boolean w = false;
273         boolean x = false;
274 
275         if (modes.length == 0) {
276             e = true;
277         } else {
278             for (AccessMode mode: modes) {
279                 switch (mode) {
280                     case READ : r = true; break;
281                     case WRITE : w = true; break;
282                     case EXECUTE : x = true; break;
283                     default: throw new AssertionError("Should not get here");
284                 }
285             }
286         }
287 
288         int mode = 0;
289         if (e || r) {
290             file.checkRead();
291             mode |= (r) ? R_OK : F_OK;
292         }
293         if (w) {
294             file.checkWrite();
295             mode |= W_OK;
296         }
297         if (x) {
298             SecurityManager sm = System.getSecurityManager();
299             if (sm != null) {
300                 // not cached
301                 sm.checkExec(file.getPathForPermissionCheck());
302             }
303             mode |= X_OK;
304         }
305         try {
306             access(file, mode);
307         } catch (UnixException exc) {
308             exc.rethrowAsIOException(file);
309         }
310     }
311 
312     @Override
isSameFile(Path obj1, Path obj2)313     public boolean isSameFile(Path obj1, Path obj2) throws IOException {
314         UnixPath file1 = UnixPath.toUnixPath(obj1);
315         if (file1.equals(obj2))
316             return true;
317         if (obj2 == null)
318             throw new NullPointerException();
319         if (!(obj2 instanceof UnixPath))
320             return false;
321         UnixPath file2 = (UnixPath)obj2;
322 
323         // check security manager access to both files
324         file1.checkRead();
325         file2.checkRead();
326 
327         UnixFileAttributes attrs1;
328         UnixFileAttributes attrs2;
329         try {
330              attrs1 = UnixFileAttributes.get(file1, true);
331         } catch (UnixException x) {
332             x.rethrowAsIOException(file1);
333             return false;    // keep compiler happy
334         }
335         try {
336             attrs2 = UnixFileAttributes.get(file2, true);
337         } catch (UnixException x) {
338             x.rethrowAsIOException(file2);
339             return false;    // keep compiler happy
340         }
341         return attrs1.isSameFile(attrs2);
342     }
343 
344     @Override
isHidden(Path obj)345     public boolean isHidden(Path obj) {
346         UnixPath file = UnixPath.toUnixPath(obj);
347         file.checkRead();
348         UnixPath name = file.getFileName();
349         if (name == null)
350             return false;
351         return (name.asByteArray()[0] == '.');
352     }
353 
354     /**
355      * Returns a FileStore to represent the file system where the given file
356      * reside.
357      */
getFileStore(UnixPath path)358     abstract FileStore getFileStore(UnixPath path) throws IOException;
359 
360     @Override
getFileStore(Path obj)361     public FileStore getFileStore(Path obj) throws IOException {
362         // Android-changed: Complete information about file systems is neither available to regular
363         // apps nor the system server due to SELinux policies.
364         //
365         // UnixPath file = UnixPath.toUnixPath(obj);
366         // SecurityManager sm = System.getSecurityManager();
367         // if (sm != null) {
368         //     sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
369         //     file.checkRead();
370         // }
371         // return getFileStore(file);
372         throw new SecurityException("getFileStore");
373     }
374 
375     @Override
createDirectory(Path obj, FileAttribute<?>... attrs)376     public void createDirectory(Path obj, FileAttribute<?>... attrs)
377         throws IOException
378     {
379         UnixPath dir = UnixPath.toUnixPath(obj);
380         dir.checkWrite();
381 
382         int mode = UnixFileModeAttribute.toUnixMode(UnixFileModeAttribute.ALL_PERMISSIONS, attrs);
383         try {
384             mkdir(dir, mode);
385         } catch (UnixException x) {
386             if (x.errno() == EISDIR)
387                 throw new FileAlreadyExistsException(dir.toString());
388             x.rethrowAsIOException(dir);
389         }
390     }
391 
392 
393     @Override
newDirectoryStream(Path obj, DirectoryStream.Filter<? super Path> filter)394     public DirectoryStream<Path> newDirectoryStream(Path obj, DirectoryStream.Filter<? super Path> filter)
395         throws IOException
396     {
397         UnixPath dir = UnixPath.toUnixPath(obj);
398         dir.checkRead();
399         if (filter == null)
400             throw new NullPointerException();
401 
402         // can't return SecureDirectoryStream on kernels that don't support openat
403         // or O_NOFOLLOW
404         if (!openatSupported() || O_NOFOLLOW == 0) {
405             try {
406                 long ptr = opendir(dir);
407                 return new UnixDirectoryStream(dir, ptr, filter);
408             } catch (UnixException x) {
409                 if (x.errno() == ENOTDIR)
410                     throw new NotDirectoryException(dir.getPathForExceptionMessage());
411                 x.rethrowAsIOException(dir);
412             }
413         }
414 
415         // open directory and dup file descriptor for use by
416         // opendir/readdir/closedir
417         int dfd1 = -1;
418         int dfd2 = -1;
419         long dp = 0L;
420         try {
421             dfd1 = open(dir, O_RDONLY, 0);
422             dfd2 = dup(dfd1);
423             dp = fdopendir(dfd1);
424         } catch (UnixException x) {
425             if (dfd1 != -1)
426                 UnixNativeDispatcher.close(dfd1);
427             if (dfd2 != -1)
428                 UnixNativeDispatcher.close(dfd2);
429             if (x.errno() == UnixConstants.ENOTDIR)
430                 throw new NotDirectoryException(dir.getPathForExceptionMessage());
431             x.rethrowAsIOException(dir);
432         }
433         return new UnixSecureDirectoryStream(dir, dp, dfd2, filter);
434     }
435 
436     @Override
createSymbolicLink(Path obj1, Path obj2, FileAttribute<?>... attrs)437     public void createSymbolicLink(Path obj1, Path obj2, FileAttribute<?>... attrs)
438         throws IOException
439     {
440         UnixPath link = UnixPath.toUnixPath(obj1);
441         UnixPath target = UnixPath.toUnixPath(obj2);
442 
443         // no attributes supported when creating links
444         if (attrs.length > 0) {
445             UnixFileModeAttribute.toUnixMode(0, attrs);  // may throw NPE or UOE
446             throw new UnsupportedOperationException("Initial file attributes" +
447                 "not supported when creating symbolic link");
448         }
449 
450         // permission check
451         SecurityManager sm = System.getSecurityManager();
452         if (sm != null) {
453             sm.checkPermission(new LinkPermission("symbolic"));
454             link.checkWrite();
455         }
456 
457         // create link
458         try {
459             symlink(target.asByteArray(), link);
460         } catch (UnixException x) {
461             x.rethrowAsIOException(link);
462         }
463     }
464 
465     @Override
createLink(Path obj1, Path obj2)466     public void createLink(Path obj1, Path obj2) throws IOException {
467         UnixPath link = UnixPath.toUnixPath(obj1);
468         UnixPath existing = UnixPath.toUnixPath(obj2);
469 
470         // permission check
471         SecurityManager sm = System.getSecurityManager();
472         if (sm != null) {
473             sm.checkPermission(new LinkPermission("hard"));
474             link.checkWrite();
475             existing.checkWrite();
476         }
477         try {
478             link(existing, link);
479         } catch (UnixException x) {
480             x.rethrowAsIOException(link, existing);
481         }
482     }
483 
484     @Override
readSymbolicLink(Path obj1)485     public Path readSymbolicLink(Path obj1) throws IOException {
486         UnixPath link = UnixPath.toUnixPath(obj1);
487         // permission check
488         SecurityManager sm = System.getSecurityManager();
489         if (sm != null) {
490             FilePermission perm = new FilePermission(link.getPathForPermissionCheck(),
491                 SecurityConstants.FILE_READLINK_ACTION);
492             sm.checkPermission(perm);
493         }
494         try {
495             byte[] target = readlink(link);
496             return new UnixPath(link.getFileSystem(), target);
497         } catch (UnixException x) {
498            if (x.errno() == UnixConstants.EINVAL)
499                 throw new NotLinkException(link.getPathForExceptionMessage());
500             x.rethrowAsIOException(link);
501             return null;    // keep compiler happy
502         }
503     }
504 
505     /**
506      * Returns a {@code FileTypeDetector} for this platform.
507      */
getFileTypeDetector()508     FileTypeDetector getFileTypeDetector() {
509         return new AbstractFileTypeDetector() {
510             @Override
511             public String implProbeContentType(Path file) {
512                 return null;
513             }
514         };
515     }
516 
517     /**
518      * Returns a {@code FileTypeDetector} that chains the given array of file
519      * type detectors. When the {@code implProbeContentType} method is invoked
520      * then each of the detectors is invoked in turn, the result from the
521      * first to detect the file type is returned.
522      */
523     final FileTypeDetector chain(final AbstractFileTypeDetector... detectors) {
524         return new AbstractFileTypeDetector() {
525             @Override
526             protected String implProbeContentType(Path file) throws IOException {
527                 for (AbstractFileTypeDetector detector : detectors) {
528                     String result = detector.implProbeContentType(file);
529                     if (result != null && !result.isEmpty()) {
530                         return result;
531                     }
532                 }
533                 return null;
534             }
535         };
536     }
537 }
538