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