mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 23:34:52 +02:00
8187443: Forest Consolidation: Move files to unified layout
Reviewed-by: darcy, ihse
This commit is contained in:
parent
270fe13182
commit
3789983e89
56923 changed files with 3 additions and 15727 deletions
|
@ -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 LinuxAsynchronousChannelProvider();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 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.SelectorProvider;
|
||||
|
||||
/**
|
||||
* Creates this platform's default SelectorProvider
|
||||
*/
|
||||
|
||||
public class DefaultSelectorProvider {
|
||||
|
||||
/**
|
||||
* Prevent instantiation.
|
||||
*/
|
||||
private DefaultSelectorProvider() { }
|
||||
|
||||
/**
|
||||
* Returns the default SelectorProvider.
|
||||
*/
|
||||
public static SelectorProvider create() {
|
||||
return new EPollSelectorProvider();
|
||||
}
|
||||
}
|
118
src/java.base/linux/classes/sun/nio/ch/EPoll.java
Normal file
118
src/java.base/linux/classes/sun/nio/ch/EPoll.java
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2013, 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.io.IOException;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
/**
|
||||
* Provides access to the Linux epoll facility.
|
||||
*/
|
||||
|
||||
class EPoll {
|
||||
private EPoll() { }
|
||||
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
/**
|
||||
* typedef union epoll_data {
|
||||
* void *ptr;
|
||||
* int fd;
|
||||
* __uint32_t u32;
|
||||
* __uint64_t u64;
|
||||
* } epoll_data_t;
|
||||
*
|
||||
* struct epoll_event {
|
||||
* __uint32_t events;
|
||||
* epoll_data_t data;
|
||||
* }
|
||||
*/
|
||||
private static final int SIZEOF_EPOLLEVENT = eventSize();
|
||||
private static final int OFFSETOF_EVENTS = eventsOffset();
|
||||
private static final int OFFSETOF_FD = dataOffset();
|
||||
|
||||
// opcodes
|
||||
static final int EPOLL_CTL_ADD = 1;
|
||||
static final int EPOLL_CTL_DEL = 2;
|
||||
static final int EPOLL_CTL_MOD = 3;
|
||||
|
||||
// flags
|
||||
static final int EPOLLONESHOT = (1 << 30);
|
||||
|
||||
/**
|
||||
* Allocates a poll array to handle up to {@code count} events.
|
||||
*/
|
||||
static long allocatePollArray(int count) {
|
||||
return unsafe.allocateMemory(count * SIZEOF_EPOLLEVENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a poll array
|
||||
*/
|
||||
static void freePollArray(long address) {
|
||||
unsafe.freeMemory(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns event[i];
|
||||
*/
|
||||
static long getEvent(long address, int i) {
|
||||
return address + (SIZEOF_EPOLLEVENT*i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns event->data.fd
|
||||
*/
|
||||
static int getDescriptor(long eventAddress) {
|
||||
return unsafe.getInt(eventAddress + OFFSETOF_FD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns event->events
|
||||
*/
|
||||
static int getEvents(long eventAddress) {
|
||||
return unsafe.getInt(eventAddress + OFFSETOF_EVENTS);
|
||||
}
|
||||
|
||||
// -- Native methods --
|
||||
|
||||
private static native int eventSize();
|
||||
|
||||
private static native int eventsOffset();
|
||||
|
||||
private static native int dataOffset();
|
||||
|
||||
static native int epollCreate() throws IOException;
|
||||
|
||||
static native int epollCtl(int epfd, int opcode, int fd, int events);
|
||||
|
||||
static native int epollWait(int epfd, long pollAddress, int numfds)
|
||||
throws IOException;
|
||||
|
||||
static {
|
||||
IOUtil.load();
|
||||
}
|
||||
}
|
341
src/java.base/linux/classes/sun/nio/ch/EPollArrayWrapper.java
Normal file
341
src/java.base/linux/classes/sun/nio/ch/EPollArrayWrapper.java
Normal file
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2016, 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.io.IOException;
|
||||
import java.security.AccessController;
|
||||
import java.util.BitSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import sun.security.action.GetIntegerAction;
|
||||
|
||||
/**
|
||||
* Manipulates a native array of epoll_event structs on Linux:
|
||||
*
|
||||
* typedef union epoll_data {
|
||||
* void *ptr;
|
||||
* int fd;
|
||||
* __uint32_t u32;
|
||||
* __uint64_t u64;
|
||||
* } epoll_data_t;
|
||||
*
|
||||
* struct epoll_event {
|
||||
* __uint32_t events;
|
||||
* epoll_data_t data;
|
||||
* };
|
||||
*
|
||||
* The system call to wait for I/O events is epoll_wait(2). It populates an
|
||||
* array of epoll_event structures that are passed to the call. The data
|
||||
* member of the epoll_event structure contains the same data as was set
|
||||
* when the file descriptor was registered to epoll via epoll_ctl(2). In
|
||||
* this implementation we set data.fd to be the file descriptor that we
|
||||
* register. That way, we have the file descriptor available when we
|
||||
* process the events.
|
||||
*/
|
||||
|
||||
class EPollArrayWrapper {
|
||||
// EPOLL_EVENTS
|
||||
private static final int EPOLLIN = 0x001;
|
||||
|
||||
// opcodes
|
||||
private static final int EPOLL_CTL_ADD = 1;
|
||||
private static final int EPOLL_CTL_DEL = 2;
|
||||
private static final int EPOLL_CTL_MOD = 3;
|
||||
|
||||
// Miscellaneous constants
|
||||
private static final int SIZE_EPOLLEVENT = sizeofEPollEvent();
|
||||
private static final int EVENT_OFFSET = 0;
|
||||
private static final int DATA_OFFSET = offsetofData();
|
||||
private static final int FD_OFFSET = DATA_OFFSET;
|
||||
private static final int OPEN_MAX = IOUtil.fdLimit();
|
||||
private static final int NUM_EPOLLEVENTS = Math.min(OPEN_MAX, 8192);
|
||||
|
||||
// Special value to indicate that an update should be ignored
|
||||
private static final byte KILLED = (byte)-1;
|
||||
|
||||
// Initial size of arrays for fd registration changes
|
||||
private static final int INITIAL_PENDING_UPDATE_SIZE = 64;
|
||||
|
||||
// maximum size of updatesLow
|
||||
private static final int MAX_UPDATE_ARRAY_SIZE = AccessController.doPrivileged(
|
||||
new GetIntegerAction("sun.nio.ch.maxUpdateArraySize", Math.min(OPEN_MAX, 64*1024)));
|
||||
|
||||
// The fd of the epoll driver
|
||||
private final int epfd;
|
||||
|
||||
// The epoll_event array for results from epoll_wait
|
||||
private final AllocatedNativeObject pollArray;
|
||||
|
||||
// Base address of the epoll_event array
|
||||
private final long pollArrayAddress;
|
||||
|
||||
// The fd of the interrupt line going out
|
||||
private int outgoingInterruptFD;
|
||||
|
||||
// The fd of the interrupt line coming in
|
||||
private int incomingInterruptFD;
|
||||
|
||||
// The index of the interrupt FD
|
||||
private int interruptedIndex;
|
||||
|
||||
// Number of updated pollfd entries
|
||||
int updated;
|
||||
|
||||
// object to synchronize fd registration changes
|
||||
private final Object updateLock = new Object();
|
||||
|
||||
// number of file descriptors with registration changes pending
|
||||
private int updateCount;
|
||||
|
||||
// file descriptors with registration changes pending
|
||||
private int[] updateDescriptors = new int[INITIAL_PENDING_UPDATE_SIZE];
|
||||
|
||||
// events for file descriptors with registration changes pending, indexed
|
||||
// by file descriptor and stored as bytes for efficiency reasons. For
|
||||
// file descriptors higher than MAX_UPDATE_ARRAY_SIZE (unlimited case at
|
||||
// least) then the update is stored in a map.
|
||||
private final byte[] eventsLow = new byte[MAX_UPDATE_ARRAY_SIZE];
|
||||
private final Map<Integer,Byte> eventsHigh = new HashMap<>();
|
||||
|
||||
// Used by release and updateRegistrations to track whether a file
|
||||
// descriptor is registered with epoll.
|
||||
private final BitSet registered = new BitSet();
|
||||
|
||||
|
||||
EPollArrayWrapper() throws IOException {
|
||||
// creates the epoll file descriptor
|
||||
epfd = epollCreate();
|
||||
|
||||
// the epoll_event array passed to epoll_wait
|
||||
int allocationSize = NUM_EPOLLEVENTS * SIZE_EPOLLEVENT;
|
||||
pollArray = new AllocatedNativeObject(allocationSize, true);
|
||||
pollArrayAddress = pollArray.address();
|
||||
}
|
||||
|
||||
void initInterrupt(int fd0, int fd1) {
|
||||
outgoingInterruptFD = fd1;
|
||||
incomingInterruptFD = fd0;
|
||||
epollCtl(epfd, EPOLL_CTL_ADD, fd0, EPOLLIN);
|
||||
}
|
||||
|
||||
void putEventOps(int i, int event) {
|
||||
int offset = SIZE_EPOLLEVENT * i + EVENT_OFFSET;
|
||||
pollArray.putInt(offset, event);
|
||||
}
|
||||
|
||||
void putDescriptor(int i, int fd) {
|
||||
int offset = SIZE_EPOLLEVENT * i + FD_OFFSET;
|
||||
pollArray.putInt(offset, fd);
|
||||
}
|
||||
|
||||
int getEventOps(int i) {
|
||||
int offset = SIZE_EPOLLEVENT * i + EVENT_OFFSET;
|
||||
return pollArray.getInt(offset);
|
||||
}
|
||||
|
||||
int getDescriptor(int i) {
|
||||
int offset = SIZE_EPOLLEVENT * i + FD_OFFSET;
|
||||
return pollArray.getInt(offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if updates for the given key (file
|
||||
* descriptor) are killed.
|
||||
*/
|
||||
private boolean isEventsHighKilled(Integer key) {
|
||||
assert key >= MAX_UPDATE_ARRAY_SIZE;
|
||||
Byte value = eventsHigh.get(key);
|
||||
return (value != null && value == KILLED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pending update events for the given file descriptor. This
|
||||
* method has no effect if the update events is already set to KILLED,
|
||||
* unless {@code force} is {@code true}.
|
||||
*/
|
||||
private void setUpdateEvents(int fd, byte events, boolean force) {
|
||||
if (fd < MAX_UPDATE_ARRAY_SIZE) {
|
||||
if ((eventsLow[fd] != KILLED) || force) {
|
||||
eventsLow[fd] = events;
|
||||
}
|
||||
} else {
|
||||
Integer key = Integer.valueOf(fd);
|
||||
if (!isEventsHighKilled(key) || force) {
|
||||
eventsHigh.put(key, Byte.valueOf(events));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pending update events for the given file descriptor.
|
||||
*/
|
||||
private byte getUpdateEvents(int fd) {
|
||||
if (fd < MAX_UPDATE_ARRAY_SIZE) {
|
||||
return eventsLow[fd];
|
||||
} else {
|
||||
Byte result = eventsHigh.get(Integer.valueOf(fd));
|
||||
// result should never be null
|
||||
return result.byteValue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the events for a given file descriptor
|
||||
*/
|
||||
void setInterest(int fd, int mask) {
|
||||
synchronized (updateLock) {
|
||||
// record the file descriptor and events
|
||||
int oldCapacity = updateDescriptors.length;
|
||||
if (updateCount == oldCapacity) {
|
||||
int newCapacity = oldCapacity + INITIAL_PENDING_UPDATE_SIZE;
|
||||
int[] newDescriptors = new int[newCapacity];
|
||||
System.arraycopy(updateDescriptors, 0, newDescriptors, 0, oldCapacity);
|
||||
updateDescriptors = newDescriptors;
|
||||
}
|
||||
updateDescriptors[updateCount++] = fd;
|
||||
|
||||
// events are stored as bytes for efficiency reasons
|
||||
byte b = (byte)mask;
|
||||
assert (b == mask) && (b != KILLED);
|
||||
setUpdateEvents(fd, b, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a file descriptor
|
||||
*/
|
||||
void add(int fd) {
|
||||
// force the initial update events to 0 as it may be KILLED by a
|
||||
// previous registration.
|
||||
synchronized (updateLock) {
|
||||
assert !registered.get(fd);
|
||||
setUpdateEvents(fd, (byte)0, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a file descriptor
|
||||
*/
|
||||
void remove(int fd) {
|
||||
synchronized (updateLock) {
|
||||
// kill pending and future update for this file descriptor
|
||||
setUpdateEvents(fd, KILLED, false);
|
||||
|
||||
// remove from epoll
|
||||
if (registered.get(fd)) {
|
||||
epollCtl(epfd, EPOLL_CTL_DEL, fd, 0);
|
||||
registered.clear(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close epoll file descriptor and free poll array
|
||||
*/
|
||||
void closeEPollFD() throws IOException {
|
||||
FileDispatcherImpl.closeIntFD(epfd);
|
||||
pollArray.free();
|
||||
}
|
||||
|
||||
int poll(long timeout) throws IOException {
|
||||
updateRegistrations();
|
||||
updated = epollWait(pollArrayAddress, NUM_EPOLLEVENTS, timeout, epfd);
|
||||
for (int i=0; i<updated; i++) {
|
||||
if (getDescriptor(i) == incomingInterruptFD) {
|
||||
interruptedIndex = i;
|
||||
interrupted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the pending registrations.
|
||||
*/
|
||||
private void updateRegistrations() {
|
||||
synchronized (updateLock) {
|
||||
int j = 0;
|
||||
while (j < updateCount) {
|
||||
int fd = updateDescriptors[j];
|
||||
short events = getUpdateEvents(fd);
|
||||
boolean isRegistered = registered.get(fd);
|
||||
int opcode = 0;
|
||||
|
||||
if (events != KILLED) {
|
||||
if (isRegistered) {
|
||||
opcode = (events != 0) ? EPOLL_CTL_MOD : EPOLL_CTL_DEL;
|
||||
} else {
|
||||
opcode = (events != 0) ? EPOLL_CTL_ADD : 0;
|
||||
}
|
||||
if (opcode != 0) {
|
||||
epollCtl(epfd, opcode, fd, events);
|
||||
if (opcode == EPOLL_CTL_ADD) {
|
||||
registered.set(fd);
|
||||
} else if (opcode == EPOLL_CTL_DEL) {
|
||||
registered.clear(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
j++;
|
||||
}
|
||||
updateCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// interrupt support
|
||||
private boolean interrupted = false;
|
||||
|
||||
public void interrupt() {
|
||||
interrupt(outgoingInterruptFD);
|
||||
}
|
||||
|
||||
public int interruptedIndex() {
|
||||
return interruptedIndex;
|
||||
}
|
||||
|
||||
boolean interrupted() {
|
||||
return interrupted;
|
||||
}
|
||||
|
||||
void clearInterrupted() {
|
||||
interrupted = false;
|
||||
}
|
||||
|
||||
static {
|
||||
IOUtil.load();
|
||||
init();
|
||||
}
|
||||
|
||||
private native int epollCreate();
|
||||
private native void epollCtl(int epfd, int opcode, int fd, int events);
|
||||
private native int epollWait(long pollAddress, int numfds, long timeout,
|
||||
int epfd) throws IOException;
|
||||
private static native int sizeofEPollEvent();
|
||||
private static native int offsetofData();
|
||||
private static native void interrupt(int fd);
|
||||
private static native void init();
|
||||
}
|
323
src/java.base/linux/classes/sun/nio/ch/EPollPort.java
Normal file
323
src/java.base/linux/classes/sun/nio/ch/EPollPort.java
Normal file
|
@ -0,0 +1,323 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2013, 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;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import static sun.nio.ch.EPoll.*;
|
||||
|
||||
/**
|
||||
* AsynchronousChannelGroup implementation based on the Linux epoll facility.
|
||||
*/
|
||||
|
||||
final class EPollPort
|
||||
extends Port
|
||||
{
|
||||
// maximum number of events to poll at a time
|
||||
private static final int MAX_EPOLL_EVENTS = 512;
|
||||
|
||||
// errors
|
||||
private static final int ENOENT = 2;
|
||||
|
||||
// epoll file descriptor
|
||||
private final int epfd;
|
||||
|
||||
// true if epoll closed
|
||||
private boolean closed;
|
||||
|
||||
// socket pair used for wakeup
|
||||
private final int sp[];
|
||||
|
||||
// number of wakeups pending
|
||||
private final AtomicInteger wakeupCount = new AtomicInteger();
|
||||
|
||||
// address of the poll array passed to epoll_wait
|
||||
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);
|
||||
|
||||
EPollPort(AsynchronousChannelProvider provider, ThreadPool pool)
|
||||
throws IOException
|
||||
{
|
||||
super(provider, pool);
|
||||
|
||||
// open epoll
|
||||
this.epfd = epollCreate();
|
||||
|
||||
// create socket pair for wakeup mechanism
|
||||
int[] sv = new int[2];
|
||||
try {
|
||||
socketpair(sv);
|
||||
// register one end with epoll
|
||||
epollCtl(epfd, EPOLL_CTL_ADD, sv[0], Net.POLLIN);
|
||||
} catch (IOException x) {
|
||||
close0(epfd);
|
||||
throw x;
|
||||
}
|
||||
this.sp = sv;
|
||||
|
||||
// allocate the poll array
|
||||
this.address = allocatePollArray(MAX_EPOLL_EVENTS);
|
||||
|
||||
// create the queue and offer the special event to ensure that the first
|
||||
// threads polls
|
||||
this.queue = new ArrayBlockingQueue<>(MAX_EPOLL_EVENTS);
|
||||
this.queue.offer(NEED_TO_POLL);
|
||||
}
|
||||
|
||||
EPollPort 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(epfd);
|
||||
}
|
||||
|
||||
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) {
|
||||
// update events (or add to epoll on first usage)
|
||||
int err = epollCtl(epfd, EPOLL_CTL_MOD, fd, (events | EPOLLONESHOT));
|
||||
if (err == ENOENT)
|
||||
err = epollCtl(epfd, EPOLL_CTL_ADD, fd, (events | EPOLLONESHOT));
|
||||
if (err != 0)
|
||||
throw new AssertionError(); // should not happen
|
||||
}
|
||||
|
||||
/*
|
||||
* Task to process events from epoll and dispatch to the channel's
|
||||
* onEvent handler.
|
||||
*
|
||||
* Events are retreived from epoll 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 = epollWait(epfd, address, MAX_EPOLL_EVENTS);
|
||||
/*
|
||||
* '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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
PollableChannel channel = fdToChannel.get(fd);
|
||||
if (channel != null) {
|
||||
int events = getEvents(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;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- Native methods --
|
||||
|
||||
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);
|
||||
|
||||
static {
|
||||
IOUtil.load();
|
||||
}
|
||||
}
|
212
src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java
Normal file
212
src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java
Normal file
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2015, 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.io.IOException;
|
||||
import java.nio.channels.*;
|
||||
import java.nio.channels.spi.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* An implementation of Selector for Linux 2.6+ kernels that uses
|
||||
* the epoll event notification facility.
|
||||
*/
|
||||
class EPollSelectorImpl
|
||||
extends SelectorImpl
|
||||
{
|
||||
|
||||
// File descriptors used for interrupt
|
||||
protected int fd0;
|
||||
protected int fd1;
|
||||
|
||||
// The poll object
|
||||
EPollArrayWrapper pollWrapper;
|
||||
|
||||
// Maps from file descriptors to keys
|
||||
private Map<Integer,SelectionKeyImpl> fdToKey;
|
||||
|
||||
// True if this Selector has been closed
|
||||
private volatile boolean closed;
|
||||
|
||||
// Lock for interrupt triggering and clearing
|
||||
private final Object interruptLock = new Object();
|
||||
private boolean interruptTriggered = false;
|
||||
|
||||
/**
|
||||
* Package private constructor called by factory method in
|
||||
* the abstract superclass Selector.
|
||||
*/
|
||||
EPollSelectorImpl(SelectorProvider sp) throws IOException {
|
||||
super(sp);
|
||||
long pipeFds = IOUtil.makePipe(false);
|
||||
fd0 = (int) (pipeFds >>> 32);
|
||||
fd1 = (int) pipeFds;
|
||||
try {
|
||||
pollWrapper = new EPollArrayWrapper();
|
||||
pollWrapper.initInterrupt(fd0, fd1);
|
||||
fdToKey = new HashMap<>();
|
||||
} catch (Throwable t) {
|
||||
try {
|
||||
FileDispatcherImpl.closeIntFD(fd0);
|
||||
} catch (IOException ioe0) {
|
||||
t.addSuppressed(ioe0);
|
||||
}
|
||||
try {
|
||||
FileDispatcherImpl.closeIntFD(fd1);
|
||||
} catch (IOException ioe1) {
|
||||
t.addSuppressed(ioe1);
|
||||
}
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
|
||||
protected int doSelect(long timeout) throws IOException {
|
||||
if (closed)
|
||||
throw new ClosedSelectorException();
|
||||
processDeregisterQueue();
|
||||
try {
|
||||
begin();
|
||||
pollWrapper.poll(timeout);
|
||||
} finally {
|
||||
end();
|
||||
}
|
||||
processDeregisterQueue();
|
||||
int numKeysUpdated = updateSelectedKeys();
|
||||
if (pollWrapper.interrupted()) {
|
||||
// Clear the wakeup pipe
|
||||
pollWrapper.putEventOps(pollWrapper.interruptedIndex(), 0);
|
||||
synchronized (interruptLock) {
|
||||
pollWrapper.clearInterrupted();
|
||||
IOUtil.drain(fd0);
|
||||
interruptTriggered = false;
|
||||
}
|
||||
}
|
||||
return numKeysUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the keys whose fd's have been selected by the epoll.
|
||||
* Add the ready keys to the ready queue.
|
||||
*/
|
||||
private int updateSelectedKeys() {
|
||||
int entries = pollWrapper.updated;
|
||||
int numKeysUpdated = 0;
|
||||
for (int i=0; i<entries; i++) {
|
||||
int nextFD = pollWrapper.getDescriptor(i);
|
||||
SelectionKeyImpl ski = fdToKey.get(Integer.valueOf(nextFD));
|
||||
// ski is null in the case of an interrupt
|
||||
if (ski != null) {
|
||||
int rOps = pollWrapper.getEventOps(i);
|
||||
if (selectedKeys.contains(ski)) {
|
||||
if (ski.channel.translateAndSetReadyOps(rOps, ski)) {
|
||||
numKeysUpdated++;
|
||||
}
|
||||
} else {
|
||||
ski.channel.translateAndSetReadyOps(rOps, ski);
|
||||
if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) {
|
||||
selectedKeys.add(ski);
|
||||
numKeysUpdated++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return numKeysUpdated;
|
||||
}
|
||||
|
||||
protected void implClose() throws IOException {
|
||||
if (closed)
|
||||
return;
|
||||
closed = true;
|
||||
|
||||
// prevent further wakeup
|
||||
synchronized (interruptLock) {
|
||||
interruptTriggered = true;
|
||||
}
|
||||
|
||||
FileDispatcherImpl.closeIntFD(fd0);
|
||||
FileDispatcherImpl.closeIntFD(fd1);
|
||||
|
||||
pollWrapper.closeEPollFD();
|
||||
// it is possible
|
||||
selectedKeys = null;
|
||||
|
||||
// Deregister channels
|
||||
Iterator<SelectionKey> i = keys.iterator();
|
||||
while (i.hasNext()) {
|
||||
SelectionKeyImpl ski = (SelectionKeyImpl)i.next();
|
||||
deregister(ski);
|
||||
SelectableChannel selch = ski.channel();
|
||||
if (!selch.isOpen() && !selch.isRegistered())
|
||||
((SelChImpl)selch).kill();
|
||||
i.remove();
|
||||
}
|
||||
|
||||
fd0 = -1;
|
||||
fd1 = -1;
|
||||
}
|
||||
|
||||
protected void implRegister(SelectionKeyImpl ski) {
|
||||
if (closed)
|
||||
throw new ClosedSelectorException();
|
||||
SelChImpl ch = ski.channel;
|
||||
int fd = Integer.valueOf(ch.getFDVal());
|
||||
fdToKey.put(fd, ski);
|
||||
pollWrapper.add(fd);
|
||||
keys.add(ski);
|
||||
}
|
||||
|
||||
protected void implDereg(SelectionKeyImpl ski) throws IOException {
|
||||
assert (ski.getIndex() >= 0);
|
||||
SelChImpl ch = ski.channel;
|
||||
int fd = ch.getFDVal();
|
||||
fdToKey.remove(Integer.valueOf(fd));
|
||||
pollWrapper.remove(fd);
|
||||
ski.setIndex(-1);
|
||||
keys.remove(ski);
|
||||
selectedKeys.remove(ski);
|
||||
deregister((AbstractSelectionKey)ski);
|
||||
SelectableChannel selch = ski.channel();
|
||||
if (!selch.isOpen() && !selch.isRegistered())
|
||||
((SelChImpl)selch).kill();
|
||||
}
|
||||
|
||||
public void putEventOps(SelectionKeyImpl ski, int ops) {
|
||||
if (closed)
|
||||
throw new ClosedSelectorException();
|
||||
SelChImpl ch = ski.channel;
|
||||
pollWrapper.setInterest(ch.getFDVal(), ops);
|
||||
}
|
||||
|
||||
public Selector wakeup() {
|
||||
synchronized (interruptLock) {
|
||||
if (!interruptTriggered) {
|
||||
pollWrapper.interrupt();
|
||||
interruptTriggered = true;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 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.io.IOException;
|
||||
import java.nio.channels.*;
|
||||
import java.nio.channels.spi.*;
|
||||
|
||||
public class EPollSelectorProvider
|
||||
extends SelectorProviderImpl
|
||||
{
|
||||
public AbstractSelector openSelector() throws IOException {
|
||||
return new EPollSelectorImpl(this);
|
||||
}
|
||||
|
||||
public Channel inheritedChannel() throws IOException {
|
||||
return InheritedChannel.getChannel();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2010, 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.*;
|
||||
import java.nio.channels.spi.AsynchronousChannelProvider;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.io.IOException;
|
||||
|
||||
public class LinuxAsynchronousChannelProvider
|
||||
extends AsynchronousChannelProvider
|
||||
{
|
||||
private static volatile EPollPort defaultPort;
|
||||
|
||||
private EPollPort defaultEventPort() throws IOException {
|
||||
if (defaultPort == null) {
|
||||
synchronized (LinuxAsynchronousChannelProvider.class) {
|
||||
if (defaultPort == null) {
|
||||
defaultPort = new EPollPort(this, ThreadPool.getDefault()).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultPort;
|
||||
}
|
||||
|
||||
public LinuxAsynchronousChannelProvider() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsynchronousChannelGroup openAsynchronousChannelGroup(int nThreads, ThreadFactory factory)
|
||||
throws IOException
|
||||
{
|
||||
return new EPollPort(this, ThreadPool.create(nThreads, factory)).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsynchronousChannelGroup openAsynchronousChannelGroup(ExecutorService executor, int initialSize)
|
||||
throws IOException
|
||||
{
|
||||
return new EPollPort(this, ThreadPool.wrap(executor, initialSize)).start();
|
||||
}
|
||||
|
||||
private Port toPort(AsynchronousChannelGroup group) throws IOException {
|
||||
if (group == null) {
|
||||
return defaultEventPort();
|
||||
} else {
|
||||
if (!(group instanceof EPollPort))
|
||||
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));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.fs;
|
||||
|
||||
import java.nio.file.spi.FileSystemProvider;
|
||||
|
||||
/**
|
||||
* Creates this platform's default FileSystemProvider.
|
||||
*/
|
||||
|
||||
public class DefaultFileSystemProvider {
|
||||
private DefaultFileSystemProvider() { }
|
||||
|
||||
/**
|
||||
* Returns the default FileSystemProvider.
|
||||
*/
|
||||
public static FileSystemProvider create() {
|
||||
return new LinuxFileSystemProvider();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2015, 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.attribute.*;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.io.IOException;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
import static sun.nio.fs.UnixNativeDispatcher.*;
|
||||
import static sun.nio.fs.UnixConstants.*;
|
||||
|
||||
/**
|
||||
* Linux implementation of DosFileAttributeView for use on file systems such
|
||||
* as ext3 that have extended attributes enabled and SAMBA configured to store
|
||||
* DOS attributes.
|
||||
*/
|
||||
|
||||
class LinuxDosFileAttributeView
|
||||
extends UnixFileAttributeViews.Basic implements DosFileAttributeView
|
||||
{
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
private static final String READONLY_NAME = "readonly";
|
||||
private static final String ARCHIVE_NAME = "archive";
|
||||
private static final String SYSTEM_NAME = "system";
|
||||
private static final String HIDDEN_NAME = "hidden";
|
||||
|
||||
private static final String DOS_XATTR_NAME = "user.DOSATTRIB";
|
||||
private static final byte[] DOS_XATTR_NAME_AS_BYTES = Util.toBytes(DOS_XATTR_NAME);
|
||||
|
||||
private static final int DOS_XATTR_READONLY = 0x01;
|
||||
private static final int DOS_XATTR_HIDDEN = 0x02;
|
||||
private static final int DOS_XATTR_SYSTEM = 0x04;
|
||||
private static final int DOS_XATTR_ARCHIVE = 0x20;
|
||||
|
||||
// the names of the DOS attributes (includes basic)
|
||||
private static final Set<String> dosAttributeNames =
|
||||
Util.newSet(basicAttributeNames, READONLY_NAME, ARCHIVE_NAME, SYSTEM_NAME, HIDDEN_NAME);
|
||||
|
||||
LinuxDosFileAttributeView(UnixPath file, boolean followLinks) {
|
||||
super(file, followLinks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "dos";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String attribute, Object value)
|
||||
throws IOException
|
||||
{
|
||||
if (attribute.equals(READONLY_NAME)) {
|
||||
setReadOnly((Boolean)value);
|
||||
return;
|
||||
}
|
||||
if (attribute.equals(ARCHIVE_NAME)) {
|
||||
setArchive((Boolean)value);
|
||||
return;
|
||||
}
|
||||
if (attribute.equals(SYSTEM_NAME)) {
|
||||
setSystem((Boolean)value);
|
||||
return;
|
||||
}
|
||||
if (attribute.equals(HIDDEN_NAME)) {
|
||||
setHidden((Boolean)value);
|
||||
return;
|
||||
}
|
||||
super.setAttribute(attribute, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String,Object> readAttributes(String[] attributes)
|
||||
throws IOException
|
||||
{
|
||||
AttributesBuilder builder =
|
||||
AttributesBuilder.create(dosAttributeNames, attributes);
|
||||
DosFileAttributes attrs = readAttributes();
|
||||
addRequestedBasicAttributes(attrs, builder);
|
||||
if (builder.match(READONLY_NAME))
|
||||
builder.add(READONLY_NAME, attrs.isReadOnly());
|
||||
if (builder.match(ARCHIVE_NAME))
|
||||
builder.add(ARCHIVE_NAME, attrs.isArchive());
|
||||
if (builder.match(SYSTEM_NAME))
|
||||
builder.add(SYSTEM_NAME, attrs.isSystem());
|
||||
if (builder.match(HIDDEN_NAME))
|
||||
builder.add(HIDDEN_NAME, attrs.isHidden());
|
||||
return builder.unmodifiableMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DosFileAttributes readAttributes() throws IOException {
|
||||
file.checkRead();
|
||||
|
||||
int fd = -1;
|
||||
try {
|
||||
fd = file.openForAttributeAccess(followLinks);
|
||||
final UnixFileAttributes attrs = UnixFileAttributes.get(fd);
|
||||
final int dosAttribute = getDosAttribute(fd);
|
||||
|
||||
return new DosFileAttributes() {
|
||||
@Override
|
||||
public FileTime lastModifiedTime() {
|
||||
return attrs.lastModifiedTime();
|
||||
}
|
||||
@Override
|
||||
public FileTime lastAccessTime() {
|
||||
return attrs.lastAccessTime();
|
||||
}
|
||||
@Override
|
||||
public FileTime creationTime() {
|
||||
return attrs.creationTime();
|
||||
}
|
||||
@Override
|
||||
public boolean isRegularFile() {
|
||||
return attrs.isRegularFile();
|
||||
}
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
return attrs.isDirectory();
|
||||
}
|
||||
@Override
|
||||
public boolean isSymbolicLink() {
|
||||
return attrs.isSymbolicLink();
|
||||
}
|
||||
@Override
|
||||
public boolean isOther() {
|
||||
return attrs.isOther();
|
||||
}
|
||||
@Override
|
||||
public long size() {
|
||||
return attrs.size();
|
||||
}
|
||||
@Override
|
||||
public Object fileKey() {
|
||||
return attrs.fileKey();
|
||||
}
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return (dosAttribute & DOS_XATTR_READONLY) != 0;
|
||||
}
|
||||
@Override
|
||||
public boolean isHidden() {
|
||||
return (dosAttribute & DOS_XATTR_HIDDEN) != 0;
|
||||
}
|
||||
@Override
|
||||
public boolean isArchive() {
|
||||
return (dosAttribute & DOS_XATTR_ARCHIVE) != 0;
|
||||
}
|
||||
@Override
|
||||
public boolean isSystem() {
|
||||
return (dosAttribute & DOS_XATTR_SYSTEM) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
} catch (UnixException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
return null; // keep compiler happy
|
||||
} finally {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(boolean value) throws IOException {
|
||||
updateDosAttribute(DOS_XATTR_READONLY, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHidden(boolean value) throws IOException {
|
||||
updateDosAttribute(DOS_XATTR_HIDDEN, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArchive(boolean value) throws IOException {
|
||||
updateDosAttribute(DOS_XATTR_ARCHIVE, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSystem(boolean value) throws IOException {
|
||||
updateDosAttribute(DOS_XATTR_SYSTEM, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the value of the user.DOSATTRIB extended attribute
|
||||
*/
|
||||
private int getDosAttribute(int fd) throws UnixException {
|
||||
final int size = 24;
|
||||
|
||||
NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
|
||||
try {
|
||||
int len = LinuxNativeDispatcher
|
||||
.fgetxattr(fd, DOS_XATTR_NAME_AS_BYTES, buffer.address(), size);
|
||||
|
||||
if (len > 0) {
|
||||
// ignore null terminator
|
||||
if (unsafe.getByte(buffer.address()+len-1) == 0)
|
||||
len--;
|
||||
|
||||
// convert to String and parse
|
||||
byte[] buf = new byte[len];
|
||||
unsafe.copyMemory(null, buffer.address(), buf,
|
||||
Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
|
||||
String value = Util.toString(buf);
|
||||
|
||||
// should be something like 0x20
|
||||
if (value.length() >= 3 && value.startsWith("0x")) {
|
||||
try {
|
||||
return Integer.parseInt(value.substring(2), 16);
|
||||
} catch (NumberFormatException x) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new UnixException("Value of " + DOS_XATTR_NAME + " attribute is invalid");
|
||||
} catch (UnixException x) {
|
||||
// default value when attribute does not exist
|
||||
if (x.errno() == ENODATA)
|
||||
return 0;
|
||||
throw x;
|
||||
} finally {
|
||||
buffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the value of the user.DOSATTRIB extended attribute
|
||||
*/
|
||||
private void updateDosAttribute(int flag, boolean enable) throws IOException {
|
||||
file.checkWrite();
|
||||
|
||||
int fd = -1;
|
||||
try {
|
||||
fd = file.openForAttributeAccess(followLinks);
|
||||
int oldValue = getDosAttribute(fd);
|
||||
int newValue = oldValue;
|
||||
if (enable) {
|
||||
newValue |= flag;
|
||||
} else {
|
||||
newValue &= ~flag;
|
||||
}
|
||||
if (newValue != oldValue) {
|
||||
byte[] value = Util.toBytes("0x" + Integer.toHexString(newValue));
|
||||
NativeBuffer buffer = NativeBuffers.asNativeBuffer(value);
|
||||
try {
|
||||
LinuxNativeDispatcher.fsetxattr(fd, DOS_XATTR_NAME_AS_BYTES,
|
||||
buffer.address(), value.length+1);
|
||||
} finally {
|
||||
buffer.release();
|
||||
}
|
||||
}
|
||||
} catch (UnixException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
} finally {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
}
|
161
src/java.base/linux/classes/sun/nio/fs/LinuxFileStore.java
Normal file
161
src/java.base/linux/classes/sun/nio/fs/LinuxFileStore.java
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2015, 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.attribute.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Linux implementation of FileStore
|
||||
*/
|
||||
|
||||
class LinuxFileStore
|
||||
extends UnixFileStore
|
||||
{
|
||||
// used when checking if extended attributes are enabled or not
|
||||
private volatile boolean xattrChecked;
|
||||
private volatile boolean xattrEnabled;
|
||||
|
||||
LinuxFileStore(UnixPath file) throws IOException {
|
||||
super(file);
|
||||
}
|
||||
|
||||
LinuxFileStore(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 {
|
||||
LinuxFileSystem fs = (LinuxFileSystem)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 (use /proc/mounts to ensure we
|
||||
// find the file system even when not in /etc/mtab)
|
||||
byte[] dir = path.asByteArray();
|
||||
for (UnixMountEntry entry: fs.getMountEntries("/proc/mounts")) {
|
||||
if (Arrays.equals(dir, entry.dir()))
|
||||
return entry;
|
||||
}
|
||||
|
||||
throw new IOException("Mount point not found");
|
||||
}
|
||||
|
||||
// returns true if extended attributes enabled on file system where given
|
||||
// file resides, returns false if disabled or unable to determine.
|
||||
private boolean isExtendedAttributesEnabled(UnixPath path) {
|
||||
int fd = -1;
|
||||
try {
|
||||
fd = path.openForAttributeAccess(false);
|
||||
|
||||
// fgetxattr returns size if called with size==0
|
||||
byte[] name = Util.toBytes("user.java");
|
||||
LinuxNativeDispatcher.fgetxattr(fd, name, 0L, 0);
|
||||
return true;
|
||||
} catch (UnixException e) {
|
||||
// attribute does not exist
|
||||
if (e.errno() == UnixConstants.ENODATA)
|
||||
return true;
|
||||
} finally {
|
||||
UnixNativeDispatcher.close(fd);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
|
||||
// support DosFileAttributeView and UserDefinedAttributeView if extended
|
||||
// attributes enabled
|
||||
if (type == DosFileAttributeView.class ||
|
||||
type == UserDefinedFileAttributeView.class)
|
||||
{
|
||||
// lookup fstypes.properties
|
||||
FeatureStatus status = checkIfFeaturePresent("user_xattr");
|
||||
if (status == FeatureStatus.PRESENT)
|
||||
return true;
|
||||
if (status == FeatureStatus.NOT_PRESENT)
|
||||
return false;
|
||||
|
||||
// if file system is mounted with user_xattr option then assume
|
||||
// extended attributes are enabled
|
||||
if ((entry().hasOption("user_xattr")))
|
||||
return true;
|
||||
|
||||
// user_xattr option not present but we special-case ext3/4 as we
|
||||
// know that extended attributes are not enabled by default.
|
||||
if (entry().fstype().equals("ext3") || entry().fstype().equals("ext4"))
|
||||
return false;
|
||||
|
||||
// not ext3/4 so probe mount point
|
||||
if (!xattrChecked) {
|
||||
UnixPath dir = new UnixPath(file().getFileSystem(), entry().dir());
|
||||
xattrEnabled = isExtendedAttributesEnabled(dir);
|
||||
xattrChecked = true;
|
||||
}
|
||||
return xattrEnabled;
|
||||
}
|
||||
// POSIX attributes not supported on FAT
|
||||
if (type == PosixFileAttributeView.class && entry().fstype().equals("vfat"))
|
||||
return false;
|
||||
return super.supportsFileAttributeView(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFileAttributeView(String name) {
|
||||
if (name.equals("dos"))
|
||||
return supportsFileAttributeView(DosFileAttributeView.class);
|
||||
if (name.equals("user"))
|
||||
return supportsFileAttributeView(UserDefinedFileAttributeView.class);
|
||||
return super.supportsFileAttributeView(name);
|
||||
}
|
||||
}
|
114
src/java.base/linux/classes/sun/nio/fs/LinuxFileSystem.java
Normal file
114
src/java.base/linux/classes/sun/nio/fs/LinuxFileSystem.java
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2013, 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.*;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import static sun.nio.fs.LinuxNativeDispatcher.*;
|
||||
|
||||
/**
|
||||
* Linux implementation of FileSystem
|
||||
*/
|
||||
|
||||
class LinuxFileSystem extends UnixFileSystem {
|
||||
LinuxFileSystem(UnixFileSystemProvider provider, String dir) {
|
||||
super(provider, dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WatchService newWatchService()
|
||||
throws IOException
|
||||
{
|
||||
// assume 2.6.13 or newer
|
||||
return new LinuxWatchService(this);
|
||||
}
|
||||
|
||||
|
||||
// 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<>();
|
||||
result.addAll(standardFileAttributeViews());
|
||||
// additional Linux-specific views
|
||||
result.add("dos");
|
||||
result.add("user");
|
||||
return Collections.unmodifiableSet(result);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> supportedFileAttributeViews() {
|
||||
return SupportedFileFileAttributeViewsHolder.supportedFileAttributeViews;
|
||||
}
|
||||
|
||||
@Override
|
||||
void copyNonPosixAttributes(int ofd, int nfd) {
|
||||
LinuxUserDefinedFileAttributeView.copyExtendedAttributes(ofd, nfd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns object to iterate over the mount entries in the given fstab file.
|
||||
*/
|
||||
Iterable<UnixMountEntry> getMountEntries(String fstab) {
|
||||
ArrayList<UnixMountEntry> entries = new ArrayList<>();
|
||||
try {
|
||||
long fp = setmntent(Util.toBytes(fstab), Util.toBytes("r"));
|
||||
try {
|
||||
for (;;) {
|
||||
UnixMountEntry entry = new UnixMountEntry();
|
||||
int res = getmntent(fp, entry);
|
||||
if (res < 0)
|
||||
break;
|
||||
entries.add(entry);
|
||||
}
|
||||
} finally {
|
||||
endmntent(fp);
|
||||
}
|
||||
|
||||
} catch (UnixException x) {
|
||||
// nothing we can do
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns object to iterate over the mount entries in /etc/mtab
|
||||
*/
|
||||
@Override
|
||||
Iterable<UnixMountEntry> getMountEntries() {
|
||||
return getMountEntries("/etc/mtab");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
FileStore getFileStore(UnixMountEntry entry) throws IOException {
|
||||
return new LinuxFileStore(this, entry);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2016, 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.*;
|
||||
import java.nio.file.attribute.*;
|
||||
import java.nio.file.spi.FileTypeDetector;
|
||||
import java.io.IOException;
|
||||
import java.security.AccessController;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
/**
|
||||
* Linux implementation of FileSystemProvider
|
||||
*/
|
||||
|
||||
public class LinuxFileSystemProvider extends UnixFileSystemProvider {
|
||||
public LinuxFileSystemProvider() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
LinuxFileSystem newFileSystem(String dir) {
|
||||
return new LinuxFileSystem(this, dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
LinuxFileStore getFileStore(UnixPath path) throws IOException {
|
||||
return new LinuxFileStore(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V extends FileAttributeView> V getFileAttributeView(Path obj,
|
||||
Class<V> type,
|
||||
LinkOption... options)
|
||||
{
|
||||
if (type == DosFileAttributeView.class) {
|
||||
return (V) new LinuxDosFileAttributeView(UnixPath.toUnixPath(obj),
|
||||
Util.followLinks(options));
|
||||
}
|
||||
if (type == UserDefinedFileAttributeView.class) {
|
||||
return (V) new LinuxUserDefinedFileAttributeView(UnixPath.toUnixPath(obj),
|
||||
Util.followLinks(options));
|
||||
}
|
||||
return super.getFileAttributeView(obj, type, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicFileAttributeView getFileAttributeView(Path obj,
|
||||
String name,
|
||||
LinkOption... options)
|
||||
{
|
||||
if (name.equals("dos")) {
|
||||
return new LinuxDosFileAttributeView(UnixPath.toUnixPath(obj),
|
||||
Util.followLinks(options));
|
||||
}
|
||||
if (name.equals("user")) {
|
||||
return new LinuxUserDefinedFileAttributeView(UnixPath.toUnixPath(obj),
|
||||
Util.followLinks(options));
|
||||
}
|
||||
return super.getFileAttributeView(obj, name, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <A extends BasicFileAttributes> A readAttributes(Path file,
|
||||
Class<A> type,
|
||||
LinkOption... options)
|
||||
throws IOException
|
||||
{
|
||||
if (type == DosFileAttributes.class) {
|
||||
DosFileAttributeView view =
|
||||
getFileAttributeView(file, DosFileAttributeView.class, options);
|
||||
return (A) view.readAttributes();
|
||||
} else {
|
||||
return super.readAttributes(file, type, options);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
FileTypeDetector getFileTypeDetector() {
|
||||
String userHome = GetPropertyAction.privilegedGetProperty("user.home");
|
||||
Path userMimeTypes = Paths.get(userHome, ".mime.types");
|
||||
Path etcMimeTypes = Paths.get("/etc/mime.types");
|
||||
|
||||
return chain(new MimeTypesFileTypeDetector(userMimeTypes),
|
||||
new MimeTypesFileTypeDetector(etcMimeTypes));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2012, 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.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
/**
|
||||
* Linux specific system calls.
|
||||
*/
|
||||
|
||||
class LinuxNativeDispatcher extends UnixNativeDispatcher {
|
||||
private LinuxNativeDispatcher() { }
|
||||
|
||||
/**
|
||||
* FILE *setmntent(const char *filename, const char *type);
|
||||
*/
|
||||
static long setmntent(byte[] filename, byte[] type) throws UnixException {
|
||||
NativeBuffer pathBuffer = NativeBuffers.asNativeBuffer(filename);
|
||||
NativeBuffer typeBuffer = NativeBuffers.asNativeBuffer(type);
|
||||
try {
|
||||
return setmntent0(pathBuffer.address(), typeBuffer.address());
|
||||
} finally {
|
||||
typeBuffer.release();
|
||||
pathBuffer.release();
|
||||
}
|
||||
}
|
||||
private static native long setmntent0(long pathAddress, long typeAddress)
|
||||
throws UnixException;
|
||||
|
||||
/**
|
||||
* int getmntent(FILE *fp, struct mnttab *mp, int len);
|
||||
*/
|
||||
static native int getmntent(long fp, UnixMountEntry entry)
|
||||
throws UnixException;
|
||||
|
||||
/**
|
||||
* int endmntent(FILE* filep);
|
||||
*/
|
||||
static native void endmntent(long stream) throws UnixException;
|
||||
|
||||
/**
|
||||
* ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size);
|
||||
*/
|
||||
static int fgetxattr(int filedes, byte[] name, long valueAddress,
|
||||
int valueLen) throws UnixException
|
||||
{
|
||||
NativeBuffer buffer = NativeBuffers.asNativeBuffer(name);
|
||||
try {
|
||||
return fgetxattr0(filedes, buffer.address(), valueAddress, valueLen);
|
||||
} finally {
|
||||
buffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
private static native int fgetxattr0(int filedes, long nameAddress,
|
||||
long valueAddress, int valueLen) throws UnixException;
|
||||
|
||||
/**
|
||||
* fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags);
|
||||
*/
|
||||
static void fsetxattr(int filedes, byte[] name, long valueAddress,
|
||||
int valueLen) throws UnixException
|
||||
{
|
||||
NativeBuffer buffer = NativeBuffers.asNativeBuffer(name);
|
||||
try {
|
||||
fsetxattr0(filedes, buffer.address(), valueAddress, valueLen);
|
||||
} finally {
|
||||
buffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
private static native void fsetxattr0(int filedes, long nameAddress,
|
||||
long valueAddress, int valueLen) throws UnixException;
|
||||
|
||||
/**
|
||||
* fremovexattr(int filedes, const char *name);
|
||||
*/
|
||||
static void fremovexattr(int filedes, byte[] name) throws UnixException {
|
||||
NativeBuffer buffer = NativeBuffers.asNativeBuffer(name);
|
||||
try {
|
||||
fremovexattr0(filedes, buffer.address());
|
||||
} finally {
|
||||
buffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
private static native void fremovexattr0(int filedes, long nameAddress)
|
||||
throws UnixException;
|
||||
|
||||
/**
|
||||
* size_t flistxattr(int filedes, const char *list, size_t size)
|
||||
*/
|
||||
static native int flistxattr(int filedes, long listAddress, int size)
|
||||
throws UnixException;
|
||||
|
||||
// initialize
|
||||
private static native void init();
|
||||
|
||||
static {
|
||||
AccessController.doPrivileged(new PrivilegedAction<>() {
|
||||
public Void run() {
|
||||
System.loadLibrary("nio");
|
||||
return null;
|
||||
}});
|
||||
init();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,375 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2015, 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.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
import static sun.nio.fs.UnixConstants.*;
|
||||
import static sun.nio.fs.LinuxNativeDispatcher.*;
|
||||
|
||||
/**
|
||||
* Linux implementation of UserDefinedFileAttributeView using extended attributes.
|
||||
*/
|
||||
|
||||
class LinuxUserDefinedFileAttributeView
|
||||
extends AbstractUserDefinedFileAttributeView
|
||||
{
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
// namespace for extended user attributes
|
||||
private static final String USER_NAMESPACE = "user.";
|
||||
|
||||
// maximum bytes in extended attribute name (includes namespace)
|
||||
private static final int XATTR_NAME_MAX = 255;
|
||||
|
||||
private byte[] nameAsBytes(UnixPath file, String name) throws IOException {
|
||||
if (name == null)
|
||||
throw new NullPointerException("'name' is null");
|
||||
name = USER_NAMESPACE + name;
|
||||
byte[] bytes = Util.toBytes(name);
|
||||
if (bytes.length > XATTR_NAME_MAX) {
|
||||
throw new FileSystemException(file.getPathForExceptionMessage(),
|
||||
null, "'" + name + "' is too big");
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// Parses buffer as array of NULL-terminated C strings.
|
||||
private List<String> asList(long address, int size) {
|
||||
List<String> list = new ArrayList<>();
|
||||
int start = 0;
|
||||
int pos = 0;
|
||||
while (pos < size) {
|
||||
if (unsafe.getByte(address + pos) == 0) {
|
||||
int len = pos - start;
|
||||
byte[] value = new byte[len];
|
||||
unsafe.copyMemory(null, address+start, value,
|
||||
Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
|
||||
String s = Util.toString(value);
|
||||
if (s.startsWith(USER_NAMESPACE)) {
|
||||
s = s.substring(USER_NAMESPACE.length());
|
||||
list.add(s);
|
||||
}
|
||||
start = pos + 1;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private final UnixPath file;
|
||||
private final boolean followLinks;
|
||||
|
||||
LinuxUserDefinedFileAttributeView(UnixPath file, boolean followLinks) {
|
||||
this.file = file;
|
||||
this.followLinks = followLinks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> list() throws IOException {
|
||||
if (System.getSecurityManager() != null)
|
||||
checkAccess(file.getPathForPermissionCheck(), true, false);
|
||||
|
||||
int fd = -1;
|
||||
try {
|
||||
fd = file.openForAttributeAccess(followLinks);
|
||||
} catch (UnixException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
}
|
||||
NativeBuffer buffer = null;
|
||||
try {
|
||||
int size = 1024;
|
||||
buffer = NativeBuffers.getNativeBuffer(size);
|
||||
for (;;) {
|
||||
try {
|
||||
int n = flistxattr(fd, buffer.address(), size);
|
||||
List<String> list = asList(buffer.address(), n);
|
||||
return Collections.unmodifiableList(list);
|
||||
} catch (UnixException x) {
|
||||
// allocate larger buffer if required
|
||||
if (x.errno() == ERANGE && size < 32*1024) {
|
||||
buffer.release();
|
||||
size *= 2;
|
||||
buffer = null;
|
||||
buffer = NativeBuffers.getNativeBuffer(size);
|
||||
continue;
|
||||
}
|
||||
throw new FileSystemException(file.getPathForExceptionMessage(),
|
||||
null, "Unable to get list of extended attributes: " +
|
||||
x.getMessage());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (buffer != null)
|
||||
buffer.release();
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size(String name) throws IOException {
|
||||
if (System.getSecurityManager() != null)
|
||||
checkAccess(file.getPathForPermissionCheck(), true, false);
|
||||
|
||||
int fd = -1;
|
||||
try {
|
||||
fd = file.openForAttributeAccess(followLinks);
|
||||
} catch (UnixException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
}
|
||||
try {
|
||||
// fgetxattr returns size if called with size==0
|
||||
return fgetxattr(fd, nameAsBytes(file,name), 0L, 0);
|
||||
} catch (UnixException x) {
|
||||
throw new FileSystemException(file.getPathForExceptionMessage(),
|
||||
null, "Unable to get size of extended attribute '" + name +
|
||||
"': " + x.getMessage());
|
||||
} finally {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(String name, ByteBuffer dst) throws IOException {
|
||||
if (System.getSecurityManager() != null)
|
||||
checkAccess(file.getPathForPermissionCheck(), true, false);
|
||||
|
||||
if (dst.isReadOnly())
|
||||
throw new IllegalArgumentException("Read-only buffer");
|
||||
int pos = dst.position();
|
||||
int lim = dst.limit();
|
||||
assert (pos <= lim);
|
||||
int rem = (pos <= lim ? lim - pos : 0);
|
||||
|
||||
NativeBuffer nb;
|
||||
long address;
|
||||
if (dst instanceof sun.nio.ch.DirectBuffer) {
|
||||
nb = null;
|
||||
address = ((sun.nio.ch.DirectBuffer)dst).address() + pos;
|
||||
} else {
|
||||
// substitute with native buffer
|
||||
nb = NativeBuffers.getNativeBuffer(rem);
|
||||
address = nb.address();
|
||||
}
|
||||
|
||||
int fd = -1;
|
||||
try {
|
||||
fd = file.openForAttributeAccess(followLinks);
|
||||
} catch (UnixException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
}
|
||||
try {
|
||||
try {
|
||||
int n = fgetxattr(fd, nameAsBytes(file,name), address, rem);
|
||||
|
||||
// if remaining is zero then fgetxattr returns the size
|
||||
if (rem == 0) {
|
||||
if (n > 0)
|
||||
throw new UnixException(ERANGE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// copy from buffer into backing array if necessary
|
||||
if (nb != null) {
|
||||
int off = dst.arrayOffset() + pos + Unsafe.ARRAY_BYTE_BASE_OFFSET;
|
||||
unsafe.copyMemory(null, address, dst.array(), off, n);
|
||||
}
|
||||
dst.position(pos + n);
|
||||
return n;
|
||||
} catch (UnixException x) {
|
||||
String msg = (x.errno() == ERANGE) ?
|
||||
"Insufficient space in buffer" : x.getMessage();
|
||||
throw new FileSystemException(file.getPathForExceptionMessage(),
|
||||
null, "Error reading extended attribute '" + name + "': " + msg);
|
||||
} finally {
|
||||
close(fd);
|
||||
}
|
||||
} finally {
|
||||
if (nb != null)
|
||||
nb.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(String name, ByteBuffer src) throws IOException {
|
||||
if (System.getSecurityManager() != null)
|
||||
checkAccess(file.getPathForPermissionCheck(), false, true);
|
||||
|
||||
int pos = src.position();
|
||||
int lim = src.limit();
|
||||
assert (pos <= lim);
|
||||
int rem = (pos <= lim ? lim - pos : 0);
|
||||
|
||||
NativeBuffer nb;
|
||||
long address;
|
||||
if (src instanceof sun.nio.ch.DirectBuffer) {
|
||||
nb = null;
|
||||
address = ((sun.nio.ch.DirectBuffer)src).address() + pos;
|
||||
} else {
|
||||
// substitute with native buffer
|
||||
nb = NativeBuffers.getNativeBuffer(rem);
|
||||
address = nb.address();
|
||||
|
||||
if (src.hasArray()) {
|
||||
// copy from backing array into buffer
|
||||
int off = src.arrayOffset() + pos + Unsafe.ARRAY_BYTE_BASE_OFFSET;
|
||||
unsafe.copyMemory(src.array(), off, null, address, rem);
|
||||
} else {
|
||||
// backing array not accessible so transfer via temporary array
|
||||
byte[] tmp = new byte[rem];
|
||||
src.get(tmp);
|
||||
src.position(pos); // reset position as write may fail
|
||||
unsafe.copyMemory(tmp, Unsafe.ARRAY_BYTE_BASE_OFFSET, null,
|
||||
address, rem);
|
||||
}
|
||||
}
|
||||
|
||||
int fd = -1;
|
||||
try {
|
||||
fd = file.openForAttributeAccess(followLinks);
|
||||
} catch (UnixException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
}
|
||||
try {
|
||||
try {
|
||||
fsetxattr(fd, nameAsBytes(file,name), address, rem);
|
||||
src.position(pos + rem);
|
||||
return rem;
|
||||
} catch (UnixException x) {
|
||||
throw new FileSystemException(file.getPathForExceptionMessage(),
|
||||
null, "Error writing extended attribute '" + name + "': " +
|
||||
x.getMessage());
|
||||
} finally {
|
||||
close(fd);
|
||||
}
|
||||
} finally {
|
||||
if (nb != null)
|
||||
nb.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String name) throws IOException {
|
||||
if (System.getSecurityManager() != null)
|
||||
checkAccess(file.getPathForPermissionCheck(), false, true);
|
||||
|
||||
int fd = -1;
|
||||
try {
|
||||
fd = file.openForAttributeAccess(followLinks);
|
||||
} catch (UnixException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
}
|
||||
try {
|
||||
fremovexattr(fd, nameAsBytes(file,name));
|
||||
} catch (UnixException x) {
|
||||
throw new FileSystemException(file.getPathForExceptionMessage(),
|
||||
null, "Unable to delete extended attribute '" + name + "': " + x.getMessage());
|
||||
} finally {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by copyTo/moveTo to copy extended attributes from source to target.
|
||||
*
|
||||
* @param ofd
|
||||
* file descriptor for source file
|
||||
* @param nfd
|
||||
* file descriptor for target file
|
||||
*/
|
||||
static void copyExtendedAttributes(int ofd, int nfd) {
|
||||
NativeBuffer buffer = null;
|
||||
try {
|
||||
|
||||
// call flistxattr to get list of extended attributes.
|
||||
int size = 1024;
|
||||
buffer = NativeBuffers.getNativeBuffer(size);
|
||||
for (;;) {
|
||||
try {
|
||||
size = flistxattr(ofd, buffer.address(), size);
|
||||
break;
|
||||
} catch (UnixException x) {
|
||||
// allocate larger buffer if required
|
||||
if (x.errno() == ERANGE && size < 32*1024) {
|
||||
buffer.release();
|
||||
size *= 2;
|
||||
buffer = null;
|
||||
buffer = NativeBuffers.getNativeBuffer(size);
|
||||
continue;
|
||||
}
|
||||
|
||||
// unable to get list of attributes
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// parse buffer as array of NULL-terminated C strings.
|
||||
long address = buffer.address();
|
||||
int start = 0;
|
||||
int pos = 0;
|
||||
while (pos < size) {
|
||||
if (unsafe.getByte(address + pos) == 0) {
|
||||
// extract attribute name and copy attribute to target.
|
||||
// FIXME: We can avoid needless copying by using address+pos
|
||||
// as the address of the name.
|
||||
int len = pos - start;
|
||||
byte[] name = new byte[len];
|
||||
unsafe.copyMemory(null, address+start, name,
|
||||
Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
|
||||
try {
|
||||
copyExtendedAttribute(ofd, name, nfd);
|
||||
} catch (UnixException ignore) {
|
||||
// ignore
|
||||
}
|
||||
start = pos + 1;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (buffer != null)
|
||||
buffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
private static void copyExtendedAttribute(int ofd, byte[] name, int nfd)
|
||||
throws UnixException
|
||||
{
|
||||
int size = fgetxattr(ofd, name, 0L, 0);
|
||||
NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
|
||||
try {
|
||||
long address = buffer.address();
|
||||
size = fgetxattr(ofd, name, address, size);
|
||||
fsetxattr(nfd, name, address, size);
|
||||
} finally {
|
||||
buffer.release();
|
||||
}
|
||||
}
|
||||
}
|
468
src/java.base/linux/classes/sun/nio/fs/LinuxWatchService.java
Normal file
468
src/java.base/linux/classes/sun/nio/fs/LinuxWatchService.java
Normal file
|
@ -0,0 +1,468 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2016, 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.*;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
import static sun.nio.fs.UnixNativeDispatcher.*;
|
||||
import static sun.nio.fs.UnixConstants.*;
|
||||
|
||||
/**
|
||||
* Linux implementation of WatchService based on inotify.
|
||||
*
|
||||
* In summary a background thread polls inotify plus a socket used for the wakeup
|
||||
* mechanism. Requests to add or remove a watch, or close the watch service,
|
||||
* cause the thread to wakeup and process the request. Events are processed
|
||||
* by the thread which causes it to signal/queue the corresponding watch keys.
|
||||
*/
|
||||
|
||||
class LinuxWatchService
|
||||
extends AbstractWatchService
|
||||
{
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
// background thread to read change events
|
||||
private final Poller poller;
|
||||
|
||||
LinuxWatchService(UnixFileSystem fs) throws IOException {
|
||||
// initialize inotify
|
||||
int ifd = - 1;
|
||||
try {
|
||||
ifd = inotifyInit();
|
||||
} catch (UnixException x) {
|
||||
String msg = (x.errno() == EMFILE) ?
|
||||
"User limit of inotify instances reached or too many open files" :
|
||||
x.errorString();
|
||||
throw new IOException(msg);
|
||||
}
|
||||
|
||||
// configure inotify to be non-blocking
|
||||
// create socketpair used in the close mechanism
|
||||
int sp[] = new int[2];
|
||||
try {
|
||||
configureBlocking(ifd, false);
|
||||
socketpair(sp);
|
||||
configureBlocking(sp[0], false);
|
||||
} catch (UnixException x) {
|
||||
UnixNativeDispatcher.close(ifd);
|
||||
throw new IOException(x.errorString());
|
||||
}
|
||||
|
||||
this.poller = new Poller(fs, this, ifd, sp);
|
||||
this.poller.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
WatchKey register(Path dir,
|
||||
WatchEvent.Kind<?>[] events,
|
||||
WatchEvent.Modifier... modifiers)
|
||||
throws IOException
|
||||
{
|
||||
// delegate to poller
|
||||
return poller.register(dir, events, modifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
void implClose() throws IOException {
|
||||
// delegate to poller
|
||||
poller.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* WatchKey implementation
|
||||
*/
|
||||
private static class LinuxWatchKey extends AbstractWatchKey {
|
||||
// inotify descriptor
|
||||
private final int ifd;
|
||||
// watch descriptor
|
||||
private volatile int wd;
|
||||
|
||||
LinuxWatchKey(UnixPath dir, LinuxWatchService watcher, int ifd, int wd) {
|
||||
super(dir, watcher);
|
||||
this.ifd = ifd;
|
||||
this.wd = wd;
|
||||
}
|
||||
|
||||
int descriptor() {
|
||||
return wd;
|
||||
}
|
||||
|
||||
void invalidate(boolean remove) {
|
||||
if (remove) {
|
||||
try {
|
||||
inotifyRmWatch(ifd, wd);
|
||||
} catch (UnixException x) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
wd = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return (wd != -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
if (isValid()) {
|
||||
// delegate to poller
|
||||
((LinuxWatchService)watcher()).poller.cancel(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Background thread to read from inotify
|
||||
*/
|
||||
private static class Poller extends AbstractPoller {
|
||||
/**
|
||||
* struct inotify_event {
|
||||
* int wd;
|
||||
* uint32_t mask;
|
||||
* uint32_t len;
|
||||
* char name __flexarr; // present if len > 0
|
||||
* } act_t;
|
||||
*/
|
||||
private static final int SIZEOF_INOTIFY_EVENT = eventSize();
|
||||
private static final int[] offsets = eventOffsets();
|
||||
private static final int OFFSETOF_WD = offsets[0];
|
||||
private static final int OFFSETOF_MASK = offsets[1];
|
||||
private static final int OFFSETOF_LEN = offsets[3];
|
||||
private static final int OFFSETOF_NAME = offsets[4];
|
||||
|
||||
private static final int IN_MODIFY = 0x00000002;
|
||||
private static final int IN_ATTRIB = 0x00000004;
|
||||
private static final int IN_MOVED_FROM = 0x00000040;
|
||||
private static final int IN_MOVED_TO = 0x00000080;
|
||||
private static final int IN_CREATE = 0x00000100;
|
||||
private static final int IN_DELETE = 0x00000200;
|
||||
|
||||
private static final int IN_UNMOUNT = 0x00002000;
|
||||
private static final int IN_Q_OVERFLOW = 0x00004000;
|
||||
private static final int IN_IGNORED = 0x00008000;
|
||||
|
||||
// sizeof buffer for when polling inotify
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
|
||||
private final UnixFileSystem fs;
|
||||
private final LinuxWatchService watcher;
|
||||
|
||||
// inotify file descriptor
|
||||
private final int ifd;
|
||||
// socketpair used to shutdown polling thread
|
||||
private final int socketpair[];
|
||||
// maps watch descriptor to Key
|
||||
private final Map<Integer,LinuxWatchKey> wdToKey;
|
||||
// address of read buffer
|
||||
private final long address;
|
||||
|
||||
Poller(UnixFileSystem fs, LinuxWatchService watcher, int ifd, int[] sp) {
|
||||
this.fs = fs;
|
||||
this.watcher = watcher;
|
||||
this.ifd = ifd;
|
||||
this.socketpair = sp;
|
||||
this.wdToKey = new HashMap<>();
|
||||
this.address = unsafe.allocateMemory(BUFFER_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
void wakeup() throws IOException {
|
||||
// write to socketpair to wakeup polling thread
|
||||
try {
|
||||
write(socketpair[1], address, 1);
|
||||
} catch (UnixException x) {
|
||||
throw new IOException(x.errorString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
Object implRegister(Path obj,
|
||||
Set<? extends WatchEvent.Kind<?>> events,
|
||||
WatchEvent.Modifier... modifiers)
|
||||
{
|
||||
UnixPath dir = (UnixPath)obj;
|
||||
|
||||
int mask = 0;
|
||||
for (WatchEvent.Kind<?> event: events) {
|
||||
if (event == StandardWatchEventKinds.ENTRY_CREATE) {
|
||||
mask |= IN_CREATE | IN_MOVED_TO;
|
||||
continue;
|
||||
}
|
||||
if (event == StandardWatchEventKinds.ENTRY_DELETE) {
|
||||
mask |= IN_DELETE | IN_MOVED_FROM;
|
||||
continue;
|
||||
}
|
||||
if (event == StandardWatchEventKinds.ENTRY_MODIFY) {
|
||||
mask |= IN_MODIFY | IN_ATTRIB;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// no modifiers supported at this time
|
||||
if (modifiers.length > 0) {
|
||||
for (WatchEvent.Modifier modifier: modifiers) {
|
||||
if (modifier == null)
|
||||
return new NullPointerException();
|
||||
if (!ExtendedOptions.SENSITIVITY_HIGH.matches(modifier) &&
|
||||
!ExtendedOptions.SENSITIVITY_MEDIUM.matches(modifier) &&
|
||||
!ExtendedOptions.SENSITIVITY_LOW.matches(modifier)) {
|
||||
return new UnsupportedOperationException("Modifier not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check file is directory
|
||||
UnixFileAttributes attrs = null;
|
||||
try {
|
||||
attrs = UnixFileAttributes.get(dir, true);
|
||||
} catch (UnixException x) {
|
||||
return x.asIOException(dir);
|
||||
}
|
||||
if (!attrs.isDirectory()) {
|
||||
return new NotDirectoryException(dir.getPathForExceptionMessage());
|
||||
}
|
||||
|
||||
// register with inotify (replaces existing mask if already registered)
|
||||
int wd = -1;
|
||||
try {
|
||||
NativeBuffer buffer =
|
||||
NativeBuffers.asNativeBuffer(dir.getByteArrayForSysCalls());
|
||||
try {
|
||||
wd = inotifyAddWatch(ifd, buffer.address(), mask);
|
||||
} finally {
|
||||
buffer.release();
|
||||
}
|
||||
} catch (UnixException x) {
|
||||
if (x.errno() == ENOSPC) {
|
||||
return new IOException("User limit of inotify watches reached");
|
||||
}
|
||||
return x.asIOException(dir);
|
||||
}
|
||||
|
||||
// ensure watch descriptor is in map
|
||||
LinuxWatchKey key = wdToKey.get(wd);
|
||||
if (key == null) {
|
||||
key = new LinuxWatchKey(dir, watcher, ifd, wd);
|
||||
wdToKey.put(wd, key);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
// cancel single key
|
||||
@Override
|
||||
void implCancelKey(WatchKey obj) {
|
||||
LinuxWatchKey key = (LinuxWatchKey)obj;
|
||||
if (key.isValid()) {
|
||||
wdToKey.remove(key.descriptor());
|
||||
key.invalidate(true);
|
||||
}
|
||||
}
|
||||
|
||||
// close watch service
|
||||
@Override
|
||||
void implCloseAll() {
|
||||
// invalidate all keys
|
||||
for (Map.Entry<Integer,LinuxWatchKey> entry: wdToKey.entrySet()) {
|
||||
entry.getValue().invalidate(true);
|
||||
}
|
||||
wdToKey.clear();
|
||||
|
||||
// free resources
|
||||
unsafe.freeMemory(address);
|
||||
UnixNativeDispatcher.close(socketpair[0]);
|
||||
UnixNativeDispatcher.close(socketpair[1]);
|
||||
UnixNativeDispatcher.close(ifd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Poller main loop
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
for (;;) {
|
||||
int nReady, bytesRead;
|
||||
|
||||
// wait for close or inotify event
|
||||
nReady = poll(ifd, socketpair[0]);
|
||||
|
||||
// read from inotify
|
||||
try {
|
||||
bytesRead = read(ifd, address, BUFFER_SIZE);
|
||||
} catch (UnixException x) {
|
||||
if (x.errno() != EAGAIN)
|
||||
throw x;
|
||||
bytesRead = 0;
|
||||
}
|
||||
|
||||
// iterate over buffer to decode events
|
||||
int offset = 0;
|
||||
while (offset < bytesRead) {
|
||||
long event = address + offset;
|
||||
int wd = unsafe.getInt(event + OFFSETOF_WD);
|
||||
int mask = unsafe.getInt(event + OFFSETOF_MASK);
|
||||
int len = unsafe.getInt(event + OFFSETOF_LEN);
|
||||
|
||||
// file name
|
||||
UnixPath name = null;
|
||||
if (len > 0) {
|
||||
int actual = len;
|
||||
|
||||
// null-terminated and maybe additional null bytes to
|
||||
// align the next event
|
||||
while (actual > 0) {
|
||||
long last = event + OFFSETOF_NAME + actual - 1;
|
||||
if (unsafe.getByte(last) != 0)
|
||||
break;
|
||||
actual--;
|
||||
}
|
||||
if (actual > 0) {
|
||||
byte[] buf = new byte[actual];
|
||||
unsafe.copyMemory(null, event + OFFSETOF_NAME,
|
||||
buf, Unsafe.ARRAY_BYTE_BASE_OFFSET, actual);
|
||||
name = new UnixPath(fs, buf);
|
||||
}
|
||||
}
|
||||
|
||||
// process event
|
||||
processEvent(wd, mask, name);
|
||||
|
||||
offset += (SIZEOF_INOTIFY_EVENT + len);
|
||||
}
|
||||
|
||||
// process any pending requests
|
||||
if ((nReady > 1) || (nReady == 1 && bytesRead == 0)) {
|
||||
try {
|
||||
read(socketpair[0], address, BUFFER_SIZE);
|
||||
boolean shutdown = processRequests();
|
||||
if (shutdown)
|
||||
break;
|
||||
} catch (UnixException x) {
|
||||
if (x.errno() != UnixConstants.EAGAIN)
|
||||
throw x;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (UnixException x) {
|
||||
x.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* map inotify event to WatchEvent.Kind
|
||||
*/
|
||||
private WatchEvent.Kind<?> maskToEventKind(int mask) {
|
||||
if ((mask & IN_MODIFY) > 0)
|
||||
return StandardWatchEventKinds.ENTRY_MODIFY;
|
||||
if ((mask & IN_ATTRIB) > 0)
|
||||
return StandardWatchEventKinds.ENTRY_MODIFY;
|
||||
if ((mask & IN_CREATE) > 0)
|
||||
return StandardWatchEventKinds.ENTRY_CREATE;
|
||||
if ((mask & IN_MOVED_TO) > 0)
|
||||
return StandardWatchEventKinds.ENTRY_CREATE;
|
||||
if ((mask & IN_DELETE) > 0)
|
||||
return StandardWatchEventKinds.ENTRY_DELETE;
|
||||
if ((mask & IN_MOVED_FROM) > 0)
|
||||
return StandardWatchEventKinds.ENTRY_DELETE;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process event from inotify
|
||||
*/
|
||||
private void processEvent(int wd, int mask, final UnixPath name) {
|
||||
// overflow - signal all keys
|
||||
if ((mask & IN_Q_OVERFLOW) > 0) {
|
||||
for (Map.Entry<Integer,LinuxWatchKey> entry: wdToKey.entrySet()) {
|
||||
entry.getValue()
|
||||
.signalEvent(StandardWatchEventKinds.OVERFLOW, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// lookup wd to get key
|
||||
LinuxWatchKey key = wdToKey.get(wd);
|
||||
if (key == null)
|
||||
return; // should not happen
|
||||
|
||||
// file deleted
|
||||
if ((mask & IN_IGNORED) > 0) {
|
||||
wdToKey.remove(wd);
|
||||
key.invalidate(false);
|
||||
key.signal();
|
||||
return;
|
||||
}
|
||||
|
||||
// event for directory itself
|
||||
if (name == null)
|
||||
return;
|
||||
|
||||
// map to event and queue to key
|
||||
WatchEvent.Kind<?> kind = maskToEventKind(mask);
|
||||
if (kind != null) {
|
||||
key.signalEvent(kind, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- native methods --
|
||||
|
||||
// sizeof inotify_event
|
||||
private static native int eventSize();
|
||||
|
||||
// offsets of inotify_event
|
||||
private static native int[] eventOffsets();
|
||||
|
||||
private static native int inotifyInit() throws UnixException;
|
||||
|
||||
private static native int inotifyAddWatch(int fd, long pathAddress, int mask)
|
||||
throws UnixException;
|
||||
|
||||
private static native void inotifyRmWatch(int fd, int wd)
|
||||
throws UnixException;
|
||||
|
||||
private static native void configureBlocking(int fd, boolean blocking)
|
||||
throws UnixException;
|
||||
|
||||
private static native void socketpair(int[] sv) throws UnixException;
|
||||
|
||||
private static native int poll(int fd1, int fd2) throws UnixException;
|
||||
|
||||
static {
|
||||
AccessController.doPrivileged(new PrivilegedAction<>() {
|
||||
public Void run() {
|
||||
System.loadLibrary("nio");
|
||||
return null;
|
||||
}});
|
||||
}
|
||||
}
|
271
src/java.base/linux/native/libjava/ProcessHandleImpl_linux.c
Normal file
271
src/java.base/linux/native/libjava/ProcessHandleImpl_linux.c
Normal file
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 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.h"
|
||||
#include "jni_util.h"
|
||||
#include "java_lang_ProcessHandleImpl.h"
|
||||
#include "java_lang_ProcessHandleImpl_Info.h"
|
||||
|
||||
#include "ProcessHandleImpl_unix.h"
|
||||
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
* Implementation of native ProcessHandleImpl functions for Linux.
|
||||
* See ProcessHandleImpl_unix.c for more details.
|
||||
*/
|
||||
|
||||
/* Signatures for internal OS specific functions. */
|
||||
static long long getBoottime(JNIEnv *env);
|
||||
|
||||
/* A static offset in milliseconds since boot. */
|
||||
static long long bootTime_ms;
|
||||
static long clock_ticks_per_second;
|
||||
static int pageSize;
|
||||
|
||||
void os_initNative(JNIEnv *env, jclass clazz) {
|
||||
bootTime_ms = getBoottime(env);
|
||||
clock_ticks_per_second = sysconf(_SC_CLK_TCK);
|
||||
pageSize = sysconf(_SC_PAGESIZE);
|
||||
}
|
||||
|
||||
jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
|
||||
jlongArray jparentArray, jlongArray jstimesArray) {
|
||||
return unix_getChildren(env, jpid, jarray, jparentArray, jstimesArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read /proc/<pid>/stat and return the ppid, total cputime and start time.
|
||||
* -1 is fail; >= 0 is parent pid
|
||||
* 'total' will contain the running time of 'pid' in nanoseconds.
|
||||
* 'start' will contain the start time of 'pid' in milliseconds since epoch.
|
||||
*/
|
||||
pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid,
|
||||
jlong *totalTime, jlong* startTime) {
|
||||
FILE* fp;
|
||||
char buffer[2048];
|
||||
int statlen;
|
||||
char fn[32];
|
||||
char* s;
|
||||
int parentPid;
|
||||
long unsigned int utime = 0; // clock tics
|
||||
long unsigned int stime = 0; // clock tics
|
||||
long long unsigned int start = 0; // microseconds
|
||||
|
||||
/*
|
||||
* Try to stat and then open /proc/%d/stat
|
||||
*/
|
||||
snprintf(fn, sizeof fn, "/proc/%d/stat", pid);
|
||||
|
||||
fp = fopen(fn, "r");
|
||||
if (fp == NULL) {
|
||||
return -1; // fail, no such /proc/pid/stat
|
||||
}
|
||||
|
||||
/*
|
||||
* The format is: pid (command) state ppid ...
|
||||
* As the command could be anything we must find the right most
|
||||
* ")" and then skip the white spaces that follow it.
|
||||
*/
|
||||
statlen = fread(buffer, 1, (sizeof buffer - 1), fp);
|
||||
fclose(fp);
|
||||
if (statlen < 0) {
|
||||
return -1; // parent pid is not available
|
||||
}
|
||||
|
||||
buffer[statlen] = '\0';
|
||||
s = strchr(buffer, '(');
|
||||
if (s == NULL) {
|
||||
return -1; // parent pid is not available
|
||||
}
|
||||
// Found start of command, skip to end
|
||||
s++;
|
||||
s = strrchr(s, ')');
|
||||
if (s == NULL) {
|
||||
return -1; // parent pid is not available
|
||||
}
|
||||
s++;
|
||||
|
||||
// Scan the needed fields from status, retaining only ppid(4),
|
||||
// utime (14), stime(15), starttime(22)
|
||||
if (4 != sscanf(s, " %*c %d %*d %*d %*d %*d %*d %*u %*u %*u %*u %lu %lu %*d %*d %*d %*d %*d %*d %llu",
|
||||
&parentPid, &utime, &stime, &start)) {
|
||||
return 0; // not all values parsed; return error
|
||||
}
|
||||
|
||||
*totalTime = (utime + stime) * (jlong)(1000000000 / clock_ticks_per_second);
|
||||
|
||||
*startTime = bootTime_ms + ((start * 1000) / clock_ticks_per_second);
|
||||
|
||||
return parentPid;
|
||||
}
|
||||
|
||||
void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
|
||||
int fd;
|
||||
int cmdlen = 0;
|
||||
char *cmdline = NULL, *cmdEnd = NULL; // used for command line args and exe
|
||||
char *args = NULL;
|
||||
jstring cmdexe = NULL;
|
||||
char fn[32];
|
||||
struct stat stat_buf;
|
||||
|
||||
/*
|
||||
* Stat /proc/<pid> to get the user id
|
||||
*/
|
||||
snprintf(fn, sizeof fn, "/proc/%d", pid);
|
||||
if (stat(fn, &stat_buf) == 0) {
|
||||
unix_getUserInfo(env, jinfo, stat_buf.st_uid);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to open /proc/<pid>/cmdline
|
||||
*/
|
||||
strncat(fn, "/cmdline", sizeof fn - strnlen(fn, sizeof fn) - 1);
|
||||
if ((fd = open(fn, O_RDONLY)) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
do { // Block to break out of on errors
|
||||
int i, truncated = 0;
|
||||
int count;
|
||||
char *s;
|
||||
|
||||
/*
|
||||
* The path name read by readlink() is limited to PATH_MAX characters.
|
||||
* The content of /proc/<pid>/cmdline is limited to PAGE_SIZE characters.
|
||||
*/
|
||||
cmdline = (char*)malloc((PATH_MAX > pageSize ? PATH_MAX : pageSize) + 1);
|
||||
if (cmdline == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* On Linux, the full path to the executable command is the link in
|
||||
* /proc/<pid>/exe. But it is only readable for processes we own.
|
||||
*/
|
||||
snprintf(fn, sizeof fn, "/proc/%d/exe", pid);
|
||||
if ((cmdlen = readlink(fn, cmdline, PATH_MAX)) > 0) {
|
||||
// null terminate and create String to store for command
|
||||
cmdline[cmdlen] = '\0';
|
||||
cmdexe = JNU_NewStringPlatform(env, cmdline);
|
||||
(*env)->ExceptionClear(env); // unconditionally clear any exception
|
||||
}
|
||||
|
||||
/*
|
||||
* The command-line arguments appear as a set of strings separated by
|
||||
* null bytes ('\0'), with a further null byte after the last
|
||||
* string. The last string is only null terminated if the whole command
|
||||
* line is not exceeding (PAGE_SIZE - 1) characters.
|
||||
*/
|
||||
cmdlen = 0;
|
||||
s = cmdline;
|
||||
while ((count = read(fd, s, pageSize - cmdlen)) > 0) {
|
||||
cmdlen += count;
|
||||
s += count;
|
||||
}
|
||||
if (count < 0) {
|
||||
break;
|
||||
}
|
||||
// We have to null-terminate because the process may have changed argv[]
|
||||
// or because the content in /proc/<pid>/cmdline is truncated.
|
||||
cmdline[cmdlen] = '\0';
|
||||
if (cmdlen == pageSize && cmdline[pageSize - 1] != '\0') {
|
||||
truncated = 1;
|
||||
} else if (cmdlen == 0) {
|
||||
// /proc/<pid>/cmdline was empty. This usually happens for kernel processes
|
||||
// like '[kthreadd]'. We could try to read /proc/<pid>/comm in the future.
|
||||
}
|
||||
if (cmdlen > 0 && (cmdexe == NULL || truncated)) {
|
||||
// We have no exact command or the arguments are truncated.
|
||||
// In this case we save the command line from /proc/<pid>/cmdline.
|
||||
args = (char*)malloc(pageSize + 1);
|
||||
if (args != NULL) {
|
||||
memcpy(args, cmdline, cmdlen + 1);
|
||||
for (i = 0; i < cmdlen; i++) {
|
||||
if (args[i] == '\0') {
|
||||
args[i] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
i = 0;
|
||||
if (!truncated) {
|
||||
// Count the arguments
|
||||
cmdEnd = &cmdline[cmdlen];
|
||||
for (s = cmdline; *s != '\0' && (s < cmdEnd); i++) {
|
||||
s += strnlen(s, (cmdEnd - s)) + 1;
|
||||
}
|
||||
}
|
||||
unix_fillArgArray(env, jinfo, i, cmdline, cmdEnd, cmdexe, args);
|
||||
} while (0);
|
||||
|
||||
if (cmdline != NULL) {
|
||||
free(cmdline);
|
||||
}
|
||||
if (args != NULL) {
|
||||
free(args);
|
||||
}
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the boottime from /proc/stat.
|
||||
*/
|
||||
static long long getBoottime(JNIEnv *env) {
|
||||
FILE *fp;
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
long long bootTime = 0;
|
||||
|
||||
fp = fopen("/proc/stat", "r");
|
||||
if (fp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (getline(&line, &len, fp) != -1) {
|
||||
if (sscanf(line, "btime %llu", &bootTime) == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(line);
|
||||
|
||||
if (fp != 0) {
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return bootTime * 1000;
|
||||
}
|
230
src/java.base/linux/native/libjsig/jsig.c
Normal file
230
src/java.base/linux/native/libjsig/jsig.c
Normal file
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* Copyright (c) 2001, 2013, 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* CopyrightVersion 1.2 */
|
||||
|
||||
/* This is a special library that should be loaded before libc &
|
||||
* libthread to interpose the signal handler installation functions:
|
||||
* sigaction(), signal(), sigset().
|
||||
* Used for signal-chaining. See RFE 4381843.
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define bool int
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
#define MASK(sig) ((uint64_t)1 << (sig-1)) // 0 is not a signal.
|
||||
// Check whether all signals fit into jvmsigs. -1 as MASK shifts by -1.
|
||||
#if (64 < NSIG-1)
|
||||
#error "Not all signals can be encoded in jvmsigs. Adapt its type!"
|
||||
#endif
|
||||
static struct sigaction sact[NSIG]; /* saved signal handlers */
|
||||
static uint64_t jvmsigs = 0; /* signals used by jvm */
|
||||
|
||||
/* used to synchronize the installation of signal handlers */
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
||||
static pthread_t tid = 0;
|
||||
|
||||
typedef void (*sa_handler_t)(int);
|
||||
typedef void (*sa_sigaction_t)(int, siginfo_t *, void *);
|
||||
typedef sa_handler_t (*signal_t)(int, sa_handler_t);
|
||||
typedef int (*sigaction_t)(int, const struct sigaction *, struct sigaction *);
|
||||
|
||||
static signal_t os_signal = 0; /* os's version of signal()/sigset() */
|
||||
static sigaction_t os_sigaction = 0; /* os's version of sigaction() */
|
||||
|
||||
static bool jvm_signal_installing = false;
|
||||
static bool jvm_signal_installed = false;
|
||||
|
||||
static void signal_lock() {
|
||||
pthread_mutex_lock(&mutex);
|
||||
/* When the jvm is installing its set of signal handlers, threads
|
||||
* other than the jvm thread should wait */
|
||||
if (jvm_signal_installing) {
|
||||
if (tid != pthread_self()) {
|
||||
pthread_cond_wait(&cond, &mutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void signal_unlock() {
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
static sa_handler_t call_os_signal(int sig, sa_handler_t disp,
|
||||
bool is_sigset) {
|
||||
if (os_signal == NULL) {
|
||||
if (!is_sigset) {
|
||||
os_signal = (signal_t)dlsym(RTLD_NEXT, "signal");
|
||||
} else {
|
||||
os_signal = (signal_t)dlsym(RTLD_NEXT, "sigset");
|
||||
}
|
||||
if (os_signal == NULL) {
|
||||
printf("%s\n", dlerror());
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
return (*os_signal)(sig, disp);
|
||||
}
|
||||
|
||||
static void save_signal_handler(int sig, sa_handler_t disp) {
|
||||
sigset_t set;
|
||||
sact[sig].sa_handler = disp;
|
||||
sigemptyset(&set);
|
||||
sact[sig].sa_mask = set;
|
||||
sact[sig].sa_flags = 0;
|
||||
}
|
||||
|
||||
static sa_handler_t set_signal(int sig, sa_handler_t disp, bool is_sigset) {
|
||||
sa_handler_t oldhandler;
|
||||
bool sigused;
|
||||
|
||||
signal_lock();
|
||||
|
||||
sigused = (sig < NSIG) && ((MASK(sig) & jvmsigs) != 0);
|
||||
if (jvm_signal_installed && sigused) {
|
||||
/* jvm has installed its signal handler for this signal. */
|
||||
/* Save the handler. Don't really install it. */
|
||||
oldhandler = sact[sig].sa_handler;
|
||||
save_signal_handler(sig, disp);
|
||||
|
||||
signal_unlock();
|
||||
return oldhandler;
|
||||
} else if (sig < NSIG && jvm_signal_installing) {
|
||||
/* jvm is installing its signal handlers. Install the new
|
||||
* handlers and save the old ones. jvm uses sigaction().
|
||||
* Leave the piece here just in case. */
|
||||
oldhandler = call_os_signal(sig, disp, is_sigset);
|
||||
save_signal_handler(sig, oldhandler);
|
||||
|
||||
/* Record the signals used by jvm */
|
||||
jvmsigs |= MASK(sig);
|
||||
|
||||
signal_unlock();
|
||||
return oldhandler;
|
||||
} else {
|
||||
/* jvm has no relation with this signal (yet). Install the
|
||||
* the handler. */
|
||||
oldhandler = call_os_signal(sig, disp, is_sigset);
|
||||
|
||||
signal_unlock();
|
||||
return oldhandler;
|
||||
}
|
||||
}
|
||||
|
||||
sa_handler_t signal(int sig, sa_handler_t disp) {
|
||||
return set_signal(sig, disp, false);
|
||||
}
|
||||
|
||||
sa_handler_t sigset(int sig, sa_handler_t disp) {
|
||||
return set_signal(sig, disp, true);
|
||||
}
|
||||
|
||||
static int call_os_sigaction(int sig, const struct sigaction *act,
|
||||
struct sigaction *oact) {
|
||||
if (os_sigaction == NULL) {
|
||||
os_sigaction = (sigaction_t)dlsym(RTLD_NEXT, "sigaction");
|
||||
if (os_sigaction == NULL) {
|
||||
printf("%s\n", dlerror());
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
return (*os_sigaction)(sig, act, oact);
|
||||
}
|
||||
|
||||
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact) {
|
||||
int res;
|
||||
bool sigused;
|
||||
struct sigaction oldAct;
|
||||
|
||||
signal_lock();
|
||||
|
||||
sigused = (sig < NSIG) && ((MASK(sig) & jvmsigs) != 0);
|
||||
if (jvm_signal_installed && sigused) {
|
||||
/* jvm has installed its signal handler for this signal. */
|
||||
/* Save the handler. Don't really install it. */
|
||||
if (oact != NULL) {
|
||||
*oact = sact[sig];
|
||||
}
|
||||
if (act != NULL) {
|
||||
sact[sig] = *act;
|
||||
}
|
||||
|
||||
signal_unlock();
|
||||
return 0;
|
||||
} else if (sig < NSIG && jvm_signal_installing) {
|
||||
/* jvm is installing its signal handlers. Install the new
|
||||
* handlers and save the old ones. */
|
||||
res = call_os_sigaction(sig, act, &oldAct);
|
||||
sact[sig] = oldAct;
|
||||
if (oact != NULL) {
|
||||
*oact = oldAct;
|
||||
}
|
||||
|
||||
/* Record the signals used by jvm */
|
||||
jvmsigs |= MASK(sig);
|
||||
|
||||
signal_unlock();
|
||||
return res;
|
||||
} else {
|
||||
/* jvm has no relation with this signal (yet). Install the
|
||||
* the handler. */
|
||||
res = call_os_sigaction(sig, act, oact);
|
||||
|
||||
signal_unlock();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/* The three functions for the jvm to call into */
|
||||
void JVM_begin_signal_setting() {
|
||||
signal_lock();
|
||||
jvm_signal_installing = true;
|
||||
tid = pthread_self();
|
||||
signal_unlock();
|
||||
}
|
||||
|
||||
void JVM_end_signal_setting() {
|
||||
signal_lock();
|
||||
jvm_signal_installed = true;
|
||||
jvm_signal_installing = false;
|
||||
pthread_cond_broadcast(&cond);
|
||||
signal_unlock();
|
||||
}
|
||||
|
||||
struct sigaction *JVM_get_signal_action(int sig) {
|
||||
/* Does race condition make sense here? */
|
||||
if ((MASK(sig) & jvmsigs) != 0) {
|
||||
return &sact[sig];
|
||||
}
|
||||
return NULL;
|
||||
}
|
458
src/java.base/linux/native/libnet/linux_close.c
Normal file
458
src/java.base/linux/native/libnet/linux_close.c
Normal file
|
@ -0,0 +1,458 @@
|
|||
/*
|
||||
* Copyright (c) 2001, 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.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.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 = (__SIGRTMAX - 2);
|
||||
|
||||
/*
|
||||
* 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));
|
||||
|
||||
{
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_ReadV(int s, const struct iovec * vector, int count) {
|
||||
BLOCKING_IO_RETURN_INT( s, readv(s, vector, count) );
|
||||
}
|
||||
|
||||
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_WriteV(int s, const struct iovec * vector, int count) {
|
||||
BLOCKING_IO_RETURN_INT( s, writev(s, vector, count) );
|
||||
}
|
||||
|
||||
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) {
|
||||
jlong newNanoTime = JVM_NanoTime(env, 0);
|
||||
nanoTimeout -= newNanoTime - prevNanoTime;
|
||||
if (nanoTimeout < NET_NSEC_PER_MSEC) {
|
||||
return 0;
|
||||
}
|
||||
prevNanoTime = newNanoTime;
|
||||
} else {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
97
src/java.base/linux/native/libnio/ch/EPoll.c
Normal file
97
src/java.base/linux/native/libnio/ch/EPoll.c
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2011, 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.h"
|
||||
#include "jni_util.h"
|
||||
#include "jvm.h"
|
||||
#include "jlong.h"
|
||||
#include "nio_util.h"
|
||||
|
||||
#include "sun_nio_ch_EPoll.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_EPoll_eventSize(JNIEnv* env, jclass this)
|
||||
{
|
||||
return sizeof(struct epoll_event);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_EPoll_eventsOffset(JNIEnv* env, jclass this)
|
||||
{
|
||||
return offsetof(struct epoll_event, events);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_EPoll_dataOffset(JNIEnv* env, jclass this)
|
||||
{
|
||||
return offsetof(struct epoll_event, data);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_EPoll_epollCreate(JNIEnv *env, jclass c) {
|
||||
/*
|
||||
* epoll_create expects a size as a hint to the kernel about how to
|
||||
* dimension internal structures. We can't predict the size in advance.
|
||||
*/
|
||||
int epfd = epoll_create(256);
|
||||
if (epfd < 0) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "epoll_create failed");
|
||||
}
|
||||
return epfd;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_EPoll_epollCtl(JNIEnv *env, jclass c, jint epfd,
|
||||
jint opcode, jint fd, jint events)
|
||||
{
|
||||
struct epoll_event event;
|
||||
int res;
|
||||
|
||||
event.events = events;
|
||||
event.data.fd = fd;
|
||||
|
||||
RESTARTABLE(epoll_ctl(epfd, (int)opcode, (int)fd, &event), res);
|
||||
|
||||
return (res == 0) ? 0 : errno;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_EPoll_epollWait(JNIEnv *env, jclass c,
|
||||
jint epfd, jlong address, jint numfds)
|
||||
{
|
||||
struct epoll_event *events = jlong_to_ptr(address);
|
||||
int res;
|
||||
|
||||
RESTARTABLE(epoll_wait(epfd, events, numfds, -1), res);
|
||||
if (res < 0) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "epoll_wait failed");
|
||||
}
|
||||
return res;
|
||||
}
|
160
src/java.base/linux/native/libnio/ch/EPollArrayWrapper.c
Normal file
160
src/java.base/linux/native/libnio/ch/EPollArrayWrapper.c
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2016, 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.h"
|
||||
#include "jni_util.h"
|
||||
#include "jvm.h"
|
||||
#include "jlong.h"
|
||||
|
||||
#include "sun_nio_ch_EPollArrayWrapper.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#define RESTARTABLE(_cmd, _result) do { \
|
||||
do { \
|
||||
_result = _cmd; \
|
||||
} while((_result == -1) && (errno == EINTR)); \
|
||||
} while(0)
|
||||
|
||||
|
||||
static int
|
||||
iepoll(int epfd, struct epoll_event *events, int numfds, jlong timeout)
|
||||
{
|
||||
jlong start, now;
|
||||
int remaining = timeout;
|
||||
struct timeval t;
|
||||
int diff;
|
||||
|
||||
gettimeofday(&t, NULL);
|
||||
start = t.tv_sec * 1000 + t.tv_usec / 1000;
|
||||
|
||||
for (;;) {
|
||||
int res = epoll_wait(epfd, events, numfds, remaining);
|
||||
if (res < 0 && errno == EINTR) {
|
||||
if (remaining >= 0) {
|
||||
gettimeofday(&t, NULL);
|
||||
now = t.tv_sec * 1000 + t.tv_usec / 1000;
|
||||
diff = now - start;
|
||||
remaining -= diff;
|
||||
if (diff < 0 || remaining <= 0) {
|
||||
return 0;
|
||||
}
|
||||
start = now;
|
||||
}
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_ch_EPollArrayWrapper_init(JNIEnv *env, jclass this)
|
||||
{
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_EPollArrayWrapper_epollCreate(JNIEnv *env, jobject this)
|
||||
{
|
||||
/*
|
||||
* epoll_create expects a size as a hint to the kernel about how to
|
||||
* dimension internal structures. We can't predict the size in advance.
|
||||
*/
|
||||
int epfd = epoll_create(256);
|
||||
if (epfd < 0) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "epoll_create failed");
|
||||
}
|
||||
return epfd;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_EPollArrayWrapper_sizeofEPollEvent(JNIEnv* env, jclass this)
|
||||
{
|
||||
return sizeof(struct epoll_event);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_EPollArrayWrapper_offsetofData(JNIEnv* env, jclass this)
|
||||
{
|
||||
return offsetof(struct epoll_event, data);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_ch_EPollArrayWrapper_epollCtl(JNIEnv *env, jobject this, jint epfd,
|
||||
jint opcode, jint fd, jint events)
|
||||
{
|
||||
struct epoll_event event;
|
||||
int res;
|
||||
|
||||
event.events = events;
|
||||
event.data.fd = fd;
|
||||
|
||||
RESTARTABLE(epoll_ctl(epfd, (int)opcode, (int)fd, &event), res);
|
||||
|
||||
/*
|
||||
* A channel may be registered with several Selectors. When each Selector
|
||||
* is polled a EPOLL_CTL_DEL op will be inserted into its pending update
|
||||
* list to remove the file descriptor from epoll. The "last" Selector will
|
||||
* close the file descriptor which automatically unregisters it from each
|
||||
* epoll descriptor. To avoid costly synchronization between Selectors we
|
||||
* allow pending updates to be processed, ignoring errors. The errors are
|
||||
* harmless as the last update for the file descriptor is guaranteed to
|
||||
* be EPOLL_CTL_DEL.
|
||||
*/
|
||||
if (res < 0 && errno != EBADF && errno != ENOENT && errno != EPERM) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "epoll_ctl failed");
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_EPollArrayWrapper_epollWait(JNIEnv *env, jobject this,
|
||||
jlong address, jint numfds,
|
||||
jlong timeout, jint epfd)
|
||||
{
|
||||
struct epoll_event *events = jlong_to_ptr(address);
|
||||
int res;
|
||||
|
||||
if (timeout <= 0) { /* Indefinite or no wait */
|
||||
RESTARTABLE(epoll_wait(epfd, events, numfds, timeout), res);
|
||||
} else { /* Bounded wait; bounded restarts */
|
||||
res = iepoll(epfd, events, numfds, timeout);
|
||||
}
|
||||
|
||||
if (res < 0) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "epoll_wait failed");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_ch_EPollArrayWrapper_interrupt(JNIEnv *env, jobject this, jint fd)
|
||||
{
|
||||
int fakebuf[1];
|
||||
fakebuf[0] = 1;
|
||||
if (write(fd, fakebuf, 1) < 0) {
|
||||
JNU_ThrowIOExceptionWithLastError(env,"write to interrupt fd failed");
|
||||
}
|
||||
}
|
76
src/java.base/linux/native/libnio/ch/EPollPort.c
Normal file
76
src/java.base/linux/native/libnio/ch/EPollPort.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2009, 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.h"
|
||||
#include "jni_util.h"
|
||||
#include "jvm.h"
|
||||
#include "jlong.h"
|
||||
#include "nio_util.h"
|
||||
|
||||
#include "sun_nio_ch_EPollPort.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_ch_EPollPort_socketpair(JNIEnv* env, jclass clazz, jintArray sv) {
|
||||
int sp[2];
|
||||
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) == -1) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "socketpair failed");
|
||||
} else {
|
||||
jint res[2];
|
||||
res[0] = (jint)sp[0];
|
||||
res[1] = (jint)sp[1];
|
||||
(*env)->SetIntArrayRegion(env, sv, 0, 2, &res[0]);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_ch_EPollPort_interrupt(JNIEnv *env, jclass c, jint fd) {
|
||||
int res;
|
||||
int buf[1];
|
||||
buf[0] = 1;
|
||||
RESTARTABLE(write(fd, buf, 1), res);
|
||||
if (res < 0) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "write failed");
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_ch_EPollPort_drain1(JNIEnv *env, jclass cl, jint fd) {
|
||||
int res;
|
||||
char buf[1];
|
||||
RESTARTABLE(read(fd, buf, 1), res);
|
||||
if (res < 0) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "drain1 failed");
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_ch_EPollPort_close0(JNIEnv *env, jclass c, jint fd) {
|
||||
int res;
|
||||
RESTARTABLE(close(fd), res);
|
||||
}
|
232
src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c
Normal file
232
src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c
Normal file
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2012, 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.h"
|
||||
#include "jni_util.h"
|
||||
#include "jvm.h"
|
||||
#include "jlong.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <mntent.h>
|
||||
|
||||
#include "sun_nio_fs_LinuxNativeDispatcher.h"
|
||||
|
||||
typedef size_t fgetxattr_func(int fd, const char* name, void* value, size_t size);
|
||||
typedef int fsetxattr_func(int fd, const char* name, void* value, size_t size, int flags);
|
||||
typedef int fremovexattr_func(int fd, const char* name);
|
||||
typedef int flistxattr_func(int fd, char* list, size_t size);
|
||||
|
||||
fgetxattr_func* my_fgetxattr_func = NULL;
|
||||
fsetxattr_func* my_fsetxattr_func = NULL;
|
||||
fremovexattr_func* my_fremovexattr_func = NULL;
|
||||
flistxattr_func* my_flistxattr_func = NULL;
|
||||
|
||||
static jfieldID entry_name;
|
||||
static jfieldID entry_dir;
|
||||
static jfieldID entry_fstype;
|
||||
static jfieldID entry_options;
|
||||
|
||||
static void throwUnixException(JNIEnv* env, int errnum) {
|
||||
jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
|
||||
"(I)V", errnum);
|
||||
if (x != NULL) {
|
||||
(*env)->Throw(env, x);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_fs_LinuxNativeDispatcher_init(JNIEnv *env, jclass clazz)
|
||||
{
|
||||
my_fgetxattr_func = (fgetxattr_func*)dlsym(RTLD_DEFAULT, "fgetxattr");
|
||||
my_fsetxattr_func = (fsetxattr_func*)dlsym(RTLD_DEFAULT, "fsetxattr");
|
||||
my_fremovexattr_func = (fremovexattr_func*)dlsym(RTLD_DEFAULT, "fremovexattr");
|
||||
my_flistxattr_func = (flistxattr_func*)dlsym(RTLD_DEFAULT, "flistxattr");
|
||||
|
||||
clazz = (*env)->FindClass(env, "sun/nio/fs/UnixMountEntry");
|
||||
CHECK_NULL(clazz);
|
||||
entry_name = (*env)->GetFieldID(env, clazz, "name", "[B");
|
||||
CHECK_NULL(entry_name);
|
||||
entry_dir = (*env)->GetFieldID(env, clazz, "dir", "[B");
|
||||
CHECK_NULL(entry_dir);
|
||||
entry_fstype = (*env)->GetFieldID(env, clazz, "fstype", "[B");
|
||||
CHECK_NULL(entry_fstype);
|
||||
entry_options = (*env)->GetFieldID(env, clazz, "opts", "[B");
|
||||
CHECK_NULL(entry_options);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_fs_LinuxNativeDispatcher_fgetxattr0(JNIEnv* env, jclass clazz,
|
||||
jint fd, jlong nameAddress, jlong valueAddress, jint valueLen)
|
||||
{
|
||||
size_t res = -1;
|
||||
const char* name = jlong_to_ptr(nameAddress);
|
||||
void* value = jlong_to_ptr(valueAddress);
|
||||
|
||||
if (my_fgetxattr_func == NULL) {
|
||||
errno = ENOTSUP;
|
||||
} else {
|
||||
/* EINTR not documented */
|
||||
res = (*my_fgetxattr_func)(fd, name, value, valueLen);
|
||||
}
|
||||
if (res == (size_t)-1)
|
||||
throwUnixException(env, errno);
|
||||
return (jint)res;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_fs_LinuxNativeDispatcher_fsetxattr0(JNIEnv* env, jclass clazz,
|
||||
jint fd, jlong nameAddress, jlong valueAddress, jint valueLen)
|
||||
{
|
||||
int res = -1;
|
||||
const char* name = jlong_to_ptr(nameAddress);
|
||||
void* value = jlong_to_ptr(valueAddress);
|
||||
|
||||
if (my_fsetxattr_func == NULL) {
|
||||
errno = ENOTSUP;
|
||||
} else {
|
||||
/* EINTR not documented */
|
||||
res = (*my_fsetxattr_func)(fd, name, value, valueLen, 0);
|
||||
}
|
||||
if (res == -1)
|
||||
throwUnixException(env, errno);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_fs_LinuxNativeDispatcher_fremovexattr0(JNIEnv* env, jclass clazz,
|
||||
jint fd, jlong nameAddress)
|
||||
{
|
||||
int res = -1;
|
||||
const char* name = jlong_to_ptr(nameAddress);
|
||||
|
||||
if (my_fremovexattr_func == NULL) {
|
||||
errno = ENOTSUP;
|
||||
} else {
|
||||
/* EINTR not documented */
|
||||
res = (*my_fremovexattr_func)(fd, name);
|
||||
}
|
||||
if (res == -1)
|
||||
throwUnixException(env, errno);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_fs_LinuxNativeDispatcher_flistxattr(JNIEnv* env, jclass clazz,
|
||||
jint fd, jlong listAddress, jint size)
|
||||
{
|
||||
size_t res = -1;
|
||||
char* list = jlong_to_ptr(listAddress);
|
||||
|
||||
if (my_flistxattr_func == NULL) {
|
||||
errno = ENOTSUP;
|
||||
} else {
|
||||
/* EINTR not documented */
|
||||
res = (*my_flistxattr_func)(fd, list, (size_t)size);
|
||||
}
|
||||
if (res == (size_t)-1)
|
||||
throwUnixException(env, errno);
|
||||
return (jint)res;
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_sun_nio_fs_LinuxNativeDispatcher_setmntent0(JNIEnv* env, jclass this, jlong pathAddress,
|
||||
jlong modeAddress)
|
||||
{
|
||||
FILE* fp = NULL;
|
||||
const char* path = (const char*)jlong_to_ptr(pathAddress);
|
||||
const char* mode = (const char*)jlong_to_ptr(modeAddress);
|
||||
|
||||
do {
|
||||
fp = setmntent(path, mode);
|
||||
} while (fp == NULL && errno == EINTR);
|
||||
if (fp == NULL) {
|
||||
throwUnixException(env, errno);
|
||||
}
|
||||
return ptr_to_jlong(fp);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_fs_LinuxNativeDispatcher_getmntent(JNIEnv* env, jclass this,
|
||||
jlong value, jobject entry)
|
||||
{
|
||||
struct mntent ent;
|
||||
char buf[1024];
|
||||
int buflen = sizeof(buf);
|
||||
struct mntent* m;
|
||||
FILE* fp = jlong_to_ptr(value);
|
||||
jsize len;
|
||||
jbyteArray bytes;
|
||||
char* name;
|
||||
char* dir;
|
||||
char* fstype;
|
||||
char* options;
|
||||
|
||||
m = getmntent_r(fp, &ent, (char*)&buf, buflen);
|
||||
if (m == NULL)
|
||||
return -1;
|
||||
name = m->mnt_fsname;
|
||||
dir = m->mnt_dir;
|
||||
fstype = m->mnt_type;
|
||||
options = m->mnt_opts;
|
||||
|
||||
len = strlen(name);
|
||||
bytes = (*env)->NewByteArray(env, len);
|
||||
if (bytes == NULL)
|
||||
return -1;
|
||||
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)name);
|
||||
(*env)->SetObjectField(env, entry, entry_name, bytes);
|
||||
|
||||
len = strlen(dir);
|
||||
bytes = (*env)->NewByteArray(env, len);
|
||||
if (bytes == NULL)
|
||||
return -1;
|
||||
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)dir);
|
||||
(*env)->SetObjectField(env, entry, entry_dir, bytes);
|
||||
|
||||
len = strlen(fstype);
|
||||
bytes = (*env)->NewByteArray(env, len);
|
||||
if (bytes == NULL)
|
||||
return -1;
|
||||
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)fstype);
|
||||
(*env)->SetObjectField(env, entry, entry_fstype, bytes);
|
||||
|
||||
len = strlen(options);
|
||||
bytes = (*env)->NewByteArray(env, len);
|
||||
if (bytes == NULL)
|
||||
return -1;
|
||||
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)options);
|
||||
(*env)->SetObjectField(env, entry, entry_options, bytes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_fs_LinuxNativeDispatcher_endmntent(JNIEnv* env, jclass this, jlong stream)
|
||||
{
|
||||
FILE* fp = jlong_to_ptr(stream);
|
||||
/* FIXME - man page doesn't explain how errors are returned */
|
||||
endmntent(fp);
|
||||
}
|
153
src/java.base/linux/native/libnio/fs/LinuxWatchService.c
Normal file
153
src/java.base/linux/native/libnio/fs/LinuxWatchService.c
Normal file
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2011, 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.h"
|
||||
#include "jni_util.h"
|
||||
#include "jvm.h"
|
||||
#include "jlong.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <poll.h>
|
||||
#include <sys/inotify.h>
|
||||
|
||||
#include "sun_nio_fs_LinuxWatchService.h"
|
||||
|
||||
static void throwUnixException(JNIEnv* env, int errnum) {
|
||||
jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
|
||||
"(I)V", errnum);
|
||||
if (x != NULL) {
|
||||
(*env)->Throw(env, x);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_fs_LinuxWatchService_eventSize(JNIEnv *env, jclass clazz)
|
||||
{
|
||||
return (jint)sizeof(struct inotify_event);
|
||||
}
|
||||
|
||||
JNIEXPORT jintArray JNICALL
|
||||
Java_sun_nio_fs_LinuxWatchService_eventOffsets(JNIEnv *env, jclass clazz)
|
||||
{
|
||||
jintArray result = (*env)->NewIntArray(env, 5);
|
||||
if (result != NULL) {
|
||||
jint arr[5];
|
||||
arr[0] = (jint)offsetof(struct inotify_event, wd);
|
||||
arr[1] = (jint)offsetof(struct inotify_event, mask);
|
||||
arr[2] = (jint)offsetof(struct inotify_event, cookie);
|
||||
arr[3] = (jint)offsetof(struct inotify_event, len);
|
||||
arr[4] = (jint)offsetof(struct inotify_event, name);
|
||||
(*env)->SetIntArrayRegion(env, result, 0, 5, arr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_fs_LinuxWatchService_inotifyInit
|
||||
(JNIEnv* env, jclass clazz)
|
||||
{
|
||||
int ifd = inotify_init();
|
||||
if (ifd == -1) {
|
||||
throwUnixException(env, errno);
|
||||
}
|
||||
return (jint)ifd;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_fs_LinuxWatchService_inotifyAddWatch
|
||||
(JNIEnv* env, jclass clazz, jint fd, jlong address, jint mask)
|
||||
{
|
||||
int wfd = -1;
|
||||
const char* path = (const char*)jlong_to_ptr(address);
|
||||
|
||||
wfd = inotify_add_watch((int)fd, path, mask);
|
||||
if (wfd == -1) {
|
||||
throwUnixException(env, errno);
|
||||
}
|
||||
return (jint)wfd;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_fs_LinuxWatchService_inotifyRmWatch
|
||||
(JNIEnv* env, jclass clazz, jint fd, jint wd)
|
||||
{
|
||||
int err = inotify_rm_watch((int)fd, (int)wd);
|
||||
if (err == -1)
|
||||
throwUnixException(env, errno);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_fs_LinuxWatchService_configureBlocking
|
||||
(JNIEnv* env, jclass clazz, jint fd, jboolean blocking)
|
||||
{
|
||||
int flags = fcntl(fd, F_GETFL);
|
||||
|
||||
if ((blocking == JNI_FALSE) && !(flags & O_NONBLOCK))
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
else if ((blocking == JNI_TRUE) && (flags & O_NONBLOCK))
|
||||
fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_fs_LinuxWatchService_socketpair
|
||||
(JNIEnv* env, jclass clazz, jintArray sv)
|
||||
{
|
||||
int sp[2];
|
||||
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) == -1) {
|
||||
throwUnixException(env, errno);
|
||||
} else {
|
||||
jint res[2];
|
||||
res[0] = (jint)sp[0];
|
||||
res[1] = (jint)sp[1];
|
||||
(*env)->SetIntArrayRegion(env, sv, 0, 2, &res[0]);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_fs_LinuxWatchService_poll
|
||||
(JNIEnv* env, jclass clazz, jint fd1, jint fd2)
|
||||
{
|
||||
struct pollfd ufds[2];
|
||||
int n;
|
||||
|
||||
ufds[0].fd = fd1;
|
||||
ufds[0].events = POLLIN;
|
||||
ufds[1].fd = fd2;
|
||||
ufds[1].events = POLLIN;
|
||||
|
||||
n = poll(&ufds[0], 2, -1);
|
||||
if (n == -1) {
|
||||
if (errno == EINTR) {
|
||||
n = 0;
|
||||
} else {
|
||||
throwUnixException(env, errno);
|
||||
}
|
||||
}
|
||||
return (jint)n;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue