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