7077769: (zipfs) ZipFileSystem.writeCEN() writes wrong "data size" for ZIP64 extended information extra field
authorsherman
Fri, 26 Aug 2011 15:36:21 -0700
changeset 10365 8a89ee820687
parent 10345 b4aebbfc5b3a
child 10366 713afa0a0349
7077769: (zipfs) ZipFileSystem.writeCEN() writes wrong "data size" for ZIP64 extended information extra field Summary: fixed the wrong size when writing out the cen table for ZIP64 Reviewed-by: alanb
jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileSystem.java
jdk/test/java/util/zip/LargeZip.java
--- a/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileSystem.java	Wed Aug 17 15:18:16 2011 -0700
+++ b/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileSystem.java	Fri Aug 26 15:36:21 2011 -0700
@@ -31,6 +31,7 @@
 
 package com.sun.nio.zipfs;
 
+import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.EOFException;
@@ -1165,7 +1166,6 @@
     // sync the zip file system, if there is any udpate
     private void sync() throws IOException {
         //System.out.printf("->sync(%s) starting....!%n", toString());
-
         // check ex-closer
         if (!exChClosers.isEmpty()) {
             for (ExChannelCloser ecc : exChClosers) {
@@ -1179,84 +1179,84 @@
         if (!hasUpdate)
             return;
         Path tmpFile = createTempFileInSameDirectoryAs(zfpath);
-        OutputStream os = Files.newOutputStream(tmpFile, WRITE);
-        ArrayList<Entry> elist = new ArrayList<>(inodes.size());
-        long written = 0;
-        byte[] buf = new byte[8192];
-        Entry e = null;
+        try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(tmpFile, WRITE)))
+        {
+            ArrayList<Entry> elist = new ArrayList<>(inodes.size());
+            long written = 0;
+            byte[] buf = new byte[8192];
+            Entry e = null;
 
-        // write loc
-        for (IndexNode inode : inodes.values()) {
-            if (inode instanceof Entry) {    // an updated inode
-                e = (Entry)inode;
-                try {
-                    if (e.type == Entry.COPY) {
-                        // entry copy: the only thing changed is the "name"
-                        // and "nlen" in LOC header, so we udpate/rewrite the
-                        // LOC in new file and simply copy the rest (data and
-                        // ext) without enflating/deflating from the old zip
-                        // file LOC entry.
-                        written += copyLOCEntry(e, true, os, written, buf);
-                    } else {                          // NEW, FILECH or CEN
-                        e.locoff = written;
-                        written += e.writeLOC(os);    // write loc header
-                        if (e.bytes != null) {        // in-memory, deflated
-                            os.write(e.bytes);        // already
-                            written += e.bytes.length;
-                        } else if (e.file != null) {  // tmp file
-                            try (InputStream is = Files.newInputStream(e.file)) {
-                                int n;
-                                if (e.type == Entry.NEW) {  // deflated already
-                                    while ((n = is.read(buf)) != -1) {
-                                        os.write(buf, 0, n);
-                                        written += n;
+            // write loc
+            for (IndexNode inode : inodes.values()) {
+                if (inode instanceof Entry) {    // an updated inode
+                    e = (Entry)inode;
+                    try {
+                        if (e.type == Entry.COPY) {
+                            // entry copy: the only thing changed is the "name"
+                            // and "nlen" in LOC header, so we udpate/rewrite the
+                            // LOC in new file and simply copy the rest (data and
+                            // ext) without enflating/deflating from the old zip
+                            // file LOC entry.
+                            written += copyLOCEntry(e, true, os, written, buf);
+                        } else {                          // NEW, FILECH or CEN
+                            e.locoff = written;
+                            written += e.writeLOC(os);    // write loc header
+                            if (e.bytes != null) {        // in-memory, deflated
+                                os.write(e.bytes);        // already
+                                written += e.bytes.length;
+                            } else if (e.file != null) {  // tmp file
+                                try (InputStream is = Files.newInputStream(e.file)) {
+                                    int n;
+                                    if (e.type == Entry.NEW) {  // deflated already
+                                        while ((n = is.read(buf)) != -1) {
+                                            os.write(buf, 0, n);
+                                            written += n;
+                                        }
+                                    } else if (e.type == Entry.FILECH) {
+                                        // the data are not deflated, use ZEOS
+                                        try (OutputStream os2 = new EntryOutputStream(e, os)) {
+                                            while ((n = is.read(buf)) != -1) {
+                                                os2.write(buf, 0, n);
+                                            }
+                                        }
+                                        written += e.csize;
+                                        if ((e.flag & FLAG_DATADESCR) != 0)
+                                            written += e.writeEXT(os);
                                     }
-                                } else if (e.type == Entry.FILECH) {
-                                    // the data are not deflated, use ZEOS
-                                    try (OutputStream os2 = new EntryOutputStream(e, os)) {
-                                        while ((n = is.read(buf)) != -1) {
-                                            os2.write(buf, 0, n);
-                                        }
-                                    }
-                                    written += e.csize;
-                                    if ((e.flag & FLAG_DATADESCR) != 0)
-                                        written += e.writeEXT(os);
                                 }
+                                Files.delete(e.file);
+                                tmppaths.remove(e.file);
+                            } else {
+                                // dir, 0-length data
                             }
-                            Files.delete(e.file);
-                            tmppaths.remove(e.file);
-                        } else {
-                            // dir, 0-length data
                         }
+                        elist.add(e);
+                    } catch (IOException x) {
+                        x.printStackTrace();    // skip any in-accurate entry
                     }
-                    elist.add(e);
-                } catch (IOException x) {
-                    x.printStackTrace();    // skip any in-accurate entry
-                }
-            } else {                        // unchanged inode
-                if (inode.pos == -1) {
-                    continue;               // pseudo directory node
-                }
-                e = Entry.readCEN(this, inode.pos);
-                try {
-                    written += copyLOCEntry(e, false, os, written, buf);
-                    elist.add(e);
-                } catch (IOException x) {
-                    x.printStackTrace();    // skip any wrong entry
+                } else {                        // unchanged inode
+                    if (inode.pos == -1) {
+                        continue;               // pseudo directory node
+                    }
+                    e = Entry.readCEN(this, inode.pos);
+                    try {
+                        written += copyLOCEntry(e, false, os, written, buf);
+                        elist.add(e);
+                    } catch (IOException x) {
+                        x.printStackTrace();    // skip any wrong entry
+                    }
                 }
             }
+
+            // now write back the cen and end table
+            end.cenoff = written;
+            for (Entry entry : elist) {
+                written += entry.writeCEN(os);
+            }
+            end.centot = elist.size();
+            end.cenlen = written - end.cenoff;
+            end.write(os, written);
         }
-
-        // now write back the cen and end table
-        end.cenoff = written;
-        for (Entry entry : elist) {
-            written += entry.writeCEN(os);
-        }
-        end.centot = elist.size();
-        end.cenlen = written - end.cenoff;
-        end.write(os, written);
-        os.close();
-
         if (!streams.isEmpty()) {
             //
             // TBD: ExChannelCloser should not be necessary if we only
@@ -1959,7 +1959,7 @@
             writeBytes(os, name);
             if (elen64 != 0) {
                 writeShort(os, EXTID_ZIP64);// Zip64 extra
-                writeShort(os, elen64);     // size of "this" extra block
+                writeShort(os, elen64 - 4); // size of "this" extra block
                 if (size0 == ZIP64_MINVAL)
                     writeLong(os, size);
                 if (csize0 == ZIP64_MINVAL)
--- a/jdk/test/java/util/zip/LargeZip.java	Wed Aug 17 15:18:16 2011 -0700
+++ b/jdk/test/java/util/zip/LargeZip.java	Fri Aug 26 15:36:21 2011 -0700
@@ -25,173 +25,242 @@
 
 import java.io.*;
 import java.nio.*;
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.nio.file.spi.*;
 import java.util.*;
 import java.util.zip.*;
 
-public class LargeZip {
-    // If true, don't delete large ZIP file created for test.
-    static final boolean debug = System.getProperty("debug") != null;
+import static java.nio.file.StandardCopyOption.*;
 
-    //static final int DATA_LEN = 1024 * 1024;
-    static final int DATA_LEN = 80 * 1024;
-    static final int DATA_SIZE = 8;
+public class LargeZip {
+     // If true, don't delete large ZIP file created for test.
+     static final boolean debug = System.getProperty("debug") != null;
 
-    static long fileSize = 6L * 1024L * 1024L * 1024L; // 6GB
-
-    static boolean userFile = false;
+     //static final int DATA_LEN = 1024 * 1024;
+     static final int DATA_LEN = 80 * 1024;
+     static final int DATA_SIZE = 8;
 
-    static byte[] data;
-    static File largeFile;
-    static String lastEntryName;
+     static long fileSize = 6L * 1024L * 1024L * 1024L; // 6GB
+
+     static boolean userFile = false;
+     static byte[] data;
+     static File largeFile;
+     static String lastEntryName;
 
-    /* args can be empty, in which case check a 3 GB file which is created for
-     * this test (and then deleted).  Or it can be a number, in which case
-     * that designates the size of the file that's created for this test (and
-     * then deleted).  Or it can be the name of a file to use for the test, in
-     * which case it is *not* deleted.  Note that in this last case, the data
-     * comparison might fail.
-     */
-    static void realMain (String[] args) throws Throwable {
-        if (args.length > 0) {
-            try {
-                fileSize = Long.parseLong(args[0]);
-                System.out.println("Testing with file of size " + fileSize);
-            } catch (NumberFormatException ex) {
-                largeFile = new File(args[0]);
-                if (!largeFile.exists()) {
-                    throw new Exception("Specified file " + args[0] + " does not exist");
-                }
-                userFile = true;
-                System.out.println("Testing with user-provided file " + largeFile);
-            }
-        }
-        File testDir = null;
-        if (largeFile == null) {
-            testDir = new File(System.getProperty("test.scratch", "."),
-                                    "LargeZip");
-            if (testDir.exists()) {
-                if (!testDir.delete()) {
-                    throw new Exception("Cannot delete already-existing test directory");
-                }
-            }
-            check(!testDir.exists() && testDir.mkdirs());
-            largeFile = new File(testDir, "largezip.zip");
-            createLargeZip();
-        }
+     /* args can be empty, in which case check a 3 GB file which is created for
+      * this test (and then deleted).  Or it can be a number, in which case
+      * that designates the size of the file that's created for this test (and
+      * then deleted).  Or it can be the name of a file to use for the test, in
+      * which case it is *not* deleted.  Note that in this last case, the data
+      * comparison might fail.
+      */
+     static void realMain (String[] args) throws Throwable {
+         if (args.length > 0) {
+             try {
+                 fileSize = Long.parseLong(args[0]);
+                 System.out.println("Testing with file of size " + fileSize);
+             } catch (NumberFormatException ex) {
+                 largeFile = new File(args[0]);
+                 if (!largeFile.exists()) {
+                     throw new Exception("Specified file " + args[0] + " does not exist");
+                 }
+                 userFile = true;
+                 System.out.println("Testing with user-provided file " + largeFile);
+             }
+         }
+         File testDir = null;
+         if (largeFile == null) {
+             testDir = new File(System.getProperty("test.scratch", "."),
+                                     "LargeZip");
+             if (testDir.exists()) {
+                 if (!testDir.delete()) {
+                     throw new Exception("Cannot delete already-existing test directory");
+                 }
+             }
+             check(!testDir.exists() && testDir.mkdirs());
+             largeFile = new File(testDir, "largezip.zip");
+             createLargeZip();
+         } else {
+             if (args.length > 1)
+                 updateLargeZip(args[1]); // add new entry with zfs
+         }
+         readLargeZip1();
+         readLargeZip2();
 
-        readLargeZip1();
-        readLargeZip2();
+         if (!userFile && !debug) {
+             check(largeFile.delete());
+             check(testDir.delete());
+         }
+     }
+
+     static void createLargeZip() throws Throwable {
+         int iterations = DATA_LEN / DATA_SIZE;
+         ByteBuffer bb = ByteBuffer.allocate(DATA_SIZE);
+         ByteArrayOutputStream baos = new ByteArrayOutputStream();
+         for (int i = 0; i < iterations; i++) {
+             bb.putDouble(0, Math.random());
+             baos.write(bb.array(), 0, DATA_SIZE);
+         }
+         data = baos.toByteArray();
+
+         try (FileOutputStream fos = new FileOutputStream(largeFile);
+              BufferedOutputStream bos = new BufferedOutputStream(fos);
+              ZipOutputStream zos = new ZipOutputStream(bos))
+         {
+             long length = 0;
+             while (length < fileSize) {
+                 ZipEntry ze = new ZipEntry("entry-" + length);
+                 lastEntryName = ze.getName();
+                 zos.putNextEntry(ze);
+                 zos.write(data, 0, data.length);
+                 zos.closeEntry();
+                 length = largeFile.length();
+             }
+             System.out.println("Last entry written is " + lastEntryName);
+         }
+     }
+
+     private static byte buf[] = new byte[4096];
+
+     static void checkEntry(ZipEntry e, InputStream is) throws Throwable {
+         long N = 0;
+         int n = 0;
+         while ((n = is.read(buf)) >= 0) {
+            N += n;
+         }
+         check(N == e.getSize());
+     }
 
-        if (!userFile && !debug) {
-            check(largeFile.delete());
-            check(testDir.delete());
-        }
-    }
+     static void readLargeZip1() throws Throwable {
+          ZipFile zipFile = new ZipFile(largeFile);
+          ZipEntry entry = null;
+          String entryName = null;
+          int count = 0;
+          System.out.println("ZipFile:");
+          Enumeration<? extends ZipEntry> entries = zipFile.entries();
+          while (entries.hasMoreElements()) {
+               entry = entries.nextElement();
+               entryName = entry.getName();
+               System.out.println("    checking " + entryName);
+               if (!entry.isDirectory()) {
+                    try (InputStream zeis = zipFile.getInputStream(entry)) {
+                        checkEntry(entry, zeis);
+                    }
+               }
+               count++;
+          }
+          System.out.println("Number of entries read: " + count);
+          check(!entry.isDirectory());
+          if (userFile || check(entryName.equals(lastEntryName))) {
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               InputStream is = zipFile.getInputStream(entry);
+               int len;
+               while ((len = is.read(buf)) >= 0) {
+                    baos.write(buf, 0, len);
+               }
+               baos.close();
+               is.close();
+               if (!userFile)
+                   check(Arrays.equals(data, baos.toByteArray()));
+          }
+     }
 
-    static void createLargeZip() throws Throwable {
-        int iterations = DATA_LEN / DATA_SIZE;
-        ByteBuffer bb = ByteBuffer.allocate(DATA_SIZE);
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        for (int i = 0; i < iterations; i++) {
-            bb.putDouble(0, Math.random());
-            baos.write(bb.array(), 0, DATA_SIZE);
-        }
-        data = baos.toByteArray();
+     static void readLargeZip2() throws Throwable {
+         System.out.println("ZipInputStream:");
+         try (FileInputStream fis = new FileInputStream(largeFile);
+              BufferedInputStream bis = new BufferedInputStream(fis);
+              ZipInputStream zis = new ZipInputStream(bis))
+         {
+             ZipEntry entry = null;
+             String entryName = null;
+             int count = 0;
+             while ((entry = zis.getNextEntry()) != null) {
+                  entryName = entry.getName();
 
-        try (FileOutputStream fos = new FileOutputStream(largeFile);
-             BufferedOutputStream bos = new BufferedOutputStream(fos);
-             ZipOutputStream zos = new ZipOutputStream(bos))
-        {
-            long length = 0;
-            while (length < fileSize) {
-                ZipEntry ze = new ZipEntry("entry-" + length);
-                lastEntryName = ze.getName();
-                zos.putNextEntry(ze);
-                zos.write(data, 0, data.length);
-                zos.closeEntry();
-                length = largeFile.length();
-            }
-            System.out.println("Last entry written is " + lastEntryName);
-        }
-    }
+                  System.out.println("    checking " + entryName +
+                                     ", method=" + entry.getMethod());
+                  if (entryName.equals(lastEntryName)) {
+                       break;
+                  }
+                  if (!entry.isDirectory()) {
+                       checkEntry(entry, zis);
+                  }
+                  count++;
+             }
+             System.out.println("Number of entries read: " + count);
+             System.out.println("Last entry read is " + entryName);
+             if (!userFile) {
+                  check(!entry.isDirectory());
+                  ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                  byte buf[] = new byte[4096];
+                  int len;
+                  while ((len = zis.read(buf)) >= 0) {
+                       baos.write(buf, 0, len);
+                  }
+                  baos.close();
+                  check(Arrays.equals(data, baos.toByteArray()));
+                  check(zis.getNextEntry() == null);
+             }
+         }
+     }
 
-    static void readLargeZip1() throws Throwable {
-        ZipFile zipFile = new ZipFile(largeFile);
-        ZipEntry entry = null;
-        String entryName = null;
-        int count = 0;
-        Enumeration<? extends ZipEntry> entries = zipFile.entries();
-        while (entries.hasMoreElements()) {
-            entry = entries.nextElement();
-            entryName = entry.getName();
-            count++;
-        }
-        System.out.println("Number of entries read: " + count);
-        System.out.println("Last entry read is " + entryName);
-        check(!entry.isDirectory());
-        if (check(entryName.equals(lastEntryName))) {
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            InputStream is = zipFile.getInputStream(entry);
-            byte buf[] = new byte[4096];
-            int len;
-            while ((len = is.read(buf)) >= 0) {
-                baos.write(buf, 0, len);
-            }
-            baos.close();
-            is.close();
-            check(Arrays.equals(data, baos.toByteArray()));
-        }
-    }
+     private static void updateFile(FileSystem fs, Path src) throws IOException {
+          Path dst = fs.getPath(src.toString());
+          Path parent = dst.getParent();
+          if (parent != null && Files.notExists(parent))
+               Files.createDirectories(parent);
+          Files.copy(src, dst, REPLACE_EXISTING);
+     }
+
+     private static FileSystemProvider getZipFSProvider() {
+         for (FileSystemProvider provider : FileSystemProvider.installedProviders()) {
+              if ("jar".equalsIgnoreCase(provider.getScheme()))
+                   return provider;
+         }
+         return null;
+     }
+
+     static void updateLargeZip(String pName) throws Throwable {
+         FileSystemProvider provider = getZipFSProvider();
+         if (provider == null) {
+             System.err.println("ZIP filesystem provider is not installed");
+             System.exit(1);
+         }
+         Map<String, Object> env = env = new HashMap<>();
+         try (FileSystem fs = provider.newFileSystem(largeFile.toPath(), env)) {
+             Path path = FileSystems.getDefault().getPath(pName);
+             Files.walkFileTree(
+                 path,
+                 new SimpleFileVisitor<Path>() {
+                     @Override
+                     public FileVisitResult visitFile(Path file,
+                                                      BasicFileAttributes attrs)
+                         throws IOException
+                     {
+                         updateFile(fs, file);
+                         return FileVisitResult.CONTINUE;
+                     }
+             });
+         }
+     }
 
 
-    static void readLargeZip2() throws Throwable {
-        try (FileInputStream fis = new FileInputStream(largeFile);
-             BufferedInputStream bis = new BufferedInputStream(fis);
-             ZipInputStream zis = new ZipInputStream(bis))
-        {
-            ZipEntry entry = null;
-            String entryName = null;
-            int count = 0;
-            while ((entry = zis.getNextEntry()) != null) {
-                entryName = entry.getName();
-                if (entryName.equals(lastEntryName)) {
-                    break;
-                }
-                count++;
-            }
-            System.out.println("Number of entries read: " + count);
-            System.out.println("Last entry read is " + entryName);
-            check(!entry.isDirectory());
-
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-
-            byte buf[] = new byte[4096];
-            int len;
-            while ((len = zis.read(buf)) >= 0) {
-                baos.write(buf, 0, len);
-            }
-            baos.close();
-            check(Arrays.equals(data, baos.toByteArray()));
-            check(zis.getNextEntry() == null);
-        }
-    }
-
-
-    //--------------------- Infrastructure ---------------------------
-    static volatile int passed = 0, failed = 0;
-    static void pass() {passed++;}
-    static void pass(String msg) {System.out.println(msg); passed++;}
-    static void fail() {failed++; Thread.dumpStack();}
-    static void fail(String msg) {System.out.println(msg); fail();}
-    static void unexpected(Throwable t) {failed++; t.printStackTrace();}
-    static void unexpected(Throwable t, String msg) {
-        System.out.println(msg); failed++; t.printStackTrace();}
-    static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
-    static void equal(Object x, Object y) {
-        if (x == null ? y == null : x.equals(y)) pass();
-        else fail(x + " not equal to " + y);}
-    public static void main(String[] args) throws Throwable {
-        try {realMain(args);} catch (Throwable t) {unexpected(t);}
-        System.out.println("\nPassed = " + passed + " failed = " + failed);
-        if (failed > 0) throw new AssertionError("Some tests failed");}
+     //--------------------- Infrastructure ---------------------------
+     static volatile int passed = 0, failed = 0;
+     static void pass() {passed++;}
+     static void pass(String msg) {System.out.println(msg); passed++;}
+     static void fail() {failed++; Thread.dumpStack();}
+     static void fail(String msg) {System.out.println(msg); fail();}
+     static void unexpected(Throwable t) {failed++; t.printStackTrace();}
+     static void unexpected(Throwable t, String msg) {
+         System.out.println(msg); failed++; t.printStackTrace();}
+     static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
+     static void equal(Object x, Object y) {
+          if (x == null ? y == null : x.equals(y)) pass();
+          else fail(x + " not equal to " + y);}
+     public static void main(String[] args) throws Throwable {
+          try {realMain(args);} catch (Throwable t) {unexpected(t);}
+          System.out.println("\nPassed = " + passed + " failed = " + failed);
+          if (failed > 0) throw new AssertionError("Some tests failed");}
 }