8187443: Forest Consolidation: Move files to unified layout

Reviewed-by: darcy, ihse
This commit is contained in:
Erik Joelsson 2017-09-12 19:03:39 +02:00
parent 270fe13182
commit 3789983e89
56923 changed files with 3 additions and 15727 deletions

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2001, 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.ch;
import java.io.*;
import java.net.*;
/**
* Allows different platforms to call different native methods
* for read and write operations.
*/
class DatagramDispatcher extends NativeDispatcher
{
static {
IOUtil.load();
}
int read(FileDescriptor fd, long address, int len) throws IOException {
return read0(fd, address, len);
}
long readv(FileDescriptor fd, long address, int len) throws IOException {
return readv0(fd, address, len);
}
int write(FileDescriptor fd, long address, int len) throws IOException {
return write0(fd, address, len);
}
long writev(FileDescriptor fd, long address, int len) throws IOException {
return writev0(fd, address, len);
}
void close(FileDescriptor fd) throws IOException {
SocketDispatcher.close0(fd);
}
static native int read0(FileDescriptor fd, long address, int len)
throws IOException;
static native long readv0(FileDescriptor fd, long address, int len)
throws IOException;
static native int write0(FileDescriptor fd, long address, int len)
throws IOException;
static native long writev0(FileDescriptor fd, long address, int len)
throws IOException;
}

View file

@ -0,0 +1,43 @@
/*
* 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.ch;
import java.nio.channels.spi.AsynchronousChannelProvider;
/**
* Creates this platform's default asynchronous channel provider
*/
public class DefaultAsynchronousChannelProvider {
private DefaultAsynchronousChannelProvider() { }
/**
* Returns the default AsynchronousChannelProvider.
*/
public static AsynchronousChannelProvider create() {
return new WindowsAsynchronousChannelProvider();
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2001, 2002, 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.ch;
import java.nio.channels.spi.SelectorProvider;
/**
* Creates this platform's default SelectorProvider
*/
public class DefaultSelectorProvider {
/**
* Prevent instantiation.
*/
private DefaultSelectorProvider() { }
/**
* Returns the default SelectorProvider.
*/
public static SelectorProvider create() {
return new sun.nio.ch.WindowsSelectorProvider();
}
}

View file

@ -0,0 +1,180 @@
/*
* Copyright (c) 2000, 2017, 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.ch;
import java.io.FileDescriptor;
import java.io.IOException;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.misc.JavaIOFileDescriptorAccess;
import sun.security.action.GetPropertyAction;
class FileDispatcherImpl extends FileDispatcher {
private static final JavaIOFileDescriptorAccess fdAccess =
SharedSecrets.getJavaIOFileDescriptorAccess();
// set to true if fast file transmission (TransmitFile) is enabled
private static final boolean fastFileTransfer;
FileDispatcherImpl() { }
@Override
boolean needsPositionLock() {
return true;
}
int read(FileDescriptor fd, long address, int len)
throws IOException
{
return read0(fd, address, len);
}
int pread(FileDescriptor fd, long address, int len, long position)
throws IOException
{
return pread0(fd, address, len, position);
}
long readv(FileDescriptor fd, long address, int len) throws IOException {
return readv0(fd, address, len);
}
int write(FileDescriptor fd, long address, int len) throws IOException {
return write0(fd, address, len, fdAccess.getAppend(fd));
}
int pwrite(FileDescriptor fd, long address, int len, long position)
throws IOException
{
return pwrite0(fd, address, len, position);
}
long writev(FileDescriptor fd, long address, int len) throws IOException {
return writev0(fd, address, len, fdAccess.getAppend(fd));
}
int force(FileDescriptor fd, boolean metaData) throws IOException {
return force0(fd, metaData);
}
int truncate(FileDescriptor fd, long size) throws IOException {
return truncate0(fd, size);
}
int allocate(FileDescriptor fd, long size) throws IOException {
// truncate0() works for extending and truncating file size
return truncate0(fd, size);
}
long size(FileDescriptor fd) throws IOException {
return size0(fd);
}
int lock(FileDescriptor fd, boolean blocking, long pos, long size,
boolean shared) throws IOException
{
return lock0(fd, blocking, pos, size, shared);
}
void release(FileDescriptor fd, long pos, long size) throws IOException {
release0(fd, pos, size);
}
void close(FileDescriptor fd) throws IOException {
close0(fd);
}
FileDescriptor duplicateForMapping(FileDescriptor fd) throws IOException {
// on Windows we need to keep a handle to the file
FileDescriptor result = new FileDescriptor();
long handle = duplicateHandle(fdAccess.getHandle(fd));
fdAccess.setHandle(result, handle);
return result;
}
boolean canTransferToDirectly(java.nio.channels.SelectableChannel sc) {
return fastFileTransfer && sc.isBlocking();
}
boolean transferToDirectlyNeedsPositionLock() {
return true;
}
static boolean isFastFileTransferRequested() {
String fileTransferProp = GetPropertyAction
.privilegedGetProperty("jdk.nio.enableFastFileTransfer");
boolean enable;
if ("".equals(fileTransferProp)) {
enable = true;
} else {
enable = Boolean.parseBoolean(fileTransferProp);
}
return enable;
}
static {
IOUtil.load();
fastFileTransfer = isFastFileTransferRequested();
}
//-- Native methods
static native int read0(FileDescriptor fd, long address, int len)
throws IOException;
static native int pread0(FileDescriptor fd, long address, int len,
long position) throws IOException;
static native long readv0(FileDescriptor fd, long address, int len)
throws IOException;
static native int write0(FileDescriptor fd, long address, int len, boolean append)
throws IOException;
static native int pwrite0(FileDescriptor fd, long address, int len,
long position) throws IOException;
static native long writev0(FileDescriptor fd, long address, int len, boolean append)
throws IOException;
static native int force0(FileDescriptor fd, boolean metaData)
throws IOException;
static native int truncate0(FileDescriptor fd, long size)
throws IOException;
static native long size0(FileDescriptor fd) throws IOException;
static native int lock0(FileDescriptor fd, boolean blocking, long pos,
long size, boolean shared) throws IOException;
static native void release0(FileDescriptor fd, long pos, long size)
throws IOException;
static native void close0(FileDescriptor fd) throws IOException;
static native long duplicateHandle(long fd) throws IOException;
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2005, 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.ch;
import java.io.FileDescriptor;
import java.io.IOException;
/*
* Represents a key to a specific file on Windows
*/
public class FileKey {
private long dwVolumeSerialNumber;
private long nFileIndexHigh;
private long nFileIndexLow;
private FileKey() { }
public static FileKey create(FileDescriptor fd) throws IOException {
FileKey fk = new FileKey();
fk.init(fd);
return fk;
}
public int hashCode() {
return (int)(dwVolumeSerialNumber ^ (dwVolumeSerialNumber >>> 32)) +
(int)(nFileIndexHigh ^ (nFileIndexHigh >>> 32)) +
(int)(nFileIndexLow ^ (nFileIndexHigh >>> 32));
}
public boolean equals(Object obj) {
if (obj == this)
return true;
if (!(obj instanceof FileKey))
return false;
FileKey other = (FileKey)obj;
if ((this.dwVolumeSerialNumber != other.dwVolumeSerialNumber) ||
(this.nFileIndexHigh != other.nFileIndexHigh) ||
(this.nFileIndexLow != other.nFileIndexLow)) {
return false;
}
return true;
}
private native void init(FileDescriptor fd) throws IOException;
private static native void initIDs();
static {
IOUtil.load();
initIDs();
}
}

View file

@ -0,0 +1,439 @@
/*
* 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.ch;
import java.nio.channels.*;
import java.nio.channels.spi.AsynchronousChannelProvider;
import java.io.Closeable;
import java.io.IOException;
import java.io.FileDescriptor;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import jdk.internal.misc.Unsafe;
/**
* Windows implementation of AsynchronousChannelGroup encapsulating an I/O
* completion port.
*/
class Iocp extends AsynchronousChannelGroupImpl {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long INVALID_HANDLE_VALUE = -1L;
// maps completion key to channel
private final ReadWriteLock keyToChannelLock = new ReentrantReadWriteLock();
private final Map<Integer,OverlappedChannel> keyToChannel =
new HashMap<Integer,OverlappedChannel>();
private int nextCompletionKey;
// handle to completion port
private final long port;
// true if port has been closed
private boolean closed;
// the set of "stale" OVERLAPPED structures. These OVERLAPPED structures
// relate to I/O operations where the completion notification was not
// received in a timely manner after the channel is closed.
private final Set<Long> staleIoSet = new HashSet<Long>();
Iocp(AsynchronousChannelProvider provider, ThreadPool pool)
throws IOException
{
super(provider, pool);
this.port =
createIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, fixedThreadCount());
this.nextCompletionKey = 1;
}
Iocp start() {
startThreads(new EventHandlerTask());
return this;
}
/*
* Channels implements this interface support overlapped I/O and can be
* associated with a completion port.
*/
static interface OverlappedChannel extends Closeable {
/**
* Returns a reference to the pending I/O result.
*/
<V,A> PendingFuture<V,A> getByOverlapped(long overlapped);
}
// release all resources
void implClose() {
synchronized (this) {
if (closed)
return;
closed = true;
}
close0(port);
synchronized (staleIoSet) {
for (Long ov: staleIoSet) {
unsafe.freeMemory(ov);
}
staleIoSet.clear();
}
}
@Override
boolean isEmpty() {
keyToChannelLock.writeLock().lock();
try {
return keyToChannel.isEmpty();
} finally {
keyToChannelLock.writeLock().unlock();
}
}
@Override
final Object attachForeignChannel(final Channel channel, FileDescriptor fdObj)
throws IOException
{
int key = associate(new OverlappedChannel() {
public <V,A> PendingFuture<V,A> getByOverlapped(long overlapped) {
return null;
}
public void close() throws IOException {
channel.close();
}
}, 0L);
return Integer.valueOf(key);
}
@Override
final void detachForeignChannel(Object key) {
disassociate((Integer)key);
}
@Override
void closeAllChannels() {
/**
* On Windows the close operation will close the socket/file handle
* and then wait until all outstanding I/O operations have aborted.
* This is necessary as each channel's cache of OVERLAPPED structures
* can only be freed once all I/O operations have completed. As I/O
* completion requires a lookup of the keyToChannel then we must close
* the channels when not holding the write lock.
*/
final int MAX_BATCH_SIZE = 32;
OverlappedChannel channels[] = new OverlappedChannel[MAX_BATCH_SIZE];
int count;
do {
// grab a batch of up to 32 channels
keyToChannelLock.writeLock().lock();
count = 0;
try {
for (Integer key: keyToChannel.keySet()) {
channels[count++] = keyToChannel.get(key);
if (count >= MAX_BATCH_SIZE)
break;
}
} finally {
keyToChannelLock.writeLock().unlock();
}
// close them
for (int i=0; i<count; i++) {
try {
channels[i].close();
} catch (IOException ignore) { }
}
} while (count > 0);
}
private void wakeup() {
try {
postQueuedCompletionStatus(port, 0);
} catch (IOException e) {
// should not happen
throw new AssertionError(e);
}
}
@Override
void executeOnHandlerTask(Runnable task) {
synchronized (this) {
if (closed)
throw new RejectedExecutionException();
offerTask(task);
wakeup();
}
}
@Override
void shutdownHandlerTasks() {
// shutdown all handler threads
int nThreads = threadCount();
while (nThreads-- > 0) {
wakeup();
}
}
/**
* Associate the given handle with this group
*/
int associate(OverlappedChannel ch, long handle) throws IOException {
keyToChannelLock.writeLock().lock();
// generate a completion key (if not shutdown)
int key;
try {
if (isShutdown())
throw new ShutdownChannelGroupException();
// generate unique key
do {
key = nextCompletionKey++;
} while ((key == 0) || keyToChannel.containsKey(key));
// associate with I/O completion port
if (handle != 0L) {
createIoCompletionPort(handle, port, key, 0);
}
// setup mapping
keyToChannel.put(key, ch);
} finally {
keyToChannelLock.writeLock().unlock();
}
return key;
}
/**
* Disassociate channel from the group.
*/
void disassociate(int key) {
boolean checkForShutdown = false;
keyToChannelLock.writeLock().lock();
try {
keyToChannel.remove(key);
// last key to be removed so check if group is shutdown
if (keyToChannel.isEmpty())
checkForShutdown = true;
} finally {
keyToChannelLock.writeLock().unlock();
}
// continue shutdown
if (checkForShutdown && isShutdown()) {
try {
shutdownNow();
} catch (IOException ignore) { }
}
}
/**
* Invoked when a channel associated with this port is closed before
* notifications for all outstanding I/O operations have been received.
*/
void makeStale(Long overlapped) {
synchronized (staleIoSet) {
staleIoSet.add(overlapped);
}
}
/**
* Checks if the given OVERLAPPED is stale and if so, releases it.
*/
private void checkIfStale(long ov) {
synchronized (staleIoSet) {
boolean removed = staleIoSet.remove(ov);
if (removed) {
unsafe.freeMemory(ov);
}
}
}
/**
* The handler for consuming the result of an asynchronous I/O operation.
*/
static interface ResultHandler {
/**
* Invoked if the I/O operation completes successfully.
*/
public void completed(int bytesTransferred, boolean canInvokeDirect);
/**
* Invoked if the I/O operation fails.
*/
public void failed(int error, IOException ioe);
}
// Creates IOException for the given I/O error.
private static IOException translateErrorToIOException(int error) {
String msg = getErrorMessage(error);
if (msg == null)
msg = "Unknown error: 0x0" + Integer.toHexString(error);
return new IOException(msg);
}
/**
* Long-running task servicing system-wide or per-file completion port
*/
private class EventHandlerTask implements Runnable {
public void run() {
Invoker.GroupAndInvokeCount myGroupAndInvokeCount =
Invoker.getGroupAndInvokeCount();
boolean canInvokeDirect = (myGroupAndInvokeCount != null);
CompletionStatus ioResult = new CompletionStatus();
boolean replaceMe = false;
try {
for (;;) {
// reset invoke count
if (myGroupAndInvokeCount != null)
myGroupAndInvokeCount.resetInvokeCount();
// wait for I/O completion event
// A error here is fatal (thread will not be replaced)
replaceMe = false;
try {
getQueuedCompletionStatus(port, ioResult);
} catch (IOException x) {
// should not happen
x.printStackTrace();
return;
}
// handle wakeup to execute task or shutdown
if (ioResult.completionKey() == 0 &&
ioResult.overlapped() == 0L)
{
Runnable task = pollTask();
if (task == null) {
// shutdown request
return;
}
// run task
// (if error/exception then replace thread)
replaceMe = true;
task.run();
continue;
}
// map key to channel
OverlappedChannel ch = null;
keyToChannelLock.readLock().lock();
try {
ch = keyToChannel.get(ioResult.completionKey());
if (ch == null) {
checkIfStale(ioResult.overlapped());
continue;
}
} finally {
keyToChannelLock.readLock().unlock();
}
// lookup I/O request
PendingFuture<?,?> result = ch.getByOverlapped(ioResult.overlapped());
if (result == null) {
// we get here if the OVERLAPPED structure is associated
// with an I/O operation on a channel that was closed
// but the I/O operation event wasn't read in a timely
// manner. Alternatively, it may be related to a
// tryLock operation as the OVERLAPPED structures for
// these operations are not in the I/O cache.
checkIfStale(ioResult.overlapped());
continue;
}
// synchronize on result in case I/O completed immediately
// and was handled by initiator
synchronized (result) {
if (result.isDone()) {
continue;
}
// not handled by initiator
}
// invoke I/O result handler
int error = ioResult.error();
ResultHandler rh = (ResultHandler)result.getContext();
replaceMe = true; // (if error/exception then replace thread)
if (error == 0) {
rh.completed(ioResult.bytesTransferred(), canInvokeDirect);
} else {
rh.failed(error, translateErrorToIOException(error));
}
}
} finally {
// last thread to exit when shutdown releases resources
int remaining = threadExit(this, replaceMe);
if (remaining == 0 && isShutdown()) {
implClose();
}
}
}
}
/**
* Container for data returned by GetQueuedCompletionStatus
*/
private static class CompletionStatus {
private int error;
private int bytesTransferred;
private int completionKey;
private long overlapped;
private CompletionStatus() { }
int error() { return error; }
int bytesTransferred() { return bytesTransferred; }
int completionKey() { return completionKey; }
long overlapped() { return overlapped; }
}
// -- native methods --
private static native void initIDs();
private static native long createIoCompletionPort(long handle,
long existingPort, int completionKey, int concurrency) throws IOException;
private static native void close0(long handle);
private static native void getQueuedCompletionStatus(long completionPort,
CompletionStatus status) throws IOException;
private static native void postQueuedCompletionStatus(long completionPort,
int completionKey) throws IOException;
private static native String getErrorMessage(int error);
static {
IOUtil.load();
initIDs();
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2002, 2012, 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.ch;
// Signalling operations on native threads
class NativeThread {
static long current() {
// return 0 to ensure that async close of blocking sockets will close
// the underlying socket.
return 0;
}
static void signal(long nt) { }
}

View file

@ -0,0 +1,163 @@
/*
* Copyright (c) 2008, 2011, 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.ch;
import java.nio.channels.*;
import java.util.*;
import jdk.internal.misc.Unsafe;
/**
* Maintains a mapping of pending I/O requests (identified by the address of
* an OVERLAPPED structure) to Futures.
*/
class PendingIoCache {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final int addressSize = unsafe.addressSize();
private static int dependsArch(int value32, int value64) {
return (addressSize == 4) ? value32 : value64;
}
/*
* typedef struct _OVERLAPPED {
* DWORD Internal;
* DWORD InternalHigh;
* DWORD Offset;
* DWORD OffsetHigh;
* HANDLE hEvent;
* } OVERLAPPED;
*/
private static final int SIZEOF_OVERLAPPED = dependsArch(20, 32);
// set to true when closed
private boolean closed;
// set to true when thread is waiting for all I/O operations to complete
private boolean closePending;
// maps OVERLAPPED to PendingFuture
@SuppressWarnings("rawtypes")
private final Map<Long,PendingFuture> pendingIoMap =
new HashMap<Long,PendingFuture>();
// per-channel cache of OVERLAPPED structures
private long[] overlappedCache = new long[4];
private int overlappedCacheCount = 0;
PendingIoCache() {
}
long add(PendingFuture<?,?> result) {
synchronized (this) {
if (closed)
throw new AssertionError("Should not get here");
long ov;
if (overlappedCacheCount > 0) {
ov = overlappedCache[--overlappedCacheCount];
} else {
ov = unsafe.allocateMemory(SIZEOF_OVERLAPPED);
}
pendingIoMap.put(ov, result);
return ov;
}
}
@SuppressWarnings("unchecked")
<V,A> PendingFuture<V,A> remove(long overlapped) {
synchronized (this) {
PendingFuture<V,A> res = pendingIoMap.remove(overlapped);
if (res != null) {
if (overlappedCacheCount < overlappedCache.length) {
overlappedCache[overlappedCacheCount++] = overlapped;
} else {
// cache full or channel closing
unsafe.freeMemory(overlapped);
}
// notify closing thread.
if (closePending) {
this.notifyAll();
}
}
return res;
}
}
void close() {
synchronized (this) {
if (closed)
return;
// handle case where I/O operations that have not completed.
if (!pendingIoMap.isEmpty())
clearPendingIoMap();
// release memory for any cached OVERLAPPED structures
while (overlappedCacheCount > 0) {
unsafe.freeMemory( overlappedCache[--overlappedCacheCount] );
}
// done
closed = true;
}
}
private void clearPendingIoMap() {
assert Thread.holdsLock(this);
// wait up to 50ms for the I/O operations to complete
closePending = true;
try {
this.wait(50);
} catch (InterruptedException x) {
Thread.currentThread().interrupt();
}
closePending = false;
if (pendingIoMap.isEmpty())
return;
// cause all pending I/O operations to fail
// simulate the failure of all pending I/O operations.
for (Long ov: pendingIoMap.keySet()) {
PendingFuture<?,?> result = pendingIoMap.get(ov);
assert !result.isDone();
// make I/O port aware of the stale OVERLAPPED structure
Iocp iocp = (Iocp)((Groupable)result.channel()).group();
iocp.makeStale(ov);
// execute a task that invokes the result handler's failed method
final Iocp.ResultHandler rh = (Iocp.ResultHandler)result.getContext();
Runnable task = new Runnable() {
public void run() {
rh.failed(-1, new AsynchronousCloseException());
}
};
iocp.executeOnPooledThread(task);
}
pendingIoMap.clear();
}
}

View file

@ -0,0 +1,185 @@
/*
* Copyright (c) 2002, 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.ch;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.*;
import java.nio.channels.*;
import java.nio.channels.spi.*;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import java.security.SecureRandom;
import java.util.Random;
/**
* A simple Pipe implementation based on a socket connection.
*/
class PipeImpl
extends Pipe
{
// Number of bytes in the secret handshake.
private static final int NUM_SECRET_BYTES = 16;
// Random object for handshake values
private static final Random RANDOM_NUMBER_GENERATOR = new SecureRandom();
// Source and sink channels
private SourceChannel source;
private SinkChannel sink;
private class Initializer
implements PrivilegedExceptionAction<Void>
{
private final SelectorProvider sp;
private IOException ioe = null;
private Initializer(SelectorProvider sp) {
this.sp = sp;
}
@Override
public Void run() throws IOException {
LoopbackConnector connector = new LoopbackConnector();
connector.run();
if (ioe instanceof ClosedByInterruptException) {
ioe = null;
Thread connThread = new Thread(connector) {
@Override
public void interrupt() {}
};
connThread.start();
for (;;) {
try {
connThread.join();
break;
} catch (InterruptedException ex) {}
}
Thread.currentThread().interrupt();
}
if (ioe != null)
throw new IOException("Unable to establish loopback connection", ioe);
return null;
}
private class LoopbackConnector implements Runnable {
@Override
public void run() {
ServerSocketChannel ssc = null;
SocketChannel sc1 = null;
SocketChannel sc2 = null;
try {
// Create secret with a backing array.
ByteBuffer secret = ByteBuffer.allocate(NUM_SECRET_BYTES);
ByteBuffer bb = ByteBuffer.allocate(NUM_SECRET_BYTES);
// Loopback address
InetAddress lb = InetAddress.getByName("127.0.0.1");
assert(lb.isLoopbackAddress());
InetSocketAddress sa = null;
for(;;) {
// Bind ServerSocketChannel to a port on the loopback
// address
if (ssc == null || !ssc.isOpen()) {
ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(lb, 0));
sa = new InetSocketAddress(lb, ssc.socket().getLocalPort());
}
// Establish connection (assume connections are eagerly
// accepted)
sc1 = SocketChannel.open(sa);
RANDOM_NUMBER_GENERATOR.nextBytes(secret.array());
do {
sc1.write(secret);
} while (secret.hasRemaining());
secret.rewind();
// Get a connection and verify it is legitimate
sc2 = ssc.accept();
do {
sc2.read(bb);
} while (bb.hasRemaining());
bb.rewind();
if (bb.equals(secret))
break;
sc2.close();
sc1.close();
}
// Create source and sink channels
source = new SourceChannelImpl(sp, sc1);
sink = new SinkChannelImpl(sp, sc2);
} catch (IOException e) {
try {
if (sc1 != null)
sc1.close();
if (sc2 != null)
sc2.close();
} catch (IOException e2) {}
ioe = e;
} finally {
try {
if (ssc != null)
ssc.close();
} catch (IOException e2) {}
}
}
}
}
PipeImpl(final SelectorProvider sp) throws IOException {
try {
AccessController.doPrivileged(new Initializer(sp));
} catch (PrivilegedActionException x) {
throw (IOException)x.getCause();
}
}
public SourceChannel source() {
return source;
}
public SinkChannel sink() {
return sink;
}
}

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2001, 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.ch;
import java.lang.annotation.Native;
/**
* Manipulates a native array of structs corresponding to (fd, events) pairs.
*
* typedef struct pollfd {
* SOCKET fd; // 4 bytes
* short events; // 2 bytes
* } pollfd_t;
*
* @author Konstantin Kladko
* @author Mike McCloskey
*/
class PollArrayWrapper {
private AllocatedNativeObject pollArray; // The fd array
long pollArrayAddress; // pollArrayAddress
@Native private static final short FD_OFFSET = 0; // fd offset in pollfd
@Native private static final short EVENT_OFFSET = 4; // events offset in pollfd
static short SIZE_POLLFD = 8; // sizeof pollfd struct
private int size; // Size of the pollArray
PollArrayWrapper(int newSize) {
int allocationSize = newSize * SIZE_POLLFD;
pollArray = new AllocatedNativeObject(allocationSize, true);
pollArrayAddress = pollArray.address();
this.size = newSize;
}
// Prepare another pollfd struct for use.
void addEntry(int index, SelectionKeyImpl ski) {
putDescriptor(index, ski.channel.getFDVal());
}
// Writes the pollfd entry from the source wrapper at the source index
// over the entry in the target wrapper at the target index.
void replaceEntry(PollArrayWrapper source, int sindex,
PollArrayWrapper target, int tindex) {
target.putDescriptor(tindex, source.getDescriptor(sindex));
target.putEventOps(tindex, source.getEventOps(sindex));
}
// Grows the pollfd array to new size
void grow(int newSize) {
PollArrayWrapper temp = new PollArrayWrapper(newSize);
for (int i = 0; i < size; i++)
replaceEntry(this, i, temp, i);
pollArray.free();
pollArray = temp.pollArray;
this.size = temp.size;
pollArrayAddress = pollArray.address();
}
void free() {
pollArray.free();
}
// Access methods for fd structures
void putDescriptor(int i, int fd) {
pollArray.putInt(SIZE_POLLFD * i + FD_OFFSET, fd);
}
void putEventOps(int i, int event) {
pollArray.putShort(SIZE_POLLFD * i + EVENT_OFFSET, (short)event);
}
int getEventOps(int i) {
return pollArray.getShort(SIZE_POLLFD * i + EVENT_OFFSET);
}
int getDescriptor(int i) {
return pollArray.getInt(SIZE_POLLFD * i + FD_OFFSET);
}
// Adds Windows wakeup socket at a given index.
void addWakeupSocket(int fdVal, int index) {
putDescriptor(index, fdVal);
putEventOps(index, Net.POLLIN);
}
}

View file

@ -0,0 +1,142 @@
/*
* Copyright (c) 2002, 2006, 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.ch;
import java.io.IOException;
import java.io.FileDescriptor;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.channels.spi.*;
/**
* Pipe.SinkChannel implementation based on socket connection.
*/
class SinkChannelImpl
extends Pipe.SinkChannel
implements SelChImpl
{
// The SocketChannel assoicated with this pipe
SocketChannel sc;
public FileDescriptor getFD() {
return ((SocketChannelImpl)sc).getFD();
}
public int getFDVal() {
return ((SocketChannelImpl)sc).getFDVal();
}
SinkChannelImpl(SelectorProvider sp, SocketChannel sc) {
super(sp);
this.sc = sc;
}
protected void implCloseSelectableChannel() throws IOException {
if (!isRegistered())
kill();
}
public void kill() throws IOException {
sc.close();
}
protected void implConfigureBlocking(boolean block) throws IOException {
sc.configureBlocking(block);
}
public boolean translateReadyOps(int ops, int initialOps,
SelectionKeyImpl sk) {
int intOps = sk.nioInterestOps(); // Do this just once, it synchronizes
int oldOps = sk.nioReadyOps();
int newOps = initialOps;
if ((ops & Net.POLLNVAL) != 0)
throw new Error("POLLNVAL detected");
if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) {
newOps = intOps;
sk.nioReadyOps(newOps);
return (newOps & ~oldOps) != 0;
}
if (((ops & Net.POLLOUT) != 0) &&
((intOps & SelectionKey.OP_WRITE) != 0))
newOps |= SelectionKey.OP_WRITE;
sk.nioReadyOps(newOps);
return (newOps & ~oldOps) != 0;
}
public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
return translateReadyOps(ops, sk.nioReadyOps(), sk);
}
public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
return translateReadyOps(ops, 0, sk);
}
public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
if ((ops & SelectionKey.OP_WRITE) != 0)
ops = Net.POLLOUT;
sk.selector.putEventOps(sk, ops);
}
public int write(ByteBuffer src) throws IOException {
try {
return sc.write(src);
} catch (AsynchronousCloseException x) {
close();
throw x;
}
}
public long write(ByteBuffer[] srcs) throws IOException {
try {
return sc.write(srcs);
} catch (AsynchronousCloseException x) {
close();
throw x;
}
}
public long write(ByteBuffer[] srcs, int offset, int length)
throws IOException
{
if ((offset < 0) || (length < 0) || (offset > srcs.length - length))
throw new IndexOutOfBoundsException();
try {
return write(Util.subsequence(srcs, offset, length));
} catch (AsynchronousCloseException x) {
close();
throw x;
}
}
}

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2000, 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.ch;
import java.io.*;
/**
* Allows different platforms to call different native methods
* for read and write operations.
*/
class SocketDispatcher extends NativeDispatcher
{
static {
IOUtil.load();
}
int read(FileDescriptor fd, long address, int len) throws IOException {
return read0(fd, address, len);
}
long readv(FileDescriptor fd, long address, int len) throws IOException {
return readv0(fd, address, len);
}
int write(FileDescriptor fd, long address, int len) throws IOException {
return write0(fd, address, len);
}
long writev(FileDescriptor fd, long address, int len) throws IOException {
return writev0(fd, address, len);
}
void preClose(FileDescriptor fd) throws IOException {
preClose0(fd);
}
void close(FileDescriptor fd) throws IOException {
close0(fd);
}
//-- Native methods
static native int read0(FileDescriptor fd, long address, int len)
throws IOException;
static native long readv0(FileDescriptor fd, long address, int len)
throws IOException;
static native int write0(FileDescriptor fd, long address, int len)
throws IOException;
static native long writev0(FileDescriptor fd, long address, int len)
throws IOException;
static native void preClose0(FileDescriptor fd) throws IOException;
static native void close0(FileDescriptor fd) throws IOException;
}

View file

@ -0,0 +1,142 @@
/*
* Copyright (c) 2002, 2006, 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.ch;
import java.io.IOException;
import java.io.FileDescriptor;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.channels.spi.*;
/**
* Pipe.SourceChannel implementation based on socket connection.
*/
class SourceChannelImpl
extends Pipe.SourceChannel
implements SelChImpl
{
// The SocketChannel assoicated with this pipe
SocketChannel sc;
public FileDescriptor getFD() {
return ((SocketChannelImpl) sc).getFD();
}
public int getFDVal() {
return ((SocketChannelImpl) sc).getFDVal();
}
SourceChannelImpl(SelectorProvider sp, SocketChannel sc) {
super(sp);
this.sc = sc;
}
protected void implCloseSelectableChannel() throws IOException {
if (!isRegistered())
kill();
}
public void kill() throws IOException {
sc.close();
}
protected void implConfigureBlocking(boolean block) throws IOException {
sc.configureBlocking(block);
}
public boolean translateReadyOps(int ops, int initialOps,
SelectionKeyImpl sk) {
int intOps = sk.nioInterestOps(); // Do this just once, it synchronizes
int oldOps = sk.nioReadyOps();
int newOps = initialOps;
if ((ops & Net.POLLNVAL) != 0)
throw new Error("POLLNVAL detected");
if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) {
newOps = intOps;
sk.nioReadyOps(newOps);
return (newOps & ~oldOps) != 0;
}
if (((ops & Net.POLLIN) != 0) &&
((intOps & SelectionKey.OP_READ) != 0))
newOps |= SelectionKey.OP_READ;
sk.nioReadyOps(newOps);
return (newOps & ~oldOps) != 0;
}
public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
return translateReadyOps(ops, sk.nioReadyOps(), sk);
}
public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
return translateReadyOps(ops, 0, sk);
}
public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
if ((ops & SelectionKey.OP_READ) != 0)
ops = Net.POLLIN;
sk.selector.putEventOps(sk, ops);
}
public int read(ByteBuffer dst) throws IOException {
try {
return sc.read(dst);
} catch (AsynchronousCloseException x) {
close();
throw x;
}
}
public long read(ByteBuffer[] dsts, int offset, int length)
throws IOException
{
if ((offset < 0) || (length < 0) || (offset > dsts.length - length))
throw new IndexOutOfBoundsException();
try {
return read(Util.subsequence(dsts, offset, length));
} catch (AsynchronousCloseException x) {
close();
throw x;
}
}
public long read(ByteBuffer[] dsts) throws IOException {
try {
return sc.read(dsts);
} catch (AsynchronousCloseException x) {
close();
throw x;
}
}
}

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2008, 2010, 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.ch;
import java.nio.channels.*;
import java.nio.channels.spi.AsynchronousChannelProvider;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.io.IOException;
public class WindowsAsynchronousChannelProvider
extends AsynchronousChannelProvider
{
private static volatile Iocp defaultIocp;
public WindowsAsynchronousChannelProvider() {
// nothing to do
}
private Iocp defaultIocp() throws IOException {
if (defaultIocp == null) {
synchronized (WindowsAsynchronousChannelProvider.class) {
if (defaultIocp == null) {
// default thread pool may be shared with AsynchronousFileChannels
defaultIocp = new Iocp(this, ThreadPool.getDefault()).start();
}
}
}
return defaultIocp;
}
@Override
public AsynchronousChannelGroup openAsynchronousChannelGroup(int nThreads, ThreadFactory factory)
throws IOException
{
return new Iocp(this, ThreadPool.create(nThreads, factory)).start();
}
@Override
public AsynchronousChannelGroup openAsynchronousChannelGroup(ExecutorService executor, int initialSize)
throws IOException
{
return new Iocp(this, ThreadPool.wrap(executor, initialSize)).start();
}
private Iocp toIocp(AsynchronousChannelGroup group) throws IOException {
if (group == null) {
return defaultIocp();
} else {
if (!(group instanceof Iocp))
throw new IllegalChannelGroupException();
return (Iocp)group;
}
}
@Override
public AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(AsynchronousChannelGroup group)
throws IOException
{
return new WindowsAsynchronousServerSocketChannelImpl(toIocp(group));
}
@Override
public AsynchronousSocketChannel openAsynchronousSocketChannel(AsynchronousChannelGroup group)
throws IOException
{
return new WindowsAsynchronousSocketChannelImpl(toIocp(group));
}
}

