From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Timur Sultanov <sultanovts@yandex.ru>
Date: Sun, 12 Jun 2022 15:58:40 -0600
Subject: [PATCH] Add minerva-specific modules to java.base and jdk.attach

It would be nice to re-direct the build to the same files *BSD use, but
for now we've got our own copy

Co-Authored-By: Andrew Kaster <akaster@serenityos.org>
---
 .../DefaultAsynchronousChannelProvider.java   |  47 ++
 .../sun/nio/ch/DefaultSelectorProvider.java   |  54 ++
 .../MinervaAsynchronousChannelProvider.java  |  91 +++
 .../classes/sun/nio/ch/MinervaPollPort.java  | 538 ++++++++++++++++++
 .../sun/nio/fs/DefaultFileSystemProvider.java |  53 ++
 .../classes/sun/nio/fs/MinervaFileStore.java | 105 ++++
 .../sun/nio/fs/MinervaFileSystem.java        |  94 +++
 .../nio/fs/MinervaFileSystemProvider.java    |  52 ++
 .../sun/nio/fs/MinervaNativeDispatcher.java  |  49 ++
 .../minerva/native/libnet/minerva_close.c   | 458 +++++++++++++++
 .../sun/tools/attach/AttachProviderImpl.java  |  82 +++
 .../sun/tools/attach/VirtualMachineImpl.java  | 326 +++++++++++
 .../native/libattach/VirtualMachineImpl.c     | 328 +++++++++++
 13 files changed, 2277 insertions(+)
 create mode 100644 src/java.base/minerva/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java
 create mode 100644 src/java.base/minerva/classes/sun/nio/ch/DefaultSelectorProvider.java
 create mode 100644 src/java.base/minerva/classes/sun/nio/ch/MinervaAsynchronousChannelProvider.java
 create mode 100644 src/java.base/minerva/classes/sun/nio/ch/MinervaPollPort.java
 create mode 100644 src/java.base/minerva/classes/sun/nio/fs/DefaultFileSystemProvider.java
 create mode 100644 src/java.base/minerva/classes/sun/nio/fs/MinervaFileStore.java
 create mode 100644 src/java.base/minerva/classes/sun/nio/fs/MinervaFileSystem.java
 create mode 100644 src/java.base/minerva/classes/sun/nio/fs/MinervaFileSystemProvider.java
 create mode 100644 src/java.base/minerva/classes/sun/nio/fs/MinervaNativeDispatcher.java
 create mode 100644 src/java.base/minerva/native/libnet/minerva_close.c
 create mode 100644 src/jdk.attach/minerva/classes/sun/tools/attach/AttachProviderImpl.java
 create mode 100644 src/jdk.attach/minerva/classes/sun/tools/attach/VirtualMachineImpl.java
 create mode 100644 src/jdk.attach/minerva/native/libattach/VirtualMachineImpl.c

