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