View file

@ -0,0 +1,736 @@
/*
* 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.ch;
import java.nio.channels.*;
import java.util.concurrent.*;
import java.nio.ByteBuffer;
import java.nio.BufferOverflowException;
import java.io.IOException;
import java.io.FileDescriptor;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.misc.JavaIOFileDescriptorAccess;
/**
* Windows implementation of AsynchronousFileChannel using overlapped I/O.
*/
public class WindowsAsynchronousFileChannelImpl
extends AsynchronousFileChannelImpl
implements Iocp.OverlappedChannel, Groupable
{
private static final JavaIOFileDescriptorAccess fdAccess =
SharedSecrets.getJavaIOFileDescriptorAccess();
// error when EOF is detected asynchronously.
private static final int ERROR_HANDLE_EOF = 38;
// Lazy initialization of default I/O completion port
private static class DefaultIocpHolder {
static final Iocp defaultIocp = defaultIocp();
private static Iocp defaultIocp() {
try {
return new Iocp(null, ThreadPool.createDefault()).start();
} catch (IOException ioe) {
throw new InternalError(ioe);
}
}
}
// Used for force/truncate/size methods
private static final FileDispatcher nd = new FileDispatcherImpl();
// The handle is extracted for use in native methods invoked from this class.
private final long handle;
// The key that identifies the channel's association with the I/O port
private final int completionKey;
// I/O completion port (group)
private final Iocp iocp;
private final boolean isDefaultIocp;
// Caches OVERLAPPED structure for each outstanding I/O operation
private final PendingIoCache ioCache;
private WindowsAsynchronousFileChannelImpl(FileDescriptor fdObj,
boolean reading,
boolean writing,
Iocp iocp,
boolean isDefaultIocp)
throws IOException
{
super(fdObj, reading, writing, iocp.executor());
this.handle = fdAccess.getHandle(fdObj);
this.iocp = iocp;
this.isDefaultIocp = isDefaultIocp;
this.ioCache = new PendingIoCache();
this.completionKey = iocp.associate(this, handle);
}
public static AsynchronousFileChannel open(FileDescriptor fdo,
boolean reading,
boolean writing,
ThreadPool pool)
throws IOException
{
Iocp iocp;
boolean isDefaultIocp;
if (pool == null) {
iocp = DefaultIocpHolder.defaultIocp;
isDefaultIocp = true;
} else {
iocp = new Iocp(null, pool).start();
isDefaultIocp = false;
}
try {
return new
WindowsAsynchronousFileChannelImpl(fdo, reading, writing, iocp, isDefaultIocp);
} catch (IOException x) {
// error binding to port so need to close it (if created for this channel)
if (!isDefaultIocp)
iocp.implClose();
throw x;
}
}
@Override
public <V,A> PendingFuture<V,A> getByOverlapped(long overlapped) {
return ioCache.remove(overlapped);
}
@Override
public void close() throws IOException {
closeLock.writeLock().lock();
try {
if (closed)
return; // already closed
closed = true;
} finally {
closeLock.writeLock().unlock();
}
// invalidate all locks held for this channel
invalidateAllLocks();
// close the file
close0(handle);
// waits until all I/O operations have completed
ioCache.close();
// disassociate from port
iocp.disassociate(completionKey);
// for the non-default group close the port
if (!isDefaultIocp)
iocp.detachFromThreadPool();
}
@Override
public AsynchronousChannelGroupImpl group() {
return iocp;
}
/**
* Translates Throwable to IOException
*/
private static IOException toIOException(Throwable x) {
if (x instanceof IOException) {
if (x instanceof ClosedChannelException)
x = new AsynchronousCloseException();
return (IOException)x;
}
return new IOException(x);
}
@Override
public long size() throws IOException {
try {
begin();
return nd.size(fdObj);
} finally {
end();
}
}
@Override
public AsynchronousFileChannel truncate(long size) throws IOException {
if (size < 0)
throw new IllegalArgumentException("Negative size");
if (!writing)
throw new NonWritableChannelException();
try {
begin();
if (size > nd.size(fdObj))
return this;
nd.truncate(fdObj, size);
} finally {
end();
}
return this;
}
@Override
public void force(boolean metaData) throws IOException {
try {
begin();
nd.force(fdObj, metaData);
} finally {
end();
}
}
// -- file locking --
/**
* Task that initiates locking operation and handles completion result.
*/
private class LockTask<A> implements Runnable, Iocp.ResultHandler {
private final long position;
private final FileLockImpl fli;
private final PendingFuture<FileLock,A> result;
LockTask(long position,
FileLockImpl fli,
PendingFuture<FileLock,A> result)
{
this.position = position;
this.fli = fli;
this.result = result;
}
@Override
public void run() {
long overlapped = 0L;
boolean pending = false;
try {
begin();
// allocate OVERLAPPED structure
overlapped = ioCache.add(result);
// synchronize on result to avoid race with handler thread
// when lock is acquired immediately.
synchronized (result) {
int n = lockFile(handle, position, fli.size(), fli.isShared(),
overlapped);
if (n == IOStatus.UNAVAILABLE) {
// I/O is pending
pending = true;
return;
}
// acquired lock immediately
result.setResult(fli);
}
} catch (Throwable x) {
// lock failed or channel closed
removeFromFileLockTable(fli);
result.setFailure(toIOException(x));
} finally {
if (!pending && overlapped != 0L)
ioCache.remove(overlapped);
end();
}
// invoke completion handler
Invoker.invoke(result);
}
@Override
public void completed(int bytesTransferred, boolean canInvokeDirect) {
// release waiters and invoke completion handler
result.setResult(fli);
if (canInvokeDirect) {
Invoker.invokeUnchecked(result);
} else {
Invoker.invoke(result);
}
}
@Override
public void failed(int error, IOException x) {
// lock not acquired so remove from lock table
removeFromFileLockTable(fli);
// release waiters
if (isOpen()) {
result.setFailure(x);
} else {
result.setFailure(new AsynchronousCloseException());
}
Invoker.invoke(result);
}
}
@Override
<A> Future<FileLock> implLock(final long position,
final long size,
final boolean shared,
A attachment,
final CompletionHandler<FileLock,? super A> handler)
{
if (shared && !reading)
throw new NonReadableChannelException();
if (!shared && !writing)
throw new NonWritableChannelException();
// add to lock table
FileLockImpl fli = addToFileLockTable(position, size, shared);
if (fli == null) {
Throwable exc = new ClosedChannelException();
if (handler == null)
return CompletedFuture.withFailure(exc);
Invoker.invoke(this, handler, attachment, null, exc);
return null;
}
// create Future and task that will be invoked to acquire lock
PendingFuture<FileLock,A> result =
new PendingFuture<FileLock,A>(this, handler, attachment);
LockTask<A> lockTask = new LockTask<A>(position, fli, result);
result.setContext(lockTask);
// initiate I/O
lockTask.run();
return result;
}
static final int NO_LOCK = -1; // Failed to lock
static final int LOCKED = 0; // Obtained requested lock
@Override
public FileLock tryLock(long position, long size, boolean shared)
throws IOException
{
if (shared && !reading)
throw new NonReadableChannelException();
if (!shared && !writing)
throw new NonWritableChannelException();
// add to lock table
final FileLockImpl fli = addToFileLockTable(position, size, shared);
if (fli == null)
throw new ClosedChannelException();
boolean gotLock = false;
try {
begin();
// try to acquire the lock
int res = nd.lock(fdObj, false, position, size, shared);
if (res == NO_LOCK)
return null;
gotLock = true;
return fli;
} finally {
if (!gotLock)
removeFromFileLockTable(fli);
end();
}
}
@Override
protected void implRelease(FileLockImpl fli) throws IOException {
nd.release(fdObj, fli.position(), fli.size());
}
/**
* Task that initiates read operation and handles completion result.
*/
private class ReadTask<A> implements Runnable, Iocp.ResultHandler {
private final ByteBuffer dst;
private final int pos, rem; // buffer position/remaining
private final long position; // file position
private final PendingFuture<Integer,A> result;
// set to dst if direct; otherwise set to substituted direct buffer
private volatile ByteBuffer buf;
ReadTask(ByteBuffer dst,
int pos,
int rem,
long position,
PendingFuture<Integer,A> result)
{
this.dst = dst;
this.pos = pos;
this.rem = rem;
this.position = position;
this.result = result;
}
void releaseBufferIfSubstituted() {
if (buf != dst)
Util.releaseTemporaryDirectBuffer(buf);
}
void updatePosition(int bytesTransferred) {
// if the I/O succeeded then adjust buffer position
if (bytesTransferred > 0) {
if (buf == dst) {
try {
dst.position(pos + bytesTransferred);
} catch (IllegalArgumentException x) {
// someone has changed the position; ignore
}
} else {
// had to substitute direct buffer
buf.position(bytesTransferred).flip();
try {
dst.put(buf);
} catch (BufferOverflowException x) {
// someone has changed the position; ignore
}
}
}
}
@Override
public void run() {
int n = -1;
long overlapped = 0L;
long address;
// Substitute a native buffer if not direct
if (dst instanceof DirectBuffer) {
buf = dst;
address = ((DirectBuffer)dst).address() + pos;
} else {
buf = Util.getTemporaryDirectBuffer(rem);
address = ((DirectBuffer)buf).address();
}
boolean pending = false;
try {
begin();
// allocate OVERLAPPED
overlapped = ioCache.add(result);
// initiate read
n = readFile(handle, address, rem, position, overlapped);
if (n == IOStatus.UNAVAILABLE) {
// I/O is pending
pending = true;
return;
} else if (n == IOStatus.EOF) {
result.setResult(n);
} else {
throw new InternalError("Unexpected result: " + n);
}
} catch (Throwable x) {
// failed to initiate read
result.setFailure(toIOException(x));
} finally {
if (!pending) {
// release resources
if (overlapped != 0L)
ioCache.remove(overlapped);
releaseBufferIfSubstituted();
}
end();
}
// invoke completion handler
Invoker.invoke(result);
}
/**
* Executed when the I/O has completed
*/
@Override
public void completed(int bytesTransferred, boolean canInvokeDirect) {
updatePosition(bytesTransferred);
// return direct buffer to cache if substituted
releaseBufferIfSubstituted();
// release waiters and invoke completion handler
result.setResult(bytesTransferred);
if (canInvokeDirect) {
Invoker.invokeUnchecked(result);
} else {
Invoker.invoke(result);
}
}
@Override
public void failed(int error, IOException x) {
// if EOF detected asynchronously then it is reported as error
if (error == ERROR_HANDLE_EOF) {
completed(-1, false);
} else {
// return direct buffer to cache if substituted
releaseBufferIfSubstituted();
// release waiters
if (isOpen()) {
result.setFailure(x);
} else {
result.setFailure(new AsynchronousCloseException());
}
Invoker.invoke(result);
}
}
}
@Override
<A> Future<Integer> implRead(ByteBuffer dst,
long position,
A attachment,
CompletionHandler<Integer,? super A> handler)
{
if (!reading)
throw new NonReadableChannelException();
if (position < 0)
throw new IllegalArgumentException("Negative position");
if (dst.isReadOnly())
throw new IllegalArgumentException("Read-only buffer");
// check if channel is closed
if (!isOpen()) {
Throwable exc = new ClosedChannelException();
if (handler == null)
return CompletedFuture.withFailure(exc);
Invoker.invoke(this, handler, attachment, null, exc);
return null;
}
int pos = dst.position();
int lim = dst.limit();
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
// no space remaining
if (rem == 0) {
if (handler == null)
return CompletedFuture.withResult(0);
Invoker.invoke(this, handler, attachment, 0, null);
return null;
}
// create Future and task that initiates read
PendingFuture<Integer,A> result =
new PendingFuture<Integer,A>(this, handler, attachment);
ReadTask<A> readTask = new ReadTask<A>(dst, pos, rem, position, result);
result.setContext(readTask);
// initiate I/O
readTask.run();
return result;
}
/**
* Task that initiates write operation and handles completion result.
*/
private class WriteTask<A> implements Runnable, Iocp.ResultHandler {
private final ByteBuffer src;
private final int pos, rem; // buffer position/remaining
private final long position; // file position
private final PendingFuture<Integer,A> result;
// set to src if direct; otherwise set to substituted direct buffer
private volatile ByteBuffer buf;
WriteTask(ByteBuffer src,
int pos,
int rem,
long position,
PendingFuture<Integer,A> result)
{
this.src = src;
this.pos = pos;
this.rem = rem;
this.position = position;
this.result = result;
}
void releaseBufferIfSubstituted() {
if (buf != src)
Util.releaseTemporaryDirectBuffer(buf);
}
void updatePosition(int bytesTransferred) {
// if the I/O succeeded then adjust buffer position
if (bytesTransferred > 0) {
try {
src.position(pos + bytesTransferred);
} catch (IllegalArgumentException x) {
// someone has changed the position
}
}
}
@Override
public void run() {
int n = -1;
long overlapped = 0L;
long address;
// Substitute a native buffer if not direct
if (src instanceof DirectBuffer) {
buf = src;
address = ((DirectBuffer)src).address() + pos;
} else {
buf = Util.getTemporaryDirectBuffer(rem);
buf.put(src);
buf.flip();
// temporarily restore position as we don't know how many bytes
// will be written
src.position(pos);
address = ((DirectBuffer)buf).address();
}
try {
begin();
// allocate an OVERLAPPED structure
overlapped = ioCache.add(result);
// initiate the write
n = writeFile(handle, address, rem, position, overlapped);
if (n == IOStatus.UNAVAILABLE) {
// I/O is pending
return;
} else {
throw new InternalError("Unexpected result: " + n);
}
} catch (Throwable x) {
// failed to initiate read:
result.setFailure(toIOException(x));
// release resources
if (overlapped != 0L)
ioCache.remove(overlapped);
releaseBufferIfSubstituted();
} finally {
end();
}
// invoke completion handler
Invoker.invoke(result);
}
/**
* Executed when the I/O has completed
*/
@Override
public void completed(int bytesTransferred, boolean canInvokeDirect) {
updatePosition(bytesTransferred);
// return direct buffer to cache if substituted
releaseBufferIfSubstituted();
// release waiters and invoke completion handler
result.setResult(bytesTransferred);
if (canInvokeDirect) {
Invoker.invokeUnchecked(result);
} else {
Invoker.invoke(result);
}
}
@Override
public void failed(int error, IOException x) {
// return direct buffer to cache if substituted
releaseBufferIfSubstituted();
// release waiters and invoker completion handler
if (isOpen()) {
result.setFailure(x);
} else {
result.setFailure(new AsynchronousCloseException());
}
Invoker.invoke(result);
}
}
<A> Future<Integer> implWrite(ByteBuffer src,
long position,
A attachment,
CompletionHandler<Integer,? super A> handler)
{
if (!writing)
throw new NonWritableChannelException();
if (position < 0)
throw new IllegalArgumentException("Negative position");
// check if channel is closed
if (!isOpen()) {
Throwable exc = new ClosedChannelException();
if (handler == null)
return CompletedFuture.withFailure(exc);
Invoker.invoke(this, handler, attachment, null, exc);
return null;
}
int pos = src.position();
int lim = src.limit();
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
// nothing to write
if (rem == 0) {
if (handler == null)
return CompletedFuture.withResult(0);
Invoker.invoke(this, handler, attachment, 0, null);
return null;
}
// create Future and task to initiate write
PendingFuture<Integer,A> result =
new PendingFuture<Integer,A>(this, handler, attachment);
WriteTask<A> writeTask = new WriteTask<A>(src, pos, rem, position, result);
result.setContext(writeTask);
// initiate I/O
writeTask.run();
return result;
}
// -- Native methods --
private static native int readFile(long handle, long address, int len,
long offset, long overlapped) throws IOException;
private static native int writeFile(long handle, long address, int len,
long offset, long overlapped) throws IOException;
private static native int lockFile(long handle, long position, long size,
boolean shared, long overlapped) throws IOException;
private static native void close0(long handle);
static {
IOUtil.load();
}
}

View file

@ -0,0 +1,365 @@
/*
* 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.ch;
import java.nio.channels.*;
import java.net.InetSocketAddress;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.io.IOException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import jdk.internal.misc.Unsafe;
/**
* Windows implementation of AsynchronousServerSocketChannel using overlapped I/O.
*/
class WindowsAsynchronousServerSocketChannelImpl
extends AsynchronousServerSocketChannelImpl implements Iocp.OverlappedChannel
{
private static final Unsafe unsafe = Unsafe.getUnsafe();
// 2 * (sizeof(SOCKET_ADDRESS) + 16)
private static final int DATA_BUFFER_SIZE = 88;
private final long handle;
private final int completionKey;
private final Iocp iocp;
// typically there will be zero, or one I/O operations pending. In rare
// cases there may be more. These rare cases arise when a sequence of accept
// operations complete immediately and handled by the initiating thread.
// The corresponding OVERLAPPED cannot be reused/released until the completion
// event has been posted.
private final PendingIoCache ioCache;
// the data buffer to receive the local/remote socket address
private final long dataBuffer;
// flag to indicate that an accept operation is outstanding
private AtomicBoolean accepting = new AtomicBoolean();
WindowsAsynchronousServerSocketChannelImpl(Iocp iocp) throws IOException {
super(iocp);
// associate socket with given completion port
long h = IOUtil.fdVal(fd);
int key;
try {
key = iocp.associate(this, h);
} catch (IOException x) {
closesocket0(h); // prevent leak
throw x;
}
this.handle = h;
this.completionKey = key;
this.iocp = iocp;
this.ioCache = new PendingIoCache();
this.dataBuffer = unsafe.allocateMemory(DATA_BUFFER_SIZE);
}
@Override
public <V,A> PendingFuture<V,A> getByOverlapped(long overlapped) {
return ioCache.remove(overlapped);
}
@Override
void implClose() throws IOException {
// close socket (which may cause outstanding accept to be aborted).
closesocket0(handle);
// waits until the accept operations have completed
ioCache.close();
// finally disassociate from the completion port
iocp.disassociate(completionKey);
// release other resources
unsafe.freeMemory(dataBuffer);
}
@Override
public AsynchronousChannelGroupImpl group() {
return iocp;
}
/**
* Task to initiate accept operation and to handle result.
*/
private class AcceptTask implements Runnable, Iocp.ResultHandler {
private final WindowsAsynchronousSocketChannelImpl channel;
private final AccessControlContext acc;
private final PendingFuture<AsynchronousSocketChannel,Object> result;
AcceptTask(WindowsAsynchronousSocketChannelImpl channel,
AccessControlContext acc,
PendingFuture<AsynchronousSocketChannel,Object> result)
{
this.channel = channel;
this.acc = acc;
this.result = result;
}
void enableAccept() {
accepting.set(false);
}
void closeChildChannel() {
try {
channel.close();
} catch (IOException ignore) { }
}
// caller must have acquired read lock for the listener and child channel.
void finishAccept() throws IOException {
/**
* Set local/remote addresses. This is currently very inefficient
* in that it requires 2 calls to getsockname and 2 calls to getpeername.
* (should change this to use GetAcceptExSockaddrs)
*/
updateAcceptContext(handle, channel.handle());
InetSocketAddress local = Net.localAddress(channel.fd);
final InetSocketAddress remote = Net.remoteAddress(channel.fd);
channel.setConnected(local, remote);
// permission check (in context of initiating thread)
if (acc != null) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
SecurityManager sm = System.getSecurityManager();
sm.checkAccept(remote.getAddress().getHostAddress(),
remote.getPort());
return null;
}
}, acc);
}
}
/**
* Initiates the accept operation.
*/
@Override
public void run() {
long overlapped = 0L;
try {
// begin usage of listener socket
begin();
try {
// begin usage of child socket (as it is registered with
// completion port and so may be closed in the event that
// the group is forcefully closed).
channel.begin();
synchronized (result) {
overlapped = ioCache.add(result);
int n = accept0(handle, channel.handle(), overlapped, dataBuffer);
if (n == IOStatus.UNAVAILABLE) {
return;
}
// connection accepted immediately
finishAccept();
// allow another accept before the result is set
enableAccept();
result.setResult(channel);
}
} finally {
// end usage on child socket
channel.end();
}
} catch (Throwable x) {
// failed to initiate accept so release resources
if (overlapped != 0L)
ioCache.remove(overlapped);
closeChildChannel();
if (x instanceof ClosedChannelException)
x = new AsynchronousCloseException();
if (!(x instanceof IOException) && !(x instanceof SecurityException))
x = new IOException(x);
enableAccept();
result.setFailure(x);
} finally {
// end of usage of listener socket
end();
}
// accept completed immediately but may not have executed on
// initiating thread in which case the operation may have been
// cancelled.
if (result.isCancelled()) {
closeChildChannel();
}
// invoke completion handler
Invoker.invokeIndirectly(result);
}
/**
* Executed when the I/O has completed
*/
@Override
public void completed(int bytesTransferred, boolean canInvokeDirect) {
try {
// connection accept after group has shutdown
if (iocp.isShutdown()) {
throw new IOException(new ShutdownChannelGroupException());
}
// finish the accept
try {
begin();
try {
channel.begin();
finishAccept();
} finally {
channel.end();
}
} finally {
end();
}
// allow another accept before the result is set
enableAccept();
result.setResult(channel);
} catch (Throwable x) {
enableAccept();
closeChildChannel();
if (x instanceof ClosedChannelException)
x = new AsynchronousCloseException();
if (!(x instanceof IOException) && !(x instanceof SecurityException))
x = new IOException(x);
result.setFailure(x);
}
// if an async cancel has already cancelled the operation then
// close the new channel so as to free resources
if (result.isCancelled()) {
closeChildChannel();
}
// invoke handler (but not directly)
Invoker.invokeIndirectly(result);
}
@Override
public void failed(int error, IOException x) {
enableAccept();
closeChildChannel();
// release waiters
if (isOpen()) {
result.setFailure(x);
} else {
result.setFailure(new AsynchronousCloseException());
}
Invoker.invokeIndirectly(result);
}
}
@Override
Future<AsynchronousSocketChannel> implAccept(Object attachment,
final CompletionHandler<AsynchronousSocketChannel,Object> handler)
{
if (!isOpen()) {
Throwable exc = new ClosedChannelException();
if (handler == null)
return CompletedFuture.withFailure(exc);
Invoker.invokeIndirectly(this, handler, attachment, null, exc);
return null;
}
if (isAcceptKilled())
throw new RuntimeException("Accept not allowed due to cancellation");
// ensure channel is bound to local address
if (localAddress == null)
throw new NotYetBoundException();
// create the socket that will be accepted. The creation of the socket
// is enclosed by a begin/end for the listener socket to ensure that
// we check that the listener is open and also to prevent the I/O
// port from being closed as the new socket is registered.
WindowsAsynchronousSocketChannelImpl ch = null;
IOException ioe = null;
try {
begin();
ch = new WindowsAsynchronousSocketChannelImpl(iocp, false);
} catch (IOException x) {
ioe = x;
} finally {
end();
}
if (ioe != null) {
if (handler == null)
return CompletedFuture.withFailure(ioe);
Invoker.invokeIndirectly(this, handler, attachment, null, ioe);
return null;
}
// need calling context when there is security manager as
// permission check may be done in a different thread without
// any application call frames on the stack
AccessControlContext acc = (System.getSecurityManager() == null) ?
null : AccessController.getContext();
PendingFuture<AsynchronousSocketChannel,Object> result =
new PendingFuture<AsynchronousSocketChannel,Object>(this, handler, attachment);
AcceptTask task = new AcceptTask(ch, acc, result);
result.setContext(task);
// check and set flag to prevent concurrent accepting
if (!accepting.compareAndSet(false, true))
throw new AcceptPendingException();
// initiate I/O
task.run();
return result;
}
// -- Native methods --
private static native void initIDs();
private static native int accept0(long listenSocket, long acceptSocket,
long overlapped, long dataBuffer) throws IOException;
private static native void updateAcceptContext(long listenSocket,
long acceptSocket) throws IOException;
private static native void closesocket0(long socket) throws IOException;
static {
IOUtil.load();
initIDs();
}
}

View file

