1 /* 2 * Copyright (c) 2008, 2018, 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.nio.file.*; 29 import java.security.AccessController; 30 import java.security.PrivilegedAction; 31 import java.util.*; 32 import java.io.IOException; 33 import jdk.internal.misc.Unsafe; 34 35 import static sun.nio.fs.UnixNativeDispatcher.*; 36 import static sun.nio.fs.UnixConstants.*; 37 38 /** 39 * Linux implementation of WatchService based on inotify. 40 * 41 * In summary a background thread polls inotify plus a socket used for the wakeup 42 * mechanism. Requests to add or remove a watch, or close the watch service, 43 * cause the thread to wakeup and process the request. Events are processed 44 * by the thread which causes it to signal/queue the corresponding watch keys. 45 */ 46 47 class LinuxWatchService 48 extends AbstractWatchService 49 { 50 private static final Unsafe unsafe = Unsafe.getUnsafe(); 51 52 // background thread to read change events 53 private final Poller poller; 54 55 LinuxWatchService(UnixFileSystem fs) throws IOException { 56 // initialize inotify 57 int ifd = - 1; 58 try { 59 ifd = inotifyInit(); 60 } catch (UnixException x) { 61 String msg = (x.errno() == EMFILE) ? 62 "User limit of inotify instances reached or too many open files" : 63 x.errorString(); 64 throw new IOException(msg); 65 } 66 67 // configure inotify to be non-blocking 68 // create socketpair used in the close mechanism 69 int sp[] = new int[2]; 70 try { 71 configureBlocking(ifd, false); 72 socketpair(sp); 73 configureBlocking(sp[0], false); 74 } catch (UnixException x) { 75 UnixNativeDispatcher.close(ifd); 76 throw new IOException(x.errorString()); 77 } 78 79 this.poller = new Poller(fs, this, ifd, sp); 80 this.poller.start(); 81 } 82 83 @Override 84 WatchKey register(Path dir, 85 WatchEvent.Kind<?>[] events, 86 WatchEvent.Modifier... modifiers) 87 throws IOException 88 { 89 // delegate to poller 90 return poller.register(dir, events, modifiers); 91 } 92 93 @Override 94 void implClose() throws IOException { 95 // delegate to poller 96 poller.close(); 97 } 98 99 /** 100 * WatchKey implementation 101 */ 102 private static class LinuxWatchKey extends AbstractWatchKey { 103 // inotify descriptor 104 private final int ifd; 105 // watch descriptor 106 private volatile int wd; 107 108 LinuxWatchKey(UnixPath dir, LinuxWatchService watcher, int ifd, int wd) { 109 super(dir, watcher); 110 this.ifd = ifd; 111 this.wd = wd; 112 } 113 114 int descriptor() { 115 return wd; 116 } 117 118 void invalidate(boolean remove) { 119 if (remove) { 120 try { 121 inotifyRmWatch(ifd, wd); 122 } catch (UnixException x) { 123 // ignore 124 } 125 } 126 wd = -1; 127 } 128 129 @Override 130 public boolean isValid() { 131 return (wd != -1); 132 } 133 134 @Override 135 public void cancel() { 136 if (isValid()) { 137 // delegate to poller 138 ((LinuxWatchService)watcher()).poller.cancel(this); 139 } 140 } 141 } 142 143 /** 144 * Background thread to read from inotify 145 */ 146 private static class Poller extends AbstractPoller { 147 /** 148 * struct inotify_event { 149 * int wd; 150 * uint32_t mask; 151 * uint32_t len; 152 * char name __flexarr; // present if len > 0 153 * } act_t; 154 */ 155 private static final int SIZEOF_INOTIFY_EVENT = eventSize(); 156 private static final int[] offsets = eventOffsets(); 157 private static final int OFFSETOF_WD = offsets[0]; 158 private static final int OFFSETOF_MASK = offsets[1]; 159 private static final int OFFSETOF_LEN = offsets[3]; 160 private static final int OFFSETOF_NAME = offsets[4]; 161 162 private static final int IN_MODIFY = 0x00000002; 163 private static final int IN_ATTRIB = 0x00000004; 164 private static final int IN_MOVED_FROM = 0x00000040; 165 private static final int IN_MOVED_TO = 0x00000080; 166 private static final int IN_CREATE = 0x00000100; 167 private static final int IN_DELETE = 0x00000200; 168 169 private static final int IN_UNMOUNT = 0x00002000; 170 private static final int IN_Q_OVERFLOW = 0x00004000; 171 private static final int IN_IGNORED = 0x00008000; 172 173 // sizeof buffer for when polling inotify 174 private static final int BUFFER_SIZE = 8192; 175 176 private final UnixFileSystem fs; 177 private final LinuxWatchService watcher; 178 179 // inotify file descriptor 180 private final int ifd; 181 // socketpair used to shutdown polling thread 182 private final int socketpair[]; 183 // maps watch descriptor to Key 184 private final Map<Integer,LinuxWatchKey> wdToKey; 185 // address of read buffer 186 private final long address; 187 188 Poller(UnixFileSystem fs, LinuxWatchService watcher, int ifd, int[] sp) { 189 this.fs = fs; 190 this.watcher = watcher; 191 this.ifd = ifd; 192 this.socketpair = sp; 193 this.wdToKey = new HashMap<>(); 194 this.address = unsafe.allocateMemory(BUFFER_SIZE); 195 } 196 197 @Override 198 void wakeup() throws IOException { 199 // write to socketpair to wakeup polling thread 200 try { 201 write(socketpair[1], address, 1); 202 } catch (UnixException x) { 203 throw new IOException(x.errorString()); 204 } 205 } 206 207 @Override 208 Object implRegister(Path obj, 209 Set<? extends WatchEvent.Kind<?>> events, 210 WatchEvent.Modifier... modifiers) 211 { 212 UnixPath dir = (UnixPath)obj; 213 214 int mask = 0; 215 for (WatchEvent.Kind<?> event: events) { 216 if (event == StandardWatchEventKinds.ENTRY_CREATE) { 217 mask |= IN_CREATE | IN_MOVED_TO; 218 continue; 219 } 220 if (event == StandardWatchEventKinds.ENTRY_DELETE) { 221 mask |= IN_DELETE | IN_MOVED_FROM; 222 continue; 223 } 224 if (event == StandardWatchEventKinds.ENTRY_MODIFY) { 225 mask |= IN_MODIFY | IN_ATTRIB; 226 continue; 227 } 228 } 229 230 // no modifiers supported at this time 231 if (modifiers.length > 0) { 232 for (WatchEvent.Modifier modifier: modifiers) { 233 if (modifier == null) 234 return new NullPointerException(); 235 if (!ExtendedOptions.SENSITIVITY_HIGH.matches(modifier) && 236 !ExtendedOptions.SENSITIVITY_MEDIUM.matches(modifier) && 237 !ExtendedOptions.SENSITIVITY_LOW.matches(modifier)) { 238 return new UnsupportedOperationException("Modifier not supported"); 239 } 240 } 241 } 242 243 // check file is directory 244 UnixFileAttributes attrs = null; 245 try { 246 attrs = UnixFileAttributes.get(dir, true); 247 } catch (UnixException x) { 248 return x.asIOException(dir); 249 } 250 if (!attrs.isDirectory()) { 251 return new NotDirectoryException(dir.getPathForExceptionMessage()); 252 } 253 254 // register with inotify (replaces existing mask if already registered) 255 int wd = -1; 256 try { 257 NativeBuffer buffer = 258 NativeBuffers.asNativeBuffer(dir.getByteArrayForSysCalls()); 259 try { 260 wd = inotifyAddWatch(ifd, buffer.address(), mask); 261 } finally { 262 buffer.release(); 263 } 264 } catch (UnixException x) { 265 if (x.errno() == ENOSPC) { 266 return new IOException("User limit of inotify watches reached"); 267 } 268 return x.asIOException(dir); 269 } 270 271 // ensure watch descriptor is in map 272 LinuxWatchKey key = wdToKey.get(wd); 273 if (key == null) { 274 key = new LinuxWatchKey(dir, watcher, ifd, wd); 275 wdToKey.put(wd, key); 276 } 277 return key; 278 } 279 280 // cancel single key 281 @Override 282 void implCancelKey(WatchKey obj) { 283 LinuxWatchKey key = (LinuxWatchKey)obj; 284 if (key.isValid()) { 285 wdToKey.remove(key.descriptor()); 286 key.invalidate(true); 287 } 288 } 289 290 // close watch service 291 @Override 292 void implCloseAll() { 293 // invalidate all keys 294 for (Map.Entry<Integer,LinuxWatchKey> entry: wdToKey.entrySet()) { 295 entry.getValue().invalidate(true); 296 } 297 wdToKey.clear(); 298 299 // free resources 300 unsafe.freeMemory(address); 301 UnixNativeDispatcher.close(socketpair[0]); 302 UnixNativeDispatcher.close(socketpair[1]); 303 UnixNativeDispatcher.close(ifd); 304 } 305 306 /** 307 * Poller main loop 308 */ 309 @Override 310 public void run() { 311 try { 312 for (;;) { 313 int nReady, bytesRead; 314 315 // wait for close or inotify event 316 nReady = poll(ifd, socketpair[0]); 317 318 // read from inotify 319 try { 320 bytesRead = read(ifd, address, BUFFER_SIZE); 321 } catch (UnixException x) { 322 if (x.errno() != EAGAIN && x.errno() != EWOULDBLOCK) 323 throw x; 324 bytesRead = 0; 325 } 326 327 // iterate over buffer to decode events 328 int offset = 0; 329 while (offset < bytesRead) { 330 long event = address + offset; 331 int wd = unsafe.getInt(event + OFFSETOF_WD); 332 int mask = unsafe.getInt(event + OFFSETOF_MASK); 333 int len = unsafe.getInt(event + OFFSETOF_LEN); 334 335 // file name 336 UnixPath name = null; 337 if (len > 0) { 338 int actual = len; 339 340 // null-terminated and maybe additional null bytes to 341 // align the next event 342 while (actual > 0) { 343 long last = event + OFFSETOF_NAME + actual - 1; 344 if (unsafe.getByte(last) != 0) 345 break; 346 actual--; 347 } 348 if (actual > 0) { 349 byte[] buf = new byte[actual]; 350 unsafe.copyMemory(null, event + OFFSETOF_NAME, 351 buf, Unsafe.ARRAY_BYTE_BASE_OFFSET, actual); 352 name = new UnixPath(fs, buf); 353 } 354 } 355 356 // process event 357 processEvent(wd, mask, name); 358 359 offset += (SIZEOF_INOTIFY_EVENT + len); 360 } 361 362 // process any pending requests 363 if ((nReady > 1) || (nReady == 1 && bytesRead == 0)) { 364 try { 365 read(socketpair[0], address, BUFFER_SIZE); 366 boolean shutdown = processRequests(); 367 if (shutdown) 368 break; 369 } catch (UnixException x) { 370 if (x.errno() != EAGAIN && x.errno() != EWOULDBLOCK) 371 throw x; 372 } 373 } 374 } 375 } catch (UnixException x) { 376 x.printStackTrace(); 377 } 378 } 379 380 381 /** 382 * map inotify event to WatchEvent.Kind 383 */ 384 private WatchEvent.Kind<?> maskToEventKind(int mask) { 385 if ((mask & IN_MODIFY) > 0) 386 return StandardWatchEventKinds.ENTRY_MODIFY; 387 if ((mask & IN_ATTRIB) > 0) 388 return StandardWatchEventKinds.ENTRY_MODIFY; 389 if ((mask & IN_CREATE) > 0) 390 return StandardWatchEventKinds.ENTRY_CREATE; 391 if ((mask & IN_MOVED_TO) > 0) 392 return StandardWatchEventKinds.ENTRY_CREATE; 393 if ((mask & IN_DELETE) > 0) 394 return StandardWatchEventKinds.ENTRY_DELETE; 395 if ((mask & IN_MOVED_FROM) > 0) 396 return StandardWatchEventKinds.ENTRY_DELETE; 397 return null; 398 } 399 400 /** 401 * Process event from inotify 402 */ 403 private void processEvent(int wd, int mask, final UnixPath name) { 404 // overflow - signal all keys 405 if ((mask & IN_Q_OVERFLOW) > 0) { 406 for (Map.Entry<Integer,LinuxWatchKey> entry: wdToKey.entrySet()) { 407 entry.getValue() 408 .signalEvent(StandardWatchEventKinds.OVERFLOW, null); 409 } 410 return; 411 } 412 413 // lookup wd to get key 414 LinuxWatchKey key = wdToKey.get(wd); 415 if (key == null) 416 return; // should not happen 417 418 // file deleted 419 if ((mask & IN_IGNORED) > 0) { 420 wdToKey.remove(wd); 421 key.invalidate(false); 422 key.signal(); 423 return; 424 } 425 426 // event for directory itself 427 if (name == null) 428 return; 429 430 // map to event and queue to key 431 WatchEvent.Kind<?> kind = maskToEventKind(mask); 432 if (kind != null) { 433 key.signalEvent(kind, name); 434 } 435 } 436 } 437 438 // -- native methods -- 439 440 // sizeof inotify_event 441 private static native int eventSize(); 442 443 // offsets of inotify_event 444 private static native int[] eventOffsets(); 445 446 private static native int inotifyInit() throws UnixException; 447 448 private static native int inotifyAddWatch(int fd, long pathAddress, int mask) 449 throws UnixException; 450 451 private static native void inotifyRmWatch(int fd, int wd) 452 throws UnixException; 453 454 private static native void configureBlocking(int fd, boolean blocking) 455 throws UnixException; 456 457 private static native void socketpair(int[] sv) throws UnixException; 458 459 private static native int poll(int fd1, int fd2) throws UnixException; 460 461 static { 462 AccessController.doPrivileged(new PrivilegedAction<>() { 463 public Void run() { 464 System.loadLibrary("nio"); 465 return null; 466 }}); 467 } 468 }