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