1 /*
   2  * Copyright (c) 2019, 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 import java.io.IOException;
  25 import java.io.InputStream;
  26 import java.nio.file.FileVisitResult;
  27 import java.nio.file.Files;
  28 import java.nio.file.Path;
  29 import java.nio.file.Paths;
  30 import java.nio.file.SimpleFileVisitor;
  31 import java.nio.file.attribute.BasicFileAttributes;
  32 import java.util.ArrayList;
  33 import java.util.List;
  34 import java.util.Properties;
  35 import java.util.zip.ZipEntry;
  36 import java.util.zip.ZipInputStream;
  37 
  38 /*
  39  * @test
  40  * @bug 8226346
  41  * @summary Check all output files for absolute path fragments
  42  * @requires !vm.debug
  43  * @run main AbsPathsInImage
  44  */
  45 public class AbsPathsInImage {
  46 
  47     // Set this property on command line to scan an alternate dir or file:
  48     // JTREG=JAVA_OPTIONS=-Djdk.test.build.AbsPathInImage.dir=/path/to/dir
  49     public static final String DIR_PROPERTY = "jdk.test.build.AbsPathsInImage.dir";
  50 
  51     private boolean matchFound = false;
  52 
  53     public static void main(String[] args) throws Exception {
  54         String jdkPathString = System.getProperty("test.jdk");
  55         Path jdkHome = Paths.get(jdkPathString);
  56 
  57         Path dirToScan = jdkHome;
  58         String overrideDir = System.getProperty(DIR_PROPERTY);
  59         if (overrideDir != null) {
  60             dirToScan = Paths.get(overrideDir);
  61         }
  62 
  63         String buildWorkspaceRoot = null;
  64         String buildOutputRoot = null;
  65         String testImageDirString = System.getenv("TEST_IMAGE_DIR");
  66         if (testImageDirString != null) {
  67             Path testImageDir = Paths.get(testImageDirString);
  68             Path buildInfoPropertiesFile = testImageDir.resolve("build-info.properties");
  69             System.out.println("Getting patterns from " + buildInfoPropertiesFile.toString());
  70             Properties buildInfoProperties = new Properties();
  71             try (InputStream inStream = Files.newInputStream(buildInfoPropertiesFile)) {
  72                 buildInfoProperties.load(inStream);
  73             }
  74             buildWorkspaceRoot = buildInfoProperties.getProperty("build.workspace.root");
  75             buildOutputRoot = buildInfoProperties.getProperty("build.output.root");
  76         } else {
  77             System.out.println("Getting patterns from local environment");
  78             // Try to resolve the workspace root based on the jtreg test root dir
  79             String testRootDirString = System.getProperty("test.root");
  80             if (testRootDirString != null) {
  81                 Path testRootDir = Paths.get(testRootDirString);
  82                 // Remove /test/jdk suffix
  83                 buildWorkspaceRoot = testRootDir.getParent().getParent().toString();
  84             }
  85             // Remove /jdk
  86             Path buildOutputRootPath = jdkHome.getParent();
  87             if (buildOutputRootPath.endsWith("images")) {
  88                 buildOutputRootPath = buildOutputRootPath.getParent();
  89             }
  90             buildOutputRoot = buildOutputRootPath.toString();
  91         }
  92         if (buildWorkspaceRoot == null) {
  93             throw new Error("Could not find workspace root, test cannot run");
  94         }
  95         if (buildOutputRoot == null) {
  96             throw new Error("Could not find build output root, test cannot run");
  97         }
  98 
  99         List<byte[]> searchPatterns = new ArrayList<>();
 100         expandPatterns(searchPatterns, buildWorkspaceRoot);
 101         expandPatterns(searchPatterns, buildOutputRoot);
 102 
 103         System.out.println("Looking for:");
 104         for (byte[] searchPattern : searchPatterns) {
 105             System.out.println(new String(searchPattern));
 106         }
 107         System.out.println();
 108 
 109         AbsPathsInImage absPathsInImage = new AbsPathsInImage();
 110         absPathsInImage.scanFiles(dirToScan, searchPatterns);
 111 
 112         if (absPathsInImage.matchFound) {
 113             throw new Exception("Test failed");
 114         }
 115     }
 116 
 117     /**
 118      * Add path pattern to list of patterns to search for. Create all possible
 119      * variants depending on platform.
 120      */
 121     private static void expandPatterns(List<byte[]> searchPatterns, String pattern) {
 122         if (System.getProperty("os.name").toLowerCase().contains("windows")) {
 123             String forward = pattern.replace('\\', '/');
 124             String back = pattern.replace('/', '\\');
 125             if (pattern.charAt(1) == ':') {
 126                 String forwardUpper = String.valueOf(pattern.charAt(0)).toUpperCase() + forward.substring(1);
 127                 String forwardLower = String.valueOf(pattern.charAt(0)).toLowerCase() + forward.substring(1);
 128                 String backUpper = String.valueOf(pattern.charAt(0)).toUpperCase() + back.substring(1);
 129                 String backLower = String.valueOf(pattern.charAt(0)).toLowerCase() + back.substring(1);
 130                 searchPatterns.add(forwardUpper.getBytes());
 131                 searchPatterns.add(forwardLower.getBytes());
 132                 searchPatterns.add(backUpper.getBytes());
 133                 searchPatterns.add(backLower.getBytes());
 134             } else {
 135                 searchPatterns.add(forward.getBytes());
 136                 searchPatterns.add(back.getBytes());
 137             }
 138         } else {
 139             searchPatterns.add(pattern.getBytes());
 140         }
 141     }
 142 
 143     private void scanFiles(Path root, List<byte[]> searchPatterns) throws IOException {
 144         Files.walkFileTree(root, new SimpleFileVisitor<>() {
 145             @Override
 146             public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
 147                 String fileName = file.toString();
 148                 if (Files.isSymbolicLink(file)) {
 149                     return super.visitFile(file, attrs);
 150                 } else if (fileName.endsWith(".debuginfo") || fileName.endsWith(".pdb")) {
 151                     // Do nothing
 152                 } else if (fileName.endsWith("jvm.dll")) {
 153                     // On Windows, the Microsoft toolchain does not provide a way
 154                     // to reliably remove all absolute paths from __FILE__ usage.
 155                     // Until that is fixed, we simply exclude jvm.dll from this
 156                     // test.
 157                 } else if (fileName.endsWith(".zip")) {
 158                     scanZipFile(file, searchPatterns);
 159                 } else {
 160                     scanFile(file, searchPatterns);
 161                 }
 162                 return super.visitFile(file, attrs);
 163             }
 164         });
 165     }
 166 
 167     private void scanFile(Path file, List<byte[]> searchPatterns) throws IOException {
 168         List<String> matches = scanBytes(Files.readAllBytes(file), searchPatterns);
 169         if (matches.size() > 0) {
 170             System.out.println(file + ":");
 171             for (String match : matches) {
 172                 System.out.println(match);
 173             }
 174             System.out.println();
 175         }
 176     }
 177 
 178     private void scanZipFile(Path zipFile, List<byte[]> searchPatterns) throws IOException {
 179         try (ZipInputStream zipInputStream = new ZipInputStream(Files.newInputStream(zipFile))) {
 180             ZipEntry zipEntry;
 181             while ((zipEntry = zipInputStream.getNextEntry()) != null) {
 182                 List<String> matches = scanBytes(zipInputStream.readAllBytes(), searchPatterns);
 183                 if (matches.size() > 0) {
 184                     System.out.println(zipFile + ", " + zipEntry.getName() + ":");
 185                     for (String match : matches) {
 186                         System.out.println(match);
 187                     }
 188                     System.out.println();
 189                 }
 190             }
 191         }
 192     }
 193 
 194     private List<String> scanBytes(byte[] data, List<byte[]> searchPatterns) {
 195         List<String> matches = new ArrayList<>();
 196         for (int i = 0; i < data.length; i++) {
 197             for (byte[] searchPattern : searchPatterns) {
 198                 boolean found = true;
 199                 for (int j = 0; j < searchPattern.length; j++) {
 200                     if ((i + j > data.length || data[i + j] != searchPattern[j])) {
 201                         found = false;
 202                         break;
 203                     }
 204                 }
 205                 if (found) {
 206                     matchFound = true;
 207                     matches.add(new String(data, charsStart(data, i), charsOffset(data, i, searchPattern.length)));
 208                     // No need to search the same string for multiple patterns
 209                     break;
 210                 }
 211             }
 212         }
 213         return matches;
 214     }
 215 
 216     private int charsStart(byte[] data, int startIndex) {
 217         int index = startIndex;
 218         while (--index > 0) {
 219             byte datum = data[index];
 220             if (datum < 32 || datum > 126) {
 221                 break;
 222             }
 223         }
 224         return index + 1;
 225     }
 226 
 227     private int charsOffset(byte[] data, int startIndex, int startOffset) {
 228         int offset = startOffset;
 229         while (startIndex + ++offset < data.length) {
 230             byte datum = data[startIndex + offset];
 231             if (datum < 32 || datum > 126) {
 232                 break;
 233             }
 234         }
 235         return offset;
 236     }
 237 }