@ -0,0 +1,933 @@
/*
* 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.ch;
import java.nio.channels.*;
import java.nio.ByteBuffer;
import java.nio.BufferOverflowException;
import java.net.*;
import java.util.concurrent.*;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import jdk.internal.misc.Unsafe;
/**
* Windows implementation of AsynchronousSocketChannel using overlapped I/O.
*/
class WindowsAsynchronousSocketChannelImpl
extends AsynchronousSocketChannelImpl implements Iocp.OverlappedChannel
{
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static int addressSize = unsafe.addressSize();
private static int dependsArch(int value32, int value64) {
return (addressSize == 4) ? value32 : value64;
}
/*
* typedef struct _WSABUF {
* u_long len;
* char FAR * buf;
* } WSABUF;
*/
private static final int SIZEOF_WSABUF = dependsArch(8, 16);
private static final int OFFSETOF_LEN = 0;
private static final int OFFSETOF_BUF = dependsArch(4, 8);
// maximum vector size for scatter/gather I/O
private static final int MAX_WSABUF = 16;
private static final int SIZEOF_WSABUFARRAY = MAX_WSABUF * SIZEOF_WSABUF;
// socket handle. Use begin()/end() around each usage of this handle.
final long handle;
// I/O completion port that the socket is associated with
private final Iocp iocp;
// completion key to identify channel when I/O completes
private final int completionKey;
// Pending I/O operations are tied to an OVERLAPPED structure that can only
// be released when the I/O completion event is posted to the completion
// port. Where I/O operations complete immediately then it is possible
// there may be more than two OVERLAPPED structures in use.
private final PendingIoCache ioCache;
// per-channel arrays of WSABUF structures
private final long readBufferArray;
private final long writeBufferArray;
WindowsAsynchronousSocketChannelImpl(Iocp iocp, boolean failIfGroupShutdown)
throws IOException
{
super(iocp);
// associate socket with default completion port
long h = IOUtil.fdVal(fd);
int key = 0;
try {
key = iocp.associate(this, h);
} catch (ShutdownChannelGroupException x) {
if (failIfGroupShutdown) {
closesocket0(h);
throw x;
}
} catch (IOException x) {
closesocket0(h);
throw x;
}
this.handle = h;
this.iocp = iocp;
this.completionKey = key;
this.ioCache = new PendingIoCache();
// allocate WSABUF arrays
this.readBufferArray = unsafe.allocateMemory(SIZEOF_WSABUFARRAY);
this.writeBufferArray = unsafe.allocateMemory(SIZEOF_WSABUFARRAY);
}
WindowsAsynchronousSocketChannelImpl(Iocp iocp) throws IOException {
this(iocp, true);
}
@Override
public AsynchronousChannelGroupImpl group() {
return iocp;
}
/**
* Invoked by Iocp when an I/O operation competes.
*/
@Override
public <V,A> PendingFuture<V,A> getByOverlapped(long overlapped) {
return ioCache.remove(overlapped);
}
// invoked by WindowsAsynchronousServerSocketChannelImpl
long handle() {
return handle;
}
// invoked by WindowsAsynchronousServerSocketChannelImpl when new connection
// accept
void setConnected(InetSocketAddress localAddress,
InetSocketAddress remoteAddress)
{
synchronized (stateLock) {
state = ST_CONNECTED;
this.localAddress = localAddress;
this.remoteAddress = remoteAddress;
}
}
@Override
void implClose() throws IOException {
// close socket (may cause outstanding async I/O operations to fail).
closesocket0(handle);
// waits until all I/O operations have completed
ioCache.close();
// release arrays of WSABUF structures
unsafe.freeMemory(readBufferArray);
unsafe.freeMemory(writeBufferArray);
// finally disassociate from the completion port (key can be 0 if
// channel created when group is shutdown)
if (completionKey != 0)
iocp.disassociate(completionKey);
}
@Override
public void onCancel(PendingFuture<?,?> task) {
if (task.getContext() instanceof ConnectTask)
killConnect();
if (task.getContext() instanceof ReadTask)
killReading();
if (task.getContext() instanceof WriteTask)
killWriting();
}
/**
* Implements the task to initiate a connection and the handler to
* consume the result when the connection is established (or fails).
*/
private class ConnectTask<A> implements Runnable, Iocp.ResultHandler {
private final InetSocketAddress remote;
private final PendingFuture<Void,A> result;
ConnectTask(InetSocketAddress remote, PendingFuture<Void,A> result) {
this.remote = remote;
this.result = result;
}
private void closeChannel() {
try {
close();
} catch (IOException ignore) { }
}
private IOException toIOException(Throwable x) {
if (x instanceof IOException) {
if (x instanceof ClosedChannelException)
x = new AsynchronousCloseException();
return (IOException)x;
}
return new IOException(x);
}
/**
* Invoke after a connection is successfully established.
*/
private void afterConnect() throws IOException {
updateConnectContext(handle);
synchronized (stateLock) {
state = ST_CONNECTED;
remoteAddress = remote;
}
}
/**
* Task to initiate a connection.
*/
@Override
public void run() {
long overlapped = 0L;
Throwable exc = null;
try {
begin();
// synchronize on result to allow this thread handle the case
// where the connection is established immediately.
synchronized (result) {
overlapped = ioCache.add(result);
// initiate the connection
int n = connect0(handle, Net.isIPv6Available(), remote.getAddress(),
remote.getPort(), overlapped);
if (n == IOStatus.UNAVAILABLE) {
// connection is pending
return;
}
// connection established immediately
afterConnect();
result.setResult(null);
}
} catch (Throwable x) {
if (overlapped != 0L)
ioCache.remove(overlapped);
exc = x;
} finally {
end();
}
if (exc != null) {
closeChannel();
result.setFailure(toIOException(exc));
}
Invoker.invoke(result);
}
/**
* Invoked by handler thread when connection established.
*/
@Override
public void completed(int bytesTransferred, boolean canInvokeDirect) {
Throwable exc = null;
try {
begin();
afterConnect();
result.setResult(null);
} catch (Throwable x) {
// channel is closed or unable to finish connect
exc = x;
} finally {
end();
}
// can't close channel while in begin/end block
if (exc != null) {
closeChannel();
result.setFailure(toIOException(exc));
}
if (canInvokeDirect) {
Invoker.invokeUnchecked(result);
} else {
Invoker.invoke(result);
}
}
/**
* Invoked by handler thread when failed to establish connection.
*/
@Override
public void failed(int error, IOException x) {
if (isOpen()) {
closeChannel();
result.setFailure(x);
} else {
result.setFailure(new AsynchronousCloseException());
}
Invoker.invoke(result);
}
}
private void doPrivilegedBind(final SocketAddress sa) throws IOException {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
public Void run() throws IOException {
bind(sa);
return null;
}
});
} catch (PrivilegedActionException e) {
throw (IOException) e.getException();
}
}
@Override
<A> Future<Void> implConnect(SocketAddress remote,
A attachment,
CompletionHandler<Void,? super A> handler)
{
if (!isOpen()) {
Throwable exc = new ClosedChannelException();
if (handler == null)
return CompletedFuture.withFailure(exc);
Invoker.invoke(this, handler, attachment, null, exc);
return null;
}
InetSocketAddress isa = Net.checkAddress(remote);
// permission check
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort());
// check and update state
// ConnectEx requires the socket to be bound to a local address
IOException bindException = null;
synchronized (stateLock) {
if (state == ST_CONNECTED)
throw new AlreadyConnectedException();
if (state == ST_PENDING)
throw new ConnectionPendingException();
if (localAddress == null) {
try {
SocketAddress any = new InetSocketAddress(0);
if (sm == null) {
bind(any);
} else {
doPrivilegedBind(any);
}
} catch (IOException x) {
bindException = x;
}
}
if (bindException == null)
state = ST_PENDING;
}
// handle bind failure
if (bindException != null) {
try {
close();
} catch (IOException ignore) { }
if (handler == null)
return CompletedFuture.withFailure(bindException);
Invoker.invoke(this, handler, attachment, null, bindException);
return null;
}
// setup task
PendingFuture<Void,A> result =
new PendingFuture<Void,A>(this, handler, attachment);
ConnectTask<A> task = new ConnectTask<A>(isa, result);
result.setContext(task);
// initiate I/O
task.run();
return result;
}
/**
* Implements the task to initiate a read and the handler to consume the
* result when the read completes.
*/
private class ReadTask<V,A> implements Runnable, Iocp.ResultHandler {
private final ByteBuffer[] bufs;
private final int numBufs;
private final boolean scatteringRead;
private final PendingFuture<V,A> result;
// set by run method
private ByteBuffer[] shadow;
ReadTask(ByteBuffer[] bufs,
boolean scatteringRead,
PendingFuture<V,A> result)
{
this.bufs = bufs;
this.numBufs = (bufs.length > MAX_WSABUF) ? MAX_WSABUF : bufs.length;
this.scatteringRead = scatteringRead;
this.result = result;
}
/**
* Invoked prior to read to prepare the WSABUF array. Where necessary,
* it substitutes non-direct buffers with direct buffers.
*/
void prepareBuffers() {
shadow = new ByteBuffer[numBufs];
long address = readBufferArray;
for (int i=0; i<numBufs; i++) {
ByteBuffer dst = bufs[i];
int pos = dst.position();
int lim = dst.limit();
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
long a;
if (!(dst instanceof DirectBuffer)) {
// substitute with direct buffer
ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
shadow[i] = bb;
a = ((DirectBuffer)bb).address();
} else {
shadow[i] = dst;
a = ((DirectBuffer)dst).address() + pos;
}
unsafe.putAddress(address + OFFSETOF_BUF, a);
unsafe.putInt(address + OFFSETOF_LEN, rem);
address += SIZEOF_WSABUF;
}
}
/**
* Invoked after a read has completed to update the buffer positions
* and release any substituted buffers.
*/
void updateBuffers(int bytesRead) {
for (int i=0; i<numBufs; i++) {
ByteBuffer nextBuffer = shadow[i];
int pos = nextBuffer.position();
int len = nextBuffer.remaining();
if (bytesRead >= len) {
bytesRead -= len;
int newPosition = pos + len;
try {
nextBuffer.position(newPosition);
} catch (IllegalArgumentException x) {
// position changed by another
}
} else { // Buffers not completely filled
if (bytesRead > 0) {
assert(pos + bytesRead < (long)Integer.MAX_VALUE);
int newPosition = pos + bytesRead;
try {
nextBuffer.position(newPosition);
} catch (IllegalArgumentException x) {
// position changed by another
}
}
break;
}
}
// Put results from shadow into the slow buffers
for (int i=0; i<numBufs; i++) {
if (!(bufs[i] instanceof DirectBuffer)) {
shadow[i].flip();
try {
bufs[i].put(shadow[i]);
} catch (BufferOverflowException x) {
// position changed by another
}
}
}
}
void releaseBuffers() {
for (int i=0; i<numBufs; i++) {
if (!(bufs[i] instanceof DirectBuffer)) {
Util.releaseTemporaryDirectBuffer(shadow[i]);
}
}
}
@Override
@SuppressWarnings("unchecked")
public void run() {
long overlapped = 0L;
boolean prepared = false;
boolean pending = false;
try {
begin();
// substitute non-direct buffers
prepareBuffers();
prepared = true;
// get an OVERLAPPED structure (from the cache or allocate)
overlapped = ioCache.add(result);
// initiate read
int n = read0(handle, numBufs, readBufferArray, overlapped);
if (n == IOStatus.UNAVAILABLE) {
// I/O is pending
pending = true;
return;
}
if (n == IOStatus.EOF) {
// input shutdown
enableReading();
if (scatteringRead) {
result.setResult((V)Long.valueOf(-1L));
} else {
result.setResult((V)Integer.valueOf(-1));
}
} else {
throw new InternalError("Read completed immediately");
}
} catch (Throwable x) {
// failed to initiate read
// reset read flag before releasing waiters
enableReading();
if (x instanceof ClosedChannelException)
x = new AsynchronousCloseException();
if (!(x instanceof IOException))
x = new IOException(x);
result.setFailure(x);
} finally {
// release resources if I/O not pending
if (!pending) {
if (overlapped != 0L)
ioCache.remove(overlapped);
if (prepared)
releaseBuffers();
}
end();
}
// invoke completion handler
Invoker.invoke(result);
}
/**
* Executed when the I/O has completed
*/
@Override
@SuppressWarnings("unchecked")
public void completed(int bytesTransferred, boolean canInvokeDirect) {
if (bytesTransferred == 0) {
bytesTransferred = -1; // EOF
} else {
updateBuffers(bytesTransferred);
}
// return direct buffer to cache if substituted
releaseBuffers();
// release waiters if not already released by timeout
synchronized (result) {
if (result.isDone())
return;
enableReading();
if (scatteringRead) {
result.setResult((V)Long.valueOf(bytesTransferred));
} else {
result.setResult((V)Integer.valueOf(bytesTransferred));
}
}
if (canInvokeDirect) {
Invoker.invokeUnchecked(result);
} else {
Invoker.invoke(result);
}
}
@Override
public void failed(int error, IOException x) {
// return direct buffer to cache if substituted
releaseBuffers();
// release waiters if not already released by timeout
if (!isOpen())
x = new AsynchronousCloseException();
synchronized (result) {
if (result.isDone())
return;
enableReading();
result.setFailure(x);
}
Invoker.invoke(result);
}
/**
* Invoked if timeout expires before it is cancelled
*/
void timeout() {
// synchronize on result as the I/O could complete/fail
synchronized (result) {
if (result.isDone())
return;
// kill further reading before releasing waiters
enableReading(true);
result.setFailure(new InterruptedByTimeoutException());
}
// invoke handler without any locks
Invoker.invoke(result);
}
}
@Override
<V extends Number,A> Future<V> implRead(boolean isScatteringRead,
ByteBuffer dst,
ByteBuffer[] dsts,
long timeout,
TimeUnit unit,
A attachment,
CompletionHandler<V,? super A> handler)
{
// setup task
PendingFuture<V,A> result =
new PendingFuture<V,A>(this, handler, attachment);
ByteBuffer[] bufs;
if (isScatteringRead) {
bufs = dsts;
} else {
bufs = new ByteBuffer[1];
bufs[0] = dst;
}
final ReadTask<V,A> readTask =
new ReadTask<V,A>(bufs, isScatteringRead, result);
result.setContext(readTask);
// schedule timeout
if (timeout > 0L) {
Future<?> timeoutTask = iocp.schedule(new Runnable() {
public void run() {
readTask.timeout();
}
}, timeout, unit);
result.setTimeoutTask(timeoutTask);
}
// initiate I/O
readTask.run();
return result;
}
/**
* Implements the task to initiate a write and the handler to consume the
* result when the write completes.
*/
private class WriteTask<V,A> implements Runnable, Iocp.ResultHandler {
private final ByteBuffer[] bufs;
private final int numBufs;
private final boolean gatheringWrite;
private final PendingFuture<V,A> result;
// set by run method
private ByteBuffer[] shadow;
WriteTask(ByteBuffer[] bufs,
boolean gatheringWrite,
PendingFuture<V,A> result)
{
this.bufs = bufs;
this.numBufs = (bufs.length > MAX_WSABUF) ? MAX_WSABUF : bufs.length;
this.gatheringWrite = gatheringWrite;
this.result = result;
}
/**
* Invoked prior to write to prepare the WSABUF array. Where necessary,
* it substitutes non-direct buffers with direct buffers.
*/
void prepareBuffers() {
shadow = new ByteBuffer[numBufs];
long address = writeBufferArray;
for (int i=0; i<numBufs; i++) {
ByteBuffer src = bufs[i];
int pos = src.position();
int lim = src.limit();
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
long a;
if (!(src instanceof DirectBuffer)) {
// substitute with direct buffer
ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
bb.put(src);
bb.flip();
src.position(pos); // leave heap buffer untouched for now
shadow[i] = bb;
a = ((DirectBuffer)bb).address();
} else {
shadow[i] = src;
a = ((DirectBuffer)src).address() + pos;
}
unsafe.putAddress(address + OFFSETOF_BUF, a);
unsafe.putInt(address + OFFSETOF_LEN, rem);
address += SIZEOF_WSABUF;
}
}
/**
* Invoked after a write has completed to update the buffer positions
* and release any substituted buffers.
*/
void updateBuffers(int bytesWritten) {
// Notify the buffers how many bytes were taken
for (int i=0; i<numBufs; i++) {
ByteBuffer nextBuffer = bufs[i];
int pos = nextBuffer.position();
int lim = nextBuffer.limit();
int len = (pos <= lim ? lim - pos : lim);
if (bytesWritten >= len) {
bytesWritten -= len;
int newPosition = pos + len;
try {
nextBuffer.position(newPosition);
} catch (IllegalArgumentException x) {
// position changed by someone else
}
} else { // Buffers not completely filled
if (bytesWritten > 0) {
assert(pos + bytesWritten < (long)Integer.MAX_VALUE);
int newPosition = pos + bytesWritten;
try {
nextBuffer.position(newPosition);
} catch (IllegalArgumentException x) {
// position changed by someone else
}
}
break;
}
}
}
void releaseBuffers() {
for (int i=0; i<numBufs; i++) {
if (!(bufs[i] instanceof DirectBuffer)) {
Util.releaseTemporaryDirectBuffer(shadow[i]);
}
}
}
@Override
//@SuppressWarnings("unchecked")
public void run() {
long overlapped = 0L;
boolean prepared = false;
boolean pending = false;
boolean shutdown = false;
try {
begin();
// substitute non-direct buffers
prepareBuffers();
prepared = true;
// get an OVERLAPPED structure (from the cache or allocate)
overlapped = ioCache.add(result);
int n = write0(handle, numBufs, writeBufferArray, overlapped);
if (n == IOStatus.UNAVAILABLE) {
// I/O is pending
pending = true;
return;
}
if (n == IOStatus.EOF) {
// special case for shutdown output
shutdown = true;
throw new ClosedChannelException();
}
// write completed immediately
throw new InternalError("Write completed immediately");
} catch (Throwable x) {
// write failed. Enable writing before releasing waiters.
enableWriting();
if (!shutdown && (x instanceof ClosedChannelException))
x = new AsynchronousCloseException();
if (!(x instanceof IOException))
x = new IOException(x);
result.setFailure(x);
} finally {
// release resources if I/O not pending
if (!pending) {
if (overlapped != 0L)
ioCache.remove(overlapped);
if (prepared)
releaseBuffers();
}
end();
}
// invoke completion handler
Invoker.invoke(result);
}
/**
* Executed when the I/O has completed
*/
@Override
@SuppressWarnings("unchecked")
public void completed(int bytesTransferred, boolean canInvokeDirect) {
updateBuffers(bytesTransferred);
// return direct buffer to cache if substituted
releaseBuffers();
// release waiters if not already released by timeout
synchronized (result) {
if (result.isDone())
return;
enableWriting();
if (gatheringWrite) {
result.setResult((V)Long.valueOf(bytesTransferred));
} else {
result.setResult((V)Integer.valueOf(bytesTransferred));
}
}
if (canInvokeDirect) {
Invoker.invokeUnchecked(result);
} else {
Invoker.invoke(result);
}
}
@Override
public void failed(int error, IOException x) {
// return direct buffer to cache if substituted
releaseBuffers();
// release waiters if not already released by timeout
if (!isOpen())
x = new AsynchronousCloseException();
synchronized (result) {
if (result.isDone())
return;
enableWriting();
result.setFailure(x);
}
Invoker.invoke(result);
}
/**
* Invoked if timeout expires before it is cancelled
*/
void timeout() {
// synchronize on result as the I/O could complete/fail
synchronized (result) {
if (result.isDone())
return;
// kill further writing before releasing waiters
enableWriting(true);
result.setFailure(new InterruptedByTimeoutException());
}
// invoke handler without any locks
Invoker.invoke(result);
}
}
@Override
<V extends Number,A> Future<V> implWrite(boolean gatheringWrite,
ByteBuffer src,
ByteBuffer[] srcs,
long timeout,
TimeUnit unit,
A attachment,
CompletionHandler<V,? super A> handler)
{
// setup task
PendingFuture<V,A> result =
new PendingFuture<V,A>(this, handler, attachment);
ByteBuffer[] bufs;
if (gatheringWrite) {
bufs = srcs;
} else {
bufs = new ByteBuffer[1];
bufs[0] = src;
}
final WriteTask<V,A> writeTask =
new WriteTask<V,A>(bufs, gatheringWrite, result);
result.setContext(writeTask);
// schedule timeout
if (timeout > 0L) {
Future<?> timeoutTask = iocp.schedule(new Runnable() {
public void run() {
writeTask.timeout();
}
}, timeout, unit);
result.setTimeoutTask(timeoutTask);
}
// initiate I/O
writeTask.run();
return result;
}
// -- Native methods --
private static native void initIDs();
private static native int connect0(long socket, boolean preferIPv6,
InetAddress remote, int remotePort, long overlapped) throws IOException;
private static native void updateConnectContext(long socket) throws IOException;
private static native int read0(long socket, int count, long addres, long overlapped)
throws IOException;
private static native int write0(long socket, int count, long address,
long overlapped) throws IOException;
private static native void shutdown0(long socket, int how) throws IOException;
private static native void closesocket0(long socket) throws IOException;
static {
IOUtil.load();
initIDs();
}
}

View file

@ -0,0 +1,617 @@
/*
* Copyright (c) 2002, 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.ch;
import java.nio.channels.spi.SelectorProvider;
import java.nio.channels.Selector;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.Pipe;
import java.nio.channels.SelectableChannel;
import java.io.IOException;
import java.nio.channels.CancelledKeyException;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
/**
* A multi-threaded implementation of Selector for Windows.
*
* @author Konstantin Kladko
* @author Mark Reinhold
*/
final class WindowsSelectorImpl extends SelectorImpl {
// Initial capacity of the poll array
private final int INIT_CAP = 8;
// Maximum number of sockets for select().
// Should be INIT_CAP times a power of 2
private static final int MAX_SELECTABLE_FDS = 1024;
// The list of SelectableChannels serviced by this Selector. Every mod
// MAX_SELECTABLE_FDS entry is bogus, to align this array with the poll
// array, where the corresponding entry is occupied by the wakeupSocket
private SelectionKeyImpl[] channelArray = new SelectionKeyImpl[INIT_CAP];
// The global native poll array holds file decriptors and event masks
private PollArrayWrapper pollWrapper;
// The number of valid entries in poll array, including entries occupied
// by wakeup socket handle.
private int totalChannels = 1;
// Number of helper threads needed for select. We need one thread per
// each additional set of MAX_SELECTABLE_FDS - 1 channels.
private int threadsCount = 0;
// A list of helper threads for select.
private final List<SelectThread> threads = new ArrayList<SelectThread>();
//Pipe used as a wakeup object.
private final Pipe wakeupPipe;
// File descriptors corresponding to source and sink
private final int wakeupSourceFd, wakeupSinkFd;
// Lock for close cleanup
private Object closeLock = new Object();
// Maps file descriptors to their indices in pollArray
private static final class FdMap extends HashMap<Integer, MapEntry> {
static final long serialVersionUID = 0L;
private MapEntry get(int desc) {
return get(Integer.valueOf(desc));
}
private MapEntry put(SelectionKeyImpl ski) {
return put(Integer.valueOf(ski.channel.getFDVal()), new MapEntry(ski));
}
private MapEntry remove(SelectionKeyImpl ski) {
Integer fd = Integer.valueOf(ski.channel.getFDVal());
MapEntry x = get(fd);
if ((x != null) && (x.ski.channel == ski.channel))
return remove(fd);
return null;
}
}
// class for fdMap entries
private static final class MapEntry {
SelectionKeyImpl ski;
long updateCount = 0;
long clearedCount = 0;
MapEntry(SelectionKeyImpl ski) {
this.ski = ski;
}
}
private final FdMap fdMap = new FdMap();
// SubSelector for the main thread
private final SubSelector subSelector = new SubSelector();
private long timeout; //timeout for poll
// Lock for interrupt triggering and clearing
private final Object interruptLock = new Object();
private volatile boolean interruptTriggered;
WindowsSelectorImpl(SelectorProvider sp) throws IOException {
super(sp);
pollWrapper = new PollArrayWrapper(INIT_CAP);
wakeupPipe = Pipe.open();
wakeupSourceFd = ((SelChImpl)wakeupPipe.source()).getFDVal();
// Disable the Nagle algorithm so that the wakeup is more immediate
SinkChannelImpl sink = (SinkChannelImpl)wakeupPipe.sink();
(sink.sc).socket().setTcpNoDelay(true);
wakeupSinkFd = ((SelChImpl)sink).getFDVal();
pollWrapper.addWakeupSocket(wakeupSourceFd, 0);
}
protected int doSelect(long timeout) throws IOException {
if (channelArray == null)
throw new ClosedSelectorException();
this.timeout = timeout; // set selector timeout
processDeregisterQueue();
if (interruptTriggered) {
resetWakeupSocket();
return 0;
}
// Calculate number of helper threads needed for poll. If necessary
// threads are created here and start waiting on startLock
adjustThreadsCount();
finishLock.reset(); // reset finishLock
// Wakeup helper threads, waiting on startLock, so they start polling.
// Redundant threads will exit here after wakeup.
startLock.startThreads();
// do polling in the main thread. Main thread is responsible for
// first MAX_SELECTABLE_FDS entries in pollArray.
try {
begin();
try {
subSelector.poll();
} catch (IOException e) {
finishLock.setException(e); // Save this exception
}
// Main thread is out of poll(). Wakeup others and wait for them
if (threads.size() > 0)
finishLock.waitForHelperThreads();
} finally {
end();
}
// Done with poll(). Set wakeupSocket to nonsignaled for the next run.
finishLock.checkForException();
processDeregisterQueue();
int updated = updateSelectedKeys();
// Done with poll(). Set wakeupSocket to nonsignaled for the next run.
resetWakeupSocket();
return updated;
}
// Helper threads wait on this lock for the next poll.
private final StartLock startLock = new StartLock();
private final class StartLock {
// A variable which distinguishes the current run of doSelect from the
// previous one. Incrementing runsCounter and notifying threads will
// trigger another round of poll.
private long runsCounter;
// Triggers threads, waiting on this lock to start polling.
private synchronized void startThreads() {
runsCounter++; // next run
notifyAll(); // wake up threads.
}
// This function is called by a helper thread to wait for the
// next round of poll(). It also checks, if this thread became
// redundant. If yes, it returns true, notifying the thread
// that it should exit.
private synchronized boolean waitForStart(SelectThread thread) {
while (true) {
while (runsCounter == thread.lastRun) {
try {
startLock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
if (thread.isZombie()) { // redundant thread
return true; // will cause run() to exit.
} else {
thread.lastRun = runsCounter; // update lastRun
return false; // will cause run() to poll.
}
}
}
}
// Main thread waits on this lock, until all helper threads are done
// with poll().
private final FinishLock finishLock = new FinishLock();
private final class FinishLock {
// Number of helper threads, that did not finish yet.
private int threadsToFinish;
// IOException which occurred during the last run.
IOException exception = null;
// Called before polling.
private void reset() {
threadsToFinish = threads.size(); // helper threads
}
// Each helper thread invokes this function on finishLock, when
// the thread is done with poll().
private synchronized void threadFinished() {
if (threadsToFinish == threads.size()) { // finished poll() first
// if finished first, wakeup others
wakeup();
}
threadsToFinish--;
if (threadsToFinish == 0) // all helper threads finished poll().
notify(); // notify the main thread
}
// The main thread invokes this function on finishLock to wait
// for helper threads to finish poll().
private synchronized void waitForHelperThreads() {
if (threadsToFinish == threads.size()) {
// no helper threads finished yet. Wakeup them up.
wakeup();
}
while (threadsToFinish != 0) {
try {
finishLock.wait();
} catch (InterruptedException e) {
// Interrupted - set interrupted state.
Thread.currentThread().interrupt();
}
}
}
// sets IOException for this run
private synchronized void setException(IOException e) {
exception = e;
}
// Checks if there was any exception during the last run.
// If yes, throws it
private void checkForException() throws IOException {
if (exception == null)
return;
StringBuffer message = new StringBuffer("An exception occurred" +
" during the execution of select(): \n");
message.append(exception);
message.append('\n');
exception = null;
throw new IOException(message.toString());
}
}
private final class SubSelector {
private final int pollArrayIndex; // starting index in pollArray to poll
// These arrays will hold result of native select().
// The first element of each array is the number of selected sockets.
// Other elements are file descriptors of selected sockets.
private final int[] readFds = new int [MAX_SELECTABLE_FDS + 1];
private final int[] writeFds = new int [MAX_SELECTABLE_FDS + 1];
private final int[] exceptFds = new int [MAX_SELECTABLE_FDS + 1];
private SubSelector() {
this.pollArrayIndex = 0; // main thread
}
private SubSelector(int threadIndex) { // helper threads
this.pollArrayIndex = (threadIndex + 1) * MAX_SELECTABLE_FDS;
}
private int poll() throws IOException{ // poll for the main thread
return poll0(pollWrapper.pollArrayAddress,
Math.min(totalChannels, MAX_SELECTABLE_FDS),
readFds, writeFds, exceptFds, timeout);
}
private int poll(int index) throws IOException {
// poll for helper threads
return poll0(pollWrapper.pollArrayAddress +
(pollArrayIndex * PollArrayWrapper.SIZE_POLLFD),
Math.min(MAX_SELECTABLE_FDS,
totalChannels - (index + 1) * MAX_SELECTABLE_FDS),
readFds, writeFds, exceptFds, timeout);
}
private native int poll0(long pollAddress, int numfds,
int[] readFds, int[] writeFds, int[] exceptFds, long timeout);
private int processSelectedKeys(long updateCount) {
int numKeysUpdated = 0;
numKeysUpdated += processFDSet(updateCount, readFds,
Net.POLLIN,
false);
numKeysUpdated += processFDSet(updateCount, writeFds,
Net.POLLCONN |
Net.POLLOUT,
false);
numKeysUpdated += processFDSet(updateCount, exceptFds,
Net.POLLIN |
Net.POLLCONN |
Net.POLLOUT,
true);
return numKeysUpdated;
}
/**
* Note, clearedCount is used to determine if the readyOps have
* been reset in this select operation. updateCount is used to
* tell if a key has been counted as updated in this select
* operation.
*
* me.updateCount <= me.clearedCount <= updateCount
*/
private int processFDSet(long updateCount, int[] fds, int rOps,
boolean isExceptFds)
{
int numKeysUpdated = 0;
for (int i = 1; i <= fds[0]; i++) {
int desc = fds[i];
if (desc == wakeupSourceFd) {
synchronized (interruptLock) {
interruptTriggered = true;
}
continue;
}
MapEntry me = fdMap.get(desc);
// If me is null, the key was deregistered in the previous
// processDeregisterQueue.
if (me == null)
continue;
SelectionKeyImpl sk = me.ski;
// The descriptor may be in the exceptfds set because there is
// OOB data queued to the socket. If there is OOB data then it
// is discarded and the key is not added to the selected set.
if (isExceptFds &&
(sk.channel() instanceof SocketChannelImpl) &&
discardUrgentData(desc))
{
continue;
}
if (selectedKeys.contains(sk)) { // Key in selected set
if (me.clearedCount != updateCount) {
if (sk.channel.translateAndSetReadyOps(rOps, sk) &&
(me.updateCount != updateCount)) {
me.updateCount = updateCount;
numKeysUpdated++;
}
} else { // The readyOps have been set; now add
if (sk.channel.translateAndUpdateReadyOps(rOps, sk) &&
(me.updateCount != updateCount)) {
me.updateCount = updateCount;
numKeysUpdated++;
}
}
me.clearedCount = updateCount;
} else { // Key is not in selected set yet
if (me.clearedCount != updateCount) {
sk.channel.translateAndSetReadyOps(rOps, sk);
if ((sk.nioReadyOps() & sk.nioInterestOps()) != 0) {
selectedKeys.add(sk);
me.updateCount = updateCount;
numKeysUpdated++;
}
} else { // The readyOps have been set; now add
sk.channel.translateAndUpdateReadyOps(rOps, sk);
if ((sk.nioReadyOps() & sk.nioInterestOps()) != 0) {
selectedKeys.add(sk);
me.updateCount = updateCount;
numKeysUpdated++;
}
}
me.clearedCount = updateCount;
}
}
return numKeysUpdated;
}
}
// Represents a helper thread used for select.
private final class SelectThread extends Thread {
private final int index; // index of this thread
final SubSelector subSelector;
private long lastRun = 0; // last run number
private volatile boolean zombie;
// Creates a new thread
private SelectThread(int i) {
super(null, null, "SelectorHelper", 0, false);
this.index = i;
this.subSelector = new SubSelector(i);
//make sure we wait for next round of poll
this.lastRun = startLock.runsCounter;
}
void makeZombie() {
zombie = true;
}
boolean isZombie() {
return zombie;
}
public void run() {
while (true) { // poll loop
// wait for the start of poll. If this thread has become
// redundant, then exit.
if (startLock.waitForStart(this))
return;
// call poll()
try {
subSelector.poll(index);
} catch (IOException e) {
// Save this exception and let other threads finish.
finishLock.setException(e);
}
// notify main thread, that this thread has finished, and
// wakeup others, if this thread is the first to finish.
finishLock.threadFinished();
}
}
}
// After some channels registered/deregistered, the number of required
// helper threads may have changed. Adjust this number.
private void adjustThreadsCount() {
if (threadsCount > threads.size()) {
// More threads needed. Start more threads.
for (int i = threads.size(); i < threadsCount; i++) {
SelectThread newThread = new SelectThread(i);
threads.add(newThread);
newThread.setDaemon(true);
newThread.start();
}
} else if (threadsCount < threads.size()) {
// Some threads become redundant. Remove them from the threads List.
for (int i = threads.size() - 1 ; i >= threadsCount; i--)
threads.remove(i).makeZombie();
}
}
// Sets Windows wakeup socket to a signaled state.
private void setWakeupSocket() {
setWakeupSocket0(wakeupSinkFd);
}
private native void setWakeupSocket0(int wakeupSinkFd);
// Sets Windows wakeup socket to a non-signaled state.
private void resetWakeupSocket() {
synchronized (interruptLock) {
if (interruptTriggered == false)
return;
resetWakeupSocket0(wakeupSourceFd);
interruptTriggered = false;
}
}
private native void resetWakeupSocket0(int wakeupSourceFd);
private native boolean discardUrgentData(int fd);
// We increment this counter on each call to updateSelectedKeys()
// each entry in SubSelector.fdsMap has a memorized value of
// updateCount. When we increment numKeysUpdated we set updateCount
// for the corresponding entry to its current value. This is used to
// avoid counting the same key more than once - the same key can
// appear in readfds and writefds.
private long updateCount = 0;
// Update ops of the corresponding Channels. Add the ready keys to the
// ready queue.
private int updateSelectedKeys() {
updateCount++;
int numKeysUpdated = 0;
numKeysUpdated += subSelector.processSelectedKeys(updateCount);
for (SelectThread t: threads) {
numKeysUpdated += t.subSelector.processSelectedKeys(updateCount);
}
return numKeysUpdated;
}
protected void implClose() throws IOException {
synchronized (closeLock) {
if (channelArray != null) {
if (pollWrapper != null) {
// prevent further wakeup
synchronized (interruptLock) {
interruptTriggered = true;
}
wakeupPipe.sink().close();
wakeupPipe.source().close();
for(int i = 1; i < totalChannels; i++) { // Deregister channels
if (i % MAX_SELECTABLE_FDS != 0) { // skip wakeupEvent
deregister(channelArray[i]);
SelectableChannel selch = channelArray[i].channel();
if (!selch.isOpen() && !selch.isRegistered())
((SelChImpl)selch).kill();
}
}
pollWrapper.free();
pollWrapper = null;
selectedKeys = null;
channelArray = null;
// Make all remaining helper threads exit
for (SelectThread t: threads)
t.makeZombie();
startLock.startThreads();
}
}
}
}
protected void implRegister(SelectionKeyImpl ski) {
synchronized (closeLock) {
if (pollWrapper == null)
throw new ClosedSelectorException();
growIfNeeded();
channelArray[totalChannels] = ski;
ski.setIndex(totalChannels);
fdMap.put(ski);
keys.add(ski);
pollWrapper.addEntry(totalChannels, ski);
totalChannels++;
}
}
private void growIfNeeded() {
if (channelArray.length == totalChannels) {
int newSize = totalChannels * 2; // Make a larger array
SelectionKeyImpl temp[] = new SelectionKeyImpl[newSize];
System.arraycopy(channelArray, 1, temp, 1, totalChannels - 1);
channelArray = temp;
pollWrapper.grow(newSize);
}
if (totalChannels % MAX_SELECTABLE_FDS == 0) { // more threads needed
pollWrapper.addWakeupSocket(wakeupSourceFd, totalChannels);
totalChannels++;
threadsCount++;
}
}
protected void implDereg(SelectionKeyImpl ski) throws IOException{
int i = ski.getIndex();
assert (i >= 0);
synchronized (closeLock) {
if (i != totalChannels - 1) {
// Copy end one over it
SelectionKeyImpl endChannel = channelArray[totalChannels-1];
channelArray[i] = endChannel;
endChannel.setIndex(i);
pollWrapper.replaceEntry(pollWrapper, totalChannels - 1,
pollWrapper, i);
}
ski.setIndex(-1);
}
channelArray[totalChannels - 1] = null;
totalChannels--;
if ( totalChannels != 1 && totalChannels % MAX_SELECTABLE_FDS == 1) {
totalChannels--;
threadsCount--; // The last thread has become redundant.
}
fdMap.remove(ski); // Remove the key from fdMap, keys and selectedKeys
keys.remove(ski);
selectedKeys.remove(ski);
deregister(ski);
SelectableChannel selch = ski.channel();
if (!selch.isOpen() && !selch.isRegistered())
((SelChImpl)selch).kill();
}
public void putEventOps(SelectionKeyImpl sk, int ops) {
synchronized (closeLock) {
if (pollWrapper == null)
throw new ClosedSelectorException();
// make sure this sk has not been removed yet
int index = sk.getIndex();
if (index == -1)
throw new CancelledKeyException();
pollWrapper.putEventOps(index, ops);
}
}
public Selector wakeup() {
synchronized (interruptLock) {
if (!interruptTriggered) {
setWakeupSocket();
interruptTriggered = true;
}
}
return this;
}
static {
IOUtil.load();
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2002, 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.ch;
import java.io.IOException;
import java.nio.channels.spi.AbstractSelector;
/*
* SelectorProvider for sun.nio.ch.WindowsSelectorImpl.
*
* @author Konstantin Kladko
* @since 1.4
*/
public class WindowsSelectorProvider extends SelectorProviderImpl {
public AbstractSelector openSelector() throws IOException {
return new WindowsSelectorImpl(this);
}
}

View file

@ -0,0 +1,38 @@
/*
* 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.spi.FileSystemProvider;
/**
* Creates default provider on Windows
*/
public class DefaultFileSystemProvider {
private DefaultFileSystemProvider() { }
public static FileSystemProvider create() {
return new WindowsFileSystemProvider();
}
}

View file

@ -0,0 +1,36 @@
/*
* 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.spi.FileTypeDetector;
public class DefaultFileTypeDetector {
private DefaultFileTypeDetector() { }
public static FileTypeDetector create() {
return new RegistryFileTypeDetector();
}
}

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2008, 2011, 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.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* File type detector that does lookup of file extension using Windows Registry.
*/
public class RegistryFileTypeDetector
extends AbstractFileTypeDetector
{
public RegistryFileTypeDetector() {
super();
}
@Override
public String implProbeContentType(Path file) throws IOException {
if (!(file instanceof Path))
return null;
// get file extension
Path name = file.getFileName();
if (name == null)
return null;
String filename = name.toString();
int dot = filename.lastIndexOf('.');
if ((dot < 0) || (dot == (filename.length()-1)))
return null;
// query HKEY_CLASSES_ROOT\<ext>
String key = filename.substring(dot);
NativeBuffer keyBuffer = WindowsNativeDispatcher.asNativeBuffer(key);
NativeBuffer nameBuffer = WindowsNativeDispatcher.asNativeBuffer("Content Type");
try {
return queryStringValue(keyBuffer.address(), nameBuffer.address());
} finally {
nameBuffer.release();
keyBuffer.release();
}
}
private static native String queryStringValue(long subKey, long name);
static {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
// nio.dll has dependency on net.dll
System.loadLibrary("net");
System.loadLibrary("nio");
return null;
}});
}
}

View file

@ -0,0 +1,226 @@
/*
* 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.ProviderMismatchException;
import java.nio.file.attribute.*;
import java.util.*;
import java.io.IOException;
import static sun.nio.fs.WindowsNativeDispatcher.*;
import static sun.nio.fs.WindowsConstants.*;
/**
* Windows implementation of AclFileAttributeView.
*/
class WindowsAclFileAttributeView
extends AbstractAclFileAttributeView
{
/**
* typedef struct _SECURITY_DESCRIPTOR {
* BYTE Revision;
* BYTE Sbz1;
* SECURITY_DESCRIPTOR_CONTROL Control;
* PSID Owner;
* PSID Group;
* PACL Sacl;
* PACL Dacl;
* } SECURITY_DESCRIPTOR;
*/
private static final short SIZEOF_SECURITY_DESCRIPTOR = 20;
private final WindowsPath file;
private final boolean followLinks;
WindowsAclFileAttributeView(WindowsPath file, boolean followLinks) {
this.file = file;
this.followLinks = followLinks;
}
// permission check
private void checkAccess(WindowsPath file,
boolean checkRead,
boolean checkWrite)
{
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
if (checkRead)
sm.checkRead(file.getPathForPermissionCheck());
if (checkWrite)
sm.checkWrite(file.getPathForPermissionCheck());
sm.checkPermission(new RuntimePermission("accessUserInformation"));
}
}
// invokes GetFileSecurity to get requested security information
static NativeBuffer getFileSecurity(String path, int request)
throws IOException
{
// invoke get to buffer size
int size = 0;
try {
size = GetFileSecurity(path, request, 0L, 0);
} catch (WindowsException x) {
x.rethrowAsIOException(path);
}
assert size > 0;
// allocate buffer and re-invoke to get security information
NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
try {
for (;;) {
int newSize = GetFileSecurity(path, request, buffer.address(), size);
if (newSize <= size)
return buffer;
// buffer was insufficient
buffer.release();
buffer = NativeBuffers.getNativeBuffer(newSize);
size = newSize;
}
} catch (WindowsException x) {
buffer.release();
x.rethrowAsIOException(path);
return null;
}
}
@Override
public UserPrincipal getOwner()
throws IOException
{
checkAccess(file, true, false);
// GetFileSecurity does not follow links so when following links we
// need the final target
String path = WindowsLinkSupport.getFinalPath(file, followLinks);
NativeBuffer buffer = getFileSecurity(path, OWNER_SECURITY_INFORMATION);
try {
// get the address of the SID
long sidAddress = GetSecurityDescriptorOwner(buffer.address());
if (sidAddress == 0L)
throw new IOException("no owner");
return WindowsUserPrincipals.fromSid(sidAddress);
} catch (WindowsException x) {
x.rethrowAsIOException(file);
return null;
} finally {
buffer.release();
}
}
@Override
public List<AclEntry> getAcl()
throws IOException
{
checkAccess(file, true, false);
// GetFileSecurity does not follow links so when following links we
// need the final target
String path = WindowsLinkSupport.getFinalPath(file, followLinks);
// ALLOW and DENY entries in DACL;
// AUDIT entries in SACL (ignore for now as it requires privileges)
NativeBuffer buffer = getFileSecurity(path, DACL_SECURITY_INFORMATION);
try {
return WindowsSecurityDescriptor.getAcl(buffer.address());
} finally {
buffer.release();
}
}
@Override
public void setOwner(UserPrincipal obj)
throws IOException
{
if (obj == null)
throw new NullPointerException("'owner' is null");
if (!(obj instanceof WindowsUserPrincipals.User))
throw new ProviderMismatchException();
WindowsUserPrincipals.User owner = (WindowsUserPrincipals.User)obj;
// permission check
checkAccess(file, false, true);
// SetFileSecurity does not follow links so when following links we
// need the final target
String path = WindowsLinkSupport.getFinalPath(file, followLinks);
// ConvertStringSidToSid allocates memory for SID so must invoke
// LocalFree to free it when we are done
long pOwner = 0L;
try {
pOwner = ConvertStringSidToSid(owner.sidString());
} catch (WindowsException x) {
throw new IOException("Failed to get SID for " + owner.getName()
+ ": " + x.errorString());
}
// Allocate buffer for security descriptor, initialize it, set
// owner information and update the file.
try {
NativeBuffer buffer = NativeBuffers.getNativeBuffer(SIZEOF_SECURITY_DESCRIPTOR);
try {
InitializeSecurityDescriptor(buffer.address());
SetSecurityDescriptorOwner(buffer.address(), pOwner);
// may need SeRestorePrivilege to set the owner
WindowsSecurity.Privilege priv =
WindowsSecurity.enablePrivilege("SeRestorePrivilege");
try {
SetFileSecurity(path,
OWNER_SECURITY_INFORMATION,
buffer.address());
} finally {
priv.drop();
}
} catch (WindowsException x) {
x.rethrowAsIOException(file);
} finally {
buffer.release();
}
} finally {
LocalFree(pOwner);
}
}
@Override
public void setAcl(List<AclEntry> acl) throws IOException {
checkAccess(file, false, true);
// SetFileSecurity does not follow links so when following links we
// need the final target
String path = WindowsLinkSupport.getFinalPath(file, followLinks);
WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.create(acl);
try {
SetFileSecurity(path, DACL_SECURITY_INFORMATION, sd.address());
} catch (WindowsException x) {
x.rethrowAsIOException(file);
} finally {
sd.release();
}
}
}

