8064601: Improve jar file handling
authorsherman
Wed, 28 Jan 2015 12:36:25 -0800
changeset 29914 48393dc87f88
parent 29913 95258013e132
child 29915 88af03f531f0
8064601: Improve jar file handling Reviewed-by: alanb, coffeys
jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java
jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java	Mon Jan 19 20:02:31 2015 +0300
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java	Wed Jan 28 12:36:25 2015 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2015, 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
@@ -74,8 +74,9 @@
      * Mflag: DO NOT generate a manifest file (just ZIP)
      * iflag: generate jar index
      * nflag: Perform jar normalization at the end
+     * pflag: preserve/don't strip leading slash and .. component from file name
      */
-    boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag, nflag;
+    boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag, nflag, pflag;
 
     static final String MANIFEST_DIR = "META-INF/";
     static final String VERSION = "1.0";
@@ -187,6 +188,7 @@
                         addMainClass(manifest, ename);
                     }
                 }
+                expand(null, files, false);
                 OutputStream out;
                 if (fname != null) {
                     out = new FileOutputStream(fname);
@@ -208,7 +210,6 @@
                     tmpfile = createTemporaryFile(tmpbase, ".jar");
                     out = new FileOutputStream(tmpfile);
                 }
-                expand(null, files, false);
                 create(new BufferedOutputStream(out, 4096), manifest);
                 if (in != null) {
                     in.close();
@@ -424,6 +425,9 @@
                 case 'e':
                      ename = args[count++];
                      break;
+                case 'P':
+                     pflag = true;
+                     break;
                 default:
                     error(formatMsg("error.illegal.option",
                                 String.valueOf(flags.charAt(i))));
@@ -713,6 +717,47 @@
         return true;
     }
 
+    private static final boolean isWinDriveLetter(char c) {
+        return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
+    }
+
+    private String safeName(String name) {
+        if (!pflag) {
+            int len = name.length();
+            int i = name.lastIndexOf("../");
+            if (i == -1) {
+                i = 0;
+            } else {
+                i += 3; // strip any dot-dot components
+            }
+            if (File.separatorChar == '\\') {
+                // the spec requests no drive letter. skip if
+                // the entry name has one.
+                while (i < len) {
+                    int off = i;
+                    if (i + 1 < len &&
+                        name.charAt(i + 1) == ':' &&
+                        isWinDriveLetter(name.charAt(i))) {
+                        i += 2;
+                    }
+                    while (i < len && name.charAt(i) == '/') {
+                        i++;
+                    }
+                    if (i == off) {
+                        break;
+                    }
+                }
+            } else {
+                while (i < len && name.charAt(i) == '/') {
+                    i++;
+                }
+            }
+            if (i != 0) {
+                name = name.substring(i);
+            }
+        }
+        return name;
+    }
 
     private String entryName(String name) {
         name = name.replace(File.separatorChar, '/');
@@ -723,11 +768,10 @@
                 matchPath = path;
             }
         }
-        name = name.substring(matchPath.length());
-
-        if (name.startsWith("/")) {
-            name = name.substring(1);
-        } else if (name.startsWith("./")) {
+        name = safeName(name.substring(matchPath.length()));
+        // the old implementaton doesn't remove
+        // "./" if it was led by "/" (?)
+        if (name.startsWith("./")) {
             name = name.substring(2);
         }
         return name;
@@ -927,8 +971,11 @@
         for (ZipEntry ze : zes) {
             long lastModified = ze.getTime();
             if (lastModified != -1) {
-                File f = new File(ze.getName().replace('/', File.separatorChar));
-                f.setLastModified(lastModified);
+                String name = safeName(ze.getName().replace(File.separatorChar, '/'));
+                if (name.length() != 0) {
+                    File f = new File(name.replace('/', File.separatorChar));
+                    f.setLastModified(lastModified);
+                }
             }
         }
     }
@@ -1002,8 +1049,16 @@
      */
     ZipEntry extractFile(InputStream is, ZipEntry e) throws IOException {
         ZipEntry rc = null;
-        String name = e.getName();
-        File f = new File(e.getName().replace('/', File.separatorChar));
+        // The spec requres all slashes MUST be forward '/', it is possible
+        // an offending zip/jar entry may uses the backwards slash in its
+        // name. It might cause problem on Windows platform as it skips
+        // our "safe" check for leading slahs and dot-dot. So replace them
+        // with '/'.
+        String name = safeName(e.getName().replace(File.separatorChar, '/'));
+        if (name.length() == 0) {
+            return rc;    // leading '/' or 'dot-dot' only path
+        }
+        File f = new File(name.replace('/', File.separatorChar));
         if (e.isDirectory()) {
             if (f.exists()) {
                 if (!f.isDirectory()) {
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties	Mon Jan 19 20:02:31 2015 +0300
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties	Wed Jan 28 12:36:25 2015 -0800
@@ -68,7 +68,7 @@
         (in = {0}) (out= {1})
 
 usage=\
-Usage: jar {ctxui}[vfmn0Me] [jar-file] [manifest-file] [entry-point] [-C dir] files ...\n\
+Usage: jar {ctxui}[vfmn0PMe] [jar-file] [manifest-file] [entry-point] [-C dir] files ...\n\
 Options:\n\
 \ \   -c  create new archive\n\
 \ \   -t  list table of contents for archive\n\
@@ -81,6 +81,7 @@
 \ \   -e  specify application entry point for stand-alone application \n\
 \ \       bundled into an executable jar file\n\
 \ \   -0  store only; use no ZIP compression\n\
+\ \   -P  preserve leading '/' (absolute path) and ".." (parent directory) components from file names\n\
 \ \   -M  do not create a manifest file for the entries\n\
 \ \   -i  generate index information for the specified jar files\n\
 \ \   -C  change to the specified directory and include the following file\n\