1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.tradefed.util; 17 18 import com.android.tradefed.log.LogUtil.CLog; 19 20 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; 21 import org.apache.commons.compress.archivers.zip.ZipFile; 22 23 import java.io.File; 24 import java.io.IOException; 25 import java.nio.file.Files; 26 import java.util.Enumeration; 27 import java.util.HashSet; 28 import java.util.Set; 29 30 /** 31 * A helper class for zip extraction that takes POSIX file permissions into account 32 */ 33 public class ZipUtil2 { 34 35 /** 36 * A util method to apply unix mode from {@link ZipArchiveEntry} to the created local file 37 * system entry if necessary 38 * 39 * @param entry the entry inside zipfile (potentially contains mode info) 40 * @param localFile the extracted local file entry 41 * @return True if the Unix permissions are set, false otherwise. 42 * @throws IOException 43 */ applyUnixModeIfNecessary(ZipArchiveEntry entry, File localFile)44 private static boolean applyUnixModeIfNecessary(ZipArchiveEntry entry, File localFile) 45 throws IOException { 46 if (entry.getPlatform() == ZipArchiveEntry.PLATFORM_UNIX) { 47 Files.setPosixFilePermissions(localFile.toPath(), 48 FileUtil.unixModeToPosix(entry.getUnixMode())); 49 return true; 50 } 51 return false; 52 } 53 54 /** 55 * Utility method to extract entire contents of zip file into given directory 56 * 57 * @param zipFile the {@link ZipFile} to extract 58 * @param destDir the local dir to extract file to 59 * @throws IOException if failed to extract file 60 */ extractZip(ZipFile zipFile, File destDir)61 public static void extractZip(ZipFile zipFile, File destDir) throws IOException { 62 Enumeration<? extends ZipArchiveEntry> entries = zipFile.getEntries(); 63 Set<String> noPermissions = new HashSet<>(); 64 while (entries.hasMoreElements()) { 65 ZipArchiveEntry entry = entries.nextElement(); 66 File childFile = new File(destDir, entry.getName()); 67 childFile.getParentFile().mkdirs(); 68 if (entry.isDirectory()) { 69 childFile.mkdirs(); 70 if (!applyUnixModeIfNecessary(entry, childFile)) { 71 noPermissions.add(entry.getName()); 72 } 73 continue; 74 } else { 75 FileUtil.writeToFile(zipFile.getInputStream(entry), childFile); 76 if (!applyUnixModeIfNecessary(entry, childFile)) { 77 noPermissions.add(entry.getName()); 78 } 79 } 80 } 81 if (!noPermissions.isEmpty()) { 82 CLog.d( 83 "Entries '%s' exist but do not contain Unix mode permission info. Files will " 84 + "have default permission.", 85 noPermissions); 86 } 87 } 88 89 /** 90 * Utility method to extract a zip file into a given directory. The zip file being presented as 91 * a {@link File}. 92 * 93 * @param zipFile a {@link File} pointing to a zip file. 94 * @param destDir the local dir to extract file to 95 * @throws IOException if failed to extract file 96 */ extractZip(File zipFile, File destDir)97 public static void extractZip(File zipFile, File destDir) throws IOException { 98 try (ZipFile zip = new ZipFile(zipFile)) { 99 extractZip(zip, destDir); 100 } 101 } 102 103 /** 104 * Utility method to extract one specific file from zip file into a tmp file 105 * 106 * @param zipFile the {@link ZipFile} to extract 107 * @param filePath the filePath of to extract 108 * @throws IOException if failed to extract file 109 * @return the {@link File} or null if not found 110 */ extractFileFromZip(ZipFile zipFile, String filePath)111 public static File extractFileFromZip(ZipFile zipFile, String filePath) throws IOException { 112 ZipArchiveEntry entry = zipFile.getEntry(filePath); 113 if (entry == null) { 114 return null; 115 } 116 File createdFile = FileUtil.createTempFile("extracted", 117 FileUtil.getExtension(filePath)); 118 FileUtil.writeToFile(zipFile.getInputStream(entry), createdFile); 119 applyUnixModeIfNecessary(entry, createdFile); 120 return createdFile; 121 } 122 123 /** 124 * Extract a zip file to a temp directory prepended with a string 125 * 126 * @param zipFile the zip file to extract 127 * @param nameHint a prefix for the temp directory 128 * @return a {@link File} pointing to the temp directory 129 */ extractZipToTemp(File zipFile, String nameHint)130 public static File extractZipToTemp(File zipFile, String nameHint) throws IOException { 131 File localRootDir = FileUtil.createTempDir(nameHint); 132 try (ZipFile zip = new ZipFile(zipFile)) { 133 extractZip(zip, localRootDir); 134 return localRootDir; 135 } catch (IOException e) { 136 // clean tmp file since we couldn't extract. 137 FileUtil.recursiveDelete(localRootDir); 138 throw e; 139 } 140 } 141 142 /** 143 * Close an open {@link ZipFile}, ignoring any exceptions. 144 * 145 * @param zipFile the file to close 146 */ closeZip(ZipFile zipFile)147 public static void closeZip(ZipFile zipFile) { 148 if (zipFile != null) { 149 try { 150 zipFile.close(); 151 } catch (IOException e) { 152 // ignore 153 } 154 } 155 } 156 } 157