Merge pull request #1758 from iBotPeaches/issue-1272

Prevent "too long" commands on Windows.
This commit is contained in:
Connor Tumbleson 2018-04-06 10:45:22 -04:00 committed by GitHub
commit 335d51c0f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 100 additions and 57 deletions

View File

@ -19,7 +19,6 @@ package brut.androlib;
import brut.androlib.meta.MetaInfo; import brut.androlib.meta.MetaInfo;
import brut.androlib.meta.UsesFramework; import brut.androlib.meta.UsesFramework;
import brut.androlib.res.AndrolibResources; import brut.androlib.res.AndrolibResources;
import brut.androlib.res.data.ResConfigFlags;
import brut.androlib.res.data.ResPackage; import brut.androlib.res.data.ResPackage;
import brut.androlib.res.data.ResTable; import brut.androlib.res.data.ResTable;
import brut.androlib.res.data.ResUnknownFiles; import brut.androlib.res.data.ResUnknownFiles;
@ -41,8 +40,6 @@ import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
@ -161,28 +158,15 @@ public class Androlib {
} }
} }
public void recordUncompressedFiles(ExtFile apkFile, Collection<String> uncompressedFilesOrExts) throws AndrolibException { public void recordUncompressedFiles(ExtFile apkFile, Collection<String> uncompressedFiles) throws AndrolibException {
try { try {
Directory unk = apkFile.getDirectory(); Directory unk = apkFile.getDirectory();
Set<String> files = unk.getFiles(true); Set<String> files = unk.getFiles(true);
String ext;
for (String file : files) { for (String file : files) {
if (isAPKFileNames(file) && !NO_COMPRESS_PATTERN.matcher(file).find()) { if (isAPKFileNames(file) && !NO_COMPRESS_PATTERN.matcher(file).find()) {
if (unk.getCompressionLevel(file) == 0) { if (unk.getCompressionLevel(file) == 0 && !uncompressedFiles.contains(file)) {
uncompressedFiles.add(file);
if (StringUtils.countMatches(file, ".") > 1) {
ext = file;
} else {
ext = FilenameUtils.getExtension(file);
if (ext.isEmpty()) {
ext = file;
}
}
if (! uncompressedFilesOrExts.contains(ext)) {
uncompressedFilesOrExts.add(ext);
}
} }
} }
} }
@ -277,6 +261,7 @@ public class Androlib {
apkOptions.isFramework = meta.isFrameworkApk; apkOptions.isFramework = meta.isFrameworkApk;
apkOptions.resourcesAreCompressed = meta.compressionType; apkOptions.resourcesAreCompressed = meta.compressionType;
apkOptions.doNotCompress = meta.doNotCompress; apkOptions.doNotCompress = meta.doNotCompress;
apkOptions.noCompressAssets = meta.noCompressAssets;
mAndRes.setSdkInfo(meta.sdkInfo); mAndRes.setSdkInfo(meta.sdkInfo);
mAndRes.setPackageId(meta.packageInfo); mAndRes.setPackageId(meta.packageInfo);
@ -308,8 +293,8 @@ public class Androlib {
buildApk(appDir, outFile); buildApk(appDir, outFile);
// we must go after the Apk is built, and copy the files in via Zip // we must go after the Apk is built, and copy the files in via Zip
// this is because Aapt won't add files it doesn't know (ex unknown files) // this is because Aapt won't add files it doesn't know (ex unknown files & uncompressed assets)
buildUnknownFiles(appDir, outFile, meta); buildUnknownAndUncompressedAssets(appDir, outFile, meta);
// we copied the AndroidManifest.xml to AndroidManifest.xml.orig so we can edit it // we copied the AndroidManifest.xml to AndroidManifest.xml.orig so we can edit it
// lets restore the unedited one, to not change the original // lets restore the unedited one, to not change the original
@ -603,38 +588,46 @@ public class Androlib {
} }
} }
public void buildUnknownFiles(File appDir, File outFile, MetaInfo meta) public void buildUnknownAndUncompressedAssets(File appDir, File outFile, MetaInfo meta)
throws AndrolibException { throws AndrolibException {
if (meta.unknownFiles == null && meta.noCompressAssets == null) {
return;
}
if (meta.unknownFiles != null) { if (meta.unknownFiles != null) {
LOGGER.info("Copying unknown files/dir..."); LOGGER.info("Copying unknown files/dir...");
Map<String, String> files = meta.unknownFiles;
File tempFile = new File(outFile.getParent(), outFile.getName() + ".apktool_temp");
boolean renamed = outFile.renameTo(tempFile);
if (!renamed) {
throw new AndrolibException("Unable to rename temporary file");
}
try (
ZipFile inputFile = new ZipFile(tempFile);
ZipOutputStream actualOutput = new ZipOutputStream(new FileOutputStream(outFile))
) {
copyExistingFiles(inputFile, actualOutput);
copyUnknownFiles(appDir, actualOutput, files);
} catch (IOException | BrutException ex) {
throw new AndrolibException(ex);
}
// Remove our temporary file.
tempFile.delete();
} }
File tempFile = new File(outFile.getParent(), outFile.getName() + ".apktool_temp");
boolean renamed = outFile.renameTo(tempFile);
if (!renamed) {
throw new AndrolibException("Unable to rename temporary file");
}
try (
ZipFile inputFile = new ZipFile(tempFile);
ZipOutputStream actualOutput = new ZipOutputStream(new FileOutputStream(outFile))
) {
copyExistingFiles(inputFile, actualOutput, meta.noCompressAssets);
copyUncompressedAssetFiles(appDir, actualOutput, meta.noCompressAssets);
copyUnknownFiles(appDir, actualOutput, meta.unknownFiles);
} catch (IOException | BrutException ex) {
throw new AndrolibException(ex);
}
// Remove our temporary file.
tempFile.delete();
} }
private void copyExistingFiles(ZipFile inputFile, ZipOutputStream outputFile) throws IOException { private void copyExistingFiles(ZipFile inputFile, ZipOutputStream outputFile, Collection<String> assets)
throws IOException {
// First, copy the contents from the existing outFile: // First, copy the contents from the existing outFile:
Enumeration<? extends ZipEntry> entries = inputFile.entries(); Enumeration<? extends ZipEntry> entries = inputFile.entries();
while (entries.hasMoreElements()) { while (entries.hasMoreElements()) {
ZipEntry entry = new ZipEntry(entries.nextElement()); ZipEntry entry = new ZipEntry(entries.nextElement());
if (assets != null && assets.contains(entry.getName())) {
continue;
}
// We can't reuse the compressed size because it depends on compression sizes. // We can't reuse the compressed size because it depends on compression sizes.
entry.setCompressedSize(-1); entry.setCompressedSize(-1);
@ -651,25 +644,26 @@ public class Androlib {
private void copyUnknownFiles(File appDir, ZipOutputStream outputFile, Map<String, String> files) private void copyUnknownFiles(File appDir, ZipOutputStream outputFile, Map<String, String> files)
throws BrutException, IOException { throws BrutException, IOException {
File unknownFileDir = new File(appDir, UNK_DIRNAME); File unknownFileDir = new File(appDir, UNK_DIRNAME);
if (files == null || files.isEmpty()) {
return;
}
// loop through unknown files // loop through unknown files
for (Map.Entry<String,String> unknownFileInfo : files.entrySet()) { for (Map.Entry<String,String> unknownFileInfo : files.entrySet()) {
File inputFile = new File(unknownFileDir, BrutIO.sanitizeUnknownFile(unknownFileDir, unknownFileInfo.getKey())); String normalizedPath = BrutIO.normalizePath(unknownFileInfo.getKey());
String cleanedPath = BrutIO.sanitizeUnknownFile(unknownFileDir, normalizedPath);
File inputFile = new File(unknownFileDir, cleanedPath);
if (inputFile.isDirectory()) { if (inputFile.isDirectory()) {
continue; continue;
} }
ZipEntry newEntry = new ZipEntry(unknownFileInfo.getKey()); ZipEntry newEntry = new ZipEntry(cleanedPath);
int method = Integer.parseInt(unknownFileInfo.getValue()); int method = Integer.parseInt(unknownFileInfo.getValue());
LOGGER.fine(String.format("Copying unknown file %s with method %d", unknownFileInfo.getKey(), method)); LOGGER.fine(String.format("Copying unknown file %s with method %d", unknownFileInfo.getKey(), method));
if (method == ZipEntry.STORED) { if (method == ZipEntry.STORED) {
newEntry.setMethod(ZipEntry.STORED); newEntry = getStoredZipEntry(cleanedPath, inputFile);
newEntry.setSize(inputFile.length());
newEntry.setCompressedSize(-1);
BufferedInputStream unknownFile = new BufferedInputStream(new FileInputStream(inputFile));
CRC32 crc = BrutIO.calculateCrc(unknownFile);
newEntry.setCrc(crc.getValue());
} else { } else {
newEntry.setMethod(ZipEntry.DEFLATED); newEntry.setMethod(ZipEntry.DEFLATED);
} }
@ -680,6 +674,46 @@ public class Androlib {
} }
} }
private void copyUncompressedAssetFiles(File appDir, ZipOutputStream outputFile, Collection<String> files)
throws BrutException, IOException {
if (files == null || files.isEmpty()) {
return;
}
File assetFileDir = new File(appDir, ASSET_DIRNAME);
for (String asset : files) {
String normalizedPath = BrutIO.normalizePath(asset);
String cleanedPath = BrutIO.sanitizeUnknownFile(assetFileDir, normalizedPath);
File inputFile = new File(appDir, cleanedPath);
if (inputFile.isDirectory()) {
continue;
}
LOGGER.fine(String.format("Copying uncompressed asset: %s", normalizedPath));
ZipEntry newEntry = getStoredZipEntry(normalizedPath, inputFile);
outputFile.putNextEntry(newEntry);
BrutIO.copy(inputFile, outputFile);
outputFile.closeEntry();
}
}
private ZipEntry getStoredZipEntry(String cleanedPath, File inputFile)
throws IOException {
ZipEntry newEntry = new ZipEntry(cleanedPath);
newEntry.setMethod(ZipEntry.STORED);
newEntry.setSize(inputFile.length());
newEntry.setCompressedSize(-1);
BufferedInputStream unknownFile = new BufferedInputStream(new FileInputStream(inputFile));
CRC32 crc = BrutIO.calculateCrc(unknownFile);
newEntry.setCrc(crc.getValue());
return newEntry;
}
public void buildApk(File appDir, File outApk) throws AndrolibException { public void buildApk(File appDir, File outApk) throws AndrolibException {
LOGGER.info("Building apk file..."); LOGGER.info("Building apk file...");
if (outApk.exists()) { if (outApk.exists()) {
@ -777,6 +811,7 @@ public class Androlib {
private final static String SMALI_DIRNAME = "smali"; private final static String SMALI_DIRNAME = "smali";
private final static String APK_DIRNAME = "build/apk"; private final static String APK_DIRNAME = "build/apk";
private final static String UNK_DIRNAME = "unknown"; private final static String UNK_DIRNAME = "unknown";
private final static String ASSET_DIRNAME = "assets";
private final static String[] APK_RESOURCES_FILENAMES = new String[] { private final static String[] APK_RESOURCES_FILENAMES = new String[] {
"resources.arsc", "AndroidManifest.xml", "res" }; "resources.arsc", "AndroidManifest.xml", "res" };
private final static String[] APK_RESOURCES_WITHOUT_RES_FILENAMES = new String[] { private final static String[] APK_RESOURCES_WITHOUT_RES_FILENAMES = new String[] {
@ -791,6 +826,6 @@ public class Androlib {
// Taken from AOSP's frameworks/base/tools/aapt/Package.cpp // Taken from AOSP's frameworks/base/tools/aapt/Package.cpp
private final static Pattern NO_COMPRESS_PATTERN = Pattern.compile("\\.(" + private final static Pattern NO_COMPRESS_PATTERN = Pattern.compile("\\.(" +
"jpg|jpeg|png|gif|wav|mp2|mp3|ogg|aac|mpg|mpeg|mid|midi|smf|jet|rtttl|imy|xmf|mp4|" + "jpg|jpeg|png|gif|wav|mp2|mp3|ogg|aac|mpg|mpeg|mid|midi|smf|jet|rtttl|imy|xmf|mp4|" +
"m4a|m4v|3gp|3gpp|3g2|3gpp2|amr|awb|wma|wmv|webm|mkv)$"); "m4a|m4v|3gp|3gpp|3g2|3gpp2|amr|awb|wma|wmv|webm|mkv|arsc)$");
} }

View File

@ -418,7 +418,7 @@ public class ApkDecoder {
private void putFileCompressionInfo(MetaInfo meta) throws AndrolibException { private void putFileCompressionInfo(MetaInfo meta) throws AndrolibException {
if (mUncompressedFiles != null && !mUncompressedFiles.isEmpty()) { if (mUncompressedFiles != null && !mUncompressedFiles.isEmpty()) {
meta.doNotCompress = mUncompressedFiles; meta.noCompressAssets = mUncompressedFiles;
} }
} }

View File

@ -28,7 +28,10 @@ public class ApkOptions {
public boolean isFramework = false; public boolean isFramework = false;
public boolean resourcesAreCompressed = false; public boolean resourcesAreCompressed = false;
public boolean useAapt2 = false; public boolean useAapt2 = false;
@Deprecated
public Collection<String> doNotCompress; public Collection<String> doNotCompress;
public Collection<String> noCompressAssets;
public String frameworkFolderLocation = null; public String frameworkFolderLocation = null;
public String frameworkTag = null; public String frameworkTag = null;

View File

@ -37,7 +37,10 @@ public class MetaInfo {
public boolean sharedLibrary; public boolean sharedLibrary;
public boolean sparseResources; public boolean sparseResources;
public Map<String, String> unknownFiles; public Map<String, String> unknownFiles;
@Deprecated
public Collection<String> doNotCompress; public Collection<String> doNotCompress;
public Collection<String> noCompressAssets;
private static Yaml getYaml() { private static Yaml getYaml() {
DumperOptions options = new DumperOptions(); DumperOptions options = new DumperOptions();

View File

@ -16,6 +16,7 @@
*/ */
package brut.androlib.res.data; package brut.androlib.res.data;
import brut.util.BrutIO;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
@ -27,7 +28,7 @@ public class ResUnknownFiles {
private final Map<String, String> mUnknownFiles = new LinkedHashMap<>(); private final Map<String, String> mUnknownFiles = new LinkedHashMap<>();
public void addUnknownFileInfo(String file, String value) { public void addUnknownFileInfo(String file, String value) {
mUnknownFiles.put(file, value); mUnknownFiles.put(BrutIO.normalizePath(file), value);
} }
public Map<String, String> getUnknownFiles() { public Map<String, String> getUnknownFiles() {

View File

@ -31,6 +31,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.logging.Logger; import java.util.logging.Logger;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
@ -45,11 +46,11 @@ public class BaseTest {
Map<String, String> controlFiles = control.unknownFiles; Map<String, String> controlFiles = control.unknownFiles;
Map<String, String> testFiles = test.unknownFiles; Map<String, String> testFiles = test.unknownFiles;
assertTrue(controlFiles.size() == testFiles.size()); assertEquals(controlFiles.size(), testFiles.size());
// Make sure that the compression methods are still the same // Make sure that the compression methods are still the same
for (Map.Entry<String, String> controlEntry : controlFiles.entrySet()) { for (Map.Entry<String, String> controlEntry : controlFiles.entrySet()) {
assertTrue(controlEntry.getValue().equals(testFiles.get(controlEntry.getKey()))); assertEquals(controlEntry.getValue(), testFiles.get(controlEntry.getKey()));
} }
} }

View File

@ -62,7 +62,7 @@ public class DoubleExtensionUnknownFileTest extends BaseTest {
apkDecoder.decode(); apkDecoder.decode();
MetaInfo metaInfo = new Androlib().readMetaFile(decodedApk); MetaInfo metaInfo = new Androlib().readMetaFile(decodedApk);
for (String string : metaInfo.doNotCompress) { for (String string : metaInfo.noCompressAssets) {
if (StringUtils.countMatches(string, ".") > 1) { if (StringUtils.countMatches(string, ".") > 1) {
assertTrue(string.equalsIgnoreCase("assets/bin/Data/sharedassets1.assets.split0")); assertTrue(string.equalsIgnoreCase("assets/bin/Data/sharedassets1.assets.split0"));
} }