893N/A/*
3909N/A * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
893N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
893N/A *
893N/A * This code is free software; you can redistribute it and/or modify it
893N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
893N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
893N/A *
893N/A * This code is distributed in the hope that it will be useful, but WITHOUT
893N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
893N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
893N/A * version 2 for more details (a copy is included in the LICENSE file that
893N/A * accompanied this code).
893N/A *
893N/A * You should have received a copy of the GNU General Public License version
893N/A * 2 along with this work; if not, write to the Free Software Foundation,
893N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
893N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
893N/A */
893N/A
893N/Apackage sun.nio.fs;
893N/A
893N/Aimport java.nio.file.*;
893N/Aimport java.io.IOException;
893N/Aimport java.util.*;
893N/Aimport com.sun.nio.file.ExtendedWatchEventModifier;
893N/Aimport sun.misc.Unsafe;
893N/A
893N/Aimport static sun.nio.fs.WindowsNativeDispatcher.*;
893N/Aimport static sun.nio.fs.WindowsConstants.*;
893N/A
893N/A/*
893N/A * Win32 implementation of WatchService based on ReadDirectoryChangesW.
893N/A */
893N/A
893N/Aclass WindowsWatchService
893N/A extends AbstractWatchService
893N/A{
893N/A private final Unsafe unsafe = Unsafe.getUnsafe();
893N/A
893N/A // background thread to service I/O completion port
893N/A private final Poller poller;
893N/A
893N/A /**
893N/A * Creates an I/O completion port and a daemon thread to service it
893N/A */
893N/A WindowsWatchService(WindowsFileSystem fs) throws IOException {
893N/A // create I/O completion port
893N/A long port = 0L;
893N/A try {
893N/A port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0);
893N/A } catch (WindowsException x) {
893N/A throw new IOException(x.getMessage());
893N/A }
893N/A
893N/A this.poller = new Poller(fs, this, port);
893N/A this.poller.start();
893N/A }
893N/A
893N/A @Override
893N/A WatchKey register(Path path,
893N/A WatchEvent.Kind<?>[] events,
893N/A WatchEvent.Modifier... modifiers)
893N/A throws IOException
893N/A {
893N/A // delegate to poller
893N/A return poller.register(path, events, modifiers);
893N/A }
893N/A
893N/A @Override
893N/A void implClose() throws IOException {
893N/A // delegate to poller
893N/A poller.close();
893N/A }
893N/A
893N/A /**
893N/A * Windows implementation of WatchKey.
893N/A */
893N/A private class WindowsWatchKey extends AbstractWatchKey {
893N/A // file key (used to detect existing registrations)
893N/A private FileKey fileKey;
893N/A
893N/A // handle to directory
893N/A private volatile long handle = INVALID_HANDLE_VALUE;
893N/A
893N/A // interest events
893N/A private Set<? extends WatchEvent.Kind<?>> events;
893N/A
893N/A // subtree
893N/A private boolean watchSubtree;
893N/A
893N/A // buffer for change events
893N/A private NativeBuffer buffer;
893N/A
893N/A // pointer to bytes returned (in buffer)
893N/A private long countAddress;
893N/A
893N/A // pointer to overlapped structure (in buffer)
893N/A private long overlappedAddress;
893N/A
893N/A // completion key (used to map I/O completion to WatchKey)
893N/A private int completionKey;
893N/A
3471N/A WindowsWatchKey(Path dir,
3471N/A AbstractWatchService watcher,
3471N/A FileKey fileKey)
3471N/A {
3471N/A super(dir, watcher);
893N/A this.fileKey = fileKey;
893N/A }
893N/A
893N/A WindowsWatchKey init(long handle,
893N/A Set<? extends WatchEvent.Kind<?>> events,
893N/A boolean watchSubtree,
893N/A NativeBuffer buffer,
893N/A long countAddress,
893N/A long overlappedAddress,
893N/A int completionKey)
893N/A {
893N/A this.handle = handle;
893N/A this.events = events;
893N/A this.watchSubtree = watchSubtree;
893N/A this.buffer = buffer;
893N/A this.countAddress = countAddress;
893N/A this.overlappedAddress = overlappedAddress;
893N/A this.completionKey = completionKey;
893N/A return this;
893N/A }
893N/A
893N/A long handle() {
893N/A return handle;
893N/A }
893N/A
893N/A Set<? extends WatchEvent.Kind<?>> events() {
893N/A return events;
893N/A }
893N/A
893N/A void setEvents(Set<? extends WatchEvent.Kind<?>> events) {
893N/A this.events = events;
893N/A }
893N/A
893N/A boolean watchSubtree() {
893N/A return watchSubtree;
893N/A }
893N/A
893N/A NativeBuffer buffer() {
893N/A return buffer;
893N/A }
893N/A
893N/A long countAddress() {
893N/A return countAddress;
893N/A }
893N/A
893N/A long overlappedAddress() {
893N/A return overlappedAddress;
893N/A }
893N/A
893N/A FileKey fileKey() {
893N/A return fileKey;
893N/A }
893N/A
893N/A int completionKey() {
893N/A return completionKey;
893N/A }
893N/A
893N/A // close directory and release buffer
893N/A void releaseResources() {
893N/A CloseHandle(handle);
893N/A buffer.cleaner().clean();
893N/A }
893N/A
893N/A // Invalidate key by closing directory and releasing buffer
893N/A void invalidate() {
893N/A releaseResources();
893N/A handle = INVALID_HANDLE_VALUE;
893N/A buffer = null;
893N/A countAddress = 0;
893N/A overlappedAddress = 0;
893N/A }
893N/A
893N/A @Override
893N/A public boolean isValid() {
893N/A return handle != INVALID_HANDLE_VALUE;
893N/A }
893N/A
893N/A @Override
893N/A public void cancel() {
893N/A if (isValid()) {
893N/A // delegate to poller
893N/A poller.cancel(this);
893N/A }
893N/A }
893N/A }
893N/A
893N/A // file key to unique identify (open) directory
893N/A private static class FileKey {
893N/A private final int volSerialNumber;
893N/A private final int fileIndexHigh;
893N/A private final int fileIndexLow;
893N/A
893N/A FileKey(int volSerialNumber, int fileIndexHigh, int fileIndexLow) {
893N/A this.volSerialNumber = volSerialNumber;
893N/A this.fileIndexHigh = fileIndexHigh;
893N/A this.fileIndexLow = fileIndexLow;
893N/A }
893N/A
893N/A @Override
893N/A public int hashCode() {
893N/A return volSerialNumber ^ fileIndexHigh ^ fileIndexLow;
893N/A }
893N/A
893N/A @Override
893N/A public boolean equals(Object obj) {
893N/A if (obj == this)
893N/A return true;
893N/A if (!(obj instanceof FileKey))
893N/A return false;
893N/A FileKey other = (FileKey)obj;
893N/A if (this.volSerialNumber != other.volSerialNumber) return false;
893N/A if (this.fileIndexHigh != other.fileIndexHigh) return false;
893N/A if (this.fileIndexLow != other.fileIndexLow) return false;
893N/A return true;
893N/A }
893N/A }
893N/A
893N/A // all change events
893N/A private static final int ALL_FILE_NOTIFY_EVENTS =
893N/A FILE_NOTIFY_CHANGE_FILE_NAME |
893N/A FILE_NOTIFY_CHANGE_DIR_NAME |
893N/A FILE_NOTIFY_CHANGE_ATTRIBUTES |
893N/A FILE_NOTIFY_CHANGE_SIZE |
893N/A FILE_NOTIFY_CHANGE_LAST_WRITE |
893N/A FILE_NOTIFY_CHANGE_CREATION |
893N/A FILE_NOTIFY_CHANGE_SECURITY;
893N/A
893N/A /**
893N/A * Background thread to service I/O completion port.
893N/A */
893N/A private class Poller extends AbstractPoller {
893N/A /*
893N/A * typedef struct _OVERLAPPED {
893N/A * DWORD Internal;
893N/A * DWORD InternalHigh;
893N/A * DWORD Offset;
893N/A * DWORD OffsetHigh;
893N/A * HANDLE hEvent;
893N/A * } OVERLAPPED;
893N/A */
893N/A private static final short SIZEOF_DWORD = 4;
893N/A private static final short SIZEOF_OVERLAPPED = 32; // 20 on 32-bit
893N/A
893N/A /*
893N/A * typedef struct _FILE_NOTIFY_INFORMATION {
893N/A * DWORD NextEntryOffset;
893N/A * DWORD Action;
893N/A * DWORD FileNameLength;
893N/A * WCHAR FileName[1];
893N/A * } FileNameLength;
893N/A */
893N/A private static final short OFFSETOF_NEXTENTRYOFFSET = 0;
893N/A private static final short OFFSETOF_ACTION = 4;
893N/A private static final short OFFSETOF_FILENAMELENGTH = 8;
893N/A private static final short OFFSETOF_FILENAME = 12;
893N/A
893N/A // size of per-directory buffer for events (FIXME - make this configurable)
893N/A private static final int CHANGES_BUFFER_SIZE = 16 * 1024;
893N/A
893N/A private final WindowsFileSystem fs;
893N/A private final WindowsWatchService watcher;
893N/A private final long port;
893N/A
893N/A // maps completion key to WatchKey
893N/A private final Map<Integer,WindowsWatchKey> int2key;
893N/A
893N/A // maps file key to WatchKey
893N/A private final Map<FileKey,WindowsWatchKey> fk2key;
893N/A
893N/A // unique completion key for each directory
893N/A private int lastCompletionKey;
893N/A
893N/A Poller(WindowsFileSystem fs, WindowsWatchService watcher, long port) {
893N/A this.fs = fs;
893N/A this.watcher = watcher;
893N/A this.port = port;
893N/A this.int2key = new HashMap<Integer,WindowsWatchKey>();
893N/A this.fk2key = new HashMap<FileKey,WindowsWatchKey>();
893N/A this.lastCompletionKey = 0;
893N/A }
893N/A
893N/A @Override
893N/A void wakeup() throws IOException {
893N/A try {
893N/A PostQueuedCompletionStatus(port, 0);
893N/A } catch (WindowsException x) {
893N/A throw new IOException(x.getMessage());
893N/A }
893N/A }
893N/A
893N/A /**
893N/A * Register a directory for changes as follows:
893N/A *
893N/A * 1. Open directory
893N/A * 2. Read its attributes (and check it really is a directory)
893N/A * 3. Assign completion key and associated handle with completion port
893N/A * 4. Call ReadDirectoryChangesW to start (async) read of changes
893N/A * 5. Create or return existing key representing registration
893N/A */
893N/A @Override
893N/A Object implRegister(Path obj,
893N/A Set<? extends WatchEvent.Kind<?>> events,
893N/A WatchEvent.Modifier... modifiers)
893N/A {
893N/A WindowsPath dir = (WindowsPath)obj;
893N/A boolean watchSubtree = false;
893N/A
893N/A // FILE_TREE modifier allowed
893N/A for (WatchEvent.Modifier modifier: modifiers) {
893N/A if (modifier == ExtendedWatchEventModifier.FILE_TREE) {
893N/A watchSubtree = true;
893N/A continue;
893N/A } else {
893N/A if (modifier == null)
893N/A return new NullPointerException();
893N/A if (modifier instanceof com.sun.nio.file.SensitivityWatchEventModifier)
893N/A continue; // ignore
893N/A return new UnsupportedOperationException("Modifier not supported");
893N/A }
893N/A }
893N/A
893N/A // open directory
893N/A long handle = -1L;
893N/A try {
893N/A handle = CreateFile(dir.getPathForWin32Calls(),
893N/A FILE_LIST_DIRECTORY,
893N/A (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
893N/A OPEN_EXISTING,
893N/A FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED);
893N/A } catch (WindowsException x) {
893N/A return x.asIOException(dir);
893N/A }
893N/A
893N/A boolean registered = false;
893N/A try {
893N/A // read attributes and check file is a directory
893N/A WindowsFileAttributes attrs = null;
893N/A try {
893N/A attrs = WindowsFileAttributes.readAttributes(handle);
893N/A } catch (WindowsException x) {
893N/A return x.asIOException(dir);
893N/A }
893N/A if (!attrs.isDirectory()) {
893N/A return new NotDirectoryException(dir.getPathForExceptionMessage());
893N/A }
893N/A
893N/A // check if this directory is already registered
893N/A FileKey fk = new FileKey(attrs.volSerialNumber(),
893N/A attrs.fileIndexHigh(),
893N/A attrs.fileIndexLow());
893N/A WindowsWatchKey existing = fk2key.get(fk);
893N/A
893N/A // if already registered and we're not changing the subtree
893N/A // modifier then simply update the event and return the key.
893N/A if (existing != null && watchSubtree == existing.watchSubtree()) {
893N/A existing.setEvents(events);
893N/A return existing;
893N/A }
893N/A
893N/A // unique completion key (skip 0)
893N/A int completionKey = ++lastCompletionKey;
893N/A if (completionKey == 0)
893N/A completionKey = ++lastCompletionKey;
893N/A
893N/A // associate handle with completion port
893N/A try {
893N/A CreateIoCompletionPort(handle, port, completionKey);
893N/A } catch (WindowsException x) {
893N/A return new IOException(x.getMessage());
893N/A }
893N/A
893N/A // allocate memory for events, including space for other structures
893N/A // needed to do overlapped I/O
893N/A int size = CHANGES_BUFFER_SIZE + SIZEOF_DWORD + SIZEOF_OVERLAPPED;
893N/A NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
893N/A
893N/A long bufferAddress = buffer.address();
893N/A long overlappedAddress = bufferAddress + size - SIZEOF_OVERLAPPED;
893N/A long countAddress = overlappedAddress - SIZEOF_DWORD;
893N/A
893N/A // start async read of changes to directory
893N/A try {
893N/A ReadDirectoryChangesW(handle,
893N/A bufferAddress,
893N/A CHANGES_BUFFER_SIZE,
893N/A watchSubtree,
893N/A ALL_FILE_NOTIFY_EVENTS,
893N/A countAddress,
893N/A overlappedAddress);
893N/A } catch (WindowsException x) {
893N/A buffer.release();
893N/A return new IOException(x.getMessage());
893N/A }
893N/A
893N/A WindowsWatchKey watchKey;
893N/A if (existing == null) {
893N/A // not registered so create new watch key
3471N/A watchKey = new WindowsWatchKey(dir, watcher, fk)
893N/A .init(handle, events, watchSubtree, buffer, countAddress,
893N/A overlappedAddress, completionKey);
893N/A // map file key to watch key
893N/A fk2key.put(fk, watchKey);
893N/A } else {
893N/A // directory already registered so need to:
893N/A // 1. remove mapping from old completion key to existing watch key
893N/A // 2. release existing key's resources (handle/buffer)
893N/A // 3. re-initialize key with new handle/buffer
893N/A int2key.remove(existing.completionKey());
893N/A existing.releaseResources();
893N/A watchKey = existing.init(handle, events, watchSubtree, buffer,
893N/A countAddress, overlappedAddress, completionKey);
893N/A }
893N/A // map completion map to watch key
893N/A int2key.put(completionKey, watchKey);
893N/A
893N/A registered = true;
893N/A return watchKey;
893N/A
893N/A } finally {
893N/A if (!registered) CloseHandle(handle);
893N/A }
893N/A }
893N/A
893N/A // cancel single key
893N/A @Override
893N/A void implCancelKey(WatchKey obj) {
893N/A WindowsWatchKey key = (WindowsWatchKey)obj;
893N/A if (key.isValid()) {
893N/A fk2key.remove(key.fileKey());
893N/A int2key.remove(key.completionKey());
893N/A key.invalidate();
893N/A }
893N/A }
893N/A
893N/A // close watch service
893N/A @Override
893N/A void implCloseAll() {
893N/A // cancel all keys
893N/A for (Map.Entry<Integer,WindowsWatchKey> entry: int2key.entrySet()) {
893N/A entry.getValue().invalidate();
893N/A }
893N/A fk2key.clear();
893N/A int2key.clear();
893N/A
893N/A // close I/O completion port
893N/A CloseHandle(port);
893N/A }
893N/A
893N/A // Translate file change action into watch event
893N/A private WatchEvent.Kind<?> translateActionToEvent(int action)
893N/A {
893N/A switch (action) {
893N/A case FILE_ACTION_MODIFIED :
4216N/A return StandardWatchEventKinds.ENTRY_MODIFY;
893N/A
893N/A case FILE_ACTION_ADDED :
893N/A case FILE_ACTION_RENAMED_NEW_NAME :
4216N/A return StandardWatchEventKinds.ENTRY_CREATE;
893N/A
893N/A case FILE_ACTION_REMOVED :
893N/A case FILE_ACTION_RENAMED_OLD_NAME :
4216N/A return StandardWatchEventKinds.ENTRY_DELETE;
893N/A
893N/A default :
893N/A return null; // action not recognized
893N/A }
893N/A }
893N/A
893N/A // process events (list of FILE_NOTIFY_INFORMATION structures)
893N/A private void processEvents(WindowsWatchKey key, int size) {
893N/A long address = key.buffer().address();
893N/A
893N/A int nextOffset;
893N/A do {
893N/A int action = unsafe.getInt(address + OFFSETOF_ACTION);
893N/A
893N/A // map action to event
893N/A WatchEvent.Kind<?> kind = translateActionToEvent(action);
893N/A if (key.events().contains(kind)) {
893N/A // copy the name
893N/A int nameLengthInBytes = unsafe.getInt(address + OFFSETOF_FILENAMELENGTH);
893N/A if ((nameLengthInBytes % 2) != 0) {
893N/A throw new AssertionError("FileNameLength.FileNameLength is not a multiple of 2");
893N/A }
893N/A char[] nameAsArray = new char[nameLengthInBytes/2];
893N/A unsafe.copyMemory(null, address + OFFSETOF_FILENAME, nameAsArray,
893N/A Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes);
893N/A
893N/A // create FileName and queue event
893N/A WindowsPath name = WindowsPath
893N/A .createFromNormalizedPath(fs, new String(nameAsArray));
893N/A key.signalEvent(kind, name);
893N/A }
893N/A
893N/A // next event
893N/A nextOffset = unsafe.getInt(address + OFFSETOF_NEXTENTRYOFFSET);
893N/A address += (long)nextOffset;
893N/A } while (nextOffset != 0);
893N/A }
893N/A
893N/A /**
893N/A * Poller main loop
893N/A */
893N/A @Override
893N/A public void run() {
893N/A for (;;) {
893N/A CompletionStatus info = null;
893N/A try {
893N/A info = GetQueuedCompletionStatus(port);
893N/A } catch (WindowsException x) {
893N/A // this should not happen
893N/A x.printStackTrace();
893N/A return;
893N/A }
893N/A
893N/A // wakeup
893N/A if (info.completionKey() == 0) {
893N/A boolean shutdown = processRequests();
893N/A if (shutdown) {
893N/A return;
893N/A }
893N/A continue;
893N/A }
893N/A
893N/A // map completionKey to get WatchKey
893N/A WindowsWatchKey key = int2key.get(info.completionKey());
893N/A if (key == null) {
893N/A // We get here when a registration is changed. In that case
893N/A // the directory is closed which causes an event with the
893N/A // old completion key.
893N/A continue;
893N/A }
893N/A
893N/A // ReadDirectoryChangesW failed
893N/A if (info.error() != 0) {
893N/A // buffer overflow
893N/A if (info.error() == ERROR_NOTIFY_ENUM_DIR) {
4216N/A key.signalEvent(StandardWatchEventKinds.OVERFLOW, null);
893N/A } else {
893N/A // other error so cancel key
893N/A implCancelKey(key);
893N/A key.signal();
893N/A }
893N/A continue;
893N/A }
893N/A
893N/A // process the events
893N/A if (info.bytesTransferred() > 0) {
893N/A processEvents(key, info.bytesTransferred());
893N/A } else {
893N/A // insufficient buffer size
4216N/A key.signalEvent(StandardWatchEventKinds.OVERFLOW, null);
893N/A }
893N/A
893N/A // start read for next batch of changes
893N/A try {
893N/A ReadDirectoryChangesW(key.handle(),
893N/A key.buffer().address(),
893N/A CHANGES_BUFFER_SIZE,
893N/A key.watchSubtree(),
893N/A ALL_FILE_NOTIFY_EVENTS,
893N/A key.countAddress(),
893N/A key.overlappedAddress());
893N/A } catch (WindowsException x) {
893N/A // no choice but to cancel key
893N/A implCancelKey(key);
893N/A key.signal();
893N/A }
893N/A }
893N/A }
893N/A }
893N/A}