View file

@ -0,0 +1,346 @@
/*
* 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;
}
}

View file

@ -0,0 +1,201 @@
/*
* 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;
/**
* Win32 APIs constants.
*/
class WindowsConstants {
private WindowsConstants() { }
// general
public static final long INVALID_HANDLE_VALUE = -1L;
// generic rights
public static final int GENERIC_READ = 0x80000000;
public static final int GENERIC_WRITE = 0x40000000;
// share modes
public static final int FILE_SHARE_READ = 0x00000001;
public static final int FILE_SHARE_WRITE = 0x00000002;
public static final int FILE_SHARE_DELETE = 0x00000004;
// creation modes
public static final int CREATE_NEW = 1;
public static final int CREATE_ALWAYS = 2;
public static final int OPEN_EXISTING = 3;
public static final int OPEN_ALWAYS = 4;
public static final int TRUNCATE_EXISTING = 5;
// attributes and flags
public static final int FILE_ATTRIBUTE_READONLY = 0x00000001;
public static final int FILE_ATTRIBUTE_HIDDEN = 0x00000002;
public static final int FILE_ATTRIBUTE_SYSTEM = 0x00000004;
public static final int FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
public static final int FILE_ATTRIBUTE_ARCHIVE = 0x00000020;
public static final int FILE_ATTRIBUTE_DEVICE = 0x00000040;
public static final int FILE_ATTRIBUTE_NORMAL = 0x00000080;
public static final int FILE_ATTRIBUTE_REPARSE_POINT = 0x400;
public static final int FILE_FLAG_NO_BUFFERING = 0x20000000;
public static final int FILE_FLAG_OVERLAPPED = 0x40000000;
public static final int FILE_FLAG_WRITE_THROUGH = 0x80000000;
public static final int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
public static final int FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
public static final int FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
// stream ids
public static final int BACKUP_ALTERNATE_DATA = 0x00000004;
public static final int BACKUP_SPARSE_BLOCK = 0x00000009;
// reparse point/symbolic link related constants
public static final int IO_REPARSE_TAG_SYMLINK = 0xA000000C;
public static final int MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024;
public static final int SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1;
// volume flags
public static final int FILE_CASE_SENSITIVE_SEARCH = 0x00000001;
public static final int FILE_CASE_PRESERVED_NAMES = 0x00000002;
public static final int FILE_PERSISTENT_ACLS = 0x00000008;
public static final int FILE_VOLUME_IS_COMPRESSED = 0x00008000;
public static final int FILE_NAMED_STREAMS = 0x00040000;
public static final int FILE_READ_ONLY_VOLUME = 0x00080000;
// error codes
public static final int ERROR_FILE_NOT_FOUND = 2;
public static final int ERROR_PATH_NOT_FOUND = 3;
public static final int ERROR_ACCESS_DENIED = 5;
public static final int ERROR_INVALID_HANDLE = 6;
public static final int ERROR_INVALID_DATA = 13;
public static final int ERROR_NOT_SAME_DEVICE = 17;
public static final int ERROR_NOT_READY = 21;
public static final int ERROR_SHARING_VIOLATION = 32;
public static final int ERROR_FILE_EXISTS = 80;
public static final int ERROR_INVALID_PARAMETER = 87;
public static final int ERROR_DISK_FULL = 112;
public static final int ERROR_INSUFFICIENT_BUFFER = 122;
public static final int ERROR_INVALID_LEVEL = 124;
public static final int ERROR_DIR_NOT_ROOT = 144;
public static final int ERROR_DIR_NOT_EMPTY = 145;
public static final int ERROR_ALREADY_EXISTS = 183;
public static final int ERROR_MORE_DATA = 234;
public static final int ERROR_DIRECTORY = 267;
public static final int ERROR_NOTIFY_ENUM_DIR = 1022;
public static final int ERROR_NONE_MAPPED = 1332;
public static final int ERROR_NOT_A_REPARSE_POINT = 4390;
public static final int ERROR_INVALID_REPARSE_DATA = 4392;
// notify filters
public static final int FILE_NOTIFY_CHANGE_FILE_NAME = 0x00000001;
public static final int FILE_NOTIFY_CHANGE_DIR_NAME = 0x00000002;
public static final int FILE_NOTIFY_CHANGE_ATTRIBUTES = 0x00000004;
public static final int FILE_NOTIFY_CHANGE_SIZE = 0x00000008;
public static final int FILE_NOTIFY_CHANGE_LAST_WRITE = 0x00000010;
public static final int FILE_NOTIFY_CHANGE_LAST_ACCESS = 0x00000020;
public static final int FILE_NOTIFY_CHANGE_CREATION = 0x00000040;
public static final int FILE_NOTIFY_CHANGE_SECURITY = 0x00000100;
// notify actions
public static final int FILE_ACTION_ADDED = 0x00000001;
public static final int FILE_ACTION_REMOVED = 0x00000002;
public static final int FILE_ACTION_MODIFIED = 0x00000003;
public static final int FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
public static final int FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
// copy flags
public static final int COPY_FILE_FAIL_IF_EXISTS = 0x00000001;
public static final int COPY_FILE_COPY_SYMLINK = 0x00000800;
// move flags
public static final int MOVEFILE_REPLACE_EXISTING = 0x00000001;
public static final int MOVEFILE_COPY_ALLOWED = 0x00000002;
// drive types
public static final int DRIVE_UNKNOWN = 0;
public static final int DRIVE_NO_ROOT_DIR = 1;
public static final int DRIVE_REMOVABLE = 2;
public static final int DRIVE_FIXED = 3;
public static final int DRIVE_REMOTE = 4;
public static final int DRIVE_CDROM = 5;
public static final int DRIVE_RAMDISK = 6;
// file security
public static final int OWNER_SECURITY_INFORMATION = 0x00000001;
public static final int GROUP_SECURITY_INFORMATION = 0x00000002;
public static final int DACL_SECURITY_INFORMATION = 0x00000004;
public static final int SACL_SECURITY_INFORMATION = 0x00000008;
public static final int SidTypeUser = 1;
public static final int SidTypeGroup = 2;
public static final int SidTypeDomain = 3;
public static final int SidTypeAlias = 4;
public static final int SidTypeWellKnownGroup = 5;
public static final int SidTypeDeletedAccount = 6;
public static final int SidTypeInvalid = 7;
public static final int SidTypeUnknown = 8;
public static final int SidTypeComputer= 9;
public static final byte ACCESS_ALLOWED_ACE_TYPE = 0x0;
public static final byte ACCESS_DENIED_ACE_TYPE = 0x1;
public static final byte OBJECT_INHERIT_ACE = 0x1;
public static final byte CONTAINER_INHERIT_ACE = 0x2;
public static final byte NO_PROPAGATE_INHERIT_ACE = 0x4;
public static final byte INHERIT_ONLY_ACE = 0x8;
public static final int DELETE = 0x00010000;
public static final int READ_CONTROL = 0x00020000;
public static final int WRITE_DAC = 0x00040000;
public static final int WRITE_OWNER = 0x00080000;
public static final int SYNCHRONIZE = 0x00100000;
public static final int FILE_LIST_DIRECTORY = 0x0001;
public static final int FILE_READ_DATA = 0x0001;
public static final int FILE_WRITE_DATA = 0x0002;
public static final int FILE_APPEND_DATA = 0x0004;
public static final int FILE_READ_EA = 0x0008;
public static final int FILE_WRITE_EA = 0x0010;
public static final int FILE_EXECUTE = 0x0020;
public static final int FILE_DELETE_CHILD = 0x0040;
public static final int FILE_READ_ATTRIBUTES = 0x0080;
public static final int FILE_WRITE_ATTRIBUTES = 0x0100;
public static final int FILE_GENERIC_READ = 0x00120089;
public static final int FILE_GENERIC_WRITE = 0x00120116;
public static final int FILE_GENERIC_EXECUTE = 0x001200a0;
public static final int FILE_ALL_ACCESS = 0x001f01ff;
// operating system security
public static final int TOKEN_DUPLICATE = 0x0002;
public static final int TOKEN_IMPERSONATE = 0x0004;
public static final int TOKEN_QUERY = 0x0008;
public static final int TOKEN_ADJUST_PRIVILEGES = 0x0020;
public static final int SE_PRIVILEGE_ENABLED = 0x00000002;
public static final int TokenUser = 1;
public static final int PROCESS_QUERY_INFORMATION = 0x0400;
}

View file

@ -0,0 +1,232 @@
/*
* Copyright (c) 2008, 2011, 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.BasicFileAttributes;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.io.IOException;
import static sun.nio.fs.WindowsNativeDispatcher.*;
import static sun.nio.fs.WindowsConstants.*;
/**
* Windows implementation of DirectoryStream
*/
class WindowsDirectoryStream
implements DirectoryStream<Path>
{
private final WindowsPath dir;
private final DirectoryStream.Filter<? super Path> filter;
// handle to directory
private final long handle;
// first entry in the directory
private final String firstName;
// buffer for WIN32_FIND_DATA structure that receives information about file
private final NativeBuffer findDataBuffer;
private final Object closeLock = new Object();
// need closeLock to access these
private boolean isOpen = true;
private Iterator<Path> iterator;
WindowsDirectoryStream(WindowsPath dir, DirectoryStream.Filter<? super Path> filter)
throws IOException
{
this.dir = dir;
this.filter = filter;
try {
// Need to append * or \* to match entries in directory.
String search = dir.getPathForWin32Calls();
char last = search.charAt(search.length() -1);
if (last == ':' || last == '\\') {
search += "*";
} else {
search += "\\*";
}
FirstFile first = FindFirstFile(search);
this.handle = first.handle();
this.firstName = first.name();
this.findDataBuffer = WindowsFileAttributes.getBufferForFindData();
} catch (WindowsException x) {
if (x.lastError() == ERROR_DIRECTORY) {
throw new NotDirectoryException(dir.getPathForExceptionMessage());
}
x.rethrowAsIOException(dir);
// keep compiler happy
throw new AssertionError();
}
}
@Override
public void close()
throws IOException
{
synchronized (closeLock) {
if (!isOpen)
return;
isOpen = false;
}
findDataBuffer.release();
try {
FindClose(handle);
} catch (WindowsException x) {
x.rethrowAsIOException(dir);
}
}
@Override
public Iterator<Path> iterator() {
if (!isOpen) {
throw new IllegalStateException("Directory stream is closed");
}
synchronized (this) {
if (iterator != null)
throw new IllegalStateException("Iterator already obtained");
iterator = new WindowsDirectoryIterator(firstName);
return iterator;
}
}
private class WindowsDirectoryIterator implements Iterator<Path> {
private boolean atEof;
private String first;
private Path nextEntry;
private String prefix;
WindowsDirectoryIterator(String first) {
atEof = false;
this.first = first;
if (dir.needsSlashWhenResolving()) {
prefix = dir.toString() + "\\";
} else {
prefix = dir.toString();
}
}
// links to self and parent directories are ignored
private boolean isSelfOrParent(String name) {
return name.equals(".") || name.equals("..");
}
// applies filter and also ignores "." and ".."
private Path acceptEntry(String s, BasicFileAttributes attrs) {
Path entry = WindowsPath
.createFromNormalizedPath(dir.getFileSystem(), prefix + s, attrs);
try {
if (filter.accept(entry))
return entry;
} catch (IOException ioe) {
throw new DirectoryIteratorException(ioe);
}
return null;
}
// reads next directory entry
private Path readNextEntry() {
// handle first element returned by search
if (first != null) {
nextEntry = isSelfOrParent(first) ? null : acceptEntry(first, null);
first = null;
if (nextEntry != null)
return nextEntry;
}
for (;;) {
String name = null;
WindowsFileAttributes attrs;
// synchronize on closeLock to prevent close while reading
synchronized (closeLock) {
try {
if (isOpen) {
name = FindNextFile(handle, findDataBuffer.address());
}
} catch (WindowsException x) {
IOException ioe = x.asIOException(dir);
throw new DirectoryIteratorException(ioe);
}
// NO_MORE_FILES or stream closed
if (name == null) {
atEof = true;
return null;
}
// ignore link to self and parent directories
if (isSelfOrParent(name))
continue;
// grab the attributes from the WIN32_FIND_DATA structure
// (needs to be done while holding closeLock because close
// will release the buffer)
attrs = WindowsFileAttributes
.fromFindData(findDataBuffer.address());
}
// return entry if accepted by filter
Path entry = acceptEntry(name, attrs);
if (entry != null)
return entry;
}
}
@Override
public synchronized boolean hasNext() {
if (nextEntry == null && !atEof)
nextEntry = readNextEntry();
return nextEntry != null;
}
@Override
public synchronized Path next() {
Path result = null;
if (nextEntry == null && !atEof) {
result = readNextEntry();
} else {
result = nextEntry;
nextEntry = null;
}
if (result == null)
throw new NoSuchElementException();
return result;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}

View file

@ -0,0 +1,115 @@
/*
* 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.nio.file.*;
import java.io.IOException;
import static sun.nio.fs.WindowsConstants.*;
/**
* Internal exception thrown when a Win32 calls fails.
*/
class WindowsException extends Exception {
static final long serialVersionUID = 2765039493083748820L;
private int lastError;
private String msg;
WindowsException(int lastError) {
this.lastError = lastError;
this.msg = null;
}
WindowsException(String msg) {
this.lastError = 0;
this.msg = msg;
}
int lastError() {
return lastError;
}
String errorString() {
if (msg == null) {
msg = WindowsNativeDispatcher.FormatMessage(lastError);
if (msg == null) {
msg = "Unknown error: 0x" + Integer.toHexString(lastError);
}
}
return msg;
}
@Override
public String getMessage() {
return errorString();
}
@Override
public Throwable fillInStackTrace() {
// This is an internal exception; the stack trace is irrelevant.
return this;
}
private IOException translateToIOException(String file, String other) {
// not created with last error
if (lastError() == 0)
return new IOException(errorString());
// handle specific cases
if (lastError() == ERROR_FILE_NOT_FOUND || lastError() == ERROR_PATH_NOT_FOUND)
return new NoSuchFileException(file, other, null);
if (lastError() == ERROR_FILE_EXISTS || lastError() == ERROR_ALREADY_EXISTS)
return new FileAlreadyExistsException(file, other, null);
if (lastError() == ERROR_ACCESS_DENIED)
return new AccessDeniedException(file, other, null);
// fallback to the more general exception
return new FileSystemException(file, other, errorString());
}
void rethrowAsIOException(String file) throws IOException {
IOException x = translateToIOException(file, null);
throw x;
}
void rethrowAsIOException(WindowsPath file, WindowsPath other) throws IOException {
String a = (file == null) ? null : file.getPathForExceptionMessage();
String b = (other == null) ? null : other.getPathForExceptionMessage();
IOException x = translateToIOException(a, b);
throw x;
}
void rethrowAsIOException(WindowsPath file) throws IOException {
rethrowAsIOException(file, null);
}
IOException asIOException(WindowsPath file) {
return translateToIOException(file.getPathForExceptionMessage(), null);
}
}

View file

@ -0,0 +1,295 @@
/*
* Copyright (c) 2008, 2011, 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.attribute.*;
import java.util.*;
import java.io.IOException;
import static sun.nio.fs.WindowsNativeDispatcher.*;
import static sun.nio.fs.WindowsConstants.*;
class WindowsFileAttributeViews {
private static class Basic extends AbstractBasicFileAttributeView {
final WindowsPath file;
final boolean followLinks;
Basic(WindowsPath file, boolean followLinks) {
this.file = file;
this.followLinks = followLinks;
}
@Override
public WindowsFileAttributes readAttributes() throws IOException {
file.checkRead();
try {
return WindowsFileAttributes.get(file, followLinks);
} catch (WindowsException x) {
x.rethrowAsIOException(file);
return null; // keep compiler happy
}
}
/**
* Adjusts a Windows time for the FAT epoch.
*/
private long adjustForFatEpoch(long time) {
// 1/1/1980 in Windows Time
final long FAT_EPOCH = 119600064000000000L;
if (time != -1L && time < FAT_EPOCH) {
return FAT_EPOCH;
} else {
return time;
}
}
/**
* Parameter values in Windows times.
*/
void setFileTimes(long createTime,
long lastAccessTime,
long lastWriteTime)
throws IOException
{
long handle = -1L;
try {
int flags = FILE_FLAG_BACKUP_SEMANTICS;
if (!followLinks)
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
handle = CreateFile(file.getPathForWin32Calls(),
FILE_WRITE_ATTRIBUTES,
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
OPEN_EXISTING,
flags);
} catch (WindowsException x) {
x.rethrowAsIOException(file);
}
// update times
try {
SetFileTime(handle,
createTime,
lastAccessTime,
lastWriteTime);
} catch (WindowsException x) {
// If ERROR_INVALID_PARAMETER is returned and the volume is
// FAT then adjust to the FAT epoch and retry.
if (followLinks && x.lastError() == ERROR_INVALID_PARAMETER) {
try {
if (WindowsFileStore.create(file).type().equals("FAT")) {
SetFileTime(handle,
adjustForFatEpoch(createTime),
adjustForFatEpoch(lastAccessTime),
adjustForFatEpoch(lastWriteTime));
// retry succeeded
x = null;
}
} catch (SecurityException ignore) {
} catch (WindowsException ignore) {
} catch (IOException ignore) {
// ignore exceptions to let original exception be thrown
}
}
if (x != null)
x.rethrowAsIOException(file);
} finally {
CloseHandle(handle);
}
}
@Override
public void setTimes(FileTime lastModifiedTime,
FileTime lastAccessTime,
FileTime createTime) throws IOException
{
// if all null then do nothing
if (lastModifiedTime == null && lastAccessTime == null &&
createTime == null)
{
// no effect
return;
}
// permission check
file.checkWrite();
// update times
long t1 = (createTime == null) ? -1L :
WindowsFileAttributes.toWindowsTime(createTime);
long t2 = (lastAccessTime == null) ? -1L :
WindowsFileAttributes.toWindowsTime(lastAccessTime);
long t3 = (lastModifiedTime == null) ? -1L :
WindowsFileAttributes.toWindowsTime(lastModifiedTime);
setFileTimes(t1, t2, t3);
}
}
static class Dos extends Basic implements DosFileAttributeView {
private static final String READONLY_NAME = "readonly";
private static final String ARCHIVE_NAME = "archive";
private static final String SYSTEM_NAME = "system";
private static final String HIDDEN_NAME = "hidden";
private static final String ATTRIBUTES_NAME = "attributes";
// the names of the DOS attributes (includes basic)
static final Set<String> dosAttributeNames =
Util.newSet(basicAttributeNames,
READONLY_NAME, ARCHIVE_NAME, SYSTEM_NAME, HIDDEN_NAME, ATTRIBUTES_NAME);
Dos(WindowsPath file, boolean followLinks) {
super(file, followLinks);
}
@Override
public String name() {
return "dos";
}
@Override
public void setAttribute(String attribute, Object value)
throws IOException
{
if (attribute.equals(READONLY_NAME)) {
setReadOnly((Boolean)value);
return;
}
if (attribute.equals(ARCHIVE_NAME)) {
setArchive((Boolean)value);
return;
}
if (attribute.equals(SYSTEM_NAME)) {
setSystem((Boolean)value);
return;
}
if (attribute.equals(HIDDEN_NAME)) {
setHidden((Boolean)value);
return;
}
super.setAttribute(attribute, value);
}
@Override
public Map<String,Object> readAttributes(String[] attributes)
throws IOException
{
AttributesBuilder builder =
AttributesBuilder.create(dosAttributeNames, attributes);
WindowsFileAttributes attrs = readAttributes();
addRequestedBasicAttributes(attrs, builder);
if (builder.match(READONLY_NAME))
builder.add(READONLY_NAME, attrs.isReadOnly());
if (builder.match(ARCHIVE_NAME))
builder.add(ARCHIVE_NAME, attrs.isArchive());
if (builder.match(SYSTEM_NAME))
builder.add(SYSTEM_NAME, attrs.isSystem());
if (builder.match(HIDDEN_NAME))
builder.add(HIDDEN_NAME, attrs.isHidden());
if (builder.match(ATTRIBUTES_NAME))
builder.add(ATTRIBUTES_NAME, attrs.attributes());
return builder.unmodifiableMap();
}
/**
* Update DOS attributes
*/
private void updateAttributes(int flag, boolean enable)
throws IOException
{
file.checkWrite();
// GetFileAttributes & SetFileAttributes do not follow links so when
// following links we need the final target
String path = WindowsLinkSupport.getFinalPath(file, followLinks);
try {
int oldValue = GetFileAttributes(path);
int newValue = oldValue;
if (enable) {
newValue |= flag;
} else {
newValue &= ~flag;
}
if (newValue != oldValue) {
SetFileAttributes(path, newValue);
}
} catch (WindowsException x) {
// don't reveal target in exception
x.rethrowAsIOException(file);
}
}
@Override
public void setReadOnly(boolean value) throws IOException {
updateAttributes(FILE_ATTRIBUTE_READONLY, value);
}
@Override
public void setHidden(boolean value) throws IOException {
updateAttributes(FILE_ATTRIBUTE_HIDDEN, value);
}
@Override
public void setArchive(boolean value) throws IOException {
updateAttributes(FILE_ATTRIBUTE_ARCHIVE, value);
}
@Override
public void setSystem(boolean value) throws IOException {
updateAttributes(FILE_ATTRIBUTE_SYSTEM, value);
}
// package-private
// Copy given attributes to the file.
void setAttributes(WindowsFileAttributes attrs)
throws IOException
{
// copy DOS attributes to target
int flags = 0;
if (attrs.isReadOnly()) flags |= FILE_ATTRIBUTE_READONLY;
if (attrs.isHidden()) flags |= FILE_ATTRIBUTE_HIDDEN;
if (attrs.isArchive()) flags |= FILE_ATTRIBUTE_ARCHIVE;
if (attrs.isSystem()) flags |= FILE_ATTRIBUTE_SYSTEM;
updateAttributes(flags, true);
// copy file times to target - must be done after updating FAT attributes
// as otherwise the last modified time may be wrong.
setFileTimes(
WindowsFileAttributes.toWindowsTime(attrs.creationTime()),
WindowsFileAttributes.toWindowsTime(attrs.lastModifiedTime()),
WindowsFileAttributes.toWindowsTime(attrs.lastAccessTime()));
}
}
static Basic createBasicView(WindowsPath file, boolean followLinks) {
return new Basic(file, followLinks);
}
static Dos createDosView(WindowsPath file, boolean followLinks) {
return new Dos(file, followLinks);
}
}

View file

