| /* | |
| * Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package jdk.nio.zipfs; | |
| import java.nio.file.Paths; | |
| import java.util.Collections; | |
| import java.util.Map; | |
| import java.util.zip.ZipException; | |
| import static jdk.nio.zipfs.ZipConstants.*; | |
| import static jdk.nio.zipfs.ZipUtils.dosToJavaTime; | |
| import static jdk.nio.zipfs.ZipUtils.unixToJavaTime; | |
| import static jdk.nio.zipfs.ZipUtils.winToJavaTime; | |
| /** | |
| * Print all loc and cen headers of the ZIP file | |
| * | |
| * @author Xueming Shen | |
| */ | |
| public class ZipInfo { | |
| public static void main(String[] args) throws Throwable { | |
| if (args.length < 1) { | |
| print("Usage: java ZipInfo zfname"); | |
| } else { | |
| Map<String, ?> env = Collections.emptyMap(); | |
| ZipFileSystem zfs = (ZipFileSystem)(new ZipFileSystemProvider() | |
| .newFileSystem(Paths.get(args[0]), env)); | |
| byte[] cen = zfs.cen; | |
| if (cen == null) { | |
| print("zip file is empty%n"); | |
| return; | |
| } | |
| int pos = 0; | |
| byte[] buf = new byte[1024]; | |
| int no = 1; | |
| while (pos + CENHDR < cen.length) { | |
| print("----------------#%d--------------------%n", no++); | |
| printCEN(cen, pos); | |
| // use size CENHDR as the extra bytes to read, just in case the | |
| // loc.extra is bigger than the cen.extra, try to avoid to read | |
| // twice | |
| long len = LOCHDR + CENNAM(cen, pos) + CENEXT(cen, pos) + CENHDR; | |
| if (zfs.readNBytesAt(buf, 0, len, locoff(cen, pos)) != len) | |
| throw new ZipException("read loc header failed"); | |
| if (LOCEXT(buf) > CENEXT(cen, pos) + CENHDR) { | |
| // have to read the second time; | |
| len = LOCHDR + LOCNAM(buf) + LOCEXT(buf); | |
| if (zfs.readNBytesAt(buf, 0, len, locoff(cen, pos)) != len) | |
| throw new ZipException("read loc header failed"); | |
| } | |
| printLOC(buf); | |
| pos += CENHDR + CENNAM(cen, pos) + CENEXT(cen, pos) + CENCOM(cen, pos); | |
| } | |
| zfs.close(); | |
| } | |
| } | |
| private static void print(String fmt, Object... objs) { | |
| System.out.printf(fmt, objs); | |
| } | |
| private static void printLOC(byte[] loc) { | |
| print("%n"); | |
| print("[Local File Header]%n"); | |
| print(" Signature : %#010x%n", LOCSIG(loc)); | |
| if (LOCSIG(loc) != LOCSIG) { | |
| print(" Wrong signature!"); | |
| return; | |
| } | |
| print(" Version : %#6x [%d.%d]%n", | |
| LOCVER(loc), LOCVER(loc) / 10, LOCVER(loc) % 10); | |
| print(" Flag : %#6x%n", LOCFLG(loc)); | |
| print(" Method : %#6x%n", LOCHOW(loc)); | |
| print(" LastMTime : %#10x [%tc]%n", | |
| LOCTIM(loc), dosToJavaTime(LOCTIM(loc))); | |
| print(" CRC : %#10x%n", LOCCRC(loc)); | |
| print(" CSize : %#10x%n", LOCSIZ(loc)); | |
| print(" Size : %#10x%n", LOCLEN(loc)); | |
| print(" NameLength : %#6x [%s]%n", | |
| LOCNAM(loc), new String(loc, LOCHDR, LOCNAM(loc))); | |
| print(" ExtraLength : %#6x%n", LOCEXT(loc)); | |
| if (LOCEXT(loc) != 0) | |
| printExtra(loc, LOCHDR + LOCNAM(loc), LOCEXT(loc)); | |
| } | |
| private static void printCEN(byte[] cen, int off) { | |
| print("[Central Directory Header]%n"); | |
| print(" Signature : %#010x%n", CENSIG(cen, off)); | |
| if (CENSIG(cen, off) != CENSIG) { | |
| print(" Wrong signature!"); | |
| return; | |
| } | |
| print(" VerMadeby : %#6x [%d, %d.%d]%n", | |
| CENVEM(cen, off), (CENVEM(cen, off) >> 8), | |
| (CENVEM(cen, off) & 0xff) / 10, | |
| (CENVEM(cen, off) & 0xff) % 10); | |
| print(" VerExtract : %#6x [%d.%d]%n", | |
| CENVER(cen, off), CENVER(cen, off) / 10, CENVER(cen, off) % 10); | |
| print(" Flag : %#6x%n", CENFLG(cen, off)); | |
| print(" Method : %#6x%n", CENHOW(cen, off)); | |
| print(" LastMTime : %#10x [%tc]%n", | |
| CENTIM(cen, off), dosToJavaTime(CENTIM(cen, off))); | |
| print(" CRC : %#10x%n", CENCRC(cen, off)); | |
| print(" CSize : %#10x%n", CENSIZ(cen, off)); | |
| print(" Size : %#10x%n", CENLEN(cen, off)); | |
| print(" NameLen : %#6x [%s]%n", | |
| CENNAM(cen, off), new String(cen, off + CENHDR, CENNAM(cen, off))); | |
| print(" ExtraLen : %#6x%n", CENEXT(cen, off)); | |
| if (CENEXT(cen, off) != 0) | |
| printExtra(cen, off + CENHDR + CENNAM(cen, off), CENEXT(cen, off)); | |
| print(" CommentLen : %#6x%n", CENCOM(cen, off)); | |
| print(" DiskStart : %#6x%n", CENDSK(cen, off)); | |
| print(" Attrs : %#6x%n", CENATT(cen, off)); | |
| print(" AttrsEx : %#10x%n", CENATX(cen, off)); | |
| print(" LocOff : %#10x%n", CENOFF(cen, off)); | |
| } | |
| private static long locoff(byte[] cen, int pos) { | |
| long locoff = CENOFF(cen, pos); | |
| if (locoff == ZIP64_MINVAL) { //ZIP64 | |
| int off = pos + CENHDR + CENNAM(cen, pos); | |
| int end = off + CENEXT(cen, pos); | |
| while (off + 4 < end) { | |
| int tag = SH(cen, off); | |
| int sz = SH(cen, off + 2); | |
| if (tag != EXTID_ZIP64) { | |
| off += 4 + sz; | |
| continue; | |
| } | |
| off += 4; | |
| if (CENLEN(cen, pos) == ZIP64_MINVAL) | |
| off += 8; | |
| if (CENSIZ(cen, pos) == ZIP64_MINVAL) | |
| off += 8; | |
| return LL(cen, off); | |
| } | |
| // should never be here | |
| } | |
| return locoff; | |
| } | |
| private static void printExtra(byte[] extra, int off, int len) { | |
| int end = off + len; | |
| while (off + 4 <= end) { | |
| int tag = SH(extra, off); | |
| int sz = SH(extra, off + 2); | |
| print(" [tag=0x%04x, sz=%d, data= ", tag, sz); | |
| if (off + sz > end) { | |
| print(" Error: Invalid extra data, beyond extra length"); | |
| break; | |
| } | |
| off += 4; | |
| for (int i = 0; i < sz; i++) | |
| print("%02x ", extra[off + i]); | |
| print("]%n"); | |
| switch (tag) { | |
| case EXTID_ZIP64 : | |
| print(" ->ZIP64: "); | |
| int pos = off; | |
| while (pos + 8 <= off + sz) { | |
| print(" *0x%x ", LL(extra, pos)); | |
| pos += 8; | |
| } | |
| print("%n"); | |
| break; | |
| case EXTID_NTFS: | |
| print(" ->PKWare NTFS%n"); | |
| // 4 bytes reserved | |
| if (SH(extra, off + 4) != 0x0001 || SH(extra, off + 6) != 24) | |
| print(" Error: Invalid NTFS sub-tag or subsz"); | |
| print(" mtime:%tc%n", | |
| winToJavaTime(LL(extra, off + 8))); | |
| print(" atime:%tc%n", | |
| winToJavaTime(LL(extra, off + 16))); | |
| print(" ctime:%tc%n", | |
| winToJavaTime(LL(extra, off + 24))); | |
| break; | |
| case EXTID_EXTT: | |
| print(" ->Info-ZIP Extended Timestamp: flag=%x%n",extra[off]); | |
| pos = off + 1 ; | |
| while (pos + 4 <= off + sz) { | |
| print(" *%tc%n", | |
| unixToJavaTime(LG(extra, pos))); | |
| pos += 4; | |
| } | |
| break; | |
| default: | |
| print(" ->[tag=%x, size=%d]%n", tag, sz); | |
| } | |
| off += sz; | |
| } | |
| } | |
| } |