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 
28 /**
29  * A helper class for zip extraction that takes POSIX file permissions into account
30  */
31 public class ZipUtil2 {
32 
33     /**
34      * A util method to apply unix mode from {@link ZipArchiveEntry} to the created local file
35      * system entry if necessary
36      * @param entry the entry inside zipfile (potentially contains mode info)
37      * @param localFile the extracted local file entry
38      * @throws IOException
39      */
applyUnixModeIfNecessary(ZipArchiveEntry entry, File localFile)40     private static void applyUnixModeIfNecessary(ZipArchiveEntry entry, File localFile)
41             throws IOException {
42         if (entry.getPlatform() == ZipArchiveEntry.PLATFORM_UNIX) {
43             Files.setPosixFilePermissions(localFile.toPath(),
44                     FileUtil.unixModeToPosix(entry.getUnixMode()));
45         } else {
46             CLog.d(
47                     "Entry '%s' exists but does not contain Unix mode permission info. File will "
48                             + "have default permission.",
49                     entry.getName());
50         }
51     }
52 
53     /**
54      * Utility method to extract entire contents of zip file into given directory
55      *
56      * @param zipFile the {@link ZipFile} to extract
57      * @param destDir the local dir to extract file to
58      * @throws IOException if failed to extract file
59      */
extractZip(ZipFile zipFile, File destDir)60     public static void extractZip(ZipFile zipFile, File destDir) throws IOException {
61         Enumeration<? extends ZipArchiveEntry> entries = zipFile.getEntries();
62         while (entries.hasMoreElements()) {
63             ZipArchiveEntry entry = entries.nextElement();
64             File childFile = new File(destDir, entry.getName());
65             childFile.getParentFile().mkdirs();
66             if (entry.isDirectory()) {
67                 childFile.mkdirs();
68                 applyUnixModeIfNecessary(entry, childFile);
69                 continue;
70             } else {
71                 FileUtil.writeToFile(zipFile.getInputStream(entry), childFile);
72                 applyUnixModeIfNecessary(entry, childFile);
73             }
74         }
75     }
76 
77     /**
78      * Utility method to extract a zip file into a given directory. The zip file being presented as
79      * a {@link File}.
80      *
81      * @param zipFile a {@link File} pointing to a zip file.
82      * @param destDir the local dir to extract file to
83      * @throws IOException if failed to extract file
84      */
extractZip(File zipFile, File destDir)85     public static void extractZip(File zipFile, File destDir) throws IOException {
86         try (ZipFile zip = new ZipFile(zipFile)) {
87             extractZip(zip, destDir);
88         }
89     }
90 
91     /**
92      * Utility method to extract one specific file from zip file into a tmp file
93      *
94      * @param zipFile the {@link ZipFile} to extract
95      * @param filePath the filePath of to extract
96      * @throws IOException if failed to extract file
97      * @return the {@link File} or null if not found
98      */
extractFileFromZip(ZipFile zipFile, String filePath)99     public static File extractFileFromZip(ZipFile zipFile, String filePath) throws IOException {
100         ZipArchiveEntry entry = zipFile.getEntry(filePath);
101         if (entry == null) {
102             return null;
103         }
104         File createdFile = FileUtil.createTempFile("extracted",
105                 FileUtil.getExtension(filePath));
106         FileUtil.writeToFile(zipFile.getInputStream(entry), createdFile);
107         applyUnixModeIfNecessary(entry, createdFile);
108         return createdFile;
109     }
110 
111     /**
112      * Extract a zip file to a temp directory prepended with a string
113      *
114      * @param zipFile the zip file to extract
115      * @param nameHint a prefix for the temp directory
116      * @return a {@link File} pointing to the temp directory
117      */
extractZipToTemp(File zipFile, String nameHint)118     public static File extractZipToTemp(File zipFile, String nameHint) throws IOException {
119         File localRootDir = FileUtil.createTempDir(nameHint);
120         try (ZipFile zip = new ZipFile(zipFile)) {
121             extractZip(zip, localRootDir);
122             return localRootDir;
123         } catch (IOException e) {
124             // clean tmp file since we couldn't extract.
125             FileUtil.recursiveDelete(localRootDir);
126             throw e;
127         }
128     }
129 
130     /**
131      * Close an open {@link ZipFile}, ignoring any exceptions.
132      *
133      * @param zipFile the file to close
134      */
closeZip(ZipFile zipFile)135     public static void closeZip(ZipFile zipFile) {
136         if (zipFile != null) {
137             try {
138                 zipFile.close();
139             } catch (IOException e) {
140                 // ignore
141             }
142         }
143     }
144 }
145