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