| /* |
| * Copyright (c) 2008, 2016, 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.io.FileDescriptor; |
| import java.io.IOException; |
| import java.nio.channels.AsynchronousFileChannel; |
| import java.nio.channels.FileChannel; |
| import java.nio.file.LinkOption; |
| import java.nio.file.OpenOption; |
| import java.nio.file.StandardOpenOption; |
| import java.util.Set; |
| |
| import jdk.internal.misc.JavaIOFileDescriptorAccess; |
| import jdk.internal.misc.SharedSecrets; |
| import sun.nio.ch.FileChannelImpl; |
| import sun.nio.ch.ThreadPool; |
| import sun.nio.ch.WindowsAsynchronousFileChannelImpl; |
| |
| import static sun.nio.fs.WindowsNativeDispatcher.*; |
| import static sun.nio.fs.WindowsConstants.*; |
| |
| /** |
| * Factory to create FileChannels and AsynchronousFileChannels. |
| */ |
| |
| class WindowsChannelFactory { |
| private static final JavaIOFileDescriptorAccess fdAccess = |
| SharedSecrets.getJavaIOFileDescriptorAccess(); |
| |
| private WindowsChannelFactory() { } |
| |
| /** |
| * Do not follow reparse points when opening an existing file. Do not fail |
| * if the file is a reparse point. |
| */ |
| static final OpenOption OPEN_REPARSE_POINT = new OpenOption() { }; |
| |
| /** |
| * Represents the flags from a user-supplied set of open options. |
| */ |
| private static class Flags { |
| boolean read; |
| boolean write; |
| boolean append; |
| boolean truncateExisting; |
| boolean create; |
| boolean createNew; |
| boolean deleteOnClose; |
| boolean sparse; |
| boolean overlapped; |
| boolean sync; |
| boolean dsync; |
| |
| // non-standard |
| boolean shareRead = true; |
| boolean shareWrite = true; |
| boolean shareDelete = true; |
| boolean noFollowLinks; |
| boolean openReparsePoint; |
| |
| 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 : flags.sparse = true; break; |
| case SYNC : flags.sync = true; break; |
| case DSYNC : flags.dsync = true; break; |
| default: throw new UnsupportedOperationException(); |
| } |
| continue; |
| } |
| if (option == LinkOption.NOFOLLOW_LINKS) { |
| flags.noFollowLinks = true; |
| continue; |
| } |
| if (option == OPEN_REPARSE_POINT) { |
| flags.openReparsePoint = true; |
| continue; |
| } |
| if (ExtendedOptions.NOSHARE_READ.matches(option)) { |
| flags.shareRead = false; |
| continue; |
| } |
| if (ExtendedOptions.NOSHARE_WRITE.matches(option)) { |
| flags.shareWrite = false; |
| continue; |
| } |
| if (ExtendedOptions.NOSHARE_DELETE.matches(option)) { |
| flags.shareDelete = false; |
| continue; |
| } |
| if (option == null) |
| throw new NullPointerException(); |
| throw new UnsupportedOperationException(); |
| } |
| return flags; |
| } |
| } |
| |
| /** |
| * Open/creates file, returning FileChannel to access the file |
| * |
| * @param pathForWindows |
| * The path of the file to open/create |
| * @param pathToCheck |
| * The path used for permission checks (if security manager) |
| */ |
| static FileChannel newFileChannel(String pathForWindows, |
| String pathToCheck, |
| Set<? extends OpenOption> options, |
| long pSecurityDescriptor) |
| throws WindowsException |
| { |
| 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(pathForWindows, pathToCheck, flags, pSecurityDescriptor); |
| return FileChannelImpl.open(fdObj, pathForWindows, flags.read, flags.write, null); |
| } |
| |
| /** |
| * Open/creates file, returning AsynchronousFileChannel to access the file |
| * |
| * @param pathForWindows |
| * The path of the file to open/create |
| * @param pathToCheck |
| * The path used for permission checks (if security manager) |
| * @param pool |
| * The thread pool that the channel is associated with |
| */ |
| static AsynchronousFileChannel newAsynchronousFileChannel(String pathForWindows, |
| String pathToCheck, |
| Set<? extends OpenOption> options, |
| long pSecurityDescriptor, |
| ThreadPool pool) |
| throws IOException |
| { |
| Flags flags = Flags.toFlags(options); |
| |
| // Overlapped I/O required |
| flags.overlapped = true; |
| |
| // default is reading |
| if (!flags.read && !flags.write) { |
| flags.read = true; |
| } |
| |
| // validation |
| if (flags.append) |
| throw new UnsupportedOperationException("APPEND not allowed"); |
| |
| // open file for overlapped I/O |
| FileDescriptor fdObj; |
| try { |
| fdObj = open(pathForWindows, pathToCheck, flags, pSecurityDescriptor); |
| } catch (WindowsException x) { |
| x.rethrowAsIOException(pathForWindows); |
| return null; |
| } |
| |
| // create the AsynchronousFileChannel |
| try { |
| return WindowsAsynchronousFileChannelImpl.open(fdObj, flags.read, flags.write, pool); |
| } catch (IOException x) { |
| // IOException is thrown if the file handle cannot be associated |
| // with the completion port. All we can do is close the file. |
| long handle = fdAccess.getHandle(fdObj); |
| CloseHandle(handle); |
| throw x; |
| } |
| } |
| |
| /** |
| * Opens file based on parameters and options, returning a FileDescriptor |
| * encapsulating the handle to the open file. |
| */ |
| private static FileDescriptor open(String pathForWindows, |
| String pathToCheck, |
| Flags flags, |
| long pSecurityDescriptor) |
| throws WindowsException |
| { |
| // set to true if file must be truncated after open |
| boolean truncateAfterOpen = false; |
| |
| // map options |
| int dwDesiredAccess = 0; |
| if (flags.read) |
| dwDesiredAccess |= GENERIC_READ; |
| if (flags.write) |
| dwDesiredAccess |= GENERIC_WRITE; |
| |
| int dwShareMode = 0; |
| if (flags.shareRead) |
| dwShareMode |= FILE_SHARE_READ; |
| if (flags.shareWrite) |
| dwShareMode |= FILE_SHARE_WRITE; |
| if (flags.shareDelete) |
| dwShareMode |= FILE_SHARE_DELETE; |
| |
| int dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; |
| int dwCreationDisposition = OPEN_EXISTING; |
| if (flags.write) { |
| if (flags.createNew) { |
| dwCreationDisposition = CREATE_NEW; |
| // force create to fail if file is orphaned reparse point |
| dwFlagsAndAttributes |= FILE_FLAG_OPEN_REPARSE_POINT; |
| } else { |
| if (flags.create) |
| dwCreationDisposition = OPEN_ALWAYS; |
| if (flags.truncateExisting) { |
| // Windows doesn't have a creation disposition that exactly |
| // corresponds to CREATE + TRUNCATE_EXISTING so we use |
| // the OPEN_ALWAYS mode and then truncate the file. |
| if (dwCreationDisposition == OPEN_ALWAYS) { |
| truncateAfterOpen = true; |
| } else { |
| dwCreationDisposition = TRUNCATE_EXISTING; |
| } |
| } |
| } |
| } |
| |
| if (flags.dsync || flags.sync) |
| dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH; |
| if (flags.overlapped) |
| dwFlagsAndAttributes |= FILE_FLAG_OVERLAPPED; |
| if (flags.deleteOnClose) |
| dwFlagsAndAttributes |= FILE_FLAG_DELETE_ON_CLOSE; |
| |
| // NOFOLLOW_LINKS and NOFOLLOW_REPARSEPOINT mean open reparse point |
| boolean okayToFollowLinks = true; |
| if (dwCreationDisposition != CREATE_NEW && |
| (flags.noFollowLinks || |
| flags.openReparsePoint || |
| flags.deleteOnClose)) |
| { |
| if (flags.noFollowLinks || flags.deleteOnClose) |
| okayToFollowLinks = false; |
| dwFlagsAndAttributes |= FILE_FLAG_OPEN_REPARSE_POINT; |
| } |
| |
| // permission check |
| if (pathToCheck != null) { |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm != null) { |
| if (flags.read) |
| sm.checkRead(pathToCheck); |
| if (flags.write) |
| sm.checkWrite(pathToCheck); |
| if (flags.deleteOnClose) |
| sm.checkDelete(pathToCheck); |
| } |
| } |
| |
| // open file |
| long handle = CreateFile(pathForWindows, |
| dwDesiredAccess, |
| dwShareMode, |
| pSecurityDescriptor, |
| dwCreationDisposition, |
| dwFlagsAndAttributes); |
| |
| // make sure this isn't a symbolic link. |
| if (!okayToFollowLinks) { |
| try { |
| if (WindowsFileAttributes.readAttributes(handle).isSymbolicLink()) |
| throw new WindowsException("File is symbolic link"); |
| } catch (WindowsException x) { |
| CloseHandle(handle); |
| throw x; |
| } |
| } |
| |
| // truncate file (for CREATE + TRUNCATE_EXISTING case) |
| if (truncateAfterOpen) { |
| try { |
| SetEndOfFile(handle); |
| } catch (WindowsException x) { |
| CloseHandle(handle); |
| throw x; |
| } |
| } |
| |
| // make the file sparse if needed |
| if (dwCreationDisposition == CREATE_NEW && flags.sparse) { |
| try { |
| DeviceIoControlSetSparse(handle); |
| } catch (WindowsException x) { |
| // ignore as sparse option is hint |
| } |
| } |
| |
| // create FileDescriptor and return |
| FileDescriptor fdObj = new FileDescriptor(); |
| fdAccess.setHandle(fdObj, handle); |
| fdAccess.setAppend(fdObj, flags.append); |
| return fdObj; |
| } |
| } |