--- /dev/null 2014-05-19 10:02:58.886376731 -0700 +++ new/make/src/classes/build/tools/tzdb/TarInputStream.java 2014-05-19 10:47:36.000000000 -0700 @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package build.tools.tzdb; + +import java.io.File; +import java.io.IOException; +import java.io.FilterInputStream; +import java.io.InputStream; + +/** + * This class implements an input stream filter for reading files in the + * UNIX tar file format. + * + * @implNote + * This class does not handle symbol link, gnu long name and check checksum. + * + * @author Xueming Shen + * + * @since 1.9 + */ +class TarInputStream extends FilterInputStream { + + /** Default block size */ + private static final int DEFAULT_BLKSIZE = 512; + + private int blockSize; + private TarEntry entry; // the current entry + private long remaining; // the remaining bytes in current entry + + private boolean closed; + + /** + * Creates a new Tar input stream. + * + * @param is the source input stream to use + */ + public TarInputStream(InputStream is) { + this(is, DEFAULT_BLKSIZE); + } + + /** + * Creates a new Tar input stream. + * + * @param is the input stream to use + * @param blockSize the block size to use + */ + public TarInputStream(InputStream is, int blockSize) { + super(is); + this.blockSize = blockSize; + this.remaining = 0; + this.closed = false; + } + + /** + * Check to make sure that this stream has not been closed + */ + private void ensureOpen() throws IOException { + if (closed) { + throw new IOException("Input stream closed"); + } + } + + /** + * Closes this input stream and releases any system resource associated. + * + * @throws IOException if an on error has occured + */ + @Override + public void close() throws IOException { + if (!closed) { + super.close(); + closed = true; + } + } + + private boolean isEOF(byte[] block) { + for (int i = 0; i < block.length; i++) { + if (block[i] != 0) { + return false; + } + } + return true; + } + + /** + * Skip the specified number of bytes in the current Tar entry. + * + * @param n the number of bytes to skip. + * @return the number actually skipped + * @throws IOException if an IO error has occurred + * @throws IllegalArgumentException if {@code n < 0} + */ + @Override + public long skip(long n) throws IOException { + if (n < 0) { + throw new IllegalArgumentException("negative skip length"); + } + ensureOpen(); + int max = (int)Math.min(n, Integer.MAX_VALUE); + byte[] tmpbuf = new byte[Math.min(max, 8192)]; + int total = 0; + while (total < max) { + int len = max - total; + if (len > tmpbuf.length) { + len = tmpbuf.length; + } + len = read(tmpbuf, 0, len); + if (len == -1) { + break; + } + total += len; + } + return total; + } + + /** + * Reads the next Tar file entry and positions the stream at + * the beginning of the entry data. + * + * @return the next Tar file entry, or null, if there are no + * more entries + * @throws IOException if an I/O error has occured + */ + public TarEntry getNextEntry() throws IOException { + ensureOpen(); + if (entry != null) { + closeEntry(); + } + byte[] header = new byte[blockSize]; + int off = 0; + // read until we get blockSize of bytes or EOF + while (off < blockSize) { + int n = super.read(header, off, blockSize - off); + if (n == -1) { // unexpected EOF, + return null; + } + off += n; + } + if (isEOF(header)) { + return null; + } + entry = new TarEntry(header); + remaining = entry.getSize(); + return entry; + } + + /** + * Closes the current Tar entry and positions the stream for reading + * the next entry. + * + * @exception IOException if an I/O error has occurred + */ + public void closeEntry() throws IOException { + ensureOpen(); + byte[] tmpbuf = new byte[blockSize]; + while (read(tmpbuf, 0, tmpbuf.length) != -1) ; + } + + /** + * Returns the available data that can be read from the current entry + * data. + * + * @return the number of available bytes for the current entry, 0 + * if EOF of current entry has reached, or Integer.MAX_VALUE, + * if more than Integer.MAX_VALUE is available + * @exception IOException if an I/O error occurs. + * + */ + public int available() throws IOException { + ensureOpen(); + if (remaining > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + return (int)remaining; + } + + /** + * Reads bytes from the current Tar entry. + * + * If {@code len} is not zero, this method blocks until some input is + * available; otherwise, no bytes are read and [@code 0} is returned. + * + * @param b the buffer into which the data is read + * @param off the start offset in the destination array b + * @param len the maximum number of bytes read + * @return the actual number of bytes read, or -1 if the end of the + * entry is reached + * @throws IOException on error + */ + @Override + public int read(byte[] b, int off, int len) throws IOException { + ensureOpen(); + if (off < 0 || len < 0 || off > b.length - len) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + if (entry == null && remaining <= 0) { + return -1; + } + if (len > remaining) { + len = (int)remaining; + } + int n = super.read(b, off, len); + if (n == -1) { + entry = null; + return -1; // throw new IOException("unexpected EOF"); + } + remaining -= n; + if (remaining == 0) { + int leftover = blockSize - (int)(entry.getSize() % blockSize); + if (leftover > 0) { + byte[] buf = new byte[leftover]; + while (leftover > 0) { + int m = super.read(buf, 0, leftover); + if (m == -1) { + break; + } + leftover -= m; + } + } + entry = null; + } + return n; + } + + public static class TarEntry { + /* + * The C structure for a Tar Entry's header is: + *
+         * struct header {
+         * char name[100];
+         * char mode[8];
+         * char uid[8];
+         * char gid[8];
+         * char size[12];
+         * char mtime[12];
+         * char chksum[8];
+         * char linkflag;
+         * char linkname[100];
+         * char magic[8];
+         * char uname[32];
+         * char gname[32];
+         * char devmajor[8];
+         * char devminor[8];
+         * } header;
+         * 
+ * + */ + private static final int NAME_OFF = 0; + private static final int NAME_LEN = 100; + /* + private static final int MODE_OFF = 100; + private static final int MODE_LEN = 8; + private static final int UID_OFF = 108; + private static final int UID_LEN = 8; + private static final int GID_OFF = 116; + private static final int GID_LEN = 8; + */ + private static final int SIZE_OFF = 124; + private static final int SIZE_LEN = 12; + /* + private static final int MTIME_OFF = 136; + private static final int MTIME_LEN = 12; + private static final int CHECKSUM_OFF = 148; + private static final int CHECKSUM_LEN = 8; + private static final int TYPEFLAG_OFF = 156; + private static final int TYPEFLAG_LEN = 1; + private static final int LINKNAME_OFF = 157; + private static final int LINKNAME_LEN = 100; + */ + private static final int MAGIC_OFF = 257; + private static final int MAGIC_LEN = 6; + /* + private static final int VERSION_OFF = 263; + private static final int VERSION_LEN = 2; + private static final int UNAME_OFF = 265; + private static final int UNAME_LEN = 32; + private static final int GNAME_OFF = 297; + private static final int GNAME_LEN = 32; + private static final int DEVMAJOR_OFF = 329; + private static final int DEVMAJOR_LEN = 8; + private static final int DEVMINOR_OFF = 337; + private static final int DEVMINOR_LEN = 8; + private static final int PREFIX_OFF = 345; + private static final int PREFIX_LEN = 155; + */ + // The maximum size of a file in a tar archive. + private static final long MAXSIZE = 077777777777L; + + //private byte[] header; + private String name; + private long size; + + /** + * Construct an entry from an archive's header bytes. + * + * @param headerBuf The header bytes from a tar archive entry. + */ + public TarEntry(byte[] header) { + //this.header = header; + this.name = toString(header, NAME_OFF, NAME_LEN); + this.size = toNumber(header, SIZE_OFF, SIZE_LEN); + // verify magic and size + String magic = toString(header, MAGIC_OFF, MAGIC_LEN); + if (!magic.startsWith("ustar") || + size < 0 || size > MAXSIZE) { + throw new IllegalArgumentException("Illegal tar entry: magic=" + + magic + ", size=" + size); + } + } + + public long getSize() { + return size; + } + + public String getName() { + return name; + } + + private static long toNumber(byte[] header, int off, int len) { + long ret = 0; + int limit = off + len; + int b; + while (off < limit && ((b = header[off]) == ' ' || b == '0')) { + off++; + } + while (off < limit && ((b = header[off++]) >= '0' && b < '8')) { + ret = (ret << 3) + (b - '0'); + } + return ret; + } + + private static String toString(byte[] header, int off, int len) { + int off0 = off; + int limit = off + len; + while (off < limit && header[off] != 0) { + off++; + } + return new String(header, off0, off - off0); + } + + } +}