8187443: Forest Consolidation: Move files to unified layout

Reviewed-by: darcy, ihse
This commit is contained in:
Erik Joelsson 2017-09-12 19:03:39 +02:00
parent 270fe13182
commit 3789983e89
56923 changed files with 3 additions and 15727 deletions

View file

@ -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();
}
}

View file

@ -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();
}
}

View 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();
}
}

View 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();
}

View 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();
}
}

View 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;
}
}

View file

@ -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();
}
}

View file

@ -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));
}
}

View file

@ -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();
}
}

View file

@ -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);
}
}
}

View 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);
}
}

View 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);
}
}

View file

@ -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));
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}
}

View 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;
}});
}
}