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 WindowsWatchKey(Path dir, 117 AbstractWatchService watcher, 118 FileKey fileKey) 119 { 120 super(dir, watcher); 121 this.fileKey = fileKey; 122 } 123 124 WindowsWatchKey init(long handle, 125 Set<? extends WatchEvent.Kind<?>> events, 126 boolean watchSubtree, 127 NativeBuffer buffer, 128 long countAddress, 129 long overlappedAddress, 130 int completionKey) 131 { 132 this.handle = handle; 133 this.events = events; 134 this.watchSubtree = watchSubtree; 135 this.buffer = buffer; 136 this.countAddress = countAddress; 137 this.overlappedAddress = overlappedAddress; 138 this.completionKey = completionKey; 139 return this; 140 } 141 142 long handle() { 143 return handle; 144 } 145 146 Set<? extends WatchEvent.Kind<?>> events() { 147 return events; 148 } 149 150 void setEvents(Set<? extends WatchEvent.Kind<?>> events) { 151 this.events = events; 152 } 153 154 boolean watchSubtree() { 155 return watchSubtree; 156 } 157 158 NativeBuffer buffer() { 159 return buffer; 160 } 161 162 long countAddress() { 163 return countAddress; 164 } 165 166 long overlappedAddress() { 167 return overlappedAddress; 168 } 169 170 FileKey fileKey() { 171 return fileKey; 172 } 173 174 int completionKey() { 175 return completionKey; 176 } 177 178 // Invalidate the key, assumes that resources have been released 179 void invalidate() { 180 ((WindowsWatchService)watcher()).poller.releaseResources(this); 181 handle = INVALID_HANDLE_VALUE; 182 buffer = null; 183 countAddress = 0; 184 overlappedAddress = 0; 185 } 186 187 @Override 188 public boolean isValid() { 189 return handle != INVALID_HANDLE_VALUE; 190 } 191 192 @Override 193 public void cancel() { 194 if (isValid()) { 195 // delegate to poller 196 ((WindowsWatchService)watcher()).poller.cancel(this); 197 } 198 } 199 } 200 201 // file key to unique identify (open) directory 202 private static class FileKey { 203 private final int volSerialNumber; 204 private final int fileIndexHigh; 205 private final int fileIndexLow; 206 207 FileKey(int volSerialNumber, int fileIndexHigh, int fileIndexLow) { 208 this.volSerialNumber = volSerialNumber; 209 this.fileIndexHigh = fileIndexHigh; 210 this.fileIndexLow = fileIndexLow; 211 } 212 213 @Override 214 public int hashCode() { 215 return volSerialNumber ^ fileIndexHigh ^ fileIndexLow; 216 } 217 218 @Override 219 public boolean equals(Object obj) { 220 if (obj == this) 221 return true; 222 if (!(obj instanceof FileKey)) 223 return false; 224 FileKey other = (FileKey)obj; 225 if (this.volSerialNumber != other.volSerialNumber) return false; 226 if (this.fileIndexHigh != other.fileIndexHigh) return false; 227 return this.fileIndexLow == other.fileIndexLow; 228 } 229 } 230 231 // all change events 232 private static final int ALL_FILE_NOTIFY_EVENTS = 233 FILE_NOTIFY_CHANGE_FILE_NAME | 234 FILE_NOTIFY_CHANGE_DIR_NAME | 235 FILE_NOTIFY_CHANGE_ATTRIBUTES | 236 FILE_NOTIFY_CHANGE_SIZE | 237 FILE_NOTIFY_CHANGE_LAST_WRITE | 238 FILE_NOTIFY_CHANGE_CREATION | 239 FILE_NOTIFY_CHANGE_SECURITY; 240 241 /** 242 * Background thread to service I/O completion port. 243 */ 244 private static class Poller extends AbstractPoller { 245 private static final Unsafe UNSAFE = Unsafe.getUnsafe(); 246 247 /* 248 * typedef struct _OVERLAPPED { 249 * ULONG_PTR Internal; 250 * ULONG_PTR InternalHigh; 251 * union { 252 * struct { DWORD Offset; DWORD OffsetHigh; }; 253 * PVOID Pointer; 254 * }; 255 * HANDLE hEvent; 256 * } OVERLAPPED; 257 */ 258 private static final short SIZEOF_DWORD = 4; 259 private static final short SIZEOF_OVERLAPPED = 32; // 20 on 32-bit 260 private static final short OFFSETOF_HEVENT = 261 (UNSAFE.addressSize() == 4) ? (short) 16 : 24; 262 263 264 /* 265 * typedef struct _FILE_NOTIFY_INFORMATION { 266 * DWORD NextEntryOffset; 267 * DWORD Action; 268 * DWORD FileNameLength; 269 * WCHAR FileName[1]; 270 * } FileNameLength; 271 */ 272 private static final short OFFSETOF_NEXTENTRYOFFSET = 0; 273 private static final short OFFSETOF_ACTION = 4; 274 private static final short OFFSETOF_FILENAMELENGTH = 8; 275 private static final short OFFSETOF_FILENAME = 12; 276 277 // size of per-directory buffer for events (FIXME - make this configurable) 278 // Need to be less than 4*16384 = 65536. DWORD align. 279 private static final int CHANGES_BUFFER_SIZE = 16 * 1024; 280 281 private final WindowsFileSystem fs; 282 private final WindowsWatchService watcher; 283 private final long port; 284 285 // maps completion key to WatchKey 286 private final Map<Integer, WindowsWatchKey> ck2key; 287 288 // maps file key to WatchKey 289 private final Map<FileKey, WindowsWatchKey> fk2key; 290 291 // unique completion key for each directory 292 // native completion key capacity is 64 bits on Win64. 293 private int lastCompletionKey; 294 295 Poller(WindowsFileSystem fs, WindowsWatchService watcher, long port) { 296 this.fs = fs; 297 this.watcher = watcher; 298 this.port = port; 299 this.ck2key = new HashMap<>(); 300 this.fk2key = new HashMap<>(); 301 this.lastCompletionKey = 0; 302 } 303 304 @Override 305 void wakeup() throws IOException { 306 try { 307 PostQueuedCompletionStatus(port, WAKEUP_COMPLETION_KEY); 308 } catch (WindowsException x) { 309 throw new IOException(x.getMessage()); 310 } 311 } 312 313 /** 314 * Register a directory for changes as follows: 315 * 316 * 1. Open directory 317 * 2. Read its attributes (and check it really is a directory) 318 * 3. Assign completion key and associated handle with completion port 319 * 4. Call ReadDirectoryChangesW to start (async) read of changes 320 * 5. Create or return existing key representing registration 321 */ 322 @Override 323 Object implRegister(Path obj, 324 Set<? extends WatchEvent.Kind<?>> events, 325 WatchEvent.Modifier... modifiers) 326 { 327 WindowsPath dir = (WindowsPath)obj; 328 boolean watchSubtree = false; 329 330 // FILE_TREE modifier allowed 331 for (WatchEvent.Modifier modifier: modifiers) { 332 if (modifier == ExtendedWatchEventModifier.FILE_TREE) { 333 watchSubtree = true; 334 } else { 335 if (modifier == null) 336 return new NullPointerException(); 337 if (modifier instanceof com.sun.nio.file.SensitivityWatchEventModifier) 338 continue; // ignore 339 return new UnsupportedOperationException("Modifier not supported"); 340 } 341 } 342 343 // open directory 344 long handle; 345 try { 346 handle = CreateFile(dir.getPathForWin32Calls(), 347 FILE_LIST_DIRECTORY, 348 (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), 349 OPEN_EXISTING, 350 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED); 351 } catch (WindowsException x) { 352 return x.asIOException(dir); 353 } 354 355 boolean registered = false; 356 try { 357 // read attributes and check file is a directory 358 WindowsFileAttributes attrs; 359 try { 360 attrs = WindowsFileAttributes.readAttributes(handle); 361 } catch (WindowsException x) { 362 return x.asIOException(dir); 363 } 364 if (!attrs.isDirectory()) { 365 return new NotDirectoryException(dir.getPathForExceptionMessage()); 366 } 367 368 // check if this directory is already registered 369 FileKey fk = new FileKey(attrs.volSerialNumber(), 370 attrs.fileIndexHigh(), 371 attrs.fileIndexLow()); 372 WindowsWatchKey existing = fk2key.get(fk); 373 374 // if already registered and we're not changing the subtree 375 // modifier then simply update the event and return the key. 376 if (existing != null && watchSubtree == existing.watchSubtree()) { 377 existing.setEvents(events); 378 return existing; 379 } 380 381 // Can overflow the int type capacity. 382 // Skip WAKEUP_COMPLETION_KEY value. 383 int completionKey = ++lastCompletionKey; 384 if (completionKey == WAKEUP_COMPLETION_KEY) 385 completionKey = ++lastCompletionKey; 386 387 // associate handle with completion port 388 try { 389 CreateIoCompletionPort(handle, port, completionKey); 390 } catch (WindowsException x) { 391 return new IOException(x.getMessage()); 392 } 393 394 // allocate memory for events, including space for other structures 395 // needed to do overlapped I/O 396 int size = CHANGES_BUFFER_SIZE + SIZEOF_DWORD + SIZEOF_OVERLAPPED; 397 NativeBuffer buffer = NativeBuffers.getNativeBuffer(size); 398 399 long bufferAddress = buffer.address(); 400 long overlappedAddress = bufferAddress + size - SIZEOF_OVERLAPPED; 401 long countAddress = overlappedAddress - SIZEOF_DWORD; 402 403 // zero the overlapped structure 404 UNSAFE.setMemory(overlappedAddress, SIZEOF_OVERLAPPED, (byte)0); 405 406 // start async read of changes to directory 407 try { 408 createAndAttachEvent(overlappedAddress); 409 410 ReadDirectoryChangesW(handle, 411 bufferAddress, 412 CHANGES_BUFFER_SIZE, 413 watchSubtree, 414 ALL_FILE_NOTIFY_EVENTS, 415 countAddress, 416 overlappedAddress); 417 } catch (WindowsException x) { 418 closeAttachedEvent(overlappedAddress); 419 buffer.release(); 420 return new IOException(x.getMessage()); 421 } 422 423 WindowsWatchKey watchKey; 424 if (existing == null) { 425 // not registered so create new watch key 426 watchKey = new WindowsWatchKey(dir, watcher, fk) 427 .init(handle, events, watchSubtree, buffer, countAddress, 428 overlappedAddress, completionKey); 429 // map file key to watch key 430 fk2key.put(fk, watchKey); 431 } else { 432 // directory already registered so need to: 433 // 1. remove mapping from old completion key to existing watch key 434 // 2. release existing key's resources (handle/buffer) 435 // 3. re-initialize key with new handle/buffer 436 ck2key.remove(existing.completionKey()); 437 releaseResources(existing); 438 watchKey = existing.init(handle, events, watchSubtree, buffer, 439 countAddress, overlappedAddress, completionKey); 440 } 441 // map completion map to watch key 442 ck2key.put(completionKey, watchKey); 443 444 registered = true; 445 return watchKey; 446 447 } finally { 448 if (!registered) CloseHandle(handle); 449 } 450 } 451 452 /** 453 * Cancels the outstanding I/O operation on the directory 454 * associated with the given key and releases the associated 455 * resources. 456 */ 457 private void releaseResources(WindowsWatchKey key) { 458 try { 459 CancelIo(key.handle()); 460 GetOverlappedResult(key.handle(), key.overlappedAddress()); 461 } catch (WindowsException expected) { 462 // expected as I/O operation has been cancelled 463 } 464 CloseHandle(key.handle()); 465 closeAttachedEvent(key.overlappedAddress()); 466 key.buffer().cleaner().clean(); 467 } 468 469 /** 470 * Creates an unnamed event and set it as the hEvent field 471 * in the given OVERLAPPED structure 472 */ 473 private void createAndAttachEvent(long ov) throws WindowsException { 474 long hEvent = CreateEvent(false, false); 475 UNSAFE.putAddress(ov + OFFSETOF_HEVENT, hEvent); 476 } 477 478 /** 479 * Closes the event attached to the given OVERLAPPED structure. A 480 * no-op if there isn't an event attached. 481 */ 482 private void closeAttachedEvent(long ov) { 483 long hEvent = UNSAFE.getAddress(ov + OFFSETOF_HEVENT); 484 if (hEvent != 0 && hEvent != INVALID_HANDLE_VALUE) 485 CloseHandle(hEvent); 486 } 487 488 // cancel single key 489 @Override 490 void implCancelKey(WatchKey obj) { 491 WindowsWatchKey key = (WindowsWatchKey)obj; 492 if (key.isValid()) { 493 fk2key.remove(key.fileKey()); 494 ck2key.remove(key.completionKey()); 495 key.invalidate(); 496 } 497 } 498 499 // close watch service 500 @Override 501 void implCloseAll() { 502 // cancel all keys 503 ck2key.values().forEach(WindowsWatchKey::invalidate); 504 505 fk2key.clear(); 506 ck2key.clear(); 507 508 // close I/O completion port 509 CloseHandle(port); 510 } 511 512 // Translate file change action into watch event 513 private WatchEvent.Kind<?> translateActionToEvent(int action) { 514 switch (action) { 515 case FILE_ACTION_MODIFIED : 516 return StandardWatchEventKinds.ENTRY_MODIFY; 517 518 case FILE_ACTION_ADDED : 519 case FILE_ACTION_RENAMED_NEW_NAME : 520 return StandardWatchEventKinds.ENTRY_CREATE; 521 522 case FILE_ACTION_REMOVED : 523 case FILE_ACTION_RENAMED_OLD_NAME : 524 return StandardWatchEventKinds.ENTRY_DELETE; 525 526 default : 527 return null; // action not recognized 528 } 529 } 530 531 // process events (list of FILE_NOTIFY_INFORMATION structures) 532 private void processEvents(WindowsWatchKey key, int size) { 533 long address = key.buffer().address(); 534 535 int nextOffset; 536 do { 537 int action = UNSAFE.getInt(address + OFFSETOF_ACTION); 538 539 // map action to event 540 WatchEvent.Kind<?> kind = translateActionToEvent(action); 541 if (key.events().contains(kind)) { 542 // copy the name 543 int nameLengthInBytes = UNSAFE.getInt(address + OFFSETOF_FILENAMELENGTH); 544 if ((nameLengthInBytes % 2) != 0) { 545 throw new AssertionError("FileNameLength is not a multiple of 2"); 546 } 547 char[] nameAsArray = new char[nameLengthInBytes/2]; 548 UNSAFE.copyMemory(null, address + OFFSETOF_FILENAME, nameAsArray, 549 Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes); 550 551 // create FileName and queue event 552 WindowsPath name = WindowsPath 553 .createFromNormalizedPath(fs, new String(nameAsArray)); 554 key.signalEvent(kind, name); 555 } 556 557 // next event 558 nextOffset = UNSAFE.getInt(address + OFFSETOF_NEXTENTRYOFFSET); 559 address += (long)nextOffset; 560 } while (nextOffset != 0); 561 } 562 563 /** 564 * Poller main loop 565 */ 566 @Override 567 public void run() { 568 for (;;) { 569 CompletionStatus info; 570 try { 571 info = GetQueuedCompletionStatus(port); 572 } catch (WindowsException x) { 573 // this should not happen 574 x.printStackTrace(); 575 return; 576 } 577 578 // wakeup 579 if (info.completionKey() == WAKEUP_COMPLETION_KEY) { 580 boolean shutdown = processRequests(); 581 if (shutdown) { 582 return; 583 } 584 continue; 585 } 586 587 // map completionKey to get WatchKey 588 WindowsWatchKey key = ck2key.get((int)info.completionKey()); 589 if (key == null) { 590 // We get here when a registration is changed. In that case 591 // the directory is closed which causes an event with the 592 // old completion key. 593 continue; 594 } 595 596 boolean criticalError = false; 597 int errorCode = info.error(); 598 int messageSize = info.bytesTransferred(); 599 if (errorCode == ERROR_NOTIFY_ENUM_DIR) { 600 // buffer overflow 601 key.signalEvent(StandardWatchEventKinds.OVERFLOW, null); 602 } else if (errorCode != 0 && errorCode != ERROR_MORE_DATA) { 603 // ReadDirectoryChangesW failed 604 criticalError = true; 605 } else { 606 // ERROR_MORE_DATA is a warning about incomplete 607 // data transfer over TCP/UDP stack. For the case 608 // [messageSize] is zero in the most of cases. 609 610 if (messageSize > 0) { 611 // process non-empty events. 612 processEvents(key, messageSize); 613 } else if (errorCode == 0) { 614 // insufficient buffer size 615 // not described, but can happen. 616 key.signalEvent(StandardWatchEventKinds.OVERFLOW, null); 617 } 618 619 // start read for next batch of changes 620 try { 621 ReadDirectoryChangesW(key.handle(), 622 key.buffer().address(), 623 CHANGES_BUFFER_SIZE, 624 key.watchSubtree(), 625 ALL_FILE_NOTIFY_EVENTS, 626 key.countAddress(), 627 key.overlappedAddress()); 628 } catch (WindowsException x) { 629 // no choice but to cancel key 630 criticalError = true; 631 } 632 } 633 if (criticalError) { 634 implCancelKey(key); 635 key.signal(); 636 } 637 } 638 } 639 } 640 }