1 /* 2 * Copyright (c) 2015, 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 /* 25 * @test 26 * @bug 8142508 8146431 27 * @summary Tests various ZipFile apis 28 * @run main/manual TestZipFile 29 */ 30 31 import java.io.*; 32 import java.lang.reflect.Method; 33 import java.nio.*; 34 import java.nio.file.*; 35 import java.nio.file.attribute.*; 36 import java.util.*; 37 import java.util.concurrent.*; 38 import java.util.zip.*; 39 40 public class TestZipFile { 41 42 private static Random r = new Random(); 43 private static int N = 50; 44 private static int NN = 10; 45 private static int ENUM = 10000; 46 private static int ESZ = 10000; 47 private static ExecutorService executor = Executors.newFixedThreadPool(20); 48 private static Set<Path> paths = new HashSet<>(); 49 50 static void realMain (String[] args) throws Throwable { 51 52 try { 53 for (int i = 0; i < N; i++) { 54 test(r.nextInt(ENUM), r.nextInt(ESZ), false, true); 55 test(r.nextInt(ENUM), r.nextInt(ESZ), true, true); 56 } 57 58 for (int i = 0; i < NN; i++) { 59 test(r.nextInt(ENUM), 100000 + r.nextInt(ESZ), false, true); 60 test(r.nextInt(ENUM), 100000 + r.nextInt(ESZ), true, true); 61 testCachedDelete(); 62 testCachedOverwrite(); 63 //test(r.nextInt(ENUM), r.nextInt(ESZ), false, true); 64 } 65 66 test(70000, 1000, false, true); // > 65536 entry number; 67 testDelete(); // OPEN_DELETE 68 69 executor.shutdown(); 70 executor.awaitTermination(10, TimeUnit.MINUTES); 71 } finally { 72 for (Path path : paths) { 73 Files.deleteIfExists(path); 74 } 75 } 76 } 77 78 static void test(int numEntry, int szMax, boolean addPrefix, boolean cleanOld) { 79 String name = "zftest" + r.nextInt() + ".zip"; 80 Zip zip = new Zip(name, numEntry, szMax, addPrefix, cleanOld); 81 for (int i = 0; i < NN; i++) { 82 executor.submit(() -> doTest(zip)); 83 } 84 } 85 86 // test scenario: 87 // (1) open the ZipFile(zip) with OPEN_READ | OPEN_DELETE 88 // (2) test the ZipFile works correctly 89 // (3) check the zip is deleted after ZipFile gets closed 90 static void testDelete() throws Throwable { 91 String name = "zftest" + r.nextInt() + ".zip"; 92 Zip zip = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); 93 try (ZipFile zf = new ZipFile(new File(zip.name), 94 ZipFile.OPEN_READ | ZipFile.OPEN_DELETE )) 95 { 96 doTest0(zip, zf); 97 } 98 Path p = Paths.get(name); 99 if (Files.exists(p)) { 100 fail("Failed to delete " + name + " with OPEN_DELETE"); 101 } 102 } 103 104 // test scenario: 105 // (1) keep a ZipFile(zip1) alive (in ZipFile's cache), dont close it 106 // (2) delete zip1 and create zip2 with the same name the zip1 with zip2 107 // (3) zip1 tests should fail, but no crash 108 // (4) zip2 tasks should all get zip2, then pass normal testing. 109 static void testCachedDelete() throws Throwable { 110 String name = "zftest" + r.nextInt() + ".zip"; 111 Zip zip1 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); 112 113 try (ZipFile zf = new ZipFile(zip1.name)) { 114 for (int i = 0; i < NN; i++) { 115 executor.submit(() -> verifyNoCrash(zip1)); 116 } 117 // delete the "zip1" and create a new one to test 118 Zip zip2 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); 119 /* 120 System.out.println("========================================"); 121 System.out.printf(" zip1=%s, mt=%d, enum=%d%n ->attrs=[key=%s, sz=%d, mt=%d]%n", 122 zip1.name, zip1.lastModified, zip1.entries.size(), 123 zip1.attrs.fileKey(), zip1.attrs.size(), zip1.attrs.lastModifiedTime().toMillis()); 124 System.out.printf(" zip2=%s, mt=%d, enum=%d%n ->attrs=[key=%s, sz=%d, mt=%d]%n", 125 zip2.name, zip2.lastModified, zip2.entries.size(), 126 zip2.attrs.fileKey(), zip2.attrs.size(), zip2.attrs.lastModifiedTime().toMillis()); 127 */ 128 for (int i = 0; i < NN; i++) { 129 executor.submit(() -> doTest(zip2)); 130 } 131 } 132 } 133 134 // overwrite the "zip1" and create a new one to test. So the two zip files 135 // have the same fileKey, but probably different lastModified() 136 static void testCachedOverwrite() throws Throwable { 137 String name = "zftest" + r.nextInt() + ".zip"; 138 Zip zip1 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); 139 try (ZipFile zf = new ZipFile(zip1.name)) { 140 for (int i = 0; i < NN; i++) { 141 executor.submit(() -> verifyNoCrash(zip1)); 142 } 143 // overwrite the "zip1" with new contents 144 Zip zip2 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, false); 145 for (int i = 0; i < NN; i++) { 146 executor.submit(() -> doTest(zip2)); 147 } 148 } 149 } 150 151 // just check the entries and contents. since the file has been either overwritten 152 // or deleted/rewritten, we only care if it crahes or not. 153 static void verifyNoCrash(Zip zip) throws RuntimeException { 154 try (ZipFile zf = new ZipFile(zip.name)) { 155 List<ZipEntry> zlist = new ArrayList(zip.entries.keySet()); 156 String[] elist = zf.stream().map( e -> e.getName()).toArray(String[]::new); 157 if (!Arrays.equals(elist, 158 zlist.stream().map( e -> e.getName()).toArray(String[]::new))) 159 { 160 //System.out.printf("++++++ LIST NG [%s] entries.len=%d, expected=%d+++++++%n", 161 // zf.getName(), elist.length, zlist.size()); 162 return; 163 } 164 for (ZipEntry ze : zlist) { 165 byte[] zdata = zip.entries.get(ze); 166 ZipEntry e = zf.getEntry(ze.getName()); 167 if (e != null) { 168 checkEqual(e, ze); 169 if (!e.isDirectory()) { 170 // check with readAllBytes 171 try (InputStream is = zf.getInputStream(e)) { 172 if (!Arrays.equals(zdata, is.readAllBytes())) { 173 //System.out.printf("++++++ BYTES NG [%s]/[%s] ++++++++%n", 174 // zf.getName(), ze.getName()); 175 } 176 } 177 } 178 } 179 } 180 } catch (Throwable t) { 181 // t.printStackTrace(); 182 // fail(t.toString()); 183 } 184 } 185 186 static void checkEqual(ZipEntry x, ZipEntry y) { 187 if (x.getName().equals(y.getName()) && 188 x.isDirectory() == y.isDirectory() && 189 x.getMethod() == y.getMethod() && 190 (x.getTime() / 2000) == y.getTime() / 2000 && 191 x.getSize() == y.getSize() && 192 x.getCompressedSize() == y.getCompressedSize() && 193 x.getCrc() == y.getCrc() && 194 x.getComment().equals(y.getComment()) 195 ) { 196 pass(); 197 } else { 198 fail(x + " not equal to " + y); 199 System.out.printf(" %s %s%n", x.getName(), y.getName()); 200 System.out.printf(" %d %d%n", x.getMethod(), y.getMethod()); 201 System.out.printf(" %d %d%n", x.getTime(), y.getTime()); 202 System.out.printf(" %d %d%n", x.getSize(), y.getSize()); 203 System.out.printf(" %d %d%n", x.getCompressedSize(), y.getCompressedSize()); 204 System.out.printf(" %d %d%n", x.getCrc(), y.getCrc()); 205 System.out.println("-----------------"); 206 } 207 } 208 209 static void doTest(Zip zip) throws RuntimeException { 210 //Thread me = Thread.currentThread(); 211 try (ZipFile zf = new ZipFile(zip.name)) { 212 doTest0(zip, zf); 213 } catch (Throwable t) { 214 throw new RuntimeException(t); 215 } 216 } 217 218 static void doTest0(Zip zip, ZipFile zf) throws Throwable { 219 // (0) check zero-length entry name, no AIOOBE 220 try { 221 check(zf.getEntry("") == null);; 222 } catch (Throwable t) { 223 unexpected(t); 224 } 225 226 List<ZipEntry> list = new ArrayList(zip.entries.keySet()); 227 // (1) check entry list, in expected order 228 if (!check(Arrays.equals( 229 list.stream().map( e -> e.getName()).toArray(String[]::new), 230 zf.stream().map( e -> e.getName()).toArray(String[]::new)))) { 231 return; 232 } 233 // (2) shuffle, and check each entry and its bytes 234 Collections.shuffle(list); 235 for (ZipEntry ze : list) { 236 byte[] data = zip.entries.get(ze); 237 ZipEntry e = zf.getEntry(ze.getName()); 238 checkEqual(e, ze); 239 if (!e.isDirectory()) { 240 // check with readAllBytes 241 try (InputStream is = zf.getInputStream(e)) { 242 check(Arrays.equals(data, is.readAllBytes())); 243 } 244 // check with smaller sized buf 245 try (InputStream is = zf.getInputStream(e)) { 246 byte[] buf = new byte[(int)e.getSize()]; 247 int sz = r.nextInt((int)e.getSize()/4 + 1) + 1; 248 int off = 0; 249 int n; 250 while ((n = is.read(buf, off, buf.length - off)) > 0) { 251 off += n; 252 } 253 check(is.read() == -1); 254 check(Arrays.equals(data, buf)); 255 } 256 } 257 } 258 // (3) check getMetaInfEntryNames 259 String[] metas = list.stream() 260 .map( e -> e.getName()) 261 .filter( s -> s.startsWith("META-INF/")) 262 .sorted() 263 .toArray(String[]::new); 264 if (metas.length > 0) { 265 // meta-inf entries 266 Method getMetas = ZipFile.class.getDeclaredMethod("getMetaInfEntryNames"); 267 getMetas.setAccessible(true); 268 String[] names = (String[])getMetas.invoke(zf); 269 if (names == null) { 270 fail("Failed to get metanames from " + zf); 271 } else { 272 Arrays.sort(names); 273 check(Arrays.equals(names, metas)); 274 } 275 } 276 } 277 278 private static class Zip { 279 String name; 280 Map<ZipEntry, byte[]> entries; 281 BasicFileAttributes attrs; 282 long lastModified; 283 284 Zip(String name, int num, int szMax, boolean prefix, boolean clean) { 285 this.name = name; 286 entries = new LinkedHashMap<>(num); 287 try { 288 Path p = Paths.get(name); 289 if (clean) { 290 Files.deleteIfExists(p); 291 } 292 paths.add(p); 293 } catch (Exception x) { 294 throw (RuntimeException)x; 295 } 296 297 try (FileOutputStream fos = new FileOutputStream(name); 298 BufferedOutputStream bos = new BufferedOutputStream(fos); 299 ZipOutputStream zos = new ZipOutputStream(bos)) 300 { 301 if (prefix) { 302 byte[] bytes = new byte[r.nextInt(1000)]; 303 r.nextBytes(bytes); 304 bos.write(bytes); 305 } 306 CRC32 crc = new CRC32(); 307 for (int i = 0; i < num; i++) { 308 String ename = "entry-" + i + "-name-" + r.nextLong(); 309 ZipEntry ze = new ZipEntry(ename); 310 int method = r.nextBoolean() ? ZipEntry.STORED : ZipEntry.DEFLATED; 311 writeEntry(zos, crc, ze, ZipEntry.STORED, szMax); 312 } 313 // add some manifest entries 314 for (int i = 0; i < r.nextInt(20); i++) { 315 String meta = "META-INF/" + "entry-" + i + "-metainf-" + r.nextLong(); 316 ZipEntry ze = new ZipEntry(meta); 317 writeEntry(zos, crc, ze, ZipEntry.STORED, szMax); 318 } 319 } catch (Exception x) { 320 throw (RuntimeException)x; 321 } 322 try { 323 this.attrs = Files.readAttributes(Paths.get(name), BasicFileAttributes.class); 324 this.lastModified = new File(name).lastModified(); 325 } catch (Exception x) { 326 throw (RuntimeException)x; 327 } 328 } 329 330 private void writeEntry(ZipOutputStream zos, CRC32 crc, 331 ZipEntry ze, int method, int szMax) 332 throws IOException 333 { 334 ze.setMethod(method); 335 byte[] data = new byte[r.nextInt(szMax + 1)]; 336 r.nextBytes(data); 337 if (method == ZipEntry.STORED) { // must set size/csize/crc 338 ze.setSize(data.length); 339 ze.setCompressedSize(data.length); 340 crc.reset(); 341 crc.update(data); 342 ze.setCrc(crc.getValue()); 343 } 344 ze.setTime(System.currentTimeMillis()); 345 ze.setComment(ze.getName()); 346 zos.putNextEntry(ze); 347 zos.write(data); 348 zos.closeEntry(); 349 entries.put(ze, data); 350 } 351 } 352 353 //--------------------- Infrastructure --------------------------- 354 static volatile int passed = 0, failed = 0; 355 static void pass() {passed++;} 356 static void pass(String msg) {System.out.println(msg); passed++;} 357 static void fail() {failed++; Thread.dumpStack();} 358 static void fail(String msg) {System.out.println(msg); fail();} 359 static void unexpected(Throwable t) {failed++; t.printStackTrace();} 360 static void unexpected(Throwable t, String msg) { 361 System.out.println(msg); failed++; t.printStackTrace();} 362 static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;} 363 364 public static void main(String[] args) throws Throwable { 365 try {realMain(args);} catch (Throwable t) {unexpected(t);} 366 System.out.println("\nPassed = " + passed + " failed = " + failed); 367 if (failed > 0) throw new AssertionError("Some tests failed");} 368 }