# HG changeset patch # User sherman # Date 1314398181 25200 # Node ID 8a89ee82068742b1218482d48fd243596c4e7ef1 # Parent b4aebbfc5b3a72209076902f6b2277d5e7fe062f 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 diff -r b4aebbfc5b3a -r 8a89ee820687 jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileSystem.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 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 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) diff -r b4aebbfc5b3a -r 8a89ee820687 jdk/test/java/util/zip/LargeZip.java --- 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 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 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 env = env = new HashMap<>(); + try (FileSystem fs = provider.newFileSystem(largeFile.toPath(), env)) { + Path path = FileSystems.getDefault().getPath(pName); + Files.walkFileTree( + path, + new SimpleFileVisitor() { + @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");} }