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 }