@ -0,0 +1,475 @@
/*
* Copyright (c) 2008, 2012, 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.attribute.*;
import java.util.concurrent.TimeUnit;
import jdk.internal.misc.Unsafe;
import sun.security.action.GetPropertyAction;
import static sun.nio.fs.WindowsNativeDispatcher.*;
import static sun.nio.fs.WindowsConstants.*;
/**
* Windows implementation of DosFileAttributes/BasicFileAttributes
*/
class WindowsFileAttributes
implements DosFileAttributes
{
private static final Unsafe unsafe = Unsafe.getUnsafe();
/*
* typedef struct _BY_HANDLE_FILE_INFORMATION {
* DWORD dwFileAttributes;
* FILETIME ftCreationTime;
* FILETIME ftLastAccessTime;
* FILETIME ftLastWriteTime;
* DWORD dwVolumeSerialNumber;
* DWORD nFileSizeHigh;
* DWORD nFileSizeLow;
* DWORD nNumberOfLinks;
* DWORD nFileIndexHigh;
* DWORD nFileIndexLow;
* } BY_HANDLE_FILE_INFORMATION;
*/
private static final short SIZEOF_FILE_INFORMATION = 52;
private static final short OFFSETOF_FILE_INFORMATION_ATTRIBUTES = 0;
private static final short OFFSETOF_FILE_INFORMATION_CREATETIME = 4;
private static final short OFFSETOF_FILE_INFORMATION_LASTACCESSTIME = 12;
private static final short OFFSETOF_FILE_INFORMATION_LASTWRITETIME = 20;
private static final short OFFSETOF_FILE_INFORMATION_VOLSERIALNUM = 28;
private static final short OFFSETOF_FILE_INFORMATION_SIZEHIGH = 32;
private static final short OFFSETOF_FILE_INFORMATION_SIZELOW = 36;
private static final short OFFSETOF_FILE_INFORMATION_INDEXHIGH = 44;
private static final short OFFSETOF_FILE_INFORMATION_INDEXLOW = 48;
/*
* typedef struct _WIN32_FILE_ATTRIBUTE_DATA {
* DWORD dwFileAttributes;
* FILETIME ftCreationTime;
* FILETIME ftLastAccessTime;
* FILETIME ftLastWriteTime;
* DWORD nFileSizeHigh;
* DWORD nFileSizeLow;
* } WIN32_FILE_ATTRIBUTE_DATA;
*/
private static final short SIZEOF_FILE_ATTRIBUTE_DATA = 36;
private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES = 0;
private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_CREATETIME = 4;
private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_LASTACCESSTIME = 12;
private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_LASTWRITETIME = 20;
private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_SIZEHIGH = 28;
private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_SIZELOW = 32;
/**
* typedef struct _WIN32_FIND_DATA {
* DWORD dwFileAttributes;
* FILETIME ftCreationTime;
* FILETIME ftLastAccessTime;
* FILETIME ftLastWriteTime;
* DWORD nFileSizeHigh;
* DWORD nFileSizeLow;
* DWORD dwReserved0;
* DWORD dwReserved1;
* TCHAR cFileName[MAX_PATH];
* TCHAR cAlternateFileName[14];
* } WIN32_FIND_DATA;
*/
private static final short SIZEOF_FIND_DATA = 592;
private static final short OFFSETOF_FIND_DATA_ATTRIBUTES = 0;
private static final short OFFSETOF_FIND_DATA_CREATETIME = 4;
private static final short OFFSETOF_FIND_DATA_LASTACCESSTIME = 12;
private static final short OFFSETOF_FIND_DATA_LASTWRITETIME = 20;
private static final short OFFSETOF_FIND_DATA_SIZEHIGH = 28;
private static final short OFFSETOF_FIND_DATA_SIZELOW = 32;
private static final short OFFSETOF_FIND_DATA_RESERVED0 = 36;
// used to adjust values between Windows and java epoch
private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L;
// indicates if accurate metadata is required (interesting on NTFS only)
private static final boolean ensureAccurateMetadata;
static {
String propValue = GetPropertyAction.privilegedGetProperty(
"sun.nio.fs.ensureAccurateMetadata", "false");
ensureAccurateMetadata = (propValue.length() == 0) ?
true : Boolean.valueOf(propValue);
}
// attributes
private final int fileAttrs;
private final long creationTime;
private final long lastAccessTime;
private final long lastWriteTime;
private final long size;
private final int reparseTag;
// additional attributes when using GetFileInformationByHandle
private final int volSerialNumber;
private final int fileIndexHigh;
private final int fileIndexLow;
/**
* Convert 64-bit value representing the number of 100-nanosecond intervals
* since January 1, 1601 to a FileTime.
*/
static FileTime toFileTime(long time) {
// 100ns -> us
time /= 10L;
// adjust to java epoch
time += WINDOWS_EPOCH_IN_MICROSECONDS;
return FileTime.from(time, TimeUnit.MICROSECONDS);
}
/**
* Convert FileTime to 64-bit value representing the number of 100-nanosecond
* intervals since January 1, 1601.
*/
static long toWindowsTime(FileTime time) {
long value = time.to(TimeUnit.MICROSECONDS);
// adjust to Windows epoch+= 11644473600000000L;
value -= WINDOWS_EPOCH_IN_MICROSECONDS;
// us -> 100ns
value *= 10L;
return value;
}
/**
* Initialize a new instance of this class
*/
private WindowsFileAttributes(int fileAttrs,
long creationTime,
long lastAccessTime,
long lastWriteTime,
long size,
int reparseTag,
int volSerialNumber,
int fileIndexHigh,
int fileIndexLow)
{
this.fileAttrs = fileAttrs;
this.creationTime = creationTime;
this.lastAccessTime = lastAccessTime;
this.lastWriteTime = lastWriteTime;
this.size = size;
this.reparseTag = reparseTag;
this.volSerialNumber = volSerialNumber;
this.fileIndexHigh = fileIndexHigh;
this.fileIndexLow = fileIndexLow;
}
/**
* Create a WindowsFileAttributes from a BY_HANDLE_FILE_INFORMATION structure
*/
private static WindowsFileAttributes fromFileInformation(long address, int reparseTag) {
int fileAttrs = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_ATTRIBUTES);
long creationTime = unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_CREATETIME);
long lastAccessTime = unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_LASTACCESSTIME);
long lastWriteTime = unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_LASTWRITETIME);
long size = ((long)(unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_SIZEHIGH)) << 32)
+ (unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_SIZELOW) & 0xFFFFFFFFL);
int volSerialNumber = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_VOLSERIALNUM);
int fileIndexHigh = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_INDEXHIGH);
int fileIndexLow = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_INDEXLOW);
return new WindowsFileAttributes(fileAttrs,
creationTime,
lastAccessTime,
lastWriteTime,
size,
reparseTag,
volSerialNumber,
fileIndexHigh,
fileIndexLow);
}
/**
* Create a WindowsFileAttributes from a WIN32_FILE_ATTRIBUTE_DATA structure
*/
private static WindowsFileAttributes fromFileAttributeData(long address, int reparseTag) {
int fileAttrs = unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES);
long creationTime = unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_CREATETIME);
long lastAccessTime = unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_LASTACCESSTIME);
long lastWriteTime = unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_LASTWRITETIME);
long size = ((long)(unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_SIZEHIGH)) << 32)
+ (unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_SIZELOW) & 0xFFFFFFFFL);
return new WindowsFileAttributes(fileAttrs,
creationTime,
lastAccessTime,
lastWriteTime,
size,
reparseTag,
0, // volSerialNumber
0, // fileIndexHigh
0); // fileIndexLow
}
/**
* Allocates a native buffer for a WIN32_FIND_DATA structure
*/
static NativeBuffer getBufferForFindData() {
return NativeBuffers.getNativeBuffer(SIZEOF_FIND_DATA);
}
/**
* Create a WindowsFileAttributes from a WIN32_FIND_DATA structure
*/
static WindowsFileAttributes fromFindData(long address) {
int fileAttrs = unsafe.getInt(address + OFFSETOF_FIND_DATA_ATTRIBUTES);
long creationTime = unsafe.getLong(address + OFFSETOF_FIND_DATA_CREATETIME);
long lastAccessTime = unsafe.getLong(address + OFFSETOF_FIND_DATA_LASTACCESSTIME);
long lastWriteTime = unsafe.getLong(address + OFFSETOF_FIND_DATA_LASTWRITETIME);
long size = ((long)(unsafe.getInt(address + OFFSETOF_FIND_DATA_SIZEHIGH)) << 32)
+ (unsafe.getInt(address + OFFSETOF_FIND_DATA_SIZELOW) & 0xFFFFFFFFL);
int reparseTag = isReparsePoint(fileAttrs) ?
unsafe.getInt(address + OFFSETOF_FIND_DATA_RESERVED0) : 0;
return new WindowsFileAttributes(fileAttrs,
creationTime,
lastAccessTime,
lastWriteTime,
size,
reparseTag,
0, // volSerialNumber
0, // fileIndexHigh
0); // fileIndexLow
}
/**
* Reads the attributes of an open file
*/
static WindowsFileAttributes readAttributes(long handle)
throws WindowsException
{
NativeBuffer buffer = NativeBuffers
.getNativeBuffer(SIZEOF_FILE_INFORMATION);
try {
long address = buffer.address();
GetFileInformationByHandle(handle, address);
// if file is a reparse point then read the tag
int reparseTag = 0;
int fileAttrs = unsafe
.getInt(address + OFFSETOF_FILE_INFORMATION_ATTRIBUTES);
if (isReparsePoint(fileAttrs)) {
int size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
NativeBuffer reparseBuffer = NativeBuffers.getNativeBuffer(size);
try {
DeviceIoControlGetReparsePoint(handle, reparseBuffer.address(), size);
reparseTag = (int)unsafe.getLong(reparseBuffer.address());
} finally {
reparseBuffer.release();
}
}
return fromFileInformation(address, reparseTag);
} finally {
buffer.release();
}
}
/**
* Returns attributes of given file.
*/
static WindowsFileAttributes get(WindowsPath path, boolean followLinks)
throws WindowsException
{
if (!ensureAccurateMetadata) {
WindowsException firstException = null;
// GetFileAttributesEx is the fastest way to read the attributes
NativeBuffer buffer =
NativeBuffers.getNativeBuffer(SIZEOF_FILE_ATTRIBUTE_DATA);
try {
long address = buffer.address();
GetFileAttributesEx(path.getPathForWin32Calls(), address);
// if reparse point then file may be a sym link; otherwise
// just return the attributes
int fileAttrs = unsafe
.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES);
if (!isReparsePoint(fileAttrs))
return fromFileAttributeData(address, 0);
} catch (WindowsException x) {
if (x.lastError() != ERROR_SHARING_VIOLATION)
throw x;
firstException = x;
} finally {
buffer.release();
}
// For sharing violations, fallback to FindFirstFile if the file
// is not a root directory.
if (firstException != null) {
String search = path.getPathForWin32Calls();
char last = search.charAt(search.length() -1);
if (last == ':' || last == '\\')
throw firstException;
buffer = getBufferForFindData();
try {
long handle = FindFirstFile(search, buffer.address());
FindClose(handle);
WindowsFileAttributes attrs = fromFindData(buffer.address());
// FindFirstFile does not follow sym links. Even if
// followLinks is false, there isn't sufficient information
// in the WIN32_FIND_DATA structure to know if the reparse
// point is a sym link.
if (attrs.isReparsePoint())
throw firstException;
return attrs;
} catch (WindowsException ignore) {
throw firstException;
} finally {
buffer.release();
}
}
}
// file is reparse point so need to open file to get attributes
long handle = path.openForReadAttributeAccess(followLinks);
try {
return readAttributes(handle);
} finally {
CloseHandle(handle);
}
}
/**
* Returns true if the attributes are of the same file - both files must
* be open.
*/
static boolean isSameFile(WindowsFileAttributes attrs1,
WindowsFileAttributes attrs2)
{
// volume serial number and file index must be the same
return (attrs1.volSerialNumber == attrs2.volSerialNumber) &&
(attrs1.fileIndexHigh == attrs2.fileIndexHigh) &&
(attrs1.fileIndexLow == attrs2.fileIndexLow);
}
/**
* Returns true if the attributes are of a file with a reparse point.
*/
static boolean isReparsePoint(int attributes) {
return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
}
// package-private
int attributes() {
return fileAttrs;
}
int volSerialNumber() {
return volSerialNumber;
}
int fileIndexHigh() {
return fileIndexHigh;
}
int fileIndexLow() {
return fileIndexLow;
}
@Override
public long size() {
return size;
}
@Override
public FileTime lastModifiedTime() {
return toFileTime(lastWriteTime);
}
@Override
public FileTime lastAccessTime() {
return toFileTime(lastAccessTime);
}
@Override
public FileTime creationTime() {
return toFileTime(creationTime);
}
@Override
public Object fileKey() {
return null;
}
// package private
boolean isReparsePoint() {
return isReparsePoint(fileAttrs);
}
boolean isDirectoryLink() {
return isSymbolicLink() && ((fileAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0);
}
@Override
public boolean isSymbolicLink() {
return reparseTag == IO_REPARSE_TAG_SYMLINK;
}
@Override
public boolean isDirectory() {
// ignore FILE_ATTRIBUTE_DIRECTORY attribute if file is a sym link
if (isSymbolicLink())
return false;
return ((fileAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0);
}
@Override
public boolean isOther() {
if (isSymbolicLink())
return false;
// return true if device or reparse point
return ((fileAttrs & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)) != 0);
}
@Override
public boolean isRegularFile() {
return !isSymbolicLink() && !isDirectory() && !isOther();
}
@Override
public boolean isReadOnly() {
return (fileAttrs & FILE_ATTRIBUTE_READONLY) != 0;
}
@Override
public boolean isHidden() {
return (fileAttrs & FILE_ATTRIBUTE_HIDDEN) != 0;
}
@Override
public boolean isArchive() {
return (fileAttrs & FILE_ATTRIBUTE_ARCHIVE) != 0;
}
@Override
public boolean isSystem() {
return (fileAttrs & FILE_ATTRIBUTE_SYSTEM) != 0;
}
}

View file

@ -0,0 +1,502 @@
/*
* 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.nio.file.*;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import static sun.nio.fs.WindowsNativeDispatcher.*;
import static sun.nio.fs.WindowsConstants.*;
/**
* Utility methods for copying and moving files.
*/
class WindowsFileCopy {
private WindowsFileCopy() {
}
/**
* Copy file from source to target
*/
static void copy(final WindowsPath source,
final WindowsPath target,
CopyOption... options)
throws IOException
{
// map options
boolean replaceExisting = false;
boolean copyAttributes = false;
boolean followLinks = true;
boolean interruptible = false;
for (CopyOption option: options) {
if (option == StandardCopyOption.REPLACE_EXISTING) {
replaceExisting = true;
continue;
}
if (option == LinkOption.NOFOLLOW_LINKS) {
followLinks = false;
continue;
}
if (option == StandardCopyOption.COPY_ATTRIBUTES) {
copyAttributes = true;
continue;
}
if (ExtendedOptions.INTERRUPTIBLE.matches(option)) {
interruptible = true;
continue;
}
if (option == null)
throw new NullPointerException();
throw new UnsupportedOperationException("Unsupported copy option");
}
// check permissions. If the source file is a symbolic link then
// later we must also check LinkPermission
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
source.checkRead();
target.checkWrite();
}
// get attributes of source file
// attempt to get attributes of target file
// if both files are the same there is nothing to do
// if target exists and !replace then throw exception
WindowsFileAttributes sourceAttrs = null;
WindowsFileAttributes targetAttrs = null;
long sourceHandle = 0L;
try {
sourceHandle = source.openForReadAttributeAccess(followLinks);
} catch (WindowsException x) {
x.rethrowAsIOException(source);
}
try {
// source attributes
try {
sourceAttrs = WindowsFileAttributes.readAttributes(sourceHandle);
} catch (WindowsException x) {
x.rethrowAsIOException(source);
}
// open target (don't follow links)
long targetHandle = 0L;
try {
targetHandle = target.openForReadAttributeAccess(false);
try {
targetAttrs = WindowsFileAttributes.readAttributes(targetHandle);
// if both files are the same then nothing to do
if (WindowsFileAttributes.isSameFile(sourceAttrs, targetAttrs)) {
return;
}
// can't replace file
if (!replaceExisting) {
throw new FileAlreadyExistsException(
target.getPathForExceptionMessage());
}
} finally {
CloseHandle(targetHandle);
}
} catch (WindowsException x) {
// ignore
}
} finally {
CloseHandle(sourceHandle);
}
// if source file is a symbolic link then we must check for LinkPermission
if (sm != null && sourceAttrs.isSymbolicLink()) {
sm.checkPermission(new LinkPermission("symbolic"));
}
final String sourcePath = asWin32Path(source);
final String targetPath = asWin32Path(target);
// if target exists then delete it.
if (targetAttrs != null) {
try {
if (targetAttrs.isDirectory() || targetAttrs.isDirectoryLink()) {
RemoveDirectory(targetPath);
} else {
DeleteFile(targetPath);
}
} catch (WindowsException x) {
if (targetAttrs.isDirectory()) {
// ERROR_ALREADY_EXISTS is returned when attempting to delete
// non-empty directory on SAMBA servers.
if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
x.lastError() == ERROR_ALREADY_EXISTS)
{
throw new DirectoryNotEmptyException(
target.getPathForExceptionMessage());
}
}
x.rethrowAsIOException(target);
}
}
// Use CopyFileEx if the file is not a directory or junction
if (!sourceAttrs.isDirectory() && !sourceAttrs.isDirectoryLink()) {
final int flags = (!followLinks) ? COPY_FILE_COPY_SYMLINK : 0;
if (interruptible) {
// interruptible copy
Cancellable copyTask = new Cancellable() {
@Override
public int cancelValue() {
return 1; // TRUE
}
@Override
public void implRun() throws IOException {
try {
CopyFileEx(sourcePath, targetPath, flags,
addressToPollForCancel());
} catch (WindowsException x) {
x.rethrowAsIOException(source, target);
}
}
};
try {
Cancellable.runInterruptibly(copyTask);
} catch (ExecutionException e) {
Throwable t = e.getCause();
if (t instanceof IOException)
throw (IOException)t;
throw new IOException(t);
}
} else {
// non-interruptible copy
try {
CopyFileEx(sourcePath, targetPath, flags, 0L);
} catch (WindowsException x) {
x.rethrowAsIOException(source, target);
}
}
if (copyAttributes) {
// CopyFileEx does not copy security attributes
try {
copySecurityAttributes(source, target, followLinks);
} catch (IOException x) {
// ignore
}
}
return;
}
// copy directory or directory junction
try {
if (sourceAttrs.isDirectory()) {
CreateDirectory(targetPath, 0L);
} else {
String linkTarget = WindowsLinkSupport.readLink(source);
int flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
CreateSymbolicLink(targetPath,
WindowsPath.addPrefixIfNeeded(linkTarget),
flags);
}
} catch (WindowsException x) {
x.rethrowAsIOException(target);
}
if (copyAttributes) {
// copy DOS/timestamps attributes
WindowsFileAttributeViews.Dos view =
WindowsFileAttributeViews.createDosView(target, false);
try {
view.setAttributes(sourceAttrs);
} catch (IOException x) {
if (sourceAttrs.isDirectory()) {
try {
RemoveDirectory(targetPath);
} catch (WindowsException ignore) { }
}
}
// copy security attributes. If this fail it doesn't cause the move
// to fail.
try {
copySecurityAttributes(source, target, followLinks);
} catch (IOException ignore) { }
}
}
/**
* Move file from source to target
*/
static void move(WindowsPath source, WindowsPath target, CopyOption... options)
throws IOException
{
// map options
boolean atomicMove = false;
boolean replaceExisting = false;
for (CopyOption option: options) {
if (option == StandardCopyOption.ATOMIC_MOVE) {
atomicMove = true;
continue;
}
if (option == StandardCopyOption.REPLACE_EXISTING) {
replaceExisting = true;
continue;
}
if (option == LinkOption.NOFOLLOW_LINKS) {
// ignore
continue;
}
if (option == null) throw new NullPointerException();
throw new UnsupportedOperationException("Unsupported copy option");
}
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
source.checkWrite();
target.checkWrite();
}
final String sourcePath = asWin32Path(source);
final String targetPath = asWin32Path(target);
// atomic case
if (atomicMove) {
try {
MoveFileEx(sourcePath, targetPath, MOVEFILE_REPLACE_EXISTING);
} catch (WindowsException x) {
if (x.lastError() == ERROR_NOT_SAME_DEVICE) {
throw new AtomicMoveNotSupportedException(
source.getPathForExceptionMessage(),
target.getPathForExceptionMessage(),
x.errorString());
}
x.rethrowAsIOException(source, target);
}
return;
}
// get attributes of source file
// attempt to get attributes of target file
// if both files are the same there is nothing to do
// if target exists and !replace then throw exception
WindowsFileAttributes sourceAttrs = null;
WindowsFileAttributes targetAttrs = null;
long sourceHandle = 0L;
try {
sourceHandle = source.openForReadAttributeAccess(false);
} catch (WindowsException x) {
x.rethrowAsIOException(source);
}
try {
// source attributes
try {
sourceAttrs = WindowsFileAttributes.readAttributes(sourceHandle);
} catch (WindowsException x) {
x.rethrowAsIOException(source);
}
// open target (don't follow links)
long targetHandle = 0L;
try {
targetHandle = target.openForReadAttributeAccess(false);
try {
targetAttrs = WindowsFileAttributes.readAttributes(targetHandle);
// if both files are the same then nothing to do
if (WindowsFileAttributes.isSameFile(sourceAttrs, targetAttrs)) {
return;
}
// can't replace file
if (!replaceExisting) {
throw new FileAlreadyExistsException(
target.getPathForExceptionMessage());
}
} finally {
CloseHandle(targetHandle);
}
} catch (WindowsException x) {
// ignore
}
} finally {
CloseHandle(sourceHandle);
}
// if target exists then delete it.
if (targetAttrs != null) {
try {
if (targetAttrs.isDirectory() || targetAttrs.isDirectoryLink()) {
RemoveDirectory(targetPath);
} else {
DeleteFile(targetPath);
}
} catch (WindowsException x) {
if (targetAttrs.isDirectory()) {
// ERROR_ALREADY_EXISTS is returned when attempting to delete
// non-empty directory on SAMBA servers.
if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
x.lastError() == ERROR_ALREADY_EXISTS)
{
throw new DirectoryNotEmptyException(
target.getPathForExceptionMessage());
}
}
x.rethrowAsIOException(target);
}
}
// first try MoveFileEx (no options). If target is on same volume then
// all attributes (including security attributes) are preserved.
try {
MoveFileEx(sourcePath, targetPath, 0);
return;
} catch (WindowsException x) {
if (x.lastError() != ERROR_NOT_SAME_DEVICE)
x.rethrowAsIOException(source, target);
}
// target is on different volume so use MoveFileEx with copy option
if (!sourceAttrs.isDirectory() && !sourceAttrs.isDirectoryLink()) {
try {
MoveFileEx(sourcePath, targetPath, MOVEFILE_COPY_ALLOWED);
} catch (WindowsException x) {
x.rethrowAsIOException(source, target);
}
// MoveFileEx does not copy security attributes when moving
// across volumes.
try {
copySecurityAttributes(source, target, false);
} catch (IOException x) {
// ignore
}
return;
}
// moving directory or directory-link to another file system
assert sourceAttrs.isDirectory() || sourceAttrs.isDirectoryLink();
// create new directory or directory junction
try {
if (sourceAttrs.isDirectory()) {
CreateDirectory(targetPath, 0L);
} else {
String linkTarget = WindowsLinkSupport.readLink(source);
CreateSymbolicLink(targetPath,
WindowsPath.addPrefixIfNeeded(linkTarget),
SYMBOLIC_LINK_FLAG_DIRECTORY);
}
} catch (WindowsException x) {
x.rethrowAsIOException(target);
}
// copy timestamps/DOS attributes
WindowsFileAttributeViews.Dos view =
WindowsFileAttributeViews.createDosView(target, false);
try {
view.setAttributes(sourceAttrs);
} catch (IOException x) {
// rollback
try {
RemoveDirectory(targetPath);
} catch (WindowsException ignore) { }
throw x;
}
// copy security attributes. If this fails it doesn't cause the move
// to fail.
try {
copySecurityAttributes(source, target, false);
} catch (IOException ignore) { }
// delete source
try {
RemoveDirectory(sourcePath);
} catch (WindowsException x) {
// rollback
try {
RemoveDirectory(targetPath);
} catch (WindowsException ignore) { }
// ERROR_ALREADY_EXISTS is returned when attempting to delete
// non-empty directory on SAMBA servers.
if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
x.lastError() == ERROR_ALREADY_EXISTS)
{
throw new DirectoryNotEmptyException(
target.getPathForExceptionMessage());
}
x.rethrowAsIOException(source);
}
}
private static String asWin32Path(WindowsPath path) throws IOException {
try {
return path.getPathForWin32Calls();
} catch (WindowsException x) {
x.rethrowAsIOException(path);
return null;
}
}
/**
* Copy DACL/owner/group from source to target
*/
private static void copySecurityAttributes(WindowsPath source,
WindowsPath target,
boolean followLinks)
throws IOException
{
String path = WindowsLinkSupport.getFinalPath(source, followLinks);
// may need SeRestorePrivilege to set file owner
WindowsSecurity.Privilege priv =
WindowsSecurity.enablePrivilege("SeRestorePrivilege");
try {
int request = (DACL_SECURITY_INFORMATION |
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION);
NativeBuffer buffer =
WindowsAclFileAttributeView.getFileSecurity(path, request);
try {
try {
SetFileSecurity(target.getPathForWin32Calls(), request,
buffer.address());
} catch (WindowsException x) {
x.rethrowAsIOException(target);
}
} finally {
buffer.release();
}
} finally {
priv.drop();
}
}
}

View file

@ -0,0 +1,230 @@
/*
* Copyright (c) 2008, 2011, 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.io.IOException;
import static sun.nio.fs.WindowsConstants.*;
import static sun.nio.fs.WindowsNativeDispatcher.*;
/**
* Windows implementation of FileStore.
*/
class WindowsFileStore
extends FileStore
{
private final String root;
private final VolumeInformation volInfo;
private final int volType;
private final String displayName; // returned by toString
private WindowsFileStore(String root) throws WindowsException {
assert root.charAt(root.length()-1) == '\\';
this.root = root;
this.volInfo = GetVolumeInformation(root);
this.volType = GetDriveType(root);
// file store "display name" is the volume name if available
String vol = volInfo.volumeName();
if (vol.length() > 0) {
this.displayName = vol;
} else {
// TBD - should we map all types? Does this need to be localized?
this.displayName = (volType == DRIVE_REMOVABLE) ? "Removable Disk" : "";
}
}
static WindowsFileStore create(String root, boolean ignoreNotReady)
throws IOException
{
try {
return new WindowsFileStore(root);
} catch (WindowsException x) {
if (ignoreNotReady && x.lastError() == ERROR_NOT_READY)
return null;
x.rethrowAsIOException(root);
return null; // keep compiler happy
}
}
static WindowsFileStore create(WindowsPath file) throws IOException {
try {
// if the file is a link then GetVolumePathName returns the
// volume that the link is on so we need to call it with the
// final target
String target = WindowsLinkSupport.getFinalPath(file, true);
try {
return createFromPath(target);
} catch (WindowsException e) {
if (e.lastError() != ERROR_DIR_NOT_ROOT)
throw e;
target = WindowsLinkSupport.getFinalPath(file);
if (target == null)
throw new FileSystemException(file.getPathForExceptionMessage(),
null, "Couldn't resolve path");
return createFromPath(target);
}
} catch (WindowsException x) {
x.rethrowAsIOException(file);
return null; // keep compiler happy
}
}
private static WindowsFileStore createFromPath(String target) throws WindowsException {
String root = GetVolumePathName(target);
return new WindowsFileStore(root);
}
VolumeInformation volumeInformation() {
return volInfo;
}
int volumeType() {
return volType;
}
@Override
public String name() {
return volInfo.volumeName(); // "SYSTEM", "DVD-RW", ...
}
@Override
public String type() {
return volInfo.fileSystemName(); // "FAT", "NTFS", ...
}
@Override
public boolean isReadOnly() {
return ((volInfo.flags() & FILE_READ_ONLY_VOLUME) != 0);
}
// read the free space info
private DiskFreeSpace readDiskFreeSpace() throws IOException {
try {
return GetDiskFreeSpaceEx(root);
} catch (WindowsException x) {
x.rethrowAsIOException(root);
return null;
}
}
@Override
public long getTotalSpace() throws IOException {
return readDiskFreeSpace().totalNumberOfBytes();
}
@Override
public long getUsableSpace() throws IOException {
return readDiskFreeSpace().freeBytesAvailable();
}
@Override
public long getUnallocatedSpace() throws IOException {
return readDiskFreeSpace().freeBytesAvailable();
}
@Override
public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) {
if (type == null)
throw new NullPointerException();
return (V) null;
}
@Override
public Object getAttribute(String attribute) throws IOException {
// standard
if (attribute.equals("totalSpace"))
return getTotalSpace();
if (attribute.equals("usableSpace"))
return getUsableSpace();
if (attribute.equals("unallocatedSpace"))
return getUnallocatedSpace();
// windows specific for testing purposes
if (attribute.equals("volume:vsn"))
return volInfo.volumeSerialNumber();
if (attribute.equals("volume:isRemovable"))
return volType == DRIVE_REMOVABLE;
if (attribute.equals("volume:isCdrom"))
return volType == DRIVE_CDROM;
throw new UnsupportedOperationException("'" + attribute + "' not recognized");
}
@Override
public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
if (type == null)
throw new NullPointerException();
if (type == BasicFileAttributeView.class || type == DosFileAttributeView.class)
return true;
if (type == AclFileAttributeView.class || type == FileOwnerAttributeView.class)
return ((volInfo.flags() & FILE_PERSISTENT_ACLS) != 0);
if (type == UserDefinedFileAttributeView.class)
return ((volInfo.flags() & FILE_NAMED_STREAMS) != 0);
return false;
}
@Override
public boolean supportsFileAttributeView(String name) {
if (name.equals("basic") || name.equals("dos"))
return true;
if (name.equals("acl"))
return supportsFileAttributeView(AclFileAttributeView.class);
if (name.equals("owner"))
return supportsFileAttributeView(FileOwnerAttributeView.class);
if (name.equals("user"))
return supportsFileAttributeView(UserDefinedFileAttributeView.class);
return false;
}
@Override
public boolean equals(Object ob) {
if (ob == this)
return true;
if (!(ob instanceof WindowsFileStore))
return false;
WindowsFileStore other = (WindowsFileStore)ob;
return root.equals(other.root);
}
@Override
public int hashCode() {
return root.hashCode();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(displayName);
if (sb.length() > 0)
sb.append(" ");
sb.append("(");
// drop trailing slash
sb.append(root.subSequence(0, root.length()-1));
sb.append(")");
return sb.toString();
}
}

View file

@ -0,0 +1,299 @@
/*
* Copyright (c) 2008, 2015, 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.file.spi.*;
import java.util.*;
import java.util.regex.Pattern;
import java.io.IOException;
class WindowsFileSystem
extends FileSystem
{
private final WindowsFileSystemProvider provider;
// default directory (is absolute), and default root
private final String defaultDirectory;
private final String defaultRoot;
// package-private
WindowsFileSystem(WindowsFileSystemProvider provider,
String dir)
{
this.provider = provider;
// parse default directory and check it is absolute
WindowsPathParser.Result result = WindowsPathParser.parse(dir);
if ((result.type() != WindowsPathType.ABSOLUTE) &&
(result.type() != WindowsPathType.UNC))
throw new AssertionError("Default directory is not an absolute path");
this.defaultDirectory = result.path();
this.defaultRoot = result.root();
}
// package-private
String defaultDirectory() {
return defaultDirectory;
}
String defaultRoot() {
return defaultRoot;
}
@Override
public FileSystemProvider provider() {
return provider;
}
@Override
public String getSeparator() {
return "\\";
}
@Override
public boolean isOpen() {
return true;
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public void close() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public Iterable<Path> getRootDirectories() {
int drives = 0;
try {
drives = WindowsNativeDispatcher.GetLogicalDrives();
} catch (WindowsException x) {
// shouldn't happen
throw new AssertionError(x.getMessage());
}
// iterate over roots, ignoring those that the security manager denies
ArrayList<Path> result = new ArrayList<>();
SecurityManager sm = System.getSecurityManager();
for (int i = 0; i <= 25; i++) { // 0->A, 1->B, 2->C...
if ((drives & (1 << i)) != 0) {
StringBuilder sb = new StringBuilder(3);
sb.append((char)('A' + i));
sb.append(":\\");
String root = sb.toString();
if (sm != null) {
try {
sm.checkRead(root);
} catch (SecurityException x) {
continue;
}
}
result.add(WindowsPath.createFromNormalizedPath(this, root));
}
}
return Collections.unmodifiableList(result);
}
/**
* Iterator returned by getFileStores method.
*/
private class FileStoreIterator implements Iterator<FileStore> {
private final Iterator<Path> roots;
private FileStore next;
FileStoreIterator() {
this.roots = getRootDirectories().iterator();
}
private FileStore readNext() {
assert Thread.holdsLock(this);
for (;;) {
if (!roots.hasNext())
return null;
WindowsPath root = (WindowsPath)roots.next();
// ignore if security manager denies access
try {
root.checkRead();
} catch (SecurityException x) {
continue;
}
try {
FileStore fs = WindowsFileStore.create(root.toString(), true);
if (fs != null)
return fs;
} catch (IOException ioe) {
// skip it
}
}
}
@Override
public synchronized boolean hasNext() {
if (next != null)
return true;
next = readNext();
return next != null;
}
@Override
public synchronized FileStore next() {
if (next == null)
next = readNext();
if (next == null) {
throw new NoSuchElementException();
} else {
FileStore result = next;
next = null;
return result;
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
@Override
public Iterable<FileStore> getFileStores() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
} catch (SecurityException se) {
return Collections.emptyList();
}
}
return new Iterable<FileStore>() {
public Iterator<FileStore> iterator() {
return new FileStoreIterator();
}
};
}
// supported views
private static final Set<String> supportedFileAttributeViews = Collections
.unmodifiableSet(new HashSet<String>(Arrays.asList("basic", "dos", "acl", "owner", "user")));
@Override
public Set<String> supportedFileAttributeViews() {
return supportedFileAttributeViews;
}
@Override
public final Path getPath(String first, String... more) {
String path;
if (more.length == 0) {
path = first;
} else {
StringBuilder sb = new StringBuilder();
sb.append(first);
for (String segment: more) {
if (segment.length() > 0) {
if (sb.length() > 0)
sb.append('\\');
sb.append(segment);
}
}
path = sb.toString();
}
return WindowsPath.parse(this, path);
}
@Override
public UserPrincipalLookupService getUserPrincipalLookupService() {
return LookupService.instance;
}
private static class LookupService {
static final UserPrincipalLookupService instance =
new UserPrincipalLookupService() {
@Override
public UserPrincipal lookupPrincipalByName(String name)
throws IOException
{
return WindowsUserPrincipals.lookup(name);
}
@Override
public GroupPrincipal lookupPrincipalByGroupName(String group)
throws IOException
{
UserPrincipal user = WindowsUserPrincipals.lookup(group);
if (!(user instanceof GroupPrincipal))
throw new UserPrincipalNotFoundException(group);
return (GroupPrincipal)user;
}
};
}
@Override
public PathMatcher getPathMatcher(String syntaxAndInput) {
int pos = syntaxAndInput.indexOf(':');
if (pos <= 0 || pos == syntaxAndInput.length())
throw new IllegalArgumentException();
String syntax = syntaxAndInput.substring(0, pos);
String input = syntaxAndInput.substring(pos+1);
String expr;
if (syntax.equalsIgnoreCase(GLOB_SYNTAX)) {
expr = Globs.toWindowsRegexPattern(input);
} else {
if (syntax.equalsIgnoreCase(REGEX_SYNTAX)) {
expr = input;
} else {
throw new UnsupportedOperationException("Syntax '" + syntax +
"' not recognized");
}
}
// match in unicode_case_insensitive
final Pattern pattern = Pattern.compile(expr,
Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
// return matcher
return new PathMatcher() {
@Override
public boolean matches(Path path) {
return pattern.matcher(path.toString()).matches();
}
};
}
private static final String GLOB_SYNTAX = "glob";
private static final String REGEX_SYNTAX = "regex";
@Override
public WatchService newWatchService()
throws IOException
{
return new WindowsWatchService(this);
}
}

View file

