diff --git a/jadx-core/build.gradle b/jadx-core/build.gradle index a5f47fde1..fa292422d 100644 --- a/jadx-core/build.gradle +++ b/jadx-core/build.gradle @@ -4,7 +4,7 @@ dependencies { runtime files(jadxClasspath) compile files('lib/dx-1.13.jar') - compile 'commons-io:commons-io:2.4' + compile 'commons-io:commons-io:2.6' compile 'org.ow2.asm:asm:5.0.3' compile 'com.intellij:annotations:12.0' compile 'uk.com.robust-it:cloning:1.9.2' diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java index 37a8e4c74..df06281f8 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java @@ -1,5 +1,12 @@ package jadx.core.dex.visitors; +import java.io.File; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.io.FilenameUtils; + import jadx.api.IJadxArgs; import jadx.core.Consts; import jadx.core.codegen.TypeGen; @@ -15,20 +22,11 @@ import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; import jadx.core.utils.exceptions.JadxException; +import jadx.core.utils.files.FileUtils; import jadx.core.utils.files.InputFile; -import java.io.File; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.IOCase; - public class RenameVisitor extends AbstractVisitor { - private static final boolean CASE_SENSITIVE_FS = IOCase.SYSTEM.isCaseSensitive(); - private Deobfuscator deobfuscator; @Override @@ -36,7 +34,7 @@ public class RenameVisitor extends AbstractVisitor { IJadxArgs args = root.getArgs(); List dexNodes = root.getDexNodes(); - if (dexNodes.size() == 0) { + if (dexNodes.isEmpty()) { return; } InputFile firstInputFile = dexNodes.get(0).getDexFile().getInputFile(); @@ -50,7 +48,8 @@ public class RenameVisitor extends AbstractVisitor { if (deobfuscationOn) { deobfuscator.execute(); } - checkClasses(root); + boolean isCaseSensitive = FileUtils.isCaseSensitiveFS(new File(inputPath)); // args.getOutDir() - not set in gui + checkClasses(root, isCaseSensitive); } @Override @@ -63,11 +62,11 @@ public class RenameVisitor extends AbstractVisitor { return false; } - private void checkClasses(RootNode root) { + private void checkClasses(RootNode root, boolean caseSensitive) { Set clsNames = new HashSet<>(); for (ClassNode cls : root.getClasses(true)) { checkClassName(cls); - if (!CASE_SENSITIVE_FS) { + if (!caseSensitive) { ClassInfo classInfo = cls.getClassInfo(); String clsFileName = classInfo.getAlias().getFullPath(); if (!clsNames.add(clsFileName.toLowerCase())) { diff --git a/jadx-core/src/main/java/jadx/core/export/ExportGradleProject.java b/jadx-core/src/main/java/jadx/core/export/ExportGradleProject.java index d6e91f742..e951b0ed5 100644 --- a/jadx-core/src/main/java/jadx/core/export/ExportGradleProject.java +++ b/jadx-core/src/main/java/jadx/core/export/ExportGradleProject.java @@ -40,8 +40,8 @@ public class ExportGradleProject { public void init() { try { - FileUtils.makeDirsForFile(srcOutDir); - FileUtils.makeDirsForFile(resOutDir); + FileUtils.makeDirs(srcOutDir); + FileUtils.makeDirs(resOutDir); saveBuildGradle(); skipGeneratedClasses(); } catch (Exception e) { diff --git a/jadx-core/src/main/java/jadx/core/utils/files/FileUtils.java b/jadx-core/src/main/java/jadx/core/utils/files/FileUtils.java index b7dc28a16..71eae79e7 100644 --- a/jadx-core/src/main/java/jadx/core/utils/files/FileUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/files/FileUtils.java @@ -1,7 +1,5 @@ package jadx.core.utils.files; -import jadx.core.utils.exceptions.JadxRuntimeException; - import java.io.BufferedInputStream; import java.io.Closeable; import java.io.File; @@ -18,10 +16,14 @@ import java.util.jar.JarOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import org.apache.commons.io.IOCase; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jadx.core.utils.exceptions.JadxRuntimeException; + public class FileUtils { private static final Logger LOG = LoggerFactory.getLogger(FileUtils.class); @@ -43,12 +45,19 @@ public class FileUtils { } public static void makeDirsForFile(File file) { - File dir = file.getParentFile(); - if (dir != null && !dir.exists()) { - // if directory already created in other thread mkdirs will return false, - // so check dir existence again - if (!dir.mkdirs() && !dir.exists()) { - throw new JadxRuntimeException("Can't create directory " + dir); + if (file != null) { + makeDirs(file.getParentFile()); + } + } + + private static final Object MKDIR_SYNC = new Object(); + + public static void makeDirs(@Nullable File dir) { + if (dir != null) { + synchronized (MKDIR_SYNC) { + if (!dir.exists() && !dir.mkdirs()) { + throw new JadxRuntimeException("Can't create directory " + dir); + } } } } @@ -186,4 +195,32 @@ public class FileUtils { } } } + + public static boolean isCaseSensitiveFS(File testDir) { + if (testDir != null) { + File caseCheckUpper = new File(testDir, "CaseCheck"); + File caseCheckLow = new File(testDir, "casecheck"); + try { + makeDirs(testDir); + if (caseCheckUpper.createNewFile()) { + boolean caseSensitive = !caseCheckLow.exists(); + LOG.debug("Filesystem at {} is {} case-sensitive", testDir.getAbsolutePath(), + (caseSensitive ? "" : "NOT")); + return caseSensitive; + } else { + LOG.debug("Failed to create file: {}", caseCheckUpper.getAbsolutePath()); + } + } catch (Exception e) { + LOG.debug("Failed to detect filesystem case-sensitivity by file creation", e); + } finally { + try { + caseCheckUpper.delete(); + caseCheckLow.delete(); + } catch (Exception e) { + // ignore + } + } + } + return IOCase.SYSTEM.isCaseSensitive(); + } }