diff --git a/build.gradle b/build.gradle index 267a399..e232689 100644 --- a/build.gradle +++ b/build.gradle @@ -15,6 +15,7 @@ repositories { dependencies { compile 'org.bouncycastle:bcprov-jdk15on:1.51' + compile 'org.kamranzafar:jtar:2.3' } sourceSets { diff --git a/src/org/nick/abe/AndroidBackup.java b/src/org/nick/abe/AndroidBackup.java index 7a07ce0..d53f0ff 100644 --- a/src/org/nick/abe/AndroidBackup.java +++ b/src/org/nick/abe/AndroidBackup.java @@ -4,6 +4,7 @@ import java.io.ByteArrayOutputStream; import java.io.Console; import java.io.DataOutputStream; +import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; @@ -27,6 +28,9 @@ import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; import org.bouncycastle.crypto.params.KeyParameter; +import org.kamranzafar.jtar.TarEntry; +import org.kamranzafar.jtar.TarInputStream; + // mostly lifted off com.android.server.BackupManagerService.java public class AndroidBackup { @@ -50,8 +54,8 @@ public class AndroidBackup { private AndroidBackup() { } - public static void extractAsTar(String backupFilename, String filename, - String password) { + private static InputStream getContentInputStream(String backupFilename, String + password) { try { InputStream rawInStream = getInputStream(backupFilename); CipherInputStream cipherStream = null; @@ -173,6 +177,16 @@ public static void extractAsTar(String backupFilename, String filename, InputStream baseStream = isEncrypted ? cipherStream : rawInStream; InputStream in = isCompressed ? new InflaterInputStream(baseStream) : baseStream; + return in; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static void extractAsTar(String backupFilename, String filename, + String password) { + try { + InputStream in = getContentInputStream(backupFilename, password); OutputStream out = null; try { out = getOutputStream(filename); @@ -204,6 +218,55 @@ public static void extractAsTar(String backupFilename, String filename, } } + public static void extractHelium(String backupFilename, String outputDirectory, String password) { + InputStream in = getContentInputStream(backupFilename, password); + + try { + TarInputStream tis = new TarInputStream(in); + + File outputDir = new File(outputDirectory); + outputDir.mkdirs(); + + TarEntry entry; + + while((entry = tis.getNextEntry()) != null) { + if (DEBUG) { + System.err.println(entry.getName()); + } + + String name = entry.getName(); + if (entry.isDirectory()) + { + new File(outputDir, entry.getName()).mkdirs(); + } + else + { + int lastSlashIndex = name.lastIndexOf("/"); + String dironly = name.substring(0, lastSlashIndex); + File destDir = new File(outputDir, dironly); + destDir.mkdirs(); + File destFile = new File (destDir, name.substring(lastSlashIndex + 1)); + + int count; + byte data[] = new byte[2048]; + FileOutputStream dest = new FileOutputStream(destFile); + + while((count = tis.read(data)) != -1) { + dest.write(data, 0, count); + } + + dest.flush(); + dest.close(); + } + } + + tis.close(); + } catch (Exception e) + { + throw new RuntimeException(e); + } + } + public static void packTar(String tarFilename, String backupFilename, String password, boolean isKitKat) { boolean encrypting = password != null && !"".equals(password); diff --git a/src/org/nick/abe/Main.java b/src/org/nick/abe/Main.java index 56f8ebf..6cd97a5 100644 --- a/src/org/nick/abe/Main.java +++ b/src/org/nick/abe/Main.java @@ -17,15 +17,12 @@ public static void main(String[] args) { } String mode = args[0]; - if (!"pack".equals(mode) && !"unpack".equals(mode) && !"pack-kk".equals(mode)) { + if (!"pack".equals(mode) && !"unpack".equals(mode) && !"pack-kk".equals(mode) && !"unpack-helium".equals(mode)) { usage(); System.exit(1); } - boolean unpack = "unpack".equals(mode); - String backupFilename = unpack ? args[1] : args[2]; - String tarFilename = unpack ? args[2] : args[1]; String password = null; if (args.length > 3) { password = args[3]; @@ -36,9 +33,17 @@ public static void main(String[] args) { password = System.getenv("ABE_PASSWD"); } - if (unpack) { + if ("unpack".equals(mode)) { + String backupFilename = args[1]; + String tarFilename = args[2]; AndroidBackup.extractAsTar(backupFilename, tarFilename, password); + } else if ("unpack-helium".equals(mode)) { + String backupFilename = args[1]; + String outputDirectory = args[2]; + AndroidBackup.extractHelium(backupFilename, outputDirectory, password); } else { + String backupFilename = args[2]; + String tarFilename = args[1]; boolean isKitKat = "pack-kk".equals(mode); AndroidBackup.packTar(tarFilename, backupFilename, password, isKitKat); } @@ -49,6 +54,8 @@ private static void usage() { System.out.println("Usage:"); System.out .println(" unpack:\tabe unpack\t [password]"); + System.out + .println(" unpack-helium:\tabe unpack-helium\t outputDir [password]"); System.out .println(" pack:\t\tabe pack\t [password]"); System.out