1 /* 2 * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.nio.fs; 27 28 import java.io.IOException; 29 import java.nio.file.NotDirectoryException; 30 import java.nio.file.Path; 31 import java.nio.file.StandardWatchEventKinds; 32 import java.nio.file.WatchEvent; 33 import java.nio.file.WatchKey; 34 import java.util.HashMap; 35 import java.util.Map; 36 import java.util.Set; 37 38 import com.sun.nio.file.ExtendedWatchEventModifier; 39 import jdk.internal.misc.Unsafe; 40 41 import static sun.nio.fs.WindowsNativeDispatcher.*; 42 import static sun.nio.fs.WindowsConstants.*; 43 44 /* 45 * Win32 implementation of WatchService based on ReadDirectoryChangesW. 46 */ 47 48 class WindowsWatchService 49 extends AbstractWatchService 50 { 51 private static final int WAKEUP_COMPLETION_KEY = 0; 52 53 // background thread to service I/O completion port 54 private final Poller poller; 55 56 /** 57 * Creates an I/O completion port and a daemon thread to service it 58 */ 59 WindowsWatchService(WindowsFileSystem fs) throws IOException { 60 // create I/O completion port 61 long port = 0L; 62 try { 63 port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0); 64 } catch (WindowsException x) { 65 throw new IOException(x.getMessage()); 66 } 67 68 this.poller = new Poller(fs, this, port); 69 this.poller.start(); 70 } 71 72 @Override 73 WatchKey register(Path path, 74 WatchEvent.Kind<?>[] events, 75 WatchEvent.Modifier... modifiers) 76 throws IOException 77 { 78 // delegate to poller 79 return poller.register(path, events, modifiers); 80 } 81 82 @Override 83 void implClose() throws IOException { 84 // delegate to poller 85 poller.close(); 86 } 87 88 /** 89 * Windows implementation of WatchKey. 90 */ 91 private static class WindowsWatchKey extends AbstractWatchKey { 92 // file key (used to detect existing registrations) 93 private final FileKey fileKey; 94 95 // handle to directory 96 private volatile long handle = INVALID_HANDLE_VALUE; 97 98 // interest events 99 private Set<? extends WatchEvent.Kind<?>> events; 100 101 // subtree 102 private boolean watchSubtree; 103 104 // buffer for change events 105 private NativeBuffer buffer; 106 107 // pointer to bytes returned (in buffer) 108 private long countAddress; 109 110 // pointer to overlapped structure (in buffer) 111 private long overlappedAddress; 112 113 // completion key (used to map I/O completion to WatchKey) 114 private int completionKey; 115 116 // flag indicates that ReadDirectoryChangesW failed 117 // and overlapped I/O operation wasn't started 118 private boolean errorStartingOverlapped; 119 120 WindowsWatchKey(Path dir, 121 AbstractWatchService watcher, 122 FileKey fileKey) 123 { 124 super(dir, watcher); 125 this.fileKey = fileKey; 126 } 127 128 WindowsWatchKey init(long handle, 129 Set<? extends WatchEvent.Kind<?>> events, 130 boolean watchSubtree, 131 NativeBuffer buffer, 132 long countAddress, 133 long overlappedAddress, 134 int completionKey) 135 { 136 this.handle = handle; 137 this.events = events; 138 this.watchSubtree = watchSubtree; 139 this.buffer = buffer; 140 this.countAddress = countAddress; 141 this.overlappedAddress = overlappedAddress; 142 this.completionKey = completionKey; 143 return this; 144 } 145 146 long handle() { 147 return handle; 148 } 149 150 Set<? extends WatchEvent.Kind<?>> events() { 151 return events; 152 } 153 154 void setEvents(Set<? extends WatchEvent.Kind<?>> events) { 155 this.events = events; 156 } 157 158 boolean watchSubtree() { 159 return watchSubtree; 160 } 161 162 NativeBuffer buffer() { 163 return buffer; 164 } 165 166 long countAddress() { 167 return countAddress; 168 } 169 170 long overlappedAddress() { 171 return overlappedAddress; 172 } 173 174 FileKey fileKey() { 175 return fileKey; 176 } 177 178 int completionKey() { 179 return completionKey; 180 } 181 182 void setErrorStartingOverlapped(boolean value) { 183 errorStartingOverlapped = value; 184 } 185 186 boolean isErrorStartingOverlapped() { 187 return errorStartingOverlapped; 188 } 189 190 // Invalidate the key, assumes that resources have been released 191 void invalidate() { 192 ((WindowsWatchService)watcher()).poller.releaseResources(this); 193 handle = INVALID_HANDLE_VALUE; 194 buffer = null; 195 countAddress = 0; 196 overlappedAddress = 0; 197 errorStartingOverlapped = false; 198 } 199 200 @Override 201 public boolean isValid() { 202 return handle != INVALID_HANDLE_VALUE; 203 } 204 205 @Override 206 public void cancel() { 207 if (isValid()) { 208 // delegate to poller 209 ((WindowsWatchService)watcher()).poller.cancel(this); 210 } 211 } 212 } 213 214 // file key to unique identify (open) directory 215 private static class FileKey { 216 private final int volSerialNumber; 217 private final int fileIndexHigh; 218 private final int fileIndexLow; 219 220 FileKey(int volSerialNumber, int fileIndexHigh, int fileIndexLow) { 221 this.volSerialNumber = volSerialNumber; 222 this.fileIndexHigh = fileIndexHigh; 223 this.fileIndexLow = fileIndexLow; 224 } 225 226 @Override 227 public int hashCode() { 228 return volSerialNumber ^ fileIndexHigh ^ fileIndexLow; 229 } 230 231 @Override 232 public boolean equals(Object obj) { 233 if (obj == this) 234 return true; 235 if (!(obj instanceof FileKey)) 236 return false; 237 FileKey other = (FileKey)obj; 238 if (this.volSerialNumber != other.volSerialNumber) return false; 239 if (this.fileIndexHigh != other.fileIndexHigh) return false; 240 return this.fileIndexLow == other.fileIndexLow; 241 } 242 } 243 244 // all change events 245 private static final int ALL_FILE_NOTIFY_EVENTS = 246 FILE_NOTIFY_CHANGE_FILE_NAME | 247 FILE_NOTIFY_CHANGE_DIR_NAME | 248 FILE_NOTIFY_CHANGE_ATTRIBUTES | 249 FILE_NOTIFY_CHANGE_SIZE | 250 FILE_NOTIFY_CHANGE_LAST_WRITE | 251 FILE_NOTIFY_CHANGE_CREATION | 252 FILE_NOTIFY_CHANGE_SECURITY; 253 254 /** 255 * Background thread to service I/O completion port. 256 */ 257 private static class Poller extends AbstractPoller { 258 private static final Unsafe UNSAFE = Unsafe.getUnsafe(); 259 260 /* 261 * typedef struct _OVERLAPPED { 262 * ULONG_PTR Internal; 263 * ULONG_PTR InternalHigh; 264 * union { 265 * struct { DWORD Offset; DWORD OffsetHigh; }; 266 * PVOID Pointer; 267 * }; 268 * HANDLE hEvent; 269 * } OVERLAPPED; 270 */ 271 private static final short SIZEOF_DWORD = 4; 272 private static final short SIZEOF_OVERLAPPED = 32; // 20 on 32-bit 273 private static final short OFFSETOF_HEVENT = 274 (UNSAFE.addressSize() == 4) ? (short) 16 : 24; 275 276 277 /* 278 * typedef struct _FILE_NOTIFY_INFORMATION { 279 * DWORD NextEntryOffset; 280 * DWORD Action; 281 * DWORD FileNameLength; 282 * WCHAR FileName[1]; 283 * } FileNameLength; 284 */ 285 private static final short OFFSETOF_NEXTENTRYOFFSET = 0; 286 private static final short OFFSETOF_ACTION = 4; 287 private static final short OFFSETOF_FILENAMELENGTH = 8; 288 private static final short OFFSETOF_FILENAME = 12; 289 290 // size of per-directory buffer for events (FIXME - make this configurable) 291 // Need to be less than 4*16384 = 65536. DWORD align. 292 private static final int CHANGES_BUFFER_SIZE = 16 * 1024; 293 294 private final WindowsFileSystem fs; 295 private final WindowsWatchService watcher; 296 private final long port; 297 298 // maps completion key to WatchKey 299 private final Map<Integer, WindowsWatchKey> ck2key; 300 301 // maps file key to WatchKey 302 private final Map<FileKey, WindowsWatchKey> fk2key; 303 304 // unique completion key for each directory 305 // native completion key capacity is 64 bits on Win64. 306 private int lastCompletionKey; 307 308 Poller(WindowsFileSystem fs, WindowsWatchService watcher, long port) { 309 this.fs = fs; 310 this.watcher = watcher; 311 this.port = port; 312 this.ck2key = new HashMap<>(); 313 this.fk2key = new HashMap<>(); 314 this.lastCompletionKey = 0; 315 } 316 317 @Override 318 void wakeup() throws IOException { 319 try { 320 PostQueuedCompletionStatus(port, WAKEUP_COMPLETION_KEY); 321 } catch (WindowsException x) { 322 throw new IOException(x.getMessage()); 323 } 324 } 325 326 /** 327 * Register a directory for changes as follows: 328 * 329 * 1. Open directory 330 * 2. Read its attributes (and check it really is a directory) 331 * 3. Assign completion key and associated handle with completion port 332 * 4. Call ReadDirectoryChangesW to start (async) read of changes 333 * 5. Create or return existing key representing registration 334 */ 335 @Override 336 Object implRegister(Path obj, 337 Set<? extends WatchEvent.Kind<?>> events, 338 WatchEvent.Modifier... modifiers) 339 { 340 WindowsPath dir = (WindowsPath)obj; 341 boolean watchSubtree = false; 342 343 // FILE_TREE modifier allowed 344 for (WatchEvent.Modifier modifier: modifiers) { 345 if (modifier == ExtendedWatchEventModifier.FILE_TREE) { 346 watchSubtree = true; 347 } else { 348 if (modifier == null) 349 return new NullPointerException(); 350 if (modifier instanceof com.sun.nio.file.SensitivityWatchEventModifier) 351 continue; // ignore 352 return new UnsupportedOperationException("Modifier not supported"); 353 } 354 } 355 356 // open directory 357 long handle; 358 try { 359 handle = CreateFile(dir.getPathForWin32Calls(), 360 FILE_LIST_DIRECTORY, 361 (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), 362 OPEN_EXISTING, 363 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED); 364 } catch (WindowsException x) { 365 return x.asIOException(dir); 366 } 367 368 boolean registered = false; 369 try { 370 // read attributes and check file is a directory 371 WindowsFileAttributes attrs; 372 try { 373 attrs = WindowsFileAttributes.readAttributes(handle); 374 } catch (WindowsException x) { 375 return x.asIOException(dir); 376 } 377 if (!attrs.isDirectory()) { 378 return new NotDirectoryException(dir.getPathForExceptionMessage()); 379 } 380 381 // check if this directory is already registered 382 FileKey fk = new FileKey(attrs.volSerialNumber(), 383 attrs.fileIndexHigh(), 384 attrs.fileIndexLow()); 385 WindowsWatchKey existing = fk2key.get(fk); 386 387 // if already registered and we're not changing the subtree 388 // modifier then simply update the event and return the key. 389 if (existing != null && watchSubtree == existing.watchSubtree()) { 390 existing.setEvents(events); 391 return existing; 392 } 393 394 // Can overflow the int type capacity. 395 // Skip WAKEUP_COMPLETION_KEY value. 396 int completionKey = ++lastCompletionKey; 397 if (completionKey == WAKEUP_COMPLETION_KEY) 398 completionKey = ++lastCompletionKey; 399 400 // associate handle with completion port 401 try { 402 CreateIoCompletionPort(handle, port, completionKey); 403 } catch (WindowsException x) { 404 return new IOException(x.getMessage()); 405 } 406 407 // allocate memory for events, including space for other structures 408 // needed to do overlapped I/O 409 int size = CHANGES_BUFFER_SIZE + SIZEOF_DWORD + SIZEOF_OVERLAPPED; 410 NativeBuffer buffer = NativeBuffers.getNativeBuffer(size); 411 412 long bufferAddress = buffer.address(); 413 long overlappedAddress = bufferAddress + size - SIZEOF_OVERLAPPED; 414 long countAddress = overlappedAddress - SIZEOF_DWORD; 415 416 // zero the overlapped structure 417 UNSAFE.setMemory(overlappedAddress, SIZEOF_OVERLAPPED, (byte)0); 418 419 // start async read of changes to directory 420 try { 421 createAndAttachEvent(overlappedAddress); 422 423 ReadDirectoryChangesW(handle, 424 bufferAddress, 425 CHANGES_BUFFER_SIZE, 426 watchSubtree, 427 ALL_FILE_NOTIFY_EVENTS, 428 countAddress, 429 overlappedAddress); 430 } catch (WindowsException x) { 431 closeAttachedEvent(overlappedAddress); 432 buffer.release(); 433 return new IOException(x.getMessage()); 434 } 435 436 WindowsWatchKey watchKey; 437 if (existing == null) { 438 // not registered so create new watch key 439 watchKey = new WindowsWatchKey(dir, watcher, fk) 440 .init(handle, events, watchSubtree, buffer, countAddress, 441 overlappedAddress, completionKey); 442 // map file key to watch key 443 fk2key.put(fk, watchKey); 444 } else { 445 // directory already registered so need to: 446 // 1. remove mapping from old completion key to existing watch key 447 // 2. release existing key's resources (handle/buffer) 448 // 3. re-initialize key with new handle/buffer 449 ck2key.remove(existing.completionKey()); 450 releaseResources(existing); 451 watchKey = existing.init(handle, events, watchSubtree, buffer, 452 countAddress, overlappedAddress, completionKey); 453 } 454 // map completion map to watch key 455 ck2key.put(completionKey, watchKey); 456 457 registered = true; 458 return watchKey; 459 460 } finally { 461 if (!registered) CloseHandle(handle); 462 } 463 } 464 465 /** 466 * Cancels the outstanding I/O operation on the directory 467 * associated with the given key and releases the associated 468 * resources. 469 */ 470 private void releaseResources(WindowsWatchKey key) { 471 if (!key.isErrorStartingOverlapped()) { 472 try { 473 CancelIo(key.handle()); 474 GetOverlappedResult(key.handle(), key.overlappedAddress()); 475 } catch (WindowsException expected) { 476 // expected as I/O operation has been cancelled 477 } 478 } 479 CloseHandle(key.handle()); 480 closeAttachedEvent(key.overlappedAddress()); 481 key.buffer().free(); 482 } 483 484 /** 485 * Creates an unnamed event and set it as the hEvent field 486 * in the given OVERLAPPED structure 487 */ 488 private void createAndAttachEvent(long ov) throws WindowsException { 489 long hEvent = CreateEvent(false, false); 490 UNSAFE.putAddress(ov + OFFSETOF_HEVENT, hEvent); 491 } 492 493 /** 494 * Closes the event attached to the given OVERLAPPED structure. A 495 * no-op if there isn't an event attached. 496 */ 497 private void closeAttachedEvent(long ov) { 498 long hEvent = UNSAFE.getAddress(ov + OFFSETOF_HEVENT); 499 if (hEvent != 0 && hEvent != INVALID_HANDLE_VALUE) 500 CloseHandle(hEvent); 501 } 502 503 // cancel single key 504 @Override 505 void implCancelKey(WatchKey obj) { 506 WindowsWatchKey key = (WindowsWatchKey)obj; 507 if (key.isValid()) { 508 fk2key.remove(key.fileKey()); 509 ck2key.remove(key.completionKey()); 510 key.invalidate(); 511 } 512 } 513 514 // close watch service 515 @Override 516 void implCloseAll() { 517 // cancel all keys 518 ck2key.values().forEach(WindowsWatchKey::invalidate); 519 520 fk2key.clear(); 521 ck2key.clear(); 522 523 // close I/O completion port 524 CloseHandle(port); 525 } 526 527 // Translate file change action into watch event 528 private WatchEvent.Kind<?> translateActionToEvent(int action) { 529 switch (action) { 530 case FILE_ACTION_MODIFIED : 531 return StandardWatchEventKinds.ENTRY_MODIFY; 532 533 case FILE_ACTION_ADDED : 534 case FILE_ACTION_RENAMED_NEW_NAME : 535 return StandardWatchEventKinds.ENTRY_CREATE; 536 537 case FILE_ACTION_REMOVED : 538 case FILE_ACTION_RENAMED_OLD_NAME : 539 return StandardWatchEventKinds.ENTRY_DELETE; 540 541 default : 542 return null; // action not recognized 543 } 544 } 545 546 // process events (list of FILE_NOTIFY_INFORMATION structures) 547 private void processEvents(WindowsWatchKey key, int size) { 548 long address = key.buffer().address(); 549 550 int nextOffset; 551 do { 552 int action = UNSAFE.getInt(address + OFFSETOF_ACTION); 553 554 // map action to event 555 WatchEvent.Kind<?> kind = translateActionToEvent(action); 556 if (key.events().contains(kind)) { 557 // copy the name 558 int nameLengthInBytes = UNSAFE.getInt(address + OFFSETOF_FILENAMELENGTH); 559 if ((nameLengthInBytes % 2) != 0) { 560 throw new AssertionError("FileNameLength is not a multiple of 2"); 561 } 562 char[] nameAsArray = new char[nameLengthInBytes/2]; 563 UNSAFE.copyMemory(null, address + OFFSETOF_FILENAME, nameAsArray, 564 Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes); 565 566 // create FileName and queue event 567 WindowsPath name = WindowsPath 568 .createFromNormalizedPath(fs, new String(nameAsArray)); 569 key.signalEvent(kind, name); 570 } 571 572 // next event 573 nextOffset = UNSAFE.getInt(address + OFFSETOF_NEXTENTRYOFFSET); 574 address += (long)nextOffset; 575 } while (nextOffset != 0); 576 } 577 578 /** 579 * Poller main loop 580 */ 581 @Override 582 public void run() { 583 for (;;) { 584 CompletionStatus info; 585 try { 586 info = GetQueuedCompletionStatus(port); 587 } catch (WindowsException x) { 588 // this should not happen 589 x.printStackTrace(); 590 return; 591 } 592 593 // wakeup 594 if (info.completionKey() == WAKEUP_COMPLETION_KEY) { 595 boolean shutdown = processRequests(); 596 if (shutdown) { 597 return; 598 } 599 continue; 600 } 601 602 // map completionKey to get WatchKey 603 WindowsWatchKey key = ck2key.get((int)info.completionKey()); 604 if (key == null) { 605 // We get here when a registration is changed. In that case 606 // the directory is closed which causes an event with the 607 // old completion key. 608 continue; 609 } 610 611 boolean criticalError = false; 612 int errorCode = info.error(); 613 int messageSize = info.bytesTransferred(); 614 if (errorCode == ERROR_NOTIFY_ENUM_DIR) { 615 // buffer overflow 616 key.signalEvent(StandardWatchEventKinds.OVERFLOW, null); 617 } else if (errorCode != 0 && errorCode != ERROR_MORE_DATA) { 618 // ReadDirectoryChangesW failed 619 criticalError = true; 620 } else { 621 // ERROR_MORE_DATA is a warning about incomplete 622 // data transfer over TCP/UDP stack. For the case 623 // [messageSize] is zero in the most of cases. 624 625 if (messageSize > 0) { 626 // process non-empty events. 627 processEvents(key, messageSize); 628 } else if (errorCode == 0) { 629 // insufficient buffer size 630 // not described, but can happen. 631 key.signalEvent(StandardWatchEventKinds.OVERFLOW, null); 632 } 633 634 // start read for next batch of changes 635 try { 636 ReadDirectoryChangesW(key.handle(), 637 key.buffer().address(), 638 CHANGES_BUFFER_SIZE, 639 key.watchSubtree(), 640 ALL_FILE_NOTIFY_EVENTS, 641 key.countAddress(), 642 key.overlappedAddress()); 643 } catch (WindowsException x) { 644 // no choice but to cancel key 645 criticalError = true; 646 key.setErrorStartingOverlapped(true); 647 } 648 } 649 if (criticalError) { 650 implCancelKey(key); 651 key.signal(); 652 } 653 } 654 } 655 } 656 }