@ -0,0 +1,624 @@
/*
* 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.file.attribute.*;
import java.nio.channels.*;
import java.net.URI;
import java.util.concurrent.ExecutorService;
import java.io.*;
import java.util.*;
import java.security.AccessController;
import jdk.internal.misc.Unsafe;
import sun.nio.ch.ThreadPool;
import sun.security.util.SecurityConstants;
import static sun.nio.fs.WindowsNativeDispatcher.*;
import static sun.nio.fs.WindowsSecurity.*;
import static sun.nio.fs.WindowsConstants.*;
public class WindowsFileSystemProvider
extends AbstractFileSystemProvider
{
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final String USER_DIR = "user.dir";
private final WindowsFileSystem theFileSystem;
public WindowsFileSystemProvider() {
theFileSystem = new WindowsFileSystem(this, System.getProperty(USER_DIR));
}
@Override
public String getScheme() {
return "file";
}
private void checkUri(URI uri) {
if (!uri.getScheme().equalsIgnoreCase(getScheme()))
throw new IllegalArgumentException("URI does not match this provider");
if (uri.getRawAuthority() != null)
throw new IllegalArgumentException("Authority component present");
String path = uri.getPath();
if (path == null)
throw new IllegalArgumentException("Path component is undefined");
if (!path.equals("/"))
throw new IllegalArgumentException("Path component should be '/'");
if (uri.getRawQuery() != null)
throw new IllegalArgumentException("Query component present");
if (uri.getRawFragment() != null)
throw new IllegalArgumentException("Fragment component present");
}
@Override
public FileSystem newFileSystem(URI uri, Map<String,?> env)
throws IOException
{
checkUri(uri);
throw new FileSystemAlreadyExistsException();
}
@Override
public final FileSystem getFileSystem(URI uri) {
checkUri(uri);
return theFileSystem;
}
@Override
public Path getPath(URI uri) {
return WindowsUriSupport.fromUri(theFileSystem, uri);
}
@Override
public FileChannel newFileChannel(Path path,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException
{
if (path == null)
throw new NullPointerException();
if (!(path instanceof WindowsPath))
throw new ProviderMismatchException();
WindowsPath file = (WindowsPath)path;
WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.fromAttribute(attrs);
try {
return WindowsChannelFactory
.newFileChannel(file.getPathForWin32Calls(),
file.getPathForPermissionCheck(),
options,
sd.address());
} catch (WindowsException x) {
x.rethrowAsIOException(file);
return null;
} finally {
if (sd != null)
sd.release();
}
}
@Override
public AsynchronousFileChannel newAsynchronousFileChannel(Path path,
Set<? extends OpenOption> options,
ExecutorService executor,
FileAttribute<?>... attrs)
throws IOException
{
if (path == null)
throw new NullPointerException();
if (!(path instanceof WindowsPath))
throw new ProviderMismatchException();
WindowsPath file = (WindowsPath)path;
ThreadPool pool = (executor == null) ? null : ThreadPool.wrap(executor, 0);
WindowsSecurityDescriptor sd =
WindowsSecurityDescriptor.fromAttribute(attrs);
try {
return WindowsChannelFactory
.newAsynchronousFileChannel(file.getPathForWin32Calls(),
file.getPathForPermissionCheck(),
options,
sd.address(),
pool);
} catch (WindowsException x) {
x.rethrowAsIOException(file);
return null;
} finally {
if (sd != null)
sd.release();
}
}
@Override
@SuppressWarnings("unchecked")
public <V extends FileAttributeView> V
getFileAttributeView(Path obj, Class<V> view, LinkOption... options)
{
WindowsPath file = WindowsPath.toWindowsPath(obj);
if (view == null)
throw new NullPointerException();
boolean followLinks = Util.followLinks(options);
if (view == BasicFileAttributeView.class)
return (V) WindowsFileAttributeViews.createBasicView(file, followLinks);
if (view == DosFileAttributeView.class)
return (V) WindowsFileAttributeViews.createDosView(file, followLinks);
if (view == AclFileAttributeView.class)
return (V) new WindowsAclFileAttributeView(file, followLinks);
if (view == FileOwnerAttributeView.class)
return (V) new FileOwnerAttributeViewImpl(
new WindowsAclFileAttributeView(file, followLinks));
if (view == UserDefinedFileAttributeView.class)
return (V) new WindowsUserDefinedFileAttributeView(file, followLinks);
return (V) null;
}
@Override
@SuppressWarnings("unchecked")
public <A extends BasicFileAttributes> A readAttributes(Path file,
Class<A> type,
LinkOption... options)
throws IOException
{
Class<? extends BasicFileAttributeView> view;
if (type == BasicFileAttributes.class)
view = BasicFileAttributeView.class;
else if (type == DosFileAttributes.class)
view = DosFileAttributeView.class;
else if (type == null)
throw new NullPointerException();
else
throw new UnsupportedOperationException();
return (A) getFileAttributeView(file, view, options).readAttributes();
}
@Override
public DynamicFileAttributeView getFileAttributeView(Path obj, String name, LinkOption... options) {
WindowsPath file = WindowsPath.toWindowsPath(obj);
boolean followLinks = Util.followLinks(options);
if (name.equals("basic"))
return WindowsFileAttributeViews.createBasicView(file, followLinks);
if (name.equals("dos"))
return WindowsFileAttributeViews.createDosView(file, followLinks);
if (name.equals("acl"))
return new WindowsAclFileAttributeView(file, followLinks);
if (name.equals("owner"))
return new FileOwnerAttributeViewImpl(
new WindowsAclFileAttributeView(file, followLinks));
if (name.equals("user"))
return new WindowsUserDefinedFileAttributeView(file, followLinks);
return null;
}
@Override
public SeekableByteChannel newByteChannel(Path obj,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException
{
WindowsPath file = WindowsPath.toWindowsPath(obj);
WindowsSecurityDescriptor sd =
WindowsSecurityDescriptor.fromAttribute(attrs);
try {
return WindowsChannelFactory
.newFileChannel(file.getPathForWin32Calls(),
file.getPathForPermissionCheck(),
options,
sd.address());
} catch (WindowsException x) {
x.rethrowAsIOException(file);
return null; // keep compiler happy
} finally {
sd.release();
}
}
@Override
boolean implDelete(Path obj, boolean failIfNotExists) throws IOException {
WindowsPath file = WindowsPath.toWindowsPath(obj);
file.checkDelete();
WindowsFileAttributes attrs = null;
try {
// need to know if file is a directory or junction
attrs = WindowsFileAttributes.get(file, false);
if (attrs.isDirectory() || attrs.isDirectoryLink()) {
RemoveDirectory(file.getPathForWin32Calls());
} else {
DeleteFile(file.getPathForWin32Calls());
}
return true;
} catch (WindowsException x) {
// no-op if file does not exist
if (!failIfNotExists &&
(x.lastError() == ERROR_FILE_NOT_FOUND ||
x.lastError() == ERROR_PATH_NOT_FOUND)) return false;
if (attrs != null && attrs.isDirectory()) {
// ERROR_ALREADY_EXISTS is returned when attempting to delete
// non-empty directory on SAMBA servers.
if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
x.lastError() == ERROR_ALREADY_EXISTS)
{
throw new DirectoryNotEmptyException(
file.getPathForExceptionMessage());
}
}
x.rethrowAsIOException(file);
return false;
}
}
@Override
public void copy(Path source, Path target, CopyOption... options)
throws IOException
{
WindowsFileCopy.copy(WindowsPath.toWindowsPath(source),
WindowsPath.toWindowsPath(target),
options);
}
@Override
public void move(Path source, Path target, CopyOption... options)
throws IOException
{
WindowsFileCopy.move(WindowsPath.toWindowsPath(source),
WindowsPath.toWindowsPath(target),
options);
}
/**
* Checks the file security against desired access.
*/
private static boolean hasDesiredAccess(WindowsPath file, int rights) throws IOException {
// read security descriptor containing ACL (symlinks are followed)
boolean hasRights = false;
String target = WindowsLinkSupport.getFinalPath(file, true);
NativeBuffer aclBuffer = WindowsAclFileAttributeView
.getFileSecurity(target,
DACL_SECURITY_INFORMATION
| OWNER_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION);
try {
hasRights = checkAccessMask(aclBuffer.address(), rights,
FILE_GENERIC_READ,
FILE_GENERIC_WRITE,
FILE_GENERIC_EXECUTE,
FILE_ALL_ACCESS);
} catch (WindowsException exc) {
exc.rethrowAsIOException(file);
} finally {
aclBuffer.release();
}
return hasRights;
}
/**
* Checks if the given file(or directory) exists and is readable.
*/
private void checkReadAccess(WindowsPath file) throws IOException {
try {
Set<OpenOption> opts = Collections.emptySet();
FileChannel fc = WindowsChannelFactory
.newFileChannel(file.getPathForWin32Calls(),
file.getPathForPermissionCheck(),
opts,
0L);
fc.close();
} catch (WindowsException exc) {
// Windows errors are very inconsistent when the file is a directory
// (ERROR_PATH_NOT_FOUND returned for root directories for example)
// so we retry by attempting to open it as a directory.
try {
new WindowsDirectoryStream(file, null).close();
} catch (IOException ioe) {
// translate and throw original exception
exc.rethrowAsIOException(file);
}
}
}
@Override
public void checkAccess(Path obj, AccessMode... modes) throws IOException {
WindowsPath file = WindowsPath.toWindowsPath(obj);
boolean r = false;
boolean w = false;
boolean x = false;
for (AccessMode mode: modes) {
switch (mode) {
case READ : r = true; break;
case WRITE : w = true; break;
case EXECUTE : x = true; break;
default: throw new AssertionError("Should not get here");
}
}
// special-case read access to avoid needing to determine effective
// access to file; default if modes not specified
if (!w && !x) {
checkReadAccess(file);
return;
}
int mask = 0;
if (r) {
file.checkRead();
mask |= FILE_READ_DATA;
}
if (w) {
file.checkWrite();
mask |= FILE_WRITE_DATA;
}
if (x) {
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkExec(file.getPathForPermissionCheck());
mask |= FILE_EXECUTE;
}
if (!hasDesiredAccess(file, mask))
throw new AccessDeniedException(
file.getPathForExceptionMessage(), null,
"Permissions does not allow requested access");
// for write access we need to check if the DOS readonly attribute
// and if the volume is read-only
if (w) {
try {
WindowsFileAttributes attrs = WindowsFileAttributes.get(file, true);
if (!attrs.isDirectory() && attrs.isReadOnly())
throw new AccessDeniedException(
file.getPathForExceptionMessage(), null,
"DOS readonly attribute is set");
} catch (WindowsException exc) {
exc.rethrowAsIOException(file);
}
if (WindowsFileStore.create(file).isReadOnly()) {
throw new AccessDeniedException(
file.getPathForExceptionMessage(), null, "Read-only file system");
}
}
}
@Override
public boolean isSameFile(Path obj1, Path obj2) throws IOException {
WindowsPath file1 = WindowsPath.toWindowsPath(obj1);
if (file1.equals(obj2))
return true;
if (obj2 == null)
throw new NullPointerException();
if (!(obj2 instanceof WindowsPath))
return false;
WindowsPath file2 = (WindowsPath)obj2;
// check security manager access to both files
file1.checkRead();
file2.checkRead();
// open both files and see if they are the same
long h1 = 0L;
try {
h1 = file1.openForReadAttributeAccess(true);
} catch (WindowsException x) {
x.rethrowAsIOException(file1);
}
try {
WindowsFileAttributes attrs1 = null;
try {
attrs1 = WindowsFileAttributes.readAttributes(h1);
} catch (WindowsException x) {
x.rethrowAsIOException(file1);
}
long h2 = 0L;
try {
h2 = file2.openForReadAttributeAccess(true);
} catch (WindowsException x) {
x.rethrowAsIOException(file2);
}
try {
WindowsFileAttributes attrs2 = null;
try {
attrs2 = WindowsFileAttributes.readAttributes(h2);
} catch (WindowsException x) {
x.rethrowAsIOException(file2);
}
return WindowsFileAttributes.isSameFile(attrs1, attrs2);
} finally {
CloseHandle(h2);
}
} finally {
CloseHandle(h1);
}
}
@Override
public boolean isHidden(Path obj) throws IOException {
WindowsPath file = WindowsPath.toWindowsPath(obj);
file.checkRead();
WindowsFileAttributes attrs = null;
try {
attrs = WindowsFileAttributes.get(file, true);
} catch (WindowsException x) {
x.rethrowAsIOException(file);
}
// DOS hidden attribute not meaningful when set on directories
if (attrs.isDirectory())
return false;
return attrs.isHidden();
}
@Override
public FileStore getFileStore(Path obj) throws IOException {
WindowsPath file = WindowsPath.toWindowsPath(obj);
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
file.checkRead();
}
return WindowsFileStore.create(file);
}
@Override
public void createDirectory(Path obj, FileAttribute<?>... attrs)
throws IOException
{
WindowsPath dir = WindowsPath.toWindowsPath(obj);
dir.checkWrite();
WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.fromAttribute(attrs);
try {
CreateDirectory(dir.getPathForWin32Calls(), sd.address());
} catch (WindowsException x) {
// convert ERROR_ACCESS_DENIED to FileAlreadyExistsException if we can
// verify that the directory exists
if (x.lastError() == ERROR_ACCESS_DENIED) {
try {
if (WindowsFileAttributes.get(dir, false).isDirectory())
throw new FileAlreadyExistsException(dir.toString());
} catch (WindowsException ignore) { }
}
x.rethrowAsIOException(dir);
} finally {
sd.release();
}
}
@Override
public DirectoryStream<Path> newDirectoryStream(Path obj, DirectoryStream.Filter<? super Path> filter)
throws IOException
{
WindowsPath dir = WindowsPath.toWindowsPath(obj);
dir.checkRead();
if (filter == null)
throw new NullPointerException();
return new WindowsDirectoryStream(dir, filter);
}
@Override
public void createSymbolicLink(Path obj1, Path obj2, FileAttribute<?>... attrs)
throws IOException
{
WindowsPath link = WindowsPath.toWindowsPath(obj1);
WindowsPath target = WindowsPath.toWindowsPath(obj2);
// no attributes allowed
if (attrs.length > 0) {
WindowsSecurityDescriptor.fromAttribute(attrs); // may throw NPE or UOE
throw new UnsupportedOperationException("Initial file attributes" +
"not supported when creating symbolic link");
}
// permission check
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new LinkPermission("symbolic"));
link.checkWrite();
}
/**
* Throw I/O exception for the drive-relative case because Windows
* creates a link with the resolved target for this case.
*/
if (target.type() == WindowsPathType.DRIVE_RELATIVE) {
throw new IOException("Cannot create symbolic link to working directory relative target");
}
/*
* Windows treats symbolic links to directories differently than it
* does to other file types. For that reason we need to check if the
* target is a directory (or a directory junction).
*/
WindowsPath resolvedTarget;
if (target.type() == WindowsPathType.RELATIVE) {
WindowsPath parent = link.getParent();
resolvedTarget = (parent == null) ? target : parent.resolve(target);
} else {
resolvedTarget = link.resolve(target);
}
int flags = 0;
try {
WindowsFileAttributes wattrs = WindowsFileAttributes.get(resolvedTarget, false);
if (wattrs.isDirectory() || wattrs.isDirectoryLink())
flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
} catch (WindowsException x) {
// unable to access target so assume target is not a directory
}
// create the link
try {
CreateSymbolicLink(link.getPathForWin32Calls(),
WindowsPath.addPrefixIfNeeded(target.toString()),
flags);
} catch (WindowsException x) {
if (x.lastError() == ERROR_INVALID_REPARSE_DATA) {
x.rethrowAsIOException(link, target);
} else {
x.rethrowAsIOException(link);
}
}
}
@Override
public void createLink(Path obj1, Path obj2) throws IOException {
WindowsPath link = WindowsPath.toWindowsPath(obj1);
WindowsPath existing = WindowsPath.toWindowsPath(obj2);
// permission check
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new LinkPermission("hard"));
link.checkWrite();
existing.checkWrite();
}
// create hard link
try {
CreateHardLink(link.getPathForWin32Calls(),
existing.getPathForWin32Calls());
} catch (WindowsException x) {
x.rethrowAsIOException(link, existing);
}
}
@Override
public Path readSymbolicLink(Path obj1) throws IOException {
WindowsPath link = WindowsPath.toWindowsPath(obj1);
WindowsFileSystem fs = link.getFileSystem();
// permission check
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
FilePermission perm = new FilePermission(link.getPathForPermissionCheck(),
SecurityConstants.FILE_READLINK_ACTION);
sm.checkPermission(perm);
}
String target = WindowsLinkSupport.readLink(link);
return WindowsPath.createFromNormalizedPath(fs, target);
}
}

View file

@ -0,0 +1,431 @@
/*
* 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.io.IOException;
import java.io.IOError;
import java.security.AccessController;
import java.security.PrivilegedAction;
import jdk.internal.misc.Unsafe;
import static sun.nio.fs.WindowsNativeDispatcher.*;
import static sun.nio.fs.WindowsConstants.*;
/**
* Utility methods for symbolic link support on Windows Vista and newer.
*/
class WindowsLinkSupport {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private WindowsLinkSupport() {
}
/**
* Returns the target of a symbolic link
*/
static String readLink(WindowsPath path) throws IOException {
long handle = 0L;
try {
handle = path.openForReadAttributeAccess(false); // don't follow links
} catch (WindowsException x) {
x.rethrowAsIOException(path);
}
try {
return readLinkImpl(handle);
} finally {
CloseHandle(handle);
}
}
/**
* Returns the final path (all symbolic links resolved) or null if this
* operation is not supported.
*/
static String getFinalPath(WindowsPath input) throws IOException {
long h = 0;
try {
h = input.openForReadAttributeAccess(true);
} catch (WindowsException x) {
x.rethrowAsIOException(input);
}
try {
return stripPrefix(GetFinalPathNameByHandle(h));
} catch (WindowsException x) {
// ERROR_INVALID_LEVEL is the error returned when not supported
// (a sym link to file on FAT32 or Samba server for example)
if (x.lastError() != ERROR_INVALID_LEVEL)
x.rethrowAsIOException(input);
} finally {
CloseHandle(h);
}
return null;
}
/**
* Returns the final path of a given path as a String. This should be used
* prior to calling Win32 system calls that do not follow links.
*/
static String getFinalPath(WindowsPath input, boolean followLinks)
throws IOException
{
WindowsFileSystem fs = input.getFileSystem();
try {
// if not following links then don't need final path
if (!followLinks)
return input.getPathForWin32Calls();
// if file is not a sym link then don't need final path
if (!WindowsFileAttributes.get(input, false).isSymbolicLink()) {
return input.getPathForWin32Calls();
}
} catch (WindowsException x) {
x.rethrowAsIOException(input);
}
// The file is a symbolic link so attempt to get the final path
String result = getFinalPath(input);
if (result != null)
return result;
// Fallback: read target of link, resolve against parent, and repeat
// until file is not a link.
WindowsPath target = input;
int linkCount = 0;
do {
try {
WindowsFileAttributes attrs =
WindowsFileAttributes.get(target, false);
// non a link so we are done
if (!attrs.isSymbolicLink()) {
return target.getPathForWin32Calls();
}
} catch (WindowsException x) {
x.rethrowAsIOException(target);
}
WindowsPath link = WindowsPath
.createFromNormalizedPath(fs, readLink(target));
WindowsPath parent = target.getParent();
if (parent == null) {
// no parent so use parent of absolute path
final WindowsPath t = target;
target = AccessController
.doPrivileged(new PrivilegedAction<WindowsPath>() {
@Override
public WindowsPath run() {
return t.toAbsolutePath();
}});
parent = target.getParent();
}
target = parent.resolve(link);
} while (++linkCount < 32);
throw new FileSystemException(input.getPathForExceptionMessage(), null,
"Too many links");
}
/**
* Returns the actual path of a file, optionally resolving all symbolic
* links.
*/
static String getRealPath(WindowsPath input, boolean resolveLinks)
throws IOException
{
WindowsFileSystem fs = input.getFileSystem();
// Start with absolute path
String path = null;
try {
path = input.toAbsolutePath().toString();
} catch (IOError x) {
throw (IOException)(x.getCause());
}
// Collapse "." and ".."
if (path.indexOf('.') >= 0) {
try {
path = GetFullPathName(path);
} catch (WindowsException x) {
x.rethrowAsIOException(input);
}
}
// string builder to build up components of path
StringBuilder sb = new StringBuilder(path.length());
// Copy root component
int start;
char c0 = path.charAt(0);
char c1 = path.charAt(1);
if ((c0 <= 'z' && c0 >= 'a' || c0 <= 'Z' && c0 >= 'A') &&
c1 == ':' && path.charAt(2) == '\\') {
// Driver specifier
sb.append(Character.toUpperCase(c0));
sb.append(":\\");
start = 3;
} else if (c0 == '\\' && c1 == '\\') {
// UNC pathname, begins with "\\\\host\\share"
int last = path.length() - 1;
int pos = path.indexOf('\\', 2);
// skip both server and share names
if (pos == -1 || (pos == last)) {
// The UNC does not have a share name (collapsed by GetFullPathName)
throw new FileSystemException(input.getPathForExceptionMessage(),
null, "UNC has invalid share");
}
pos = path.indexOf('\\', pos+1);
if (pos < 0) {
pos = last;
sb.append(path).append("\\");
} else {
sb.append(path, 0, pos+1);
}
start = pos + 1;
} else {
throw new AssertionError("path type not recognized");
}
// if the result is only a root component then we simply check it exists
if (start >= path.length()) {
String result = sb.toString();
try {
GetFileAttributes(result);
} catch (WindowsException x) {
x.rethrowAsIOException(path);
}
return result;
}
// iterate through each component to get its actual name in the
// directory
int curr = start;
while (curr < path.length()) {
int next = path.indexOf('\\', curr);
int end = (next == -1) ? path.length() : next;
String search = sb.toString() + path.substring(curr, end);
try {
FirstFile fileData = FindFirstFile(WindowsPath.addPrefixIfNeeded(search));
FindClose(fileData.handle());
// if a reparse point is encountered then we must return the
// final path.
if (resolveLinks &&
WindowsFileAttributes.isReparsePoint(fileData.attributes()))
{
String result = getFinalPath(input);
if (result == null) {
// Fallback to slow path, usually because there is a sym
// link to a file system that doesn't support sym links.
WindowsPath resolved = resolveAllLinks(
WindowsPath.createFromNormalizedPath(fs, path));
result = getRealPath(resolved, false);
}
return result;
}
// add the name to the result
sb.append(fileData.name());
if (next != -1) {
sb.append('\\');
}
} catch (WindowsException e) {
e.rethrowAsIOException(path);
}
curr = end + 1;
}
return sb.toString();
}
/**
* Returns target of a symbolic link given the handle of an open file
* (that should be a link).
*/
private static String readLinkImpl(long handle) throws IOException {
int size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
try {
try {
DeviceIoControlGetReparsePoint(handle, buffer.address(), size);
} catch (WindowsException x) {
// FIXME: exception doesn't have file name
if (x.lastError() == ERROR_NOT_A_REPARSE_POINT)
throw new NotLinkException(null, null, x.errorString());
x.rethrowAsIOException((String)null);
}
/*
* typedef struct _REPARSE_DATA_BUFFER {
* ULONG ReparseTag;
* USHORT ReparseDataLength;
* USHORT Reserved;
* union {
* struct {
* USHORT SubstituteNameOffset;
* USHORT SubstituteNameLength;
* USHORT PrintNameOffset;
* USHORT PrintNameLength;
* WCHAR PathBuffer[1];
* } SymbolicLinkReparseBuffer;
* struct {
* USHORT SubstituteNameOffset;
* USHORT SubstituteNameLength;
* USHORT PrintNameOffset;
* USHORT PrintNameLength;
* WCHAR PathBuffer[1];
* } MountPointReparseBuffer;
* struct {
* UCHAR DataBuffer[1];
* } GenericReparseBuffer;
* };
* } REPARSE_DATA_BUFFER
*/
final short OFFSETOF_REPARSETAG = 0;
final short OFFSETOF_PATHOFFSET = 8;
final short OFFSETOF_PATHLENGTH = 10;
final short OFFSETOF_PATHBUFFER = 16 + 4; // check this
int tag = (int)unsafe.getLong(buffer.address() + OFFSETOF_REPARSETAG);
if (tag != IO_REPARSE_TAG_SYMLINK) {
// FIXME: exception doesn't have file name
throw new NotLinkException(null, null, "Reparse point is not a symbolic link");
}
// get offset and length of target
short nameOffset = unsafe.getShort(buffer.address() + OFFSETOF_PATHOFFSET);
short nameLengthInBytes = unsafe.getShort(buffer.address() + OFFSETOF_PATHLENGTH);
if ((nameLengthInBytes % 2) != 0)
throw new FileSystemException(null, null, "Symbolic link corrupted");
// copy into char array
char[] name = new char[nameLengthInBytes/2];
unsafe.copyMemory(null, buffer.address() + OFFSETOF_PATHBUFFER + nameOffset,
name, Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes);
// remove special prefix
String target = stripPrefix(new String(name));
if (target.length() == 0) {
throw new IOException("Symbolic link target is invalid");
}
return target;
} finally {
buffer.release();
}
}
/**
* Resolve all symbolic-links in a given absolute and normalized path
*/
private static WindowsPath resolveAllLinks(WindowsPath path)
throws IOException
{
assert path.isAbsolute();
WindowsFileSystem fs = path.getFileSystem();
// iterate through each name element of the path, resolving links as
// we go.
int linkCount = 0;
int elem = 0;
while (elem < path.getNameCount()) {
WindowsPath current = path.getRoot().resolve(path.subpath(0, elem+1));
WindowsFileAttributes attrs = null;
try {
attrs = WindowsFileAttributes.get(current, false);
} catch (WindowsException x) {
x.rethrowAsIOException(current);
}
/**
* If a symbolic link then we resolve it against the parent
* of the current name element. We then resolve any remaining
* part of the path against the result. The target of the link
* may have "." and ".." components so re-normalize and restart
* the process from the first element.
*/
if (attrs.isSymbolicLink()) {
linkCount++;
if (linkCount > 32)
throw new IOException("Too many links");
WindowsPath target = WindowsPath
.createFromNormalizedPath(fs, readLink(current));
WindowsPath remainder = null;
int count = path.getNameCount();
if ((elem+1) < count) {
remainder = path.subpath(elem+1, count);
}
path = current.getParent().resolve(target);
try {
String full = GetFullPathName(path.toString());
if (!full.equals(path.toString())) {
path = WindowsPath.createFromNormalizedPath(fs, full);
}
} catch (WindowsException x) {
x.rethrowAsIOException(path);
}
if (remainder != null) {
path = path.resolve(remainder);
}
// reset
elem = 0;
} else {
// not a link
elem++;
}
}
return path;
}
/**
* Strip long path or symbolic link prefix from path
*/
private static String stripPrefix(String path) {
// prefix for resolved/long path
if (path.startsWith("\\\\?\\")) {
if (path.startsWith("\\\\?\\UNC\\")) {
path = "\\" + path.substring(7);
} else {
path = path.substring(4);
}
return path;
}
// prefix for target of symbolic link
if (path.startsWith("\\??\\")) {
if (path.startsWith("\\??\\UNC\\")) {
path = "\\" + path.substring(7);
} else {
path = path.substring(4);
}
return path;
}
return path;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,932 @@
/*
* 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.nio.file.*;
import java.nio.file.attribute.*;
import java.io.*;
import java.net.URI;
import java.util.*;
import java.lang.ref.WeakReference;
import static sun.nio.fs.WindowsNativeDispatcher.*;
import static sun.nio.fs.WindowsConstants.*;
/**
* Windows implementation of Path
*/
class WindowsPath implements Path {
// The maximum path that does not require long path prefix. On Windows
// the maximum path is 260 minus 1 (NUL) but for directories it is 260
// minus 12 minus 1 (to allow for the creation of a 8.3 file in the
// directory).
private static final int MAX_PATH = 247;
// Maximum extended-length path
private static final int MAX_LONG_PATH = 32000;
// FIXME - eliminate this reference to reduce space
private final WindowsFileSystem fs;
// path type
private final WindowsPathType type;
// root component (may be empty)
private final String root;
// normalized path
private final String path;
// the path to use in Win32 calls. This differs from path for relative
// paths and has a long path prefix for all paths longer than MAX_PATH.
private volatile WeakReference<String> pathForWin32Calls;
// offsets into name components (computed lazily)
private volatile Integer[] offsets;
// computed hash code (computed lazily, no need to be volatile)
private int hash;
/**
* Initializes a new instance of this class.
*/
private WindowsPath(WindowsFileSystem fs,
WindowsPathType type,
String root,
String path)
{
this.fs = fs;
this.type = type;
this.root = root;
this.path = path;
}
/**
* Creates a Path by parsing the given path.
*/
static WindowsPath parse(WindowsFileSystem fs, String path) {
WindowsPathParser.Result result = WindowsPathParser.parse(path);
return new WindowsPath(fs, result.type(), result.root(), result.path());
}
/**
* Creates a Path from a given path that is known to be normalized.
*/
static WindowsPath createFromNormalizedPath(WindowsFileSystem fs,
String path,
BasicFileAttributes attrs)
{
try {
WindowsPathParser.Result result =
WindowsPathParser.parseNormalizedPath(path);
if (attrs == null) {
return new WindowsPath(fs,
result.type(),
result.root(),
result.path());
} else {
return new WindowsPathWithAttributes(fs,
result.type(),
result.root(),
result.path(),
attrs);
}
} catch (InvalidPathException x) {
throw new AssertionError(x.getMessage());
}
}
/**
* Creates a WindowsPath from a given path that is known to be normalized.
*/
static WindowsPath createFromNormalizedPath(WindowsFileSystem fs,
String path)
{
return createFromNormalizedPath(fs, path, null);
}
/**
* Special implementation with attached/cached attributes (used to quicken
* file tree traversal)
*/
private static class WindowsPathWithAttributes
extends WindowsPath implements BasicFileAttributesHolder
{
final WeakReference<BasicFileAttributes> ref;
WindowsPathWithAttributes(WindowsFileSystem fs,
WindowsPathType type,
String root,
String path,
BasicFileAttributes attrs)
{
super(fs, type, root, path);
ref = new WeakReference<BasicFileAttributes>(attrs);
}
@Override
public BasicFileAttributes get() {
return ref.get();
}
@Override
public void invalidate() {
ref.clear();
}
// no need to override equals/hashCode.
}
// use this message when throwing exceptions
String getPathForExceptionMessage() {
return path;
}
// use this path for permission checks
String getPathForPermissionCheck() {
return path;
}
// use this path for Win32 calls
// This method will prefix long paths with \\?\ or \\?\UNC as required.
String getPathForWin32Calls() throws WindowsException {
// short absolute paths can be used directly
if (isAbsolute() && path.length() <= MAX_PATH)
return path;
// return cached values if available
WeakReference<String> ref = pathForWin32Calls;
String resolved = (ref != null) ? ref.get() : null;
if (resolved != null) {
// Win32 path already available
return resolved;
}
// resolve against default directory
resolved = getAbsolutePath();
// Long paths need to have "." and ".." removed and be prefixed with
// "\\?\". Note that it is okay to remove ".." even when it follows
// a link - for example, it is okay for foo/link/../bar to be changed
// to foo/bar. The reason is that Win32 APIs to access foo/link/../bar
// will access foo/bar anyway (which differs to Unix systems)
if (resolved.length() > MAX_PATH) {
if (resolved.length() > MAX_LONG_PATH) {
throw new WindowsException("Cannot access file with path exceeding "
+ MAX_LONG_PATH + " characters");
}
resolved = addPrefixIfNeeded(GetFullPathName(resolved));
}
// cache the resolved path (except drive relative paths as the working
// directory on removal media devices can change during the lifetime
// of the VM)
if (type != WindowsPathType.DRIVE_RELATIVE) {
synchronized (path) {
pathForWin32Calls = new WeakReference<String>(resolved);
}
}
return resolved;
}
// return this path resolved against the file system's default directory
private String getAbsolutePath() throws WindowsException {
if (isAbsolute())
return path;
// Relative path ("foo" for example)
if (type == WindowsPathType.RELATIVE) {
String defaultDirectory = getFileSystem().defaultDirectory();
if (isEmpty())
return defaultDirectory;
if (defaultDirectory.endsWith("\\")) {
return defaultDirectory + path;
} else {
StringBuilder sb =
new StringBuilder(defaultDirectory.length() + path.length() + 1);
return sb.append(defaultDirectory).append('\\').append(path).toString();
}
}
// Directory relative path ("\foo" for example)
if (type == WindowsPathType.DIRECTORY_RELATIVE) {
String defaultRoot = getFileSystem().defaultRoot();
return defaultRoot + path.substring(1);
}
// Drive relative path ("C:foo" for example).
if (isSameDrive(root, getFileSystem().defaultRoot())) {
// relative to default directory
String remaining = path.substring(root.length());
String defaultDirectory = getFileSystem().defaultDirectory();
if (remaining.length() == 0) {
return defaultDirectory;
} else if (defaultDirectory.endsWith("\\")) {
return defaultDirectory + remaining;
} else {
return defaultDirectory + "\\" + remaining;
}
} else {
// relative to some other drive
String wd;
try {
int dt = GetDriveType(root + "\\");
if (dt == DRIVE_UNKNOWN || dt == DRIVE_NO_ROOT_DIR)
throw new WindowsException("");
wd = GetFullPathName(root + ".");
} catch (WindowsException x) {
throw new WindowsException("Unable to get working directory of drive '" +
Character.toUpperCase(root.charAt(0)) + "'");
}
String result = wd;
if (wd.endsWith("\\")) {
result += path.substring(root.length());
} else {
if (path.length() > root.length())
result += "\\" + path.substring(root.length());
}
return result;
}
}
// returns true if same drive letter
private static boolean isSameDrive(String root1, String root2) {
return Character.toUpperCase(root1.charAt(0)) ==
Character.toUpperCase(root2.charAt(0));
}
// Add long path prefix to path if required
static String addPrefixIfNeeded(String path) {
if (path.length() > MAX_PATH) {
if (path.startsWith("\\\\")) {
path = "\\\\?\\UNC" + path.substring(1, path.length());
} else {
path = "\\\\?\\" + path;
}
}
return path;
}
@Override
public WindowsFileSystem getFileSystem() {
return fs;
}
// -- Path operations --
private boolean isEmpty() {
return path.length() == 0;
}
private WindowsPath emptyPath() {
return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", "");
}
@Override
public Path getFileName() {
int len = path.length();
// represents empty path
if (len == 0)
return this;
// represents root component only
if (root.length() == len)
return null;
int off = path.lastIndexOf('\\');
if (off < root.length())
off = root.length();
else
off++;
return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", path.substring(off));
}
@Override
public WindowsPath getParent() {
// represents root component only
if (root.length() == path.length())
return null;
int off = path.lastIndexOf('\\');
if (off < root.length())
return getRoot();
else
return new WindowsPath(getFileSystem(),
type,
root,
path.substring(0, off));
}
@Override
public WindowsPath getRoot() {
if (root.length() == 0)
return null;
return new WindowsPath(getFileSystem(), type, root, root);
}
// package-private
WindowsPathType type() {
return type;
}
// package-private
boolean isUnc() {
return type == WindowsPathType.UNC;
}
boolean needsSlashWhenResolving() {
if (path.endsWith("\\"))
return false;
return path.length() > root.length();
}
@Override
public boolean isAbsolute() {
return type == WindowsPathType.ABSOLUTE || type == WindowsPathType.UNC;
}
static WindowsPath toWindowsPath(Path path) {
if (path == null)
throw new NullPointerException();
if (!(path instanceof WindowsPath)) {
throw new ProviderMismatchException();
}
return (WindowsPath)path;
}
// return true if this path has "." or ".."
private boolean hasDotOrDotDot() {
int n = getNameCount();
for (int i=0; i<n; i++) {
String name = elementAsString(i);
if (name.length() == 1 && name.charAt(0) == '.')
return true;
if (name.length() == 2
&& name.charAt(0) == '.' && name.charAt(1) == '.')
return true;
}
return false;
}
@Override
public WindowsPath relativize(Path obj) {
WindowsPath child = toWindowsPath(obj);
if (this.equals(child))
return emptyPath();
// can only relativize paths of the same type
if (this.type != child.type)
throw new IllegalArgumentException("'other' is different type of Path");
// can only relativize paths if root component matches
if (!this.root.equalsIgnoreCase(child.root))
throw new IllegalArgumentException("'other' has different root");
// this path is the empty path
if (this.isEmpty())
return child;
WindowsPath base = this;
if (base.hasDotOrDotDot() || child.hasDotOrDotDot()) {
base = base.normalize();
child = child.normalize();
}
int baseCount = base.getNameCount();
int childCount = child.getNameCount();
// skip matching names
int n = Math.min(baseCount, childCount);
int i = 0;
while (i < n) {
if (!base.getName(i).equals(child.getName(i)))
break;
i++;
}
// remaining elements in child
WindowsPath childRemaining;
boolean isChildEmpty;
if (i == childCount) {
childRemaining = emptyPath();
isChildEmpty = true;
} else {
childRemaining = child.subpath(i, childCount);
isChildEmpty = childRemaining.isEmpty();
}
// matched all of base
if (i == baseCount) {
return childRemaining;
}
// the remainder of base cannot contain ".."
WindowsPath baseRemaining = base.subpath(i, baseCount);
if (baseRemaining.hasDotOrDotDot()) {
throw new IllegalArgumentException("Unable to compute relative "
+ " path from " + this + " to " + obj);
}
if (baseRemaining.isEmpty())
return childRemaining;
// number of ".." needed
int dotdots = baseRemaining.getNameCount();
if (dotdots == 0) {
return childRemaining;
}
StringBuilder result = new StringBuilder();
for (int j=0; j<dotdots; j++) {
result.append("..\\");
}
// append remaining names in child
if (!isChildEmpty) {
for (int j=0; j<childRemaining.getNameCount(); j++) {
result.append(childRemaining.getName(j).toString());
result.append("\\");
}
}
// drop trailing slash
result.setLength(result.length()-1);
return createFromNormalizedPath(getFileSystem(), result.toString());
}
@Override
public WindowsPath normalize() {
final int count = getNameCount();
if (count == 0 || isEmpty())
return this;
boolean[] ignore = new boolean[count]; // true => ignore name
int remaining = count; // number of names remaining
// multiple passes to eliminate all occurrences of "." and "name/.."
int prevRemaining;
do {
prevRemaining = remaining;
int prevName = -1;
for (int i=0; i<count; i++) {
if (ignore[i])
continue;
String name = elementAsString(i);
// not "." or ".."
if (name.length() > 2) {
prevName = i;
continue;
}
// "." or something else
if (name.length() == 1) {
// ignore "."
if (name.charAt(0) == '.') {
ignore[i] = true;
remaining--;
} else {
prevName = i;
}
continue;
}
// not ".."
if (name.charAt(0) != '.' || name.charAt(1) != '.') {
prevName = i;
continue;
}
// ".." found
if (prevName >= 0) {
// name/<ignored>/.. found so mark name and ".." to be
// ignored
ignore[prevName] = true;
ignore[i] = true;
remaining = remaining - 2;
prevName = -1;
} else {
// Cases:
// C:\<ignored>\..
// \\server\\share\<ignored>\..
// \<ignored>..
if (isAbsolute() || type == WindowsPathType.DIRECTORY_RELATIVE) {
boolean hasPrevious = false;
for (int j=0; j<i; j++) {
if (!ignore[j]) {
hasPrevious = true;
break;
}
}
if (!hasPrevious) {
// all proceeding names are ignored
ignore[i] = true;
remaining--;
}
}
}
}
} while (prevRemaining > remaining);
// no redundant names
if (remaining == count)
return this;
// corner case - all names removed
if (remaining == 0) {
return (root.length() == 0) ? emptyPath() : getRoot();
}
// re-constitute the path from the remaining names.
StringBuilder result = new StringBuilder();
if (root != null)
result.append(root);
for (int i=0; i<count; i++) {
if (!ignore[i]) {
result.append(getName(i));
result.append("\\");
}
}
// drop trailing slash in result
result.setLength(result.length()-1);
return createFromNormalizedPath(getFileSystem(), result.toString());
}
@Override
public WindowsPath resolve(Path obj) {
WindowsPath other = toWindowsPath(obj);
if (other.isEmpty())
return this;
if (other.isAbsolute())
return other;
switch (other.type) {
case RELATIVE: {
String result;
if (path.endsWith("\\") || (root.length() == path.length())) {
result = path + other.path;
} else {
result = path + "\\" + other.path;
}
return new WindowsPath(getFileSystem(), type, root, result);
}
case DIRECTORY_RELATIVE: {
String result;
if (root.endsWith("\\")) {
result = root + other.path.substring(1);
} else {
result = root + other.path;
}
return createFromNormalizedPath(getFileSystem(), result);
}
case DRIVE_RELATIVE: {
if (!root.endsWith("\\"))
return other;
// if different roots then return other
String thisRoot = root.substring(0, root.length()-1);
if (!thisRoot.equalsIgnoreCase(other.root))
return other;
// same roots
String remaining = other.path.substring(other.root.length());
String result;
if (path.endsWith("\\")) {
result = path + remaining;
} else {
result = path + "\\" + remaining;
}
return createFromNormalizedPath(getFileSystem(), result);
}
default:
throw new AssertionError();
}
}
// generate offset array
private void initOffsets() {
if (offsets == null) {
ArrayList<Integer> list = new ArrayList<>();
if (isEmpty()) {
// empty path considered to have one name element
list.add(0);
} else {
int start = root.length();
int off = root.length();
while (off < path.length()) {
if (path.charAt(off) != '\\') {
off++;
} else {
list.add(start);
start = ++off;
}
}
if (start != off)
list.add(start);
}
synchronized (this) {
if (offsets == null)
offsets = list.toArray(new Integer[list.size()]);
}
}
}
@Override
public int getNameCount() {
initOffsets();
return offsets.length;
}
private String elementAsString(int i) {
initOffsets();
if (i == (offsets.length-1))
return path.substring(offsets[i]);
return path.substring(offsets[i], offsets[i+1]-1);
}
@Override
public WindowsPath getName(int index) {
initOffsets();
if (index < 0 || index >= offsets.length)
throw new IllegalArgumentException();
return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", elementAsString(index));
}
@Override
public WindowsPath subpath(int beginIndex, int endIndex) {
initOffsets();
if (beginIndex < 0)
throw new IllegalArgumentException();
if (beginIndex >= offsets.length)
throw new IllegalArgumentException();
if (endIndex > offsets.length)
throw new IllegalArgumentException();
if (beginIndex >= endIndex)
throw new IllegalArgumentException();
StringBuilder sb = new StringBuilder();
Integer[] nelems = new Integer[endIndex - beginIndex];
for (int i = beginIndex; i < endIndex; i++) {
nelems[i-beginIndex] = sb.length();
sb.append(elementAsString(i));
if (i != (endIndex-1))
sb.append("\\");
}
return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", sb.toString());
}
@Override
public boolean startsWith(Path obj) {
if (!(Objects.requireNonNull(obj) instanceof WindowsPath))
return false;
WindowsPath other = (WindowsPath)obj;
// if this path has a root component the given path's root must match
if (!this.root.equalsIgnoreCase(other.root)) {
return false;
}
// empty path starts with itself
if (other.isEmpty())
return this.isEmpty();
// roots match so compare elements
int thisCount = getNameCount();
int otherCount = other.getNameCount();
if (otherCount <= thisCount) {
while (--otherCount >= 0) {
String thisElement = this.elementAsString(otherCount);
String otherElement = other.elementAsString(otherCount);
// FIXME: should compare in uppercase
if (!thisElement.equalsIgnoreCase(otherElement))
return false;
}
return true;
}
return false;
}
@Override
public boolean endsWith(Path obj) {
if (!(Objects.requireNonNull(obj) instanceof WindowsPath))
return false;
WindowsPath other = (WindowsPath)obj;
// other path is longer
if (other.path.length() > this.path.length()) {
return false;
}
// empty path ends in itself
if (other.isEmpty()) {
return this.isEmpty();
}
int thisCount = this.getNameCount();
int otherCount = other.getNameCount();
// given path has more elements that this path
if (otherCount > thisCount) {
return false;
}
// compare roots
if (other.root.length() > 0) {
if (otherCount < thisCount)
return false;
// FIXME: should compare in uppercase
if (!this.root.equalsIgnoreCase(other.root))
return false;
}
// match last 'otherCount' elements
int off = thisCount - otherCount;
while (--otherCount >= 0) {
String thisElement = this.elementAsString(off + otherCount);
String otherElement = other.elementAsString(otherCount);
// FIXME: should compare in uppercase
if (!thisElement.equalsIgnoreCase(otherElement))
return false;
}
return true;
}
@Override
public int compareTo(Path obj) {
if (obj == null)
throw new NullPointerException();
String s1 = path;
String s2 = ((WindowsPath)obj).path;
int n1 = s1.length();
int n2 = s2.length();
int min = Math.min(n1, n2);
for (int i = 0; i < min; i++) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
return c1 - c2;
}
}
}
return n1 - n2;
}
@Override
public boolean equals(Object obj) {
if ((obj != null) && (obj instanceof WindowsPath)) {
return compareTo((Path)obj) == 0;
}
return false;
}
@Override
public int hashCode() {
// OK if two or more threads compute hash
int h = hash;
if (h == 0) {
for (int i = 0; i< path.length(); i++) {
h = 31*h + Character.toUpperCase(path.charAt(i));
}
hash = h;
}
return h;
}
@Override
public String toString() {
return path;
}
// -- file operations --
// package-private
long openForReadAttributeAccess(boolean followLinks)
throws WindowsException
{
int flags = FILE_FLAG_BACKUP_SEMANTICS;
if (!followLinks)
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
return CreateFile(getPathForWin32Calls(),
FILE_READ_ATTRIBUTES,
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
0L,
OPEN_EXISTING,
flags);
}
void checkRead() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkRead(getPathForPermissionCheck());
}
}
void checkWrite() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkWrite(getPathForPermissionCheck());
}
}
void checkDelete() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkDelete(getPathForPermissionCheck());
}
}
@Override
public URI toUri() {
return WindowsUriSupport.toUri(this);
}
@Override
public WindowsPath toAbsolutePath() {
if (isAbsolute())
return this;
// permission check as per spec
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPropertyAccess("user.dir");
}
try {
return createFromNormalizedPath(getFileSystem(), getAbsolutePath());
} catch (WindowsException x) {
throw new IOError(new IOException(x.getMessage()));
}
}
@Override
public WindowsPath toRealPath(LinkOption... options) throws IOException {
checkRead();
String rp = WindowsLinkSupport.getRealPath(this, Util.followLinks(options));
return createFromNormalizedPath(getFileSystem(), rp);
}
@Override
public WatchKey register(WatchService watcher,
WatchEvent.Kind<?>[] events,
WatchEvent.Modifier... modifiers)
throws IOException
{
if (watcher == null)
throw new NullPointerException();
if (!(watcher instanceof WindowsWatchService))
throw new ProviderMismatchException();
// When a security manager is set then we need to make a defensive
// copy of the modifiers and check for the Windows specific FILE_TREE
// modifier. When the modifier is present then check that permission
// has been granted recursively.
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
boolean watchSubtree = false;
final int ml = modifiers.length;
if (ml > 0) {
modifiers = Arrays.copyOf(modifiers, ml);
int i=0;
while (i < ml) {
if (ExtendedOptions.FILE_TREE.matches(modifiers[i++])) {
watchSubtree = true;
break;
}
}
}
String s = getPathForPermissionCheck();
sm.checkRead(s);
if (watchSubtree)
sm.checkRead(s + "\\-");
}
return ((WindowsWatchService)watcher).register(this, events, modifiers);
}
}

