--- a/jdk/src/share/classes/java/lang/Integer.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/lang/Integer.java Fri Apr 03 10:29:13 2009 -0700
@@ -25,6 +25,8 @@
package java.lang;
+import java.util.Properties;
+
/**
* The {@code Integer} class wraps a value of the primitive type
* {@code int} in an object. An object of type {@code Integer}
@@ -442,6 +444,12 @@
public static int parseInt(String s, int radix)
throws NumberFormatException
{
+ /*
+ * WARNING: This method may be invoked early during VM initialization
+ * before IntegerCache is initialized. Care must be taken to not use
+ * the valueOf method.
+ */
+
if (s == null) {
throw new NumberFormatException("null");
}
@@ -545,7 +553,7 @@
* does not contain a parsable {@code int}.
*/
public static Integer valueOf(String s, int radix) throws NumberFormatException {
- return new Integer(parseInt(s,radix));
+ return Integer.valueOf(parseInt(s,radix));
}
/**
@@ -570,20 +578,56 @@
* @exception NumberFormatException if the string cannot be parsed
* as an integer.
*/
- public static Integer valueOf(String s) throws NumberFormatException
- {
- return new Integer(parseInt(s, 10));
+ public static Integer valueOf(String s) throws NumberFormatException {
+ return Integer.valueOf(parseInt(s, 10));
+ }
+
+ /**
+ * Cache to support the object identity semantics of autoboxing for values between
+ * -128 and 127 (inclusive) as required by JLS.
+ *
+ * The cache is initialized on first usage. During VM initialization the
+ * getAndRemoveCacheProperties method may be used to get and remove any system
+ * properites that configure the cache size. At this time, the size of the
+ * cache may be controlled by the -XX:AutoBoxCacheMax=<size> option.
+ */
+
+ // value of java.lang.Integer.IntegerCache.high property (obtained during VM init)
+ private static String integerCacheHighPropValue;
+
+ static void getAndRemoveCacheProperties() {
+ if (!sun.misc.VM.isBooted()) {
+ Properties props = System.getProperties();
+ integerCacheHighPropValue =
+ (String)props.remove("java.lang.Integer.IntegerCache.high");
+ if (integerCacheHighPropValue != null)
+ System.setProperties(props); // remove from system props
+ }
}
private static class IntegerCache {
- private IntegerCache(){}
-
- static final Integer cache[] = new Integer[-(-128) + 127 + 1];
+ static final int low = -128;
+ static final int high;
+ static final Integer cache[];
static {
- for(int i = 0; i < cache.length; i++)
- cache[i] = new Integer(i - 128);
+ // high value may be configured by property
+ int h = 127;
+ if (integerCacheHighPropValue != null) {
+ int i = parseInt(integerCacheHighPropValue);
+ i = Math.max(i, 127);
+ // Maximum array size is Integer.MAX_VALUE
+ h = Math.min(i, Integer.MAX_VALUE - (-low));
+ }
+ high = h;
+
+ cache = new Integer[(high - low) + 1];
+ int j = low;
+ for(int k = 0; k < cache.length; k++)
+ cache[k] = new Integer(j++);
}
+
+ private IntegerCache() {}
}
/**
@@ -599,10 +643,9 @@
* @since 1.5
*/
public static Integer valueOf(int i) {
- final int offset = 128;
- if (i >= -128 && i <= 127) { // must cache
- return IntegerCache.cache[i + offset];
- }
+ assert IntegerCache.high >= 127;
+ if (i >= IntegerCache.low && i <= IntegerCache.high)
+ return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
@@ -806,7 +849,7 @@
*/
public static Integer getInteger(String nm, int val) {
Integer result = getInteger(nm, null);
- return (result == null) ? new Integer(val) : result;
+ return (result == null) ? Integer.valueOf(val) : result;
}
/**
@@ -938,7 +981,7 @@
try {
result = Integer.valueOf(nm.substring(index), radix);
- result = negative ? new Integer(-result.intValue()) : result;
+ result = negative ? Integer.valueOf(-result.intValue()) : result;
} catch (NumberFormatException e) {
// If number is Integer.MIN_VALUE, we'll end up here. The next line
// handles this case, and causes any genuine format error to be
--- a/jdk/src/share/classes/java/lang/Long.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/lang/Long.java Fri Apr 03 10:29:13 2009 -0700
@@ -510,7 +510,7 @@
* contain a parsable {@code long}.
*/
public static Long valueOf(String s, int radix) throws NumberFormatException {
- return new Long(parseLong(s, radix));
+ return Long.valueOf(parseLong(s, radix));
}
/**
@@ -537,7 +537,7 @@
*/
public static Long valueOf(String s) throws NumberFormatException
{
- return new Long(parseLong(s, 10));
+ return Long.valueOf(parseLong(s, 10));
}
private static class LongCache {
@@ -650,7 +650,7 @@
try {
result = Long.valueOf(nm.substring(index), radix);
- result = negative ? new Long(-result.longValue()) : result;
+ result = negative ? Long.valueOf(-result.longValue()) : result;
} catch (NumberFormatException e) {
// If number is Long.MIN_VALUE, we'll end up here. The next line
// handles this case, and causes any genuine format error to be
@@ -869,7 +869,7 @@
*/
public static Long getLong(String nm, long val) {
Long result = Long.getLong(nm, null);
- return (result == null) ? new Long(val) : result;
+ return (result == null) ? Long.valueOf(val) : result;
}
/**
--- a/jdk/src/share/classes/java/lang/System.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/lang/System.java Fri Apr 03 10:29:13 2009 -0700
@@ -1105,6 +1105,13 @@
props = new Properties();
initProperties(props);
sun.misc.Version.init();
+
+ // Gets and removes system properties that configure the Integer
+ // cache used to support the object identity semantics of autoboxing.
+ // At this time, the size of the cache may be controlled by the
+ // -XX:AutoBoxCacheMax=<size> option.
+ Integer.getAndRemoveCacheProperties();
+
FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
--- a/jdk/src/share/classes/java/nio/channels/DatagramChannel.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/nio/channels/DatagramChannel.java Fri Apr 03 10:29:13 2009 -0700
@@ -261,7 +261,10 @@
*
* <p> This method may be invoked at any time. It will not have any effect
* on read or write operations that are already in progress at the moment
- * that it is invoked. </p>
+ * that it is invoked. If this channel's socket is not bound then this method
+ * will first cause the socket to be bound to an address that is assigned
+ * automatically, as if invoking the {@link #bind bind} method with a
+ * parameter of {@code null}. </p>
*
* @param remote
* The remote address to which this channel is to be connected
@@ -356,7 +359,10 @@
* <p> This method may be invoked at any time. If another thread has
* already initiated a read operation upon this channel, however, then an
* invocation of this method will block until the first operation is
- * complete. </p>
+ * complete. If this channel's socket is not bound then this method will
+ * first cause the socket to be bound to an address that is assigned
+ * automatically, as if invoking the {@link #bind bind} method with a
+ * parameter of {@code null}. </p>
*
* @param dst
* The buffer into which the datagram is to be transferred
@@ -413,7 +419,10 @@
* <p> This method may be invoked at any time. If another thread has
* already initiated a write operation upon this channel, however, then an
* invocation of this method will block until the first operation is
- * complete. </p>
+ * complete. If this channel's socket is not bound then this method will
+ * first cause the socket to be bound to an address that is assigned
+ * automatically, as if by invoking the {@link #bind bind) method with a
+ * parameter of {@code null}. </p>
*
* @param src
* The buffer containing the datagram to be sent
--- a/jdk/src/share/classes/java/util/TreeMap.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/TreeMap.java Fri Apr 03 10:29:13 2009 -0700
@@ -1068,14 +1068,14 @@
}
public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
E toElement, boolean toInclusive) {
- return new TreeSet<E>(m.subMap(fromElement, fromInclusive,
- toElement, toInclusive));
+ return new KeySet<E>(m.subMap(fromElement, fromInclusive,
+ toElement, toInclusive));
}
public NavigableSet<E> headSet(E toElement, boolean inclusive) {
- return new TreeSet<E>(m.headMap(toElement, inclusive));
+ return new KeySet<E>(m.headMap(toElement, inclusive));
}
public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
- return new TreeSet<E>(m.tailMap(fromElement, inclusive));
+ return new KeySet<E>(m.tailMap(fromElement, inclusive));
}
public SortedSet<E> subSet(E fromElement, E toElement) {
return subSet(fromElement, true, toElement, false);
@@ -1087,7 +1087,7 @@
return tailSet(fromElement, true);
}
public NavigableSet<E> descendingSet() {
- return new TreeSet(m.descendingMap());
+ return new KeySet(m.descendingMap());
}
}
--- a/jdk/src/share/classes/java/util/concurrent/ConcurrentSkipListMap.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/ConcurrentSkipListMap.java Fri Apr 03 10:29:13 2009 -0700
@@ -2394,15 +2394,14 @@
boolean fromInclusive,
E toElement,
boolean toInclusive) {
- return new ConcurrentSkipListSet<E>
- (m.subMap(fromElement, fromInclusive,
- toElement, toInclusive));
+ return new KeySet<E>(m.subMap(fromElement, fromInclusive,
+ toElement, toInclusive));
}
public NavigableSet<E> headSet(E toElement, boolean inclusive) {
- return new ConcurrentSkipListSet<E>(m.headMap(toElement, inclusive));
+ return new KeySet<E>(m.headMap(toElement, inclusive));
}
public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
- return new ConcurrentSkipListSet<E>(m.tailMap(fromElement, inclusive));
+ return new KeySet<E>(m.tailMap(fromElement, inclusive));
}
public NavigableSet<E> subSet(E fromElement, E toElement) {
return subSet(fromElement, true, toElement, false);
@@ -2414,7 +2413,7 @@
return tailSet(fromElement, true);
}
public NavigableSet<E> descendingSet() {
- return new ConcurrentSkipListSet(m.descendingMap());
+ return new KeySet(m.descendingMap());
}
}
--- a/jdk/src/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java Fri Apr 03 10:29:13 2009 -0700
@@ -166,6 +166,11 @@
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
+ /**
+ * waitStatus value to indicate the next acquireShared should
+ * unconditionally propagate
+ */
+ static final int PROPAGATE = -3;
/**
* Status field, taking on only the values:
@@ -180,10 +185,16 @@
* Nodes never leave this state. In particular,
* a thread with cancelled node never again blocks.
* CONDITION: This node is currently on a condition queue.
- * It will not be used as a sync queue node until
- * transferred. (Use of this value here
- * has nothing to do with the other uses
- * of the field, but simplifies mechanics.)
+ * It will not be used as a sync queue node
+ * until transferred, at which time the status
+ * will be set to 0. (Use of this value here has
+ * nothing to do with the other uses of the
+ * field, but simplifies mechanics.)
+ * PROPAGATE: A releaseShared should be propagated to other
+ * nodes. This is set (for head node only) in
+ * doReleaseShared to ensure propagation
+ * continues, even if other operations have
+ * since intervened.
* 0: None of the above
*
* The values are arranged numerically to simplify use.
@@ -403,10 +414,13 @@
*/
private void unparkSuccessor(Node node) {
/*
- * Try to clear status in anticipation of signalling. It is
- * OK if this fails or if status is changed by waiting thread.
+ * If status is negative (i.e., possibly needing signal) try
+ * to clear in anticipation of signalling. It is OK if this
+ * fails or if status is changed by waiting thread.
*/
- compareAndSetWaitStatus(node, Node.SIGNAL, 0);
+ int ws = node.waitStatus;
+ if (ws < 0)
+ compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
@@ -426,23 +440,70 @@
}
/**
+ * Release action for shared mode -- signal successor and ensure
+ * propagation. (Note: For exclusive mode, release just amounts
+ * to calling unparkSuccessor of head if it needs signal.)
+ */
+ private void doReleaseShared() {
+ /*
+ * Ensure that a release propagates, even if there are other
+ * in-progress acquires/releases. This proceeds in the usual
+ * way of trying to unparkSuccessor of head if it needs
+ * signal. But if it does not, status is set to PROPAGATE to
+ * ensure that upon release, propagation continues.
+ * Additionally, we must loop in case a new node is added
+ * while we are doing this. Also, unlike other uses of
+ * unparkSuccessor, we need to know if CAS to reset status
+ * fails, if so rechecking.
+ */
+ for (;;) {
+ Node h = head;
+ if (h != null && h != tail) {
+ int ws = h.waitStatus;
+ if (ws == Node.SIGNAL) {
+ if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
+ continue; // loop to recheck cases
+ unparkSuccessor(h);
+ }
+ else if (ws == 0 &&
+ !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
+ continue; // loop on failed CAS
+ }
+ if (h == head) // loop if head changed
+ break;
+ }
+ }
+
+ /**
* Sets head of queue, and checks if successor may be waiting
- * in shared mode, if so propagating if propagate > 0.
+ * in shared mode, if so propagating if either propagate > 0 or
+ * PROPAGATE status was set.
*
- * @param pred the node holding waitStatus for node
* @param node the node
* @param propagate the return value from a tryAcquireShared
*/
private void setHeadAndPropagate(Node node, long propagate) {
+ Node h = head; // Record old head for check below
setHead(node);
- if (propagate > 0 && node.waitStatus != 0) {
- /*
- * Don't bother fully figuring out successor. If it
- * looks null, call unparkSuccessor anyway to be safe.
- */
+ /*
+ * Try to signal next queued node if:
+ * Propagation was indicated by caller,
+ * or was recorded (as h.waitStatus) by a previous operation
+ * (note: this uses sign-check of waitStatus because
+ * PROPAGATE status may transition to SIGNAL.)
+ * and
+ * The next node is waiting in shared mode,
+ * or we don't know, because it appears null
+ *
+ * The conservatism in both of these checks may cause
+ * unnecessary wake-ups, but only when there are multiple
+ * racing acquires/releases, so most need signals now or soon
+ * anyway.
+ */
+ if (propagate > 0 || h == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
- unparkSuccessor(node);
+ doReleaseShared();
}
}
@@ -465,23 +526,27 @@
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
- // Getting this before setting waitStatus ensures staleness
+ // predNext is the apparent node to unsplice. CASes below will
+ // fail if not, in which case, we lost race vs another cancel
+ // or signal, so no further action is necessary.
Node predNext = pred.next;
- // Can use unconditional write instead of CAS here
+ // Can use unconditional write instead of CAS here.
+ // After this atomic step, other Nodes can skip past us.
+ // Before, we are free of interference from other threads.
node.waitStatus = Node.CANCELLED;
- // If we are the tail, remove ourselves
+ // If we are the tail, remove ourselves.
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
} else {
- // If "active" predecessor found...
- if (pred != head
- && (pred.waitStatus == Node.SIGNAL
- || compareAndSetWaitStatus(pred, 0, Node.SIGNAL))
- && pred.thread != null) {
-
- // If successor is active, set predecessor's next link
+ // If successor needs signal, try to set pred's next-link
+ // so it will get one. Otherwise wake it up to propagate.
+ int ws;
+ if (pred != head &&
+ ((ws = pred.waitStatus) == Node.SIGNAL ||
+ (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
+ pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
@@ -503,14 +568,14 @@
* @return {@code true} if thread should block
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
- int s = pred.waitStatus;
- if (s < 0)
+ int ws = pred.waitStatus;
+ if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
- if (s > 0) {
+ if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
@@ -519,14 +584,14 @@
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
- }
- else
+ } else {
/*
- * Indicate that we need a signal, but don't park yet. Caller
- * will need to retry to make sure it cannot acquire before
- * parking.
+ * waitStatus must be 0 or PROPAGATE. Indicate that we
+ * need a signal, but don't park yet. Caller will need to
+ * retry to make sure it cannot acquire before parking.
*/
- compareAndSetWaitStatus(pred, 0, Node.SIGNAL);
+ compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
+ }
return false;
}
@@ -1046,9 +1111,7 @@
*/
public final boolean releaseShared(long arg) {
if (tryReleaseShared(arg)) {
- Node h = head;
- if (h != null && h.waitStatus != 0)
- unparkSuccessor(h);
+ doReleaseShared();
return true;
}
return false;
@@ -1390,8 +1453,8 @@
* case the waitStatus can be transiently and harmlessly wrong).
*/
Node p = enq(node);
- int c = p.waitStatus;
- if (c > 0 || !compareAndSetWaitStatus(p, c, Node.SIGNAL))
+ int ws = p.waitStatus;
+ if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
--- a/jdk/src/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java Fri Apr 03 10:29:13 2009 -0700
@@ -389,6 +389,11 @@
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
+ /**
+ * waitStatus value to indicate the next acquireShared should
+ * unconditionally propagate
+ */
+ static final int PROPAGATE = -3;
/**
* Status field, taking on only the values:
@@ -403,10 +408,16 @@
* Nodes never leave this state. In particular,
* a thread with cancelled node never again blocks.
* CONDITION: This node is currently on a condition queue.
- * It will not be used as a sync queue node until
- * transferred. (Use of this value here
- * has nothing to do with the other uses
- * of the field, but simplifies mechanics.)
+ * It will not be used as a sync queue node
+ * until transferred, at which time the status
+ * will be set to 0. (Use of this value here has
+ * nothing to do with the other uses of the
+ * field, but simplifies mechanics.)
+ * PROPAGATE: A releaseShared should be propagated to other
+ * nodes. This is set (for head node only) in
+ * doReleaseShared to ensure propagation
+ * continues, even if other operations have
+ * since intervened.
* 0: None of the above
*
* The values are arranged numerically to simplify use.
@@ -626,10 +637,13 @@
*/
private void unparkSuccessor(Node node) {
/*
- * Try to clear status in anticipation of signalling. It is
- * OK if this fails or if status is changed by waiting thread.
+ * If status is negative (i.e., possibly needing signal) try
+ * to clear in anticipation of signalling. It is OK if this
+ * fails or if status is changed by waiting thread.
*/
- compareAndSetWaitStatus(node, Node.SIGNAL, 0);
+ int ws = node.waitStatus;
+ if (ws < 0)
+ compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
@@ -649,23 +663,70 @@
}
/**
+ * Release action for shared mode -- signal successor and ensure
+ * propagation. (Note: For exclusive mode, release just amounts
+ * to calling unparkSuccessor of head if it needs signal.)
+ */
+ private void doReleaseShared() {
+ /*
+ * Ensure that a release propagates, even if there are other
+ * in-progress acquires/releases. This proceeds in the usual
+ * way of trying to unparkSuccessor of head if it needs
+ * signal. But if it does not, status is set to PROPAGATE to
+ * ensure that upon release, propagation continues.
+ * Additionally, we must loop in case a new node is added
+ * while we are doing this. Also, unlike other uses of
+ * unparkSuccessor, we need to know if CAS to reset status
+ * fails, if so rechecking.
+ */
+ for (;;) {
+ Node h = head;
+ if (h != null && h != tail) {
+ int ws = h.waitStatus;
+ if (ws == Node.SIGNAL) {
+ if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
+ continue; // loop to recheck cases
+ unparkSuccessor(h);
+ }
+ else if (ws == 0 &&
+ !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
+ continue; // loop on failed CAS
+ }
+ if (h == head) // loop if head changed
+ break;
+ }
+ }
+
+ /**
* Sets head of queue, and checks if successor may be waiting
- * in shared mode, if so propagating if propagate > 0.
+ * in shared mode, if so propagating if either propagate > 0 or
+ * PROPAGATE status was set.
*
- * @param pred the node holding waitStatus for node
* @param node the node
* @param propagate the return value from a tryAcquireShared
*/
private void setHeadAndPropagate(Node node, int propagate) {
+ Node h = head; // Record old head for check below
setHead(node);
- if (propagate > 0 && node.waitStatus != 0) {
- /*
- * Don't bother fully figuring out successor. If it
- * looks null, call unparkSuccessor anyway to be safe.
- */
+ /*
+ * Try to signal next queued node if:
+ * Propagation was indicated by caller,
+ * or was recorded (as h.waitStatus) by a previous operation
+ * (note: this uses sign-check of waitStatus because
+ * PROPAGATE status may transition to SIGNAL.)
+ * and
+ * The next node is waiting in shared mode,
+ * or we don't know, because it appears null
+ *
+ * The conservatism in both of these checks may cause
+ * unnecessary wake-ups, but only when there are multiple
+ * racing acquires/releases, so most need signals now or soon
+ * anyway.
+ */
+ if (propagate > 0 || h == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
- unparkSuccessor(node);
+ doReleaseShared();
}
}
@@ -688,23 +749,27 @@
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
- // Getting this before setting waitStatus ensures staleness
+ // predNext is the apparent node to unsplice. CASes below will
+ // fail if not, in which case, we lost race vs another cancel
+ // or signal, so no further action is necessary.
Node predNext = pred.next;
- // Can use unconditional write instead of CAS here
+ // Can use unconditional write instead of CAS here.
+ // After this atomic step, other Nodes can skip past us.
+ // Before, we are free of interference from other threads.
node.waitStatus = Node.CANCELLED;
- // If we are the tail, remove ourselves
+ // If we are the tail, remove ourselves.
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
} else {
- // If "active" predecessor found...
- if (pred != head
- && (pred.waitStatus == Node.SIGNAL
- || compareAndSetWaitStatus(pred, 0, Node.SIGNAL))
- && pred.thread != null) {
-
- // If successor is active, set predecessor's next link
+ // If successor needs signal, try to set pred's next-link
+ // so it will get one. Otherwise wake it up to propagate.
+ int ws;
+ if (pred != head &&
+ ((ws = pred.waitStatus) == Node.SIGNAL ||
+ (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
+ pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
@@ -726,14 +791,14 @@
* @return {@code true} if thread should block
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
- int s = pred.waitStatus;
- if (s < 0)
+ int ws = pred.waitStatus;
+ if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
- if (s > 0) {
+ if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
@@ -742,14 +807,14 @@
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
- }
- else
+ } else {
/*
- * Indicate that we need a signal, but don't park yet. Caller
- * will need to retry to make sure it cannot acquire before
- * parking.
+ * waitStatus must be 0 or PROPAGATE. Indicate that we
+ * need a signal, but don't park yet. Caller will need to
+ * retry to make sure it cannot acquire before parking.
*/
- compareAndSetWaitStatus(pred, 0, Node.SIGNAL);
+ compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
+ }
return false;
}
@@ -1269,9 +1334,7 @@
*/
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
- Node h = head;
- if (h != null && h.waitStatus != 0)
- unparkSuccessor(h);
+ doReleaseShared();
return true;
}
return false;
@@ -1613,8 +1676,8 @@
* case the waitStatus can be transiently and harmlessly wrong).
*/
Node p = enq(node);
- int c = p.waitStatus;
- if (c > 0 || !compareAndSetWaitStatus(p, c, Node.SIGNAL))
+ int ws = p.waitStatus;
+ if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
--- a/jdk/src/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java Fri Apr 03 10:29:13 2009 -0700
@@ -276,7 +276,7 @@
* Maintained as a ThreadLocal; cached in cachedHoldCounter
*/
static final class HoldCounter {
- int count;
+ int count = 0;
// Use id, not reference, to avoid garbage retention
final long tid = Thread.currentThread().getId();
}
@@ -293,8 +293,9 @@
}
/**
- * The number of read locks held by current thread.
+ * The number of reentrant read locks held by current thread.
* Initialized only in constructor and readObject.
+ * Removed whenever a thread's read hold count drops to 0.
*/
private transient ThreadLocalHoldCounter readHolds;
@@ -304,17 +305,35 @@
* where the next thread to release is the last one to
* acquire. This is non-volatile since it is just used
* as a heuristic, and would be great for threads to cache.
+ *
+ * <p>Can outlive the Thread for which it is caching the read
+ * hold count, but avoids garbage retention by not retaining a
+ * reference to the Thread.
+ *
+ * <p>Accessed via a benign data race; relies on the memory
+ * model's final field and out-of-thin-air guarantees.
*/
private transient HoldCounter cachedHoldCounter;
/**
* firstReader is the first thread to have acquired the read lock.
* firstReaderHoldCount is firstReader's hold count.
- * This allows tracking of read holds for uncontended read
+ *
+ * <p>More precisely, firstReader is the unique thread that last
+ * changed the shared count from 0 to 1, and has not released the
+ * read lock since then; null if there is no such thread.
+ *
+ * <p>Cannot cause garbage retention unless the thread terminated
+ * without relinquishing its read locks, since tryReleaseShared
+ * sets it to null.
+ *
+ * <p>Accessed via a benign data race; relies on the memory
+ * model's out-of-thin-air guarantees for references.
+ *
+ * <p>This allows tracking of read holds for uncontended read
* locks to be very cheap.
*/
- private final static long INVALID_THREAD_ID = -1;
- private transient long firstReader = INVALID_THREAD_ID;
+ private transient Thread firstReader = null;
private transient int firstReaderHoldCount;
Sync() {
@@ -393,16 +412,16 @@
}
protected final boolean tryReleaseShared(int unused) {
- long tid = Thread.currentThread().getId();
- if (firstReader == tid) {
+ Thread current = Thread.currentThread();
+ if (firstReader == current) {
// assert firstReaderHoldCount > 0;
if (firstReaderHoldCount == 1)
- firstReader = INVALID_THREAD_ID;
+ firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
- if (rh == null || rh.tid != tid)
+ if (rh == null || rh.tid != current.getId())
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
@@ -416,6 +435,9 @@
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
+ // Releasing the read lock has no effect on readers,
+ // but it may allow waiting writers to proceed if
+ // both read and write locks are now free.
return nextc == 0;
}
}
@@ -450,15 +472,14 @@
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
- long tid = current.getId();
if (r == 0) {
- firstReader = tid;
+ firstReader = current;
firstReaderHoldCount = 1;
- } else if (firstReader == tid) {
+ } else if (firstReader == current) {
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
- if (rh == null || rh.tid != tid)
+ if (rh == null || rh.tid != current.getId())
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
@@ -485,19 +506,17 @@
int c = getState();
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
- //if (removeNeeded) readHolds.remove();
return -1;
// else we hold the exclusive lock; blocking here
// would cause deadlock.
} else if (readerShouldBlock()) {
// Make sure we're not acquiring read lock reentrantly
- long tid = current.getId();
- if (firstReader == tid) {
+ if (firstReader == current) {
// assert firstReaderHoldCount > 0;
} else {
if (rh == null) {
rh = cachedHoldCounter;
- if (rh == null || rh.tid != tid) {
+ if (rh == null || rh.tid != current.getId()) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
@@ -510,25 +529,20 @@
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) {
- long tid = current.getId();
if (sharedCount(c) == 0) {
- firstReader = tid;
+ firstReader = current;
firstReaderHoldCount = 1;
- } else if (firstReader == tid) {
+ } else if (firstReader == current) {
firstReaderHoldCount++;
} else {
- if (rh == null) {
+ if (rh == null)
rh = cachedHoldCounter;
- if (rh != null && rh.tid == tid) {
- if (rh.count == 0)
- readHolds.set(rh);
- } else {
- rh = readHolds.get();
- }
- } else if (rh.count == 0)
+ if (rh == null || rh.tid != current.getId())
+ rh = readHolds.get();
+ else if (rh.count == 0)
readHolds.set(rh);
+ rh.count++;
cachedHoldCounter = rh; // cache for release
- rh.count++;
}
return 1;
}
@@ -572,15 +586,14 @@
if (r == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) {
- long tid = current.getId();
if (r == 0) {
- firstReader = tid;
+ firstReader = current;
firstReaderHoldCount = 1;
- } else if (firstReader == tid) {
+ } else if (firstReader == current) {
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
- if (rh == null || rh.tid != tid)
+ if (rh == null || rh.tid != current.getId())
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
@@ -626,12 +639,12 @@
if (getReadLockCount() == 0)
return 0;
- long tid = Thread.currentThread().getId();
- if (firstReader == tid)
+ Thread current = Thread.currentThread();
+ if (firstReader == current)
return firstReaderHoldCount;
HoldCounter rh = cachedHoldCounter;
- if (rh != null && rh.tid == tid)
+ if (rh != null && rh.tid == current.getId())
return rh.count;
int count = readHolds.get().count;
@@ -647,7 +660,6 @@
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
readHolds = new ThreadLocalHoldCounter();
- firstReader = INVALID_THREAD_ID;
setState(0); // reset to unlocked state
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/util/zip/ZipConstants64.java Fri Apr 03 10:29:13 2009 -0700
@@ -0,0 +1,77 @@
+/*
+ * Copyright 1995-1996 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.util.zip;
+
+/*
+ * This class defines the constants that are used by the classes
+ * which manipulate Zip64 files.
+ */
+
+class ZipConstants64 {
+
+ /*
+ * ZIP64 constants
+ */
+ static final long ZIP64_ENDSIG = 0x06064b50L; // "PK\006\006"
+ static final long ZIP64_LOCSIG = 0x07064b50L; // "PK\006\007"
+ static final int ZIP64_ENDHDR = 56; // ZIP64 end header size
+ static final int ZIP64_LOCHDR = 20; // ZIP64 end loc header size
+ static final int ZIP64_EXTHDR = 24; // EXT header size
+ static final int ZIP64_EXTID = 0x0001; // Extra field Zip64 header ID
+
+ static final int ZIP64_MAGICCOUNT = 0xFFFF;
+ static final long ZIP64_MAGICVAL = 0xFFFFFFFFL;
+
+ /*
+ * Zip64 End of central directory (END) header field offsets
+ */
+ static final int ZIP64_ENDLEN = 4; // size of zip64 end of central dir
+ static final int ZIP64_ENDVEM = 12; // version made by
+ static final int ZIP64_ENDVER = 14; // version needed to extract
+ static final int ZIP64_ENDNMD = 16; // number of this disk
+ static final int ZIP64_ENDDSK = 20; // disk number of start
+ static final int ZIP64_ENDTOD = 24; // total number of entries on this disk
+ static final int ZIP64_ENDTOT = 32; // total number of entries
+ static final int ZIP64_ENDSIZ = 40; // central directory size in bytes
+ static final int ZIP64_ENDOFF = 48; // offset of first CEN header
+ static final int ZIP64_ENDEXT = 56; // zip64 extensible data sector
+
+ /*
+ * Zip64 End of central directory locator field offsets
+ */
+ static final int ZIP64_LOCDSK = 4; // disk number start
+ static final int ZIP64_LOCOFF = 8; // offset of zip64 end
+ static final int ZIP64_LOCTOT = 16; // total number of disks
+
+ /*
+ * Zip64 Extra local (EXT) header field offsets
+ */
+ static final int ZIP64_EXTCRC = 4; // uncompressed file crc-32 value
+ static final int ZIP64_EXTSIZ = 8; // compressed size, 8-byte
+ static final int ZIP64_EXTLEN = 16; // uncompressed size, 8-byte
+
+ private ZipConstants64() {}
+}
--- a/jdk/src/share/classes/java/util/zip/ZipEntry.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/zip/ZipEntry.java Fri Apr 03 10:29:13 2009 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 1995-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1995-2009 Sun Microsystems, Inc. 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
@@ -144,11 +144,13 @@
* Sets the uncompressed size of the entry data.
* @param size the uncompressed size in bytes
* @exception IllegalArgumentException if the specified size is less
- * than 0 or greater than 0xFFFFFFFF bytes
+ * than 0, is greater than 0xFFFFFFFF when
+ * <a href="package-summary.html#zip64">ZIP64 format</a> is not supported,
+ * or is less than 0 when ZIP64 is supported
* @see #getSize()
*/
public void setSize(long size) {
- if (size < 0 || size > 0xFFFFFFFFL) {
+ if (size < 0) {
throw new IllegalArgumentException("invalid entry size");
}
this.size = size;
--- a/jdk/src/share/classes/java/util/zip/ZipInputStream.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/zip/ZipInputStream.java Fri Apr 03 10:29:13 2009 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2009 Sun Microsystems, Inc. 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
@@ -29,6 +29,7 @@
import java.io.IOException;
import java.io.EOFException;
import java.io.PushbackInputStream;
+import static java.util.zip.ZipConstants64.*;
/**
* This class implements an input stream filter for reading files in the
@@ -285,6 +286,29 @@
byte[] bb = new byte[len];
readFully(bb, 0, len);
e.setExtra(bb);
+ // extra fields are in "HeaderID(2)DataSize(2)Data... format
+ if (e.csize == ZIP64_MAGICVAL || e.size == ZIP64_MAGICVAL) {
+ int off = 0;
+ while (off + 4 < len) {
+ int sz = get16(bb, off + 2);
+ if (get16(bb, off) == ZIP64_EXTID) {
+ off += 4;
+ // LOC extra zip64 entry MUST include BOTH original and
+ // compressed file size fields
+ if (sz < 16 || (off + sz) > len ) {
+ // Invalid zip64 extra fields, simply skip. Even it's
+ // rare, it's possible the entry size happens to be
+ // the magic value and it "accidnetly" has some bytes
+ // in extra match the id.
+ return e;
+ }
+ e.size = get64(bb, off);
+ e.csize = get64(bb, off + 8);
+ break;
+ }
+ off += (sz + 4);
+ }
+ }
}
return e;
}
@@ -375,18 +399,36 @@
}
if ((flag & 8) == 8) {
/* "Data Descriptor" present */
- readFully(tmpbuf, 0, EXTHDR);
- long sig = get32(tmpbuf, 0);
- if (sig != EXTSIG) { // no EXTSIG present
- e.crc = sig;
- e.csize = get32(tmpbuf, EXTSIZ - EXTCRC);
- e.size = get32(tmpbuf, EXTLEN - EXTCRC);
- ((PushbackInputStream)in).unread(
- tmpbuf, EXTHDR - EXTCRC - 1, EXTCRC);
+ if (inf.getBytesWritten() > ZIP64_MAGICVAL ||
+ inf.getBytesRead() > ZIP64_MAGICVAL) {
+ // ZIP64 format
+ readFully(tmpbuf, 0, ZIP64_EXTHDR);
+ long sig = get32(tmpbuf, 0);
+ if (sig != EXTSIG) { // no EXTSIG present
+ e.crc = sig;
+ e.csize = get64(tmpbuf, ZIP64_EXTSIZ - ZIP64_EXTCRC);
+ e.size = get64(tmpbuf, ZIP64_EXTLEN - ZIP64_EXTCRC);
+ ((PushbackInputStream)in).unread(
+ tmpbuf, ZIP64_EXTHDR - ZIP64_EXTCRC - 1, ZIP64_EXTCRC);
+ } else {
+ e.crc = get32(tmpbuf, ZIP64_EXTCRC);
+ e.csize = get64(tmpbuf, ZIP64_EXTSIZ);
+ e.size = get64(tmpbuf, ZIP64_EXTLEN);
+ }
} else {
- e.crc = get32(tmpbuf, EXTCRC);
- e.csize = get32(tmpbuf, EXTSIZ);
- e.size = get32(tmpbuf, EXTLEN);
+ readFully(tmpbuf, 0, EXTHDR);
+ long sig = get32(tmpbuf, 0);
+ if (sig != EXTSIG) { // no EXTSIG present
+ e.crc = sig;
+ e.csize = get32(tmpbuf, EXTSIZ - EXTCRC);
+ e.size = get32(tmpbuf, EXTLEN - EXTCRC);
+ ((PushbackInputStream)in).unread(
+ tmpbuf, EXTHDR - EXTCRC - 1, EXTCRC);
+ } else {
+ e.crc = get32(tmpbuf, EXTCRC);
+ e.csize = get32(tmpbuf, EXTSIZ);
+ e.size = get32(tmpbuf, EXTLEN);
+ }
}
}
if (e.size != inf.getBytesWritten()) {
@@ -433,6 +475,14 @@
* The bytes are assumed to be in Intel (little-endian) byte order.
*/
private static final long get32(byte b[], int off) {
- return get16(b, off) | ((long)get16(b, off+2) << 16);
+ return (get16(b, off) | ((long)get16(b, off+2) << 16)) & 0xffffffffL;
+ }
+
+ /*
+ * Fetches signed 64-bit value from byte array at specified offset.
+ * The bytes are assumed to be in Intel (little-endian) byte order.
+ */
+ private static final long get64(byte b[], int off) {
+ return get32(b, off) | (get32(b, off+4) << 32);
}
}
--- a/jdk/src/share/classes/java/util/zip/ZipOutputStream.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/zip/ZipOutputStream.java Fri Apr 03 10:29:13 2009 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2009 Sun Microsystems, Inc. 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
@@ -29,6 +29,7 @@
import java.io.IOException;
import java.util.Vector;
import java.util.HashSet;
+import static java.util.zip.ZipConstants64.*;
/**
* This class implements an output stream filter for writing files in the
@@ -343,26 +344,52 @@
private void writeLOC(XEntry xentry) throws IOException {
ZipEntry e = xentry.entry;
int flag = xentry.flag;
+ int elen = (e.extra != null) ? e.extra.length : 0;
+ boolean hasZip64 = false;
+
writeInt(LOCSIG); // LOC header signature
- writeShort(version(e)); // version needed to extract
- writeShort(flag); // general purpose bit flag
- writeShort(e.method); // compression method
- writeInt(e.time); // last modification time
+
if ((flag & 8) == 8) {
+ writeShort(version(e)); // version needed to extract
+ writeShort(flag); // general purpose bit flag
+ writeShort(e.method); // compression method
+ writeInt(e.time); // last modification time
+
// store size, uncompressed size, and crc-32 in data descriptor
// immediately following compressed entry data
writeInt(0);
writeInt(0);
writeInt(0);
} else {
- writeInt(e.crc); // crc-32
- writeInt(e.csize); // compressed size
- writeInt(e.size); // uncompressed size
+ if (e.csize >= ZIP64_MAGICVAL || e.size >= ZIP64_MAGICVAL) {
+ hasZip64 = true;
+ writeShort(45); // ver 4.5 for zip64
+ } else {
+ writeShort(version(e)); // version needed to extract
+ }
+ writeShort(flag); // general purpose bit flag
+ writeShort(e.method); // compression method
+ writeInt(e.time); // last modification time
+ writeInt(e.crc); // crc-32
+ if (hasZip64) {
+ writeInt(ZIP64_MAGICVAL);
+ writeInt(ZIP64_MAGICVAL);
+ elen += 20; //headid(2) + size(2) + size(8) + csize(8)
+ } else {
+ writeInt(e.csize); // compressed size
+ writeInt(e.size); // uncompressed size
+ }
}
byte[] nameBytes = getUTF8Bytes(e.name);
writeShort(nameBytes.length);
- writeShort(e.extra != null ? e.extra.length : 0);
+ writeShort(elen);
writeBytes(nameBytes, 0, nameBytes.length);
+ if (hasZip64) {
+ writeShort(ZIP64_EXTID);
+ writeShort(16);
+ writeLong(e.size);
+ writeLong(e.csize);
+ }
if (e.extra != null) {
writeBytes(e.extra, 0, e.extra.length);
}
@@ -375,8 +402,13 @@
private void writeEXT(ZipEntry e) throws IOException {
writeInt(EXTSIG); // EXT header signature
writeInt(e.crc); // crc-32
- writeInt(e.csize); // compressed size
- writeInt(e.size); // uncompressed size
+ if (e.csize >= ZIP64_MAGICVAL || e.size >= ZIP64_MAGICVAL) {
+ writeLong(e.csize);
+ writeLong(e.size);
+ } else {
+ writeInt(e.csize); // compressed size
+ writeInt(e.size); // uncompressed size
+ }
}
/*
@@ -387,18 +419,49 @@
ZipEntry e = xentry.entry;
int flag = xentry.flag;
int version = version(e);
+
+ long csize = e.csize;
+ long size = e.size;
+ long offset = xentry.offset;
+ int e64len = 0;
+ boolean hasZip64 = false;
+ if (e.csize >= ZIP64_MAGICVAL) {
+ csize = ZIP64_MAGICVAL;
+ e64len += 8; // csize(8)
+ hasZip64 = true;
+ }
+ if (e.size >= ZIP64_MAGICVAL) {
+ size = ZIP64_MAGICVAL; // size(8)
+ e64len += 8;
+ hasZip64 = true;
+ }
+ if (xentry.offset >= ZIP64_MAGICVAL) {
+ offset = ZIP64_MAGICVAL;
+ e64len += 8; // offset(8)
+ hasZip64 = true;
+ }
writeInt(CENSIG); // CEN header signature
- writeShort(version); // version made by
- writeShort(version); // version needed to extract
+ if (hasZip64) {
+ writeShort(45); // ver 4.5 for zip64
+ writeShort(45);
+ } else {
+ writeShort(version); // version made by
+ writeShort(version); // version needed to extract
+ }
writeShort(flag); // general purpose bit flag
writeShort(e.method); // compression method
writeInt(e.time); // last modification time
writeInt(e.crc); // crc-32
- writeInt(e.csize); // compressed size
- writeInt(e.size); // uncompressed size
+ writeInt(csize); // compressed size
+ writeInt(size); // uncompressed size
byte[] nameBytes = getUTF8Bytes(e.name);
writeShort(nameBytes.length);
- writeShort(e.extra != null ? e.extra.length : 0);
+ if (hasZip64) {
+ // + headid(2) + datasize(2)
+ writeShort(e64len + 4 + (e.extra != null ? e.extra.length : 0));
+ } else {
+ writeShort(e.extra != null ? e.extra.length : 0);
+ }
byte[] commentBytes;
if (e.comment != null) {
commentBytes = getUTF8Bytes(e.comment);
@@ -410,8 +473,18 @@
writeShort(0); // starting disk number
writeShort(0); // internal file attributes (unused)
writeInt(0); // external file attributes (unused)
- writeInt(xentry.offset); // relative offset of local header
+ writeInt(offset); // relative offset of local header
writeBytes(nameBytes, 0, nameBytes.length);
+ if (hasZip64) {
+ writeShort(ZIP64_EXTID);// Zip64 extra
+ writeShort(e64len);
+ if (size == ZIP64_MAGICVAL)
+ writeLong(e.size);
+ if (csize == ZIP64_MAGICVAL)
+ writeLong(e.csize);
+ if (offset == ZIP64_MAGICVAL)
+ writeLong(xentry.offset);
+ }
if (e.extra != null) {
writeBytes(e.extra, 0, e.extra.length);
}
@@ -424,15 +497,50 @@
* Writes end of central directory (END) header.
*/
private void writeEND(long off, long len) throws IOException {
+ boolean hasZip64 = false;
+ long xlen = len;
+ long xoff = off;
+ if (xlen >= ZIP64_MAGICVAL) {
+ xlen = ZIP64_MAGICVAL;
+ hasZip64 = true;
+ }
+ if (xoff >= ZIP64_MAGICVAL) {
+ xoff = ZIP64_MAGICVAL;
+ hasZip64 = true;
+ }
int count = xentries.size();
- writeInt(ENDSIG); // END record signature
- writeShort(0); // number of this disk
- writeShort(0); // central directory start disk
- writeShort(count); // number of directory entries on disk
- writeShort(count); // total number of directory entries
- writeInt(len); // length of central directory
- writeInt(off); // offset of central directory
- if (comment != null) { // zip file comment
+ if (count >= ZIP64_MAGICCOUNT) {
+ count = ZIP64_MAGICCOUNT;
+ hasZip64 = true;
+ }
+ if (hasZip64) {
+ long off64 = written;
+ //zip64 end of central directory record
+ writeInt(ZIP64_ENDSIG); // zip64 END record signature
+ writeLong(ZIP64_ENDHDR - 12); // size of zip64 end
+ writeShort(45); // version made by
+ writeShort(45); // version needed to extract
+ writeInt(0); // number of this disk
+ writeInt(0); // central directory start disk
+ writeLong(xentries.size()); // number of directory entires on disk
+ writeLong(xentries.size()); // number of directory entires
+ writeLong(len); // length of central directory
+ writeLong(off); // offset of central directory
+
+ //zip64 end of central directory locator
+ writeInt(ZIP64_LOCSIG); // zip64 END locator signature
+ writeInt(0); // zip64 END start disk
+ writeLong(off64); // offset of zip64 END
+ writeInt(1); // total number of disks (?)
+ }
+ writeInt(ENDSIG); // END record signature
+ writeShort(0); // number of this disk
+ writeShort(0); // central directory start disk
+ writeShort(count); // number of directory entries on disk
+ writeShort(count); // total number of directory entries
+ writeInt(xlen); // length of central directory
+ writeInt(xoff); // offset of central directory
+ if (comment != null) { // zip file comment
byte[] b = getUTF8Bytes(comment);
writeShort(b.length);
writeBytes(b, 0, b.length);
@@ -464,6 +572,22 @@
}
/*
+ * Writes a 64-bit int to the output stream in little-endian byte order.
+ */
+ private void writeLong(long v) throws IOException {
+ OutputStream out = this.out;
+ out.write((int)((v >>> 0) & 0xff));
+ out.write((int)((v >>> 8) & 0xff));
+ out.write((int)((v >>> 16) & 0xff));
+ out.write((int)((v >>> 24) & 0xff));
+ out.write((int)((v >>> 32) & 0xff));
+ out.write((int)((v >>> 40) & 0xff));
+ out.write((int)((v >>> 48) & 0xff));
+ out.write((int)((v >>> 56) & 0xff));
+ written += 8;
+ }
+
+ /*
* Writes an array of bytes to the output stream.
*/
private void writeBytes(byte[] b, int off, int len) throws IOException {
--- a/jdk/src/share/classes/java/util/zip/package.html Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/zip/package.html Fri Apr 03 10:29:13 2009 -0700
@@ -46,6 +46,13 @@
</a> - a detailed description of the Info-ZIP format upon which
the <code>java.util.zip</code> classes are based.
<p>
+ <a name="zip64">
+ <li>An implementation may optionally support the ZIP64(tm) format extensions
+ defined by the
+ <a href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">
+ PKWARE ZIP File Format Specification</a>. The ZIP64(tm) format extensions
+ are used to overcome the size limitations of the original ZIP format.
+<p>
<li><a href="http://www.isi.edu/in-notes/rfc1950.txt">
ZLIB Compressed Data Format Specification version 3.3</a>
@@ -70,7 +77,6 @@
<li>CRC-32 checksum is described in RFC 1952 (above)
<p>
<li>Adler-32 checksum is described in RFC 1950 (above)
-
</ul>
--- a/jdk/src/share/classes/sun/net/spi/DefaultProxySelector.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/sun/net/spi/DefaultProxySelector.java Fri Apr 03 10:29:13 2009 -0700
@@ -78,7 +78,6 @@
};
private static boolean hasSystemProxies = false;
- private static Properties defprops = new Properties();
static {
final String key = "java.net.useSystemProxies";
@@ -107,6 +106,9 @@
RegexpPool hostsPool;
String property;
+ static NonProxyInfo ftpNonProxyInfo = new NonProxyInfo("ftp.nonProxyHosts", null, null);
+ static NonProxyInfo httpNonProxyInfo = new NonProxyInfo("http.nonProxyHosts", null, null);
+
NonProxyInfo(String p, String s, RegexpPool pool) {
property = p;
hostsSource = s;
@@ -114,8 +116,6 @@
}
}
- private static NonProxyInfo ftpNonProxyInfo = new NonProxyInfo("ftp.nonProxyHosts", null, null);
- private static NonProxyInfo httpNonProxyInfo = new NonProxyInfo("http.nonProxyHosts", null, null);
/**
* select() method. Where all the hard work is done.
@@ -175,13 +175,13 @@
NonProxyInfo pinfo = null;
if ("http".equalsIgnoreCase(protocol)) {
- pinfo = httpNonProxyInfo;
+ pinfo = NonProxyInfo.httpNonProxyInfo;
} else if ("https".equalsIgnoreCase(protocol)) {
// HTTPS uses the same property as HTTP, for backward
// compatibility
- pinfo = httpNonProxyInfo;
+ pinfo = NonProxyInfo.httpNonProxyInfo;
} else if ("ftp".equalsIgnoreCase(protocol)) {
- pinfo = ftpNonProxyInfo;
+ pinfo = NonProxyInfo.ftpNonProxyInfo;
}
/**
@@ -334,7 +334,6 @@
}
}
- private static final Pattern p6 = Pattern.compile("::1|(0:){7}1|(0:){1,6}:1");
private boolean isLoopback(String host) {
if (host == null || host.length() == 0)
return false;
@@ -364,6 +363,7 @@
}
if (host.endsWith(":1")) {
+ final Pattern p6 = Pattern.compile("::1|(0:){7}1|(0:){1,6}:1");
return p6.matcher(host).matches();
}
return false;
--- a/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java Fri Apr 03 10:29:13 2009 -0700
@@ -313,11 +313,9 @@
throw new NullPointerException();
synchronized (readLock) {
ensureOpen();
- // If socket is not bound then behave as if nothing received
- // Will be fixed by 6621699
- if (localAddress() == null) {
- return null;
- }
+ // Socket was not bound before attempting receive
+ if (localAddress() == null)
+ bind(null);
int n = 0;
ByteBuffer bb = null;
try {
--- a/jdk/src/share/classes/sun/nio/ch/SelChImpl.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/sun/nio/ch/SelChImpl.java Fri Apr 03 10:29:13 2009 -0700
@@ -25,6 +25,7 @@
package sun.nio.ch;
+import java.nio.channels.Channel;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -35,7 +36,7 @@
* @since 1.4
*/
-interface SelChImpl {
+interface SelChImpl extends Channel {
FileDescriptor getFD();
--- a/jdk/src/share/classes/sun/security/tools/JarSigner.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/sun/security/tools/JarSigner.java Fri Apr 03 10:29:13 2009 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. 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
@@ -32,28 +32,44 @@
import java.math.BigInteger;
import java.net.URI;
import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.net.SocketTimeoutException;
import java.text.Collator;
import java.text.MessageFormat;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateException;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
import java.security.*;
import java.lang.reflect.Constructor;
import com.sun.jarsigner.ContentSigner;
import com.sun.jarsigner.ContentSignerParameters;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.PKIXParameters;
+import java.security.cert.TrustAnchor;
+import java.util.Map.Entry;
import sun.security.x509.*;
import sun.security.util.*;
import sun.misc.BASE64Encoder;
+
/**
* <p>The jarsigner utility.
*
+ * The exit codes for the main method are:
+ *
+ * 0: success
+ * 1: any error that the jar cannot be signed or verified, including:
+ * keystore loading error
+ * TSP communciation error
+ * jarsigner command line error...
+ * otherwise: error codes from -strict
+ *
* @author Roland Schemers
* @author Jan Luehe
*/
@@ -84,8 +100,6 @@
// Attention:
// This is the entry that get launched by the security tool jarsigner.
- // It's marked as exported private per AppServer Team's request.
- // See http://ccc.sfbay/6428446
public static void main(String args[]) throws Exception {
JarSigner js = new JarSigner();
js.run(args);
@@ -93,31 +107,32 @@
static final String VERSION = "1.0";
- static final int IN_KEYSTORE = 0x01;
+ static final int IN_KEYSTORE = 0x01; // signer is in keystore
static final int IN_SCOPE = 0x02;
-
- // signer's certificate chain (when composing)
- X509Certificate[] certChain;
+ static final int NOT_ALIAS = 0x04; // alias list is NOT empty and
+ // signer is not in alias list
+ static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list
- /*
- * private key
- */
- PrivateKey privateKey;
- KeyStore store;
+ X509Certificate[] certChain; // signer's cert chain (when composing)
+ PrivateKey privateKey; // private key
+ KeyStore store; // the keystore specified by -keystore
+ // or the default keystore, never null
IdentityScope scope;
String keystore; // key store file
boolean nullStream = false; // null keystore input stream (NONE)
boolean token = false; // token-based keystore
- String jarfile; // jar file to sign
+ String jarfile; // jar file to sign or verify
String alias; // alias to sign jar with
+ List<String> ckaliases = new ArrayList<String>(); // aliases in -verify
char[] storepass; // keystore password
boolean protectedPath; // protected authentication path
String storetype; // keystore type
String providerName; // provider name
Vector<String> providers = null; // list of providers
- HashMap<String,String> providerArgs = new HashMap<String, String>(); // arguments for provider constructors
+ // arguments for provider constructors
+ HashMap<String,String> providerArgs = new HashMap<String, String>();
char[] keypass; // private key password
String sigfile; // name of .SF file
String sigalg; // name of signature algorithm
@@ -125,12 +140,14 @@
String signedjar; // output filename
String tsaUrl; // location of the Timestamping Authority
String tsaAlias; // alias for the Timestamping Authority's certificate
+ String altCertChain; // file to read alternative cert chain from
boolean verify = false; // verify the jar
- boolean verbose = false; // verbose output when signing/verifying
+ String verbose = null; // verbose output when signing/verifying
boolean showcerts = false; // show certs when verifying
boolean debug = false; // debug
boolean signManifest = true; // "sign" the whole manifest
boolean externalSF = true; // leave the .SF out of the PKCS7 block
+ boolean strict = false; // treat warnings as error
// read zip entry raw bytes
private ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
@@ -139,14 +156,22 @@
private String altSignerClass = null;
private String altSignerClasspath = null;
private ZipFile zipFile = null;
+
private boolean hasExpiredCert = false;
private boolean hasExpiringCert = false;
private boolean notYetValidCert = false;
-
+ private boolean chainNotValidated = false;
+ private boolean notSignedByAlias = false;
+ private boolean aliasNotInStore = false;
+ private boolean hasUnsignedEntry = false;
private boolean badKeyUsage = false;
private boolean badExtendedKeyUsage = false;
private boolean badNetscapeCertType = false;
+ CertificateFactory certificateFactory;
+ CertPathValidator validator;
+ PKIXParameters pkixParameters;
+
public void run(String args[]) {
try {
parseArgs(args);
@@ -184,14 +209,6 @@
}
}
- hasExpiredCert = false;
- hasExpiringCert = false;
- notYetValidCert = false;
-
- badKeyUsage = false;
- badExtendedKeyUsage = false;
- badNetscapeCertType = false;
-
if (verify) {
try {
loadKeyStore(keystore, false);
@@ -238,6 +255,29 @@
storepass = null;
}
}
+
+ if (strict) {
+ int exitCode = 0;
+ if (hasExpiringCert) {
+ exitCode |= 2;
+ }
+ if (chainNotValidated) {
+ // hasExpiredCert and notYetValidCert included in this case
+ exitCode |= 4;
+ }
+ if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) {
+ exitCode |= 8;
+ }
+ if (hasUnsignedEntry) {
+ exitCode |= 16;
+ }
+ if (notSignedByAlias || aliasNotInStore) {
+ exitCode |= 32;
+ }
+ if (exitCode != 0) {
+ System.exit(exitCode);
+ }
+ }
}
/*
@@ -247,25 +287,26 @@
/* parse flags */
int n = 0;
- for (n=0; (n < args.length) && args[n].startsWith("-"); n++) {
+ if (args.length == 0) fullusage();
+ for (n=0; n < args.length; n++) {
String flags = args[n];
if (collator.compare(flags, "-keystore") == 0) {
- if (++n == args.length) usage();
+ if (++n == args.length) usageNoArg();
keystore = args[n];
} else if (collator.compare(flags, "-storepass") ==0) {
- if (++n == args.length) usage();
+ if (++n == args.length) usageNoArg();
storepass = args[n].toCharArray();
} else if (collator.compare(flags, "-storetype") ==0) {
- if (++n == args.length) usage();
+ if (++n == args.length) usageNoArg();
storetype = args[n];
} else if (collator.compare(flags, "-providerName") ==0) {
- if (++n == args.length) usage();
+ if (++n == args.length) usageNoArg();
providerName = args[n];
} else if ((collator.compare(flags, "-provider") == 0) ||
(collator.compare(flags, "-providerClass") == 0)) {
- if (++n == args.length) usage();
+ if (++n == args.length) usageNoArg();
if (providers == null) {
providers = new Vector<String>(3);
}
@@ -274,35 +315,38 @@
if (args.length > (n+1)) {
flags = args[n+1];
if (collator.compare(flags, "-providerArg") == 0) {
- if (args.length == (n+2)) usage();
+ if (args.length == (n+2)) usageNoArg();
providerArgs.put(args[n], args[n+2]);
n += 2;
}
}
} else if (collator.compare(flags, "-protected") ==0) {
protectedPath = true;
+ } else if (collator.compare(flags, "-certchain") ==0) {
+ if (++n == args.length) usageNoArg();
+ altCertChain = args[n];
} else if (collator.compare(flags, "-debug") ==0) {
debug = true;
} else if (collator.compare(flags, "-keypass") ==0) {
- if (++n == args.length) usage();
+ if (++n == args.length) usageNoArg();
keypass = args[n].toCharArray();
} else if (collator.compare(flags, "-sigfile") ==0) {
- if (++n == args.length) usage();
+ if (++n == args.length) usageNoArg();
sigfile = args[n];
} else if (collator.compare(flags, "-signedjar") ==0) {
- if (++n == args.length) usage();
+ if (++n == args.length) usageNoArg();
signedjar = args[n];
} else if (collator.compare(flags, "-tsa") ==0) {
- if (++n == args.length) usage();
+ if (++n == args.length) usageNoArg();
tsaUrl = args[n];
} else if (collator.compare(flags, "-tsacert") ==0) {
- if (++n == args.length) usage();
+ if (++n == args.length) usageNoArg();
tsaAlias = args[n];
} else if (collator.compare(flags, "-altsigner") ==0) {
- if (++n == args.length) usage();
+ if (++n == args.length) usageNoArg();
altSignerClass = args[n];
} else if (collator.compare(flags, "-altsignerpath") ==0) {
- if (++n == args.length) usage();
+ if (++n == args.length) usageNoArg();
altSignerClasspath = args[n];
} else if (collator.compare(flags, "-sectionsonly") ==0) {
signManifest = false;
@@ -311,30 +355,56 @@
} else if (collator.compare(flags, "-verify") ==0) {
verify = true;
} else if (collator.compare(flags, "-verbose") ==0) {
- verbose = true;
+ verbose = "all";
+ } else if (collator.compare(flags, "-verbose:all") ==0) {
+ verbose = "all";
+ } else if (collator.compare(flags, "-verbose:summary") ==0) {
+ verbose = "summary";
+ } else if (collator.compare(flags, "-verbose:grouped") ==0) {
+ verbose = "grouped";
} else if (collator.compare(flags, "-sigalg") ==0) {
- if (++n == args.length) usage();
+ if (++n == args.length) usageNoArg();
sigalg = args[n];
} else if (collator.compare(flags, "-digestalg") ==0) {
- if (++n == args.length) usage();
+ if (++n == args.length) usageNoArg();
digestalg = args[n];
} else if (collator.compare(flags, "-certs") ==0) {
showcerts = true;
+ } else if (collator.compare(flags, "-strict") ==0) {
+ strict = true;
} else if (collator.compare(flags, "-h") == 0 ||
collator.compare(flags, "-help") == 0) {
- usage();
+ fullusage();
} else {
- System.err.println(rb.getString("Illegal option: ") + flags);
- usage();
+ if (!flags.startsWith("-")) {
+ if (jarfile == null) {
+ jarfile = flags;
+ } else {
+ alias = flags;
+ ckaliases.add(alias);
+ }
+ } else {
+ System.err.println(
+ rb.getString("Illegal option: ") + flags);
+ usage();
+ }
}
}
- if (n == args.length) usage();
- jarfile = args[n++];
+ // -certs must always be specified with -verbose
+ if (verbose == null) showcerts = false;
- if (!verify) {
- if (n == args.length) usage();
- alias = args[n++];
+ if (jarfile == null) {
+ System.err.println(rb.getString("Please specify jarfile name"));
+ usage();
+ }
+ if (!verify && alias == null) {
+ System.err.println(rb.getString("Please specify alias name"));
+ usage();
+ }
+ if (!verify && ckaliases.size() > 1) {
+ System.err.println(rb.getString("Only one alias can be specified"));
+ usage();
}
if (storetype == null) {
@@ -357,7 +427,6 @@
if (token && !nullStream) {
System.err.println(MessageFormat.format(rb.getString
("-keystore must be NONE if -storetype is {0}"), storetype));
- System.err.println();
usage();
}
@@ -365,7 +434,6 @@
System.err.println(MessageFormat.format(rb.getString
("-keypass can not be specified " +
"if -storetype is {0}"), storetype));
- System.err.println();
usage();
}
@@ -374,7 +442,6 @@
System.err.println(rb.getString
("If -protected is specified, " +
"then -storepass and -keypass must not be specified"));
- System.err.println();
usage();
}
}
@@ -383,17 +450,27 @@
System.err.println(rb.getString
("If keystore is not password protected, " +
"then -storepass and -keypass must not be specified"));
- System.err.println();
usage();
}
}
}
+ void usageNoArg() {
+ System.out.println(rb.getString("Option lacks argument"));
+ usage();
+ }
+
void usage() {
+ System.out.println();
+ System.out.println(rb.getString("Please type jarsigner -help for usage"));
+ System.exit(1);
+ }
+
+ void fullusage() {
System.out.println(rb.getString
("Usage: jarsigner [options] jar-file alias"));
System.out.println(rb.getString
- (" jarsigner -verify [options] jar-file"));
+ (" jarsigner -verify [options] jar-file [alias...]"));
System.out.println();
System.out.println(rb.getString
("[-keystore <url>] keystore location"));
@@ -408,6 +485,9 @@
("[-keypass <password>] password for private key (if different)"));
System.out.println();
System.out.println(rb.getString
+ ("[-certchain <file>] name of alternative certchain file"));
+ System.out.println();
+ System.out.println(rb.getString
("[-sigfile <file>] name of .SF/.DSA file"));
System.out.println();
System.out.println(rb.getString
@@ -423,7 +503,9 @@
("[-verify] verify a signed JAR file"));
System.out.println();
System.out.println(rb.getString
- ("[-verbose] verbose output when signing/verifying"));
+ ("[-verbose[:suboptions]] verbose output when signing/verifying."));
+ System.out.println(rb.getString
+ (" suboptions can be all, grouped or summary"));
System.out.println();
System.out.println(rb.getString
("[-certs] display certificates when verbose and verifying"));
@@ -457,15 +539,17 @@
System.out.println(rb.getString
(" [-providerArg <arg>]] ... master class file and constructor argument"));
System.out.println();
+ System.out.println(rb.getString
+ ("[-strict] treat warnings as errors"));
+ System.out.println();
- System.exit(1);
+ System.exit(0);
}
void verifyJar(String jarName)
throws Exception
{
- boolean anySigned = false;
- boolean hasUnsignedEntry = false;
+ boolean anySigned = false; // if there exists entry inside jar signed
JarFile jf = null;
try {
@@ -494,11 +578,18 @@
Manifest man = jf.getManifest();
+ // The map to record display info, only used when -verbose provided
+ // key: signer info string
+ // value: the list of files with common key
+ Map<String,List<String>> output =
+ new LinkedHashMap<String,List<String>>();
+
if (man != null) {
- if (verbose) System.out.println();
+ if (verbose != null) System.out.println();
Enumeration<JarEntry> e = entriesVec.elements();
long now = System.currentTimeMillis();
+ String tab = rb.getString(" ");
while (e.hasMoreElements()) {
JarEntry je = e.nextElement();
@@ -509,77 +600,118 @@
hasUnsignedEntry |= !je.isDirectory() && !isSigned
&& !signatureRelated(name);
- if (verbose) {
- int inStoreOrScope = inKeyStore(signers);
- boolean inStore = (inStoreOrScope & IN_KEYSTORE) != 0;
- boolean inScope = (inStoreOrScope & IN_SCOPE) != 0;
+ int inStoreOrScope = inKeyStore(signers);
+
+ boolean inStore = (inStoreOrScope & IN_KEYSTORE) != 0;
+ boolean inScope = (inStoreOrScope & IN_SCOPE) != 0;
+
+ notSignedByAlias |= (inStoreOrScope & NOT_ALIAS) != 0;
+ aliasNotInStore |= isSigned && (!inStore && !inScope);
+
+ // Only used when -verbose provided
+ StringBuffer sb = null;
+ if (verbose != null) {
+ sb = new StringBuffer();
boolean inManifest =
((man.getAttributes(name) != null) ||
(man.getAttributes("./"+name) != null) ||
(man.getAttributes("/"+name) != null));
- System.out.print(
+ sb.append(
(isSigned ? rb.getString("s") : rb.getString(" ")) +
(inManifest ? rb.getString("m") : rb.getString(" ")) +
(inStore ? rb.getString("k") : rb.getString(" ")) +
(inScope ? rb.getString("i") : rb.getString(" ")) +
- rb.getString(" "));
- StringBuffer sb = new StringBuffer();
- String s = Long.toString(je.getSize());
- for (int i = 6 - s.length(); i > 0; --i) {
- sb.append(' ');
- }
- sb.append(s).append(' ').
- append(new Date(je.getTime()).toString());
- sb.append(' ').append(je.getName());
- System.out.println(sb.toString());
+ ((inStoreOrScope & NOT_ALIAS) != 0 ?"X":" ") +
+ rb.getString(" "));
+ sb.append("|");
+ }
- if (signers != null && showcerts) {
- String tab = rb.getString(" ");
- for (int i = 0; i < signers.length; i++) {
- System.out.println();
- List<? extends Certificate> certs =
- signers[i].getSignerCertPath()
- .getCertificates();
- // display the signature timestamp, if present
- Timestamp timestamp = signers[i].getTimestamp();
- if (timestamp != null) {
- System.out.println(
- printTimestamp(tab, timestamp));
- }
- // display the certificate(s)
- for (Certificate c : certs) {
- System.out.println(
- printCert(tab, c, true, now));
- }
+ // When -certs provided, display info has extra empty
+ // lines at the beginning and end.
+ if (isSigned) {
+ if (showcerts) sb.append('\n');
+ for (CodeSigner signer: signers) {
+ // signerInfo() must be called even if -verbose
+ // not provided. The method updates various
+ // warning flags.
+ String si = signerInfo(signer, tab, now);
+ if (showcerts) {
+ sb.append(si);
+ sb.append('\n');
}
- System.out.println();
}
-
- }
- if (isSigned) {
- for (int i = 0; i < signers.length; i++) {
- Certificate cert =
- signers[i].getSignerCertPath()
- .getCertificates().get(0);
- if (cert instanceof X509Certificate) {
- checkCertUsage((X509Certificate)cert, null);
- if (!showcerts) {
- long notAfter = ((X509Certificate)cert)
- .getNotAfter().getTime();
-
- if (notAfter < now) {
- hasExpiredCert = true;
- } else if (notAfter < now + SIX_MONTHS) {
- hasExpiringCert = true;
- }
- }
- }
+ } else if (showcerts && !verbose.equals("all")) {
+ // Print no info for unsigned entries when -verbose:all,
+ // to be consistent with old behavior.
+ if (signatureRelated(name)) {
+ sb.append("\n" + tab + rb.getString(
+ "(Signature related entries)") + "\n\n");
+ } else {
+ sb.append("\n" + tab + rb.getString(
+ "(Unsigned entries)") + "\n\n");
}
}
+ if (verbose != null) {
+ String label = sb.toString();
+ if (signatureRelated(name)) {
+ // Entries inside META-INF and other unsigned
+ // entries are grouped separately.
+ label = "-" + label.substring(1);
+ }
+
+ // The label finally contains 2 parts separated by '|':
+ // The legend displayed before the entry names, and
+ // the cert info (if -certs specfied).
+
+ if (!output.containsKey(label)) {
+ output.put(label, new ArrayList<String>());
+ }
+
+ StringBuffer fb = new StringBuffer();
+ String s = Long.toString(je.getSize());
+ for (int i = 6 - s.length(); i > 0; --i) {
+ fb.append(' ');
+ }
+ fb.append(s).append(' ').
+ append(new Date(je.getTime()).toString());
+ fb.append(' ').append(name);
+
+ output.get(label).add(fb.toString());
+ }
}
}
- if (verbose) {
+ if (verbose != null) {
+ for (Entry<String,List<String>> s: output.entrySet()) {
+ List<String> files = s.getValue();
+ String key = s.getKey();
+ if (key.charAt(0) == '-') { // the signature-related group
+ key = ' ' + key.substring(1);
+ }
+ int pipe = key.indexOf('|');
+ if (verbose.equals("all")) {
+ for (String f: files) {
+ System.out.println(key.substring(0, pipe) + f);
+ System.out.printf(key.substring(pipe+1));
+ }
+ } else {
+ if (verbose.equals("grouped")) {
+ for (String f: files) {
+ System.out.println(key.substring(0, pipe) + f);
+ }
+ } else if (verbose.equals("summary")) {
+ System.out.print(key.substring(0, pipe));
+ if (files.size() > 1) {
+ System.out.println(files.get(0) + " " +
+ String.format(rb.getString(
+ "(and %d more)"), files.size()-1));
+ } else {
+ System.out.println(files.get(0));
+ }
+ }
+ System.out.printf(key.substring(pipe+1));
+ }
+ }
System.out.println();
System.out.println(rb.getString(
" s = signature was verified "));
@@ -589,9 +721,12 @@
" k = at least one certificate was found in keystore"));
System.out.println(rb.getString(
" i = at least one certificate was found in identity scope"));
+ if (ckaliases.size() > 0) {
+ System.out.println((
+ " X = not signed by specified alias(es)"));
+ }
System.out.println();
}
-
if (man == null)
System.out.println(rb.getString("no manifest."));
@@ -602,7 +737,8 @@
System.out.println(rb.getString("jar verified."));
if (hasUnsignedEntry || hasExpiredCert || hasExpiringCert ||
badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
- notYetValidCert) {
+ notYetValidCert || chainNotValidated ||
+ aliasNotInStore || notSignedByAlias) {
System.out.println();
System.out.println(rb.getString("Warning: "));
@@ -638,14 +774,27 @@
"This jar contains entries whose signer certificate is not yet valid. "));
}
- if (! (verbose && showcerts)) {
+ if (chainNotValidated) {
+ System.out.println(
+ rb.getString("This jar contains entries whose certificate chain is not validated."));
+ }
+
+ if (notSignedByAlias) {
+ System.out.println(
+ rb.getString("This jar contains signed entries which is not signed by the specified alias(es)."));
+ }
+
+ if (aliasNotInStore) {
+ System.out.println(rb.getString("This jar contains signed entries that's not signed by alias in this keystore."));
+ }
+ if (! (verbose != null && showcerts)) {
System.out.println();
System.out.println(rb.getString(
"Re-run with the -verbose and -certs options for more details."));
}
}
}
- System.exit(0);
+ return;
} catch (Exception e) {
System.out.println(rb.getString("jarsigner: ") + e);
if (debug) {
@@ -660,15 +809,6 @@
System.exit(1);
}
- /*
- * Display some details about a certificate:
- *
- * <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"]
- */
- String printCert(Certificate c) {
- return printCert("", c, false, 0);
- }
-
private static MessageFormat validityTimeForm = null;
private static MessageFormat notYetTimeForm = null;
private static MessageFormat expiredTimeForm = null;
@@ -679,6 +819,8 @@
*
* [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"]
* [<validity-period> | <expiry-warning>]
+ *
+ * Note: no newline character at the end
*/
String printCert(String tab, Certificate c, boolean checkValidityPeriod,
long now) {
@@ -788,54 +930,75 @@
.append(signTimeForm.format(source)).append("]").toString();
}
+ private Map<CodeSigner,Integer> cacheForInKS =
+ new IdentityHashMap<CodeSigner,Integer>();
+
+ private int inKeyStoreForOneSigner(CodeSigner signer) {
+ if (cacheForInKS.containsKey(signer)) {
+ return cacheForInKS.get(signer);
+ }
+
+ boolean found = false;
+ int result = 0;
+ List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
+ for (Certificate c : certs) {
+ String alias = storeHash.get(c);
+ if (alias != null) {
+ if (alias.startsWith("(")) {
+ result |= IN_KEYSTORE;
+ } else if (alias.startsWith("[")) {
+ result |= IN_SCOPE;
+ }
+ if (ckaliases.contains(alias.substring(1, alias.length() - 1))) {
+ result |= SIGNED_BY_ALIAS;
+ }
+ } else {
+ if (store != null) {
+ try {
+ alias = store.getCertificateAlias(c);
+ } catch (KeyStoreException kse) {
+ // never happens, because keystore has been loaded
+ }
+ if (alias != null) {
+ storeHash.put(c, "(" + alias + ")");
+ found = true;
+ result |= IN_KEYSTORE;
+ }
+ }
+ if (!found && (scope != null)) {
+ Identity id = scope.getIdentity(c.getPublicKey());
+ if (id != null) {
+ result |= IN_SCOPE;
+ storeHash.put(c, "[" + id.getName() + "]");
+ }
+ }
+ if (ckaliases.contains(alias)) {
+ result |= SIGNED_BY_ALIAS;
+ }
+ }
+ }
+ cacheForInKS.put(signer, result);
+ return result;
+ }
+
Hashtable<Certificate, String> storeHash =
new Hashtable<Certificate, String>();
int inKeyStore(CodeSigner[] signers) {
- int result = 0;
if (signers == null)
return 0;
- boolean found = false;
-
- for (int i = 0; i < signers.length; i++) {
- found = false;
- List<? extends Certificate> certs =
- signers[i].getSignerCertPath().getCertificates();
-
- for (Certificate c : certs) {
- String alias = storeHash.get(c);
+ int output = 0;
- if (alias != null) {
- if (alias.startsWith("("))
- result |= IN_KEYSTORE;
- else if (alias.startsWith("["))
- result |= IN_SCOPE;
- } else {
- if (store != null) {
- try {
- alias = store.getCertificateAlias(c);
- } catch (KeyStoreException kse) {
- // never happens, because keystore has been loaded
- }
- if (alias != null) {
- storeHash.put(c, "("+alias+")");
- found = true;
- result |= IN_KEYSTORE;
- }
- }
- if (!found && (scope != null)) {
- Identity id = scope.getIdentity(c.getPublicKey());
- if (id != null) {
- result |= IN_SCOPE;
- storeHash.put(c, "["+id.getName()+"]");
- }
- }
- }
- }
+ for (CodeSigner signer: signers) {
+ int result = inKeyStoreForOneSigner(signer);
+ output |= result;
}
- return result;
+ if (ckaliases.size() > 0 && (output & SIGNED_BY_ALIAS) == 0) {
+ output |= NOT_ALIAS;
+ }
+ return output;
}
void signJar(String jarName, String alias, String[] args)
@@ -1025,7 +1188,7 @@
// manifest file has new length
mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
}
- if (verbose) {
+ if (verbose != null) {
if (mfCreated) {
System.out.println(rb.getString(" adding: ") +
mfFile.getName());
@@ -1076,7 +1239,7 @@
// signature file
zos.putNextEntry(sfFile);
sf.write(zos);
- if (verbose) {
+ if (verbose != null) {
if (zipFile.getEntry(sfFilename) != null) {
System.out.println(rb.getString(" updating: ") +
sfFilename);
@@ -1086,7 +1249,7 @@
}
}
- if (verbose) {
+ if (verbose != null) {
if (tsaUrl != null || tsaCert != null) {
System.out.println(
rb.getString("requesting a signature timestamp"));
@@ -1101,8 +1264,8 @@
System.out.println(rb.getString("TSA location: ") +
certUrl);
}
- System.out.println(
- rb.getString("TSA certificate: ") + printCert(tsaCert));
+ System.out.println(rb.getString("TSA certificate: ") +
+ printCert("", tsaCert, false, 0));
}
if (signingMechanism != null) {
System.out.println(
@@ -1113,7 +1276,7 @@
// signature block file
zos.putNextEntry(bkFile);
block.write(zos);
- if (verbose) {
+ if (verbose != null) {
if (zipFile.getEntry(bkFilename) != null) {
System.out.println(rb.getString(" updating: ") +
bkFilename);
@@ -1140,7 +1303,7 @@
ZipEntry ze = enum_.nextElement();
if (!ze.getName().startsWith(META_INF)) {
- if (verbose) {
+ if (verbose != null) {
if (manifest.getAttributes(ze.getName()) != null)
System.out.println(rb.getString(" signing: ") +
ze.getName());
@@ -1194,7 +1357,8 @@
}
if (hasExpiredCert || hasExpiringCert || notYetValidCert
- || badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) {
+ || badKeyUsage || badExtendedKeyUsage
+ || badNetscapeCertType || chainNotValidated) {
System.out.println();
System.out.println(rb.getString("Warning: "));
@@ -1223,6 +1387,11 @@
System.out.println(
rb.getString("The signer certificate is not yet valid."));
}
+
+ if (chainNotValidated) {
+ System.out.println(
+ rb.getString("The signer's certificate chain is not validated."));
+ }
}
// no IOException thrown in the above try clause, so disable
@@ -1274,6 +1443,40 @@
return false;
}
+ Map<CodeSigner,String> cacheForSignerInfo = new IdentityHashMap<CodeSigner,String>();
+
+ /**
+ * Returns a string of singer info, with a newline at the end
+ */
+ private String signerInfo(CodeSigner signer, String tab, long now) {
+ if (cacheForSignerInfo.containsKey(signer)) {
+ return cacheForSignerInfo.get(signer);
+ }
+ StringBuffer s = new StringBuffer();
+ List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
+ // display the signature timestamp, if present
+ Timestamp timestamp = signer.getTimestamp();
+ if (timestamp != null) {
+ s.append(printTimestamp(tab, timestamp));
+ }
+ // display the certificate(s)
+ for (Certificate c : certs) {
+ s.append(printCert(tab, c, true, now));
+ s.append('\n');
+ }
+ try {
+ CertPath cp = certificateFactory.generateCertPath(certs);
+ validator.validate(cp, pkixParameters);
+ } catch (Exception e) {
+ chainNotValidated = true;
+ s.append(tab + rb.getString("[CertPath not validated: ") +
+ e.getLocalizedMessage() + "]\n"); // TODO
+ }
+ String result = s.toString();
+ cacheForSignerInfo.put(signer, result);
+ return result;
+ }
+
private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze)
throws IOException
{
@@ -1360,6 +1563,48 @@
}
}
}
+ Set<TrustAnchor> tas = new HashSet<TrustAnchor>();
+ try {
+ KeyStore caks = KeyTool.getCacertsKeyStore();
+ if (caks != null) {
+ Enumeration<String> aliases = caks.aliases();
+ while (aliases.hasMoreElements()) {
+ String a = aliases.nextElement();
+ try {
+ tas.add(new TrustAnchor((X509Certificate)caks.getCertificate(a), null));
+ } catch (Exception e2) {
+ // ignore, when a SecretkeyEntry does not include a cert
+ }
+ }
+ }
+ } catch (Exception e) {
+ // Ignore, if cacerts cannot be loaded
+ }
+ if (store != null) {
+ Enumeration<String> aliases = store.aliases();
+ while (aliases.hasMoreElements()) {
+ String a = aliases.nextElement();
+ try {
+ X509Certificate c = (X509Certificate)store.getCertificate(a);
+ // Only add TrustedCertificateEntry and self-signed
+ // PrivateKeyEntry
+ if (store.isCertificateEntry(a) ||
+ c.getSubjectDN().equals(c.getIssuerDN())) {
+ tas.add(new TrustAnchor(c, null));
+ }
+ } catch (Exception e2) {
+ // ignore, when a SecretkeyEntry does not include a cert
+ }
+ }
+ }
+ certificateFactory = CertificateFactory.getInstance("X.509");
+ validator = CertPathValidator.getInstance("PKIX");
+ try {
+ pkixParameters = new PKIXParameters(tas);
+ pkixParameters.setRevocationEnabled(false);
+ } catch (InvalidAlgorithmParameterException ex) {
+ // Only if tas is empty
+ }
} catch (IOException ioe) {
throw new RuntimeException(rb.getString("keystore load: ") +
ioe.getMessage());
@@ -1408,7 +1653,8 @@
void checkCertUsage(X509Certificate userCert, boolean[] bad) {
// Can act as a signer?
- // 1. if KeyUsage, then [0] should be true
+ // 1. if KeyUsage, then [0:digitalSignature] or
+ // [1:nonRepudiation] should be true
// 2. if ExtendedKeyUsage, then should contains ANY or CODE_SIGNING
// 3. if NetscapeCertType, then should contains OBJECT_SIGNING
// 1,2,3 must be true
@@ -1419,10 +1665,10 @@
boolean[] keyUsage = userCert.getKeyUsage();
if (keyUsage != null) {
- if (keyUsage.length < 1 || !keyUsage[0]) {
+ keyUsage = Arrays.copyOf(keyUsage, 9);
+ if (!keyUsage[0] && !keyUsage[1]) {
if (bad != null) {
bad[0] = true;
- } else {
badKeyUsage = true;
}
}
@@ -1435,7 +1681,6 @@
&& !xKeyUsage.contains("1.3.6.1.5.5.7.3.3")) { // codeSigning
if (bad != null) {
bad[1] = true;
- } else {
badExtendedKeyUsage = true;
}
}
@@ -1462,7 +1707,6 @@
if (!val) {
if (bad != null) {
bad[2] = true;
- } else {
badNetscapeCertType = true;
}
}
@@ -1477,19 +1721,36 @@
Key key = null;
try {
-
java.security.cert.Certificate[] cs = null;
-
- try {
- cs = store.getCertificateChain(alias);
- } catch (KeyStoreException kse) {
- // this never happens, because keystore has been loaded
+ if (altCertChain != null) {
+ try {
+ cs = CertificateFactory.getInstance("X.509").
+ generateCertificates(new FileInputStream(altCertChain)).
+ toArray(new Certificate[0]);
+ } catch (CertificateException ex) {
+ error(rb.getString("Cannot restore certchain from file specified"));
+ } catch (FileNotFoundException ex) {
+ error(rb.getString("File specified by -certchain does not exist"));
+ }
+ } else {
+ try {
+ cs = store.getCertificateChain(alias);
+ } catch (KeyStoreException kse) {
+ // this never happens, because keystore has been loaded
+ }
}
- if (cs == null) {
- MessageFormat form = new MessageFormat(rb.getString
- ("Certificate chain not found for: alias. alias must reference a valid KeyStore key entry containing a private key and corresponding public key certificate chain."));
- Object[] source = {alias, alias};
- error(form.format(source));
+ if (cs == null || cs.length == 0) {
+ if (altCertChain != null) {
+ error(rb.getString
+ ("Certificate chain not found in the file specified."));
+ } else {
+ MessageFormat form = new MessageFormat(rb.getString
+ ("Certificate chain not found for: alias. alias must" +
+ " reference a valid KeyStore key entry containing a" +
+ " private key and corresponding public key certificate chain."));
+ Object[] source = {alias, alias};
+ error(form.format(source));
+ }
}
certChain = new X509Certificate[cs.length];
@@ -1501,56 +1762,15 @@
certChain[i] = (X509Certificate)cs[i];
}
- // order the cert chain if necessary (put user cert first,
- // root-cert last in the chain)
- X509Certificate userCert
- = (X509Certificate)store.getCertificate(alias);
-
- // check validity of signer certificate
- try {
- userCert.checkValidity();
-
- if (userCert.getNotAfter().getTime() <
- System.currentTimeMillis() + SIX_MONTHS) {
-
- hasExpiringCert = true;
- }
- } catch (CertificateExpiredException cee) {
- hasExpiredCert = true;
-
- } catch (CertificateNotYetValidException cnyve) {
- notYetValidCert = true;
- }
-
- checkCertUsage(userCert, null);
+ // We don't meant to print anything, the next call
+ // checks validity and keyUsage etc
+ printCert("", certChain[0], true, 0);
- if (!userCert.equals(certChain[0])) {
- // need to order ...
- X509Certificate[] certChainTmp
- = new X509Certificate[certChain.length];
- certChainTmp[0] = userCert;
- Principal issuer = userCert.getIssuerDN();
- for (int i=1; i<certChain.length; i++) {
- int j;
- // look for the cert whose subject corresponds to the
- // given issuer
- for (j=0; j<certChainTmp.length; j++) {
- if (certChainTmp[j] == null)
- continue;
- Principal subject = certChainTmp[j].getSubjectDN();
- if (issuer.equals(subject)) {
- certChain[i] = certChainTmp[j];
- issuer = certChainTmp[j].getIssuerDN();
- certChainTmp[j] = null;
- break;
- }
- }
- if (j == certChainTmp.length) {
- error(rb.getString("incomplete certificate chain"));
- }
-
- }
- certChain = certChainTmp; // ordered
+ try {
+ CertPath cp = certificateFactory.generateCertPath(Arrays.asList(certChain));
+ validator.validate(cp, pkixParameters);
+ } catch (Exception e) {
+ chainNotValidated = true;
}
try {
--- a/jdk/src/share/classes/sun/security/tools/JarSignerResources.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/sun/security/tools/JarSignerResources.java Fri Apr 03 10:29:13 2009 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -54,8 +54,8 @@
"If keystore is not password protected, then -storepass and -keypass must not be specified"},
{"Usage: jarsigner [options] jar-file alias",
"Usage: jarsigner [options] jar-file alias"},
- {" jarsigner -verify [options] jar-file",
- " jarsigner -verify [options] jar-file"},
+ {" jarsigner -verify [options] jar-file [alias...]",
+ " jarsigner -verify [options] jar-file [alias...]"},
{"[-keystore <url>] keystore location",
"[-keystore <url>] keystore location"},
{"[-storepass <password>] password for keystore integrity",
@@ -64,6 +64,8 @@
"[-storetype <type>] keystore type"},
{"[-keypass <password>] password for private key (if different)",
"[-keypass <password>] password for private key (if different)"},
+ {"[-certchain <file>] name of alternative certchain file",
+ "[-certchain <file>] name of alternative certchain file"},
{"[-sigfile <file>] name of .SF/.DSA file",
"[-sigfile <file>] name of .SF/.DSA file"},
{"[-signedjar <file>] name of signed JAR file",
@@ -74,8 +76,10 @@
"[-sigalg <algorithm>] name of signature algorithm"},
{"[-verify] verify a signed JAR file",
"[-verify] verify a signed JAR file"},
- {"[-verbose] verbose output when signing/verifying",
- "[-verbose] verbose output when signing/verifying"},
+ {"[-verbose[:suboptions]] verbose output when signing/verifying.",
+ "[-verbose[:suboptions]] verbose output when signing/verifying."},
+ {" suboptions can be all, grouped or summary",
+ " suboptions can be all, grouped or summary"},
{"[-certs] display certificates when verbose and verifying",
"[-certs] display certificates when verbose and verifying"},
{"[-tsa <url>] location of the Timestamping Authority",
@@ -98,10 +102,22 @@
"[-providerClass <class> name of cryptographic service provider's"},
{" [-providerArg <arg>]] ... master class file and constructor argument",
" [-providerArg <arg>]] ... master class file and constructor argument"},
+ {"[-strict] treat warnings as errors",
+ "[-strict] treat warnings as errors"},
+ {"Option lacks argument", "Option lacks argument"},
+ {"Please type jarsigner -help for usage", "Please type jarsigner -help for usage"},
+ {"Please specify jarfile name", "Please specify jarfile name"},
+ {"Please specify alias name", "Please specify alias name"},
+ {"Only one alias can be specified", "Only one alias can be specified"},
+ {"This jar contains signed entries which is not signed by the specified alias(es).",
+ "This jar contains signed entries which is not signed by the specified alias(es)."},
+ {"This jar contains signed entries that's not signed by alias in this keystore.",
+ "This jar contains signed entries that's not signed by alias in this keystore."},
{"s", "s"},
{"m", "m"},
{"k", "k"},
{"i", "i"},
+ {"(and %d more)", "(and %d more)"},
{" s = signature was verified ",
" s = signature was verified "},
{" m = entry is listed in manifest",
@@ -110,7 +126,11 @@
" k = at least one certificate was found in keystore"},
{" i = at least one certificate was found in identity scope",
" i = at least one certificate was found in identity scope"},
+ {" X = not signed by specified alias(es)",
+ " X = not signed by specified alias(es)"},
{"no manifest.", "no manifest."},
+ {"(Signature related entries)","(Signature related entries)"},
+ {"(Unsigned entries)", "(Unsigned entries)"},
{"jar is unsigned. (signatures missing or not parsable)",
"jar is unsigned. (signatures missing or not parsable)"},
{"jar verified.", "jar verified."},
@@ -134,6 +154,12 @@
"unable to instantiate keystore class: "},
{"Certificate chain not found for: alias. alias must reference a valid KeyStore key entry containing a private key and corresponding public key certificate chain.",
"Certificate chain not found for: {0}. {1} must reference a valid KeyStore key entry containing a private key and corresponding public key certificate chain."},
+ {"File specified by -certchain does not exist",
+ "File specified by -certchain does not exist"},
+ {"Cannot restore certchain from file specified",
+ "Cannot restore certchain from file specified"},
+ {"Certificate chain not found in the file specified.",
+ "Certificate chain not found in the file specified."},
{"found non-X.509 certificate in signer's chain",
"found non-X.509 certificate in signer's chain"},
{"incomplete certificate chain", "incomplete certificate chain"},
@@ -149,6 +175,7 @@
{"certificate is not valid until",
"certificate is not valid until {0}"},
{"certificate will expire on", "certificate will expire on {0}"},
+ {"[CertPath not validated: ", "[CertPath not validated: "},
{"requesting a signature timestamp",
"requesting a signature timestamp"},
{"TSA location: ", "TSA location: "},
@@ -189,14 +216,18 @@
"The signer certificate's ExtendedKeyUsage extension doesn't allow code signing."},
{"The signer certificate's NetscapeCertType extension doesn't allow code signing.",
"The signer certificate's NetscapeCertType extension doesn't allow code signing."},
- {"This jar contains entries whose signer certificate's KeyUsage extension doesn't allow code signing.",
- "This jar contains entries whose signer certificate's KeyUsage extension doesn't allow code signing."},
- {"This jar contains entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing.",
- "This jar contains entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing."},
- {"This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing.",
- "This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing."},
+ {"This jar contains entries whose signer certificate's KeyUsage extension doesn't allow code signing.",
+ "This jar contains entries whose signer certificate's KeyUsage extension doesn't allow code signing."},
+ {"This jar contains entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing.",
+ "This jar contains entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing."},
+ {"This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing.",
+ "This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing."},
{"[{0} extension does not support code signing]",
"[{0} extension does not support code signing]"},
+ {"The signer's certificate chain is not validated.",
+ "The signer's certificate chain is not validated."},
+ {"This jar contains entries whose certificate chain is not validated.",
+ "This jar contains entries whose certificate chain is not validated."},
};
/**
--- a/jdk/src/share/classes/sun/security/tools/KeyTool.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/sun/security/tools/KeyTool.java Fri Apr 03 10:29:13 2009 -0700
@@ -2545,7 +2545,19 @@
* Returns true if the certificate is self-signed, false otherwise.
*/
private boolean isSelfSigned(X509Certificate cert) {
- return cert.getSubjectDN().equals(cert.getIssuerDN());
+ return signedBy(cert, cert);
+ }
+
+ private boolean signedBy(X509Certificate end, X509Certificate ca) {
+ if (!ca.getSubjectDN().equals(end.getIssuerDN())) {
+ return false;
+ }
+ try {
+ end.verify(ca.getPublicKey());
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
}
/**
@@ -2869,20 +2881,18 @@
Certificate tmpCert = replyCerts[0];
replyCerts[0] = replyCerts[i];
replyCerts[i] = tmpCert;
- Principal issuer = ((X509Certificate)replyCerts[0]).getIssuerDN();
+
+ X509Certificate thisCert = (X509Certificate)replyCerts[0];
for (i=1; i < replyCerts.length-1; i++) {
- // find a cert in the reply whose "subject" is the same as the
- // given "issuer"
+ // find a cert in the reply who signs thisCert
int j;
for (j=i; j<replyCerts.length; j++) {
- Principal subject;
- subject = ((X509Certificate)replyCerts[j]).getSubjectDN();
- if (subject.equals(issuer)) {
+ if (signedBy(thisCert, (X509Certificate)replyCerts[j])) {
tmpCert = replyCerts[i];
replyCerts[i] = replyCerts[j];
replyCerts[j] = tmpCert;
- issuer = ((X509Certificate)replyCerts[i]).getIssuerDN();
+ thisCert = (X509Certificate)replyCerts[i];
break;
}
}
@@ -2892,18 +2902,6 @@
}
}
- // now verify each cert in the ordered chain
- for (i=0; i<replyCerts.length-1; i++) {
- PublicKey pubKey = replyCerts[i+1].getPublicKey();
- try {
- replyCerts[i].verify(pubKey);
- } catch (Exception e) {
- throw new Exception(rb.getString
- ("Certificate chain in reply does not verify: ") +
- e.getMessage());
- }
- }
-
if (noprompt) {
return replyCerts;
}
@@ -3035,9 +3033,8 @@
private boolean buildChain(X509Certificate certToVerify,
Vector<Certificate> chain,
Hashtable<Principal, Vector<Certificate>> certs) {
- Principal subject = certToVerify.getSubjectDN();
Principal issuer = certToVerify.getIssuerDN();
- if (subject.equals(issuer)) {
+ if (isSelfSigned(certToVerify)) {
// reached self-signed root cert;
// no verification needed because it's trusted.
chain.addElement(certToVerify);
@@ -3108,7 +3105,7 @@
/**
* Returns the keystore with the configured CA certificates.
*/
- private KeyStore getCacertsKeyStore()
+ public static KeyStore getCacertsKeyStore()
throws Exception
{
String sep = File.separator;
--- a/jdk/src/share/classes/sun/security/util/ManifestEntryVerifier.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/sun/security/util/ManifestEntryVerifier.java Fri Apr 03 10:29:13 2009 -0700
@@ -44,8 +44,6 @@
private static final Debug debug = Debug.getInstance("jar");
- private static final Provider digestProvider = Providers.getSunProvider();
-
/** the created digest objects */
HashMap<String, MessageDigest> createdDigests;
@@ -127,7 +125,7 @@
try {
digest = MessageDigest.getInstance
- (algorithm, digestProvider);
+ (algorithm, Providers.getSunProvider());
createdDigests.put(algorithm, digest);
} catch (NoSuchAlgorithmException nsae) {
// ignore
--- a/jdk/src/share/native/java/util/zip/zip_util.c Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/native/java/util/zip/zip_util.c Fri Apr 03 10:29:13 2009 -0700
@@ -313,6 +313,38 @@
}
/*
+ * Searches for the ZIP64 end of central directory (END) header. The
+ * contents of the ZIP64 END header will be read and placed in end64buf.
+ * Returns the file position of the ZIP64 END header, otherwise returns
+ * -1 if the END header was not found or an error occurred.
+ *
+ * The ZIP format specifies the "position" of each related record as
+ * ...
+ * [central directory]
+ * [zip64 end of central directory record]
+ * [zip64 end of central directory locator]
+ * [end of central directory record]
+ *
+ * The offset of zip64 end locator can be calculated from endpos as
+ * "endpos - ZIP64_LOCHDR".
+ * The "offset" of zip64 end record is stored in zip64 end locator.
+ */
+static jlong
+findEND64(jzfile *zip, void *end64buf, jlong endpos)
+{
+ char loc64[ZIP64_LOCHDR];
+ jlong end64pos;
+ if (readFullyAt(zip->zfd, loc64, ZIP64_LOCHDR, endpos - ZIP64_LOCHDR) == -1) {
+ return -1; // end64 locator not found
+ }
+ end64pos = ZIP64_LOCOFF(loc64);
+ if (readFullyAt(zip->zfd, end64buf, ZIP64_ENDHDR, end64pos) == -1) {
+ return -1; // end64 record not found
+ }
+ return end64pos;
+}
+
+/*
* Returns a hash code value for a C-style NUL-terminated string.
*/
static unsigned int
@@ -463,7 +495,7 @@
readCEN(jzfile *zip, jint knownTotal)
{
/* Following are unsigned 32-bit */
- jlong endpos, cenpos, cenlen;
+ jlong endpos, end64pos, cenpos, cenlen, cenoff;
/* Following are unsigned 16-bit */
jint total, tablelen, i, j;
unsigned char *cenbuf = NULL;
@@ -474,6 +506,7 @@
jlong offset;
#endif
unsigned char endbuf[ENDHDR];
+ jint endhdrlen = ENDHDR;
jzcell *entries;
jint *table;
@@ -490,13 +523,27 @@
/* Get position and length of central directory */
cenlen = ENDSIZ(endbuf);
+ cenoff = ENDOFF(endbuf);
+ total = ENDTOT(endbuf);
+ if (cenlen == ZIP64_MAGICVAL || cenoff == ZIP64_MAGICVAL ||
+ total == ZIP64_MAGICCOUNT) {
+ unsigned char end64buf[ZIP64_ENDHDR];
+ if ((end64pos = findEND64(zip, end64buf, endpos)) != -1) {
+ cenlen = ZIP64_ENDSIZ(end64buf);
+ cenoff = ZIP64_ENDOFF(end64buf);
+ total = (jint)ZIP64_ENDTOT(end64buf);
+ endpos = end64pos;
+ endhdrlen = ZIP64_ENDHDR;
+ }
+ }
+
if (cenlen > endpos)
ZIP_FORMAT_ERROR("invalid END header (bad central directory size)");
cenpos = endpos - cenlen;
/* Get position of first local file (LOC) header, taking into
* account that there may be a stub prefixed to the zip file. */
- zip->locpos = cenpos - ENDOFF(endbuf);
+ zip->locpos = cenpos - cenoff;
if (zip->locpos < 0)
ZIP_FORMAT_ERROR("invalid END header (bad central directory offset)");
@@ -527,7 +574,7 @@
out the page size in order to make offset to be multiples of
page size.
*/
- zip->mlen = cenpos - offset + cenlen + ENDHDR;
+ zip->mlen = cenpos - offset + cenlen + endhdrlen;
zip->offset = offset;
mappedAddr = mmap64(0, zip->mlen, PROT_READ, MAP_SHARED, zip->zfd, (off64_t) offset);
zip->maddr = (mappedAddr == (void*) MAP_FAILED) ? NULL :
@@ -551,8 +598,13 @@
* is a 2-byte field, but we (and other zip implementations)
* support approx. 2**31 entries, we do not trust ENDTOT, but
* treat it only as a strong hint. When we call ourselves
- * recursively, knownTotal will have the "true" value. */
- total = (knownTotal != -1) ? knownTotal : ENDTOT(endbuf);
+ * recursively, knownTotal will have the "true" value.
+ *
+ * Keep this path alive even with the Zip64 END support added, just
+ * for zip files that have more than 0xffff entries but don't have
+ * the Zip64 enabled.
+ */
+ total = (knownTotal != -1) ? knownTotal : total;
entries = zip->entries = calloc(total, sizeof(entries[0]));
tablelen = zip->tablelen = ((total/2) | 1); // Odd -> fewer collisions
table = zip->table = malloc(tablelen * sizeof(table[0]));
@@ -854,6 +906,7 @@
static jzentry *
newEntry(jzfile *zip, jzcell *zc, AccessHint accessHint)
{
+ jlong locoff;
jint nlen, elen, clen;
jzentry *ze;
char *cen;
@@ -880,18 +933,55 @@
ze->size = CENLEN(cen);
ze->csize = (CENHOW(cen) == STORED) ? 0 : CENSIZ(cen);
ze->crc = CENCRC(cen);
- ze->pos = -(zip->locpos + CENOFF(cen));
+ locoff = CENOFF(cen);
+ ze->pos = -(zip->locpos + locoff);
if ((ze->name = malloc(nlen + 1)) == NULL) goto Catch;
memcpy(ze->name, cen + CENHDR, nlen);
ze->name[nlen] = '\0';
if (elen > 0) {
+ char *extra = cen + CENHDR + nlen;
+
/* This entry has "extra" data */
if ((ze->extra = malloc(elen + 2)) == NULL) goto Catch;
ze->extra[0] = (unsigned char) elen;
ze->extra[1] = (unsigned char) (elen >> 8);
- memcpy(ze->extra+2, cen + CENHDR + nlen, elen);
+ memcpy(ze->extra+2, extra, elen);
+ if (ze->csize == ZIP64_MAGICVAL || ze->size == ZIP64_MAGICVAL ||
+ locoff == ZIP64_MAGICVAL) {
+ jint off = 0;
+ while ((off + 4) < elen) { // spec: HeaderID+DataSize+Data
+ jint sz = SH(extra, off + 2);
+ if (SH(extra, off) == ZIP64_EXTID) {
+ off += 4;
+ if (ze->size == ZIP64_MAGICVAL) {
+ // if invalid zip64 extra fields, just skip
+ if (sz < 8 || (off + 8) > elen)
+ break;
+ ze->size = LL(extra, off);
+ sz -= 8;
+ off += 8;
+ }
+ if (ze->csize == ZIP64_MAGICVAL) {
+ if (sz < 8 || (off + 8) > elen)
+ break;
+ ze->csize = LL(extra, off);
+ sz -= 8;
+ off += 8;
+ }
+ if (locoff == ZIP64_MAGICVAL) {
+ if (sz < 8 || (off + 8) > elen)
+ break;
+ ze->pos = -(zip->locpos + LL(extra, off));
+ sz -= 8;
+ off += 8;
+ }
+ break;
+ }
+ off += (sz + 4);
+ }
+ }
}
if (clen > 0) {
--- a/jdk/src/share/native/java/util/zip/zip_util.h Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/native/java/util/zip/zip_util.h Fri Apr 03 10:29:13 2009 -0700
@@ -38,9 +38,13 @@
#define CENSIG 0x02014b50L /* "PK\001\002" */
#define ENDSIG 0x06054b50L /* "PK\005\006" */
+#define ZIP64_ENDSIG 0x06064b50L /* "PK\006\006" */
+#define ZIP64_LOCSIG 0x07064b50L /* "PK\006\007" */
+
/*
* Header sizes including signatures
*/
+
#ifdef USE_MMAP
#define SIGSIZ 4
#endif
@@ -49,12 +53,22 @@
#define CENHDR 46
#define ENDHDR 22
+#define ZIP64_ENDHDR 56 // ZIP64 end header size
+#define ZIP64_LOCHDR 20 // ZIP64 end loc header size
+#define ZIP64_EXTHDR 24 // EXT header size
+#define ZIP64_EXTID 1 // Extra field Zip64 header ID
+
+#define ZIP64_MAGICVAL 0xffffffffLL
+#define ZIP64_MAGICCOUNT 0xffff
+
+
/*
* Header field access macros
*/
#define CH(b, n) (((unsigned char *)(b))[n])
#define SH(b, n) (CH(b, n) | (CH(b, n+1) << 8))
-#define LG(b, n) (SH(b, n) | (SH(b, n+2) << 16))
+#define LG(b, n) ((SH(b, n) | (SH(b, n+2) << 16)) &0xffffffffUL)
+#define LL(b, n) (((jlong)LG(b, n)) | (((jlong)LG(b, n+4)) << 32))
#define GETSIG(b) LG(b, 0)
/*
@@ -106,6 +120,26 @@
#define ENDCOM(b) SH(b, 20) /* size of zip file comment */
/*
+ * Macros for getting Zip64 end of central directory header fields
+ */
+#define ZIP64_ENDLEN(b) LL(b, 4) /* size of zip64 end of central dir */
+#define ZIP64_ENDVEM(b) SH(b, 12) /* version made by */
+#define ZIP64_ENDVER(b) SH(b, 14) /* version needed to extract */
+#define ZIP64_ENDNMD(b) LG(b, 16) /* number of this disk */
+#define ZIP64_ENDDSK(b) LG(b, 20) /* disk number of start */
+#define ZIP64_ENDTOD(b) LL(b, 24) /* total number of entries on this disk */
+#define ZIP64_ENDTOT(b) LL(b, 32) /* total number of entries */
+#define ZIP64_ENDSIZ(b) LL(b, 40) /* central directory size in bytes */
+#define ZIP64_ENDOFF(b) LL(b, 48) /* offset of first CEN header */
+
+/*
+ * Macros for getting Zip64 end of central directory locator fields
+ */
+#define ZIP64_LOCDSK(b) LG(b, 4) /* disk number start */
+#define ZIP64_LOCOFF(b) LL(b, 8) /* offset of zip64 end */
+#define ZIP64_LOCTOT(b) LG(b, 16) /* total number of disks */
+
+/*
* Supported compression methods
*/
#define STORED 0
@@ -145,7 +179,7 @@
*/
typedef struct jzcell {
unsigned int hash; /* 32 bit hashcode on name */
- unsigned int cenpos; /* Offset of central directory file header */
+ jlong cenpos; /* Offset of central directory file header */
unsigned int next; /* hash chain: index into jzfile->entries */
} jzcell;
--- a/jdk/src/share/native/java/util/zip/zlib-1.1.3/zlib.h Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/native/java/util/zip/zlib-1.1.3/zlib.h Fri Apr 03 10:29:13 2009 -0700
@@ -106,11 +106,11 @@
typedef struct z_stream_s {
Bytef *next_in; /* next input byte */
uInt avail_in; /* number of bytes available at next_in */
- uLong total_in; /* total nb of input bytes read so far */
+ long long total_in; /* total nb of input bytes read so far */
Bytef *next_out; /* next output byte should be put there */
uInt avail_out; /* remaining free space at next_out */
- uLong total_out; /* total nb of bytes output so far */
+ long long total_out; /* total nb of bytes output so far */
char *msg; /* last error message, NULL if no error */
struct internal_state FAR *state; /* not visible by applications */
--- a/jdk/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java Fri Apr 03 10:29:13 2009 -0700
@@ -78,8 +78,8 @@
// Base address of the native pollArray
private final long pollArrayAddress;
- // Set of "idle" file descriptors
- private final HashSet<Integer> idleSet;
+ // Set of "idle" channels
+ private final HashSet<SelChImpl> idleSet;
EPollArrayWrapper() {
// creates the epoll file descriptor
@@ -96,19 +96,22 @@
}
// create idle set
- idleSet = new HashSet<Integer>();
+ idleSet = new HashSet<SelChImpl>();
}
// Used to update file description registrations
private static class Updator {
+ SelChImpl channel;
int opcode;
- int fd;
int events;
- Updator(int opcode, int fd, int events) {
+ Updator(SelChImpl channel, int opcode, int events) {
+ this.channel = channel;
this.opcode = opcode;
- this.fd = fd;
this.events = events;
}
+ Updator(SelChImpl channel, int opcode) {
+ this(channel, opcode, 0);
+ }
}
private LinkedList<Updator> updateList = new LinkedList<Updator>();
@@ -163,60 +166,54 @@
}
/**
- * Update the events for a given file descriptor.
+ * Update the events for a given channel.
*/
- void setInterest(int fd, int mask) {
+ void setInterest(SelChImpl channel, int mask) {
synchronized (updateList) {
-
- // if the interest events are 0 then add to idle set, and delete
- // from epoll if registered (or pending)
- if (mask == 0) {
- if (idleSet.add(fd)) {
- updateList.add(new Updator(EPOLL_CTL_DEL, fd, 0));
- }
- return;
- }
-
- // if file descriptor is idle then add to epoll
- if (!idleSet.isEmpty() && idleSet.remove(fd)) {
- updateList.add(new Updator(EPOLL_CTL_ADD, fd, mask));
- return;
- }
-
// if the previous pending operation is to add this file descriptor
// to epoll then update its event set
if (updateList.size() > 0) {
Updator last = updateList.getLast();
- if (last.fd == fd && last.opcode == EPOLL_CTL_ADD) {
+ if (last.channel == channel && last.opcode == EPOLL_CTL_ADD) {
last.events = mask;
return;
}
}
// update existing registration
- updateList.add(new Updator(EPOLL_CTL_MOD, fd, mask));
+ updateList.add(new Updator(channel, EPOLL_CTL_MOD, mask));
+ }
+ }
+
+ /**
+ * Add a channel's file descriptor to epoll
+ */
+ void add(SelChImpl channel) {
+ synchronized (updateList) {
+ updateList.add(new Updator(channel, EPOLL_CTL_ADD));
}
}
/**
- * Add a new file descriptor to epoll
+ * Remove a channel's file descriptor from epoll
*/
- void add(int fd) {
+ void release(SelChImpl channel) {
synchronized (updateList) {
- updateList.add(new Updator(EPOLL_CTL_ADD, fd, 0));
- }
- }
+ // flush any pending updates
+ int i = 0;
+ while (i < updateList.size()) {
+ if (updateList.get(i).channel == channel) {
+ updateList.remove(i);
+ } else {
+ i++;
+ }
+ }
- /**
- * Remove a file descriptor from epoll
- */
- void release(int fd) {
- synchronized (updateList) {
- // if file descriptor is idle then remove from idle set, otherwise
- // delete from epoll
- if (!idleSet.remove(fd)) {
- updateList.add(new Updator(EPOLL_CTL_DEL, fd, 0));
- }
+ // remove from the idle set (if present)
+ idleSet.remove(channel);
+
+ // remove from epoll (if registered)
+ epollCtl(epfd, EPOLL_CTL_DEL, channel.getFDVal(), 0);
}
}
@@ -248,7 +245,26 @@
synchronized (updateList) {
Updator u = null;
while ((u = updateList.poll()) != null) {
- epollCtl(epfd, u.opcode, u.fd, u.events);
+ SelChImpl ch = u.channel;
+ if (!ch.isOpen())
+ continue;
+
+ // if the events are 0 then file descriptor is put into "idle
+ // set" to prevent it being polled
+ if (u.events == 0) {
+ boolean added = idleSet.add(u.channel);
+ // if added to idle set then remove from epoll if registered
+ if (added && (u.opcode == EPOLL_CTL_MOD))
+ epollCtl(epfd, EPOLL_CTL_DEL, ch.getFDVal(), 0);
+ } else {
+ // events are specified. If file descriptor was in idle set
+ // it must be re-registered (by converting opcode to ADD)
+ boolean idle = false;
+ if (!idleSet.isEmpty())
+ idle = idleSet.remove(u.channel);
+ int opcode = (idle) ? EPOLL_CTL_ADD : u.opcode;
+ epollCtl(epfd, opcode, ch.getFDVal(), u.events);
+ }
}
}
}
--- a/jdk/src/solaris/classes/sun/nio/ch/EPollSelectorImpl.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/solaris/classes/sun/nio/ch/EPollSelectorImpl.java Fri Apr 03 10:29:13 2009 -0700
@@ -139,7 +139,6 @@
FileDispatcherImpl.closeIntFD(fd0);
FileDispatcherImpl.closeIntFD(fd1);
- pollWrapper.release(fd0);
pollWrapper.closeEPollFD();
// it is possible
selectedKeys = null;
@@ -162,17 +161,18 @@
protected void implRegister(SelectionKeyImpl ski) {
if (closed)
throw new ClosedSelectorException();
- int fd = IOUtil.fdVal(ski.channel.getFD());
- fdToKey.put(Integer.valueOf(fd), ski);
- pollWrapper.add(fd);
+ SelChImpl ch = ski.channel;
+ fdToKey.put(Integer.valueOf(ch.getFDVal()), ski);
+ pollWrapper.add(ch);
keys.add(ski);
}
protected void implDereg(SelectionKeyImpl ski) throws IOException {
assert (ski.getIndex() >= 0);
- int fd = ski.channel.getFDVal();
+ SelChImpl ch = ski.channel;
+ int fd = ch.getFDVal();
fdToKey.remove(Integer.valueOf(fd));
- pollWrapper.release(fd);
+ pollWrapper.release(ch);
ski.setIndex(-1);
keys.remove(ski);
selectedKeys.remove(ski);
@@ -185,8 +185,7 @@
void putEventOps(SelectionKeyImpl sk, int ops) {
if (closed)
throw new ClosedSelectorException();
- int fd = IOUtil.fdVal(sk.channel.getFD());
- pollWrapper.setInterest(fd, ops);
+ pollWrapper.setInterest(sk.channel, ops);
}
public Selector wakeup() {
--- a/jdk/src/solaris/native/sun/nio/ch/DevPollArrayWrapper.c Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/solaris/native/sun/nio/ch/DevPollArrayWrapper.c Fri Apr 03 10:29:13 2009 -0700
@@ -28,6 +28,7 @@
#include "jvm.h"
#include "jlong.h"
#include "sun_nio_ch_DevPollArrayWrapper.h"
+#include "java_lang_Integer.h"
#include <sys/poll.h>
#include <sys/resource.h>
#include <unistd.h>
@@ -192,7 +193,11 @@
JNU_ThrowIOExceptionWithLastError(env,
"getrlimit failed");
}
- return (jint)rlp.rlim_max;
+ if (rlp.rlim_max < 0 || rlp.rlim_max > java_lang_Integer_MAX_VALUE) {
+ return java_lang_Integer_MAX_VALUE;
+ } else {
+ return (jint)rlp.rlim_max;
+ }
}
JNIEXPORT void JNICALL
--- a/jdk/src/windows/native/java/io/WinNTFileSystem_md.c Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/windows/native/java/io/WinNTFileSystem_md.c Fri Apr 03 10:29:13 2009 -0700
@@ -309,12 +309,13 @@
/* No template file */
NULL);
if (h != INVALID_HANDLE_VALUE) {
- GetFileTime(h, NULL, NULL, &t);
+ if (GetFileTime(h, NULL, NULL, &t)) {
+ modTime.LowPart = (DWORD) t.dwLowDateTime;
+ modTime.HighPart = (LONG) t.dwHighDateTime;
+ rv = modTime.QuadPart / 10000;
+ rv -= 11644473600000;
+ }
CloseHandle(h);
- modTime.LowPart = (DWORD) t.dwLowDateTime;
- modTime.HighPart = (LONG) t.dwHighDateTime;
- rv = modTime.QuadPart / 10000;
- rv -= 11644473600000;
}
free(pathbuf);
return rv;
--- a/jdk/src/windows/native/java/lang/java_props_md.c Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/windows/native/java/lang/java_props_md.c Fri Apr 03 10:29:13 2009 -0700
@@ -717,6 +717,7 @@
* Windows Vista family 6 0
* Windows 2008 6 0
* where ((&ver.wServicePackMinor) + 2) = 1
+ * Windows 7 6 1
*
* This mapping will presumably be augmented as new Windows
* versions are released.
@@ -773,13 +774,18 @@
* and Windows Vista are identical, you must also test
* whether the wProductType member is VER_NT_WORKSTATION.
* If wProductType is VER_NT_WORKSTATION, the operating
- * system is Windows Vista; otherwise, it is Windows
+ * system is Windows Vista or 7; otherwise, it is Windows
* Server 2008."
*/
- if (ver.wProductType == VER_NT_WORKSTATION)
- sprops.os_name = "Windows Vista";
- else
+ if (ver.wProductType == VER_NT_WORKSTATION) {
+ switch (ver.dwMinorVersion) {
+ case 0: sprops.os_name = "Windows Vista"; break;
+ case 1: sprops.os_name = "Windows 7"; break;
+ default: sprops.os_name = "Windows NT (unknown)";
+ }
+ } else {
sprops.os_name = "Windows Server 2008";
+ }
} else {
sprops.os_name = "Windows NT (unknown)";
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/Integer/ValueOf.java Fri Apr 03 10:29:13 2009 -0700
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/**
+ * @test
+ * @bug 6807702
+ * @summary Basic test for Integer.valueOf
+ * @run main ValueOf
+ * @run main/othervm -esa -XX:+AggressiveOpts ValueOf
+ */
+
+public class ValueOf {
+
+ // test Integer.valueOf over this range (inclusive)
+ private static final int TEST_LOW = -1024;
+ private static final int TEST_HIGH = 24576;
+
+ public static void main(String[] args) {
+ int i = TEST_LOW;
+ while (i <= TEST_HIGH) {
+ // check that valueOf stores i
+ if (Integer.valueOf(i).intValue() != i)
+ throw new RuntimeException();
+
+ // check that the same object is returned for integral values
+ // in the range -128 to 127 (inclusive)
+ if (i >= -128 && i <= 127) {
+ if (Integer.valueOf(i) != Integer.valueOf(i))
+ throw new RuntimeException();
+ }
+
+ i++;
+ }
+ }
+}
--- a/jdk/test/java/nio/channels/DatagramChannel/NotBound.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/test/java/nio/channels/DatagramChannel/NotBound.java Fri Apr 03 10:29:13 2009 -0700
@@ -22,27 +22,110 @@
*/
/* @test
- * @bug 4512723
- * @summary Unit test for datagram-socket-channel adaptors
+ * @bug 4512723 6621689
+ * @summary Test that connect/send/receive with unbound DatagramChannel causes
+ * the channel's socket to be bound to a local address
*/
import java.net.*;
-import java.nio.*;
-import java.nio.channels.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.DatagramChannel;
+import java.io.IOException;
+
+public class NotBound {
+
+ static void checkBound(DatagramChannel dc) throws IOException {
+ if (dc.getLocalAddress() == null)
+ throw new RuntimeException("Not bound??");
+ }
-class NotBound {
- public static void main(String[] args) throws Exception {
- test1(false);
- test1(true);
+ // starts a thread to send a datagram to the given channel once the channel
+ // is bound to a local address
+ static void wakeupWhenBound(final DatagramChannel dc) {
+ Runnable wakeupTask = new Runnable() {
+ public void run() {
+ try {
+ // poll for local address
+ InetSocketAddress local;
+ do {
+ Thread.sleep(50);
+ local = (InetSocketAddress)dc.getLocalAddress();
+ } while (local == null);
+
+ // send message to channel to wakeup receiver
+ DatagramChannel sender = DatagramChannel.open();
+ try {
+ ByteBuffer bb = ByteBuffer.wrap("hello".getBytes());
+ InetAddress lh = InetAddress.getLocalHost();
+ SocketAddress target =
+ new InetSocketAddress(lh, local.getPort());
+ sender.send(bb, target);
+ } finally {
+ sender.close();
+ }
+
+ } catch (Exception x) {
+ x.printStackTrace();
+ }
+ }};
+ new Thread(wakeupTask).start();
}
- static void test1(boolean blocking) throws Exception {
- ByteBuffer bb = ByteBuffer.allocateDirect(256);
- DatagramChannel dc1 = DatagramChannel.open();
- dc1.configureBlocking(false);
- SocketAddress isa = dc1.receive(bb);
- if (isa != null)
- throw new Exception("Unbound dc returned non-null");
- dc1.close();
+ public static void main(String[] args) throws IOException {
+ DatagramChannel dc;
+
+ // connect
+ dc = DatagramChannel.open();
+ try {
+ DatagramChannel peer = DatagramChannel.open()
+ .bind(new InetSocketAddress(0));
+ int peerPort = ((InetSocketAddress)(peer.getLocalAddress())).getPort();
+ try {
+ dc.connect(new InetSocketAddress(InetAddress.getLocalHost(), peerPort));
+ checkBound(dc);
+ } finally {
+ peer.close();
+ }
+ } finally {
+ dc.close();
+ }
+
+ // send
+ dc = DatagramChannel.open();
+ try {
+ ByteBuffer bb = ByteBuffer.wrap("ignore this".getBytes());
+ SocketAddress target =
+ new InetSocketAddress(InetAddress.getLocalHost(), 5000);
+ dc.send(bb, target);
+ checkBound(dc);
+ } finally {
+ dc.close();
+ }
+
+ // receive (blocking)
+ dc = DatagramChannel.open();
+ try {
+ ByteBuffer bb = ByteBuffer.allocateDirect(128);
+ wakeupWhenBound(dc);
+ SocketAddress sender = dc.receive(bb);
+ if (sender == null)
+ throw new RuntimeException("Sender should not be null");
+ checkBound(dc);
+ } finally {
+ dc.close();
+ }
+
+ // receive (non-blocking)
+ dc = DatagramChannel.open();
+ try {
+ dc.configureBlocking(false);
+ ByteBuffer bb = ByteBuffer.allocateDirect(128);
+ SocketAddress sender = dc.receive(bb);
+ if (sender != null)
+ throw new RuntimeException("Sender should be null");
+ checkBound(dc);
+ } finally {
+ dc.close();
+ }
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/nio/channels/Selector/RegAfterPreClose.java Fri Apr 03 10:29:13 2009 -0700
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* @test
+ * @bug 6693490
+ * @summary Pre-close file descriptor may inadvertently get registered with
+ * epoll during close
+ */
+
+import java.net.*;
+import java.nio.channels.*;
+import java.util.concurrent.*;
+import java.util.*;
+import java.io.IOException;
+
+public class RegAfterPreClose {
+
+ static volatile boolean done;
+
+ /**
+ * A task that continuously connects to a given address and immediately
+ * closes the connection.
+ */
+ static class Connector implements Runnable {
+ private final SocketAddress sa;
+ Connector(int port) throws IOException {
+ InetAddress lh = InetAddress.getLocalHost();
+ this.sa = new InetSocketAddress(lh, port);
+ }
+ public void run() {
+ while (!done) {
+ try {
+ SocketChannel.open(sa).close();
+ } catch (IOException x) {
+ // back-off as probably resource related
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException ignore) { }
+ }
+ }
+ }
+ }
+
+ /**
+ * A task that closes a channel.
+ */
+ static class Closer implements Runnable {
+ private final Channel channel;
+ Closer(Channel sc) {
+ this.channel = sc;
+ }
+ public void run() {
+ try {
+ channel.close();
+ } catch (IOException ignore) { }
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ // create listener
+ InetSocketAddress isa = new InetSocketAddress(0);
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ ssc.socket().bind(isa);
+
+ // register with Selector
+ final Selector sel = Selector.open();
+ ssc.configureBlocking(false);
+ SelectionKey key = ssc.register(sel, SelectionKey.OP_ACCEPT);
+
+ ThreadFactory factory = new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(r);
+ t.setDaemon(true);
+ return t;
+ }
+ };
+
+ // schedule test to run for 1 minute
+ Executors.newScheduledThreadPool(1, factory).schedule(new Runnable() {
+ public void run() {
+ done = true;
+ sel.wakeup();
+ }}, 1, TimeUnit.MINUTES);
+
+ // create Executor that handles tasks that closes channels
+ // "asynchronously" - this creates the conditions to provoke the bug.
+ Executor executor = Executors.newFixedThreadPool(2, factory);
+
+ // submit task that connects to listener
+ executor.execute(new Connector(ssc.socket().getLocalPort()));
+
+ // loop accepting connections until done (or an IOException is thrown)
+ while (!done) {
+ sel.select();
+ if (key.isAcceptable()) {
+ SocketChannel sc = ssc.accept();
+ if (sc != null) {
+ sc.configureBlocking(false);
+ sc.register(sel, SelectionKey.OP_READ);
+ executor.execute(new Closer(sc));
+ }
+ }
+ sel.selectedKeys().clear();
+ }
+ }
+}
--- a/jdk/test/java/util/Collection/MOAT.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/test/java/util/Collection/MOAT.java Fri Apr 03 10:29:13 2009 -0700
@@ -555,6 +555,7 @@
NavigableMap<Integer,Integer> nm =
(NavigableMap<Integer,Integer>) m;
+ testNavigableMapRemovers(nm);
testNavigableMap(nm);
testNavigableMap(nm.headMap(6, false));
testNavigableMap(nm.headMap(5, true));
@@ -742,6 +743,97 @@
equal(it.next(), expected);
}
+ static void equalMaps(Map m1, Map m2) {
+ equal(m1, m2);
+ equal(m2, m1);
+ equal(m1.size(), m2.size());
+ equal(m1.isEmpty(), m2.isEmpty());
+ equal(m1.toString(), m2.toString());
+ check(Arrays.equals(m1.entrySet().toArray(), m2.entrySet().toArray()));
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ static void testNavigableMapRemovers(NavigableMap m)
+ {
+ final Map emptyMap = new HashMap();
+
+ final Map singletonMap = new HashMap();
+ singletonMap.put(1, 2);
+
+ abstract class NavigableMapView {
+ abstract NavigableMap view(NavigableMap m);
+ }
+
+ NavigableMapView[] views = {
+ new NavigableMapView() { NavigableMap view(NavigableMap m) {
+ return m; }},
+ new NavigableMapView() { NavigableMap view(NavigableMap m) {
+ return m.headMap(99, true); }},
+ new NavigableMapView() { NavigableMap view(NavigableMap m) {
+ return m.tailMap(-99, false); }},
+ new NavigableMapView() { NavigableMap view(NavigableMap m) {
+ return m.subMap(-99, true, 99, false); }},
+ };
+
+ abstract class Remover {
+ abstract void remove(NavigableMap m, Object k, Object v);
+ }
+
+ Remover[] removers = {
+ new Remover() { void remove(NavigableMap m, Object k, Object v) {
+ equal(m.remove(k), v); }},
+
+ new Remover() { void remove(NavigableMap m, Object k, Object v) {
+ equal(m.descendingMap().remove(k), v); }},
+ new Remover() { void remove(NavigableMap m, Object k, Object v) {
+ equal(m.descendingMap().headMap(-86, false).remove(k), v); }},
+ new Remover() { void remove(NavigableMap m, Object k, Object v) {
+ equal(m.descendingMap().tailMap(86, true).remove(k), v); }},
+
+ new Remover() { void remove(NavigableMap m, Object k, Object v) {
+ equal(m.headMap(86, true).remove(k), v); }},
+ new Remover() { void remove(NavigableMap m, Object k, Object v) {
+ equal(m.tailMap(-86, true).remove(k), v); }},
+ new Remover() { void remove(NavigableMap m, Object k, Object v) {
+ equal(m.subMap(-86, false, 86, true).remove(k), v); }},
+
+ new Remover() { void remove(NavigableMap m, Object k, Object v) {
+ check(m.keySet().remove(k)); }},
+ new Remover() { void remove(NavigableMap m, Object k, Object v) {
+ check(m.navigableKeySet().remove(k)); }},
+
+ new Remover() { void remove(NavigableMap m, Object k, Object v) {
+ check(m.navigableKeySet().headSet(86, true).remove(k)); }},
+ new Remover() { void remove(NavigableMap m, Object k, Object v) {
+ check(m.navigableKeySet().tailSet(-86, false).remove(k)); }},
+ new Remover() { void remove(NavigableMap m, Object k, Object v) {
+ check(m.navigableKeySet().subSet(-86, true, 86, false)
+ .remove(k)); }},
+
+ new Remover() { void remove(NavigableMap m, Object k, Object v) {
+ check(m.descendingKeySet().headSet(-86, false).remove(k)); }},
+ new Remover() { void remove(NavigableMap m, Object k, Object v) {
+ check(m.descendingKeySet().tailSet(86, true).remove(k)); }},
+ new Remover() { void remove(NavigableMap m, Object k, Object v) {
+ check(m.descendingKeySet().subSet(86, true, -86, false)
+ .remove(k)); }},
+ };
+
+ for (NavigableMapView view : views) {
+ for (Remover remover : removers) {
+ try {
+ m.clear();
+ equalMaps(m, emptyMap);
+ equal(m.put(1, 2), null);
+ equalMaps(m, singletonMap);
+ NavigableMap v = view.view(m);
+ remover.remove(v, 1, 2);
+ equalMaps(m, emptyMap);
+ } catch (Throwable t) { unexpected(t); }
+ }
+ }
+ }
+
private static void testNavigableMap(NavigableMap<Integer,Integer> m)
{
clear(m);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/concurrent/Semaphore/RacingReleases.java Fri Apr 03 10:29:13 2009 -0700
@@ -0,0 +1,116 @@
+/*
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/licenses/publicdomain
+ */
+
+/*
+ * @test
+ * @bug 6801020 6803402
+ * @summary Try to tickle race conditions in
+ * AbstractQueuedSynchronizer "shared" code
+ */
+
+import java.util.concurrent.Semaphore;
+
+public class RacingReleases {
+
+ /** Increase this for better chance of tickling races */
+ static final int iterations = 1000;
+
+ public static void test(final boolean fair,
+ final boolean interruptibly)
+ throws Throwable {
+ for (int i = 0; i < iterations; i++) {
+ final Semaphore sem = new Semaphore(0, fair);
+ final Throwable[] badness = new Throwable[1];
+ Runnable blocker = interruptibly ?
+ new Runnable() {
+ public void run() {
+ try {
+ sem.acquire();
+ } catch (Throwable t) {
+ badness[0] = t;
+ throw new Error(t);
+ }}}
+ :
+ new Runnable() {
+ public void run() {
+ try {
+ sem.acquireUninterruptibly();
+ } catch (Throwable t) {
+ badness[0] = t;
+ throw new Error(t);
+ }}};
+
+ Thread b1 = new Thread(blocker);
+ Thread b2 = new Thread(blocker);
+ Runnable signaller = new Runnable() {
+ public void run() {
+ try {
+ sem.release();
+ } catch (Throwable t) {
+ badness[0] = t;
+ throw new Error(t);
+ }}};
+ Thread s1 = new Thread(signaller);
+ Thread s2 = new Thread(signaller);
+ Thread[] threads = { b1, b2, s1, s2 };
+ java.util.Collections.shuffle(java.util.Arrays.asList(threads));
+ for (Thread thread : threads)
+ thread.start();
+ for (Thread thread : threads) {
+ thread.join(60 * 1000);
+ if (thread.isAlive())
+ throw new Error
+ (String.format
+ ("Semaphore stuck: permits %d, thread waiting %s%n",
+ sem.availablePermits(),
+ sem.hasQueuedThreads() ? "true" : "false"));
+ }
+ if (badness[0] != null)
+ throw new Error(badness[0]);
+ if (sem.availablePermits() != 0)
+ throw new Error(String.valueOf(sem.availablePermits()));
+ if (sem.hasQueuedThreads())
+ throw new Error(String.valueOf(sem.hasQueuedThreads()));
+ if (sem.getQueueLength() != 0)
+ throw new Error(String.valueOf(sem.getQueueLength()));
+ if (sem.isFair() != fair)
+ throw new Error(String.valueOf(sem.isFair()));
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ for (boolean fair : new boolean[] { true, false })
+ for (boolean interruptibly : new boolean[] { true, false })
+ test(fair, interruptibly);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/zip/LargeZip.java Fri Apr 03 10:29:13 2009 -0700
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.util.*;
+import java.util.zip.*;
+
+public class LargeZip {
+ // If true, don't delete large ZIP file created for test.
+ static final boolean debug = System.getProperty("debug") != null;
+
+ //static final int DATA_LEN = 1024 * 1024;
+ static final int DATA_LEN = 80 * 1024;
+ static final int DATA_SIZE = 8;
+
+ static long fileSize = 6L * 1024L * 1024L * 1024L; // 6GB
+
+ static boolean userFile = false;
+
+ static byte[] data;
+ static File largeFile;
+ static String lastEntryName;
+
+ /* args can be empty, in which case check a 3 GB file which is created for
+ * this test (and then deleted). Or it can be a number, in which case
+ * that designates the size of the file that's created for this test (and
+ * then deleted). Or it can be the name of a file to use for the test, in
+ * which case it is *not* deleted. Note that in this last case, the data
+ * comparison might fail.
+ */
+ static void realMain (String[] args) throws Throwable {
+ if (args.length > 0) {
+ try {
+ fileSize = Long.parseLong(args[0]);
+ System.out.println("Testing with file of size " + fileSize);
+ } catch (NumberFormatException ex) {
+ largeFile = new File(args[0]);
+ if (!largeFile.exists()) {
+ throw new Exception("Specified file " + args[0] + " does not exist");
+ }
+ userFile = true;
+ System.out.println("Testing with user-provided file " + largeFile);
+ }
+ }
+ File testDir = null;
+ if (largeFile == null) {
+ testDir = new File(System.getProperty("test.scratch", "."),
+ "LargeZip");
+ if (testDir.exists()) {
+ if (!testDir.delete()) {
+ throw new Exception("Cannot delete already-existing test directory");
+ }
+ }
+ check(!testDir.exists() && testDir.mkdirs());
+ largeFile = new File(testDir, "largezip.zip");
+ createLargeZip();
+ }
+
+ readLargeZip1();
+ readLargeZip2();
+
+ if (!userFile && !debug) {
+ check(largeFile.delete());
+ check(testDir.delete());
+ }
+ }
+
+ static void createLargeZip() throws Throwable {
+ int iterations = DATA_LEN / DATA_SIZE;
+ ByteBuffer bb = ByteBuffer.allocate(DATA_SIZE);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for (int i = 0; i < iterations; i++) {
+ bb.putDouble(0, Math.random());
+ baos.write(bb.array(), 0, DATA_SIZE);
+ }
+ data = baos.toByteArray();
+
+ ZipOutputStream zos = new ZipOutputStream(
+ new BufferedOutputStream(new FileOutputStream(largeFile)));
+ long length = 0;
+ while (length < fileSize) {
+ ZipEntry ze = new ZipEntry("entry-" + length);
+ lastEntryName = ze.getName();
+ zos.putNextEntry(ze);
+ zos.write(data, 0, data.length);
+ zos.closeEntry();
+ length = largeFile.length();
+ }
+ System.out.println("Last entry written is " + lastEntryName);
+ zos.close();
+ }
+
+ static void readLargeZip1() throws Throwable {
+ ZipFile zipFile = new ZipFile(largeFile);
+ ZipEntry entry = null;
+ String entryName = null;
+ int count = 0;
+ Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ entry = entries.nextElement();
+ entryName = entry.getName();
+ count++;
+ }
+ System.out.println("Number of entries read: " + count);
+ System.out.println("Last entry read is " + entryName);
+ check(!entry.isDirectory());
+ if (check(entryName.equals(lastEntryName))) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ InputStream is = zipFile.getInputStream(entry);
+ byte buf[] = new byte[4096];
+ int len;
+ while ((len = is.read(buf)) >= 0) {
+ baos.write(buf, 0, len);
+ }
+ baos.close();
+ is.close();
+ check(Arrays.equals(data, baos.toByteArray()));
+ }
+ }
+
+
+ static void readLargeZip2() throws Throwable {
+ ZipInputStream zis = new ZipInputStream(
+ new BufferedInputStream(new FileInputStream(largeFile)));
+ ZipEntry entry = null;
+ String entryName = null;
+ int count = 0;
+ while ((entry = zis.getNextEntry()) != null) {
+ entryName = entry.getName();
+ if (entryName.equals(lastEntryName)) {
+ break;
+ }
+ count++;
+ }
+ System.out.println("Number of entries read: " + count);
+ System.out.println("Last entry read is " + entryName);
+ check(!entry.isDirectory());
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ byte buf[] = new byte[4096];
+ int len;
+ while ((len = zis.read(buf)) >= 0) {
+ baos.write(buf, 0, len);
+ }
+ baos.close();
+ check(Arrays.equals(data, baos.toByteArray()));
+ check(zis.getNextEntry() == null);
+ zis.close();
+ }
+
+
+ //--------------------- Infrastructure ---------------------------
+ static volatile int passed = 0, failed = 0;
+ static void pass() {passed++;}
+ static void pass(String msg) {System.out.println(msg); passed++;}
+ static void fail() {failed++; Thread.dumpStack();}
+ static void fail(String msg) {System.out.println(msg); fail();}
+ static void unexpected(Throwable t) {failed++; t.printStackTrace();}
+ static void unexpected(Throwable t, String msg) {
+ System.out.println(msg); failed++; t.printStackTrace();}
+ static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
+ static void equal(Object x, Object y) {
+ if (x == null ? y == null : x.equals(y)) pass();
+ else fail(x + " not equal to " + y);}
+ public static void main(String[] args) throws Throwable {
+ try {realMain(args);} catch (Throwable t) {unexpected(t);}
+ System.out.println("\nPassed = " + passed + " failed = " + failed);
+ if (failed > 0) throw new AssertionError("Some tests failed");}
+}
--- a/jdk/test/java/util/zip/ZipFile/LargeZipFile.java Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/test/java/util/zip/ZipFile/LargeZipFile.java Fri Apr 03 10:29:13 2009 -0700
@@ -158,4 +158,3 @@
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/tools/jarsigner/concise_jarsigner.sh Fri Apr 03 10:29:13 2009 -0700
@@ -0,0 +1,200 @@
+#
+# Copyright 2009 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+# CA 95054 USA or visit www.sun.com if you need additional information or
+# have any questions.
+#
+
+# @test
+# @bug 6802846
+# @summary jarsigner needs enhanced cert validation(options)
+#
+# @run shell concise_jarsigner.sh
+#
+
+if [ "${TESTJAVA}" = "" ] ; then
+ JAVAC_CMD=`which javac`
+ TESTJAVA=`dirname $JAVAC_CMD`/..
+fi
+
+# set platform-dependent variables
+OS=`uname -s`
+case "$OS" in
+ Windows_* )
+ FS="\\"
+ ;;
+ * )
+ FS="/"
+ ;;
+esac
+
+KT="$TESTJAVA${FS}bin${FS}keytool -storepass changeit -keypass changeit -keystore js.jks"
+JAR=$TESTJAVA${FS}bin${FS}jar
+JARSIGNER=$TESTJAVA${FS}bin${FS}jarsigner
+JAVAC=$TESTJAVA${FS}bin${FS}javac
+
+rm js.jks
+
+echo class A1 {} > A1.java
+echo class A2 {} > A2.java
+echo class A3 {} > A3.java
+echo class A4 {} > A4.java
+echo class A5 {} > A5.java
+echo class A6 {} > A6.java
+
+$JAVAC A1.java A2.java A3.java A4.java A5.java A6.java
+YEAR=`date +%Y`
+
+# ==========================================================
+# First part: output format
+# ==========================================================
+
+$KT -genkeypair -alias a1 -dname CN=a1 -validity 365
+$KT -genkeypair -alias a2 -dname CN=a2 -validity 365
+
+# a.jar includes 8 unsigned, 2 signed by a1 and a2, 2 signed by a3
+$JAR cvf a.jar A1.class A2.class
+$JARSIGNER -keystore js.jks -storepass changeit a.jar a1
+$JAR uvf a.jar A3.class A4.class
+$JARSIGNER -keystore js.jks -storepass changeit a.jar a2
+$JAR uvf a.jar A5.class A6.class
+
+# Verify OK
+$JARSIGNER -verify a.jar
+[ $? = 0 ] || exit $LINENO
+
+# 4(chainNotValidated)+16(hasUnsignedEntry)+32(aliasNotInStore)
+$JARSIGNER -verify a.jar -strict
+[ $? = 52 ] || exit $LINENO
+
+# 16(hasUnsignedEntry)
+$JARSIGNER -verify a.jar -strict -keystore js.jks
+[ $? = 16 ] || exit $LINENO
+
+# 16(hasUnsignedEntry)+32(notSignedByAlias)
+$JARSIGNER -verify a.jar a1 -strict -keystore js.jks
+[ $? = 48 ] || exit $LINENO
+
+# 16(hasUnsignedEntry)
+$JARSIGNER -verify a.jar a1 a2 -strict -keystore js.jks
+[ $? = 16 ] || exit $LINENO
+
+# 12 entries all together
+LINES=`$JARSIGNER -verify a.jar -verbose | grep $YEAR | wc -l`
+[ $LINES = 12 ] || exit $LINENO
+
+# 12 entries all listed
+LINES=`$JARSIGNER -verify a.jar -verbose:grouped | grep $YEAR | wc -l`
+[ $LINES = 12 ] || exit $LINENO
+
+# 3 groups: unrelated, signed, unsigned
+LINES=`$JARSIGNER -verify a.jar -verbose:summary | grep $YEAR | wc -l`
+[ $LINES = 3 ] || exit $LINENO
+
+# 4 groups: unrelated, signed by a1/a2, signed by a2, unsigned
+LINES=`$JARSIGNER -verify a.jar -verbose:summary -certs | grep $YEAR | wc -l`
+[ $LINES = 4 ] || exit $LINENO
+
+# 2*2 for A1/A2, 2 for A3/A4
+LINES=`$JARSIGNER -verify a.jar -verbose -certs | grep "\[certificate" | wc -l`
+[ $LINES = 6 ] || exit $LINENO
+
+# a1,a2 for A1/A2, a2 for A3/A4
+LINES=`$JARSIGNER -verify a.jar -verbose:grouped -certs | grep "\[certificate" | wc -l`
+[ $LINES = 3 ] || exit $LINENO
+
+# a1,a2 for A1/A2, a2 for A3/A4
+LINES=`$JARSIGNER -verify a.jar -verbose:summary -certs | grep "\[certificate" | wc -l`
+[ $LINES = 3 ] || exit $LINENO
+
+# 4 groups
+LINES=`$JARSIGNER -verify a.jar -verbose:summary -certs | grep "more)" | wc -l`
+[ $LINES = 4 ] || exit $LINENO
+
+# ==========================================================
+# Second part: exit code 2, 4, 8
+# 16 and 32 already covered in the first part
+# ==========================================================
+
+$KT -genkeypair -alias expiring -dname CN=expiring -startdate -1m
+$KT -genkeypair -alias expired -dname CN=expired -startdate -10m
+$KT -genkeypair -alias notyetvalid -dname CN=notyetvalid -startdate +1m
+$KT -genkeypair -alias badku -dname CN=badku -ext KU=cRLSign -validity 365
+$KT -genkeypair -alias badeku -dname CN=badeku -ext EKU=sa -validity 365
+$KT -genkeypair -alias goodku -dname CN=goodku -ext KU=dig -validity 365
+$KT -genkeypair -alias goodeku -dname CN=goodeku -ext EKU=codesign -validity 365
+
+# badchain signed by ca, but ca is removed later
+$KT -genkeypair -alias badchain -dname CN=badchain -validity 365
+$KT -genkeypair -alias ca -dname CN=ca -ext bc -validity 365
+$KT -certreq -alias badchain | $KT -gencert -alias ca -validity 365 | \
+ $KT -importcert -alias badchain
+$KT -delete -alias ca
+
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar expiring
+[ $? = 2 ] || exit $LINENO
+
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar expired
+[ $? = 4 ] || exit $LINENO
+
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar notyetvalid
+[ $? = 4 ] || exit $LINENO
+
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar badku
+[ $? = 8 ] || exit $LINENO
+
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar badeku
+[ $? = 8 ] || exit $LINENO
+
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar goodku
+[ $? = 0 ] || exit $LINENO
+
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar goodeku
+[ $? = 0 ] || exit $LINENO
+
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar badchain
+[ $? = 4 ] || exit $LINENO
+
+$JARSIGNER -verify a.jar
+[ $? = 0 ] || exit $LINENO
+
+# ==========================================================
+# Third part: -certchain test
+# ==========================================================
+
+# altchain signed by ca2, but ca2 is removed later
+$KT -genkeypair -alias altchain -dname CN=altchain -validity 365
+$KT -genkeypair -alias ca2 -dname CN=ca2 -ext bc -validity 365
+$KT -certreq -alias altchain | $KT -gencert -alias ca2 -validity 365 -rfc > certchain
+$KT -exportcert -alias ca2 -rfc >> certchain
+$KT -delete -alias ca2
+
+# Now altchain is still self-signed
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar altchain
+[ $? = 0 ] || exit $LINENO
+
+# If -certchain is used, then it's bad
+$JARSIGNER -strict -keystore js.jks -storepass changeit -certchain certchain a.jar altchain
+[ $? = 4 ] || exit $LINENO
+
+$JARSIGNER -verify a.jar
+[ $? = 0 ] || exit $LINENO
+
+echo OK
+exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/tools/keytool/selfissued.sh Fri Apr 03 10:29:13 2009 -0700
@@ -0,0 +1,69 @@
+#
+# Copyright 2009 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+# CA 95054 USA or visit www.sun.com if you need additional information or
+# have any questions.
+#
+
+# @test
+# @bug 6825352
+# @summary support self-issued certificate in keytool
+#
+# @run shell selfissued.sh
+#
+
+if [ "${TESTJAVA}" = "" ] ; then
+ JAVAC_CMD=`which javac`
+ TESTJAVA=`dirname $JAVAC_CMD`/..
+fi
+
+# set platform-dependent variables
+OS=`uname -s`
+case "$OS" in
+ Windows_* )
+ FS="\\"
+ ;;
+ * )
+ FS="/"
+ ;;
+esac
+
+KS=selfsigned.jks
+KT="$TESTJAVA${FS}bin${FS}keytool -storepass changeit -keypass changeit -keystore $KS"
+
+rm $KS
+
+$KT -alias ca -dname CN=CA -genkeypair
+$KT -alias me -dname CN=CA -genkeypair
+$KT -alias e1 -dname CN=E1 -genkeypair
+$KT -alias e2 -dname CN=E2 -genkeypair
+
+# me signed by ca, self-issued
+$KT -alias me -certreq | $KT -alias ca -gencert | $KT -alias me -importcert
+
+# Import e1 signed by me, should add me and ca
+$KT -alias e1 -certreq | $KT -alias me -gencert | $KT -alias e1 -importcert
+$KT -alias e1 -list -v | grep '\[3\]' || { echo Bad E1; exit 1; }
+
+# Import (e2 signed by me,ca,me), should reorder to (e2,me,ca)
+( $KT -alias e2 -certreq | $KT -alias me -gencert; $KT -exportcert -alias ca; $KT -exportcert -alias me ) | $KT -alias e2 -importcert
+$KT -alias e2 -list -v | grep '\[3\]' || { echo Bad E2; exit 1; }
+
+echo Good
+