1 /* 2 * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* @test 25 * @bug 4770745 6234507 6303183 8048990 26 * @summary test a variety of zip file entries 27 * @author Martin Buchholz 28 * @key randomness 29 */ 30 31 import java.util.*; 32 import java.util.zip.*; 33 import java.util.jar.*; 34 import java.io.*; 35 36 public class Assortment { 37 static int passed = 0, failed = 0; 38 39 static void fail(String msg) { 40 failed++; 41 new Exception(msg).printStackTrace(); 42 } 43 44 static void unexpected(Throwable t) { 45 failed++; 46 t.printStackTrace(); 47 } 48 49 static void check(boolean condition, String msg) { 50 if (! condition) 51 fail(msg); 52 } 53 54 static void check(boolean condition) { 55 check(condition, "Something's wrong"); 56 } 57 58 static final int get16(byte b[], int off) { 59 return Byte.toUnsignedInt(b[off]) | (Byte.toUnsignedInt(b[off+1]) << 8); 60 } 61 62 // check if all "expected" extra fields equal to their 63 // corresponding fields in "extra". The "extra" might have 64 // timestamp fields added by ZOS. 65 static boolean equalsExtraData(byte[] expected, byte[] extra) { 66 if (expected == null) 67 return true; 68 int off = 0; 69 int len = expected.length; 70 while (off + 4 < len) { 71 int tag = get16(expected, off); 72 int sz = get16(expected, off + 2); 73 int off0 = 0; 74 int len0 = extra.length; 75 boolean matched = false; 76 while (off0 + 4 < len0) { 77 int tag0 = get16(extra, off0); 78 int sz0 = get16(extra, off0 + 2); 79 if (tag == tag0 && sz == sz0) { 80 matched = true; 81 for (int i = 0; i < sz; i++) { 82 if (expected[off + i] != extra[off0 +i]) 83 matched = false; 84 } 85 break; 86 } 87 off0 += (4 + sz0); 88 } 89 if (!matched) 90 return false; 91 off += (4 + sz); 92 } 93 return true; 94 } 95 96 private static class Entry { 97 private String name; 98 private int method; 99 private byte[] data; 100 private byte[] extra; 101 private String comment; 102 103 Entry(String name, 104 int method, 105 byte[] data, 106 byte[] extra, 107 String comment) { 108 this.name = name; 109 this.method = method; 110 this.data = data; 111 this.extra = extra; 112 this.comment = comment; 113 } 114 115 void write(ZipOutputStream s) throws Exception { 116 ZipEntry e = new ZipEntry(name); 117 CRC32 crc32 = new CRC32(); 118 e.setMethod(method); 119 if (method == ZipEntry.STORED) { 120 e.setSize(data == null ? 0 : data.length); 121 crc32.reset(); 122 if (data != null) crc32.update(data); 123 e.setCrc(crc32.getValue()); 124 } else { 125 e.setSize(0); 126 e.setCrc(0); 127 } 128 if (comment != null) e.setComment(comment); 129 if (extra != null) e.setExtra(extra); 130 s.putNextEntry(e); 131 if (data != null) s.write(data); 132 } 133 134 byte[] getData(ZipFile f, ZipEntry e) throws Exception { 135 byte[] fdata = new byte[(int)e.getSize()]; 136 InputStream is = f.getInputStream(e); 137 is.read(fdata); 138 return fdata; 139 } 140 141 void verify(ZipFile f, ZipEntry e) throws Exception { 142 verify(e, getData(f, e)); 143 } 144 145 void verify(ZipEntry e, byte[] eData) throws Exception { 146 byte[] data = (this.data == null) ? new byte[]{} : this.data; 147 byte[] extra = (this.extra != null && this.extra.length == 0) ? 148 null : this.extra; 149 check(name.equals(e.getName())); 150 check(method == e.getMethod()); 151 check((((comment == null) || comment.equals("")) 152 && (e.getComment() == null)) 153 || comment.equals(e.getComment())); 154 check(equalsExtraData(extra, e.getExtra())); 155 check(Arrays.equals(data, eData)); 156 check(e.getSize() == data.length); 157 check((method == ZipEntry.DEFLATED) || 158 (e.getCompressedSize() == data.length)); 159 } 160 161 void verify(ZipFile f) throws Exception { 162 ZipEntry e = f.getEntry(name); 163 verify(e, getData(f, e)); 164 } 165 166 void verifyZipInputStream(ZipInputStream s) throws Exception { 167 ZipEntry e = s.getNextEntry(); 168 169 byte[] data = (this.data == null) ? new byte[]{} : this.data; 170 byte[] otherData = new byte[data.length]; 171 s.read(otherData); 172 check(Arrays.equals(data, otherData)); 173 174 byte[] extra = (this.extra != null && this.extra.length == 0) ? 175 null : this.extra; 176 check(equalsExtraData(extra, e.getExtra())); 177 check(name.equals(e.getName())); 178 check(method == e.getMethod()); 179 check(e.getSize() == -1 || e.getSize() == data.length); 180 check((method == ZipEntry.DEFLATED) || 181 (e.getCompressedSize() == data.length)); 182 } 183 184 void verifyJarInputStream(JarInputStream s) throws Exception { 185 // JarInputStream "automatically" reads the manifest 186 if (name.equals("meta-iNf/ManIfEst.Mf")) 187 return; 188 189 verifyZipInputStream(s); 190 } 191 } 192 193 private static int uniquifier = 86; 194 private static String uniquify(String name) { 195 return name + (uniquifier++); 196 } 197 198 private static byte[] toBytes(String s) throws Exception { 199 return s.getBytes("UTF-8"); 200 } 201 202 private static byte[] toExtra(byte[] bytes) throws Exception { 203 if (bytes == null) return null; 204 // Construct a fake extra field with valid header length 205 byte[] v = new byte[bytes.length + 4]; 206 v[0] = (byte) 0x47; 207 v[1] = (byte) 0xff; 208 v[2] = (byte) bytes.length; 209 v[3] = (byte) (bytes.length << 8); 210 System.arraycopy(bytes, 0, v, 4, bytes.length); 211 return v; 212 } 213 214 private static Random random = new Random(); 215 216 private static String makeName(int length) { 217 StringBuilder sb = new StringBuilder(length); 218 for (int i = 0; i < length; i++) 219 sb.append((char)(random.nextInt(10000)+1)); 220 return sb.toString(); 221 } 222 223 public static void main(String[] args) throws Exception { 224 File zipName = new File("x.zip"); 225 int[] methods = {ZipEntry.STORED, ZipEntry.DEFLATED}; 226 String[] names = {makeName(1), makeName(160), makeName(9000)}; 227 byte[][] datas = {null, new byte[]{}, new byte[]{'d'}}; 228 byte[][] extras = {null, new byte[]{}, new byte[]{'e'}}; 229 String[] comments = {null, "", "c"}; 230 231 List<Entry> entries = new ArrayList<Entry>(); 232 233 // Highly unusual manifest 234 entries.add(new Entry("meta-iNf/ManIfEst.Mf", 235 ZipEntry.STORED, 236 toBytes("maNiFest-VeRsIon: 1.0\n"), 237 toExtra(toBytes("Can manifests have extra??")), 238 "Can manifests have comments??")); 239 240 // The emptiest possible entry 241 entries.add(new Entry("", ZipEntry.STORED, null, null, "")); 242 243 for (String name : names) 244 for (int method : methods) 245 for (byte[] data : datas) // datae?? 246 for (byte[] extra : extras) 247 for (String comment : comments) 248 entries.add(new Entry(uniquify(name), method, data, 249 toExtra(extra), comment)); 250 251 //---------------------------------------------------------------- 252 // Write zip file using ZipOutputStream 253 //---------------------------------------------------------------- 254 try (FileOutputStream fos = new FileOutputStream(zipName); 255 ZipOutputStream zos = new ZipOutputStream(fos)) 256 { 257 for (Entry e : entries) 258 e.write(zos); 259 } 260 261 //---------------------------------------------------------------- 262 // Verify zip file contents using ZipFile.getEntry() 263 //---------------------------------------------------------------- 264 try (ZipFile f = new ZipFile(zipName)) { 265 for (Entry e : entries) 266 e.verify(f); 267 } 268 269 //---------------------------------------------------------------- 270 // Verify zip file contents using JarFile.getEntry() 271 //---------------------------------------------------------------- 272 try (JarFile f = new JarFile(zipName)) { 273 check(f.getManifest() != null); 274 for (Entry e : entries) 275 e.verify(f); 276 } 277 278 //---------------------------------------------------------------- 279 // Verify zip file contents using ZipFile.entries() 280 //---------------------------------------------------------------- 281 try (ZipFile f = new ZipFile(zipName)) { 282 Enumeration<? extends ZipEntry> en = f.entries(); 283 for (Entry e : entries) 284 e.verify(f, en.nextElement()); 285 286 check(!en.hasMoreElements()); 287 } 288 289 //---------------------------------------------------------------- 290 // Verify zip file contents using JarFile.entries() 291 //---------------------------------------------------------------- 292 try (JarFile f = new JarFile(zipName)) { 293 Enumeration<? extends ZipEntry> en = f.entries(); 294 for (Entry e : entries) 295 e.verify(f, en.nextElement()); 296 297 check(!en.hasMoreElements()); 298 } 299 300 //---------------------------------------------------------------- 301 // Verify zip file contents using ZipInputStream class 302 //---------------------------------------------------------------- 303 try (FileInputStream fis = new FileInputStream(zipName); 304 ZipInputStream s = new ZipInputStream(fis)) { 305 306 for (Entry e : entries) 307 e.verifyZipInputStream(s); 308 } 309 310 //---------------------------------------------------------------- 311 // Verify zip file contents using JarInputStream class 312 //---------------------------------------------------------------- 313 try (FileInputStream fis = new FileInputStream(zipName); 314 JarInputStream s = new JarInputStream(fis)) { 315 316 // JarInputStream "automatically" reads the manifest 317 check(s.getManifest() != null); 318 319 for (Entry e : entries) 320 e.verifyJarInputStream(s); 321 } 322 323 // String cmd = "unzip -t " + zipName.getPath() + " >/dev/tty"; 324 // new ProcessBuilder(new String[]{"/bin/sh", "-c", cmd}).start().waitFor(); 325 326 zipName.deleteOnExit(); 327 328 System.out.printf("passed = %d, failed = %d%n", passed, failed); 329 if (failed > 0) throw new Exception("Some tests failed"); 330 } 331 }