View file

@ -0,0 +1,229 @@
/*
* Copyright (c) 2008, 2011, 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.InvalidPathException;
/**
* A parser of Windows path strings
*/
class WindowsPathParser {
private WindowsPathParser() { }
/**
* The result of a parse operation
*/
static class Result {
private final WindowsPathType type;
private final String root;
private final String path;
Result(WindowsPathType type, String root, String path) {
this.type = type;
this.root = root;
this.path = path;
}
/**
* The path type
*/
WindowsPathType type() {
return type;
}
/**
* The root component
*/
String root() {
return root;
}
/**
* The normalized path (includes root)
*/
String path() {
return path;
}
}
/**
* Parses the given input as a Windows path
*/
static Result parse(String input) {
return parse(input, true);
}
/**
* Parses the given input as a Windows path where it is known that the
* path is already normalized.
*/
static Result parseNormalizedPath(String input) {
return parse(input, false);
}
/**
* Parses the given input as a Windows path.
*
* @param requireToNormalize
* Indicates if the path requires to be normalized
*/
private static Result parse(String input, boolean requireToNormalize) {
String root = "";
WindowsPathType type = null;
int len = input.length();
int off = 0;
if (len > 1) {
char c0 = input.charAt(0);
char c1 = input.charAt(1);
char c = 0;
int next = 2;
if (isSlash(c0) && isSlash(c1)) {
// UNC: We keep the first two slash, collapse all the
// following, then take the hostname and share name out,
// meanwhile collapsing all the redundant slashes.
type = WindowsPathType.UNC;
off = nextNonSlash(input, next, len);
next = nextSlash(input, off, len);
if (off == next)
throw new InvalidPathException(input, "UNC path is missing hostname");
String host = input.substring(off, next); //host
off = nextNonSlash(input, next, len);
next = nextSlash(input, off, len);
if (off == next)
throw new InvalidPathException(input, "UNC path is missing sharename");
root = "\\\\" + host + "\\" + input.substring(off, next) + "\\";
off = next;
} else {
if (isLetter(c0) && c1 == ':') {
char c2;
if (len > 2 && isSlash(c2 = input.charAt(2))) {
// avoid concatenation when root is "D:\"
if (c2 == '\\') {
root = input.substring(0, 3);
} else {
root = input.substring(0, 2) + '\\';
}
off = 3;
type = WindowsPathType.ABSOLUTE;
} else {
root = input.substring(0, 2);
off = 2;
type = WindowsPathType.DRIVE_RELATIVE;
}
}
}
}
if (off == 0) {
if (len > 0 && isSlash(input.charAt(0))) {
type = WindowsPathType.DIRECTORY_RELATIVE;
root = "\\";
} else {
type = WindowsPathType.RELATIVE;
}
}
if (requireToNormalize) {
StringBuilder sb = new StringBuilder(input.length());
sb.append(root);
return new Result(type, root, normalize(sb, input, off));
} else {
return new Result(type, root, input);
}
}
/**
* Remove redundant slashes from the rest of the path, forcing all slashes
* into the preferred slash.
*/
private static String normalize(StringBuilder sb, String path, int off) {
int len = path.length();
off = nextNonSlash(path, off, len);
int start = off;
char lastC = 0;
while (off < len) {
char c = path.charAt(off);
if (isSlash(c)) {
if (lastC == ' ')
throw new InvalidPathException(path,
"Trailing char <" + lastC + ">",
off - 1);
sb.append(path, start, off);
off = nextNonSlash(path, off, len);
if (off != len) //no slash at the end of normalized path
sb.append('\\');
start = off;
} else {
if (isInvalidPathChar(c))
throw new InvalidPathException(path,
"Illegal char <" + c + ">",
off);
lastC = c;
off++;
}
}
if (start != off) {
if (lastC == ' ')
throw new InvalidPathException(path,
"Trailing char <" + lastC + ">",
off - 1);
sb.append(path, start, off);
}
return sb.toString();
}
private static final boolean isSlash(char c) {
return (c == '\\') || (c == '/');
}
private static final int nextNonSlash(String path, int off, int end) {
while (off < end && isSlash(path.charAt(off))) { off++; }
return off;
}
private static final int nextSlash(String path, int off, int end) {
char c;
while (off < end && !isSlash(c=path.charAt(off))) {
if (isInvalidPathChar(c))
throw new InvalidPathException(path,
"Illegal character [" + c + "] in path",
off);
off++;
}
return off;
}
private static final boolean isLetter(char c) {
return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
}
// Reserved characters for window path name
private static final String reservedChars = "<>:\"|?*";
private static final boolean isInvalidPathChar(char ch) {
return ch < '\u0020' || reservedChars.indexOf(ch) != -1;
}
}

View file

@ -0,0 +1,38 @@
/*
* 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;
/**
* A type safe enum of Windows path types.
*/
enum WindowsPathType {
ABSOLUTE, // C:\foo
UNC, // \\server\share\foo
RELATIVE, // foo
DIRECTORY_RELATIVE, // \foo
DRIVE_RELATIVE // C:foo
}

View file

@ -0,0 +1,150 @@
/*
* 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 static sun.nio.fs.WindowsNativeDispatcher.*;
import static sun.nio.fs.WindowsConstants.*;
/**
* Security related utility methods.
*/
class WindowsSecurity {
private WindowsSecurity() { }
// opens process token for given access
private static long openProcessToken(int access) {
try {
return OpenProcessToken(GetCurrentProcess(), access);
} catch (WindowsException x) {
return 0L;
}
}
/**
* Returns the access token for this process with TOKEN_DUPLICATE access
*/
static final long processTokenWithDuplicateAccess =
openProcessToken(TOKEN_DUPLICATE);
/**
* Returns the access token for this process with TOKEN_QUERY access
*/
static final long processTokenWithQueryAccess =
openProcessToken(TOKEN_QUERY);
/**
* Returned by enablePrivilege when code may require a given privilege.
* The drop method should be invoked after the operation completes so as
* to revert the privilege.
*/
static interface Privilege {
void drop();
}
/**
* Attempts to enable the given privilege for this method.
*/
static Privilege enablePrivilege(String priv) {
final long pLuid;
try {
pLuid = LookupPrivilegeValue(priv);
} catch (WindowsException x) {
// indicates bug in caller
throw new AssertionError(x);
}
long hToken = 0L;
boolean impersontating = false;
boolean elevated = false;
try {
hToken = OpenThreadToken(GetCurrentThread(),
TOKEN_ADJUST_PRIVILEGES, false);
if (hToken == 0L && processTokenWithDuplicateAccess != 0L) {
hToken = DuplicateTokenEx(processTokenWithDuplicateAccess,
(TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE));
SetThreadToken(0L, hToken);
impersontating = true;
}
if (hToken != 0L) {
AdjustTokenPrivileges(hToken, pLuid, SE_PRIVILEGE_ENABLED);
elevated = true;
}
} catch (WindowsException x) {
// nothing to do, privilege not enabled
}
final long token = hToken;
final boolean stopImpersontating = impersontating;
final boolean needToRevert = elevated;
return new Privilege() {
@Override
public void drop() {
if (token != 0L) {
try {
if (stopImpersontating)
SetThreadToken(0L, 0L);
else if (needToRevert)
AdjustTokenPrivileges(token, pLuid, 0);
} catch (WindowsException x) {
// should not happen
throw new AssertionError(x);
} finally {
CloseHandle(token);
}
}
}
};
}
/**
* Check the access right against the securityInfo in the current thread.
*/
static boolean checkAccessMask(long securityInfo, int accessMask,
int genericRead, int genericWrite, int genericExecute, int genericAll)
throws WindowsException
{
int privileges = TOKEN_QUERY;
long hToken = OpenThreadToken(GetCurrentThread(), privileges, false);
if (hToken == 0L && processTokenWithDuplicateAccess != 0L)
hToken = DuplicateTokenEx(processTokenWithDuplicateAccess,
privileges);
boolean hasRight = false;
if (hToken != 0L) {
try {
hasRight = AccessCheck(hToken, securityInfo, accessMask,
genericRead, genericWrite, genericExecute, genericAll);
} finally {
CloseHandle(hToken);
}
}
return hasRight;
}
}

View file

@ -0,0 +1,392 @@
/*
* Copyright (c) 2008, 2011, 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.ProviderMismatchException;
import java.nio.file.attribute.*;
import java.util.*;
import java.io.IOException;
import jdk.internal.misc.Unsafe;
import static sun.nio.fs.WindowsNativeDispatcher.*;
import static sun.nio.fs.WindowsConstants.*;
/**
* A SecurityDescriptor for use when setting a file's ACL or creating a file
* with an initial ACL.
*/
class WindowsSecurityDescriptor {
private static final Unsafe unsafe = Unsafe.getUnsafe();
/**
* typedef struct _ACL {
* BYTE AclRevision;
* BYTE Sbz1;
* WORD AclSize;
* WORD AceCount;
* WORD Sbz2;
* } ACL;
*
* typedef struct _ACE_HEADER {
* BYTE AceType;
* BYTE AceFlags;
* WORD AceSize;
* } ACE_HEADER;
*
* typedef struct _ACCESS_ALLOWED_ACE {
* ACE_HEADER Header;
* ACCESS_MASK Mask;
* DWORD SidStart;
* } ACCESS_ALLOWED_ACE;
*
* typedef struct _ACCESS_DENIED_ACE {
* ACE_HEADER Header;
* ACCESS_MASK Mask;
* DWORD SidStart;
* } ACCESS_DENIED_ACE;
*
* typedef struct _SECURITY_DESCRIPTOR {
* BYTE Revision;
* BYTE Sbz1;
* SECURITY_DESCRIPTOR_CONTROL Control;
* PSID Owner;
* PSID Group;
* PACL Sacl;
* PACL Dacl;
* } SECURITY_DESCRIPTOR;
*/
private static final short SIZEOF_ACL = 8;
private static final short SIZEOF_ACCESS_ALLOWED_ACE = 12;
private static final short SIZEOF_ACCESS_DENIED_ACE = 12;
private static final short SIZEOF_SECURITY_DESCRIPTOR = 20;
private static final short OFFSETOF_TYPE = 0;
private static final short OFFSETOF_FLAGS = 1;
private static final short OFFSETOF_ACCESS_MASK = 4;
private static final short OFFSETOF_SID = 8;
// null security descriptor
private static final WindowsSecurityDescriptor NULL_DESCRIPTOR =
new WindowsSecurityDescriptor();
// native resources
private final List<Long> sidList;
private final NativeBuffer aclBuffer, sdBuffer;
/**
* Creates the "null" SecurityDescriptor
*/
private WindowsSecurityDescriptor() {
this.sidList = null;
this.aclBuffer = null;
this.sdBuffer = null;
}
/**
* Creates a SecurityDescriptor from the given ACL
*/
private WindowsSecurityDescriptor(List<AclEntry> acl) throws IOException {
boolean initialized = false;
// SECURITY: need to copy list in case size changes during processing
acl = new ArrayList<AclEntry>(acl);
// list of SIDs
sidList = new ArrayList<Long>(acl.size());
try {
// initial size of ACL
int size = SIZEOF_ACL;
// get the SID for each entry
for (AclEntry entry: acl) {
UserPrincipal user = entry.principal();
if (!(user instanceof WindowsUserPrincipals.User))
throw new ProviderMismatchException();
String sidString = ((WindowsUserPrincipals.User)user).sidString();
try {
long pSid = ConvertStringSidToSid(sidString);
sidList.add(pSid);
// increase size to allow for entry
size += GetLengthSid(pSid) +
Math.max(SIZEOF_ACCESS_ALLOWED_ACE, SIZEOF_ACCESS_DENIED_ACE);
} catch (WindowsException x) {
throw new IOException("Failed to get SID for " + user.getName()
+ ": " + x.errorString());
}
}
// allocate memory for the ACL
aclBuffer = NativeBuffers.getNativeBuffer(size);
sdBuffer = NativeBuffers.getNativeBuffer(SIZEOF_SECURITY_DESCRIPTOR);
InitializeAcl(aclBuffer.address(), size);
// Add entry ACE to the ACL
int i = 0;
while (i < acl.size()) {
AclEntry entry = acl.get(i);
long pSid = sidList.get(i);
try {
encode(entry, pSid, aclBuffer.address());
} catch (WindowsException x) {
throw new IOException("Failed to encode ACE: " +
x.errorString());
}
i++;
}
// initialize security descriptor and set DACL
InitializeSecurityDescriptor(sdBuffer.address());
SetSecurityDescriptorDacl(sdBuffer.address(), aclBuffer.address());
initialized = true;
} catch (WindowsException x) {
throw new IOException(x.getMessage());
} finally {
// release resources if not completely initialized
if (!initialized)
release();
}
}
/**
* Releases memory associated with SecurityDescriptor
*/
void release() {
if (sdBuffer != null)
sdBuffer.release();
if (aclBuffer != null)
aclBuffer.release();
if (sidList != null) {
// release memory for SIDs
for (Long sid: sidList) {
LocalFree(sid);
}
}
}
/**
* Returns address of SecurityDescriptor
*/
long address() {
return (sdBuffer == null) ? 0L : sdBuffer.address();
}
// decode Windows ACE to NFSv4 AclEntry
private static AclEntry decode(long aceAddress)
throws IOException
{
// map type
byte aceType = unsafe.getByte(aceAddress + OFFSETOF_TYPE);
if (aceType != ACCESS_ALLOWED_ACE_TYPE && aceType != ACCESS_DENIED_ACE_TYPE)
return null;
AclEntryType type;
if (aceType == ACCESS_ALLOWED_ACE_TYPE) {
type = AclEntryType.ALLOW;
} else {
type = AclEntryType.DENY;
}
// map flags
byte aceFlags = unsafe.getByte(aceAddress + OFFSETOF_FLAGS);
Set<AclEntryFlag> flags = EnumSet.noneOf(AclEntryFlag.class);
if ((aceFlags & OBJECT_INHERIT_ACE) != 0)
flags.add(AclEntryFlag.FILE_INHERIT);
if ((aceFlags & CONTAINER_INHERIT_ACE) != 0)
flags.add(AclEntryFlag.DIRECTORY_INHERIT);
if ((aceFlags & NO_PROPAGATE_INHERIT_ACE) != 0)
flags.add(AclEntryFlag.NO_PROPAGATE_INHERIT);
if ((aceFlags & INHERIT_ONLY_ACE) != 0)
flags.add(AclEntryFlag.INHERIT_ONLY);
// map access mask
int mask = unsafe.getInt(aceAddress + OFFSETOF_ACCESS_MASK);
Set<AclEntryPermission> perms = EnumSet.noneOf(AclEntryPermission.class);
if ((mask & FILE_READ_DATA) > 0)
perms.add(AclEntryPermission.READ_DATA);
if ((mask & FILE_WRITE_DATA) > 0)
perms.add(AclEntryPermission.WRITE_DATA);
if ((mask & FILE_APPEND_DATA ) > 0)
perms.add(AclEntryPermission.APPEND_DATA);
if ((mask & FILE_READ_EA) > 0)
perms.add(AclEntryPermission.READ_NAMED_ATTRS);
if ((mask & FILE_WRITE_EA) > 0)
perms.add(AclEntryPermission.WRITE_NAMED_ATTRS);
if ((mask & FILE_EXECUTE) > 0)
perms.add(AclEntryPermission.EXECUTE);
if ((mask & FILE_DELETE_CHILD ) > 0)
perms.add(AclEntryPermission.DELETE_CHILD);
if ((mask & FILE_READ_ATTRIBUTES) > 0)
perms.add(AclEntryPermission.READ_ATTRIBUTES);
if ((mask & FILE_WRITE_ATTRIBUTES) > 0)
perms.add(AclEntryPermission.WRITE_ATTRIBUTES);
if ((mask & DELETE) > 0)
perms.add(AclEntryPermission.DELETE);
if ((mask & READ_CONTROL) > 0)
perms.add(AclEntryPermission.READ_ACL);
if ((mask & WRITE_DAC) > 0)
perms.add(AclEntryPermission.WRITE_ACL);
if ((mask & WRITE_OWNER) > 0)
perms.add(AclEntryPermission.WRITE_OWNER);
if ((mask & SYNCHRONIZE) > 0)
perms.add(AclEntryPermission.SYNCHRONIZE);
// lookup SID to create UserPrincipal
long sidAddress = aceAddress + OFFSETOF_SID;
UserPrincipal user = WindowsUserPrincipals.fromSid(sidAddress);
return AclEntry.newBuilder()
.setType(type)
.setPrincipal(user)
.setFlags(flags).setPermissions(perms).build();
}
// encode NFSv4 AclEntry as Windows ACE to given ACL
private static void encode(AclEntry ace, long sidAddress, long aclAddress)
throws WindowsException
{
// ignore non-allow/deny entries for now
if (ace.type() != AclEntryType.ALLOW && ace.type() != AclEntryType.DENY)
return;
boolean allow = (ace.type() == AclEntryType.ALLOW);
// map access mask
Set<AclEntryPermission> aceMask = ace.permissions();
int mask = 0;
if (aceMask.contains(AclEntryPermission.READ_DATA))
mask |= FILE_READ_DATA;
if (aceMask.contains(AclEntryPermission.WRITE_DATA))
mask |= FILE_WRITE_DATA;
if (aceMask.contains(AclEntryPermission.APPEND_DATA))
mask |= FILE_APPEND_DATA;
if (aceMask.contains(AclEntryPermission.READ_NAMED_ATTRS))
mask |= FILE_READ_EA;
if (aceMask.contains(AclEntryPermission.WRITE_NAMED_ATTRS))
mask |= FILE_WRITE_EA;
if (aceMask.contains(AclEntryPermission.EXECUTE))
mask |= FILE_EXECUTE;
if (aceMask.contains(AclEntryPermission.DELETE_CHILD))
mask |= FILE_DELETE_CHILD;
if (aceMask.contains(AclEntryPermission.READ_ATTRIBUTES))
mask |= FILE_READ_ATTRIBUTES;
if (aceMask.contains(AclEntryPermission.WRITE_ATTRIBUTES))
mask |= FILE_WRITE_ATTRIBUTES;
if (aceMask.contains(AclEntryPermission.DELETE))
mask |= DELETE;
if (aceMask.contains(AclEntryPermission.READ_ACL))
mask |= READ_CONTROL;
if (aceMask.contains(AclEntryPermission.WRITE_ACL))
mask |= WRITE_DAC;
if (aceMask.contains(AclEntryPermission.WRITE_OWNER))
mask |= WRITE_OWNER;
if (aceMask.contains(AclEntryPermission.SYNCHRONIZE))
mask |= SYNCHRONIZE;
// map flags
Set<AclEntryFlag> aceFlags = ace.flags();
byte flags = 0;
if (aceFlags.contains(AclEntryFlag.FILE_INHERIT))
flags |= OBJECT_INHERIT_ACE;
if (aceFlags.contains(AclEntryFlag.DIRECTORY_INHERIT))
flags |= CONTAINER_INHERIT_ACE;
if (aceFlags.contains(AclEntryFlag.NO_PROPAGATE_INHERIT))
flags |= NO_PROPAGATE_INHERIT_ACE;
if (aceFlags.contains(AclEntryFlag.INHERIT_ONLY))
flags |= INHERIT_ONLY_ACE;
if (allow) {
AddAccessAllowedAceEx(aclAddress, flags, mask, sidAddress);
} else {
AddAccessDeniedAceEx(aclAddress, flags, mask, sidAddress);
}
}
/**
* Creates a security descriptor with a DACL representing the given ACL.
*/
static WindowsSecurityDescriptor create(List<AclEntry> acl)
throws IOException
{
return new WindowsSecurityDescriptor(acl);
}
/**
* Processes the array of attributes looking for the attribute "acl:acl".
* Returns security descriptor representing the ACL or the "null" security
* descriptor if the attribute is not in the array.
*/
@SuppressWarnings("unchecked")
static WindowsSecurityDescriptor fromAttribute(FileAttribute<?>... attrs)
throws IOException
{
WindowsSecurityDescriptor sd = NULL_DESCRIPTOR;
for (FileAttribute<?> attr: attrs) {
// if more than one ACL specified then last one wins
if (sd != NULL_DESCRIPTOR)
sd.release();
if (attr == null)
throw new NullPointerException();
if (attr.name().equals("acl:acl")) {
List<AclEntry> acl = (List<AclEntry>)attr.value();
sd = new WindowsSecurityDescriptor(acl);
} else {
throw new UnsupportedOperationException("'" + attr.name() +
"' not supported as initial attribute");
}
}
return sd;
}
/**
* Extracts DACL from security descriptor.
*/
static List<AclEntry> getAcl(long pSecurityDescriptor) throws IOException {
// get address of DACL
long aclAddress = GetSecurityDescriptorDacl(pSecurityDescriptor);
// get ACE count
int aceCount = 0;
if (aclAddress == 0L) {
// no ACEs
aceCount = 0;
} else {
AclInformation aclInfo = GetAclInformation(aclAddress);
aceCount = aclInfo.aceCount();
}
ArrayList<AclEntry> result = new ArrayList<>(aceCount);
// decode each of the ACEs to AclEntry objects
for (int i=0; i<aceCount; i++) {
long aceAddress = GetAce(aclAddress, i);
AclEntry entry = decode(aceAddress);
if (entry != null)
result.add(entry);
}
return result;
}
}

