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