diff --git a/src/java.base/minerva/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java b/src/java.base/minerva/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a7bfe0897390a5828dfea7a7f9d0e83f25af9af
--- /dev/null
+++ b/src/java.base/minerva/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.spi.AsynchronousChannelProvider;
+
+/**
+ * Creates this platform's default AsynchronousChannelProvider
+ */
+
+public class DefaultAsynchronousChannelProvider {
+
+    /**
+     * Prevent instantiation.
+     */
+    private DefaultAsynchronousChannelProvider() { }
+
+    /**
+     * Returns the default AsynchronousChannelProvider.
+     */
+    public static AsynchronousChannelProvider create() {
+        return new MinervaAsynchronousChannelProvider();
+    }
+}
diff --git a/src/java.base/minerva/classes/sun/nio/ch/DefaultSelectorProvider.java b/src/java.base/minerva/classes/sun/nio/ch/DefaultSelectorProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..86d3ade19de292048b3b028a7f78a1cc61f4784f
--- /dev/null
+++ b/src/java.base/minerva/classes/sun/nio/ch/DefaultSelectorProvider.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.ch;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Creates this platform's default SelectorProvider
+ */
+
+@SuppressWarnings("removal")
+public class DefaultSelectorProvider {
+    private static final SelectorProviderImpl INSTANCE;
+    static {
+        PrivilegedAction<SelectorProviderImpl> pa = PollSelectorProvider::new;
+        INSTANCE = AccessController.doPrivileged(pa);
+    }
+
+    /**
+     * Prevent instantiation.
+     */
+    private DefaultSelectorProvider() { }
+
+    /**
+     * Returns the default SelectorProvider implementation.
+     */
+    public static SelectorProviderImpl get() {
+        return INSTANCE;
+    }
+}
\ No newline at end of file
diff --git a/src/java.base/minerva/classes/sun/nio/ch/MinervaAsynchronousChannelProvider.java b/src/java.base/minerva/classes/sun/nio/ch/MinervaAsynchronousChannelProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..2daa2cca4717a6db0dff166bc4befd19d9fdb528
--- /dev/null
+++ b/src/java.base/minerva/classes/sun/nio/ch/MinervaAsynchronousChannelProvider.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.nio.channels.spi.AsynchronousChannelProvider;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.io.IOException;
+
+public class MinervaAsynchronousChannelProvider
+    extends AsynchronousChannelProvider
+{
+    private static volatile MinervaPollPort defaultPort;
+
+    private MinervaPollPort defaultEventPort() throws IOException {
+        if (defaultPort == null) {
+            synchronized (MinervaAsynchronousChannelProvider.class) {
+                if (defaultPort == null) {
+                    defaultPort = new MinervaPollPort(this, ThreadPool.getDefault()).start();
+                }
+            }
+        }
+        return defaultPort;
+    }
+
+    public MinervaAsynchronousChannelProvider() {
+    }
+
+    @Override
+    public AsynchronousChannelGroup openAsynchronousChannelGroup(int nThreads, ThreadFactory factory)
+        throws IOException
+    {
+        return new MinervaPollPort(this, ThreadPool.create(nThreads, factory)).start();
+    }
+
+    @Override
+    public AsynchronousChannelGroup openAsynchronousChannelGroup(ExecutorService executor, int initialSize)
+        throws IOException
+    {
+        return new MinervaPollPort(this, ThreadPool.wrap(executor, initialSize)).start();
+    }
+
+    private Port toPort(AsynchronousChannelGroup group) throws IOException {
+        if (group == null) {
+            return defaultEventPort();
+        } else {
+            if (!(group instanceof MinervaPollPort))
+                throw new IllegalChannelGroupException();
+            return (Port)group;
+        }
+    }
+
+    @Override
+    public AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(AsynchronousChannelGroup group)
+        throws IOException
+    {
+        return new UnixAsynchronousServerSocketChannelImpl(toPort(group));
+    }
+
+    @Override
+    public AsynchronousSocketChannel openAsynchronousSocketChannel(AsynchronousChannelGroup group)
+        throws IOException
+    {
+        return new UnixAsynchronousSocketChannelImpl(toPort(group));
+    }
+}
diff --git a/src/java.base/minerva/classes/sun/nio/ch/MinervaPollPort.java b/src/java.base/minerva/classes/sun/nio/ch/MinervaPollPort.java
new file mode 100644
index 0000000000000000000000000000000000000000..0894d1814244f39887d119da00290798c960e53c
--- /dev/null
+++ b/src/java.base/minerva/classes/sun/nio/ch/MinervaPollPort.java
@@ -0,0 +1,538 @@
+/*
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.spi.AsynchronousChannelProvider;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantLock;
+import jdk.internal.misc.Unsafe;
+
+/**
+ * AsynchronousChannelGroup implementation based on the AIX pollset framework.
+ */
+final class MinervaPollPort
+    extends Port
+{
+    private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+    static {
+        IOUtil.load();
+        init();
+    }
+
+    /**
+     * struct pollfd {
+     *     int fd;
+     *     short events;
+     *     short revents;
+     * }
+     */
+    private static final int SIZEOF_POLLFD    = eventSize();
+    private static final int OFFSETOF_EVENTS  = eventsOffset();
+    private static final int OFFSETOF_REVENTS = reventsOffset();
+    private static final int OFFSETOF_FD      = fdOffset();
+
+    // opcodes
+    private static final int PS_ADD     = 0x0;
+    private static final int PS_MOD     = 0x1;
+    private static final int PS_DELETE  = 0x2;
+
+    // maximum number of events to poll at a time
+    private static final int MAX_POLL_EVENTS = 512;
+
+    // pollset ID
+    private final int pollset;
+
+    // true if port is closed
+    private boolean closed;
+
+    // socket pair used for wakeup
+    private final int sp[];
+
+    // socket pair used to indicate pending pollsetCtl calls
+    // Background info: pollsetCtl blocks when another thread is in a pollsetPoll call.
+    private final int ctlSp[];
+
+    // number of wakeups pending
+    private final AtomicInteger wakeupCount = new AtomicInteger();
+
+    // address of the poll array passed to pollset_poll
+    private final long address;
+
+    // encapsulates an event for a channel
+    static class Event {
+        final PollableChannel channel;
+        final int events;
+
+        Event(PollableChannel channel, int events) {
+            this.channel = channel;
+            this.events = events;
+        }
+
+        PollableChannel channel()   { return channel; }
+        int events()                { return events; }
+    }
+
+    // queue of events for cases that a polling thread dequeues more than one
+    // event
+    private final ArrayBlockingQueue<Event> queue;
+    private final Event NEED_TO_POLL = new Event(null, 0);
+    private final Event EXECUTE_TASK_OR_SHUTDOWN = new Event(null, 0);
+    private final Event CONTINUE_AFTER_CTL_EVENT = new Event(null, 0);
+
+    // encapsulates a pollset control event for a file descriptor
+    static class ControlEvent {
+        final int fd;
+        final int events;
+        final boolean removeOnly;
+        int error = 0;
+
+        ControlEvent(int fd, int events, boolean removeOnly) {
+            this.fd = fd;
+            this.events = events;
+            this.removeOnly = removeOnly;
+        }
+
+        int fd()                 { return fd; }
+        int events()             { return events; }
+        boolean removeOnly()     { return removeOnly; }
+        int error()              { return error; }
+        void setError(int error) { this.error = error; }
+    }
+
+    // queue of control events that need to be processed
+    // (this object is also used for synchronization)
+    private final HashSet<ControlEvent> controlQueue = new HashSet<ControlEvent>();
+
+    // lock used to check whether a poll operation is ongoing
+    private final ReentrantLock controlLock = new ReentrantLock();
+
+    MinervaPollPort(AsynchronousChannelProvider provider, ThreadPool pool)
+        throws IOException
+    {
+        super(provider, pool);
+
+        // open pollset
+        this.pollset = pollsetCreate();
+
+        // create socket pair for wakeup mechanism
+        int[] sv = new int[2];
+        try {
+            socketpair(sv);
+            // register one end with pollset
+            pollsetCtl(pollset, PS_ADD, sv[0], Net.POLLIN);
+        } catch (IOException x) {
+            pollsetDestroy(pollset);
+            throw x;
+        }
+        this.sp = sv;
+
+        // create socket pair for pollset control mechanism
+        sv = new int[2];
+        try {
+            socketpair(sv);
+            // register one end with pollset
+            pollsetCtl(pollset, PS_ADD, sv[0], Net.POLLIN);
+        } catch (IOException x) {
+            pollsetDestroy(pollset);
+            throw x;
+        }
+        this.ctlSp = sv;
+
+        // allocate the poll array
+        this.address = allocatePollArray(MAX_POLL_EVENTS);
+
+        // create the queue and offer the special event to ensure that the first
+        // threads polls
+        this.queue = new ArrayBlockingQueue<Event>(MAX_POLL_EVENTS);
+        this.queue.offer(NEED_TO_POLL);
+    }
+
+    MinervaPollPort start() {
+        startThreads(new EventHandlerTask());
+        return this;
+    }
+
+    /**
+     * Release all resources
+     */
+    private void implClose() {
+        synchronized (this) {
+            if (closed)
+                return;
+            closed = true;
+        }
+        freePollArray(address);
+        close0(sp[0]);
+        close0(sp[1]);
+        close0(ctlSp[0]);
+        close0(ctlSp[1]);
+        pollsetDestroy(pollset);
+    }
+
+    private void wakeup() {
+        if (wakeupCount.incrementAndGet() == 1) {
+            // write byte to socketpair to force wakeup
+            try {
+                interrupt(sp[1]);
+            } catch (IOException x) {
+                throw new AssertionError(x);
+            }
+        }
+    }
+
+    @Override
+    void executeOnHandlerTask(Runnable task) {
+        synchronized (this) {
+            if (closed)
+                throw new RejectedExecutionException();
+            offerTask(task);
+            wakeup();
+        }
+    }
+
+    @Override
+    void shutdownHandlerTasks() {
+        /*
+         * If no tasks are running then just release resources; otherwise
+         * write to the one end of the socketpair to wakeup any polling threads.
+         */
+        int nThreads = threadCount();
+        if (nThreads == 0) {
+            implClose();
+        } else {
+            // send interrupt to each thread
+            while (nThreads-- > 0) {
+                wakeup();
+            }
+        }
+    }
+
+    // invoke by clients to register a file descriptor
+    @Override
+    void startPoll(int fd, int events) {
+        queueControlEvent(new ControlEvent(fd, events, false));
+    }
+
+    // Callback method for implementations that need special handling when fd is removed
+    @Override
+    protected void preUnregister(int fd) {
+        queueControlEvent(new ControlEvent(fd, 0, true));
+    }
+
+    // Add control event into queue and wait for completion.
+    // In case the control lock is free, this method also tries to apply the control change directly.
+    private void queueControlEvent(ControlEvent ev) {
+        // pollsetCtl blocks when a poll call is ongoing. This is very probable.
+        // Therefore we let the polling thread do the pollsetCtl call.
+        synchronized (controlQueue) {
+            controlQueue.add(ev);
+            // write byte to socketpair to force wakeup
+            try {
+                interrupt(ctlSp[1]);
+            } catch (IOException x) {
+                throw new AssertionError(x);
+            }
+            do {
+                // Directly empty queue if no poll call is ongoing.
+                if (controlLock.tryLock()) {
+                    try {
+                        processControlQueue();
+                    } finally {
+                        controlLock.unlock();
+                    }
+                } else {
+                    try {
+                        // Do not starve in case the polling thread returned before
+                        // we could write to ctlSp[1] but the polling thread did not
+                        // release the control lock until we checked. Therefore, use
+                        // a timed wait for the time being.
+                        controlQueue.wait(100);
+                    } catch (InterruptedException e) {
+                        // ignore exception and try again
+                    }
+                }
+            } while (controlQueue.contains(ev));
+        }
+        if (ev.error() != 0) {
+            throw new AssertionError();
+        }
+    }
+
+    // Process all events currently stored in the control queue.
+    private void processControlQueue() {
+        synchronized (controlQueue) {
+            // TODO: this is ripped straight out of AIX implementation, who knows if it will work for Minerva
+            Iterator<ControlEvent> iter = controlQueue.iterator();
+            while (iter.hasNext()) {
+                ControlEvent ev = iter.next();
+                pollsetCtl(pollset, PS_DELETE, ev.fd(), 0);
+                if (!ev.removeOnly()) {
+                    ev.setError(pollsetCtl(pollset, PS_MOD, ev.fd(), ev.events()));
+                }
+                iter.remove();
+            }
+            controlQueue.notifyAll();
+        }
+    }
+
+    /*
+     * Task to process events from pollset and dispatch to the channel's
+     * onEvent handler.
+     *
+     * Events are retreived from pollset in batch and offered to a BlockingQueue
+     * where they are consumed by handler threads. A special "NEED_TO_POLL"
+     * event is used to signal one consumer to re-poll when all events have
+     * been consumed.
+     */
+    private class EventHandlerTask implements Runnable {
+        private Event poll() throws IOException {
+            try {
+                for (;;) {
+                    int n;
+                    controlLock.lock();
+                    try {
+                        n = pollsetPoll(pollset, address, MAX_POLL_EVENTS);
+                    } finally {
+                        controlLock.unlock();
+                    }
+                    /*
+                     * 'n' events have been read. Here we map them to their
+                     * corresponding channel in batch and queue n-1 so that
+                     * they can be handled by other handler threads. The last
+                     * event is handled by this thread (and so is not queued).
+                     */
+                    fdToChannelLock.readLock().lock();
+                    try {
+                        while (n-- > 0) {
+                            long eventAddress = getEvent(address, n);
+                            int fd = getDescriptor(eventAddress);
+
+                            // To emulate one shot semantic we need to remove
+                            // the file descriptor here.
+                            if (fd != sp[0] && fd != ctlSp[0]) {
+                                synchronized (controlQueue) {
+                                    pollsetCtl(pollset, PS_DELETE, fd, 0);
+                                }
+                            }
+
+                            // wakeup
+                            if (fd == sp[0]) {
+                                if (wakeupCount.decrementAndGet() == 0) {
+                                    // no more wakeups so drain pipe
+                                    drain1(sp[0]);
+                                }
+
+                                // queue special event if there are more events
+                                // to handle.
+                                if (n > 0) {
+                                    queue.offer(EXECUTE_TASK_OR_SHUTDOWN);
+                                    continue;
+                                }
+                                return EXECUTE_TASK_OR_SHUTDOWN;
+                            }
+
+                            // wakeup to process control event
+                            if (fd == ctlSp[0]) {
+                                synchronized (controlQueue) {
+                                    drain1(ctlSp[0]);
+                                    processControlQueue();
+                                }
+                                if (n > 0) {
+                                    continue;
+                                }
+                                return CONTINUE_AFTER_CTL_EVENT;
+                            }
+
+                            PollableChannel channel = fdToChannel.get(fd);
+                            if (channel != null) {
+                                int events = getRevents(eventAddress);
+                                Event ev = new Event(channel, events);
+
+                                // n-1 events are queued; This thread handles
+                                // the last one except for the wakeup
+                                if (n > 0) {
+                                    queue.offer(ev);
+                                } else {
+                                    return ev;
+                                }
+                            }
+                        }
+                    } finally {
+                        fdToChannelLock.readLock().unlock();
+                    }
+                }
+            } finally {
+                // to ensure that some thread will poll when all events have
+                // been consumed
+                queue.offer(NEED_TO_POLL);
+            }
+        }
+
+        public void run() {
+            Invoker.GroupAndInvokeCount myGroupAndInvokeCount =
+                Invoker.getGroupAndInvokeCount();
+            final boolean isPooledThread = (myGroupAndInvokeCount != null);
+            boolean replaceMe = false;
+            Event ev;
+            try {
+                for (;;) {
+                    // reset invoke count
+                    if (isPooledThread)
+                        myGroupAndInvokeCount.resetInvokeCount();
+
+                    try {
+                        replaceMe = false;
+                        ev = queue.take();
+
+                        // no events and this thread has been "selected" to
+                        // poll for more.
+                        if (ev == NEED_TO_POLL) {
+                            try {
+                                ev = poll();
+                            } catch (IOException x) {
+                                x.printStackTrace();
+                                return;
+                            }
+                        }
+                    } catch (InterruptedException x) {
+                        continue;
+                    }
+
+                    // contine after we processed a control event
+                    if (ev == CONTINUE_AFTER_CTL_EVENT) {
+                        continue;
+                    }
+
+                    // handle wakeup to execute task or shutdown
+                    if (ev == EXECUTE_TASK_OR_SHUTDOWN) {
+                        Runnable task = pollTask();
+                        if (task == null) {
+                            // shutdown request
+                            return;
+                        }
+                        // run task (may throw error/exception)
+                        replaceMe = true;
+                        task.run();
+                        continue;
+                    }
+
+                    // process event
+                    try {
+                        ev.channel().onEvent(ev.events(), isPooledThread);
+                    } catch (Error x) {
+                        replaceMe = true; throw x;
+                    } catch (RuntimeException x) {
+                        replaceMe = true; throw x;
+                    }
+                }
+            } finally {
+                // last handler to exit when shutdown releases resources
+                int remaining = threadExit(this, replaceMe);
+                if (remaining == 0 && isShutdown()) {
+                    implClose();
+                }
+            }
+        }
+    }
+
+    /**
+     * Allocates a poll array to handle up to {@code count} events.
+     */
+    private static long allocatePollArray(int count) {
+        return unsafe.allocateMemory(count * SIZEOF_POLLFD);
+    }
+
+    /**
+     * Free a poll array
+     */
+    private static void freePollArray(long address) {
+        unsafe.freeMemory(address);
+    }
+
+    /**
+     * Returns event[i];
+     */
+    private static long getEvent(long address, int i) {
+        return address + (SIZEOF_POLLFD*i);
+    }
+
+    /**
+     * Returns event->fd
+     */
+    private static int getDescriptor(long eventAddress) {
+        return unsafe.getInt(eventAddress + OFFSETOF_FD);
+    }
+
+    /**
+     * Returns event->events
+     */
+    private static int getEvents(long eventAddress) {
+        return unsafe.getChar(eventAddress + OFFSETOF_EVENTS);
+    }
+
+    /**
+     * Returns event->revents
+     */
+    private static int getRevents(long eventAddress) {
+        return unsafe.getChar(eventAddress + OFFSETOF_REVENTS);
+    }
+
+    // -- Native methods --
+
+    private static native void init();
+
+    private static native int eventSize();
+
+    private static native int eventsOffset();
+
+    private static native int reventsOffset();
+
+    private static native int fdOffset();
+
+    private static native int pollsetCreate() throws IOException;
+
+    private static native int pollsetCtl(int pollset, int opcode, int fd, int events);
+
+    private static native int pollsetPoll(int pollset, long pollAddress, int numfds)
+        throws IOException;
+
+    private static native void pollsetDestroy(int pollset);
+
+    private static native void socketpair(int[] sv) throws IOException;
+
+    private static native void interrupt(int fd) throws IOException;
+
+    private static native void drain1(int fd) throws IOException;
+
+    private static native void close0(int fd);
+}
diff --git a/src/java.base/minerva/classes/sun/nio/fs/DefaultFileSystemProvider.java b/src/java.base/minerva/classes/sun/nio/fs/DefaultFileSystemProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..b24f3de0139e5447455417914c47b3bfcff1301c
--- /dev/null
+++ b/src/java.base/minerva/classes/sun/nio/fs/DefaultFileSystemProvider.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.FileSystem;
+
+/**
+ * Creates this platform's default FileSystemProvider.
+ */
+
+public class DefaultFileSystemProvider {
+    private static final MinervaFileSystemProvider INSTANCE
+        = new MinervaFileSystemProvider();
+
+    private DefaultFileSystemProvider() { }
+
+    /**
+     * Returns the platform's default file system provider.
+     */
+    public static MinervaFileSystemProvider instance() {
+        return INSTANCE;
+    }
+
+    /**
+     * Returns the platform's default file system.
+     */
+    public static FileSystem theFileSystem() {
+        return INSTANCE.theFileSystem();
+    }
+}
diff --git a/src/java.base/minerva/classes/sun/nio/fs/MinervaFileStore.java b/src/java.base/minerva/classes/sun/nio/fs/MinervaFileStore.java
new file mode 100644
index 0000000000000000000000000000000000000000..3f408ec9bd370405e4b2287742b3ba09c9233bbc
--- /dev/null
+++ b/src/java.base/minerva/classes/sun/nio/fs/MinervaFileStore.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.attribute.*;
+import java.util.*;
+import java.io.IOException;
+
+/**
+ * AIX implementation of FileStore
+ */
+
+class MinervaFileStore
+    extends UnixFileStore
+{
+
+    MinervaFileStore(UnixPath file) throws IOException {
+        super(file);
+    }
+
+    MinervaFileStore(UnixFileSystem fs, UnixMountEntry entry) throws IOException {
+        super(fs, entry);
+    }
+
+    /**
+     * Finds, and returns, the mount entry for the file system where the file
+     * resides.
+     */
+    @Override
+    UnixMountEntry findMountEntry() throws IOException {
+        MinervaFileSystem fs = (MinervaFileSystem)file().getFileSystem();
+
+        // step 1: get realpath
+        UnixPath path = null;
+        try {
+            byte[] rp = UnixNativeDispatcher.realpath(file());
+            path = new UnixPath(fs, rp);
+        } catch (UnixException x) {
+            x.rethrowAsIOException(file());
+        }
+
+        // step 2: find mount point
+        UnixPath parent = path.getParent();
+        while (parent != null) {
+            UnixFileAttributes attrs = null;
+            try {
+                attrs = UnixFileAttributes.get(parent, true);
+            } catch (UnixException x) {
+                x.rethrowAsIOException(parent);
+            }
+            if (attrs.dev() != dev())
+                break;
+            path = parent;
+            parent = parent.getParent();
+        }
+
+        // step 3: lookup mounted file systems
+        byte[] dir = path.asByteArray();
+        for (UnixMountEntry entry: fs.getMountEntries()) {
+            if (Arrays.equals(dir, entry.dir()))
+                return entry;
+        }
+
+        throw new IOException("Mount point not found");
+    }
+
+    @Override
+    protected boolean isExtendedAttributesEnabled(UnixPath path) {
+        return false;
+    }
+
+    @Override
+    public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
+        return super.supportsFileAttributeView(type);
+    }
+
+    @Override
+    public boolean supportsFileAttributeView(String name) {
+        return super.supportsFileAttributeView(name);
+    }
+}
diff --git a/src/java.base/minerva/classes/sun/nio/fs/MinervaFileSystem.java b/src/java.base/minerva/classes/sun/nio/fs/MinervaFileSystem.java
new file mode 100644
index 0000000000000000000000000000000000000000..bee588a7ebc0e5d3994a05ac733ba6a8e0d34117
--- /dev/null
+++ b/src/java.base/minerva/classes/sun/nio/fs/MinervaFileSystem.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.io.IOException;
+import java.util.*;
+import static sun.nio.fs.MinervaNativeDispatcher.*;
+
+/**
+ * AIX implementation of FileSystem
+ */
+
+class MinervaFileSystem extends UnixFileSystem {
+
+    MinervaFileSystem(UnixFileSystemProvider provider, String dir) {
+        super(provider, dir);
+    }
+
+    @Override
+    public WatchService newWatchService()
+        throws IOException
+    {
+        return new PollingWatchService();
+    }
+
+    // lazy initialization of the list of supported attribute views
+    private static class SupportedFileFileAttributeViewsHolder {
+        static final Set<String> supportedFileAttributeViews =
+            supportedFileAttributeViews();
+        private static Set<String> supportedFileAttributeViews() {
+            Set<String> result = new HashSet<String>();
+            result.addAll(UnixFileSystem.standardFileAttributeViews());
+            return Collections.unmodifiableSet(result);
+        }
+    }
+
+    @Override
+    public Set<String> supportedFileAttributeViews() {
+        return SupportedFileFileAttributeViewsHolder.supportedFileAttributeViews;
+    }
+
+    @Override
+    void copyNonPosixAttributes(int ofd, int nfd) {
+        // TODO: Implement if needed.
+    }
+
+    /**
+     * Returns object to iterate over the mount entries returned by mntctl
+     */
+    @Override
+    Iterable<UnixMountEntry> getMountEntries() {
+        UnixMountEntry[] entries = null;
+        try {
+            entries = getmntctl();
+        } catch (UnixException x) {
+            // nothing we can do
+        }
+        if (entries == null) {
+            return Collections.emptyList();
+        }
+        return Arrays.asList(entries);
+    }
+
+    @Override
+    FileStore getFileStore(UnixMountEntry entry) throws IOException {
+        return new MinervaFileStore(this, entry);
+    }
+}
diff --git a/src/java.base/minerva/classes/sun/nio/fs/MinervaFileSystemProvider.java b/src/java.base/minerva/classes/sun/nio/fs/MinervaFileSystemProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..8582190afb01b4f1415789aa36b2dd11431443c4
--- /dev/null
+++ b/src/java.base/minerva/classes/sun/nio/fs/MinervaFileSystemProvider.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.fs;
+
+import java.io.IOException;
+
+/**
+ * Minerva implementation of FileSystemProvider
+ */
+
+class MinervaFileSystemProvider extends UnixFileSystemProvider {
+    public MinervaFileSystemProvider() {
+        super();
+    }
+
+    @Override
+    MinervaFileSystem newFileSystem(String dir) {
+        return new MinervaFileSystem(this, dir);
+    }
+
+    /**
+     * @see sun.nio.fs.UnixFileSystemProvider#getFileStore(sun.nio.fs.UnixPath)
+     */
+    @Override
+    MinervaFileStore getFileStore(UnixPath path) throws IOException {
+        return new MinervaFileStore(path);
+    }
+}
diff --git a/src/java.base/minerva/classes/sun/nio/fs/MinervaNativeDispatcher.java b/src/java.base/minerva/classes/sun/nio/fs/MinervaNativeDispatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..7c50b719c792a157f53348d00ecfdc6f89a1f726
--- /dev/null
+++ b/src/java.base/minerva/classes/sun/nio/fs/MinervaNativeDispatcher.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.fs;
+
+/**
+ * Minerva specific system calls.
+ */
+
+class MinervaNativeDispatcher extends UnixNativeDispatcher {
+    private MinervaNativeDispatcher() { }
+
+    /**
+     * Special implementation of 'getextmntent' (see SolarisNativeDispatcher)
+     * that returns all entries at once.
+     */
+    static native UnixMountEntry[] getmntctl() throws UnixException;
+
+    // initialize
+    private static native void init();
+
+    static {
+        jdk.internal.loader.BootLoader.loadLibrary("nio");
+        init();
+    }
+}
diff --git a/src/java.base/minerva/native/libnet/minerva_close.c b/src/java.base/minerva/native/libnet/minerva_close.c
new file mode 100644
index 0000000000000000000000000000000000000000..6a177bbb90a59efdcc6734ee68a06fdd12e4fa61
--- /dev/null
+++ b/src/java.base/minerva/native/libnet/minerva_close.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+#include "jvm.h"
+#include "net_util.h"
+
+/*
+ * Stack allocated by thread when doing blocking operation
+ */
+typedef struct threadEntry {
+    pthread_t thr;                      /* this thread */
+    struct threadEntry *next;           /* next thread */
+    int intr;                           /* interrupted */
+} threadEntry_t;
+
+/*
+ * Heap allocated during initialized - one entry per fd
+ */
+typedef struct {
+    pthread_mutex_t lock;               /* fd lock */
+    threadEntry_t *threads;             /* threads blocked on fd */
+} fdEntry_t;
+
+/*
+ * Signal to unblock thread
+ */
+static int sigWakeup = SIGIO;
+
+/*
+ * fdTable holds one entry per file descriptor, up to a certain
+ * maximum.
+ * Theoretically, the number of possible file descriptors can get
+ * large, though usually it does not. Entries for small value file
+ * descriptors are kept in a simple table, which covers most scenarios.
+ * Entries for large value file descriptors are kept in an overflow
+ * table, which is organized as a sparse two dimensional array whose
+ * slabs are allocated on demand. This covers all corner cases while
+ * keeping memory consumption reasonable.
+ */
+
+/* Base table for low value file descriptors */
+static fdEntry_t* fdTable = NULL;
+/* Maximum size of base table (in number of entries). */
+static const int fdTableMaxSize = 0x1000; /* 4K */
+/* Actual size of base table (in number of entries) */
+static int fdTableLen = 0;
+/* Max. theoretical number of file descriptors on system. */
+static int fdLimit = 0;
+
+/* Overflow table, should base table not be large enough. Organized as
+ *   an array of n slabs, each holding 64k entries.
+ */
+static fdEntry_t** fdOverflowTable = NULL;
+/* Number of slabs in the overflow table */
+static int fdOverflowTableLen = 0;
+/* Number of entries in one slab */
+static const int fdOverflowTableSlabSize = 0x10000; /* 64k */
+pthread_mutex_t fdOverflowTableLock = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * Null signal handler
+ */
+static void sig_wakeup(int sig) {
+}
+
+/*
+ * Initialization routine (executed when library is loaded)
+ * Allocate fd tables and sets up signal handler.
+ */
+static void __attribute((constructor)) init() {
+    struct rlimit nbr_files;
+    sigset_t sigset;
+    struct sigaction sa;
+    int i = 0;
+
+    /* Determine the maximum number of possible file descriptors. */
+    if (-1 == getrlimit(RLIMIT_NOFILE, &nbr_files)) {
+        fprintf(stderr, "library initialization failed - "
+                "unable to get max # of allocated fds\n");
+        abort();
+    }
+    if (nbr_files.rlim_max != RLIM_INFINITY) {
+        fdLimit = nbr_files.rlim_max;
+    } else {
+        /* We just do not know. */
+        fdLimit = INT_MAX;
+    }
+
+    /* Allocate table for low value file descriptors. */
+    fdTableLen = fdLimit < fdTableMaxSize ? fdLimit : fdTableMaxSize;
+    fdTable = (fdEntry_t*) calloc(fdTableLen, sizeof(fdEntry_t));
+    if (fdTable == NULL) {
+        fprintf(stderr, "library initialization failed - "
+                "unable to allocate file descriptor table - out of memory");
+        abort();
+    } else {
+        for (i = 0; i < fdTableLen; i ++) {
+            pthread_mutex_init(&fdTable[i].lock, NULL);
+        }
+    }
+
+    /* Allocate overflow table, if needed */
+    if (fdLimit > fdTableMaxSize) {
+        fdOverflowTableLen = ((fdLimit - fdTableMaxSize) / fdOverflowTableSlabSize) + 1;
+        fdOverflowTable = (fdEntry_t**) calloc(fdOverflowTableLen, sizeof(fdEntry_t*));
+        if (fdOverflowTable == NULL) {
+            fprintf(stderr, "library initialization failed - "
+                    "unable to allocate file descriptor overflow table - out of memory");
+            abort();
+        }
+    }
+
+    /*
+     * Setup the signal handler
+     */
+    sa.sa_handler = sig_wakeup;
+    sa.sa_flags   = 0;
+    sigemptyset(&sa.sa_mask);
+    sigaction(sigWakeup, &sa, NULL);
+
+    sigemptyset(&sigset);
+    sigaddset(&sigset, sigWakeup);
+    sigprocmask(SIG_UNBLOCK, &sigset, NULL);
+}
+
+/*
+ * Return the fd table for this fd.
+ */
+static inline fdEntry_t *getFdEntry(int fd)
+{
+    fdEntry_t* result = NULL;
+
+    if (fd < 0) {
+        return NULL;
+    }
+
+    /* This should not happen. If it does, our assumption about
+     * max. fd value was wrong. */
+    assert(fd < fdLimit);
+
+    if (fd < fdTableMaxSize) {
+        /* fd is in base table. */
+        assert(fd < fdTableLen);
+        result = &fdTable[fd];
+    } else {
+        /* fd is in overflow table. */
+        const int indexInOverflowTable = fd - fdTableMaxSize;
+        const int rootindex = indexInOverflowTable / fdOverflowTableSlabSize;
+        const int slabindex = indexInOverflowTable % fdOverflowTableSlabSize;
+        fdEntry_t* slab = NULL;
+        assert(rootindex < fdOverflowTableLen);
+        assert(slabindex < fdOverflowTableSlabSize);
+        pthread_mutex_lock(&fdOverflowTableLock);
+        /* Allocate new slab in overflow table if needed */
+        if (fdOverflowTable[rootindex] == NULL) {
+            fdEntry_t* const newSlab =
+                (fdEntry_t*)calloc(fdOverflowTableSlabSize, sizeof(fdEntry_t));
+            if (newSlab == NULL) {
+                fprintf(stderr, "Unable to allocate file descriptor overflow"
+                        " table slab - out of memory");
+                pthread_mutex_unlock(&fdOverflowTableLock);
+                abort();
+            } else {
+                int i;
+                for (i = 0; i < fdOverflowTableSlabSize; i ++) {
+                    pthread_mutex_init(&newSlab[i].lock, NULL);
+                }
+                fdOverflowTable[rootindex] = newSlab;
+            }
+        }
+        pthread_mutex_unlock(&fdOverflowTableLock);
+        slab = fdOverflowTable[rootindex];
+        result = &slab[slabindex];
+    }
+
+    return result;
+
+}
+
+
+/*
+ * Start a blocking operation :-
+ *    Insert thread onto thread list for the fd.
+ */
+static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self)
+{
+    self->thr = pthread_self();
+    self->intr = 0;
+
+    pthread_mutex_lock(&(fdEntry->lock));
+    {
+        self->next = fdEntry->threads;
+        fdEntry->threads = self;
+    }
+    pthread_mutex_unlock(&(fdEntry->lock));
+}
+
+/*
+ * End a blocking operation :-
+ *     Remove thread from thread list for the fd
+ *     If fd has been interrupted then set errno to EBADF
+ */
+static inline void endOp
+    (fdEntry_t *fdEntry, threadEntry_t *self)
+{
+    int orig_errno = errno;
+    pthread_mutex_lock(&(fdEntry->lock));
+    {
+        threadEntry_t *curr, *prev=NULL;
+        curr = fdEntry->threads;
+        while (curr != NULL) {
+            if (curr == self) {
+                if (curr->intr) {
+                    orig_errno = EBADF;
+                }
+                if (prev == NULL) {
+                    fdEntry->threads = curr->next;
+                } else {
+                    prev->next = curr->next;
+                }
+                break;
+            }
+            prev = curr;
+            curr = curr->next;
+        }
+    }
+    pthread_mutex_unlock(&(fdEntry->lock));
+    errno = orig_errno;
+}
+
+/*
+ * Close or dup2 a file descriptor ensuring that all threads blocked on
+ * the file descriptor are notified via a wakeup signal.
+ *
+ *      fd1 < 0    => close(fd2)
+ *      fd1 >= 0   => dup2(fd1, fd2)
+ *
+ * Returns -1 with errno set if operation fails.
+ */
+static int closefd(int fd1, int fd2) {
+    int rv, orig_errno;
+    fdEntry_t *fdEntry = getFdEntry(fd2);
+    if (fdEntry == NULL) {
+        errno = EBADF;
+        return -1;
+    }
+
+    /*
+     * Lock the fd to hold-off additional I/O on this fd.
+     */
+    pthread_mutex_lock(&(fdEntry->lock));
+
+    {
+        /*
+         * Send a wakeup signal to all threads blocked on this
+         * file descriptor.
+         */
+        threadEntry_t *curr = fdEntry->threads;
+        while (curr != NULL) {
+            curr->intr = 1;
+            pthread_kill( curr->thr, sigWakeup );
+            curr = curr->next;
+        }
+
+        /*
+         * And close/dup the file descriptor
+         * (restart if interrupted by signal)
+         */
+        do {
+            if (fd1 < 0) {
+                rv = close(fd2);
+            } else {
+                rv = dup2(fd1, fd2);
+            }
+        } while (rv == -1 && errno == EINTR);
+
+    }
+
+    /*
+     * Unlock without destroying errno
+     */
+    orig_errno = errno;
+    pthread_mutex_unlock(&(fdEntry->lock));
+    errno = orig_errno;
+
+    return rv;
+}
+
+/*
+ * Wrapper for dup2 - same semantics as dup2 system call except
+ * that any threads blocked in an I/O system call on fd2 will be
+ * preempted and return -1/EBADF;
+ */
+int NET_Dup2(int fd, int fd2) {
+    if (fd < 0) {
+        errno = EBADF;
+        return -1;
+    }
+    return closefd(fd, fd2);
+}
+
+/*
+ * Wrapper for close - same semantics as close system call
+ * except that any threads blocked in an I/O on fd will be
+ * preempted and the I/O system call will return -1/EBADF.
+ */
+int NET_SocketClose(int fd) {
+    return closefd(-1, fd);
+}
+
+/************** Basic I/O operations here ***************/
+
+/*
+ * Macro to perform a blocking IO operation. Restarts
+ * automatically if interrupted by signal (other than
+ * our wakeup signal)
+ */
+#define BLOCKING_IO_RETURN_INT(FD, FUNC) {      \
+    int ret;                                    \
+    threadEntry_t self;                         \
+    fdEntry_t *fdEntry = getFdEntry(FD);        \
+    if (fdEntry == NULL) {                      \
+        errno = EBADF;                          \
+        return -1;                              \
+    }                                           \
+    do {                                        \
+        startOp(fdEntry, &self);                \
+        ret = FUNC;                             \
+        endOp(fdEntry, &self);                  \
+    } while (ret == -1 && errno == EINTR);      \
+    return ret;                                 \
+}
+
+int NET_Read(int s, void* buf, size_t len) {
+    BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, 0) );
+}
+
+int NET_NonBlockingRead(int s, void* buf, size_t len) {
+    BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, MSG_DONTWAIT));
+}
+
+int NET_RecvFrom(int s, void *buf, int len, unsigned int flags,
+       struct sockaddr *from, socklen_t *fromlen) {
+    BLOCKING_IO_RETURN_INT( s, recvfrom(s, buf, len, flags, from, fromlen) );
+}
+
+int NET_Send(int s, void *msg, int len, unsigned int flags) {
+    BLOCKING_IO_RETURN_INT( s, send(s, msg, len, flags) );
+}
+
+int NET_SendTo(int s, const void *msg, int len,  unsigned  int
+       flags, const struct sockaddr *to, int tolen) {
+    BLOCKING_IO_RETURN_INT( s, sendto(s, msg, len, flags, to, tolen) );
+}
+
+int NET_Accept(int s, struct sockaddr *addr, socklen_t *addrlen) {
+    BLOCKING_IO_RETURN_INT( s, accept(s, addr, addrlen) );
+}
+
+int NET_Connect(int s, struct sockaddr *addr, int addrlen) {
+    BLOCKING_IO_RETURN_INT( s, connect(s, addr, addrlen) );
+}
+
+int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) {
+    BLOCKING_IO_RETURN_INT( ufds[0].fd, poll(ufds, nfds, timeout) );
+}
+
+/*
+ * Wrapper for poll(s, timeout).
+ * Auto restarts with adjusted timeout if interrupted by
+ * signal other than our wakeup signal.
+ */
+int NET_Timeout(JNIEnv *env, int s, long timeout, jlong nanoTimeStamp) {
+    jlong prevNanoTime = nanoTimeStamp;
+    jlong nanoTimeout = (jlong)timeout * NET_NSEC_PER_MSEC;
+    fdEntry_t *fdEntry = getFdEntry(s);
+
+    /*
+     * Check that fd hasn't been closed.
+     */
+    if (fdEntry == NULL) {
+        errno = EBADF;
+        return -1;
+    }
+
+    for(;;) {
+        struct pollfd pfd;
+        int rv;
+        threadEntry_t self;
+
+        /*
+         * Poll the fd. If interrupted by our wakeup signal
+         * errno will be set to EBADF.
+         */
+        pfd.fd = s;
+        pfd.events = POLLIN | POLLERR;
+
+        startOp(fdEntry, &self);
+        rv = poll(&pfd, 1, nanoTimeout / NET_NSEC_PER_MSEC);
+        endOp(fdEntry, &self);
+        /*
+         * If interrupted then adjust timeout. If timeout
+         * has expired return 0 (indicating timeout expired).
+         */
+        if (rv < 0 && errno == EINTR) {
+            if (timeout > 0) {
+                jlong newNanoTime = JVM_NanoTime(env, 0);
+                nanoTimeout -= newNanoTime - prevNanoTime;
+                if (nanoTimeout < NET_NSEC_PER_MSEC) {
+                    return 0;
+                }
+                prevNanoTime = newNanoTime;
+            } else {
+                continue; // timeout is -1, so  loop again.
+            }
+        } else {
+            return rv;
+        }
+    }
+}
diff --git a/src/jdk.attach/minerva/classes/sun/tools/attach/AttachProviderImpl.java b/src/jdk.attach/minerva/classes/sun/tools/attach/AttachProviderImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..2f6fc4d4df22ccfda0c36d5ce1cbb6704ec05bbf
--- /dev/null
+++ b/src/jdk.attach/minerva/classes/sun/tools/attach/AttachProviderImpl.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.tools.attach;
+
+import com.sun.tools.attach.VirtualMachine;
+import com.sun.tools.attach.VirtualMachineDescriptor;
+import com.sun.tools.attach.AttachNotSupportedException;
+import java.io.IOException;
+
+// Based on linux/classes/sun/tools/attach/AttachProviderImpl.java.
+
+/*
+ * An AttachProvider implementation for Aix that uses a UNIX domain
+ * socket.
+ */
+public class AttachProviderImpl extends HotSpotAttachProvider {
+
+    public AttachProviderImpl() {
+    }
+
+    public String name() {
+        return "sun";
+    }
+
+    public String type() {
+        return "socket";
+    }
+
+    public VirtualMachine attachVirtualMachine(String vmid)
+        throws AttachNotSupportedException, IOException
+    {
+        checkAttachPermission();
+
+        // AttachNotSupportedException will be thrown if the target VM can be determined
+        // to be not attachable.
+        testAttachable(vmid);
+
+        return new VirtualMachineImpl(this, vmid);
+    }
+
+    public VirtualMachine attachVirtualMachine(VirtualMachineDescriptor vmd)
+        throws AttachNotSupportedException, IOException
+    {
+        if (vmd.provider() != this) {
+            throw new AttachNotSupportedException("provider mismatch");
+        }
+        // To avoid re-checking if the VM if attachable, we check if the descriptor
+        // is for a hotspot VM - these descriptors are created by the listVirtualMachines
+        // implementation which only returns a list of attachable VMs.
+        if (vmd instanceof HotSpotVirtualMachineDescriptor) {
+            assert ((HotSpotVirtualMachineDescriptor)vmd).isAttachable();
+            checkAttachPermission();
+            return new VirtualMachineImpl(this, vmd.id());
+        } else {
+            return attachVirtualMachine(vmd.id());
+        }
+    }
+
+}
diff --git a/src/jdk.attach/minerva/classes/sun/tools/attach/VirtualMachineImpl.java b/src/jdk.attach/minerva/classes/sun/tools/attach/VirtualMachineImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..0c432edeee3c7d5241006d1aaaf68c1b6d81dd3c
--- /dev/null
+++ b/src/jdk.attach/minerva/classes/sun/tools/attach/VirtualMachineImpl.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.tools.attach;
+
+import com.sun.tools.attach.AttachOperationFailedException;
+import com.sun.tools.attach.AgentLoadException;
+import com.sun.tools.attach.AttachNotSupportedException;
+import com.sun.tools.attach.spi.AttachProvider;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.File;
+
+/*
+ * Aix implementation of HotSpotVirtualMachine
+ */
+public class VirtualMachineImpl extends HotSpotVirtualMachine {
+    // "/tmp" is used as a global well-known location for the files
+    // .java_pid<pid>. and .attach_pid<pid>. It is important that this
+    // location is the same for all processes, otherwise the tools
+    // will not be able to find all Hotspot processes.
+    // Any changes to this needs to be synchronized with HotSpot.
+    private static final String tmpdir = "/tmp";
+    String socket_path;
+
+    /**
+     * Attaches to the target VM
+     */
+    VirtualMachineImpl(AttachProvider provider, String vmid)
+        throws AttachNotSupportedException, IOException
+    {
+        super(provider, vmid);
+
+        // This provider only understands pids
+        int pid;
+        try {
+            pid = Integer.parseInt(vmid);
+            if (pid < 1) {
+                throw new NumberFormatException();
+            }
+        } catch (NumberFormatException x) {
+            throw new AttachNotSupportedException("Invalid process identifier: " + vmid);
+        }
+
+        // Find the socket file. If not found then we attempt to start the
+        // attach mechanism in the target VM by sending it a QUIT signal.
+        // Then we attempt to find the socket file again.
+        File socket_file = new File(tmpdir, ".java_pid" + pid);
+        socket_path = socket_file.getPath();
+        if (!socket_file.exists()) {
+            // Keep canonical version of File, to delete, in case target process ends and /proc link has gone:
+            File f = createAttachFile(pid).getCanonicalFile();
+            try {
+                sendQuitTo(pid);
+
+                // give the target VM time to start the attach mechanism
+                final int delay_step = 100;
+                final long timeout = attachTimeout();
+                long time_spend = 0;
+                long delay = 0;
+                do {
+                    // Increase timeout on each attempt to reduce polling
+                    delay += delay_step;
+                    try {
+                        Thread.sleep(delay);
+                    } catch (InterruptedException x) { }
+
+                    time_spend += delay;
+                    if (time_spend > timeout/2 && !socket_file.exists()) {
+                        // Send QUIT again to give target VM the last chance to react
+                        sendQuitTo(pid);
+                    }
+                } while (time_spend <= timeout && !socket_file.exists());
+                if (!socket_file.exists()) {
+                    throw new AttachNotSupportedException(
+                        String.format("Unable to open socket file %s: " +
+                          "target process %d doesn't respond within %dms " +
+                           "or HotSpot VM not loaded", socket_path, pid,
+                                      time_spend));
+                }
+            } finally {
+                f.delete();
+            }
+        }
+
+        // Check that the file owner/permission to avoid attaching to
+        // bogus process
+        checkPermissions(socket_path);
+
+        // Check that we can connect to the process
+        // - this ensures we throw the permission denied error now rather than
+        // later when we attempt to enqueue a command.
+        int s = socket();
+        try {
+            connect(s, socket_path);
+        } finally {
+            close(s);
+        }
+    }
+
+    /**
+     * Detach from the target VM
+     */
+    public void detach() throws IOException {
+        synchronized (this) {
+            if (socket_path != null) {
+                socket_path = null;
+            }
+        }
+    }
+
+    // protocol version
+    private final static String PROTOCOL_VERSION = "1";
+
+    // known errors
+    private final static int ATTACH_ERROR_BADVERSION = 101;
+
+    /**
+     * Execute the given command in the target VM.
+     */
+    InputStream execute(String cmd, Object ... args) throws AgentLoadException, IOException {
+        assert args.length <= 3;                // includes null
+
+        // did we detach?
+        synchronized (this) {
+            if (socket_path == null) {
+                throw new IOException("Detached from target VM");
+            }
+        }
+
+        // create UNIX socket
+        int s = socket();
+
+        // connect to target VM
+        try {
+            connect(s, socket_path);
+        } catch (IOException x) {
+            close(s);
+            throw x;
+        }
+
+        IOException ioe = null;
+
+        // connected - write request
+        // <ver> <cmd> <args...>
+        try {
+            writeString(s, PROTOCOL_VERSION);
+            writeString(s, cmd);
+
+            for (int i = 0; i < 3; i++) {
+                if (i < args.length && args[i] != null) {
+                    writeString(s, (String)args[i]);
+                } else {
+                    writeString(s, "");
+                }
+            }
+        } catch (IOException x) {
+            ioe = x;
+        }
+
+
+        // Create an input stream to read reply
+        SocketInputStream sis = new SocketInputStream(s);
+
+        // Read the command completion status
+        int completionStatus;
+        try {
+            completionStatus = readInt(sis);
+        } catch (IOException x) {
+            sis.close();
+            if (ioe != null) {
+                throw ioe;
+            } else {
+                throw x;
+            }
+        }
+
+        if (completionStatus != 0) {
+            // read from the stream and use that as the error message
+            String message = readErrorMessage(sis);
+            sis.close();
+
+            // In the event of a protocol mismatch then the target VM
+            // returns a known error so that we can throw a reasonable
+            // error.
+            if (completionStatus == ATTACH_ERROR_BADVERSION) {
+                throw new IOException("Protocol mismatch with target VM");
+            }
+
+            // Special-case the "load" command so that the right exception is
+            // thrown.
+            if (cmd.equals("load")) {
+                String msg = "Failed to load agent library";
+                if (!message.isEmpty())
+                    msg += ": " + message;
+                throw new AgentLoadException(msg);
+            } else {
+                if (message.isEmpty())
+                    message = "Command failed in target VM";
+                throw new AttachOperationFailedException(message);
+            }
+        }
+
+        // Return the input stream so that the command output can be read
+        return sis;
+    }
+
+    /*
+     * InputStream for the socket connection to get target VM
+     */
+    private class SocketInputStream extends InputStream {
+        int s;
+
+        public SocketInputStream(int s) {
+            this.s = s;
+        }
+
+        public synchronized int read() throws IOException {
+            byte b[] = new byte[1];
+            int n = this.read(b, 0, 1);
+            if (n == 1) {
+                return b[0] & 0xff;
+            } else {
+                return -1;
+            }
+        }
+
+        public synchronized int read(byte[] bs, int off, int len) throws IOException {
+            if ((off < 0) || (off > bs.length) || (len < 0) ||
+                ((off + len) > bs.length) || ((off + len) < 0)) {
+                throw new IndexOutOfBoundsException();
+            } else if (len == 0)
+                return 0;
+
+            return VirtualMachineImpl.read(s, bs, off, len);
+        }
+
+        public synchronized void close() throws IOException {
+            if (s != -1) {
+                int toClose = s;
+                s = -1;
+                VirtualMachineImpl.close(toClose);
+            }
+        }
+    }
+
+    // On Aix a simple handshake is used to start the attach mechanism
+    // if not already started. The client creates a .attach_pid<pid> file in the
+    // target VM's working directory (or temp directory), and the SIGQUIT handler
+    // checks for the file.
+    private File createAttachFile(int pid) throws IOException {
+        String fn = ".attach_pid" + pid;
+        String path = "/proc/" + pid + "/cwd/" + fn;
+        File f = new File(path);
+        try {
+            f.createNewFile();
+        } catch (IOException x) {
+            f = new File(tmpdir, fn);
+            f.createNewFile();
+        }
+        return f;
+    }
+
+    /*
+     * Write/sends the given to the target VM. String is transmitted in
+     * UTF-8 encoding.
+     */
+    private void writeString(int fd, String s) throws IOException {
+        if (s.length() > 0) {
+            byte b[];
+            try {
+                b = s.getBytes("UTF-8");
+            } catch (java.io.UnsupportedEncodingException x) {
+                throw new InternalError(x);
+            }
+            VirtualMachineImpl.write(fd, b, 0, b.length);
+        }
+        byte b[] = new byte[1];
+        b[0] = 0;
+        write(fd, b, 0, 1);
+    }
+
+
+    //-- native methods
+
+    static native void sendQuitTo(int pid) throws IOException;
+
+    static native void checkPermissions(String path) throws IOException;
+
+    static native int socket() throws IOException;
+
+    static native void connect(int fd, String path) throws IOException;
+
+    static native void close(int fd) throws IOException;
+
+    static native int read(int fd, byte buf[], int off, int bufLen) throws IOException;
+
+    static native void write(int fd, byte buf[], int off, int bufLen) throws IOException;
+
+    static {
+        System.loadLibrary("attach");
+    }
+}
diff --git a/src/jdk.attach/minerva/native/libattach/VirtualMachineImpl.c b/src/jdk.attach/minerva/native/libattach/VirtualMachineImpl.c
new file mode 100644
index 0000000000000000000000000000000000000000..d20a6f012f20be4d3ef49b4b05417b92c1f42975
--- /dev/null
+++ b/src/jdk.attach/minerva/native/libattach/VirtualMachineImpl.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "jni_util.h"
+
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syslimits.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sun_tools_attach_VirtualMachineImpl.h"
+
+#define RESTARTABLE(_cmd, _result) do { \
+  do { \
+    _result = _cmd; \
+  } while((_result == -1) && (errno == EINTR)); \
+} while(0)
+
+#define ROOT_UID 0
+
+/*
+ * Declare library specific JNI_Onload entry if static build
+ */
+DEF_STATIC_JNI_OnLoad
+
+/*
+ * Class:     sun_tools_attach_VirtualMachineImpl
+ * Method:    socket
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_socket
+  (JNIEnv *env, jclass cls)
+{
+    int fd = socket(PF_UNIX, SOCK_STREAM, 0);
+    if (fd == -1) {
+        JNU_ThrowIOExceptionWithLastError(env, "socket");
+    }
+    return (jint)fd;
+}
+
+/*
+ * Class:     sun_tools_attach_VirtualMachineImpl
+ * Method:    connect
+ * Signature: (ILjava/lang/String;)I
+ */
+JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connect
+  (JNIEnv *env, jclass cls, jint fd, jstring path)
+{
+    jboolean isCopy;
+    const char* p = GetStringPlatformChars(env, path, &isCopy);
+    if (p != NULL) {
+        struct sockaddr_un addr;
+        int err = 0;
+
+        memset(&addr, 0, sizeof(addr));
+        addr.sun_family = AF_UNIX;
+        /* strncpy is safe because addr.sun_path was zero-initialized before. */
+        strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1);
+
+        if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
+            err = errno;
+        }
+
+        if (isCopy) {
+            JNU_ReleaseStringPlatformChars(env, path, p);
+        }
+
+        /*
+         * If the connect failed then we throw the appropriate exception
+         * here (can't throw it before releasing the string as can't call
+         * JNI with pending exception)
+         */
+        if (err != 0) {
+            if (err == ENOENT) {
+                JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL);
+            } else {
+                char* msg = strdup(strerror(err));
+                JNU_ThrowIOException(env, msg);
+                if (msg != NULL) {
+                    free(msg);
+                }
+            }
+        }
+    }
+}
+
+/*
+ * Class:     sun_tools_attach_VirtualMachineImpl
+ * Method:    sendQuitTo
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_sendQuitTo
+  (JNIEnv *env, jclass cls, jint pid)
+{
+    if (kill((pid_t)pid, SIGQUIT)) {
+        JNU_ThrowIOExceptionWithLastError(env, "kill");
+    }
+}
+
+/*
+ * Class:     sun_tools_attach_VirtualMachineImpl
+ * Method:    checkPermissions
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_checkPermissions
+  (JNIEnv *env, jclass cls, jstring path)
+{
+    jboolean isCopy;
+    const char* p = GetStringPlatformChars(env, path, &isCopy);
+    if (p != NULL) {
+        struct stat sb;
+        uid_t uid, gid;
+        int res;
+
+        memset(&sb, 0, sizeof(struct stat));
+
+        /*
+         * Check that the path is owned by the effective uid/gid of this
+         * process. Also check that group/other access is not allowed.
+         */
+        uid = geteuid();
+        gid = getegid();
+
+        res = stat(p, &sb);
+        if (res != 0) {
+            /* save errno */
+            res = errno;
+        }
+
+        if (res == 0) {
+            char msg[100];
+            jboolean isError = JNI_FALSE;
+            if (sb.st_uid != uid && uid != ROOT_UID) {
+                snprintf(msg, sizeof(msg),
+                    "file should be owned by the current user (which is %d) but is owned by %d", uid, sb.st_uid);
+                isError = JNI_TRUE;
+            } else if (sb.st_gid != gid && uid != ROOT_UID) {
+                snprintf(msg, sizeof(msg),
+                    "file's group should be the current group (which is %d) but the group is %d", gid, sb.st_gid);
+                isError = JNI_TRUE;
+            } else if ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) {
+                snprintf(msg, sizeof(msg),
+                    "file should only be readable and writable by the owner but has 0%03o access", sb.st_mode & 0777);
+                isError = JNI_TRUE;
+            }
+            if (isError) {
+                char buf[256];
+                snprintf(buf, sizeof(buf), "well-known file %s is not secure: %s", p, msg);
+                JNU_ThrowIOException(env, buf);
+            }
+        } else {
+            char* msg = strdup(strerror(res));
+            JNU_ThrowIOException(env, msg);
+            if (msg != NULL) {
+                free(msg);
+            }
+        }
+
+        if (isCopy) {
+            JNU_ReleaseStringPlatformChars(env, path, p);
+        }
+    }
+}
+
+/*
+ * Class:     sun_tools_attach_VirtualMachineImpl
+ * Method:    close
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_close
+  (JNIEnv *env, jclass cls, jint fd)
+{
+    int res;
+    shutdown(fd, SHUT_RDWR);
+    RESTARTABLE(close(fd), res);
+}
+
+/*
+ * Class:     sun_tools_attach_VirtualMachineImpl
+ * Method:    read
+ * Signature: (I[BI)I
+ */
+JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_read
+  (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen)
+{
+    unsigned char buf[128];
+    size_t len = sizeof(buf);
+    ssize_t n;
+
+    size_t remaining = (size_t)(baLen - off);
+    if (len > remaining) {
+        len = remaining;
+    }
+
+    RESTARTABLE(read(fd, buf, len), n);
+    if (n == -1) {
+        JNU_ThrowIOExceptionWithLastError(env, "read");
+    } else {
+        if (n == 0) {
+            n = -1;     // EOF
+        } else {
+            (*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf));
+        }
+    }
+    return n;
+}
+
+/*
+ * Class:     sun_tools_attach_VirtualMachineImpl
+ * Method:    write
+ * Signature: (I[B)V
+ */
+JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_write
+  (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint bufLen)
+{
+    size_t remaining = bufLen;
+    do {
+        unsigned char buf[128];
+        size_t len = sizeof(buf);
+        int n;
+
+        if (len > remaining) {
+            len = remaining;
+        }
+        (*env)->GetByteArrayRegion(env, ba, off, len, (jbyte *)buf);
+
+        RESTARTABLE(write(fd, buf, len), n);
+        if (n > 0) {
+            off += n;
+            remaining -= n;
+        } else {
+            JNU_ThrowIOExceptionWithLastError(env, "write");
+            return;
+        }
+
+    } while (remaining > 0);
+}
+
+/*
+ * Class:     sun_tools_attach_BSDVirtualMachine
+ * Method:    createAttachFile
+ * Signature: (Ljava.lang.String;)V
+ */
+JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_createAttachFile0(JNIEnv *env, jclass cls, jstring path)
+{
+    const char* _path;
+    jboolean isCopy;
+    int fd, rc;
+
+    _path = GetStringPlatformChars(env, path, &isCopy);
+    if (_path == NULL) {
+        JNU_ThrowIOException(env, "Must specify a path");
+        return;
+    }
+
+    RESTARTABLE(open(_path, O_CREAT | O_EXCL, S_IWUSR | S_IRUSR), fd);
+    if (fd == -1) {
+        /* release p here before we throw an I/O exception */
+        if (isCopy) {
+            JNU_ReleaseStringPlatformChars(env, path, _path);
+        }
+        JNU_ThrowIOExceptionWithLastError(env, "open");
+        return;
+    }
+
+    RESTARTABLE(chown(_path, geteuid(), getegid()), rc);
+
+    RESTARTABLE(close(fd), rc);
+
+    /* release p here */
+    if (isCopy) {
+        JNU_ReleaseStringPlatformChars(env, path, _path);
+    }
+}
+
+/*
+ * Class:     sun_tools_attach_BSDVirtualMachine
+ * Method:    getTempDir
+ * Signature: (V)Ljava.lang.String;
+ */
+JNIEXPORT jstring JNICALL Java_sun_tools_attach_VirtualMachineImpl_getTempDir(JNIEnv *env, jclass cls)
+{
+    // This must be hard coded because it's the system's temporary
+    // directory not the java application's temp directory, ala java.io.tmpdir.
+
+#ifdef __APPLE__
+    // macosx has a secure per-user temporary directory.
+    // Don't cache the result as this is only called once.
+    char path[PATH_MAX];
+    int pathSize = confstr(_CS_DARWIN_USER_TEMP_DIR, path, PATH_MAX);
+    if (pathSize == 0 || pathSize > PATH_MAX) {
+        strlcpy(path, "/tmp", sizeof(path));
+    }
+    return JNU_NewStringPlatform(env, path);
+#else /* __APPLE__ */
+    return (*env)->NewStringUTF(env, "/tmp");
+#endif /* __APPLE__ */
+}
