| /* |
| * Copyright (c) 2008, 2013, 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.channels.*; |
| import java.io.FileDescriptor; |
| import java.util.Set; |
| |
| import sun.nio.ch.FileChannelImpl; |
| import sun.nio.ch.ThreadPool; |
| import sun.nio.ch.SimpleAsynchronousFileChannelImpl; |
| import jdk.internal.access.SharedSecrets; |
| import jdk.internal.access.JavaIOFileDescriptorAccess; |
| |
| import static sun.nio.fs.UnixNativeDispatcher.*; |
| import static sun.nio.fs.UnixConstants.*; |
| |
| /** |
| * Factory for FileChannels and AsynchronousFileChannels |
| */ |
| |
| class UnixChannelFactory { |
| private static final JavaIOFileDescriptorAccess fdAccess = |
| SharedSecrets.getJavaIOFileDescriptorAccess(); |
| |
| protected UnixChannelFactory() { |
| } |
| |
| /** |
| * Represents the flags from a user-supplied set of open options. |
| */ |
| protected static class Flags { |
| boolean read; |
| boolean write; |
| boolean append; |
| boolean truncateExisting; |
| boolean noFollowLinks; |
| boolean create; |
| boolean createNew; |
| boolean deleteOnClose; |
| boolean sync; |
| boolean dsync; |
| |
| static Flags toFlags(Set<? extends OpenOption> options) { |
| Flags flags = new Flags(); |
| for (OpenOption option: options) { |
| if (option instanceof StandardOpenOption) { |
| switch ((StandardOpenOption)option) { |
| case READ : flags.read = true; break; |
| case WRITE : flags.write = true; break; |
| case APPEND : flags.append = true; break; |
| case TRUNCATE_EXISTING : flags.truncateExisting = true; break; |
| case CREATE : flags.create = true; break; |
| case CREATE_NEW : flags.createNew = true; break; |
| case DELETE_ON_CLOSE : flags.deleteOnClose = true; break; |
| case SPARSE : /* ignore */ break; |
| case SYNC : flags.sync = true; break; |
| case DSYNC : flags.dsync = true; break; |
| default: throw new UnsupportedOperationException(); |
| } |
| continue; |
| } |
| if (option == LinkOption.NOFOLLOW_LINKS && O_NOFOLLOW != 0) { |
| flags.noFollowLinks = true; |
| continue; |
| } |
| if (option == null) |
| throw new NullPointerException(); |
| throw new UnsupportedOperationException(option + " not supported"); |
| } |
| return flags; |
| } |
| } |
| |
| |
| /** |
| * Constructs a file channel from an existing (open) file descriptor |
| */ |
| static FileChannel newFileChannel(int fd, String path, boolean reading, boolean writing) { |
| FileDescriptor fdObj = new FileDescriptor(); |
| fdAccess.set(fdObj, fd); |
| return FileChannelImpl.open(fdObj, path, reading, writing, null); |
| } |
| |
| /** |
| * Constructs a file channel by opening a file using a dfd/path pair |
| */ |
| static FileChannel newFileChannel(int dfd, |
| UnixPath path, |
| String pathForPermissionCheck, |
| Set<? extends OpenOption> options, |
| int mode) |
| throws UnixException |
| { |
| Flags flags = Flags.toFlags(options); |
| |
| // default is reading; append => writing |
| if (!flags.read && !flags.write) { |
| if (flags.append) { |
| flags.write = true; |
| } else { |
| flags.read = true; |
| } |
| } |
| |
| // validation |
| if (flags.read && flags.append) |
| throw new IllegalArgumentException("READ + APPEND not allowed"); |
| if (flags.append && flags.truncateExisting) |
| throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed"); |
| |
| FileDescriptor fdObj = open(dfd, path, pathForPermissionCheck, flags, mode); |
| return FileChannelImpl.open(fdObj, path.toString(), flags.read, |
| // Android-changed: TODO: Remove this patch when the direct flag is supported. |
| // flags.write, flags.direct, null); |
| flags.write, null); |
| } |
| |
| /** |
| * Constructs a file channel by opening the given file. |
| */ |
| static FileChannel newFileChannel(UnixPath path, |
| Set<? extends OpenOption> options, |
| int mode) |
| throws UnixException |
| { |
| return newFileChannel(-1, path, null, options, mode); |
| } |
| |
| /** |
| * Constructs an asynchronous file channel by opening the given file. |
| */ |
| static AsynchronousFileChannel newAsynchronousFileChannel(UnixPath path, |
| Set<? extends OpenOption> options, |
| int mode, |
| ThreadPool pool) |
| throws UnixException |
| { |
| Flags flags = Flags.toFlags(options); |
| |
| // default is reading |
| if (!flags.read && !flags.write) { |
| flags.read = true; |
| } |
| |
| // validation |
| if (flags.append) |
| throw new UnsupportedOperationException("APPEND not allowed"); |
| |
| // for now use simple implementation |
| FileDescriptor fdObj = open(-1, path, null, flags, mode); |
| return SimpleAsynchronousFileChannelImpl.open(fdObj, flags.read, flags.write, pool); |
| } |
| |
| /** |
| * Opens file based on parameters and options, returning a FileDescriptor |
| * encapsulating the handle to the open file. |
| */ |
| protected static FileDescriptor open(int dfd, |
| UnixPath path, |
| String pathForPermissionCheck, |
| Flags flags, |
| int mode) |
| throws UnixException |
| { |
| // map to oflags |
| int oflags; |
| if (flags.read && flags.write) { |
| oflags = O_RDWR; |
| } else { |
| oflags = (flags.write) ? O_WRONLY : O_RDONLY; |
| } |
| if (flags.write) { |
| if (flags.truncateExisting) |
| oflags |= O_TRUNC; |
| if (flags.append) |
| oflags |= O_APPEND; |
| |
| // create flags |
| if (flags.createNew) { |
| byte[] pathForSysCall = path.asByteArray(); |
| |
| // throw exception if file name is "." to avoid confusing error |
| if ((pathForSysCall[pathForSysCall.length-1] == '.') && |
| (pathForSysCall.length == 1 || |
| (pathForSysCall[pathForSysCall.length-2] == '/'))) |
| { |
| throw new UnixException(EEXIST); |
| } |
| oflags |= (O_CREAT | O_EXCL); |
| } else { |
| if (flags.create) |
| oflags |= O_CREAT; |
| } |
| } |
| |
| // follow links by default |
| boolean followLinks = true; |
| if (!flags.createNew && (flags.noFollowLinks || flags.deleteOnClose)) { |
| if (flags.deleteOnClose && O_NOFOLLOW == 0) { |
| try { |
| if (UnixFileAttributes.get(path, false).isSymbolicLink()) |
| throw new UnixException("DELETE_ON_CLOSE specified and file is a symbolic link"); |
| } catch (UnixException x) { |
| if (!flags.create || x.errno() != ENOENT) |
| throw x; |
| } |
| } |
| followLinks = false; |
| oflags |= O_NOFOLLOW; |
| } |
| |
| if (flags.dsync) |
| oflags |= O_DSYNC; |
| if (flags.sync) |
| oflags |= O_SYNC; |
| |
| // permission check before we open the file |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm != null) { |
| if (pathForPermissionCheck == null) |
| pathForPermissionCheck = path.getPathForPermissionCheck(); |
| if (flags.read) |
| sm.checkRead(pathForPermissionCheck); |
| if (flags.write) |
| sm.checkWrite(pathForPermissionCheck); |
| if (flags.deleteOnClose) |
| sm.checkDelete(pathForPermissionCheck); |
| } |
| |
| int fd; |
| try { |
| if (dfd >= 0) { |
| fd = openat(dfd, path.asByteArray(), oflags, mode); |
| } else { |
| fd = UnixNativeDispatcher.open(path, oflags, mode); |
| } |
| } catch (UnixException x) { |
| // Linux error can be EISDIR or EEXIST when file exists |
| if (flags.createNew && (x.errno() == EISDIR)) { |
| x.setError(EEXIST); |
| } |
| |
| // handle ELOOP to avoid confusing message |
| if (!followLinks && (x.errno() == ELOOP)) { |
| x = new UnixException(x.getMessage() + " (NOFOLLOW_LINKS specified)"); |
| } |
| |
| throw x; |
| } |
| |
| // unlink file immediately if delete on close. The spec is clear that |
| // an implementation cannot guarantee to unlink the correct file when |
| // replaced by an attacker after it is opened. |
| if (flags.deleteOnClose) { |
| try { |
| if (dfd >= 0) { |
| unlinkat(dfd, path.asByteArray(), 0); |
| } else { |
| unlink(path); |
| } |
| } catch (UnixException ignore) { |
| // best-effort |
| } |
| } |
| |
| // create java.io.FileDescriptor |
| FileDescriptor fdObj = new FileDescriptor(); |
| fdAccess.set(fdObj, fd); |
| return fdObj; |
| } |
| } |