View file

@ -0,0 +1,167 @@
/*
* 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.net.URI;
import java.net.URISyntaxException;
/**
* Utility methods to convert between Path and URIs.
*/
class WindowsUriSupport {
private WindowsUriSupport() {
}
// suffix for IPv6 literal address
private static final String IPV6_LITERAL_SUFFIX = ".ipv6-literal.net";
/**
* Returns URI to represent the given (absolute) path
*/
private static URI toUri(String path, boolean isUnc, boolean addSlash) {
String uriHost;
String uriPath;
if (isUnc) {
int slash = path.indexOf('\\', 2);
uriHost = path.substring(2, slash);
uriPath = path.substring(slash).replace('\\', '/');
// handle IPv6 literal addresses
// 1. drop .ivp6-literal.net
// 2. replace "-" with ":"
// 3. replace "s" with "%" (zone/scopeID delimiter)
if (uriHost.endsWith(IPV6_LITERAL_SUFFIX)) {
uriHost = uriHost
.substring(0, uriHost.length() - IPV6_LITERAL_SUFFIX.length())
.replace('-', ':')
.replace('s', '%');
}
} else {
uriHost = "";
uriPath = "/" + path.replace('\\', '/');
}
// append slash if known to be directory
if (addSlash)
uriPath += "/";
// return file:///C:/My%20Documents or file://server/share/foo
try {
return new URI("file", uriHost, uriPath, null);
} catch (URISyntaxException x) {
if (!isUnc)
throw new AssertionError(x);
}
// if we get here it means we've got a UNC with reserved characters
// in the server name. The authority component cannot contain escaped
// octets so fallback to encoding the server name into the URI path
// component.
uriPath = "//" + path.replace('\\', '/');
if (addSlash)
uriPath += "/";
try {
return new URI("file", null, uriPath, null);
} catch (URISyntaxException x) {
throw new AssertionError(x);
}
}
/**
* Converts given Path to a URI
*/
static URI toUri(WindowsPath path) {
path = path.toAbsolutePath();
String s = path.toString();
// trailing slash will be added if file is a directory. Skip check if
// already have trailing space
boolean addSlash = false;
if (!s.endsWith("\\")) {
try {
addSlash = WindowsFileAttributes.get(path, true).isDirectory();
} catch (WindowsException x) {
}
}
return toUri(s, path.isUnc(), addSlash);
}
/**
* Converts given URI to a Path
*/
static WindowsPath fromUri(WindowsFileSystem fs, URI uri) {
if (!uri.isAbsolute())
throw new IllegalArgumentException("URI is not absolute");
if (uri.isOpaque())
throw new IllegalArgumentException("URI is not hierarchical");
String scheme = uri.getScheme();
if ((scheme == null) || !scheme.equalsIgnoreCase("file"))
throw new IllegalArgumentException("URI scheme is not \"file\"");
if (uri.getRawFragment() != null)
throw new IllegalArgumentException("URI has a fragment component");
if (uri.getRawQuery() != null)
throw new IllegalArgumentException("URI has a query component");
String path = uri.getPath();
if (path.equals(""))
throw new IllegalArgumentException("URI path component is empty");
// UNC
String auth = uri.getRawAuthority();
if (auth != null && !auth.equals("")) {
String host = uri.getHost();
if (host == null)
throw new IllegalArgumentException("URI authority component has undefined host");
if (uri.getUserInfo() != null)
throw new IllegalArgumentException("URI authority component has user-info");
if (uri.getPort() != -1)
throw new IllegalArgumentException("URI authority component has port number");
// IPv6 literal
// 1. drop enclosing brackets
// 2. replace ":" with "-"
// 3. replace "%" with "s" (zone/scopeID delimiter)
// 4. Append .ivp6-literal.net
if (host.startsWith("[")) {
host = host.substring(1, host.length()-1)
.replace(':', '-')
.replace('%', 's');
host += IPV6_LITERAL_SUFFIX;
}
// reconstitute the UNC
path = "\\\\" + host + path;
} else {
if ((path.length() > 2) && (path.charAt(2) == ':')) {
// "/c:/foo" --> "c:/foo"
path = path.substring(1);
}
}
return WindowsPath.parse(fs, path);
}
}

View file

@ -0,0 +1,232 @@
/*
* Copyright (c) 2008, 2011, 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 static java.nio.file.StandardOpenOption.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.io.IOException;
import java.util.*;
import jdk.internal.misc.Unsafe;
import static sun.nio.fs.WindowsNativeDispatcher.*;
import static sun.nio.fs.WindowsConstants.*;
/**
* Windows emulation of NamedAttributeView using Alternative Data Streams
*/
class WindowsUserDefinedFileAttributeView
extends AbstractUserDefinedFileAttributeView
{
private static final Unsafe unsafe = Unsafe.getUnsafe();
// syntax to address named streams
private String join(String file, String name) {
if (name == null)
throw new NullPointerException("'name' is null");
return file + ":" + name;
}
private String join(WindowsPath file, String name) throws WindowsException {
return join(file.getPathForWin32Calls(), name);
}
private final WindowsPath file;
private final boolean followLinks;
WindowsUserDefinedFileAttributeView(WindowsPath file, boolean followLinks) {
this.file = file;
this.followLinks = followLinks;
}
// enumerates the file streams using FindFirstStream/FindNextStream APIs.
private List<String> listUsingStreamEnumeration() throws IOException {
List<String> list = new ArrayList<>();
try {
FirstStream first = FindFirstStream(file.getPathForWin32Calls());
if (first != null) {
long handle = first.handle();
try {
// first stream is always ::$DATA for files
String name = first.name();
if (!name.equals("::$DATA")) {
String[] segs = name.split(":");
list.add(segs[1]);
}
while ((name = FindNextStream(handle)) != null) {
String[] segs = name.split(":");
list.add(segs[1]);
}
} finally {
FindClose(handle);
}
}
} catch (WindowsException x) {
x.rethrowAsIOException(file);
}
return Collections.unmodifiableList(list);
}
@Override
public List<String> list() throws IOException {
if (System.getSecurityManager() != null)
checkAccess(file.getPathForPermissionCheck(), true, false);
return listUsingStreamEnumeration();
}
@Override
public int size(String name) throws IOException {
if (System.getSecurityManager() != null)
checkAccess(file.getPathForPermissionCheck(), true, false);
// wrap with channel
FileChannel fc = null;
try {
Set<OpenOption> opts = new HashSet<>();
opts.add(READ);
if (!followLinks)
opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT);
fc = WindowsChannelFactory
.newFileChannel(join(file, name), null, opts, 0L);
} catch (WindowsException x) {
x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name));
}
try {
long size = fc.size();
if (size > Integer.MAX_VALUE)
throw new ArithmeticException("Stream too large");
return (int)size;
} finally {
fc.close();
}
}
@Override
public int read(String name, ByteBuffer dst) throws IOException {
if (System.getSecurityManager() != null)
checkAccess(file.getPathForPermissionCheck(), true, false);
// wrap with channel
FileChannel fc = null;
try {
Set<OpenOption> opts = new HashSet<>();
opts.add(READ);
if (!followLinks)
opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT);
fc = WindowsChannelFactory
.newFileChannel(join(file, name), null, opts, 0L);
} catch (WindowsException x) {
x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name));
}
// read to EOF (nothing we can do if I/O error occurs)
try {
if (fc.size() > dst.remaining())
throw new IOException("Stream too large");
int total = 0;
while (dst.hasRemaining()) {
int n = fc.read(dst);
if (n < 0)
break;
total += n;
}
return total;
} finally {
fc.close();
}
}
@Override
public int write(String name, ByteBuffer src) throws IOException {
if (System.getSecurityManager() != null)
checkAccess(file.getPathForPermissionCheck(), false, true);
/**
* Creating a named stream will cause the unnamed stream to be created
* if it doesn't already exist. To avoid this we open the unnamed stream
* for reading and hope it isn't deleted/moved while we create or
* replace the named stream. Opening the file without sharing options
* may cause sharing violations with other programs that are accessing
* the unnamed stream.
*/
long handle = -1L;
try {
int flags = FILE_FLAG_BACKUP_SEMANTICS;
if (!followLinks)
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
handle = CreateFile(file.getPathForWin32Calls(),
GENERIC_READ,
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
OPEN_EXISTING,
flags);
} catch (WindowsException x) {
x.rethrowAsIOException(file);
}
try {
Set<OpenOption> opts = new HashSet<>();
if (!followLinks)
opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT);
opts.add(CREATE);
opts.add(WRITE);
opts.add(StandardOpenOption.TRUNCATE_EXISTING);
FileChannel named = null;
try {
named = WindowsChannelFactory
.newFileChannel(join(file, name), null, opts, 0L);
} catch (WindowsException x) {
x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name));
}
// write value (nothing we can do if I/O error occurs)
try {
int rem = src.remaining();
while (src.hasRemaining()) {
named.write(src);
}
return rem;
} finally {
named.close();
}
} finally {
CloseHandle(handle);
}
}
@Override
public void delete(String name) throws IOException {
if (System.getSecurityManager() != null)
checkAccess(file.getPathForPermissionCheck(), false, true);
String path = WindowsLinkSupport.getFinalPath(file, followLinks);
String toDelete = join(path, name);
try {
DeleteFile(toDelete);
} catch (WindowsException x) {
x.rethrowAsIOException(toDelete);
}
}
}

View file

@ -0,0 +1,169 @@
/*
* 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.attribute.*;
import java.io.IOException;
import static sun.nio.fs.WindowsConstants.*;
import static sun.nio.fs.WindowsNativeDispatcher.*;
class WindowsUserPrincipals {
private WindowsUserPrincipals() { }
static class User implements UserPrincipal {
// String representation of SID
private final String sidString;
// SID type
private final int sidType;
// Account name (if available) or SID
private final String accountName;
User(String sidString, int sidType, String accountName) {
this.sidString = sidString;
this.sidType = sidType;
this.accountName = accountName;
}
// package-private
String sidString() {
return sidString;
}
@Override
public String getName() {
return accountName;
}
@Override
public String toString() {
String type;
switch (sidType) {
case SidTypeUser : type = "User"; break;
case SidTypeGroup : type = "Group"; break;
case SidTypeDomain : type = "Domain"; break;
case SidTypeAlias : type = "Alias"; break;
case SidTypeWellKnownGroup : type = "Well-known group"; break;
case SidTypeDeletedAccount : type = "Deleted"; break;
case SidTypeInvalid : type = "Invalid"; break;
case SidTypeComputer : type = "Computer"; break;
default: type = "Unknown";
}
return accountName + " (" + type + ")";
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (!(obj instanceof WindowsUserPrincipals.User))
return false;
WindowsUserPrincipals.User other = (WindowsUserPrincipals.User)obj;
return this.sidString.equals(other.sidString);
}
@Override
public int hashCode() {
return sidString.hashCode();
}
}
static class Group extends User implements GroupPrincipal {
Group(String sidString, int sidType, String accountName) {
super(sidString, sidType, accountName);
}
}
static UserPrincipal fromSid(long sidAddress) throws IOException {
String sidString;
try {
sidString = ConvertSidToStringSid(sidAddress);
if (sidString == null) {
// pre-Windows XP system?
throw new AssertionError();
}
} catch (WindowsException x) {
throw new IOException("Unable to convert SID to String: " +
x.errorString());
}
// lookup account; if not available then use the SID as the name
Account account = null;
String name;
try {
account = LookupAccountSid(sidAddress);
name = account.domain() + "\\" + account.name();
} catch (WindowsException x) {
name = sidString;
}
int sidType = (account == null) ? SidTypeUnknown : account.use();
if ((sidType == SidTypeGroup) ||
(sidType == SidTypeWellKnownGroup) ||
(sidType == SidTypeAlias)) // alias for local group
{
return new Group(sidString, sidType, name);
} else {
return new User(sidString, sidType, name);
}
}
static UserPrincipal lookup(String name) throws IOException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("lookupUserInformation"));
}
// invoke LookupAccountName to get buffer size needed for SID
int size = 0;
try {
size = LookupAccountName(name, 0L, 0);
} catch (WindowsException x) {
if (x.lastError() == ERROR_NONE_MAPPED)
throw new UserPrincipalNotFoundException(name);
throw new IOException(name + ": " + x.errorString());
}
assert size > 0;
// allocate buffer and re-invoke LookupAccountName get SID
NativeBuffer sidBuffer = NativeBuffers.getNativeBuffer(size);
try {
int newSize = LookupAccountName(name, sidBuffer.address(), size);
if (newSize != size) {
// can this happen?
throw new AssertionError("SID change during lookup");
}
// return user principal
return fromSid(sidBuffer.address());
} catch (WindowsException x) {
throw new IOException(name + ": " + x.errorString());
} finally {
sidBuffer.release();
}
}
}

View file

@ -0,0 +1,657 @@
/*
* 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.IOException;
import java.nio.file.NotDirectoryException;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import jdk.internal.misc.Unsafe;
import static sun.nio.fs.WindowsNativeDispatcher.*;
import static sun.nio.fs.WindowsConstants.*;
/*
* Win32 implementation of WatchService based on ReadDirectoryChangesW.
*/
class WindowsWatchService
extends AbstractWatchService
{
private static final int WAKEUP_COMPLETION_KEY = 0;
// background thread to service I/O completion port
private final Poller poller;
/**
* Creates an I/O completion port and a daemon thread to service it
*/
WindowsWatchService(WindowsFileSystem fs) throws IOException {
// create I/O completion port
long port = 0L;
try {
port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0);
} catch (WindowsException x) {
throw new IOException(x.getMessage());
}
this.poller = new Poller(fs, this, port);
this.poller.start();
}
@Override
WatchKey register(Path path,
WatchEvent.Kind<?>[] events,
WatchEvent.Modifier... modifiers)
throws IOException
{
// delegate to poller
return poller.register(path, events, modifiers);
}
@Override
void implClose() throws IOException {
// delegate to poller
poller.close();
}
/**
* Windows implementation of WatchKey.
*/
private static class WindowsWatchKey extends AbstractWatchKey {
// file key (used to detect existing registrations)
private final FileKey fileKey;
// handle to directory
private volatile long handle = INVALID_HANDLE_VALUE;
// interest events
private Set<? extends WatchEvent.Kind<?>> events;
// subtree
private boolean watchSubtree;
// buffer for change events
private NativeBuffer buffer;
// pointer to bytes returned (in buffer)
private long countAddress;
// pointer to overlapped structure (in buffer)
private long overlappedAddress;
// completion key (used to map I/O completion to WatchKey)
private int completionKey;
// flag indicates that ReadDirectoryChangesW failed
// and overlapped I/O operation wasn't started
private boolean errorStartingOverlapped;
WindowsWatchKey(Path dir,
AbstractWatchService watcher,
FileKey fileKey)
{
super(dir, watcher);
this.fileKey = fileKey;
}
WindowsWatchKey init(long handle,
Set<? extends WatchEvent.Kind<?>> events,
boolean watchSubtree,
NativeBuffer buffer,
long countAddress,
long overlappedAddress,
int completionKey)
{
this.handle = handle;
this.events = events;
this.watchSubtree = watchSubtree;
this.buffer = buffer;
this.countAddress = countAddress;
this.overlappedAddress = overlappedAddress;
this.completionKey = completionKey;
return this;
}
long handle() {
return handle;
}
Set<? extends WatchEvent.Kind<?>> events() {
return events;
}
void setEvents(Set<? extends WatchEvent.Kind<?>> events) {
this.events = events;
}
boolean watchSubtree() {
return watchSubtree;
}
NativeBuffer buffer() {
return buffer;
}
long countAddress() {
return countAddress;
}
long overlappedAddress() {
return overlappedAddress;
}
FileKey fileKey() {
return fileKey;
}
int completionKey() {
return completionKey;
}
void setErrorStartingOverlapped(boolean value) {
errorStartingOverlapped = value;
}
boolean isErrorStartingOverlapped() {
return errorStartingOverlapped;
}
// Invalidate the key, assumes that resources have been released
void invalidate() {
((WindowsWatchService)watcher()).poller.releaseResources(this);
handle = INVALID_HANDLE_VALUE;
buffer = null;
countAddress = 0;
overlappedAddress = 0;
errorStartingOverlapped = false;
}
@Override
public boolean isValid() {
return handle != INVALID_HANDLE_VALUE;
}
@Override
public void cancel() {
if (isValid()) {
// delegate to poller
((WindowsWatchService)watcher()).poller.cancel(this);
}
}
}
// file key to unique identify (open) directory
private static class FileKey {
private final int volSerialNumber;
private final int fileIndexHigh;
private final int fileIndexLow;
FileKey(int volSerialNumber, int fileIndexHigh, int fileIndexLow) {
this.volSerialNumber = volSerialNumber;
this.fileIndexHigh = fileIndexHigh;
this.fileIndexLow = fileIndexLow;
}
@Override
public int hashCode() {
return volSerialNumber ^ fileIndexHigh ^ fileIndexLow;
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (!(obj instanceof FileKey))
return false;
FileKey other = (FileKey)obj;
if (this.volSerialNumber != other.volSerialNumber) return false;
if (this.fileIndexHigh != other.fileIndexHigh) return false;
return this.fileIndexLow == other.fileIndexLow;
}
}
// all change events
private static final int ALL_FILE_NOTIFY_EVENTS =
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_CREATION |
FILE_NOTIFY_CHANGE_SECURITY;
/**
* Background thread to service I/O completion port.
*/
private static class Poller extends AbstractPoller {
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
/*
* typedef struct _OVERLAPPED {
* ULONG_PTR Internal;
* ULONG_PTR InternalHigh;
* union {
* struct { DWORD Offset; DWORD OffsetHigh; };
* PVOID Pointer;
* };
* HANDLE hEvent;
* } OVERLAPPED;
*/
private static final short SIZEOF_DWORD = 4;
private static final short SIZEOF_OVERLAPPED = 32; // 20 on 32-bit
private static final short OFFSETOF_HEVENT =
(UNSAFE.addressSize() == 4) ? (short) 16 : 24;
/*
* typedef struct _FILE_NOTIFY_INFORMATION {
* DWORD NextEntryOffset;
* DWORD Action;
* DWORD FileNameLength;
* WCHAR FileName[1];
* } FileNameLength;
*/
private static final short OFFSETOF_NEXTENTRYOFFSET = 0;
private static final short OFFSETOF_ACTION = 4;
private static final short OFFSETOF_FILENAMELENGTH = 8;
private static final short OFFSETOF_FILENAME = 12;
// size of per-directory buffer for events (FIXME - make this configurable)
// Need to be less than 4*16384 = 65536. DWORD align.
private static final int CHANGES_BUFFER_SIZE = 16 * 1024;
private final WindowsFileSystem fs;
private final WindowsWatchService watcher;
private final long port;
// maps completion key to WatchKey
private final Map<Integer, WindowsWatchKey> ck2key;
// maps file key to WatchKey
private final Map<FileKey, WindowsWatchKey> fk2key;
// unique completion key for each directory
// native completion key capacity is 64 bits on Win64.
private int lastCompletionKey;
Poller(WindowsFileSystem fs, WindowsWatchService watcher, long port) {
this.fs = fs;
this.watcher = watcher;
this.port = port;
this.ck2key = new HashMap<>();
this.fk2key = new HashMap<>();
this.lastCompletionKey = 0;
}
@Override
void wakeup() throws IOException {
try {
PostQueuedCompletionStatus(port, WAKEUP_COMPLETION_KEY);
} catch (WindowsException x) {
throw new IOException(x.getMessage());
}
}
/**
* Register a directory for changes as follows:
*
* 1. Open directory
* 2. Read its attributes (and check it really is a directory)
* 3. Assign completion key and associated handle with completion port
* 4. Call ReadDirectoryChangesW to start (async) read of changes
* 5. Create or return existing key representing registration
*/
@Override
Object implRegister(Path obj,
Set<? extends WatchEvent.Kind<?>> events,
WatchEvent.Modifier... modifiers)
{
WindowsPath dir = (WindowsPath)obj;
boolean watchSubtree = false;
// FILE_TREE modifier allowed
for (WatchEvent.Modifier modifier: modifiers) {
if (ExtendedOptions.FILE_TREE.matches(modifier)) {
watchSubtree = true;
} else {
if (modifier == null)
return new NullPointerException();
if (!ExtendedOptions.SENSITIVITY_HIGH.matches(modifier) &&
!ExtendedOptions.SENSITIVITY_MEDIUM.matches(modifier) &&
!ExtendedOptions.SENSITIVITY_LOW.matches(modifier)) {
return new UnsupportedOperationException("Modifier not supported");
}
}
}
// open directory
long handle;
try {
handle = CreateFile(dir.getPathForWin32Calls(),
FILE_LIST_DIRECTORY,
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED);
} catch (WindowsException x) {
return x.asIOException(dir);
}
boolean registered = false;
try {
// read attributes and check file is a directory
WindowsFileAttributes attrs;
try {
attrs = WindowsFileAttributes.readAttributes(handle);
} catch (WindowsException x) {
return x.asIOException(dir);
}
if (!attrs.isDirectory()) {
return new NotDirectoryException(dir.getPathForExceptionMessage());
}
// check if this directory is already registered
FileKey fk = new FileKey(attrs.volSerialNumber(),
attrs.fileIndexHigh(),
attrs.fileIndexLow());
WindowsWatchKey existing = fk2key.get(fk);
// if already registered and we're not changing the subtree
// modifier then simply update the event and return the key.
if (existing != null && watchSubtree == existing.watchSubtree()) {
existing.setEvents(events);
return existing;
}
// Can overflow the int type capacity.
// Skip WAKEUP_COMPLETION_KEY value.
int completionKey = ++lastCompletionKey;
if (completionKey == WAKEUP_COMPLETION_KEY)
completionKey = ++lastCompletionKey;
// associate handle with completion port
try {
CreateIoCompletionPort(handle, port, completionKey);
} catch (WindowsException x) {
return new IOException(x.getMessage());
}
// allocate memory for events, including space for other structures
// needed to do overlapped I/O
int size = CHANGES_BUFFER_SIZE + SIZEOF_DWORD + SIZEOF_OVERLAPPED;
NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
long bufferAddress = buffer.address();
long overlappedAddress = bufferAddress + size - SIZEOF_OVERLAPPED;
long countAddress = overlappedAddress - SIZEOF_DWORD;
// zero the overlapped structure
UNSAFE.setMemory(overlappedAddress, SIZEOF_OVERLAPPED, (byte)0);
// start async read of changes to directory
try {
createAndAttachEvent(overlappedAddress);
ReadDirectoryChangesW(handle,
bufferAddress,
CHANGES_BUFFER_SIZE,
watchSubtree,
ALL_FILE_NOTIFY_EVENTS,
countAddress,
overlappedAddress);
} catch (WindowsException x) {
closeAttachedEvent(overlappedAddress);
buffer.release();
return new IOException(x.getMessage());
}
WindowsWatchKey watchKey;
if (existing == null) {
// not registered so create new watch key
watchKey = new WindowsWatchKey(dir, watcher, fk)
.init(handle, events, watchSubtree, buffer, countAddress,
overlappedAddress, completionKey);
// map file key to watch key
fk2key.put(fk, watchKey);
} else {
// directory already registered so need to:
// 1. remove mapping from old completion key to existing watch key
// 2. release existing key's resources (handle/buffer)
// 3. re-initialize key with new handle/buffer
ck2key.remove(existing.completionKey());
releaseResources(existing);
watchKey = existing.init(handle, events, watchSubtree, buffer,
countAddress, overlappedAddress, completionKey);
}
// map completion map to watch key
ck2key.put(completionKey, watchKey);
registered = true;
return watchKey;
} finally {
if (!registered) CloseHandle(handle);
}
}
/**
* Cancels the outstanding I/O operation on the directory
* associated with the given key and releases the associated
* resources.
*/
private void releaseResources(WindowsWatchKey key) {
if (!key.isErrorStartingOverlapped()) {
try {
CancelIo(key.handle());
GetOverlappedResult(key.handle(), key.overlappedAddress());
} catch (WindowsException expected) {
// expected as I/O operation has been cancelled
}
}
CloseHandle(key.handle());
closeAttachedEvent(key.overlappedAddress());
key.buffer().free();
}
/**
* Creates an unnamed event and set it as the hEvent field
* in the given OVERLAPPED structure
*/
private void createAndAttachEvent(long ov) throws WindowsException {
long hEvent = CreateEvent(false, false);
UNSAFE.putAddress(ov + OFFSETOF_HEVENT, hEvent);
}
/**
* Closes the event attached to the given OVERLAPPED structure. A
* no-op if there isn't an event attached.
*/
private void closeAttachedEvent(long ov) {
long hEvent = UNSAFE.getAddress(ov + OFFSETOF_HEVENT);
if (hEvent != 0 && hEvent != INVALID_HANDLE_VALUE)
CloseHandle(hEvent);
}
// cancel single key
@Override
void implCancelKey(WatchKey obj) {
WindowsWatchKey key = (WindowsWatchKey)obj;
if (key.isValid()) {
fk2key.remove(key.fileKey());
ck2key.remove(key.completionKey());
key.invalidate();
}
}
// close watch service
@Override
void implCloseAll() {
// cancel all keys
ck2key.values().forEach(WindowsWatchKey::invalidate);
fk2key.clear();
ck2key.clear();
// close I/O completion port
CloseHandle(port);
}
// Translate file change action into watch event
private WatchEvent.Kind<?> translateActionToEvent(int action) {
switch (action) {
case FILE_ACTION_MODIFIED :
return StandardWatchEventKinds.ENTRY_MODIFY;
case FILE_ACTION_ADDED :
case FILE_ACTION_RENAMED_NEW_NAME :
return StandardWatchEventKinds.ENTRY_CREATE;
case FILE_ACTION_REMOVED :
case FILE_ACTION_RENAMED_OLD_NAME :
return StandardWatchEventKinds.ENTRY_DELETE;
default :
return null; // action not recognized
}
}
// process events (list of FILE_NOTIFY_INFORMATION structures)
private void processEvents(WindowsWatchKey key, int size) {
long address = key.buffer().address();
int nextOffset;
do {
int action = UNSAFE.getInt(address + OFFSETOF_ACTION);
// map action to event
WatchEvent.Kind<?> kind = translateActionToEvent(action);
if (key.events().contains(kind)) {
// copy the name
int nameLengthInBytes = UNSAFE.getInt(address + OFFSETOF_FILENAMELENGTH);
if ((nameLengthInBytes % 2) != 0) {
throw new AssertionError("FileNameLength is not a multiple of 2");
}
char[] nameAsArray = new char[nameLengthInBytes/2];
UNSAFE.copyMemory(null, address + OFFSETOF_FILENAME, nameAsArray,
Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes);
// create FileName and queue event
WindowsPath name = WindowsPath
.createFromNormalizedPath(fs, new String(nameAsArray));
key.signalEvent(kind, name);
}
// next event
nextOffset = UNSAFE.getInt(address + OFFSETOF_NEXTENTRYOFFSET);
address += (long)nextOffset;
} while (nextOffset != 0);
}
/**
* Poller main loop
*/
@Override
public void run() {
for (;;) {
CompletionStatus info;
try {
info = GetQueuedCompletionStatus(port);
} catch (WindowsException x) {
// this should not happen
x.printStackTrace();
return;
}
// wakeup
if (info.completionKey() == WAKEUP_COMPLETION_KEY) {
boolean shutdown = processRequests();
if (shutdown) {
return;
}
continue;
}
// map completionKey to get WatchKey
WindowsWatchKey key = ck2key.get((int)info.completionKey());
if (key == null) {
// We get here when a registration is changed. In that case
// the directory is closed which causes an event with the
// old completion key.
continue;
}
boolean criticalError = false;
int errorCode = info.error();
int messageSize = info.bytesTransferred();
if (errorCode == ERROR_NOTIFY_ENUM_DIR) {
// buffer overflow
key.signalEvent(StandardWatchEventKinds.OVERFLOW, null);
} else if (errorCode != 0 && errorCode != ERROR_MORE_DATA) {
// ReadDirectoryChangesW failed
criticalError = true;
} else {
// ERROR_MORE_DATA is a warning about incomplete
// data transfer over TCP/UDP stack. For the case
// [messageSize] is zero in the most of cases.
if (messageSize > 0) {
// process non-empty events.
processEvents(key, messageSize);
} else if (errorCode == 0) {
// insufficient buffer size
// not described, but can happen.
key.signalEvent(StandardWatchEventKinds.OVERFLOW, null);
}
// start read for next batch of changes
try {
ReadDirectoryChangesW(key.handle(),
key.buffer().address(),
CHANGES_BUFFER_SIZE,
key.watchSubtree(),
ALL_FILE_NOTIFY_EVENTS,
key.countAddress(),
key.overlappedAddress());
} catch (WindowsException x) {
// no choice but to cancel key
criticalError = true;
key.setErrorStartingOverlapped(true);
}
}
if (criticalError) {
implCancelKey(key);
key.signal();
}
}
}
}
}