1 /*
   2  * Copyright (c) 2008, 2009, 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.UnixConstants.*;
  36 
  37 /**
  38  * Solaris implementation of WatchService based on file events notification
  39  * facility.
  40  */
  41 
  42 class SolarisWatchService
  43     extends AbstractWatchService
  44 {
  45     private static final Unsafe unsafe = Unsafe.getUnsafe();
  46     private static int addressSize = unsafe.addressSize();
  47 
  48     private static int dependsArch(int value32, int value64) {
  49         return (addressSize == 4) ? value32 : value64;
  50     }
  51 
  52     /*
  53      * typedef struct port_event {
  54      *     int             portev_events;
  55      *     ushort_t        portev_source;
  56      *     ushort_t        portev_pad;
  57      *     uintptr_t       portev_object;
  58      *     void            *portev_user;
  59      * } port_event_t;
  60      */
  61     private static final int SIZEOF_PORT_EVENT  = dependsArch(16, 24);
  62     private static final int OFFSETOF_EVENTS    = 0;
  63     private static final int OFFSETOF_SOURCE    = 4;
  64     private static final int OFFSETOF_OBJECT    = 8;
  65 
  66     /*
  67      * typedef struct file_obj {
  68      *     timestruc_t     fo_atime;
  69      *     timestruc_t     fo_mtime;
  70      *     timestruc_t     fo_ctime;
  71      *     uintptr_t       fo_pad[3];
  72      *     char            *fo_name;
  73      * } file_obj_t;
  74      */
  75     private static final int SIZEOF_FILEOBJ    = dependsArch(40, 80);
  76     private static final int OFFSET_FO_NAME    = dependsArch(36, 72);
  77 
  78     // port sources
  79     private static final short PORT_SOURCE_USER     = 3;
  80     private static final short PORT_SOURCE_FILE     = 7;
  81 
  82     // user-watchable events
  83     private static final int FILE_MODIFIED      = 0x00000002;
  84     private static final int FILE_ATTRIB        = 0x00000004;
  85     private static final int FILE_NOFOLLOW      = 0x10000000;
  86 
  87     // exception events
  88     private static final int FILE_DELETE        = 0x00000010;
  89     private static final int FILE_RENAME_TO     = 0x00000020;
  90     private static final int FILE_RENAME_FROM   = 0x00000040;
  91     private static final int UNMOUNTED          = 0x20000000;
  92     private static final int MOUNTEDOVER        = 0x40000000;
  93 
  94     // background thread to read change events
  95     private final Poller poller;
  96 
  97     SolarisWatchService(UnixFileSystem fs) throws IOException {
  98         int port = -1;
  99         try {
 100             port = portCreate();
 101         } catch (UnixException x) {
 102             throw new IOException(x.errorString());
 103         }
 104 
 105         this.poller = new Poller(fs, this, port);
 106         this.poller.start();
 107     }
 108 
 109     @Override
 110     WatchKey register(Path dir,
 111                       WatchEvent.Kind<?>[] events,
 112                       WatchEvent.Modifier... modifiers)
 113          throws IOException
 114     {
 115         // delegate to poller
 116         return poller.register(dir, events, modifiers);
 117     }
 118 
 119     @Override
 120     void implClose() throws IOException {
 121         // delegate to poller
 122         poller.close();
 123     }
 124 
 125     /**
 126      * WatchKey implementation
 127      */
 128     private class SolarisWatchKey extends AbstractWatchKey
 129         implements DirectoryNode
 130     {
 131         private final UnixFileKey fileKey;
 132 
 133         // pointer to native file_obj object
 134         private final long object;
 135 
 136         // events (may be changed). set to null when watch key is invalid
 137         private volatile Set<? extends WatchEvent.Kind<?>> events;
 138 
 139         // map of entries in directory; created lazily; accessed only by
 140         // poller thread.
 141         private Map<Path,EntryNode> children;
 142 
 143         SolarisWatchKey(SolarisWatchService watcher,
 144                         UnixPath dir,
 145                         UnixFileKey fileKey,
 146                         long object,
 147                         Set<? extends WatchEvent.Kind<?>> events)
 148         {
 149             super(dir, watcher);
 150             this.fileKey = fileKey;
 151             this.object = object;
 152             this.events = events;
 153         }
 154 
 155         UnixPath getDirectory() {
 156             return (UnixPath)watchable();
 157         }
 158 
 159         UnixFileKey getFileKey() {
 160             return fileKey;
 161         }
 162 
 163         @Override
 164         public long object() {
 165             return object;
 166         }
 167 
 168         void invalidate() {
 169             events = null;
 170         }
 171 
 172         Set<? extends WatchEvent.Kind<?>> events() {
 173             return events;
 174         }
 175 
 176         void setEvents(Set<? extends WatchEvent.Kind<?>> events) {
 177             this.events = events;
 178         }
 179 
 180         @Override
 181         public boolean isValid() {
 182             return events != null;
 183         }
 184 
 185         @Override
 186         public void cancel() {
 187             if (isValid()) {
 188                 // delegate to poller
 189                 poller.cancel(this);
 190             }
 191         }
 192 
 193         @Override
 194         public void addChild(Path name, EntryNode node) {
 195             if (children == null)
 196                 children = new HashMap<Path,EntryNode>();
 197             children.put(name, node);
 198         }
 199 
 200         @Override
 201         public void removeChild(Path name) {
 202             children.remove(name);
 203         }
 204 
 205         @Override
 206         public EntryNode getChild(Path name) {
 207             if (children != null)
 208                 return children.get(name);
 209             return null;
 210         }
 211     }
 212 
 213     /**
 214      * Background thread to read from port
 215      */
 216     private class Poller extends AbstractPoller {
 217 
 218         // maximum number of events to read per call to port_getn
 219         private static final int MAX_EVENT_COUNT            = 128;
 220 
 221         // events that map to ENTRY_DELETE
 222         private static final int FILE_REMOVED =
 223             (FILE_DELETE|FILE_RENAME_TO|FILE_RENAME_FROM);
 224 
 225         // events that tell us not to re-associate the object
 226         private static final int FILE_EXCEPTION =
 227             (FILE_REMOVED|UNMOUNTED|MOUNTEDOVER);
 228 
 229         // address of event buffers (used to receive events with port_getn)
 230         private final long bufferAddress;
 231 
 232         private final SolarisWatchService watcher;
 233 
 234         // the I/O port
 235         private final int port;
 236 
 237         // maps file key (dev/inode) to WatchKey
 238         private final Map<UnixFileKey,SolarisWatchKey> fileKey2WatchKey;
 239 
 240         // maps file_obj object to Node
 241         private final Map<Long,Node> object2Node;
 242 
 243         /**
 244          * Create a new instance
 245          */
 246         Poller(UnixFileSystem fs, SolarisWatchService watcher, int port) {
 247             this.watcher = watcher;
 248             this.port = port;
 249             this.bufferAddress =
 250                 unsafe.allocateMemory(SIZEOF_PORT_EVENT * MAX_EVENT_COUNT);
 251             this.fileKey2WatchKey = new HashMap<UnixFileKey,SolarisWatchKey>();
 252             this.object2Node = new HashMap<Long,Node>();
 253         }
 254 
 255         @Override
 256         void wakeup() throws IOException {
 257             // write to port to wakeup polling thread
 258             try {
 259                 portSend(port, 0);
 260             } catch (UnixException x) {
 261                 throw new IOException(x.errorString());
 262             }
 263         }
 264 
 265         @Override
 266         Object implRegister(Path obj,
 267                             Set<? extends WatchEvent.Kind<?>> events,
 268                             WatchEvent.Modifier... modifiers)
 269         {
 270             // no modifiers supported at this time
 271             if (modifiers.length > 0) {
 272                 for (WatchEvent.Modifier modifier: modifiers) {
 273                     if (modifier == null)
 274                         return new NullPointerException();
 275                     if (modifier instanceof com.sun.nio.file.SensitivityWatchEventModifier)
 276                         continue; // ignore
 277                     return new UnsupportedOperationException("Modifier not supported");
 278                 }
 279             }
 280 
 281             UnixPath dir = (UnixPath)obj;
 282 
 283             // check file is directory
 284             UnixFileAttributes attrs = null;
 285             try {
 286                 attrs = UnixFileAttributes.get(dir, true);
 287             } catch (UnixException x) {
 288                 return x.asIOException(dir);
 289             }
 290             if (!attrs.isDirectory()) {
 291                 return new NotDirectoryException(dir.getPathForExecptionMessage());
 292             }
 293 
 294             // return existing watch key after updating events if already
 295             // registered
 296             UnixFileKey fileKey = attrs.fileKey();
 297             SolarisWatchKey watchKey = fileKey2WatchKey.get(fileKey);
 298             if (watchKey != null) {
 299                 updateEvents(watchKey, events);
 300                 return watchKey;
 301             }
 302 
 303             // register directory
 304             long object = 0L;
 305             try {
 306                 object = registerImpl(dir, (FILE_MODIFIED | FILE_ATTRIB));
 307             } catch (UnixException x) {
 308                 return x.asIOException(dir);
 309             }
 310 
 311             // create watch key and insert it into maps
 312             watchKey = new SolarisWatchKey(watcher, dir, fileKey, object, events);
 313             object2Node.put(object, watchKey);
 314             fileKey2WatchKey.put(fileKey, watchKey);
 315 
 316             // register all entries in directory
 317             registerChildren(dir, watchKey, false);
 318 
 319             return watchKey;
 320         }
 321 
 322         // cancel single key
 323         @Override
 324         void implCancelKey(WatchKey obj) {
 325            SolarisWatchKey key = (SolarisWatchKey)obj;
 326            if (key.isValid()) {
 327                fileKey2WatchKey.remove(key.getFileKey());
 328 
 329                // release resources for entries in directory
 330                if (key.children != null) {
 331                    for (Map.Entry<Path,EntryNode> entry: key.children.entrySet()) {
 332                        EntryNode node = entry.getValue();
 333                        long object = node.object();
 334                        object2Node.remove(object);
 335                        releaseObject(object, true);
 336                    }
 337                }
 338 
 339                // release resources for directory
 340                long object = key.object();
 341                object2Node.remove(object);
 342                releaseObject(object, true);
 343 
 344                // and finally invalidate the key
 345                key.invalidate();
 346            }
 347         }
 348 
 349         // close watch service
 350         @Override
 351         void implCloseAll() {
 352             // release all native resources
 353             for (Long object: object2Node.keySet()) {
 354                 releaseObject(object, true);
 355             }
 356 
 357             // invalidate all keys
 358             for (Map.Entry<UnixFileKey,SolarisWatchKey> entry: fileKey2WatchKey.entrySet()) {
 359                 entry.getValue().invalidate();
 360             }
 361 
 362             // clean-up
 363             object2Node.clear();
 364             fileKey2WatchKey.clear();
 365 
 366             // free global resources
 367             unsafe.freeMemory(bufferAddress);
 368             UnixNativeDispatcher.close(port);
 369         }
 370 
 371         /**
 372          * Poller main loop. Blocks on port_getn waiting for events and then
 373          * processes them.
 374          */
 375         @Override
 376         public void run() {
 377             try {
 378                 for (;;) {
 379                     int n = portGetn(port, bufferAddress, MAX_EVENT_COUNT);
 380                     assert n > 0;
 381 
 382                     long address = bufferAddress;
 383                     for (int i=0; i<n; i++) {
 384                         boolean shutdown = processEvent(address);
 385                         if (shutdown)
 386                             return;
 387                         address += SIZEOF_PORT_EVENT;
 388                     }
 389                 }
 390             } catch (UnixException x) {
 391                 x.printStackTrace();
 392             }
 393         }
 394 
 395         /**
 396          * Process a single port_event
 397          *
 398          * Returns true if poller thread is requested to shutdown.
 399          */
 400         boolean processEvent(long address) {
 401             // pe->portev_source
 402             short source = unsafe.getShort(address + OFFSETOF_SOURCE);
 403             // pe->portev_object
 404             long object = unsafe.getAddress(address + OFFSETOF_OBJECT);
 405             // pe->portev_events
 406             int events = unsafe.getInt(address + OFFSETOF_EVENTS);
 407 
 408             // user event is trigger to process pending requests
 409             if (source != PORT_SOURCE_FILE) {
 410                 if (source == PORT_SOURCE_USER) {
 411                     // process any pending requests
 412                     boolean shutdown = processRequests();
 413                     if (shutdown)
 414                         return true;
 415                 }
 416                 return false;
 417             }
 418 
 419             // lookup object to get Node
 420             Node node = object2Node.get(object);
 421             if (node == null) {
 422                 // should not happen
 423                 return false;
 424             }
 425 
 426             // As a workaround for 6642290 and 6636438/6636412 we don't use
 427             // FILE_EXCEPTION events to tell use not to register the file.
 428             // boolean reregister = (events & FILE_EXCEPTION) == 0;
 429             boolean reregister = true;
 430 
 431             // If node is EntryNode then event relates to entry in directory
 432             // If node is a SolarisWatchKey (DirectoryNode) then event relates
 433             // to a watched directory.
 434             boolean isDirectory = (node instanceof SolarisWatchKey);
 435             if (isDirectory) {
 436                 processDirectoryEvents((SolarisWatchKey)node, events);
 437             } else {
 438                 boolean ignore = processEntryEvents((EntryNode)node, events);
 439                 if (ignore)
 440                     reregister = false;
 441             }
 442 
 443             // need to re-associate to get further events
 444             if (reregister) {
 445                 try {
 446                     events = FILE_MODIFIED | FILE_ATTRIB;
 447                     if (!isDirectory) events |= FILE_NOFOLLOW;
 448                     portAssociate(port,
 449                                   PORT_SOURCE_FILE,
 450                                   object,
 451                                   events);
 452                 } catch (UnixException x) {
 453                     // unable to re-register
 454                     reregister = false;
 455                 }
 456             }
 457 
 458             // object is not re-registered so release resources. If
 459             // object is a watched directory then signal key
 460             if (!reregister) {
 461                 // release resources
 462                 object2Node.remove(object);
 463                 releaseObject(object, false);
 464 
 465                 // if watch key then signal it
 466                 if (isDirectory) {
 467                     SolarisWatchKey key = (SolarisWatchKey)node;
 468                     fileKey2WatchKey.remove( key.getFileKey() );
 469                     key.invalidate();
 470                     key.signal();
 471                 } else {
 472                     // if entry then remove it from parent
 473                     EntryNode entry = (EntryNode)node;
 474                     SolarisWatchKey key = (SolarisWatchKey)entry.parent();
 475                     key.removeChild(entry.name());
 476                 }
 477             }
 478 
 479             return false;
 480         }
 481 
 482         /**
 483          * Process directory events. If directory is modified then re-scan
 484          * directory to register any new entries
 485          */
 486         void processDirectoryEvents(SolarisWatchKey key, int mask) {
 487             if ((mask & (FILE_MODIFIED | FILE_ATTRIB)) != 0) {
 488                 registerChildren(key.getDirectory(), key,
 489                     key.events().contains(StandardWatchEventKind.ENTRY_CREATE));
 490             }
 491         }
 492 
 493         /**
 494          * Process events for entries in registered directories. Returns {@code
 495          * true} if events are ignored because the watch key has been cancelled.
 496          */
 497         boolean processEntryEvents(EntryNode node, int mask) {
 498             SolarisWatchKey key = (SolarisWatchKey)node.parent();
 499             Set<? extends WatchEvent.Kind<?>> events = key.events();
 500             if (events == null) {
 501                 // key has been cancelled so ignore event
 502                 return true;
 503             }
 504 
 505             // entry modified
 506             if (((mask & (FILE_MODIFIED | FILE_ATTRIB)) != 0) &&
 507                 events.contains(StandardWatchEventKind.ENTRY_MODIFY))
 508             {
 509                 key.signalEvent(StandardWatchEventKind.ENTRY_MODIFY, node.name());
 510             }
 511 
 512             // entry removed
 513             if (((mask & (FILE_REMOVED)) != 0) &&
 514                 events.contains(StandardWatchEventKind.ENTRY_DELETE))
 515             {
 516                 // Due to 6636438/6636412 we may get a remove event for cases
 517                 // where a rmdir/unlink/rename is attempted but fails. Until
 518                 // this issue is resolved we re-lstat the file to check if it
 519                 // exists. If it exists then we ignore the event. To keep the
 520                 // workaround simple we don't check the st_ino so it isn't
 521                 // effective when the file is replaced.
 522                 boolean removed = true;
 523                 try {
 524                     UnixFileAttributes
 525                         .get(key.getDirectory().resolve(node.name()), false);
 526                     removed = false;
 527                 } catch (UnixException x) { }
 528 
 529                 if (removed)
 530                     key.signalEvent(StandardWatchEventKind.ENTRY_DELETE, node.name());
 531             }
 532             return false;
 533         }
 534 
 535         /**
 536          * Registers all entries in the given directory
 537          *
 538          * The {@code sendEvents} parameter indicates if ENTRY_CREATE events
 539          * should be queued when new entries are found. When initially
 540          * registering a directory then will always be false. When re-scanning
 541          * a directory then it depends on if the event is enabled or not.
 542          */
 543         void registerChildren(UnixPath dir,
 544                               SolarisWatchKey parent,
 545                               boolean sendEvents)
 546         {
 547             // if the ENTRY_MODIFY event is not enabled then we don't need
 548             // modification events for entries in the directory
 549             int events = FILE_NOFOLLOW;
 550             if (parent.events().contains(StandardWatchEventKind.ENTRY_MODIFY))
 551                 events |= (FILE_MODIFIED | FILE_ATTRIB);
 552 
 553             DirectoryStream<Path> stream = null;
 554             try {
 555                 stream = Files.newDirectoryStream(dir);
 556             } catch (IOException x) {
 557                 // nothing we can do
 558                 return;
 559             }
 560             try {
 561                 for (Path entry: stream) {
 562                     Path name = entry.getFileName();
 563 
 564                     // skip entry if already registered
 565                     if (parent.getChild(name) != null)
 566                         continue;
 567 
 568                     // send ENTRY_CREATE if enabled
 569                     if (sendEvents) {
 570                         parent.signalEvent(StandardWatchEventKind.ENTRY_CREATE, name);
 571                     }
 572 
 573                     // register it
 574                     long object = 0L;
 575                     try {
 576                         object = registerImpl((UnixPath)entry, events);
 577                     } catch (UnixException x) {
 578                         // can't register so ignore for now.
 579                         continue;
 580                     }
 581 
 582                     // create node
 583                     EntryNode node = new EntryNode(object, entry.getFileName(), parent);
 584                     // tell the parent about it
 585                     parent.addChild(entry.getFileName(), node);
 586                     object2Node.put(object, node);
 587                 }
 588             } catch (ConcurrentModificationException x) {
 589                 // error during iteration which we ignore for now
 590             } finally {
 591                 try {
 592                     stream.close();
 593                 } catch (IOException x) { }
 594             }
 595         }
 596 
 597         /**
 598          * Update watch key's events. Where the ENTRY_MODIFY changes then we
 599          * need to update the events of registered children.
 600          */
 601         void updateEvents(SolarisWatchKey key, Set<? extends WatchEvent.Kind<?>> events) {
 602             // update events, rembering if ENTRY_MODIFY was previously
 603             // enabled or disabled.
 604             boolean wasModifyEnabled = key.events()
 605                 .contains(StandardWatchEventKind.ENTRY_MODIFY);
 606             key.setEvents(events);
 607 
 608             // check if ENTRY_MODIFY has changed
 609             boolean isModifyEnabled = events
 610                 .contains(StandardWatchEventKind.ENTRY_MODIFY);
 611             if (wasModifyEnabled == isModifyEnabled) {
 612                 return;
 613             }
 614 
 615             // if changed then update events of children
 616             if (key.children != null) {
 617                 int ev = FILE_NOFOLLOW;
 618                 if (isModifyEnabled)
 619                     ev |= (FILE_MODIFIED | FILE_ATTRIB);
 620 
 621                 for (Map.Entry<Path,EntryNode> entry: key.children.entrySet()) {
 622                     long object = entry.getValue().object();
 623                     try {
 624                         portAssociate(port,
 625                                       PORT_SOURCE_FILE,
 626                                       object,
 627                                       ev);
 628                     } catch (UnixException x) {
 629                         // nothing we can do.
 630                     }
 631                 }
 632             }
 633         }
 634 
 635         /**
 636          * Calls port_associate to register the given path.
 637          * Returns pointer to fileobj structure that is allocated for
 638          * the registration.
 639          */
 640         long registerImpl(UnixPath dir, int events)
 641             throws UnixException
 642         {
 643             // allocate memory for the path (file_obj->fo_name field)
 644             byte[] path = dir.getByteArrayForSysCalls();
 645             int len = path.length;
 646             long name = unsafe.allocateMemory(len+1);
 647             unsafe.copyMemory(path, Unsafe.ARRAY_BYTE_BASE_OFFSET, null,
 648                 name, (long)len);
 649             unsafe.putByte(name + len, (byte)0);
 650 
 651             // allocate memory for filedatanode structure - this is the object
 652             // to port_associate
 653             long object = unsafe.allocateMemory(SIZEOF_FILEOBJ);
 654             unsafe.setMemory(null, object, SIZEOF_FILEOBJ, (byte)0);
 655             unsafe.putAddress(object + OFFSET_FO_NAME, name);
 656 
 657             // associate the object with the port
 658             try {
 659                 portAssociate(port,
 660                               PORT_SOURCE_FILE,
 661                               object,
 662                               events);
 663             } catch (UnixException x) {
 664                 // debugging
 665                 if (x.errno() == EAGAIN) {
 666                     System.err.println("The maximum number of objects associated "+
 667                         "with the port has been reached");
 668                 }
 669 
 670                 unsafe.freeMemory(name);
 671                 unsafe.freeMemory(object);
 672                 throw x;
 673             }
 674             return object;
 675         }
 676 
 677         /**
 678          * Frees all resources for an file_obj object; optionally remove
 679          * association from port
 680          */
 681         void releaseObject(long object, boolean dissociate) {
 682             // remove association
 683             if (dissociate) {
 684                 try {
 685                     portDissociate(port, PORT_SOURCE_FILE, object);
 686                 } catch (UnixException x) {
 687                     // ignore
 688                 }
 689             }
 690 
 691             // free native memory
 692             long name = unsafe.getAddress(object + OFFSET_FO_NAME);
 693             unsafe.freeMemory(name);
 694             unsafe.freeMemory(object);
 695         }
 696     }
 697 
 698     /**
 699      * A node with native (file_obj) resources
 700      */
 701     private static interface Node {
 702         long object();
 703     }
 704 
 705     /**
 706      * A directory node with a map of the entries in the directory
 707      */
 708     private static interface DirectoryNode extends Node {
 709         void addChild(Path name, EntryNode node);
 710         void removeChild(Path name);
 711         EntryNode getChild(Path name);
 712     }
 713 
 714     /**
 715      * An implementation of a node that is an entry in a directory.
 716      */
 717     private static class EntryNode implements Node {
 718         private final long object;
 719         private final Path name;
 720         private final DirectoryNode parent;
 721 
 722         EntryNode(long object, Path name, DirectoryNode parent) {
 723             this.object = object;
 724             this.name = name;
 725             this.parent = parent;
 726         }
 727 
 728         @Override
 729         public long object() {
 730             return object;
 731         }
 732 
 733         Path name() {
 734             return name;
 735         }
 736 
 737         DirectoryNode parent() {
 738             return parent;
 739         }
 740     }
 741 
 742     // -- native methods --
 743 
 744     private static native void init();
 745 
 746     private static native int portCreate() throws UnixException;
 747 
 748     private static native void portAssociate(int port, int source, long object, int events)
 749         throws UnixException;
 750 
 751     private static native void portDissociate(int port, int source, long object)
 752         throws UnixException;
 753 
 754     private static native void portSend(int port, int events)
 755         throws UnixException;
 756 
 757     private static native int portGetn(int port, long address, int max)
 758         throws UnixException;
 759 
 760     static {
 761         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 762             public Void run() {
 763                 System.loadLibrary("nio");
 764                 return null;
 765         }});
 766         init();
 767     }
 768 }