| /* |
| * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.nio.fs; |
| |
| import java.nio.file.*; |
| import java.nio.file.attribute.*; |
| import java.nio.channels.SeekableByteChannel; |
| import java.util.*; |
| import java.util.concurrent.TimeUnit; |
| import java.io.IOException; |
| |
| import static sun.nio.fs.UnixNativeDispatcher.*; |
| import static sun.nio.fs.UnixConstants.*; |
| |
| /** |
| * Unix implementation of SecureDirectoryStream. |
| */ |
| |
| class UnixSecureDirectoryStream |
| extends SecureDirectoryStream<Path> |
| { |
| private final UnixDirectoryStream ds; |
| private final int dfd; |
| |
| UnixSecureDirectoryStream(UnixPath dir, |
| long dp, |
| int dfd, |
| DirectoryStream.Filter<? super Path> filter) |
| { |
| this.ds = new UnixDirectoryStream(dir, dp, filter); |
| this.dfd = dfd; |
| } |
| |
| @Override |
| public void close() |
| throws IOException |
| { |
| ds.writeLock().lock(); |
| try { |
| if (ds.closeImpl()) { |
| UnixNativeDispatcher.close(dfd); |
| } |
| } finally { |
| ds.writeLock().unlock(); |
| } |
| } |
| |
| @Override |
| public Iterator<Path> iterator() { |
| return ds.iterator(this); |
| } |
| |
| private UnixPath getName(Path obj) { |
| if (obj == null) |
| throw new NullPointerException(); |
| if (!(obj instanceof UnixPath)) |
| throw new ProviderMismatchException(); |
| return (UnixPath)obj; |
| } |
| |
| /** |
| * Opens sub-directory in this directory |
| */ |
| @Override |
| public SecureDirectoryStream<Path> newDirectoryStream(Path obj, |
| LinkOption... options) |
| throws IOException |
| { |
| UnixPath file = getName(obj); |
| UnixPath child = ds.directory().resolve(file); |
| boolean followLinks = file.getFileSystem().followLinks(options); |
| |
| // permission check using name resolved against original path of directory |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm != null) { |
| child.checkRead(); |
| } |
| |
| ds.readLock().lock(); |
| try { |
| if (!ds.isOpen()) |
| throw new ClosedDirectoryStreamException(); |
| |
| // open directory and create new secure directory stream |
| int newdfd1 = -1; |
| int newdfd2 = -1; |
| long ptr = 0L; |
| try { |
| int flags = O_RDONLY; |
| if (!followLinks) |
| flags |= O_NOFOLLOW; |
| newdfd1 = openat(dfd, file.asByteArray(), flags , 0); |
| newdfd2 = dup(newdfd1); |
| ptr = fdopendir(newdfd1); |
| } catch (UnixException x) { |
| if (newdfd1 != -1) |
| UnixNativeDispatcher.close(newdfd1); |
| if (newdfd2 != -1) |
| UnixNativeDispatcher.close(newdfd2); |
| if (x.errno() == UnixConstants.ENOTDIR) |
| throw new NotDirectoryException(file.toString()); |
| x.rethrowAsIOException(file); |
| } |
| return new UnixSecureDirectoryStream(child, ptr, newdfd2, null); |
| } finally { |
| ds.readLock().unlock(); |
| } |
| } |
| |
| /** |
| * Opens file in this directory |
| */ |
| @Override |
| public SeekableByteChannel newByteChannel(Path obj, |
| Set<? extends OpenOption> options, |
| FileAttribute<?>... attrs) |
| throws IOException |
| { |
| UnixPath file = getName(obj); |
| |
| int mode = UnixFileModeAttribute |
| .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs); |
| |
| // path for permission check |
| String pathToCheck = ds.directory().resolve(file).getPathForPermissionCheck(); |
| |
| ds.readLock().lock(); |
| try { |
| if (!ds.isOpen()) |
| throw new ClosedDirectoryStreamException(); |
| try { |
| return UnixChannelFactory.newFileChannel(dfd, file, pathToCheck, options, mode); |
| } catch (UnixException x) { |
| x.rethrowAsIOException(file); |
| return null; // keep compiler happy |
| } |
| } finally { |
| ds.readLock().unlock(); |
| } |
| } |
| |
| /** |
| * Deletes file/directory in this directory. Works in a race-free manner |
| * when invoked with flags. |
| */ |
| void implDelete(Path obj, boolean haveFlags, int flags) |
| throws IOException |
| { |
| UnixPath file = getName(obj); |
| |
| // permission check using name resolved against original path of directory |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm != null) { |
| ds.directory().resolve(file).checkDelete(); |
| } |
| |
| ds.readLock().lock(); |
| try { |
| if (!ds.isOpen()) |
| throw new ClosedDirectoryStreamException(); |
| |
| if (!haveFlags) { |
| // need file attribute to know if file is directory. This creates |
| // a race in that the file may be replaced by a directory or a |
| // directory replaced by a file between the time we query the |
| // file type and unlink it. |
| UnixFileAttributes attrs = null; |
| try { |
| attrs = UnixFileAttributes.get(dfd, file, false); |
| } catch (UnixException x) { |
| x.rethrowAsIOException(file); |
| } |
| flags = (attrs.isDirectory()) ? AT_REMOVEDIR : 0; |
| } |
| |
| try { |
| unlinkat(dfd, file.asByteArray(), flags); |
| } catch (UnixException x) { |
| if ((flags & AT_REMOVEDIR) != 0) { |
| if (x.errno() == EEXIST || x.errno() == ENOTEMPTY) { |
| throw new DirectoryNotEmptyException(null); |
| } |
| } |
| x.rethrowAsIOException(file); |
| } |
| } finally { |
| ds.readLock().unlock(); |
| } |
| } |
| |
| @Override |
| public void deleteFile(Path file) throws IOException { |
| implDelete(file, true, 0); |
| } |
| |
| @Override |
| public void deleteDirectory(Path dir) throws IOException { |
| implDelete(dir, true, AT_REMOVEDIR); |
| } |
| |
| /** |
| * Rename/move file in this directory to another (open) directory |
| */ |
| @Override |
| public void move(Path fromObj, SecureDirectoryStream<Path> dir, Path toObj) |
| throws IOException |
| { |
| UnixPath from = getName(fromObj); |
| UnixPath to = getName(toObj); |
| if (dir == null) |
| throw new NullPointerException(); |
| if (!(dir instanceof UnixSecureDirectoryStream)) |
| throw new ProviderMismatchException(); |
| UnixSecureDirectoryStream that = (UnixSecureDirectoryStream)dir; |
| |
| // permission check |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm != null) { |
| this.ds.directory().resolve(from).checkWrite(); |
| that.ds.directory().resolve(to).checkWrite(); |
| } |
| |
| // lock ordering doesn't matter |
| this.ds.readLock().lock(); |
| try { |
| that.ds.readLock().lock(); |
| try { |
| if (!this.ds.isOpen() || !that.ds.isOpen()) |
| throw new ClosedDirectoryStreamException(); |
| try { |
| renameat(this.dfd, from.asByteArray(), that.dfd, to.asByteArray()); |
| } catch (UnixException x) { |
| if (x.errno() == EXDEV) { |
| throw new AtomicMoveNotSupportedException( |
| from.toString(), to.toString(), x.errorString()); |
| } |
| x.rethrowAsIOException(from, to); |
| } |
| } finally { |
| that.ds.readLock().unlock(); |
| } |
| } finally { |
| this.ds.readLock().unlock(); |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| private <V extends FileAttributeView> V getFileAttributeViewImpl(UnixPath file, |
| Class<V> type, |
| boolean followLinks) |
| { |
| if (type == null) |
| throw new NullPointerException(); |
| Class<?> c = type; |
| if (c == BasicFileAttributeView.class) { |
| return (V) new BasicFileAttributeViewImpl(file, followLinks); |
| } |
| if (c == PosixFileAttributeView.class || c == FileOwnerAttributeView.class) { |
| return (V) new PosixFileAttributeViewImpl(file, followLinks); |
| } |
| // TBD - should also support AclFileAttributeView |
| return (V) null; |
| } |
| |
| /** |
| * Returns file attribute view bound to this directory |
| */ |
| @Override |
| public <V extends FileAttributeView> V getFileAttributeView(Class<V> type) { |
| return getFileAttributeViewImpl(null, type, false); |
| } |
| |
| /** |
| * Returns file attribute view bound to dfd/filename. |
| */ |
| @Override |
| public <V extends FileAttributeView> V getFileAttributeView(Path obj, |
| Class<V> type, |
| LinkOption... options) |
| { |
| UnixPath file = getName(obj); |
| boolean followLinks = file.getFileSystem().followLinks(options); |
| return getFileAttributeViewImpl(file, type, followLinks); |
| } |
| |
| /** |
| * A BasicFileAttributeView implementation that using a dfd/name pair. |
| */ |
| private class BasicFileAttributeViewImpl |
| implements BasicFileAttributeView |
| { |
| final UnixPath file; |
| final boolean followLinks; |
| |
| BasicFileAttributeViewImpl(UnixPath file, boolean followLinks) |
| { |
| this.file = file; |
| this.followLinks = followLinks; |
| } |
| |
| int open() throws IOException { |
| int oflags = O_RDONLY; |
| if (!followLinks) |
| oflags |= O_NOFOLLOW; |
| try { |
| return openat(dfd, file.asByteArray(), oflags, 0); |
| } catch (UnixException x) { |
| x.rethrowAsIOException(file); |
| return -1; // keep compiler happy |
| } |
| } |
| |
| private void checkWriteAccess() { |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm != null) { |
| ds.directory().resolve(file).checkWrite(); |
| } |
| } |
| |
| @Override |
| public String name() { |
| return "basic"; |
| } |
| |
| @Override |
| public BasicFileAttributes readAttributes() throws IOException { |
| ds.readLock().lock(); |
| try { |
| if (!ds.isOpen()) |
| throw new ClosedDirectoryStreamException(); |
| |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm != null) { |
| if (file == null) { |
| ds.directory().checkRead(); |
| } else { |
| ds.directory().resolve(file).checkRead(); |
| } |
| } |
| try { |
| UnixFileAttributes attrs = (file == null) ? |
| UnixFileAttributes.get(dfd) : |
| UnixFileAttributes.get(dfd, file, followLinks); |
| |
| // SECURITY: must return as BasicFileAttribute |
| return attrs.asBasicFileAttributes(); |
| } catch (UnixException x) { |
| x.rethrowAsIOException(file); |
| return null; // keep compiler happy |
| } |
| } finally { |
| ds.readLock().unlock(); |
| } |
| } |
| |
| @Override |
| public void setTimes(FileTime lastModifiedTime, |
| FileTime lastAccessTime, |
| FileTime createTime) // ignore |
| throws IOException |
| { |
| checkWriteAccess(); |
| |
| ds.readLock().lock(); |
| try { |
| if (!ds.isOpen()) |
| throw new ClosedDirectoryStreamException(); |
| |
| int fd = (file == null) ? dfd : open(); |
| try { |
| // if not changing both attributes then need existing attributes |
| if (lastModifiedTime == null || lastAccessTime == null) { |
| try { |
| UnixFileAttributes attrs = UnixFileAttributes.get(fd); |
| if (lastModifiedTime == null) |
| lastModifiedTime = attrs.lastModifiedTime(); |
| if (lastAccessTime == null) |
| lastAccessTime = attrs.lastAccessTime(); |
| } catch (UnixException x) { |
| x.rethrowAsIOException(file); |
| } |
| } |
| // update times |
| try { |
| futimes(fd, |
| lastAccessTime.to(TimeUnit.MICROSECONDS), |
| lastModifiedTime.to(TimeUnit.MICROSECONDS)); |
| } catch (UnixException x) { |
| x.rethrowAsIOException(file); |
| } |
| } finally { |
| if (file != null) |
| UnixNativeDispatcher.close(fd); |
| } |
| } finally { |
| ds.readLock().unlock(); |
| } |
| } |
| } |
| |
| /** |
| * A PosixFileAttributeView implementation that using a dfd/name pair. |
| */ |
| private class PosixFileAttributeViewImpl |
| extends BasicFileAttributeViewImpl implements PosixFileAttributeView |
| { |
| PosixFileAttributeViewImpl(UnixPath file, boolean followLinks) { |
| super(file, followLinks); |
| } |
| |
| private void checkWriteAndUserAccess() { |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm != null) { |
| super.checkWriteAccess(); |
| sm.checkPermission(new RuntimePermission("accessUserInformation")); |
| } |
| } |
| |
| @Override |
| public String name() { |
| return "posix"; |
| } |
| |
| @Override |
| public PosixFileAttributes readAttributes() throws IOException { |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm != null) { |
| if (file == null) |
| ds.directory().checkRead(); |
| else |
| ds.directory().resolve(file).checkRead(); |
| sm.checkPermission(new RuntimePermission("accessUserInformation")); |
| } |
| |
| ds.readLock().lock(); |
| try { |
| if (!ds.isOpen()) |
| throw new ClosedDirectoryStreamException(); |
| |
| try { |
| UnixFileAttributes attrs = (file == null) ? |
| UnixFileAttributes.get(dfd) : |
| UnixFileAttributes.get(dfd, file, followLinks); |
| return attrs; |
| } catch (UnixException x) { |
| x.rethrowAsIOException(file); |
| return null; // keep compiler happy |
| } |
| } finally { |
| ds.readLock().unlock(); |
| } |
| } |
| |
| @Override |
| public void setPermissions(Set<PosixFilePermission> perms) |
| throws IOException |
| { |
| // permission check |
| checkWriteAndUserAccess(); |
| |
| ds.readLock().lock(); |
| try { |
| if (!ds.isOpen()) |
| throw new ClosedDirectoryStreamException(); |
| |
| int fd = (file == null) ? dfd : open(); |
| try { |
| fchmod(fd, UnixFileModeAttribute.toUnixMode(perms)); |
| } catch (UnixException x) { |
| x.rethrowAsIOException(file); |
| } finally { |
| if (file != null && fd >= 0) |
| UnixNativeDispatcher.close(fd); |
| } |
| } finally { |
| ds.readLock().unlock(); |
| } |
| } |
| |
| private void setOwners(int uid, int gid) throws IOException { |
| // permission check |
| checkWriteAndUserAccess(); |
| |
| ds.readLock().lock(); |
| try { |
| if (!ds.isOpen()) |
| throw new ClosedDirectoryStreamException(); |
| |
| int fd = (file == null) ? dfd : open(); |
| try { |
| fchown(fd, uid, gid); |
| } catch (UnixException x) { |
| x.rethrowAsIOException(file); |
| } finally { |
| if (file != null && fd >= 0) |
| UnixNativeDispatcher.close(fd); |
| } |
| } finally { |
| ds.readLock().unlock(); |
| } |
| } |
| |
| @Override |
| public UserPrincipal getOwner() throws IOException { |
| return readAttributes().owner(); |
| } |
| |
| @Override |
| public void setOwner(UserPrincipal owner) |
| throws IOException |
| { |
| if (!(owner instanceof UnixUserPrincipals.User)) |
| throw new ProviderMismatchException(); |
| if (owner instanceof UnixUserPrincipals.Group) |
| throw new IOException("'owner' parameter can't be a group"); |
| int uid = ((UnixUserPrincipals.User)owner).uid(); |
| setOwners(uid, -1); |
| } |
| |
| @Override |
| public void setGroup(GroupPrincipal group) |
| throws IOException |
| { |
| if (!(group instanceof UnixUserPrincipals.Group)) |
| throw new ProviderMismatchException(); |
| int gid = ((UnixUserPrincipals.Group)group).gid(); |
| setOwners(-1, gid); |
| } |
| } |
| } |