1 /* 2 * Copyright (c) 2008, 2012, 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 sun.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 (modifier instanceof com.sun.nio.file.SensitivityWatchEventModifier) 236 continue; // ignore 237 return new UnsupportedOperationException("Modifier not supported"); 238 } 239 } 240 241 // check file is directory 242 UnixFileAttributes attrs = null; 243 try { 244 attrs = UnixFileAttributes.get(dir, true); 245 } catch (UnixException x) { 246 return x.asIOException(dir); 247 } 248 if (!attrs.isDirectory()) { 249 return new NotDirectoryException(dir.getPathForExceptionMessage()); 250 } 251 252 // register with inotify (replaces existing mask if already registered) 253 int wd = -1; 254 try { 255 NativeBuffer buffer = 256 NativeBuffers.asNativeBuffer(dir.getByteArrayForSysCalls()); 257 try { 258 wd = inotifyAddWatch(ifd, buffer.address(), mask); 259 } finally { 260 buffer.release(); 261 } 262 } catch (UnixException x) { 263 if (x.errno() == ENOSPC) { 264 return new IOException("User limit of inotify watches reached"); 265 } 266 return x.asIOException(dir); 267 } 268 269 // ensure watch descriptor is in map 270 LinuxWatchKey key = wdToKey.get(wd); 271 if (key == null) { 272 key = new LinuxWatchKey(dir, watcher, ifd, wd); 273 wdToKey.put(wd, key); 274 } 275 return key; 276 } 277 278 // cancel single key 279 @Override 280 void implCancelKey(WatchKey obj) { 281 LinuxWatchKey key = (LinuxWatchKey)obj; 282 if (key.isValid()) { 283 wdToKey.remove(key.descriptor()); 284 key.invalidate(true); 285 } 286 } 287 288 // close watch service 289 @Override 290 void implCloseAll() { 291 // invalidate all keys 292 for (Map.Entry<Integer,LinuxWatchKey> entry: wdToKey.entrySet()) { 293 entry.getValue().invalidate(true); 294 } 295 wdToKey.clear(); 296 297 // free resources 298 unsafe.freeMemory(address); 299 UnixNativeDispatcher.close(socketpair[0]); 300 UnixNativeDispatcher.close(socketpair[1]); 301 UnixNativeDispatcher.close(ifd); 302 } 303 304 /** 305 * Poller main loop 306 */ 307 @Override 308 public void run() { 309 try { 310 for (;;) { 311 int nReady, bytesRead; 312 313 // wait for close or inotify event 314 nReady = poll(ifd, socketpair[0]); 315 316 // read from inotify 317 try { 318 bytesRead = read(ifd, address, BUFFER_SIZE); 319 } catch (UnixException x) { 320 if (x.errno() != EAGAIN) 321 throw x; 322 bytesRead = 0; 323 } 324 325 // process any pending requests 326 if ((nReady > 1) || (nReady == 1 && bytesRead == 0)) { 327 try { 328 read(socketpair[0], address, BUFFER_SIZE); 329 boolean shutdown = processRequests(); 330 if (shutdown) 331 break; 332 } catch (UnixException x) { 333 if (x.errno() != UnixConstants.EAGAIN) 334 throw x; 335 } 336 } 337 338 // iterate over buffer to decode events 339 int offset = 0; 340 while (offset < bytesRead) { 341 long event = address + offset; 342 int wd = unsafe.getInt(event + OFFSETOF_WD); 343 int mask = unsafe.getInt(event + OFFSETOF_MASK); 344 int len = unsafe.getInt(event + OFFSETOF_LEN); 345 346 // file name 347 UnixPath name = null; 348 if (len > 0) { 349 int actual = len; 350 351 // null-terminated and maybe additional null bytes to 352 // align the next event 353 while (actual > 0) { 354 long last = event + OFFSETOF_NAME + actual - 1; 355 if (unsafe.getByte(last) != 0) 356 break; 357 actual--; 358 } 359 if (actual > 0) { 360 byte[] buf = new byte[actual]; 361 unsafe.copyMemory(null, event + OFFSETOF_NAME, 362 buf, Unsafe.ARRAY_BYTE_BASE_OFFSET, actual); 363 name = new UnixPath(fs, buf); 364 } 365 } 366 367 // process event 368 processEvent(wd, mask, name); 369 370 offset += (SIZEOF_INOTIFY_EVENT + len); 371 } 372 } 373 } catch (UnixException x) { 374 x.printStackTrace(); 375 } 376 } 377 378 379 /** 380 * map inotify event to WatchEvent.Kind 381 */ 382 private WatchEvent.Kind<?> maskToEventKind(int mask) { 383 if ((mask & IN_MODIFY) > 0) 384 return StandardWatchEventKinds.ENTRY_MODIFY; 385 if ((mask & IN_ATTRIB) > 0) 386 return StandardWatchEventKinds.ENTRY_MODIFY; 387 if ((mask & IN_CREATE) > 0) 388 return StandardWatchEventKinds.ENTRY_CREATE; 389 if ((mask & IN_MOVED_TO) > 0) 390 return StandardWatchEventKinds.ENTRY_CREATE; 391 if ((mask & IN_DELETE) > 0) 392 return StandardWatchEventKinds.ENTRY_DELETE; 393 if ((mask & IN_MOVED_FROM) > 0) 394 return StandardWatchEventKinds.ENTRY_DELETE; 395 return null; 396 } 397 398 /** 399 * Process event from inotify 400 */ 401 private void processEvent(int wd, int mask, final UnixPath name) { 402 // overflow - signal all keys 403 if ((mask & IN_Q_OVERFLOW) > 0) { 404 for (Map.Entry<Integer,LinuxWatchKey> entry: wdToKey.entrySet()) { 405 entry.getValue() 406 .signalEvent(StandardWatchEventKinds.OVERFLOW, null); 407 } 408 return; 409 } 410 411 // lookup wd to get key 412 LinuxWatchKey key = wdToKey.get(wd); 413 if (key == null) 414 return; // should not happen 415 416 // file deleted 417 if ((mask & IN_IGNORED) > 0) { 418 wdToKey.remove(wd); 419 key.invalidate(false); 420 key.signal(); 421 return; 422 } 423 424 // event for directory itself 425 if (name == null) 426 return; 427 428 // map to event and queue to key 429 WatchEvent.Kind<?> kind = maskToEventKind(mask); 430 if (kind != null) { 431 key.signalEvent(kind, name); 432 } 433 } 434 } 435 436 // -- native methods -- 437 438 // sizeof inotify_event 439 private static native int eventSize(); 440 441 // offsets of inotify_event 442 private static native int[] eventOffsets(); 443 444 private static native int inotifyInit() throws UnixException; 445 446 private static native int inotifyAddWatch(int fd, long pathAddress, int mask) 447 throws UnixException; 448 449 private static native void inotifyRmWatch(int fd, int wd) 450 throws UnixException; 451 452 private static native void configureBlocking(int fd, boolean blocking) 453 throws UnixException; 454 455 private static native void socketpair(int[] sv) throws UnixException; 456 457 private static native int poll(int fd1, int fd2) throws UnixException; 458 459 static { 460 AccessController.doPrivileged(new PrivilegedAction<>() { 461 public Void run() { 462 System.loadLibrary("nio"); 463 return null; 464 }}); 465 } 466 }