1 // Copyright 2022 Code Intelligence GmbH 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package com.code_intelligence.jazzer.tools; 16 17 import java.io.IOException; 18 import java.net.URI; 19 import java.net.URISyntaxException; 20 import java.nio.file.FileSystem; 21 import java.nio.file.FileSystems; 22 import java.nio.file.Files; 23 import java.nio.file.Path; 24 import java.nio.file.Paths; 25 import java.util.Arrays; 26 import java.util.Collections; 27 import java.util.Comparator; 28 import java.util.HashMap; 29 import java.util.Map; 30 import java.util.TimeZone; 31 import java.util.stream.Collectors; 32 import java.util.stream.Stream; 33 34 public class JarStripper { 35 private static final Map<String, String> ZIP_FS_PROPERTIES = new HashMap<>(); 36 static { 37 // We copy the input to the output path before modifying, so don't try to create a new file at 38 // that path if something went wrong. 39 ZIP_FS_PROPERTIES.put("create", "false"); 40 } 41 main(String[] args)42 public static void main(String[] args) { 43 if (args.length < 2) { 44 System.err.println( 45 "Hermetically removes files and directories from .jar files by relative paths."); 46 System.err.println("Usage: in.jar out.jar [relative path]..."); 47 System.exit(1); 48 } 49 50 Path inFile = Paths.get(args[0]); 51 Path outFile = Paths.get(args[1]); 52 Iterable<String> pathsToDelete = 53 Collections.unmodifiableList(Arrays.stream(args).skip(2).collect(Collectors.toList())); 54 55 try { 56 Files.copy(inFile, outFile); 57 if (!outFile.toFile().setWritable(true)) { 58 System.err.printf("Failed to make %s writable", outFile); 59 System.exit(1); 60 } 61 } catch (IOException e) { 62 e.printStackTrace(); 63 System.exit(1); 64 } 65 66 URI outUri = null; 67 try { 68 outUri = new URI("jar", outFile.toUri().toString(), null); 69 } catch (URISyntaxException e) { 70 e.printStackTrace(); 71 System.exit(1); 72 } 73 74 // Ensure that the ZipFileSystem uses a system-independent time zone for mtimes. 75 // https://github.com/openjdk/jdk/blob/4d64076058a4ec5df101b06572195ed5fdee6f64/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipUtils.java#L241 76 TimeZone.setDefault(TimeZone.getTimeZone("UTC")); 77 78 try (FileSystem zipFs = FileSystems.newFileSystem(outUri, ZIP_FS_PROPERTIES)) { 79 for (String pathToDelete : pathsToDelete) { 80 // Visit files before the directory they are contained in by sorting in reverse order. 81 try (Stream<Path> walk = Files.walk(zipFs.getPath(pathToDelete))) { 82 Iterable<Path> subpaths = 83 walk.sorted(Comparator.reverseOrder()).collect(Collectors.toList()); 84 for (Path subpath : subpaths) { 85 Files.delete(subpath); 86 } 87 } 88 } 89 } catch (IOException e) { 90 e.printStackTrace(); 91 System.exit(1); 92 } 93 } 94 } 95