一、前言
基于apache的commons-compress包中的org.apache.commons.compress.archivers.tar.TarUtils打包工具类对文件进行加解密、并对加密包文件解析解析parseOctal/parseBinaryLong/parseBinaryBigInteger/parseName、对文件包字节数据进行格式化formatNameBytes/formatUnsignedOctalString/formatLongBinary/formatBigIntegerBinary/formatCheckSumOctalBytes等操作。
二、源码说明
package org.apache.commons.compress.archivers.tar;@b@@b@import java.io.IOException;@b@import java.math.BigInteger;@b@import java.nio.ByteBuffer;@b@import org.apache.commons.compress.archivers.zip.ZipEncoding;@b@import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;@b@@b@public class TarUtils@b@{@b@ private static final int BYTE_MASK = 255;@b@ static final ZipEncoding DEFAULT_ENCODING = ZipEncodingHelper.getZipEncoding(null);@b@ static final ZipEncoding FALLBACK_ENCODING = new ZipEncoding() {@b@ public boolean canEncode(String name) { return true; }@b@@b@ public ByteBuffer encode(String name) {@b@ int length = name.length();@b@ byte[] buf = new byte[length];@b@@b@ for (int i = 0; i < length; ++i)@b@ buf[i] = (byte)name.charAt(i);@b@@b@ return ByteBuffer.wrap(buf);@b@ }@b@@b@ public String decode(byte[] buffer) {@b@ int length = buffer.length;@b@ StringBuffer result = new StringBuffer(length);@b@@b@ for (int i = 0; i < length; ++i) {@b@ byte b = buffer[i];@b@ if (b == 0)@b@ break;@b@@b@ result.append((char)(b & 0xFF));@b@ }@b@@b@ return result.toString();@b@ }@b@ };@b@@b@ public static long parseOctal(byte[] buffer, int offset, int length)@b@ {@b@ long result = 0L;@b@ int end = offset + length;@b@ int start = offset;@b@@b@ if (length < 2) {@b@ throw new IllegalArgumentException("Length " + length + " must be at least 2");@b@ }@b@@b@ if (buffer[start] == 0) {@b@ return 0L;@b@ }@b@@b@ while ((start < end) && @b@ (buffer[start] == 32)) {@b@ ++start;@b@ }@b@@b@ byte trailer = buffer[(end - 1)];@b@ if ((trailer == 0) || (trailer == 32))@b@ --end;@b@ else {@b@ throw new IllegalArgumentException(exceptionMessage(buffer, offset, length, end - 1, trailer));@b@ }@b@@b@ trailer = buffer[(end - 1)];@b@ while ((start < end - 1) && (((trailer == 0) || (trailer == 32)))) {@b@ --end;@b@ trailer = buffer[(end - 1)];@b@ }@b@@b@ for (; start < end; ++start) {@b@ byte currentByte = buffer[start];@b@@b@ if ((currentByte < 48) || (currentByte > 55)) {@b@ throw new IllegalArgumentException(exceptionMessage(buffer, offset, length, start, currentByte));@b@ }@b@@b@ result = (result << 3) + currentByte - 48;@b@ }@b@@b@ return result;@b@ }@b@@b@ public static long parseOctalOrBinary(byte[] buffer, int offset, int length)@b@ {@b@ if ((buffer[offset] & 0x80) == 0)@b@ return parseOctal(buffer, offset, length);@b@@b@ boolean negative = buffer[offset] == -1;@b@ if (length < 9)@b@ return parseBinaryLong(buffer, offset, length, negative);@b@@b@ return parseBinaryBigInteger(buffer, offset, length, negative);@b@ }@b@@b@ private static long parseBinaryLong(byte[] buffer, int offset, int length, boolean negative)@b@ {@b@ if (length >= 9) {@b@ throw new IllegalArgumentException("At offset " + offset + ", " + length + " byte binary number" + " exceeds maximum signed long" + " value");@b@ }@b@@b@ long val = 0L;@b@ for (int i = 1; i < length; ++i)@b@ val = (val << 8) + (buffer[(offset + i)] & 0xFF);@b@@b@ if (negative)@b@ {@b@ val -= 1L;@b@ val ^= ()Math.pow(2.0D, (length - 1) * 8) - 1L;@b@ }@b@ return ((negative) ? -val : val);@b@ }@b@@b@ private static long parseBinaryBigInteger(byte[] buffer, int offset, int length, boolean negative)@b@ {@b@ byte[] remainder = new byte[length - 1];@b@ System.arraycopy(buffer, offset + 1, remainder, 0, length - 1);@b@ BigInteger val = new BigInteger(remainder);@b@ if (negative)@b@ {@b@ val = val.add(BigInteger.valueOf(-1L)).not();@b@ }@b@ if (val.bitLength() > 63) {@b@ throw new IllegalArgumentException("At offset " + offset + ", " + length + " byte binary number" + " exceeds maximum signed long" + " value");@b@ }@b@@b@ return ((negative) ? -val.longValue() : val.longValue());@b@ }@b@@b@ public static boolean parseBoolean(byte[] buffer, int offset)@b@ {@b@ return (buffer[offset] == 1);@b@ }@b@@b@ private static String exceptionMessage(byte[] buffer, int offset, int length, int current, byte currentByte)@b@ {@b@ String string = new String(buffer, offset, length);@b@ string = string.replaceAll("", "{NUL}");@b@ String s = "Invalid byte " + currentByte + " at offset " + (current - offset) + " in '" + string + "' len=" + length;@b@ return s;@b@ }@b@@b@ public static String parseName(byte[] buffer, int offset, int length)@b@ {@b@ try@b@ {@b@ return parseName(buffer, offset, length, DEFAULT_ENCODING);@b@ } catch (IOException ex) {@b@ try {@b@ return parseName(buffer, offset, length, FALLBACK_ENCODING);@b@ }@b@ catch (IOException ex2) {@b@ throw new RuntimeException(ex2);@b@ }@b@ }@b@ }@b@@b@ public static String parseName(byte[] buffer, int offset, int length, ZipEncoding encoding)@b@ throws IOException@b@ {@b@ int len = length;@b@ for (; len > 0; --len)@b@ if (buffer[(offset + len - 1)] != 0)@b@ break;@b@@b@@b@ if (len > 0) {@b@ byte[] b = new byte[len];@b@ System.arraycopy(buffer, offset, b, 0, len);@b@ return encoding.decode(b);@b@ }@b@ return "";@b@ }@b@@b@ public static int formatNameBytes(String name, byte[] buf, int offset, int length)@b@ {@b@ try@b@ {@b@ return formatNameBytes(name, buf, offset, length, DEFAULT_ENCODING);@b@ } catch (IOException ex) {@b@ try {@b@ return formatNameBytes(name, buf, offset, length, FALLBACK_ENCODING);@b@ }@b@ catch (IOException ex2)@b@ {@b@ throw new RuntimeException(ex2);@b@ }@b@ }@b@ }@b@@b@ public static int formatNameBytes(String name, byte[] buf, int offset, int length, ZipEncoding encoding)@b@ throws IOException@b@ {@b@ int len = name.length();@b@ ByteBuffer b = encoding.encode(name);@b@ while ((b.limit() > length) && (len > 0))@b@ b = encoding.encode(name.substring(0, --len));@b@@b@ int limit = b.limit() - b.position();@b@ System.arraycopy(b.array(), b.arrayOffset(), buf, offset, limit);@b@@b@ for (int i = limit; i < length; ++i) {@b@ buf[(offset + i)] = 0;@b@ }@b@@b@ return (offset + length);@b@ }@b@@b@ public static void formatUnsignedOctalString(long value, byte[] buffer, int offset, int length)@b@ {@b@ int remaining = length;@b@ --remaining;@b@ if (value == 0L) {@b@ buffer[(offset + remaining--)] = 48;@b@ } else {@b@ long val = value;@b@ for (; (remaining >= 0) && (val != 0L); --remaining)@b@ {@b@ buffer[(offset + remaining)] = (byte)(48 + (byte)(int)(val & 0x7));@b@ val >>>= 3;@b@ }@b@@b@ if (val != 0L) {@b@ throw new IllegalArgumentException(value + "=" + Long.toOctalString(value) + " will not fit in octal number buffer of length " + length);@b@ }@b@@b@ }@b@@b@ for (; remaining >= 0; --remaining)@b@ buffer[(offset + remaining)] = 48;@b@ }@b@@b@ public static int formatOctalBytes(long value, byte[] buf, int offset, int length)@b@ {@b@ int idx = length - 2;@b@ formatUnsignedOctalString(value, buf, offset, idx);@b@@b@ buf[(offset + idx++)] = 32;@b@ buf[(offset + idx)] = 0;@b@@b@ return (offset + length);@b@ }@b@@b@ public static int formatLongOctalBytes(long value, byte[] buf, int offset, int length)@b@ {@b@ int idx = length - 1;@b@@b@ formatUnsignedOctalString(value, buf, offset, idx);@b@ buf[(offset + idx)] = 32;@b@@b@ return (offset + length);@b@ }@b@@b@ public static int formatLongOctalOrBinaryBytes(long value, byte[] buf, int offset, int length)@b@ {@b@ long maxAsOctalChar = (length == 8) ? 2097151L : 8589934591L;@b@@b@ boolean negative = value < 0L;@b@ if ((!(negative)) && (value <= maxAsOctalChar)) {@b@ return formatLongOctalBytes(value, buf, offset, length);@b@ }@b@@b@ if (length < 9)@b@ formatLongBinary(value, buf, offset, length, negative);@b@@b@ formatBigIntegerBinary(value, buf, offset, length, negative);@b@@b@ buf[offset] = (byte)((negative) ? 255 : 128);@b@ return (offset + length);@b@ }@b@@b@ private static void formatLongBinary(long value, byte[] buf, int offset, int length, boolean negative)@b@ {@b@ int bits = (length - 1) * 8;@b@ long max = 1L << bits;@b@ long val = Math.abs(value);@b@ if (val >= max) {@b@ throw new IllegalArgumentException("Value " + value + " is too large for " + length + " byte field.");@b@ }@b@@b@ if (negative) {@b@ val ^= max - 1L;@b@ val |= 255 << bits;@b@ val += 1L;@b@ }@b@ for (int i = offset + length - 1; i >= offset; --i) {@b@ buf[i] = (byte)(int)val;@b@ val >>= 8;@b@ }@b@ }@b@@b@ private static void formatBigIntegerBinary(long value, byte[] buf, int offset, int length, boolean negative)@b@ {@b@ BigInteger val = BigInteger.valueOf(value);@b@ byte[] b = val.toByteArray();@b@ int len = b.length;@b@ int off = offset + length - len;@b@ System.arraycopy(b, 0, buf, off, len);@b@ byte fill = (byte)((negative) ? 255 : 0);@b@ for (int i = offset + 1; i < off; ++i)@b@ buf[i] = fill;@b@ }@b@@b@ public static int formatCheckSumOctalBytes(long value, byte[] buf, int offset, int length)@b@ {@b@ int idx = length - 2;@b@ formatUnsignedOctalString(value, buf, offset, idx);@b@@b@ buf[(offset + idx++)] = 0;@b@ buf[(offset + idx)] = 32;@b@@b@ return (offset + length);@b@ }@b@@b@ public static long computeCheckSum(byte[] buf)@b@ {@b@ long sum = 0L;@b@@b@ for (int i = 0; i < buf.length; ++i) {@b@ sum += (0xFF & buf[i]);@b@ }@b@@b@ return sum;@b@ }@b@@b@ public static boolean verifyCheckSum(byte[] header)@b@ {@b@ long storedSum = 0L;@b@ long unsignedSum = 0L;@b@ long signedSum = 0L;@b@@b@ int digits = 0;@b@ for (int i = 0; i < header.length; ++i) {@b@ byte b = header[i];@b@ if ((148 <= i) && (i < 156)) {@b@ if ((48 <= b) && (b <= 55) && (digits++ < 6))@b@ storedSum = storedSum * 8L + b - 48L;@b@ else if (digits > 0)@b@ digits = 6;@b@@b@ b = 32;@b@ }@b@ unsignedSum += (0xFF & b);@b@ signedSum += b;@b@ }@b@@b@ return ((storedSum == unsignedSum) || (storedSum == signedSum) || (storedSum > unsignedSum));@b@ }@b@}
ZipEncoding接口
package org.apache.commons.compress.archivers.zip;@b@@b@import java.io.IOException;@b@import java.nio.ByteBuffer;@b@@b@public abstract interface ZipEncoding@b@{@b@ public abstract boolean canEncode(String paramString);@b@@b@ public abstract ByteBuffer encode(String paramString)@b@ throws IOException;@b@@b@ public abstract String decode(byte[] paramArrayOfByte)@b@ throws IOException;@b@}
ZipEncodingHelper类
package org.apache.commons.compress.archivers.zip;@b@@b@import java.nio.ByteBuffer;@b@import java.nio.charset.Charset;@b@import java.nio.charset.UnsupportedCharsetException;@b@import java.util.Collections;@b@import java.util.HashMap;@b@import java.util.Map;@b@@b@public abstract class ZipEncodingHelper@b@{@b@ private static final Map<String, SimpleEncodingHolder> simpleEncodings;@b@ private static final byte[] HEX_DIGITS;@b@ static final String UTF8 = "UTF8";@b@ private static final String UTF_DASH_8 = "UTF-8";@b@ static final ZipEncoding UTF8_ZIP_ENCODING;@b@@b@ static ByteBuffer growBuffer(ByteBuffer b, int newCapacity)@b@ {@b@ b.limit(b.position());@b@ b.rewind();@b@@b@ int c2 = b.capacity() * 2;@b@ ByteBuffer on = ByteBuffer.allocate((c2 < newCapacity) ? newCapacity : c2);@b@@b@ on.put(b);@b@ return on;@b@ }@b@@b@ static void appendSurrogate(ByteBuffer bb, char c)@b@ {@b@ bb.put(37);@b@ bb.put(85);@b@@b@ bb.put(HEX_DIGITS[(c >> '\f' & 0xF)]);@b@ bb.put(HEX_DIGITS[(c >> '\b' & 0xF)]);@b@ bb.put(HEX_DIGITS[(c >> '\4' & 0xF)]);@b@ bb.put(HEX_DIGITS[(c & 0xF)]);@b@ }@b@@b@ public static ZipEncoding getZipEncoding(String name)@b@ {@b@ if (isUTF8(name)) {@b@ return UTF8_ZIP_ENCODING;@b@ }@b@@b@ if (name == null) {@b@ return new FallbackZipEncoding();@b@ }@b@@b@ SimpleEncodingHolder h = (SimpleEncodingHolder)simpleEncodings.get(name);@b@@b@ if (h != null) {@b@ return h.getEncoding();@b@ }@b@@b@ try@b@ {@b@ Charset cs = Charset.forName(name);@b@ return new NioZipEncoding(cs);@b@ } catch (UnsupportedCharsetException e) {@b@ }@b@ return new FallbackZipEncoding(name);@b@ }@b@@b@ static boolean isUTF8(String encoding)@b@ {@b@ if (encoding == null)@b@ {@b@ encoding = System.getProperty("file.encoding");@b@ }@b@ return (("UTF8".equalsIgnoreCase(encoding)) || ("UTF-8".equalsIgnoreCase(encoding)));@b@ }@b@@b@ static@b@ {@b@ Map se = new HashMap();@b@@b@ char[] cp437_high_chars = { 199, 252, 233, 226, 228, 224, 229, 231, 234, 235, 232, 239, 238, 236, 196, 197, 201, 230, 198, 244, 246, 242, 251, 249, 255, 214, 220, 162, 163, 165, 8359, 402, 225, 237, 243, 250, 241, 209, 170, 186, 191, 8976, 172, 189, 188, 161, 171, 187, 9617, 9618, 9619, 9474, 9508, 9569, 9570, 9558, 9557, 9571, 9553, 9559, 9565, 9564, 9563, 9488, 9492, 9524, 9516, 9500, 9472, 9532, 9566, 9567, 9562, 9556, 9577, 9574, 9568, 9552, 9580, 9575, 9576, 9572, 9573, 9561, 9560, 9554, 9555, 9579, 9578, 9496, 9484, 9608, 9604, 9612, 9616, 9600, 945, 223, 915, 960, 931, 963, 181, 964, 934, 920, 937, 948, 8734, 966, 949, 8745, 8801, 177, 8805, 8804, 8992, 8993, 247, 8776, 176, 8729, 183, 8730, 8319, 178, 9632, 160 };@b@@b@ SimpleEncodingHolder cp437 = new SimpleEncodingHolder(cp437_high_chars);@b@@b@ se.put("CP437", cp437);@b@ se.put("Cp437", cp437);@b@ se.put("cp437", cp437);@b@ se.put("IBM437", cp437);@b@ se.put("ibm437", cp437);@b@@b@ char[] cp850_high_chars = { 199, 252, 233, 226, 228, 224, 229, 231, 234, 235, 232, 239, 238, 236, 196, 197, 201, 230, 198, 244, 246, 242, 251, 249, 255, 214, 220, 248, 163, 216, 215, 402, 225, 237, 243, 250, 241, 209, 170, 186, 191, 174, 172, 189, 188, 161, 171, 187, 9617, 9618, 9619, 9474, 9508, 193, 194, 192, 169, 9571, 9553, 9559, 9565, 162, 165, 9488, 9492, 9524, 9516, 9500, 9472, 9532, 227, 195, 9562, 9556, 9577, 9574, 9568, 9552, 9580, 164, 240, 208, 202, 203, 200, 305, 205, 206, 207, 9496, 9484, 9608, 9604, 166, 204, 9600, 211, 223, 212, 210, 245, 213, 181, 254, 222, 218, 219, 217, 253, 221, 175, 180, 173, 177, 8215, 190, 182, 167, 247, 184, 176, 168, 183, 185, 179, 178, 9632, 160 };@b@@b@ SimpleEncodingHolder cp850 = new SimpleEncodingHolder(cp850_high_chars);@b@@b@ se.put("CP850", cp850);@b@ se.put("Cp850", cp850);@b@ se.put("cp850", cp850);@b@ se.put("IBM850", cp850);@b@ se.put("ibm850", cp850);@b@ simpleEncodings = Collections.unmodifiableMap(se);@b@@b@ HEX_DIGITS = new byte[] { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70 };@b@@b@ UTF8_ZIP_ENCODING = new FallbackZipEncoding("UTF8");@b@ }@b@@b@ private static class SimpleEncodingHolder@b@ {@b@ private final char[] highChars;@b@ private Simple8BitZipEncoding encoding;@b@@b@ SimpleEncodingHolder(char[] highChars)@b@ {@b@ this.highChars = highChars;@b@ }@b@@b@ public synchronized Simple8BitZipEncoding getEncoding()@b@ {@b@ if (this.encoding == null)@b@ this.encoding = new Simple8BitZipEncoding(this.highChars);@b@@b@ return this.encoding;@b@ }@b@ }@b@}